【问题标题】:How can I use "sizeof" in a preprocessor macro?如何在预处理器宏中使用“sizeof”?
【发布时间】:2011-05-04 00:22:27
【问题描述】:

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

例如,这些年来有很多情况我想做这样的事情:

#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif

我在这里检查的确切内容是完全编造的 - 重点是,我经常喜欢进行这些类型的(大小或对齐)编译时检查,以防止有人修改可能未对齐的数据结构或重新调整会破坏它们的东西的大小。

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

【问题讨论】:

  • 这就是构建系统存在的确切原因。
  • 这就是为什么#error 指令应该始终使用双引号(由于“不”而导致的未终止字符常量)的确切原因。
  • 你好@Brad。请考虑将您接受的答案更改为 nevermind 的答案,因为与此同时,当前接受的答案有点过时了。
  • @BodoThiesen 完成。
  • 看起来整个问题和答案都复制到newbedev.com/how-can-i-use-sizeof-in-a-preprocessor-macro

标签: c gcc


【解决方案1】:

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

1。 C11方式

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

用法:

static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");

2。自定义宏

如果您只是想在sizeof(something) 不是您所期望的情况下得到一个编译时错误,您可以使用以下宏:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

用法:

BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );

This article 详细解释了它的工作原理。

3。特定于 MS 的

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

用法:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);

【讨论】:

  • ...这太疯狂了。为什么这不是公认的答案,@Brad (OP)?
  • 很好地参考了 BUILD_BUG_ON。
  • 宏在 GNU gcc(在 4.8.4 版测试)(Linux)中不起作用。在 ((void)sizeof(... 它与 expected identifier or '(' before 'void'expected ')' before 'sizeof' 出错。但原则上size_t x = (sizeof(... 会按预期工作。你必须以某种方式“使用”结果。为了允许在函数内或全局范围内多次调用,可以重复使用extern char _BUILD_BUG_ON_ [ (sizeof(...) ]; 之类的东西(没有副作用,实际上不要在任何地方引用_BUILD_BUG_ON_)。
  • @Engineer:因为这个问题是在 2010 年提出并回答的,当时它被接受了,而这个答案是在 2013 年发布的。你以后不能“不接受”一个答案(至于至少我的知识),所以,现在更好的答案不是被接受的。
  • @Engineer 看,精神错乱已经停止;)
【解决方案2】:

有没有在预处理器宏中使用“sizeof”?

没有。条件指令采用一组受限的条件表达式; sizeof 是不允许的事情之一。

预处理指令在解析源代码之前进行评估(至少在概念上),因此还没有任何类型或变量来获取它们的大小。

但是,有一些技术可以在 C 中获取编译时断言(例如,请参阅 this page)。

【讨论】:

  • 伟大的文章 - 聪明的解决方案!尽管您必须进行管理-他们确实将 C 语法推到了极限,以使该语法正常工作! :-O
  • 结果 - 正如文章所说 - 我现在正在构建 Linux 内核代码 - 内核中已经有一个定义 - BUILD_BUG_ON - 内核将其用于以下用途:BUILD_BUG_ON(sizeof (char) != 8)
  • @Brad BUILD_BUG_ON 和其他生成肯定不正确的代码,这些代码将无法编译(并在处理过程中给出一些不明显的错误消息)。不是真的#if 语句,所以你不能例如基于此排除代码块。
【解决方案3】:

我知道这是一个迟到的答案,但要添加到 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];

【讨论】:

  • 这对于一个标准的 C 项目实际上非常有效......我喜欢它!
  • 这个应该是正确的答案,因为零分配。更好的定义:#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];
  • p__LINE__ 不会产生唯一的名称。它产生 p__LINE__ 作为变量。您需要一个 preproc 宏并使用 sys/cdefs.h 中的 __CONCAT。
【解决方案4】:

我知道这个帖子真的很老了,但是......

我的解决方案:

extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];

只要该表达式等于零,它就可以正常编译。其他任何东西,它都会在那里爆炸。因为该变量是外部变量,所以它不会占用任何空间,并且只要没有人引用它(他们不会)它就不会导致链接错误。

不如 assert 宏灵活,但我无法在我的 GCC 版本中编译它,这将......而且我认为它几乎可以在任何地方编译。

【讨论】:

  • 从不以两个下划线开头创建自己的宏。这条路是疯狂的(又名未定义的行为)。
  • 用 arm gcc 编译器编译时不起作用。给出预期的错误“错误:在文件范围内可变地修改了'CHECK'”
  • @Jens 你是对的,但这实际上不是宏,而是变量声明。当然,它可能会干扰宏。
【解决方案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); }

编译它。用您最喜欢的脚本语言编写一个脚本,该脚本运行上述 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

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

在需要使用基于大小的预处理器条件的任何地方包括 sizes.h

完成!

(你有没有输入过./configure &amp;&amp; make 来构建程序?configure 脚本的作用基本上和上面一样......)

【讨论】:

  • 当您使用“autoconf”之类的工具时,情况类似。
【解决方案6】:

下一个宏呢:

/* 
 * Simple compile time assertion.
 * Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes);
 */
#define CT_ASSERT(exp, message_identifier) \
    struct compile_time_assertion { \
        char message_identifier : 8 + !(exp); \
    }

例如在评论中 MSVC 告诉类似:

test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits

【讨论】:

  • 这不是问题的答案,因为您不能在 #if 预处理器指令中使用它。
【解决方案7】:

作为本次讨论的参考,我报告说一些编译器会在预处理器时间得到 sizeof()。

JamesMcNellis 的回答是正确的,但是一些编译器会遇到这个限制(这可能违反了严格的 ansi c)。

作为一个例子,我指的是 IAR C 编译器(可能是专业微控制器/嵌入式编程的领先者)。

【讨论】:

  • 你确定吗? IAR claims 他们的编译器符合 ISO C90 和 C99 标准,这些标准不允许在预处理时评估 sizeofsizeof 应该被视为一个标识符。
  • 1998 年,comp.std.c 新闻组中的某个人写道:“在 #if (sizeof(int) == 8) 之类的东西(在某些编译器上)实际工作的日子里,感觉很好。”回应:“一定是在我之前。”,来自丹尼斯·里奇。
  • 抱歉回复晚了...是的,我确定,我有为 8/16/32 位微控制器、瑞萨编译器(R8 和 RX)编译的代码的工作示例。
  • 实际上,应该有一些选项来要求“严格” ISO C
  • 只要标准不禁止,不违反标准。那么我将其称为罕见且非标准的功能 - 因此,为了保持编译器独立性和平台可移植性,您将在常规情况下避免使用它。
【解决方案8】:

#define SIZEOF(x) ((char*)(&amp;(x) + 1) - (char*)&amp;(x)) 可能有用

【讨论】:

  • 这是一个有趣的解决方案,但它仅适用于已定义的变量,不适用于类型。另一个适用于类型但不适用于变量的解决方案是:#define SIZEOF_TYPE(x) (((x*)0) + 1)
  • 它不起作用,因为您仍然不能在 #if 条件中使用它的结果。与sizeof(x) 相比,它没有任何好处。
【解决方案9】:

在 C11 中添加了 _Static_assert 关键字。它可以像这样使用:

_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")

【讨论】:

    【解决方案10】:

    在我的可移植 c++ 代码 (http://www.starmessagesoftware.com/cpcclibrary/) 中,我想对我的某些结构或类的大小进行安全防护。

    我没有找到一种方法让预处理器抛出错误(它不能与 sizeof() 一起工作,正如这里所说的那样),我在这里找到了一个导致编译器抛出错误的解决方案。 http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99

    我必须修改该代码以使其在我的编译器 (xcode) 中引发错误:

    static union
    {
        char   int8_t_incorrect[sizeof(  int8_t) == 1 ? 1: -1];
        char  uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1];
        char  int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1];
        char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1];
        char  int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1];
        char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1];
    };
    

    【讨论】:

    • 您确定那些“-1”永远不会被解释为 0xFFFF...FF,从而导致您的程序请求所有可寻址内存吗?
    【解决方案11】:

    在尝试了上述宏之后,这个片段似乎产生了预期的结果 (t.h):

    #include <sys/cdefs.h>
    #define STATIC_ASSERT(condition) typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
    STATIC_ASSERT(sizeof(int) == 4);
    STATIC_ASSERT(sizeof(int) == 42);
    

    运行cc -E t.h:

    # 1 "t.h"
    ...
    # 2 "t.h" 2
    
    typedef char _static_assert_3[ (sizeof(int) == 4) ? 1 : -1];
    typedef char _static_assert_4[ (sizeof(int) == 42) ? 1 : -1];
    

    运行cc -o t.o t.h:

    % cc -o t.o t.h
    t.h:4:1: error: '_static_assert_4' declared as an array with a negative size
    STATIC_ASSERT(sizeof(int) == 42);
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    t.h:2:84: note: expanded from macro 'STATIC_ASSERT'
      ...typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
                                                           ^~~~~~~~~~~~~~~~~~~~
    1 error generated.
    

    毕竟,42 并不是所有问题的答案......

    【讨论】:

      【解决方案12】:

      为了在编译时检查数据结构的大小是否符合它们的约束,我使用了这个技巧。

      #if defined(__GNUC__)
      { char c1[sizeof(x)-MAX_SIZEOF_X-1]; } // brakets limit c1's scope
      #else
      { char c1[sizeof(x)-MAX_SIZEOF_X]; }   
      #endif
      

      如果 x 的大小大于或等于它的限制 MAX_SIZEOF_X,则 gcc 将抱怨“数组大小太大”错误。 VC++ 将发出错误 C2148(“数组的总大小不得超过 0x7fffffff 字节”)或 C4266“无法分配常量大小为 0 的数组”。

      这两个定义是必需的,因为 gcc 将允许以这种方式定义一个大小为零的数组 (sizeof x - n)。

      【讨论】:

        【解决方案13】:

        sizeof 运算符不适用于预处理器,但您可以将sizeof 传递给编译器并在运行时检查条件:

        #define elem_t double
        
        #define compiler_size(x) sizeof(x)
        
        elem_t n;
        if (compiler_size(elem_t) == sizeof(int)) {
            printf("%d",(int)n);
        } else {
            printf("%lf",(double)n);
        }
        

        【讨论】:

        • 如何改进已经接受的答案?定义compiler_size 的目的是什么?你的例子试图展示什么?
        猜你喜欢
        • 2017-08-09
        • 1970-01-01
        • 1970-01-01
        • 2016-07-30
        • 2016-09-02
        • 2021-12-29
        • 1970-01-01
        • 2014-02-24
        • 2015-10-09
        相关资源
        最近更新 更多