【问题标题】:Is there logical short-circuiting in the C preprocessor?C 预处理器中是否存在逻辑短路?
【发布时间】:2014-06-04 12:35:11
【问题描述】:

gcc docs for cpp 解释了#if 指令:

[...] 和逻辑运算(&& 和 ||)。后两者遵循标准 C 的通常短路规则。

这是什么意思?预处理时没有表达式求值,怎么可能短路呢?

【问题讨论】:

  • 我认为在这里,短路意味着如果第一项的评估完全确定了结果值,则不评估第二项
  • 但是你怎么知道呢?为什么这很重要?
  • 有趣的问题。
  • 在标准 C 中,#if 只需要一个常量表达式。如果该常量表达式是my_const1 && my_const2,那么该表达式的求值就如同它在代码中的其他任何地方一样,就这么简单。但我确信 GCC 对预处理器有很多奇怪的、多余的扩展,所以评论似乎适用于它们。
  • 你从哪里知道这里没有在预处理期间评估表达式?

标签: c++ c gcc


【解决方案1】:

很简单:未定义的宏的数值为零,除以零是非法的。

#if FIXEDSIZE && CHUNKSIZE/FIXEDSIZE > 42
#define USE_CELLPOOL
#endif

#if 确实将其行的其余部分评估为整数常量表达式。您的链接文档开始:

“#if”指令允许您测试算术表达式的值,而不仅仅是一个宏的存在。

这不是 gcc 扩展,#if 的标准语法是

#ifconstant-expression new-line groupopt

C99 预处理器将所有常量视为[u]intmax_t

【讨论】:

  • 因为如果你没有FIXEDSIZE 测试,你会得到“错误:#if 中除以零”。我正要发布一个非常相似的答案。
  • @hacks 但是从这个例子中实现预处理器正在评估除法不是微不足道的吗?
  • @MM.;唔。这就是问题所在:在预处理过程中没有表达式的求值,那么它怎么能短路呢?
  • @hacks 我现在明白了,你是对的。填补其他人的暂时盲点并不能直接回答OP的问题。修复。
  • @temple -Wundef,正如 Jeffrey Thomas 的回答中提到的那样,尽管我不会准确地将其称为 副作用。但就是这样。 #if 中支持的所有运算符都没有副作用,#if 行上的标识符既不是宏也不是#if 中有效的少数关键字(在 C99 中仅 defined;在 C++ 中还有 @ 987654334@,bitandtrue,和false;C2011可能还会增加一些,不记得了)统一替换为常量0,不管它们在#if之外的意义如何。
【解决方案2】:

他们指的是&&|| 运算符,用于#if

#if defined (AAA) || defined (BBB)

如果定义了defined (AAA),则永远不会评估defined (BBB)


更新

所以运行计算会短路。例如,如果您使用-Wundef 构建以警告未定义宏的使用。

#if defined FOO && FOO > 1000
#endif

#if FOO > 1000
#endif

会导致

thomas:~ jeffery$ gcc foo.c -Wundef
foo.c:4:5: warning: 'FOO' is not defined, evaluates to 0 [-Wundef]
#if FOO > 1000
    ^
1 warning generated.

所以第一个版本不会产生未定义的宏警告,因为FOO > 1000 没有被评估。


老沉思

如果第二部分是具有副作用的宏,这将变得很重要。不会评估宏,因此不会发生副作用。


为了避免宏滥用,我将举一个比较理智的例子

#define FOO
#define IF_WARN(x) _Pragma (#x) 1
#if defined(FOO) || IF_WARN(GCC warning "FOO not defined")
#endif

现在我构建了这个例子,现在我遇到了一个问题。 IF_WARN 总是被评估。

嗯,需要更多研究。


嗯……现在我又读了一遍。

宏。表达式中的所有宏都在表达式值的实际计算开始之前展开。

【讨论】:

  • 它有什么变化,因为预处理器没有副作用?编辑:似乎我错了,所以我想看一个样本:)
  • 宏可能有副作用。
  • @JefferyThomas:比如?
  • 如果您 #define BBB {volatile int x=0;} 则宏包含副作用,但该副作用仅在从程序调用宏时相关,而不是在预处理器评估期间。不要把这些东西混在一起。
  • @RudyVelthuis:问题的重点在于询问一个表达式是否被评估是否重要;这个答案只是教导短路是什么,甚至没有尝试回答这个问题。至于您的最后一条评论,“预处理器表达式”和“C++ 表达式”是两个非常不同的东西,我认为 OP 指的是哪一个很清楚。
【解决方案3】:

预处理时没有表达式求值,怎么会短路呢?

是的,在预处理过程中会评估表达式

C11:6.10.1 条件包含(p4):

在评估之前,预处理标记列表中的宏调用将变为 ...

在脚注 166 中:

由于控制常量表达式在翻译阶段 4 进行评估,所有标识符....

这些陈述清楚地证明了预处理中有表达式的评估。必要条件是控制表达式必须计算为整数值。
现在运算符 &&|| 将遵守标准 C 中常见的短路规则,如 GNU doc 中所述。

现在在有和没有// 的情况下运行这个程序并查看结果以查看短路行为:

#include<stdio.h>
#define macro1 1
//#define macro2 1
int main( void )
{
    #if  defined (macro1)  && defined (macro2)
    printf( "Hello!\n" );
    #endif
    printf("World\n"); 
    return 0;
}

【讨论】:

  • #if 表达式中扩展宏的一个简单示例:#define VERSION 5.0 ... #if VERSION &gt; 6.0 &amp;&amp; defined (cplusplus)。这根本不检查是否定义了 cplusplus,因为 MIN_VERSION 无论如何都太低了。而且这个宏肯定会扩展成5.0
  • @RudyVelthuis;我同意。但是,OP实际上询问的是表达式的评估而不是短路行为。 (顺便说一句,我没有对你的答案投反对票,总的来说,我已经停止对这样一个典型问题的答案投反对票)。
  • 我没有答案,只有几个 cmets,所以没有什么可否决的。
  • @RudyVelthuis;哎呀,我以为你是杰弗里·托马斯:P
【解决方案4】:

评估宏观条件是预处理的一部分(主要部分),因此它发生并且短路在那里是有意义的。您可以查看其他答案的示例。

条件是指示预处理器选择的指令 是否在最终的令牌流中包含一段代码 传递给编译器。 预处理器条件可以测试算术 表达式,或名称是否定义为宏,或两者兼而有之 同时使用特殊定义的运算符。

此外,它可以减少编译时间。更改以下评估可以加快编译速度(取决于编译器的实现)。

【讨论】:

    猜你喜欢
    • 2010-12-18
    • 2011-07-02
    • 2019-12-06
    • 1970-01-01
    • 2020-05-11
    • 2010-11-02
    • 2018-05-21
    • 1970-01-01
    相关资源
    最近更新 更多