Я наткнулся на этот странный код макроса в /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); }))
Что делает :-!!
?
c
linux
macros
linux-kernel
chmurli
источник
источник
sizeof
«оценивает» тип, но не значение. Это тип, который является недействительным в этом случае.Ответы:
По сути, это способ проверить, может ли выражение e быть оценено как 0, а если нет, выполнить сборку .
Макрос несколько неправильно назван; это должно быть что-то более похожее
BUILD_BUG_OR_ZERO
, чем...ON_ZERO
. (Были случайные дискуссии о том, является ли это запутанное имя .)Вы должны прочитать выражение так:
(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
это поле, поэтому мы получаем asize_t
с соответствующей шириной (которая будет равна нулю в случае, когдаe
ноль).Некоторые люди спрашивают: почему бы просто не использовать
assert
?Ответ Кейтмо здесь имеет хороший ответ:
Абсолютно верно. Вы не хотите обнаруживать проблемы в вашем ядре во время выполнения, которые могли быть обнаружены ранее! Это важная часть операционной системы. В какой-то степени проблемы могут быть обнаружены во время компиляции, тем лучше.
источник
static_assert
для связанных целей.!
,<
,>
,<=
,>=
,==
,!=
,&&
,||
) всегда дают 0 или 1. Других выражений могут давать результаты , которые могут быть использованы в качестве условий, но являются просто ноль или не ноль; например,isdigit(c)
гдеc
цифра, может дать любое ненулевое значение (которое затем рассматривается как истинное в условии).Это
:
битовое поле. Что касается!!
, это логическое двойное отрицание и поэтому возвращает0
ложь или1
истину. И-
это знак минус, то есть арифметическое отрицание.Это всего лишь хитрость, чтобы заставить компилятор заблокировать неверные входные данные.
Посмотрим
BUILD_BUG_ON_ZERO
. Когда-!!(e)
оценивается как отрицательное значение, это приводит к ошибке компиляции. В противном случае-!!(e)
оценивается как 0, а битовое поле шириной 0 имеет размер 0. И, следовательно, макрос оценивается как asize_t
со значением 0.На мой взгляд, это слабое имя, потому что на самом деле сборка завершается неудачно, когда ввод не равен нулю.
BUILD_BUG_ON_NULL
очень похоже, но дает указатель, а неint
.источник
sizeof(struct { int:0; })
строго в соответствии?0
? Astruct
только с пустым битовым полем, правда, но я не думаю, что структура с размером 0 допускается. Например, если вы создадите массив этого типа, отдельные элементы массива все равно должны иметь разные адреса, нет?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; })
это не так (неопределенное поведение).Некоторые люди, кажется, путают эти макросы с
assert()
.Эти макросы реализуют тест во время компиляции, в то время
assert()
как это тест во время выполнения.источник
Ну, я весьма удивлен, что альтернативы этому синтаксису не были упомянуты. Другим распространенным (но более старым) механизмом является вызов функции, которая не определена, и использование оптимизатора для компиляции вызова функции, если ваше утверждение верно.
Хотя этот механизм работает (при условии, что оптимизация включена), его недостатком является то, что он не сообщает об ошибке до тех пор, пока вы не создадите ссылку, и в этот момент он не может найти определение функции you_did_something_bad (). Вот почему разработчики ядра начали использовать такие приемы, как ширина битового поля отрицательного размера и массивы отрицательного размера (последний из которых прекратил ломать сборки в GCC 4.4).
Сочувствуя необходимости утверждений во время компиляции, GCC 4.3 представил
error
атрибут функции, который позволяет вам расширить эту более старую концепцию, но генерирует ошибку времени компиляции с сообщением по вашему выбору - не более загадочный массив отрицательного размера " Сообщения об ошибках!Фактически, начиная с Linux 3.9, теперь у нас есть макрос,
compiletime_assert
который использует эту функцию, и большинство макросовbug.h
были соответствующим образом обновлены. Тем не менее, этот макрос не может быть использован в качестве инициализатора. Однако, используя выражения выражений (другое C-расширение GCC), вы можете!Этот макрос оценит свой параметр ровно один раз (в случае, если у него есть побочные эффекты) и создаст ошибку во время компиляции, которая говорит: «Я сказал вам не давать мне пять!» если выражение оценивается как пять или не является константой времени компиляции.
Так почему же мы не используем это вместо битовых полей отрицательного размера? Увы, в настоящее время существует много ограничений на использование выражений операторов, в том числе их использование в качестве инициализаторов констант (для констант перечисления, ширины битового поля и т. Д.), Даже если выражение оператора полностью само по себе (т. Е. Может быть полностью оценено). во время компиляции и в противном случае проходит
__builtin_constant_p()
тест). Кроме того, они не могут использоваться вне функционального тела.Надеемся, что GCC вскоре исправит эти недостатки и позволит использовать выражения константных выражений в качестве инициализаторов констант. Проблема здесь заключается в спецификации языка, определяющей, что является законным постоянным выражением. C ++ 11 добавил ключевое слово constexpr только для этого типа или вещи, но в C11 не существует аналога. Хотя C11 получил статические утверждения, которые решат часть этой проблемы, он не решит все эти недостатки. Поэтому я надеюсь, что gcc может сделать функциональность constexpr доступной как расширение через -std = gnuc99 & -std = gnuc11 или что-то подобное и разрешить его использование в выражениях выражений et. и др.
источник
so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted).
" Макрос возвращает выражение типаsize_t
error: bit-field ‘<anonymous>’ width not an integer constant
Это позволяет только константы. Итак, какая польза?Он создает
0
битовое поле размера, если условие ложно, но битовое поле размера-1
(-!!1
), если условие истинно / не равно нулю. В первом случае ошибки нет, и структура инициализируется с помощью члена int. В последнем случае возникает ошибка компиляции (и,-1
конечно, не создается такая вещь, как битовое поле размера ).источник
size_t
значение 0, если условие истинно.