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

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

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

在我的例子中(旧的 GCC) ,#warning sizeof(MyStruct)在打印警告之前并不实际解释 sizeof ()。

39933 次浏览

你不能这么做,不能这么做。预处理器在编译之前被调用,所以甚至没有结构的概念; 您不能计算不存在/没有定义的东西的大小。预处理器 是的对转换单元进行标记化,但这样做只是为了定位宏调用。

最接近的方法是依赖一些实现定义的宏,这些宏计算内置类型的大小。在 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

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

我在到处寻找类似的功能时偶然发现了这个:

有没有可能在编译时打印出 C + + 类的大小?

这给了我这样的想法:

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

这导致 VS2015中出现以下警告:

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

88就是你要找的尺寸。

超级粗俗,但还是有用的。可能晚了几年,但是希望这对某些人有用。

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

编辑: 开箱即用的叮当3.6

我能为 GCC 工作的唯一诀窍就是滥用 -Wformat,让宏定义如下函数:

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

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

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

比最初的建议稍微恶心一点,但是也许更了解 gcc 的人可以想出一个更好的警告滥用。

重复的 case常量是一个技巧,无论每个 C 编译器如何报告错误,它都能保证在所有 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) < = 16。

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被添加为最终猜测。

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

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

这将产生错误消息

Function cannot return array type 'char [8]'

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

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

嗯... 这是个选择。

虽然这不是在编译时,但是它是在运行时之前的 ,所以它仍然可能与一些人有关。

您可以这样定义一个数组:

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

还有一种方法(确实有效) :

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 特有的。所有其他方法对我都不起作用。

下面的方法可以在 GCC、 Clang、 MSVC 以及更多版本中工作,甚至在更老的版本中也可以,它基于函数参数从 指向数组的指针到标量类型的转换失败。编译器会打印数组的大小,所以您可以从输出中获取值。可以在 C 和 C + + 模式下工作。

查找 sizeof(long)(在网上玩)的示例代码:

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

相关产出实例:

  • 海湾合作委员会4.4.7

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

  • 叮当3.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]'

@ jws nice idea!. Howewer, sizeof(xxx) is a constant expression (except VLA, https://en.cppreference.com/w/c/language/sizeof), so the sizeof operator should work even in the case selection:

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

它在我的 GCC 中工作: “ . . WinThreads.c: 18:9: 警告: case 值‘4’不在枚举类型‘ enum e1’[-Wswitch ]中”

这是任何 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)<170true(非零) ,而 sizeof(X)<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)是 < 130,而不是 < 120,并且等于128

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

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

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

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

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

一个更简单的方法是使用 gdb。

例如:

在 test.c 文件中,我声明

typedef struct {
int a;
int b;
char d;
long e[10];
} my_struct_t;

我用 gcc 编译它

gcc test.c -o app.elf -g

我跑步

gdb app.elf

在不运行可执行文件的情况下,您可以执行

gdb app.elf
(gdb) ptype /o my_struct_t


type = struct {
/*    0      |     4 */    int a;
/*    4      |     4 */    int b;
/*    8      |     1 */    char d;
/* XXX  7-byte hole  */
/*   16      |    80 */    long e[10];


/* total size (bytes):   96 */
}

还可以在 gdb 中打印 sizeof 函数的结果

(gdb) p sizeof(my_struct_t)
$1 = 96

由于不需要运行可执行文件,因此。Elf 甚至可以是交叉编译的产物(只要使用工具链的 gdb 或 gdb-multiarch)。