Es saskāros ar šo dīvaino makro kodu /usr/include/linux/kernel.h:
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
Ko dara :-!!
?
Tas būtībā ir veids, kā pārbaudīt, vai izteiksmi e var novērtēt kā 0, un, ja nē, būvēt neizdodas.
Makro ir nedaudz nepareizi nosaukts; tam vajadzētu būt kaut kam līdzīgam BUILD_BUG_OR_ZERO
, nevis ...ON_ZERO
. (Par to, vai šis nosaukums ir mulsinošs, ir bijušas periodiskas diskusijas.)
Izteiciens jālasa šādi:
sizeof(struct { int: -!!(e); }))
(e)
: Aprēķina izteiksmi e
.
!!(e)
: Logiski noliegt divas reizes: 0
, ja e == 0
; citādi 1
.
-!!(e)
: Skaitliski noliegt 2. soļa izteiksmi: 0
, ja tā bija 0
; citādi -1
.
`strukt{int: -!!(0);} --> struct{int: 0;}``: Ja tas bija nulle, tad mēs deklarējam struct ar anonīmu veselu skaitļu bītu lauku, kura platums ir nulle. Viss ir kārtībā, un mēs rīkojamies kā parasti.
struct{int: -!!!(1);} --> struct{int: -1;}
: No otras puses, ja tas nav nulle, tad tas būs kāds negatīvs skaitlis. Jebkura bitu lauka ar negatīvu platumu deklarēšana ir kompilācijas kļūda.
Tātad vai nu mēs iegūsim bītu lauku, kura platums struktūrā ir 0, kas ir kārtībā, vai arī bītu lauku ar negatīvu platumu, kas ir kompilācijas kļūda. Tad mēs ņemam sizeof
šī lauka lielumu, lai iegūtu size_t
ar atbilstošu platumu (kas būs nulle gadījumā, kad e
ir nulle).
Daži cilvēki ir jautājuši: **Kāpēc vienkārši neizmantot assert
?
keithmo's atbilde šeit ir laba atbilde:
Šie makroši īsteno kompilēšanas laika testu, bet assert() ir izpildes laika tests.
Tieši tā. Jūs taču nevēlaties, lai jūsu kodolā izpildes laikā tiktu atklātas problēmas, kuras varēja noķert agrāk! Tas ir kritiski svarīgs operētājsistēmas elements. Lai cik lielā mērā problēmas var atklāt kompilēšanas laikā, jo labāk.
:
ir bītu lauks. Attiecībā uz !!
tas ir loģiskā dubultā noliegšana, tāpēc tas atgriež 0
, ja tas ir false, vai 1
, ja tas ir true. Un -
ir mīnusa zīme, t. i., aritmētiskā noliegšana.
Tas viss ir tikai triks, lai liktu kompilatoram bļaut par nederīgiem ievades datiem.
Apskatiet BUILD_BUG_ON_ZERO
. Ja -!!(e)
tiek novērtēts kā negatīva vērtība, tas rada kompilēšanas kļūdu. Pretējā gadījumā -!!!(e)
tiek novērtēts kā 0, un 0 platuma bītu laukam ir izmērs 0. Tādējādi makro tiek novērtēts kā size_t
ar vērtību 0.
Pēc manām domām, nosaukums ir vājš, jo kompilēšana faktiski neizdodas, ja ievade nav nulle.
BUILD_BUG_ON_NULL
ir ļoti līdzīgs, bet tas dod rādītāju, nevis int
.
Ja nosacījums ir nepatiess, tiek izveidots 0
lieluma bītu lauks, bet, ja nosacījums ir patiess/nē nulle, tiek izveidots -1
(-!!1
) lieluma bītu lauks. Pirmajā gadījumā nav kļūdas, un struct tiek inicializēta ar elementu int. Otrajā gadījumā tiek pieļauta kompilēšanas kļūda (un, protams, netiek izveidots -1
izmēra bitfīldarbs).