Nə ": - !!" C kodunda?

/Usr/include/linux/kernel.h saytında bu qəribə makrokodla tanış oldum :

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

Nədir :-!! ?

1470
10 февр. şurlu 10 fevral təyin etdi 2012-02-10 17:50 '12 at 5:50 pm 2012-02-10 17:50
@ 6 cavablar

Bu, əslində, e ifadəsinin 0 kimi qiymətləndirilə biləcəyini yoxlamaq üçün bir üsuldur, əgər olmasa, qurmaqdan imtina etməkdir .

Makroya bir qədər adı verilmir; BUILD_BUG_OR_ZERO kimi bir şey BUILD_BUG_OR_ZERO , BUILD_BUG_OR_ZERO deyil. ( Bu, çaşqın bir ad olub-olmaması ilə bağlı ara sıra müzakirələr aparıldı .)

Aşağıdakı ifadəni oxumaq lazımdır:

 sizeof(struct { int: -!!(e); })) 
  • (e) : ifadəsini qiymətləndirmək e .

  • !!(e) : məntiqi olaraq iki dəfə inkar edir: 0 e == 0 ; əksinə 1 .

  • -!!(e) : ifadə 2: 0 dan nümunəvi sayılmır, əgər 0 olarsa; əksinə -1 .

  • struct{int: -!!(0);} --> struct{int: 0;} : sıfır olsaydı, sıfır genişlikli anonim bit sahəsi olan bir quruluşu elan edirik. Hər şey normaldır və normal davranırıq.

  • struct{int: -!!(1);} --> struct{int: -1;} : Bir tərəfdən, əgər sıfır deyilsə, o zaman bəzi mənfi rəqəmlər olacaqdır. Negativ eni ilə hər hansı bitfildən bəyan etmək bir tərtibat səhvidir.

Beləliklə, biz ya quruluşda 0 geniş olan bir bitfield ilə bitiririk və ya tərsi bir səhv olan mənfi bir eni olan bir bitfield. Sonra bu sahənin size_t , beləliklə müvafiq eni ilə size_t malik size_t ( e sıfır olduğu halda sıfır olacaq).


Bəzi insanlar soruşdular: Niyə yalnız assert istifadə etmirsiniz?

keithmo burada cavab yaxşı cavab:

Bu makrolar derleme zamanı testini həyata keçirir və assert () bir run-time testidir.

Həqiqətən. Daha əvvəl aşkarlana biləcək iş vaxtında çekirdekinizdeki problemləri aşkar etmək istəmirsiniz! Bu əməliyyat sisteminin kritik bir hissəsidir. Hər halda problemlər tərtib vaxtında daha yaxşı olar.

1524
10 февр. Cavab 10 fevralda John Feminella tərəfindən verilmişdir. 2012-02-10 18:04 '12 at 18:04 2012-02-10 18:04

: - bit. İstəyirəm !! , yəni bir mantıksal ikiqat mənfi və bu səbəbdən 0 yalan və ya 1 gerçəklik üçün qaytarır. Və - bir mənfi işarə, yəni. Aritmetik inkar.

Bu, yalnış girişləri bloklamaq üçün kompilyatoru almaq üçün yalnız bir oyun.

BUILD_BUG_ON_ZERO düşünün. Zaman -!!(e) mənfi bir dəyəri qiymətləndirir, bu nəticə bir tərtibat səhvidir. Əks halda -!!(e) 0 olaraq qiymətləndirilir və eni 0 olan bit sahəsi 0 ölçüsünə malikdir. Buna görə də makro, size_t 0 olan 0 ilə qiymətləndirilir.

border=0

Mənim fikrimcə, ad zəifdir, çünki giriş sıfır olmadıqda yığıncaq həqiqətən icra edilmir.

BUILD_BUG_ON_NULL çox oxşardır, ancaq int deyil, göstərici verir.

234
10 февр. David Heffernan tərəfindən cavab 10 fevral 2012-02-10 17:54 '12 at 17:54 2012-02-10 17:54

Bəzi insanlar bu makroları assert() ilə qarışdırırlar.

Bu makrolar derleme zamanı testini həyata keçirir və assert() bir run-time testidir.

148
10 февр. Cavab keithmo 10 fevral. 2012-02-10 18:37 '12 at 18:37 2012-02-10 18:37

Bəli, bu sözdizimə alternativlərin qeyd edilmədiyi çox təəccübləndim. Başqa bir ümumi (amma köhnə) mexanizm müəyyən edilməyən funksiya çağırışıdır və bəyanatınız düzgün olduğunda bir funksiya çağırışını tərtib etmək üçün optimizerə əsaslanır.

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

Bu mexanizm işləyərkən (optimallaşdırma effektiv olduğu müddətcə), əlaqələndirilməyənə qədər bir səhv bildirməyin dezavantajı var və bu zamanda you_did_something_bad () funksiyasının tərifini tapa bilmədi. Buna görə, çekirdek geliştiricileri, negatif boyutlu ve az sayıdaki (daha sonra KİK 4.4'te montaj sonu durdurulmuş) dizilerle bit genişlikleri kullanmaya başlar.

GCC 4.3 kompilyasiya hesabatlarına ehtiyac duyduqda, bu köhnə konsepsiyanı genişləndirməyə imkan verən error funksiyasını təqdim etdi, lakin seçdiyiniz bir mesajla kompilyasiya zamanı səhvini yaratdı - heç bir kritik "mənfi ölçülü" səhv mesajı!

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

Əslində, Linux 3.9 ilə başlayaraq, indi bu funksiyanı və bug.h ən çox bug.h istifadə edən compiletime_assert adlı bir compiletime_assert . Ancaq bu makro, başlatıcı olaraq istifadə edilə bilməz. Ancaq ifadə ifadələrini (başqa bir C-GCC uzadılması) istifadə edə bilərsiniz!

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

Bu makro parametrini bir dəfə (yan təsirləri olması halında) qiymətləndirəcək və kompilyasiya zamanı səhvini yaradır: "Mənə beş verməyinizi söylədim". ifadəsi beşə baxılırsa və ya kompilyasiya zamanı sabit deyilsə.

Niyə biz bu mənfi olmayan ölçülü bit sahələri yerinə istifadə etmirik? Əfsuslar olsun ki, hazırda operator ifadələrinin istifadəsi ilə bağlı çoxlu məhdudiyyətlər var, bunlar daimi başlanğıc kimi istifadə olunur (sayılan növün sabitləri, bit sahəsinin eni və s.), Operatorun ifadə tamamilə özünü sabit __builtin_constant_p() müddətində tamamilə qiymətləndirilə və başqa __builtin_constant_p() testindən __builtin_constant_p() . Bundan əlavə, funksiya orqanından kənarda istifadə edilə bilməz.

Ümid edirəm ki, GCC bu qüsurları tezliklə dəyişəcək və daimi ifadələrin sabit başlanğıc kimi istifadə edilməsini təmin edəcəkdir. Buradakı vəzifə, qanuni sabit ifadənin nə olduğunu müəyyən edən bir dil xüsusiyyətidir. C + + 11 bu tip və ya şey üçün constexpr sözünü əlavə etdi, lakin C11-də heç bir ekvivalent yoxdur. C11 bu problemin bir hissəsini həll edən statik bəyanatlar alsa da, bütün bu çatışmazlıqları həll etməyəcəklər. Buna görə də, ümid edirəm ki, gcc, constexpr -std = gnuc99 və -std = gnuc11 və ya bəzilərindən bir uzantı kimi təqdim edə bilər və istifadə üçün ifadələrdə istifadə etməyə imkan verir. və digərləri

42
27 июня '13 в 11:21 2013-06-27 11:21 Cavab Daniel Santos tərəfindən 27 İyun 2013 tarixində saat 11: 21-də verilir. 2013-06-27 11:21

Vəziyyət səhvdirsə, 0 ölçüsü bir az sahə yaradır, lakin şərait doğru / sıfır olmayan halda, bir ölçülü -1 ölçüsü ( -!!1 ). Birinci halda, heç bir səhv yoxdur və struktur bir int üzvü istifadə edərək başlatılır. Sonuncu halda yığılma səhvi meydana gəlir (və, əlbəttə, belə bir -1 bit sahə yaradılmır).

31
10 февр. Cavab Matt Phillips tərəfindən verilir 10 fevral. 2012-02-10 17:54 '12 at 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 Cavab 21 iyun 18:18 tarixində saat 10: 18-də 2018-06-21 10:18 tarixində verilir

etiketlə bağlı suallar və ya bir sual