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

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

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

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


person altendky    schedule 07.01.2014    source источник
comment
Какая мотивация?   -  person Ed Heal    schedule 07.01.2014
comment
Чтобы узнать размер многоуровневой структуры, не копаясь в файле карты.   -  person altendky    schedule 07.01.2014
comment
Что ж, оператор #warning обрабатывается препроцессором до того, как соответствующий компилятор даже запустился, поэтому я не думаю, что это возможно. Думаю, решением будет написание небольшой тестовой программы, которая вызывается в качестве настраиваемого шага в процессе сборки. Удачи.   -  person user422005    schedule 07.01.2014
comment
Что вы имеете в виду под далеко не ... быстро? Статические утверждения проверяются во время компиляции (и поэтому sizeof оценивается во время компиляции).   -  person mafso    schedule 08.01.2014
comment
И нет ничего лучше звонка sizeof. Это оператор, а не функция.   -  person mafso    schedule 08.01.2014
comment
@ user422005 Я, по крайней мере, согласен с тем, что мне никогда не следовало ожидать, что #warning сработает, поскольку это команда прекомпилятора, а не директива компилятора.   -  person altendky    schedule 08.01.2014
comment
@mafso far from ... fast относится к написанию кучи статических утверждений. Например, MY_STATIC_ASSERT (sizeof (MyStruct) ›100). Если мне нужно выполнить поиск, изменив 100 на 200, затем на 300, затем на 250, затем на 275, тогда ... это далеко не быстрый процесс. Кроме того, я удалил ссылку на вызов sizeof ().   -  person altendky    schedule 08.01.2014
comment
вам нужен точный размер или просто примерная фигура?   -  person Chaim Geretz    schedule 08.01.2014
comment
Если у вас есть компилятор C ++ для вашей цели, вы можете проверить его с помощью stackoverflow.com/questions/2008398/   -  person nos    schedule 08.01.2014
comment
@Chaim Geretz в некоторых случаях точен, но сегодня было бы достаточно.   -  person altendky    schedule 08.01.2014
comment
@nos Возможно, я мог бы добавить что-нибудь в наш стандартный Makefile, чтобы упростить использование подхода C ++. Я могу пойти по этому пути, если Филипе прав, что то, что я хочу, невозможно.   -  person altendky    schedule 08.01.2014
comment
@altendky Другой подход, если вы можете сгенерировать файлы elf с отладочной информацией в них, - это использовать инструмент pahole для конечных исполняемых или объектных файлов. (Или даже с помощью objdump --dwarf)   -  person nos    schedule 08.01.2014
comment
@altendky: вы можете немного ускорить это, используя кучу #if #else MY_STATIC_ASSERTS, чтобы получить приблизительное предупреждение.   -  person Chaim Geretz    schedule 08.01.2014
comment
@nos Я пробовал objdump --dwarf на небольшой структуре, и это показалось неточным. Я должен вернуться, чтобы увидеть, что я сделал не так.   -  person altendky    schedule 08.01.2014
comment
@ChaimGeretz Статическое утверждение работает, typedef 'присваивая типу массива положительный (подтверждение пройдено) или отрицательный (утверждение не удалось) размер. Это ничего не делает с препроцессором, поэтому #if не работает с ним. Тем не менее, что я сделал, так это создал группу утверждений, работающих с #define, которые я мог указать на интересующую структуру.   -  person altendky    schedule 08.01.2014


Ответы (12)


Я искал подобные функции, когда наткнулся на это:

Возможно ли распечатать размер класса 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 немного лучше, может придумать лучшее предупреждение о злоупотреблениях.

person Bakhazard    schedule 08.02.2016
comment
Посетив это еще год спустя, я обнаружил, что указанное выше решение для gcc больше не работает (gcc 4.4.2). После небольшого поиска я обнаружил, что stackoverflow.com/questions/21001044/ (с использованием слишком большого массива с -Wframe-larger-than) все еще работает (вам нужно прокрутить вниз до принятый ответ, так как он почему-то не на высоте ...). - person blackghost; 28.03.2017
comment
Мне повезло с последней версией Clang, но ваша ссылка также сработала вдвойне хорошо. - person Michael Dorgan; 23.06.2017
comment
Мне нравится это решение! В любом случае, может кто-нибудь удалить последнюю кавычку в printf в функции kaboom_print? Это просто дает мне дополнительную ошибку, которая меня не интересует. - person ola1olsson; 04.01.2018
comment
Отличное решение - хотя оно требует компиляции как C ++ с помощью gcc. - person jws; 21.11.2018
comment
Сегодня это действительно сэкономило мне время. Странно только то, что статическое утверждение не выполняется из-за того, что размер не равен X ... Делаем это, чтобы проверить, что компилятор думает о размере ... дайте мне X: P - person inquam; 12.10.2019

Дублирование константы case - это уловка, которая гарантированно работает ВО ВСЕХ КОМПИЛЯТОРАХ C, независимо от того, как каждый из них сообщает об ошибке. Для Visual C ++ это просто:

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

EDITED (3jun2020): для gcc или любых других компиляторов, которые выводят только «повторяющееся значение регистра», я использую этот трюк, чтобы сузить значение:

1) добавьте значение case 1 == 2 (для представления false)

2) методом проб и ошибок сузьте значение, например Я пытаюсь угадать, что sizeof(X)> 16:

#include <stdio.h>
typedef struct _X {
    int a;
    char b[10];
} X;
int main()
{
    printf("Hello World");

    int dummy=0   ;
    switch (dummy) {
    case  1==2:
    case sizeof( X)>16:
    //case 16:
    break;
    }
    return 0;
}

результат:

main.c: In function ‘main’:
main.c:14:5: error: duplicate case value
     case sizeof( X)>16:
     ^~~~
main.c:13:5: error: previously used here
     case  1==2:

так что это ложь, т.е. sizeof (X) ‹= 16.

3) повторить с некоторыми другими разумными ценностями. например попробуйте угадать, что это 16, т.е. sizeof(X)==16. Если он не жалуется на повторяющееся значение case. Тогда выражение верное.

4) при желании добавьте case 16, чтобы проверить это, например.

#include <stdio.h>
typedef struct _X {
    int a;
    char b[10];
} X;
int main()
{
    printf("Hello World");

    int dummy=0   ;
    switch (dummy) {
   // case  1==2:
    case sizeof( X):
    case 16:
    break;
    }
    return 0;
}

результат

main.c: In function ‘main’:
main.c:15:5: error: duplicate case value
     case 16:
     ^~~~
main.c:14:5: error: previously used here
     case sizeof( X):

подтверждающий, что sizeof (X) равен 16.

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

#include <stdio.h>
typedef struct _X {
    int a;
    char b[10];
} X;
int main()
{
    printf("Hello World");

    int dummy=0   ;
    switch (dummy) {
    case  1==2: //represents false
    case 1==1: //represents true
    case sizeof( X)>10:
    case sizeof( X)>12:
    case sizeof( X)>14:
    case sizeof( X)>16:
    case  sizeof( X)==16:
    //case 16:
    break;
    }
    return 0;
}

результат

main.c: In function ‘main’:
main.c:14:5: error: duplicate case value
     case sizeof( X)>10:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~
main.c:15:5: error: duplicate case value
     case sizeof( X)>12:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~
main.c:16:5: error: duplicate case value
     case sizeof( X)>14:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~
main.c:17:5: error: duplicate case value
     case sizeof( X)>16:
     ^~~~
main.c:12:5: error: previously used here
     case  1==2:
     ^~~~
main.c:18:5: error: duplicate case value
     case  sizeof( X)==16:
     ^~~~
main.c:13:5: error: previously used here
     case 1==1:
     ^~~~

предполагая, что sizeof(X)> 10,> 12,> 14, но не> 16. == 16 добавляется в качестве окончательного предположения.

person JavaMan    schedule 09.05.2016
comment
К сожалению, не работает в моей более старой версии gcc 4.2.0, он просто говорит «повторяющееся значение регистра» без вывода значения. - person eresonance; 09.03.2017
comment
некоторые общие методы печати вычисленных значений int во время компиляции: stackoverflow.com/questions/28852574/ - person JavaMan; 08.06.2017
comment
это был единственный, который работал с gcc в c для меня - person Michael; 15.10.2018
comment
отредактирован, чтобы использовать уловку в gcc, которая печатает только повторяющееся значение case без печати фактического значения case. - person JavaMan; 02.06.2020

Следующий способ, который работает в 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]'

person Ruslan    schedule 21.12.2018

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

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

Работает со старым 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]')

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

person Konstantin Stupnik    schedule 13.12.2018
comment
Вам даже не нужно указывать размер массива: char __foo[] = {[sizeof(MyStruct)] = ""}; - person Garogolun; 20.12.2018

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

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

Появляется сообщение об ошибке

Function cannot return array type 'char [8]'

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

person EHYPERCHICKEN    schedule 05.01.2018

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

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

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

person dsptech    schedule 12.03.2019
comment
У меня работал с gcc версии 8.3.0 - person Sneh Shikhar; 23.04.2020

//main.cpp
#include <cstddef>
template <std::size_t x>
struct show_size;

void foo()
{
    show_size<sizeof(my_type)>();//!!please change `my_type` to your expected
}

int main()
{
    return 0;
}

Вы можете скомпилировать этот довольно простой код, и на этапе предварительной компиляции компилятор выдаст ошибку, в которой sizeof(my_type) даст конкретное значение. например.:

g++ main.cpp
person ChrisZZ    schedule 29.07.2020
comment
Пожалуйста, не публикуйте только код в качестве ответа, но также объясните, что делает ваш код и как он решает проблему вопроса. Ответы с объяснением обычно более полезны и качественнее, и с большей вероятностью получат положительные отзывы. - person Dima Kozhevin; 29.07.2020

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

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

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
person user10746164    schedule 04.12.2018

Быстрое и простое решение, которое сработало для меня (GCC):

(char[sizeof(long long)])"bla";

Это приводит к появлению сообщения об ошибке, в котором указан размер long long:

ISO C++ forbids casting to an array type 'char [8]'
person urish    schedule 20.11.2020

Мой компилятор 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 штуки.

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

person jws    schedule 21.11.2018
comment
Gcc жалуется на необработанные случаи в свитчах. Поэтому, если у вас есть некоторая недопустимая запись, например case 1: и нет значения по умолчанию, gcc должен пожаловаться, что case 8 не обработан. - person Atilla Filiz; 09.07.2019

Это универсальное решение для любых компиляторов C.

Я понял, что если наша цель - знать значение sizeof(), а не выводить его значение, то нам просто нужно оценить несколько выражений времени компиляции sizeof(X)>??, чтобы сузить вниз значение.

Уловка состоит в том, чтобы создавать ошибки времени компиляции, когда выражения оцениваются как false (ноль) или true (ненулевое значение).

Многие стандартные конструкции C могут достичь нашей цели. Трюк с дублированием case значений, который я описал отдельно, является одним из них. Другой - проверка деления на ноль в инициализаторе, который компилятор оценивает во время компиляции. Например, чтобы получить размер X:

struct _X {
  int a;
  char c;
  double d;
  float f[30];
} X;

скомпилировать с помощью нескольких строк:

#include <stdio.h>
struct _X {
  int a;
  char c;
  double d;
  float f[30];
} X;
int r2=1/(sizeof(X)<170);
int r3=1/(sizeof(X)<100);
int r4=1/(sizeof(X)<80);
int r5=1/(sizeof(X)<60);
int main()
{
   return 0;
}

результат

main.c:17:9: warning: division by zero [-Wdiv-by-zero]
 int r3=1/(sizeof(X)<100);
         ^
main.c:17:8: error: initializer element is not constant
 int r3=1/(sizeof(X)<100);
        ^
main.c:18:9: warning: division by zero [-Wdiv-by-zero]
 int r4=1/(sizeof(X)<80);
         ^
main.c:18:8: error: initializer element is not constant
 int r4=1/(sizeof(X)<80);
        ^
main.c:19:9: warning: division by zero [-Wdiv-by-zero]
 int r5=1/(sizeof(X)<60);
         ^
main.c:19:8: error: initializer element is not constant
 int r5=1/(sizeof(X)<60);
        ^

подразумевая, что sizeof(X)<170 равно true (ненулевое значение), но sizeof(X)<100 равно false (вызывая деление на ноль во время компиляции). Затем мы можем получить фактическое значение, повторив тест с некоторыми другими значениями. например

#include <stdio.h>
struct _X {
  int a;
  char c;
  double d;
  float f[30];
} X;
int r2=1/(sizeof(X)<140);
int r3=1/(sizeof(X)<137);
int r4=1/(sizeof(X)<136);
int r5=1/(sizeof(X)!=136);

int main()
{
    return 0;
}

результат

main.c:18:9: warning: division by zero [-Wdiv-by-zero]
 int r4=1/(sizeof(X)<136);
         ^
main.c:18:8: error: initializer element is not constant
 int r4=1/(sizeof(X)<136);
        ^
main.c:19:9: warning: division by zero [-Wdiv-by-zero]
 int r5=1/(sizeof(X)!=136);
         ^
main.c:19:8: error: initializer element is not constant
 int r5=1/(sizeof(X)!=136);
        ^

Следовательно, мы знаем sizeof(X)==136.

В качестве альтернативы, используя оператор ?:, мы можем использовать больше конструкций языка C, которые оцениваются во время компиляции. Пример Visual C ++ с использованием объявления массива:

#include "stdafx.h"
struct X {
  int a;
  char b[30];
  double d;
  float f[20];
};
int a1[sizeof(X)<130?-1:1];
int a2[sizeof(X)<120?1:-1];
int a3[sizeof(X)==128?-1:1];

int _tmain(int argc, _TCHAR* argv[]){
  return 0;
}

результат

1>------ Build started: Project: cpptest, Configuration: Release Win32 ------
1>  cpptest.cpp
1>cpptest.cpp(11): error C2118: negative subscript
1>cpptest.cpp(12): error C2118: negative subscript
1>cpptest.cpp(13): error C2118: negative subscript
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

подразумевая, что sizeof(X) составляет ‹130, а не‹ 120, и равно 128

person JavaMan    schedule 05.06.2020

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

Самое близкое, что вы можете иметь, - это полагаться на некоторые макросы, определенные реализацией, которые оценивают размер встроенных типов. В 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

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

person Filipe Gonçalves    schedule 07.01.2014
comment
У меня уже есть статический макрос assert, который успешно запускает ошибку времени компиляции на основе вызова sizeof (MyStruct), поэтому неверно, что программа должна выполняться, чтобы узнать размер настраиваемой структуры. Единственное, чего мне не хватает, так это команды компилятора (как вы указываете, а не прекомпилятора) для печати значения. - person altendky; 08.01.2014
comment
Я не говорил, что программа должна быть выполнена, чтобы знать размер настраиваемой структуры - конечно, компилятор знает это в какой-то момент. Я сказал, что у вас нет возможности попросить компилятор выгрузить его во время компиляции, поэтому ваш единственный выбор - выполнить программу, которая это делает. Хотя вы можете сравнить его с жестко запрограммированными значениями, нет инструкции, которую вы можете дать ему для печати размера. - person Filipe Gonçalves; 08.01.2014
comment
Вы действительно ничего не можете сделать, чтобы узнать размер настраиваемой структуры, не написав программу и не выполнив ее. ??? как ты узнал наверняка? Смотри мой ответ - person JavaMan; 09.05.2016