【问题标题】:C MACRO evaluationC 宏观评价
【发布时间】:2018-08-09 11:42:53
【问题描述】:

我希望声明一个静态分配的数组。 我们来看看下面的代码:

#define MAX(a,b) ((a)>(b)?(a):(b))
#define FAST            16
#define SLOW            6
#define MAX_NUM         MAX(FAST,SLOW)
U8*   pBuffers[MAX_NUM];

当 MAX_NUM 被 GCC 编译器评估时(FAST 和 SLOW 是常量)? 我想确保 MAX_NUM 是常量,并作为编译的一部分或由预处理器进行评估。

【问题讨论】:

  • 这个可以工作,这取决于MAX的定义。请出示。
  • 这段代码应该没问题。有什么问题?
  • 宏总是在编译代码之前展开,如果你想知道的话。
  • 我认为这不是关于替换宏,而是将表达式 ((16)>(8)?(16):(8)) 评估为数组的大小。
  • 预处理器计算结果为U8 *pBuffers[(16)>(6)?(16):(6))]; 其余由编译器决定

标签: c gcc c-preprocessor


【解决方案1】:

当您启动编译器时,会(按顺序)执行以下阶段:

  • 预处理:它管理#define、#ifdef / #endif...
  • 代码生成:生成可在目标 CPU 上运行的机器代码
  • 优化:根据用户选项进行优化

预处理阶段,预处理器将例如“替换”您的行:

U8*   pBuffers[MAX(FAST,SLOW)]

然后:

U8*   pBuffers[((FAST)>(SLOW)?(FAST):(SLOW))]

最后:

U8*   pBuffers[((16)>(6)?(16):(6))]

确实,预处理器不是很聪明,没有走得更远。

代码生成阶段,您的行将被解释为:

U8*   pBuffers[16]

因为代码生成器非常聪明。

【讨论】:

  • 但是#if ((16)>(6)?(16):(6)) == 16 中的((16)>(6)?(16):(6)) 不是由预处理器计算出来的吗?
  • @WeatherVane 是的,在这种情况下,是的。
  • 许多(大部分)优化过程在代码生成之前运行。
【解决方案2】:

The C standard 要求使用 整数常量表达式 声明大多数数组的大小,这可以并且在这种情况下需要在编译时进行完全评估。 (唯一的例外是“可变长度数组”,它们必须是具有“自动存储持续时间”的函数局部变量——不是静态分配的。)

因此,您的问题的一个答案是您不必担心。如果你写

WHATEVER_TYPE variable[SOME EXPRESSION];

在文件范围内,SOME EXPRESSION 将在编译时评估为常量,否则编译将失败并出现错误。

但更有用的答案是解释如何在阅读代码时亲自查看SOME EXPRESSION 是否为整数常量表达式。首先,你必须在心里扩展所有的宏。然后,您可能会得到某种算术表达式(如果没有,那就是语法错误)。

这个算术表达式是一个 constant 表达式,如果它没有副作用,不进行任何函数调用,并且不引用任何变量的值(即使它是const) (不过,enum 常量很好,字符串文字也很好,sizeof variable 只要 variable 被完全声明并且不是可变长度数组)。它是一个 integer 常量表达式,此外,它不尝试执行任何浮点或指针运算(您可以将浮点文字写为强制转换的立即操作数, 但是;例如 ((int)3.1415926) 是一个整数常量表达式)。

所以,举个例子,

#define MAX(a,b) ((a)>(b)?(a):(b))
#define FAST            16
#define SLOW            6
#define MAX_NUM         MAX(FAST,SLOW)
U8*   pBuffers[MAX_NUM];

宏展开后我们有

U8* pBuffers[((16)>(6)?(16):(6))];

方括号内的表达式没有副作用,不进行任何函数调用,不引用任何变量的值,也不做任何浮点或指针运算,所以它是一个整数常量表达式,编译器需要在编译时对其求值。

相比之下,如果您改用 MAX 的这个定义:

static inline size_t MAX(size_t a, size_t b)
{ return a > b ? a : b; }

然后宏扩展会产生

U8* pBuffers[MAX(16, 8)];

并且方括号内的表达式将进行函数调用,因此它不会是整数常量表达式,甚至不是常量表达式,并且会出现编译时错误。

(仅供参考,C++ 中的规则要复杂得多;如果您需要了解这一点,请提出一个新问题。)

【讨论】:

    【解决方案3】:

    宏总是在编译过程开始之前进行评估。所以这段代码没什么好担心的,它应该可以正常工作。

    同时,这整件事是依赖于编译器的,我相信gcc 可以正常工作。也许,对于某些裸机应用程序,它可能会发出警告。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-12
      • 2012-02-03
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多