Как я могу использовать "sizeof" в макросе препроцессора? - PullRequest
81 голосов
/ 02 ноября 2010

Есть ли способ использовать sizeof в макросе препроцессора?

Например, за эти годы было множество ситуаций, в которых я хотел сделать что-то вроде:

#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif

Точная вещь, которую я проверяю здесь, полностью составлена ​​- суть в том, что мне часто нравится вставлять эти типы (размер или выравнивание) проверок во время компиляции, чтобы защититься от того, кто-то изменяет структуру данныхможет привести к неправильному выравниванию или изменению размера вещей, которые могут их сломать.

Само собой разумеется, я не могу использовать sizeof, как описано выше.

Ответы [ 11 ]

67 голосов
/ 02 ноября 2010

Можно ли в любом случае использовать "sizeof" в макросе препроцессора?

Нет. Условные директивы принимают ограниченный набор условных выражений; sizeof - это одна из вещей, которая не допускается.

Директивы предварительной обработки оцениваются до синтаксического анализа источника (по крайней мере, концептуально), поэтому пока нет никаких типов или переменных, чтобы получить их размер.

Однако существуют методы получения утверждений времени компиляции в C (например, см. эту страницу ).

54 голосов
/ 29 августа 2013

Есть несколько способов сделать это. Следующие фрагменты не приведут к коду, если sizeof(someThing) равно PAGE_SIZE; в противном случае они приведут к ошибке времени компиляции.

1. C11 way

Начиная с C11, вы можете использовать static_assert (требуется #include <assert.h>).

Использование:

static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");

2. Пользовательский макрос

Если вы просто хотите получить ошибку во время компиляции, когда sizeof(something) не соответствует ожидаемому, вы можете использовать следующий макрос:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

Использование:

BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );

В этой статье подробно объясняется, почему она работает.

3. МС-специфический

В компиляторе Microsoft C ++ вы можете использовать макрос C_ASSERT (требуется #include <windows.h>), который использует прием, аналогичный описанному в разделе 2.

Использование:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);
9 голосов
/ 29 октября 2014

Я знаю, что этот поток действительно старый, но ...

Мое решение:

extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];

Пока это выражение равно нулю, оно прекрасно компилируется.Что-нибудь еще, и это взрывается прямо там.Поскольку переменная extern'd, она не будет занимать пробела, и до тех пор, пока никто не ссылается на нее (чего они не будут), она не вызовет ошибку ссылки.макрос assert, но я не смог заставить его скомпилировать в моей версии GCC, и это будет ... и я думаю, что он скомпилируется где угодно.

7 голосов
/ 23 марта 2017

Я знаю, что это поздний ответ, но, чтобы добавить к версии Майка, вот версия, которую мы используем, которая не выделяет никакой памяти.Я не придумал оригинальную проверку размера, я нашел ее в Интернете много лет назад и, к сожалению, не могу сослаться на автора.Два других являются просто продолжением одной и той же идеи.

Поскольку они являются typedef, ничего не выделяется.С именем __LINE__ это всегда другое имя, поэтому его можно копировать и вставлять в случае необходимости.Это работает в компиляторах MS Visual Studio C и GCC Arm.Он не работает в CodeWarrior, CW жалуется на переопределение, не используя конструкцию препроцессора __LINE__.

//Check overall structure size
typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1];

//check 8 byte alignment for flash write or similar
typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1];

//check offset in structure to ensure a piece didn't move
typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];
4 голосов
/ 14 октября 2013

А как насчет следующего макроса:

/* 
 * Simple compile time assertion.
 * Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes);
 */
#define CT_ASSERT(exp, message_identifier) \
    struct compile_time_assertion { \
        char message_identifier : 8 + !(exp); \
    }

Например, в комментарии MSVC говорит что-то вроде:

test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits
3 голосов
/ 31 августа 2015

Существующие ответы просто показывают, как добиться эффекта «утверждений времени компиляции» в зависимости от размера типа.Это может удовлетворить потребности OP в этом конкретном случае, но есть и другие случаи, когда вам действительно нужен условный препроцессор, основанный на размере типа.Вот как это сделать:

Напишите себе небольшую программу на C, например:

/* you could call this sizeof_int.c if you like... */
#include <stdio.h>
/* 'int' is just an example, it could be any other type */
int main(void) { printf("%zd", sizeof(int); }

Скомпилируйте это.Напишите скрипт на вашем любимом языке сценариев, который запускает вышеуказанную C-программу и записывает ее вывод.Используйте этот вывод для генерации файла заголовка C.Например, если вы использовали Ruby, он может выглядеть следующим образом:

sizeof_int = `./sizeof_int`
File.open('include/sizes.h','w') { |f| f.write(<<HEADER) }
/* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */
#define SIZEOF_INT #{sizeof_int}
/* others can go here... */
HEADER

Затем добавьте правило в ваш Makefile или другой скрипт сборки, который заставит его запустить приведенный выше скрипт для сборки sizes.h.

Включите sizes.h везде, где вам необходимо использовать условные выражения препроцессора, основанные на размерах.

Готово!

(Вы когда-нибудь набирали ./configure && make для создания программы?* Сценарии делают в основном так же, как и выше ...)

1 голос
/ 26 июля 2013

#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x)) может работать

1 голос
/ 26 июля 2013

Так же, как ссылка на это обсуждение, я сообщаю, что некоторые компиляторы получают sizeof () или время препроцессора.

JamesMcNellis ответ верен, но некоторые компиляторы проходят это ограничение (это, вероятно, нарушает строгий ответ).

В связи с этим я ссылаюсь на C-компилятор IAR (вероятно, ведущий для профессионального микроконтроллера / встроенного программирования).

0 голосов
/ 12 февраля 2018

В C11 _Static_assert добавлено ключевое слово.Может использоваться как:

_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")
0 голосов
/ 23 февраля 2015

В моем переносимом коде c ++ (http://www.starmessagesoftware.com/cpcclibrary/) я хотел установить надежную защиту для размеров некоторых из моих структур или классов.

Вместо того, чтобы найти способ, которым препроцессор выдает ошибку (которая не может работать с sizeof (), как указано здесь), я нашел здесь решение, которое заставляет компилятор выдавать ошибку. http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99

Мне пришлось адаптировать этот код, чтобы он выдавал ошибку в моем компиляторе (xcode):

static union
{
    char   int8_t_incorrect[sizeof(  int8_t) == 1 ? 1: -1];
    char  uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1];
    char  int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1];
    char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1];
    char  int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1];
    char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1];
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...