Kas ": - !!" C kode?

Šį keistą makrokodą susidūriau /usr/include/linux/kernel.h:

  #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) 

:-!! ?

1470 m
10 февр. rinkinys chmurli vasaris 10 2012-02-10 17:50 '12, 5:50 val. 2012-02-10 17:50
@ 6 atsakymai

Iš esmės tai yra būdas patikrinti, ar išraiška e gali būti vertinama kaip 0, o jei ne, atsisakyti statyti .

Makro yra šiek tiek nenurodytas; tai turėtų būti kažkas panašaus į BUILD_BUG_OR_ZERO , ne ...ON_ZERO . ( Kartais buvo diskutuojama, ar tai yra painus pavadinimas .)

Turėtumėte perskaityti išraišką taip:

 sizeof(struct { int: -!!(e); })) 
  • (e) : Įvertinkite išraišką e .

  • !!(e) : logiškai paneigti du kartus: 0 jei e == 0 ; kitaip 1 .

  • -!!(e) : skaitinė išraiška iš 2: 0 išraiška, jei ji buvo 0 ; kitaip -1 .

  • struct{int: -!!(0);} --> struct{int: 0;} : Jei tai buvo nulis, mes paskelbiame struktūrą, kurios anoniminis bitų laukas yra nulis. Viskas yra normalu, ir mes elgiamės normaliai.

  • struct{int: -!!(1);} --> struct{int: -1;} : Kita vertus, jei jis nėra nulis, tai bus tam tikras neigiamas skaičius. Bet kokio neigiamo pločio bitų deklaravimas yra kompiliavimo klaida.

Taigi, mes arba baigiame bitų lauką, kuris yra 0 pločio struktūroje, o tai yra gerai, arba bitų laukas, turintis neigiamą plotį, o tai yra kompiliavimo klaida. Tada mes imamės šio lauko dydžio, todėl gauname size_t su atitinkamu pločiu (kuris bus lygus nuliui, kai e yra nulis).


Kai kurie žmonės paklausė: kodėl gi ne naudoti?

Keithmo atsakymas čia yra geras atsakymas:

Šie makrokomandos įdiegia kompiliavimo laiko testą, ir patvirtinti () yra vykdymo laiko testas.

Tiksliai. Jūs nenorite aptikti jūsų branduolio problemų vykdymo metu, kurios gali būti aptiktos anksčiau! Tai yra svarbi operacinės sistemos dalis. Bet kokiu atveju problemos gali būti aptinkamos kompiliavimo metu, tuo geriau.

1524
10 февр. Atsakymą pateikė John Feminella , vasario 10 d. 2012-02-10 18:04 '12, 18:04, 2012-02-10 18:04

: - bitų. Kaip !! , tai yra, loginis dvigubas neigiamas , todėl grąžina 0 už klaidingą arba 1 už tikrąjį. Ir - yra minuso ženklas, t.y. Aritmetinis neigimas.

Tai tik apgauti, kad kompiliatorius blokuotų neteisingus įrašus.

Apsvarstykite BUILD_BUG_ON_ZERO . Kai -!!(e) įvertina neigiamą vertę, tai sukelia kompiliavimo klaidą. Priešingu atveju, -!!(e) yra apskaičiuota kaip 0, o bitų laukas, kurio plotis yra 0, yra 0 dydžio. Todėl makro vertė vertinama kaip size_t , kurios vertė yra 0.

border=0

Mano nuomone, pavadinimas yra silpnas, nes surinkimas nėra faktiškai įvykdytas, kai įėjimas yra nulinis.

BUILD_BUG_ON_NULL labai panašus, bet suteikia rodyklę, o ne int .

234
10 февр. David Heffernan atsakymas vasario 10 d 2012-02-10 17:54 '12, 17:54, 2012-02-10 17:54

Kai kurie žmonės, atrodo, sumaišyti šiuos makrokomandas su assert() .

Šie makrokomandos įdiegia kompiliavimo laiko testą, ir assert() yra vykdymo laiko testas.

148
10 февр. atsakymą pateikė keithmo 10 vasaris. 2012-02-10 18:37 '12, 18:37, 2012-02-10 18:37

Na, esu labai nustebęs, kad šios sintaksės alternatyvos nebuvo paminėtos. Kitas bendras (bet senesnis) mechanizmas yra funkcijų skambutis, kuris nėra apibrėžtas, ir remiasi optimizatoriumi, kad sukompiliuotų funkcijų skambutį, jei jūsų pareiškimas yra teisingas.

 #define MY_COMPILETIME_ASSERT(test) \ do { \ extern void you_did_something_bad(void); \ if (!(test)) \ you_did_something_bad(void); \ } while (0) 

Nors šis mechanizmas veikia (tol, kol įjungiamas optimizavimas), jis turi trūkumą, nes nepraneša apie klaidą, kol neprisijungsite, ir šiuo metu jis negalėjo rasti „you_did_something_bad“ () funkcijos apibrėžties. Todėl branduolių kūrėjai pradeda naudoti triukus, tokius kaip bitų pločiai, turintys neigiamą dydį, ir masyvai, turintys neigiamą dydį (vėlesnis iš jų sustabdė surinkimo pertrauką GCC 4.4).

Kartu su kompiliavimo laiko pareiškimų poreikiu GCC 4.3 pristatė error funkcijos atributą, leidžiančią išplėsti šią seną koncepciją, bet generuoti kompiliavimo laiko klaidą su jūsų pasirinktu pranešimu - ne daugiau kritinių „neigiamų dydžių“ klaidų pranešimų!

 #define MAKE_SURE_THIS_IS_FIVE(number) \ do { \ extern void this_isnt_five(void) __attribute__((error( \ "I asked for five and you gave me " #number))); \ if ((number) != 5) \ this_isnt_five(); \ } while (0) 

Iš tiesų, pradedant nuo Linux 3.9, dabar turime makrokomandą, pavadintą compiletime_assert , kuris naudoja šią funkciją ir daugumą makrokomandų atitinkamai bug.h Tačiau šio makro negalima naudoti kaip iniciatorių. Tačiau, naudodami išraiškos išraiškas (kitą C-GCC plėtinį), galite!

 #define ANY_NUMBER_BUT_FIVE(number) \ ({ \ typeof(number) n = (number); \ extern void this_number_is_five(void) __attribute__(( \ error("I told you not to give me a five!"))); \ if (n == 5) \ this_number_is_five(); \ n; \ }) 

Šis makrokomandas įvertins jo parametrą tiksliai vieną kartą (tuo atveju, jei jis turi šalutinį poveikį) ir sukuria kompiliavimo laiko klaidą, kuri sako: „Aš jums sakiau, kad nesuteikite man penkių!“. jei išraiška vertinama iki penkių arba nėra kompiliavimo laiko konstanta.

Tad kodėl mes nenaudojame to vietoj neigiamo dydžio bitų laukų? Deja, šiuo metu yra daug apribojimų operatoriaus išraiškų naudojimui, įskaitant jų naudojimą kaip pastovius iniciatorius (išvardytų tipų konstantų, bitų lauko pločio ir kt.), Net jei operatoriaus išraiška yra visiškai savarankiška (ty Jis gali būti visiškai įvertintas kompiliavimo metu ir kitaip praeina testą __builtin_constant_p() ). Be to, jie negali būti naudojami už funkcijų kūno ribų.

Tikimės, kad GCC netrukus pakeis šiuos trūkumus ir leis naudoti pastovias išraiškas kaip nuolatinius iniciatorius. Čia užduotis yra kalbos specifikacija, apibrėžianti teisinę pastovią išraišką. C ++ 11 pridėjo šio tipo ar dalyko constexpr raktinį žodį, tačiau C11 ekvivalento nėra. Nors C11 gavo statinius teiginius, kurie išsprendžia dalį šios problemos, jie neišspręs visų šių trūkumų. Todėl tikiuosi, kad gcc gali padaryti constexpr prieinamą kaip -std = gnuc99 ir -std = gnuc11 arba kai kuriuos iš jų ir leisti jį naudoti et. ir kiti

42
27 июня '13 в 11:21 2013-06-27 11:21 atsakymą pateikė Daniel Santos birželio 13 d. 11:21 2013-06-27 11:21

Jis sukuria 0 dydžio bitų lauką, jei sąlyga yra klaidinga, bet bitų laukas, kurio dydis -1 ( -!!1 ), jei sąlyga yra tiesa / ne. Pirmuoju atveju nėra klaidos ir struktūra inicijuojama naudojant int narį. Pastaruoju atveju įvyksta kompiliavimo klaida (ir, žinoma, toks -1 bitų laukas nėra sukurtas).

31
10 февр. Atsakymą pateikė Matt Phillips 10 vasaris. 2012-02-10 17:54 '12, 17:54, 2012-02-10 17:54
  Linux Kernel :  #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) 
-1
21 июня '18 в 10:18 2018-06-21 10:18 Atsakymą duoda leesagacious birželio 21 d. 18 val. 10:18 2018-06-21 10:18

Kiti klausimai apie žymes arba Užduoti klausimą