Что такое ":-!!- на языке Си?
я наткнулся на этот странный макрокод в / 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); }))
Что значит :-!! сделать?
6 ответов:
это, по сути, способ проверить, может ли выражение e быть оценено как 0, а если нет, то не выполнить сборку.
макрос несколько неправильно назван; это должно быть что-то вроде
BUILD_BUG_OR_ZERO, а не...ON_ZERO. (Там были случайные дискуссии о том, является ли это название.)вы должны прочитать выражение так:
sizeof(struct { int: -!!(e); }))
(e): Вычислить выражениеe.
!!(e): логически отрицать дважды:0еслиe == 0, иначе1.
-!!(e): численно отрицать выражение из шага 2:0если бы это было0, иначе-1.
struct{int: -!!(0);} --> struct{int: 0;}: если он был равен нулю, то мы объявляем структуру с анонимным целочисленным битовым полем, которое имеет нулевую ширину. Все в порядке, и мы переходим в нормальный.
struct{int: -!!(1);} --> struct{int: -1;}: С другой стороны, если это не ноль, то это будет какое-то отрицательное число. Объявление любого битового поля с помощью отрицательный ширина-это ошибка компиляции.таким образом, мы либо получим битовое поле с шириной 0 в структуре, что нормально, либо битовое поле с отрицательной шириной, что является ошибкой компиляции. Тогда берем
sizeofэто поле, так что мы получаемsize_tс соответствующим ширина (которая будет равна нулю в случае, когдаeравна нулю).
некоторые люди спрашивают: почему бы просто не использовать
assert?keithmo это здесь есть хороший ответ:
эти макросы реализуют тест времени компиляции, в то время как assert() является тестом времени выполнения.
совершенно верно. Вы не хотите, чтобы обнаружить проблемы в вашем ядро во время выполнения это могло быть поймали раньше! Это важная часть операционной системы. В какой бы степени проблемы не были обнаружены во время компиляции, тем лучше.
The
:- Это битовое поле. Что касается!!, что составляет логические двойное отрицание и так возвращает0за ложные или1для настоящих. А то-- это знак "минус", т. е. арифметическое отрицание.это всего лишь трюк, чтобы заставить компилятор блевать на недопустимые входы.
считают
BUILD_BUG_ON_ZERO. Когда-!!(e)вычисляет отрицательное значение, что приводит к ошибке компиляции. В противном случае-!!(e)вычисляется как 0, а битовое поле с шириной 0 имеет размер 0. И, следовательно, макрос оценивается вsize_tсо значением 0.имя слабое на мой взгляд, потому что сборка на самом деле терпит неудачу, когда вход не ноль.
BUILD_BUG_ON_NULLочень похоже, но дает указатель, а неint.
некоторые люди, кажется, путают эти макросы с
assert().эти макросы реализуют тест времени компиляции, в то время как
assert()тест время выполнения.
Ну, я очень удивлен, что альтернативы этому синтаксису не были упомянуты. Другим распространенным (но более старым) механизмом является вызов функции, которая не определена, и полагается на оптимизатор для компиляции вызова функции, если ваше утверждение правильно.
#define MY_COMPILETIME_ASSERT(test) \ do { \ extern void you_did_something_bad(void); \ if (!(test)) \ you_did_something_bad(void); \ } while (0)в то время как этот механизм работает (до тех пор, пока оптимизация включена), он имеет обратную сторону, не сообщая об ошибке, пока вы не свяжетесь, и в это время он не сможет найти определение для функции you_did_something_bad (). Вот почему разработчики ядра начинают использовать такие трюки, как ширина битового поля отрицательного размера и массивы отрицательного размера (более поздние из которых перестали ломать сборки в GCC 4.4).
в знак сочувствия к необходимости утверждения времени компиляции, GCC 4.3 ввел the
errorсвойства функция это позволяет вам расширить эту старую концепцию, но генерировать ошибку времени компиляции с сообщением по вашему выбору - нет более загадочного " отрицательного размер массива" сообщения об ошибках!#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)фактически, начиная с Linux 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; \ })этот макрос будет оценить свой параметр ровно один раз (в случае он имеет побочные эффекты) и создает ошибку времени компиляции, которая говорит: "Я говорил вам не давать мне пять!"если выражение имеет значение пять или не является константой времени компиляции.
так почему же мы не используем это вместо битовых полей отрицательного размера? Увы, в настоящее время существует много ограничений на использование выражений операторов, в том числе их использование в качестве инициализаторов констант (для констант перечисления, ширины битового поля и т. д.) даже если выражение утверждения полностью постоянно, его самость (т. е. может быть полностью оценены во время компиляции и в противном случае передает
__builtin_constant_p()
он создает размер
0битовое поле, если условие ложно, но в размере-1(-!!1) битовое поле, если условие истинно / ненулевое. В первом случае ошибки нет, и структура инициализируется с помощью члена int. В последнем случае возникает ошибка компиляции (и нет такого понятия, как размер-1bitfield создается, конечно).
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); }))
Comments