【问题标题】:How can I print the result of sizeof() at compile time in C?如何在 C 的编译时打印 sizeof() 的结果?
【发布时间】:2014-01-25 14:29:49
【问题描述】:

如何在 C 语言编译时打印 sizeof() 的结果?

现在我正在使用静态断言(基于其他网络资源的自制)将 sizeof() 结果与各种常量进行比较。虽然这可行……但它远非优雅或快速。我还可以创建变量/结构的实例并查看映射文件,但这也没有直接调用/命令/操作符那么优雅和快速。此外,这是一个使用多个交叉编译器的嵌入式项目......因此构建示例程序并将其加载到目标然后读取一个值比上述任何一个都更加麻烦。

在我的情况下(旧 GCC),#warning sizeof(MyStruct) 在打印警告之前实际上并没有解释 sizeof()。

【问题讨论】:

  • 动机是什么?
  • 无需深入地图文件即可了解多层结构的大小。
  • 好吧 - #warning 语句在正确的编译器启动之前由预处理器处理 - 所以我认为这是不可能的。我想编写一个小型测试程序,在构建过程中作为自定义步骤调用,是一种解决方案。祝你好运。
  • 您所说的“远非...快”是什么意思?静态断言在编译时进行测试(sizeof 在编译时进行评估)。
  • 如果你的目标有一个 C++ 编译器,你可以使用stackoverflow.com/questions/2008398/…检查它

标签: c sizeof compile-time


【解决方案1】:

当我偶然发现这个时,我正在四处寻找类似的功能:

Is it possible to print out the size of a C++ class at compile-time?

这给了我这个想法:

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

这会在 VS2015 中导致以下警告:

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

在这种情况下,88 就是您要查找的尺寸。

超级hacky,但它确实有用。可能晚了几年,但希望这对某人有用。

我还没有机会尝试使用 gcc 或 clang,但如果有人在我之前没有使用它,我会尝试确认它是否有效。

编辑:clang 3.6 开箱即用

我可以为 GCC 工作的唯一技巧是滥用 -Wformat 并让宏定义如下函数:

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

这会给你一个警告,比如:

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

比最初的建议稍微粗暴一些,但也许对 gcc 有一点了解的人可以想到一个更好的滥用警告。

【讨论】:

  • 一年后访问这个,我发现上面的 gcc 解决方案不再有效(gcc 4.4.2)。经过一番搜索,我发现stackoverflow.com/questions/21001044/…(使用一个很大的数组,带有-Wframe-larger-than)仍然有效(你必须向下滚动到接受的答案,因为它出于某种原因不在顶部...... )。
  • 我最近的 Clang 版本很幸运,但您的链接也非常有效。
  • 我喜欢这个解决方案!无论如何,有人可以删除kaboom_print函数中printf中的最后一个引号吗?这只是给了我一个我不感兴趣的额外错误。
  • 很好的解决方案 - 虽然它需要使用 gcc 编译为 C++。
  • 这实际上节省了我今天的一些时间。唯一奇怪的是,静态断言由于大小不是 X 而失败......这样做是为了检查编译器认为大小是什么......给我 X :P
【解决方案2】:

Duplicate 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

已编辑(2020 年 6 月 3 日): 对于 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)

3) 重复一些其他合理的值。例如尝试猜测它是 16,即sizeof(X)==16。如果它不抱怨重复的案例价值。那么表达式为真。

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 作为最终猜测。

【讨论】:

  • 不幸的是在我的旧版本 gcc 4.2.0 中不起作用,它只是说“重复大小写值”而不打印值。
  • 在编译过程中打印计算的 int 值的一些通用方法:stackoverflow.com/questions/28852574/…
  • 这是我唯一在 c 中使用 gcc 的工具
  • 编辑使用 gcc 上的技巧,即只打印“重复大小写值”而不打印实际大小写值。
  • 为什么这不是最佳答案?使用 MSVS C++ 为我工作
【解决方案3】:

以下方法适用于 GCC、Clang、MSVC 等,即使在旧版本中,也是基于将函数参数从 指针到数组 转换为标量类型失败。编译器会打印数组的大小,因此您可以从输出中获取值。适用于 C 和 C++ 模式。

找出sizeof(long)play with it online)的示例代码:

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

相关输出示例:

  • GCC 4.4.7

&lt;source&gt;:1: note: expected 'int' but argument is of type 'char (*)[8]'

  • clang 3.0.0

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

  • MSVC 19.14

&lt;source&gt;(2): warning C4047: 'function': 'int' differs in levels of indirection from 'char (*)[4]'

【讨论】:

    【解决方案4】:

    另一种方法(实际可行):

    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]')
    

    附言显然,这个是(非常)gcc 特定的。所有其他方法都不适合我。

    【讨论】:

    • 您甚至不必指定数组的大小:char __foo[] = {[sizeof(MyStruct)] = ""};
    【解决方案5】:

    对我有用的快速简单的解决方案 (GCC):

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

    这会导致显示long long 大小的错误消息:

    ISO C++ forbids casting to an array type 'char [8]'
    

    【讨论】:

    • 注意,这实际上是在 VS 2019 中为我编译的。但是将“bla”更改为数字(例如 4)有效。
    【解决方案6】:

    我偶然发现了一个类似于Bakhazardgreat solution 的解决方案,这个解决方案产生的警告要少得多,因此您可能会发现它很有用:

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

    这会产生错误消息

    Function cannot return array type 'char [8]'
    

    这是使用最新版本的clang(1) 测试的。

    【讨论】:

      【解决方案7】:

      @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 中工作:“..\WinThreads.c:18:9: 警告:case value '4' not in enumerated type 'enum e1' [-Wswitch]”

      【讨论】:

      • 使用 gcc 8.3.0 版为我工作
      【解决方案8】:
      //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
      

      【讨论】:

      • 请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助、质量更好,并且更有可能吸引投票。
      【解决方案9】:

      虽然这并不完全是在编译时,但它在运行时之前,所以它对某些人来说仍然是相关的。

      你可以像这样定义一个数组:

      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
      

      【讨论】:

        【解决方案10】:

        我的 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 个。

        嗯……这是一个选择。

        【讨论】:

        • Gcc 抱怨交换机中未处理的情况。因此,如果您有一些无效条目,例如 case 1: 并且没有默认值,gcc 应该抱怨 case 8 not processed。
        【解决方案11】:

        这是任何 C 编译器的通用解决方案。

        我已经意识到,如果我们的目标是知道 sizeof() 的值而不是打印它的值,那么我们只需要评估一些编译time sizeof(X)&gt;?? 表达式来缩小值。

        诀窍是当表达式计算为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)&lt;170true(非零)但sizeof(X)&lt;100false(导致在编译时除以零)。然后我们可以通过使用其他一些值重复测试来获得实际值。例如

        #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)

        【讨论】:

          【解决方案12】:

          你不能这样做,而不是结构。预处理器在编译之前被调用,所以甚至没有结构的概念;您无法评估不存在/未定义的事物的大小。预处理器确实标记翻译单元,但它这样做只是为了定位宏调用。

          您可以拥有的最接近的方法是依赖一些实现定义的宏,这些宏评估为内置类型的大小。在 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
          

          如果不编写程序并执行它,你真的无法知道自定义结构的大小。

          【讨论】:

          • 我已经有一个静态断言宏,它基于 sizeof(MyStruct) 调用成功触发编译时错误,因此必须执行程序才能知道自定义结构的大小是错误的。我唯一缺少的是用于打印值的编译器(正如您所指出的,而不是预编译器)命令。
          • 我没有说必须执行程序才能知道自定义结构的大小——当然编译器在某些时候知道。我说的是你没有办法在编译期间要求编译器转储它,所以你唯一的选择是执行一个程序来做到这一点。虽然您可以将其与硬编码值进行比较,但没有任何指令可以让您打印尺寸。
          • “如果不编写程序并执行它,你真的无法知道自定义结构的大小。”???你怎么确定的?看我的回答
          猜你喜欢
          • 2015-05-05
          • 2011-12-17
          • 2011-01-03
          • 2017-03-04
          • 2019-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-08
          相关资源
          最近更新 更多