Что такое ":-!!- на языке Си?



я наткнулся на этот странный макрокод в / 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); }))


Что значит :-!! сделать?

662   6  

6 ответов:

это, по сути, способ проверить, может ли выражение e быть оценено как 0, а если нет, то не выполнить сборку.

макрос несколько неправильно назван; это должно быть что-то вроде BUILD_BUG_OR_ZERO, а не ...ON_ZERO. (Там были случайные дискуссии о том, является ли это название.)

вы должны прочитать выражение так:

sizeof(struct { int: -!!(e); }))
  1. (e): Вычислить выражение e.

  2. !!(e): логически отрицать дважды:0 если e == 0, иначе 1.

  3. -!!(e): численно отрицать выражение из шага 2:0 если бы это было 0, иначе -1.

  4. struct{int: -!!(0);} --> struct{int: 0;}: если он был равен нулю, то мы объявляем структуру с анонимным целочисленным битовым полем, которое имеет нулевую ширину. Все в порядке, и мы переходим в нормальный.

  5. 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. В последнем случае возникает ошибка компиляции (и нет такого понятия, как размер -1 bitfield создается, конечно).

 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

    Ничего не найдено.