Как я могу напечатать результат sizeof() во время компиляции в C?

Как я могу напечатать результат sizeof() во время компиляции в C?

В настоящее время я использую статическое утверждение (домашнее заваривание на основе других веб-ресурсов), чтобы сравнить результат sizeof() с различными константами. Пока это работает... это далеко не изящно или быстро. Я также могу создать экземпляр переменной/структуры и посмотреть в файле карты, но это также менее элегантно и быстро, чем прямой вызов/команда/оператор. Кроме того, это встроенный проект с использованием нескольких кросс-компиляторов... поэтому создание и загрузка примерной программы в цель, а затем считывание значения еще более затруднительно, чем любой из вышеперечисленных.

В моем случае (старый GCC) #warning sizeof(MyStruct) фактически не интерпретирует sizeof() перед печатью предупреждения.

Ответы

Ответ 1

Я слонялся вокруг в поисках подобной функциональности, когда наткнулся на это:

Можно ли распечатать размер класса C++ во время компиляции?

Что дало мне идею для этого:

char (*__kaboom)[sizeof( YourTypeHere )] = 1;

Что приводит к следующему предупреждению в VS2015:

warning C4047: 'initializing': 'DWORD (*)[88]' differs in levels of indirection from 'int'

где 88 в этом случае будет размер, который вы ищете.

Супер хаки, но это делает свое дело. Возможно, на пару лет поздно, но, надеюсь, это кому-нибудь пригодится.

У меня еще не было возможности попробовать gcc или clang, но я постараюсь подтвердить, работает ли он, если кто-то до меня не добрался.

Изменение: работает из коробки для Clang 3.6

Единственная уловка, которую я мог получить для работы в GCC, это злоупотребление -Wformat и наличие макроса, определяющего функцию, подобную следующей:

void kaboom_print( void )
{
    printf( "%d", __kaboom );
}

Который даст вам предупреждение, как:

...blah blah blah... argument 2 has type 'char (*)[88]'

Чуть более грубый, чем первоначальное предложение, но, возможно, кто-то, кто знает gcc немного лучше, может придумать лучшее предупреждение для злоупотреблений.

Ответ 2

Все, что вам нужно, это хитрость, которая заставляет компилятор жаловаться на то, что некоторые целочисленные значения времени компиляции используются неправильно, например, дублированная константа case:

struct X {
    int a,b;
    int c[10];
};
int _tmain(int argc, _TCHAR* argv[])
{
    int dummy;

    switch (dummy) {
    case sizeof(X):
    case sizeof(X):
        break;
    }
    return 0;
}

Результат компиляции:

 ------ Build started: Project: cpptest, Configuration: Debug Win32 ------
 cpptest.cpp c:\work\cpptest\cpptest\cpptest.cpp(29): error C2196: case value '48' already used
 ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Таким образом, размер структуры X составляет 48

Ответ 3

Еще один способ (который на самом деле работает):

char __foo[sizeof(MyStruct) + 1] = {[sizeof(MyStruct)] = ""};

Работает с old'ish gcc 5.x. Выдает ошибку, подобную этой:

a.c:8:54: error: initializer element is not computable at load time
a.c:8:54: note: (near initialization for 'a[8]')

ps, очевидно, этот (очень) специфичен для gcc. Все остальные методы не работают для меня.

Ответ 4

Следующий способ, который работает в GCC, Clang, MSVC и других, даже в более старых версиях, основан на неудачном преобразовании параметра функции из указателя в массив в скалярный тип. Компиляторы определяют размер массива, так что вы можете получить значение из выходных данных. Работает как в режиме C, так и в режиме C++.

Пример кода, чтобы узнать sizeof(long) (играть с ним онлайн):

char checker(int);
char checkSizeOfInt[sizeof(long)]={checker(&checkSizeOfInt)};

Примеры соответствующих результатов:

  • GCC 4.4.7

<source>:1: note: expected 'int' but argument is of type 'char (*)[8]'

  • лязг 3.0.0

<source>:1:6: note: candidate function not viable: no known conversion from 'char (*)[8]' to 'int' for 1st argument;

  • MSVC 19.14

<source>(2): warning C4047: 'function': 'int' differs in levels of indirection from 'char (*)[4]'

Ответ 5

Я наткнулся на решение, подобное Bakhazard отличному решению, и этот менее подробное предупреждение, поэтому вы можете счесть это полезным:

char (*__fail)(void)[sizeof(uint64_t)] = 1;

Выдает сообщение об ошибке

Function cannot return array type 'char [8]'

Это было протестировано с последней версией clang(1).

Ответ 6

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

enum e
{
    X = sizeof(struct mystruct)
};

void foo()
{
    static enum e ev;

    switch (ev)
    {
    case 0:
    case 4:
    case 8:
    case 12:
    case 16:
    case 20:
        break;
    }
}

Затем я должен просмотреть предупреждения о недостающем числе.

warning: case value '0' not in enumerated type 'e' [-Wswitch]
warning: case value '4' not in enumerated type 'e' [-Wswitch]
warning: case value '12' not in enumerated type 'e' [-Wswitch]
warning: case value '16' not in enumerated type 'e' [-Wswitch]
warning: case value '20' not in enumerated type 'e' [-Wswitch]

Итак, размер моей структуры равен 8.

Моя упаковка 4.

Мех... это вариант.

Ответ 7

Хотя это не совсем во время компиляции, это до запуска, поэтому оно может быть актуальным для некоторых людей.

Вы можете определить такой массив:

uint8_t __some_distinct_name[sizeof(YourTypeHere)];

И затем, после компиляции, получите размер из объектного файла:

$ nm -td -S your_object_file |       # list symbols and their sizes, in decimal
  grep ' __some_distinct_name$' |    # select the right one
  cut -d' ' -f2 |                    # grab the size field
  xargs printf "Your type is %d B\n" # print

Ответ 8

@jws хорошая идея!. Однако sizeof (xxx) является константным выражением (кроме VLA, https://en.cppreference.com/w/c/language/sizeof), поэтому оператор sizeof должен работать даже в случае выбора:

enum e1 {dummy=-1};
enum e1 ev;
switch (ev) {
    case sizeof(myType):;
    break;
    default:;
}

.. он работает в моем GCC: "..\находящихсяreads.c: 18: 9: предупреждение: значение регистра '4' отсутствует в перечисляемом типе 'enum e1' [-Wswitch]"

Ответ 9

Вы не можете сделать это, а не со структурами. Препроцессор вызывается перед компиляцией, поэтому нет даже концепции структуры; вы не можете оценить размер того, что не существует/не определено. Препроцессор делает токенизацию единицы перевода, но он делает это только с целью обнаружения вызова макроса.

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

gcc -dM -E - </dev/null | grep -i size

Что в моей системе напечатано:

#define __SIZE_MAX__ 18446744073709551615UL
#define __SIZEOF_INT__ 4
#define __SIZEOF_POINTER__ 8
#define __SIZEOF_LONG__ 8
#define __SIZEOF_LONG_DOUBLE__ 16
#define __SIZEOF_SIZE_T__ 8
#define __SIZEOF_WINT_T__ 4
#define __SIZE_TYPE__ long unsigned int
#define __SIZEOF_PTRDIFF_T__ 8
#define __SIZEOF_FLOAT__ 4
#define __SIZEOF_SHORT__ 2
#define __SIZEOF_INT128__ 16
#define __SIZEOF_WCHAR_T__ 4
#define __SIZEOF_DOUBLE__ 8
#define __SIZEOF_LONG_LONG__ 8

Нет ничего, что можно было бы узнать, чтобы узнать размер настраиваемой структуры без написания программы и ее выполнения.