Co to jest ": -!!"w kodzie C?
Natknąłem się na ten dziwny kod makra w /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); }))
Co robi :-!!
?
6 answers
Jest to w efekcie sposób na sprawdzenie, czy wyrażenie e może być ocenione na 0, a jeśli nie, to niepowodzenie kompilacji.
Makro jest nieco błędnie nazwane; powinno być bardziej podobne do BUILD_BUG_OR_ZERO
, a nie do ...ON_ZERO
. (Były sporadyczne dyskusje na temat tego, czy jest to myląca nazwa.)
Powinieneś przeczytać wyrażenie w ten sposób:
sizeof(struct { int: -!!(e); }))
(e)
: Oblicz wyrażeniee
.!!(e)
: Logicznie neguj dwa razy:0
ife == 0
; w przeciwnym razie1
.-!!(e)
: numerycznie neguj wyrażenie z kroku 2:0
jeśli było0
; w przeciwnym razie-1
.struct{int: -!!(0);} --> struct{int: 0;}
: jeśli jest to zero, to deklarujemy strukturę z anonimową liczbą całkowitą bitfield, która ma szerokość zero. Wszystko jest w porządku i postępujemy normalnie.struct{int: -!!(1);} --> struct{int: -1;}
: z drugiej strony, Jeśli nie jest zero, to będzie to jakaś liczba ujemna. Deklarowanie dowolnego bitfielda z negative width jest błędem kompilacji.
Więc albo skończymy z bitfieldem o szerokości 0 w strukturze, co jest w porządku, albo z bitfieldem o ujemnej szerokości, co jest błędem kompilacji. Następnie bierzemy sizeof
to pole, więc otrzymujemy size_t
o odpowiedniej szerokości (która będzie równa zero w przypadku, gdy e
jest równa zero).
Niektórzy pytali: dlaczego po prostu nie użyć assert
?
Odpowiedź Keithmo tutaj ma dobra odpowiedź:
Dokładnie tak. Nie chcesz wykrywać problemów w jądrze podczas pracy, które mogły zostać wcześniej wykryte! To kluczowy element systemu operacyjnego. W jakim stopniu problemy można wykryć w czasie kompilacji, tym lepiej.Te makra implementują test w czasie kompilacji, podczas gdy assert() jest testem w czasie wykonywania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:00
{[0] } jest bitfield. Jeśli chodzi o !!
, to jest to Podwójna negacja logiczna i tak zwraca 0
dla false lub 1
dla true. A -
jest znakiem minus, czyli negacją arytmetyczną.
To tylko sztuczka, aby kompilator zwymiotował na nieprawidłowe dane wejściowe.
Rozważ BUILD_BUG_ON_ZERO
. Gdy -!!(e)
zostanie obliczona na wartość ujemną, spowoduje to błąd kompilacji. W przeciwnym wypadku -!!(e)
oblicza się na 0, a pole bitowe o szerokości 0 ma rozmiar 0. I stąd makro ewaluuje do size_t
z wartość 0.
Nazwa jest słaba moim zdaniem, ponieważ budowanie w rzeczywistości nie powiedzie się, gdy wejście jest , a nie zero.
BUILD_BUG_ON_NULL
jest bardzo podobny, ale daje wskaźnik zamiast int
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45
Niektórzy mylą te makra z assert()
.
Te makra implementują test w czasie kompilacji, podczas gdy {[0] } jest testem uruchomieniowym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-08 22:22:41
Cóż, jestem dość zaskoczony, że alternatywy dla tej składni nie zostały wymienione. Innym powszechnym (ale starszym) mechanizmem jest wywołanie funkcji, która nie jest zdefiniowana i poleganie na optymalizatorze, aby skompilować wywołanie funkcji, jeśli Twoje twierdzenie jest poprawne.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Podczas gdy ten mechanizm działa (tak długo, jak optymalizacje są włączone), ma minusem nie zgłaszanie błędu, dopóki nie połączysz się, w tym czasie nie znajdzie definicji funkcji you_did_something_bad (). Dlatego programiści kernela zaczęli używać sztuczek takich jak pola bitowe o ujemnej wielkości i tablice o ujemnej wielkości (z których późniejsze przestały łamać Kompilacje w GCC 4.4).
W sympatii dla potrzeby twierdzeń w czasie kompilacji, GCC 4.3 wprowadził error
atrybut funkcji, który pozwala rozszerzyć tę starszą koncepcję, ale generuje błąd w czasie kompilacji z wybranym przez Ciebie Komunikatem-koniec z tajemniczym błędem "negative sized array" wiadomości!
#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)
W rzeczywistości, od Linuksa 3.9, mamy teraz makro o nazwie compiletime_assert
który wykorzystuje tę funkcję i większość makr w bug.h
zostały odpowiednio zaktualizowane. Mimo to makro nie może być używane jako inicjalizacja. Jednak za pomocą wyrażenia statement (kolejny GCC C-extension), możesz!
#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; \
})
To makro oceni swój parametr dokładnie raz (w przypadku, gdy ma efekty uboczne) i utworzy błąd w czasie kompilacji, który mówi " I mówiłem, żebyś nie dawał mi piątki!"jeśli wyrażenie jest obliczane na pięć lub nie jest stałą czasu kompilacji.
Dlaczego więc nie używamy tego zamiast pól bitowych o ujemnej wielkości? Niestety, obecnie istnieje wiele ograniczeń stosowania wyrażeń instrukcji, w tym ich stosowania jako stałych inicjalizatorów (dla stałych enum, szerokości pola bitowego, itd.) nawet jeśli wyrażenie instrukcji jest całkowicie stałe jego jaźń (tzn. może być w pełni obliczone w czasie kompilacji i w przeciwnym razie przechodzi __builtin_constant_p()
test). Ponadto nie mogą być używane poza ciałem funkcyjnym.
Miejmy nadzieję, że GCC wkrótce poprawi te niedociągnięcia i pozwoli na używanie wyrażeń stałych jako stałych inicjatorów. Wyzwaniem jest tutaj specyfikacja języka określająca, co jest wyrażeniem stałej prawnej. W C++11 dodano słowo kluczowe constexpr tylko dla tego typu lub rzeczy, ale nie ma odpowiednika w C11. Podczas gdy C11 otrzymał statyczne twierdzenia, które rozwiążą część tego problemu, to nie rozwiąże wszystkich tych niedociągnięć. Mam więc nadzieję, że gcc może udostępnić funkcjonalność constexpr jako rozszerzenie poprzez -std = gnuc99 & - std=gnuc11 lub coś takiego i zezwolić na jej użycie na wyrażeniach instrukcji et. al.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-17 22:08:28
Tworzy rozmiar 0
bitfield jeśli warunek jest false, ale Rozmiar -1
(-!!1
) bitfield, jeśli warunek jest true / non-zero. W pierwszym przypadku, nie ma błędu i struktura jest inicjalizowana przez element int. W tym drugim przypadku występuje błąd kompilacji(i oczywiście nie tworzy się czegoś takiego jak rozmiar -1
bitfield).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 14:56:35
Linux Kernel :
/* 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); }))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-24 11:07:40