【问题标题】:Compile-time sizeof conditional编译时大小的条件
【发布时间】:2011-05-21 10:29:01
【问题描述】:

如果涉及sizeof 的条件为真,我想定义一个宏,如果它为假,则什么都不做(但仍然编译)。如果预处理器支持sizeof,它看起来像这样:

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

有一些页面(例如http://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/)解释了如何在sizeof 上进行编译时断言(如果失败则编译失败),但我没有看到一种将这种方法扩展到我想要的方法的方法。

【问题讨论】:

  • ... 根据指针是否适合 uint,您究竟想做什么不同的事情?不管是什么,这通常是个坏主意....
  • 将指针传递给稍后将回调到库中的不同进程(unsigned int 可以直接传递,而 64 位类型必须作为指针本身传递)。
  • 不,他们没有。您可以以传统方式传递 64 位值 - 即使您编译为 32 位(如果您的指针是 64 位,您可能不是)。
  • 是的,他们在这种情况下这样做:见erlang.org/doc/man/erl_driver.html#driver_output_term中的表格
  • 总是使用intptr_t有什么问题?

标签: c c-preprocessor compile-time


【解决方案1】:

鉴于其他答案已经解释了为什么 sizeof 不能与 #if 一起使用,让我为您的案例提供一个简单的解决方案(令人惊讶的是尚未提及)。来看看

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros.

它提到了几个预定义的__SIZEOF_XYZ__ 宏,实际上可以在预处理阶段使用,即也可以在#if 中使用。假设unsigned intint 大小相同,您的示例可以这样完成:

#if __SIZEOF_POINTER__ == __SIZEOF_INT__
#define POINTER_FITS_INTO_UINT
#endif

【讨论】:

    【解决方案2】:

    理解这一点的一种方法是数据模型的概念(参见例如http://sourceforge.net/p/predef/wiki/DataModels/)。

    有多种数据模型,包括 LP32 ILP32 LP64 LLP64 ILP64,在大多数平台上,cc 前端命令定义当前模型(例如 _ILP32 表示 int、long 和 pointer 是 32 位,而 _LP64 表示 long 和指针是 64 位)。

    【讨论】:

      【解决方案3】:

      This 描述了如何在 C 中伪造编译时断言。简短的版本是使用 switch 语句:

      #define COMPILE_TIME_ASSERT(pred)            \  
          switch(0){case 0:case pred:;}
      

      如果 pred 的计算结果为 0,就像 C 中的假布尔表达式一样,编译器将抛出错误。

      【讨论】:

      • +1。我使用过类似的方法来声明大小为 0 或 -1 的数组(-1 会导致编译错误),但我认为 switch 的使用更简洁。
      【解决方案4】:

      解决问题的正确方法是使用 C99 标准头文件:

      #include <stdint.h>
      #include <inttypes.h>
      

      您只需要两者之一,因为#include &lt;inttypes.h&gt; 包含来自#include &lt;stdint.h&gt; 的材料;但是,&lt;inttypes.h&gt; 中的很多材料仅与带有 scanf()printf() 的格式化 I/O 相关。

      假设条件:

      #if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
      #  define POINTER_FITS_INTO_UINT
      #endif
      

      你似乎追求的是:

      uintptr_t
      

      这是无符号整数类型,它大到足以容纳任何指针(即,C 标准中的任何数据指针;POSIX 强加了一个附加规则,即它也必须大到足以容纳函数指针)。 uintptr_t 类型在 &lt;stdint.h&gt; 中定义。

      如果您随后要打印这些值或原始指针,您可以使用来自&lt;inttypes.h&gt; 的信息:

      printf("Pointer = 0x%" PRIXPTR "\n", uintptr_value);
      printf("Pointer = 0x%" PRIXPTR "\n", (uintptr_t)any_pointer);
      

      【讨论】:

        【解决方案5】:

        即使问题被标记为 C 而不是 C++,您可能会发现知道 C++0x 为静态断言定义了由编译器而不是预处理器检查的机制会很有帮助。

        Wikipedia example 特别相关:

        static_assert (sizeof(int) <= sizeof(T), "T is not big enough!")
        

        【讨论】:

        【解决方案6】:

        假设 C99,你可以使用

        #include <limits.h>
        #include <stdint.h>
        
        #if UINTPTR_MAX <= UINT_MAX
        ...
        

        这意味着 sizeof (void *) &lt;= sizeof (intptr_t) &lt;= sizeof (int) 在 C 语言的任何合理实现上。

        【讨论】:

        • 或者:UINTPTR_MAX == UINT_MAXUINTPTR_MAX == ULONG_MAX
        【解决方案7】:

        您在这里混淆了两个编译步骤。当你编译一个 C 程序时,第一步是预处理器,它解析包括、宏、任何以“#”开头的行。 然后是编译,顺便计算表达式的 sizeof。

        这是两个不同的二进制文件,您不能将这种类型的信息从一个传递到另一个。如果您想确定您所在的架构,然后推断 int 和指针大小,则必须使用系统定义的宏,例如 __i386__ 或 __x86_64__。

        【讨论】:

          【解决方案8】:

          编辑

          没关系,正如 Steve Rowe 指出的那样,这些预处理器值也是由 sizeof 设置的,所以我们只是绕了一圈。

          由于sizeof 直到编译时才进行评估,因此您需要依赖其他预处理器值。 我会这样做:

          #include <values.h>
          #if PTRBITS <= INTBITS
          #  define POINTER_FITS_INTO_UINT
          #endif
          

          【讨论】:

          • 如何设置 PTRBITS 和 INTBITS?
          • 来自 values.h : #define _TYPEBITS(type) (sizeof (type) * CHAR_BIT) #define INTBITS _TYPEBITS (int) #define PTRBITS _TYPEBITS (char *) 我不知道 CHAR_BIT 在哪里设置但 INTBITS 和 PTRBITS 仍然是 sizeof 表达式。
          • @Opera:这只是意味着INTBITS等不能被预处理器分析;不能在 #if 或类似语句中使用宏。宏可以出现在实际代码中 - 但不能出现在预处理器指令中。
          【解决方案9】:

          你就是做不到。 sizeof 是编译时运算符。 #if 和#define 与预处理器相关。由于预处理器在编译器之前运行,这将不起作用。但是,您可能能够找到一个神秘的编译器开关,它允许您多次传递它(即预处理、假装编译、预处理、编译),但平心而论,我会放弃尝试做您想做的事情。它并不意味着工作,简单地说,它没有。

          最好的办法是将这些定义设置为传递给编译器的 -D 命令。您可以静态断言所选择的那些是正确的。这样,您只需为给定的编译模式(例如 PowerPC Release)等在外部设置一些定义。

          【讨论】:

          • 最好的方法是使用您的构建系统(自动工具等)运行检查并使用#define SIZEOF_POINTER 4 或诸如此类的东西制作系统配置文件,然后使用那个。
          • 值得注意的是,在 C11 中 _Static_assert 将完成 OP 想要的。
          猜你喜欢
          • 2011-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多