如何在预处理器宏中使用"sizeof"?

Bra*_*rad 85 c gcc

有没有办法sizeof在预处理器宏中使用?

例如,多年来我一直有很多情况需要做以下事情:

#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif
Run Code Online (Sandbox Code Playgroud)

我在这里检查的确切内容是完全弥补的 - 重点是,我经常喜欢在这些类型的(大小或对齐)编译时检查,以防止某人修改数据结构可能会错位或重新大小会破坏它们的东西.

不用说 - 我似乎无法以sizeof上述方式使用a .

Jam*_*lis 69

无论如何sizeof在预处理器宏中使用" "?

不可以.条件指令采用一组有限的条件表达式; sizeof是不允许的事情之一.

在解析源之前(至少在概念上)评估预处理指令,因此还没有任何类型或变量可以获得它们的大小.

但是,有一些技术可以在C中获得编译时断言(例如,请参阅本页).

  • @Brad BUILD_BUG_ON和其他生成肯定不正确的代码将无法编译(并在处理过程中给出一些非明显的错误消息).不是#if语句,所以你不能在此基础上排除代码块. (2认同)

nev*_*ind 59

有几种方法可以做到这一点.如果sizeof(someThing)等于PAGE_SIZE,则以下代码段将不会生成代码; 否则会产生编译时错误.

1. C11方式

从C11开始,您可以使用static_assert(要求#include <assert.h>).

用法:

static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");
Run Code Online (Sandbox Code Playgroud)

2.自定义宏

如果您只是想在sizeof(something)不符合预期的情况下获得编译时错误,可以使用以下宏:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
Run Code Online (Sandbox Code Playgroud)

用法:

BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );
Run Code Online (Sandbox Code Playgroud)

本文详细解释了它的工作原理.

3.特定于MS

在Microsoft C++编译器上,您可以使用C_ASSERT宏(需要#include <windows.h>),它使用类似于第2节中描述的技巧.

用法:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);
Run Code Online (Sandbox Code Playgroud)

  • 那太疯狂了 为什么这不是公认的答案,@ Brad(OP)? (4认同)
  • 该宏在 GNU `gcc`(在 4.8.4 版测试)(Linux)中不起作用。在`((void)sizeof(...`) 处,它会出现“预期标识符”或“(”之前的“void”和“预期的”)”和“sizeof”之前的错误。但原则上,“size_t x = (sizeof( ...` 而是按预期工作。您必须以某种方式“使用”结果。要允许在函数内或在全局范围内*多次*调用此结果,例如`extern char _BUILD_BUG_ON_ [(sizeof (...) ];` 可以重复使用(没有副作用,实际上不要在任何地方引用 `_BUILD_BUG_ON_`)。 (3认同)

Sco*_*ott 9

我知道这个帖子真的很旧但是......

我的解决方案

extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];
Run Code Online (Sandbox Code Playgroud)

只要该表达式等于零,它就可以很好地编译.还有别的东西,就在那里爆炸了.因为变量是extern'd,它将不占用任何空间,并且只要没有人引用它(它们不会)它就不会导致链接错误.

不像assert宏那么灵活,但是我无法在我的GCC版本中编译,这将...而且我认为它将在任何地方编译.

  • *从不*从两个下划线开始发明自己的宏.这条路是疯狂的(又名*未定义的行为*). (6认同)

小智 7

我知道这是一个迟到的答案,但是要添加到Mike的版本,这是我们使用的不分配任何内存的版本.我没有拿出原始尺寸检查,我几年前在互联网上找到它,不幸的是无法引用作者.另外两个只是同一个想法的扩展.

因为它们是typedef,所以没有分配.使用名称中的__LINE__,它始终是一个不同的名称,因此可以在需要时复制和粘贴它.这适用于MS Visual Studio C编译器和GCC Arm编译器.它在CodeWarrior中不起作用,CW抱怨重新定义,而不是使用__LINE__预处理器结构.

//Check overall structure size
typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1];

//check 8 byte alignment for flash write or similar
typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1];

//check offset in structure to ensure a piece didn't move
typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];
Run Code Online (Sandbox Code Playgroud)

  • 由于零分配,这应该是正确答案。更好的是定义:`#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];` (2认同)

Ale*_*x D 5

现有的答案只是展示了如何根据类型的大小实现“编译时断言”的效果。在这种特殊情况下,这可能满足 OP 的需求,但在其他情况下,您确实需要一个基于类型大小的预处理器。这是如何做到的:

为自己编写一个小 C 程序,例如:

/* you could call this sizeof_int.c if you like... */
#include <stdio.h>
/* 'int' is just an example, it could be any other type */
int main(void) { printf("%zd", sizeof(int); }
Run Code Online (Sandbox Code Playgroud)

编译那个。用您最喜欢的脚本语言编写一个脚本,该脚本运行上述 C 程序并捕获其输出。使用该输出生成 C 头文件。例如,如果您使用的是 Ruby,它可能如下所示:

sizeof_int = `./sizeof_int`
File.open('include/sizes.h','w') { |f| f.write(<<HEADER) }
/* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */
#define SIZEOF_INT #{sizeof_int}
/* others can go here... */
HEADER
Run Code Online (Sandbox Code Playgroud)

然后将规则添加到您的 Makefile 或其他构建脚本中,这将使其运行上述脚本以构建sizes.h.

包括sizes.h您需要根据大小使用预处理器条件的任何地方。

完毕!

(你有没有打字./configure && make来构建程序?configure脚本所做的基本上就像上面一样......)