Как я могу использовать "sizeof" в макросе препроцессора?

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

Например, в течение многих лет, когда я хотел сделать что-то вроде:

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

1. С++ 11 way

Начиная с С++ 11 вы можете использовать static_assert.

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

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_ASSERT (требуется #include <windows.h>), в котором используется трюк, подобный тому, который описан в разделе 2.

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

C_ASSERT(sizeof(someThing) == PAGE_SIZE);

Ответ 3

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

Мое решение:

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

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

Не так гибко, как макрос assert, но я не мог получить это для компиляции в моей версии GCC, и это будет... и я думаю, что он скомпилируется где угодно.

Ответ 4

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

/* 
 * 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

Ответ 5

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

Поскольку они являются 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];

Ответ 6

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

Ответ 7

Существующие ответы просто показывают, как добиться эффекта "утверждений времени компиляции" в зависимости от размера типа. Это может удовлетворить потребности 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); }

Скомпилируйте это. Напишите script на вашем любимом языке сценариев, который запускает вышеуказанную программу на 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 или другую сборку script, которая заставит ее запустить выше script для сборки sizes.h.

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

Готово!

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

Ответ 8

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

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

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

Ответ 9

В моем портативном коде на С++ (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];
};

Ответ 10

Оператор sizeof недоступен для препроцессора, но вы можете передать sizeof в компилятор и проверить условие во время выполнения:

#define elem_t double

#define compiler_size(x) sizeof(x)

elem_t n;
if (compiler_size(elem_t) == sizeof(int)) {
    printf("%d",(int)n);
} else {
    printf("%lf",(double)n);
}