මොකක්ද ":-!!" සී කේතයෙන්ද?


1673

මම /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); }))

මොකද කරන්නේ :-!!?


71
8c87df4 හි ජෑන් බියුලිච් විසින් මෙම නිශ්චිත ස්ථිතික ප්‍රකාශය හඳුන්වා දුන් බව git blame අපට පවසයි . නිසැකවම ඔහුට එය කිරීමට හොඳ හේතු තිබුණි (කැපවීමේ පණිවිඩය බලන්න).
නික්ලස් බී.

57
Und ලුන්ඩින්: තහවුරු කිරීම () සම්පාදක කාල දෝෂයක් ඇති නොකරයි. ඉහත ඉදිකිරීමේ සමස්ත කරුණ එයයි.
ක්‍රිස් පැසෙජෝ

4
@GreweKokkor බොළඳ නොවන්න, ලිනක්ස් එක් පුද්ගලයෙකුට ඒ සියල්ල හැසිරවිය නොහැකි තරම් විශාලය. ලිනස්ට ඔහුගේ ලුතිනන්වරුන් සිටින අතර ඔවුන් වෙනස්වීම් සහ වැඩිදියුණු කිරීම් පහළ සිට ඉහළට තල්ලු කරන අයද සිටිති. ලිනස් තීරණය කරන්නේ ඔහුට විශේෂාංගයක් අවශ්‍යද නැද්ද යන්න පමණි, නමුත් ඔහු යම් තරමකට සහායකයින් විශ්වාස කරයි. විවෘත මූලාශ්‍ර පරිසරය තුළ බෙදා හරින ලද පද්ධතිය ක්‍රියාත්මක වන ආකාරය ගැන වැඩි විස්තර දැන ගැනීමට ඔබට අවශ්‍ය නම්, යූ ටියුබ් වීඩියෝව පරීක්ෂා කරන්න: youtube.com/watch?v=4XpnKHJAok8 (එය ඉතා රසවත් කතාවකි).
ටොමාස් ප්‍රුසිනා

3
pcpcloud, sizeofවර්ගය "ඇගයීම" කරයි, අගය පමණක් නොවේ. එහි වර්ගය මෙම නඩුවේ වලංගු නොවේ.
වින්ස්ටන් එවර්ට්

4
:-!!සිනහවක්, ඔබට එය පෙනෙන්නේ නැද්ද? :)
ලුයිස් කොලරාඩෝ

Answers:


1702

මෙය ඇත්ත වශයෙන්ම ඊ ප්‍රකාශනය 0 ලෙස තක්සේරු කළ හැකිද යන්න පරීක්ෂා කිරීමේ ක්‍රමයක් වන අතර එසේ නොවුවහොත් ගොඩනැගීම අසාර්ථක වේ.

සාර්ව තරමක් වැරදි ලෙස නම් කර ඇත; එය වඩා වගේ දෙයක් විය යුතු BUILD_BUG_OR_ZEROවඩා, ...ON_ZERO. ( මෙය ව්‍යාකූල නමක් ද යන්න පිළිබඳව ඉඳහිට සාකච්ඡා පවත්වා ඇත .)

ඔබ මේ වගේ ප්‍රකාශනය කියවිය යුතුයි:

sizeof(struct { int: -!!(e); }))
  1. (e): ප්‍රකාශනය ගණනය කරන්න e.

  2. !!(e): තර්කානුකූලව දෙවරක් ප්‍රතික්ෂේප කරන්න: 0if e == 0; නැතිනම් 1.

  3. -!!(e): 2 වන පියවරෙන් ප්‍රකාශනය සංඛ්‍යාත්මකව ප්‍රතික්ෂේප කරන්න: 0එය නම් 0; නැතිනම් -1.

  4. struct{int: -!!(0);} --> struct{int: 0;}: එය ශුන්‍ය නම්, පළල ශුන්‍යයක් ඇති නිර්නාමික පූර්ණ සංඛ්‍යා බිට්ෆීල්ඩ් සහිත ව්‍යුහයක් අපි ප්‍රකාශ කරමු. සෑම දෙයක්ම හොඳයි, අපි සාමාන්‍ය පරිදි ඉදිරියට යමු.

  5. struct{int: -!!(1);} --> struct{int: -1;}: අනෙක් අතට, එය ශුන්‍ය නොවේ නම්, එය යම් negative ණ සංඛ්‍යාවක් වනු ඇත. ඕනෑම bitfield ප්රකාශ සෘණ පළල සංක්ෂිප්තයක් දෝෂයක් ඇත.

එබැවින් අපි එක්කෝ ව්‍යුහයක පළල 0 ඇති බිට්ෆීල්ඩ් එකක් සමඟ සුළං හමන්නෙමු, එය හොඳයි, නැතහොත් negative ණ පළල සහිත බිට්ෆීල්ඩ්, එය සම්පාදක දෝෂයකි. එවිට අපි sizeofඑම ක්ෂේත්‍රය ගනිමු, එබැවින් අපට size_tසුදුසු පළලක් සහිත එකක් ලැබේ (එය ශුන්‍ය වන විට eශුන්‍ය වේ).


සමහර අය ඇසුවේ: ඇයි හුදෙක් භාවිතා නොකරන්නේ assert?

keithmo ගේ පිළිතුරට හොඳ ප්‍රතිචාරයක් ඇත:

මෙම මැක්‍රෝස් සම්පාදක-කාල පරීක්ෂණයක් ක්‍රියාත්මක කරන අතර, තහවුරු කිරීම () යනු ධාවන කාල පරීක්ෂණයකි.

හරියටම හරි. ඔබගේ කර්නලයේ ධාවන වේලාවේදී කලින් හඳුනාගත හැකි ගැටළු හඳුනා ගැනීමට ඔබට අවශ්‍ය නැත ! එය මෙහෙයුම් පද්ධතියේ තීරණාත්මක අංගයකි. සම්පාදනය කරන වේලාවේදී ගැටළු හඳුනාගත හැකි තරමට, වඩා හොඳය.


5
@ වෙස්ටන් විවිධ ස්ථාන ගොඩක්. ඔබම බලන්න!
ජෝන් ෆෙමිනෙලා

167
C ++ හෝ C ප්‍රමිතිවල මෑත ප්‍රභේදයන්ට static_assertඅදාළ අරමුණු සඳහා සමාන යමක් ඇත.
බැසිල් ස්ටාරින්කෙවිච්

54
Und ලුන්ඩින් - # දෝෂයට # පේළි 3 ක් භාවිතා කළ යුතු නම් # නම් / # දෝෂය / # එන්ඩිෆ් වන අතර එය ක්‍රියා කරන්නේ පූර්ව සකසනයට ප්‍රවේශ විය හැකි ඇගයීම් සඳහා පමණි. සම්පාදකයාට ප්‍රවේශ විය හැකි ඕනෑම ඇගයීමක් සඳහා මෙම හැක් ක්‍රියා කරයි.
එඩ් ස්ටෝබ්

237
ලිනක්ස් කර්නලය C ++ භාවිතා නොකරයි, අවම වශයෙන් ලිනස් ජීවතුන් අතර සිටියදීවත් නොවේ.
මාර්ක් රැන්සම්

6
D ඩොල්ඩා 2000: " සී හි බූලියන් ප්‍රකාශන සෑම විටම ශුන්‍යයට හෝ එකකට තක්සේරු කිරීමට අර්ථ දක්වා ඇත " - හරියටම නොවේ. මෙම ක්රියාකරුවන් අස්වැන්න "තර්කානුකූලව වීජ" ප්රතිඵලයක් ( !, <, >, <=, >=, ==, !=, &&, ||) සෑම විටම 0 හෝ 1. වෙනත් ප්රකාශන යටත් වූ කොන්දේසි ලෙස භාවිතා කළ හැකි බව ප්රතිඵල විය හැක, නමුත් හුදෙක් ශුන්ය හෝ non-ශුන්ය ය; නිදසුනක් ලෙස, ඉලක්කම් isdigit(c)කොහේද c, ඕනෑම ශුන්‍ය නොවන අගයක් ලබා දිය හැකිය (එය පසුව කොන්දේසියක් ලෙස සලකනු ලැබේ).
කීත් තොම්සන්

256

මෙම :ඉතා bitfield වේ. සඳහා පරිදි !!, එනම් ද්විත්ව නිශේධනය තාර්කික හා නැවත එසේ 0බොරු හෝ 1සත්ය. හා -ඍණ ලකුණක්, එනම්, අංක ගණිතමය නිශේධනය වේ.

අවලංගු යෙදවුම් මත සම්පාදකයා බාර්ෆ් වෙත ලබා ගැනීම උපක්‍රමයක් පමණි.

සලකා බලන්න BUILD_BUG_ON_ZERO. -!!(e)Negative ණ අගයකට තක්සේරු කරන විට , එය සම්පාදක දෝෂයක් ඇති කරයි. එසේ නොමැතිනම් -!!(e)0 ට තක්සේරු කරන අතර 0 පළල බිට්ෆීල්ඩ් 0 ක විශාලත්වයක් ඇත. එබැවින් සාර්ව size_tඅගය 0 සමඟ අගය කරයි.

මගේ දැක්ම අනුව නම දුර්වල ය, මන්ද ආදානය ශුන්‍ය නොවන විට ගොඩනැගීම අසාර්ථක වේ.

BUILD_BUG_ON_NULLඉතා සමාන ය, නමුත් a ට වඩා දර්ශකයක් ලබා දෙයි int.


15
ඇත sizeof(struct { int:0; })දැඩි අනුකූල?
ouah

8
ප්‍රති result ලය පොදුවේ 0කුමක් විය හැකිද? ඒ structහිස් bitfield පමණක් සැබෑ, නමුත් මම ප්රමාණය 0 struct අවසර ඇති කියලා මං හිතන්නේ නෑ. උදා: ඔබ එම වර්ගයේ පෙළක් නිර්මාණය කරන්නේ නම්, තනි අරාව මූලද්‍රව්‍යයන්ට තවමත් වෙනස් ලිපින තිබිය යුතුය, නැත?
ජෙන්ස් ගුස්ටඩ්

2
ඔවුන් ඇත්ත වශයෙන්ම GNU දිගු භාවිතා කරන විට එය ගණන් ගන්නේ නැත, ඔවුන් දැඩි අන්වර්ථකරණ රීතිය අක්‍රීය කරන අතර පූර්ණ සංඛ්‍යා පිටාර ගැලීම් UB ලෙස නොසලකයි. නමුත් මම කල්පනා කරමින් සිටියේ මෙය සී.
ඕවා

3
නම් නොකළ ශුන්‍ය දිග බිට්ෆීල්ඩ් සම්බන්ධයෙන් @ouah, මෙහි බලන්න: stackoverflow.com/questions/4297095/…
ඩේවිඩ් හෙෆර්නන්

10
Av ඩේවිඩ්හෙෆර්නන් ඇත්ත වශයෙන්ම C නම් නොකල බිට්-ක්ෂේත්‍ර පළලකට ඉඩ දෙයි 0, නමුත් ව්‍යුහයේ නම් කරන ලද වෙනත් සාමාජිකයෙකු නොමැති නම් නොවේ. (C99, 6.7.2.1p2) "If the struct-declaration-list contains no named members, the behavior is undefined."උදාහරණයක් sizeof (struct {int a:1; int:0;})ලෙස දැඩි ලෙස අනුකූල වන නමුත් sizeof(struct { int:0; })එය (නිර්වචනය නොකළ හැසිරීම) නොවේ.
ouah

168

සමහර අය මෙම මැක්‍රෝස් සමඟ ව්‍යාකූල වන බවක් පෙනේ assert().

මෙම සාර්වයන් සම්පාදක-කාල පරීක්ෂණයක් ක්‍රියාත්මක කරන අතර assert()ධාවන කාල පරීක්ෂණයකි.


52

හොඳයි, මෙම වාක්‍ය ඛණ්ඩයට විකල්පයන් සඳහන් නොකිරීම ගැන මම පුදුම වෙමි. තවත් පොදු (නමුත් පැරණි) යාන්ත්‍රණයක් නම්, නිර්වචනය කර නොමැති ශ්‍රිතයක් ඇමතීම සහ ඔබේ ප්‍රකාශය නිවැරදි නම් ශ්‍රිත ඇමතුම සම්පාදනය කිරීම සඳහා ප්‍රශස්තකරණය මත රඳා සිටීමයි.

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

මෙම යාන්ත්‍රණය ක්‍රියාත්මක වන අතර (ප්‍රශස්තිකරණය සක්‍රීය කර ඇති තාක් කල්) ඔබ සම්බන්ධ වන තුරු දෝෂයක් වාර්තා නොකිරීමේ අවාසිය එයට ඇත, එම අවස්ථාවේදී ඔබ_did_something_bad () ශ්‍රිතය සඳහා අර්ථ දැක්වීම සොයා ගැනීමට අපොහොසත් වේ. කර්නල් සංවර්ධකයින් negative ණ ප්‍රමාණයේ බිට්-ක්ෂේත්‍ර පළල සහ negative ණ ප්‍රමාණයේ අරා වැනි උපක්‍රම භාවිතා කිරීමට පටන් ගන්නේ එබැවිනි (පසුකාලීනව GCC 4.4 හි ගොඩනැගීම බිඳ දැමීම නතර විය).

සම්පාදක-කාල ප්‍රකාශයන්ගේ අවශ්‍යතාවයට අනුකම්පා කරමින්, GCC 4.3 මඟින් මෙම පැරණි සංකල්පය පුළුල් කිරීමට ඔබට ඉඩ සලසන errorශ්‍රිත ගුණාංගය හඳුන්වා දුන් නමුත් ඔබ තෝරාගත් පණිවිඩයක් සමඟ සම්පාදක-කාල දෝෂයක් ජනනය කරයි - තවත් ගුප්ත "negative ණ ප්‍රමාණයේ අරාව නැත "දෝෂ පණිවිඩ!

#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)

ඇත්ත වශයෙන්ම, ලිනක්ස් 3.9 වන විට, අපට දැන් compiletime_assertමෙම අංගය භාවිතා කරන මැක්‍රෝ නමින් හැඳින්වෙන අතර එහි ඇති බොහෝ මැක්‍රෝස් bug.hඒ අනුව යාවත්කාලීන කර ඇත. තවමත්, මෙම සාර්ව ආරම්භකය ලෙස භාවිතා කළ නොහැක. කෙසේ වෙතත්, ප්‍රකාශන ප්‍රකාශන මගින් භාවිතා කිරීම (තවත් GCC C- දිගුවක්), ඔබට හැකිය!

#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;                                                    \
    })

මෙම සාර්ව එහි පරාමිතිය හරියටම එක් වරක් ඇගයීමට ලක් කරනු ඇත (එය අතුරු ආබාධ ඇති වුවහොත්) සහ සම්පාදක කාල දෝෂයක් නිර්මාණය කර "මට පහක් දෙන්න එපා යැයි මම ඔබට කීවෙමි!" ප්‍රකාශනය පහකට තක්සේරු කරන්නේ නම් හෝ සම්පාදක කාල නියතය නොවේ නම්.

ඉතින් අපි මෙය negative ණ ප්‍රමාණයේ බිට්-ක්ෂේත්‍ර වෙනුවට භාවිතා නොකරන්නේ ඇයි? අහෝ, ප්‍රකාශන ප්‍රකාශන භාවිතා කිරීම සඳහා දැනට බොහෝ සීමාවන් තිබේ, ඒවා නියත ආරම්භකයින් ලෙස භාවිතා කිරීම ඇතුළුව (එනුම් නියතයන්, බිට්-ක්ෂේත්‍ර පළල යනාදිය) ප්‍රකාශන ප්‍රකාශනය සම්පූර්ණයෙන්ම නියත වුවද එහි ස්වභාවය (එනම්, සම්පූර්ණයෙන්ම ඇගයීමට ලක් කළ හැකිය) සම්පාදනය කරන වේලාවේදී සහ වෙනත් ආකාරයකින් __builtin_constant_p()පරීක්ෂණය සමත් වේ ). තවද, ඒවා ක්‍රියාකාරී ශරීරයකින් පිටත භාවිතා කළ නොහැක.

GCC විසින් මෙම අඩුපාඩු ඉතා ඉක්මණින් සංශෝධනය කරනු ඇති අතර නිරන්තර ප්‍රකාශන ප්‍රකාශන නිරන්තර ආරම්භකයින් ලෙස භාවිතා කිරීමට ඉඩ දෙනු ඇතැයි අපේක්ෂා කරමු. මෙහි ඇති අභියෝගය වන්නේ නෛතික නියත ප්‍රකාශනයක් යනු කුමක්ද යන්න නිර්වචනය කරන භාෂා පිරිවිතරයි. C ++ 11 මෙම වර්ගයට හෝ දෙයක් සඳහා constexpr යතුරු පදය එක් කළ නමුත් C11 හි කිසිදු ප්‍රතිවිරුද්ධ කොටසක් නොමැත. C11 හට ස්ථිතික ප්‍රකාශයන් ලැබුනද, එය මෙම ගැටලුවේ කොටසක් විසඳනු ඇත, නමුත් මෙම අඩුපාඩු සියල්ලම විසඳන්නේ නැත. එබැවින් gcc විසින් -std = gnuc99 & -std = gnuc11 හෝ එවැනි සමහරක් හරහා දිගුවක් ලෙස ලබා ගත හැකි වන අතර ප්‍රකාශන ප්‍රකාශන සහ එහි භාවිතය සඳහා ඉඩ ලබා දේ. අල්.


6
ඔබගේ සියලු විසඳුම් විකල්ප නොවේ. සාර්වයට ඉහළින් ඇති අදහස් ඉතා පැහැදිලිය ” so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted).“ සාර්ව වර්ගය ප්‍රකාශනයක් ලබා දෙයිsize_t
Wiz

3
@ විස් ඔව්, මම මේ ගැන දන්නවා. සමහර විට මෙය ටිකක් වාචික විය හැකි අතර සමහර විට මට මගේ වචන නැවත බැලීමට අවශ්‍ය විය හැකිය, නමුත් මගේ අදහස වූයේ ස්ථිතික ප්‍රකාශ සඳහා විවිධ යාන්ත්‍රණ ගවේෂණය කිරීම සහ අප තවමත් negative ණ ප්‍රමාණයේ බිට්ෆීල්ඩ් භාවිතා කරන්නේ මන්ද යන්න පෙන්වීමයි. කෙටියෙන් කිවහොත්, නිරන්තර ප්‍රකාශන ප්‍රකාශනය සඳහා යාන්ත්‍රණයක් අපට ලැබුනේ නම්, අපට වෙනත් විකල්ප විවෘත වනු ඇත.
ඩැනියෙල් සැන්ටොස්

කෙසේ වෙතත් අපට විචල්‍යයක් සඳහා මෙම සාර්ව භාවිතා කළ නොහැක. හරිද? error: bit-field ‘<anonymous>’ width not an integer constantඑය නියතයන්ට පමණක් ඉඩ දෙයි. ඉතින්, එහි ඇති ප්‍රයෝජනය කුමක්ද?
කාර්තික් රාජ් පලනිචාමි

1
Arth කාර්තික් ලිනක්ස් කර්නලයේ ප්‍රභවයන් සොයන්න එය භාවිතා කරන්නේ ඇයි දැයි බලන්න.
ඩැනියෙල් සැන්ටොස්

up සුපර්කැට් ඔබේ අදහස කිසිසේත්ම සම්බන්ධ වන්නේ කෙසේදැයි මම නොදනිමි. කරුණාකර ඔබට එය සංශෝධනය කිරීමට, ඔබ අදහස් කරන දේ වඩා හොඳින් පැහැදිලි කිරීමට හෝ ඉවත් කිරීමට හැකිද?
ඩැනියෙල් සැන්ටොස්

36

0තත්වය අසත්‍ය නම් එය ප්‍රමාණයේ බිට්ෆීල්ඩ් එකක් නිර්මාණය කරයි , නමුත් තත්වය සත්‍ය නම් / ශුන්‍ය නොවන නම් විශාල -1( -!!1) බිට්ෆීල්ඩ්. කලින් අවස්ථාවෙහිදී, කිසිදු දෝෂයක් නොමැති අතර, int සාමාජිකයෙකු සමඟ struct ආරම්භ කරනු ලැබේ. දෙවැන්න නම්, සම්පාදක දෝෂයක් ඇත ( -1ඇත්ත වශයෙන්ම ප්‍රමාණයේ බිට්ෆීල්ඩ් වැනි දෙයක් නිර්මාණය නොවේ).


3
තත්වය සත්‍ය නම් ඇත්ත වශයෙන්ම එය size_t0 අගය සමඟ ආපසු එයි.
ඩේවිඩ් හෙෆර්නන්
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.