Размер компиляции условного - PullRequest
14 голосов
/ 07 декабря 2010

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

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

Есть несколько страниц (например, http://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/), которые объясняют, как сделать утверждение времени компиляции * 1009).* on sizeof (и не удается скомпилировать, если это не удается), но я не вижу способа расширить этот подход до того, что я хочу.

Ответы [ 9 ]

14 голосов
/ 07 декабря 2010

Ты просто не можешь этого сделать. sizeof - это оператор времени компиляции. #if и #define и связанные с препроцессором. Поскольку препроцессор запускается ДО компилятора, это просто не будет работать. Тем не менее, вы можете найти тайный переключатель компилятора, который позволит вам передавать его несколько раз (т. Е. Предварительную обработку, предварительную обработку, предварительную обработку, компиляцию), но, честно говоря, я бы прекратил попытки делать то, что вы хотите. Он не предназначен для работы и просто не работает.

Лучше всего установить такие определения, как команды -D, передаваемые компилятору. Вы можете статически утверждать, что выбранные верны. Таким образом, вам просто нужно настроить несколько определений внешне для данного режима компиляции (например, PowerPC Release) и т. Д.

10 голосов
/ 07 декабря 2010

Правильное решение вашей проблемы - использовать стандартные заголовки C99:

#include <stdint.h>
#include <inttypes.h>

Вам нужен только один из двух, потому что #include <inttypes.h> включает материал из #include <stdint.h>;тем не менее, большая часть материала в <inttypes.h> относится только к форматированному вводу / выводу с scanf() и printf().

При условии предполагаемого условия:

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

То, к чему вы стремитесь, известно как:

uintptr_t

Это целочисленный тип без знака, который достаточно большой, чтобы содержать любой указатель (то есть любой указатель данных в стандарте C; POSIX).накладывает дополнительное правило, которое также должно быть достаточно большим, чтобы также содержать указатели на функции).Тип uintptr_t определен в <stdint.h>.

Если вы впоследствии собираетесь печатать такие значения или необработанные указатели, вы можете использовать информацию из <inttypes.h>:

printf("Pointer = 0x%" PRIXPTR "\n", uintptr_value);
printf("Pointer = 0x%" PRIXPTR "\n", (uintptr_t)any_pointer);
9 голосов
/ 15 февраля 2012

В этом описывается, как подделывать утверждения времени компиляции в C. Краткая версия заключается в использовании операторов switch:

#define COMPILE_TIME_ASSERT(pred)            \  
    switch(0){case 0:case pred:;}

Если pred оценивается в 0, как ложноелогическое выражение делает в C, компилятор выдаст ошибку.

5 голосов
/ 07 декабря 2010

Предполагая C99, вы можете использовать

#include <limits.h>
#include <stdint.h>

#if UINTPTR_MAX <= UINT_MAX
...

, что подразумевает sizeof (void *) <= sizeof (intptr_t) <= sizeof (int) для любой разумной реализации языка C.

4 голосов
/ 07 декабря 2010

Несмотря на то, что вопрос помечен C, а не C ++, вам может быть полезно знать, что C ++ 0x определяет механизм для статических утверждений, которые проверяются компилятором, а не препроцессором.

Пример Википедии особенно актуален:

static_assert (sizeof(int) <= sizeof(T), "T is not big enough!")
3 голосов
/ 12 февраля 2016

Учитывая, что другие ответы уже объяснили, почему sizeof нельзя использовать с #if, позвольте мне предоставить простое решение для вашего случая (на удивление, пока не упомянутое).Взгляните на

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros.

В нем упоминается несколько предопределенных макросов __SIZEOF_XYZ__, которые фактически могут использоваться на этапе предварительной обработки, то есть также в #if.Предполагая, что unsigned int и int имеют одинаковый размер, ваш пример можно сделать так:

#if __SIZEOF_POINTER__ == __SIZEOF_INT__
#define POINTER_FITS_INTO_UINT
#endif
1 голос
/ 07 декабря 2010

Редактировать

Неважно, как отметил Стив Роу, эти значения препроцессора также устанавливаются на sizeof, поэтому мы только что прошли полный круг.Оценка до времени компиляции, вы должны полагаться на другие значения препроцессора. Вот как бы я это сделал:

#include <values.h>
#if PTRBITS <= INTBITS
#  define POINTER_FITS_INTO_UINT
#endif
0 голосов
/ 12 августа 2015

Одним из способов понять это является концепция модели данных (см., Например, http://sourceforge.net/p/predef/wiki/DataModels/).

Существует несколько моделей данных, включая LP32 ILP32 LP64 LLP64 ILP64 и на большинстве платформ команда cc frontend определяет текущую модель (например, _ILP32 означает int, long и pointer, 32-битные, а _LP64 означает long и указатель 64-битные). *

0 голосов
/ 07 декабря 2010

Вы путаете два шага компиляции здесь. Когда вы компилируете программу на C, первым шагом является препроцессор, который разрешает в том числе макросы, любую строку, начинающуюся с «#». Затем идет компиляция, которая случайно оценивает размер выражений.

Это два разных двоичных файла, и вы не можете передавать такую ​​информацию от одного к другому. Вам придется использовать системные макросы, такие как __i386__ или __x86_64__, если вы хотите выяснить, на какой архитектуре вы работаете, а затем определить размеры int и указателя.

...