如果我编写一个使用其他预处理器常量执行操作的#define,那么每次宏在运行时出现时计算的最终值是多少?这取决于编译器中的优化,还是属于标准?
例:
#define EXTERNAL_CLOCK_FREQUENCY 32768 #define TIMER_1_S EXTERNAL_CLOCK_FREQUENCY #define TIMER_100_MS TIMERB_1_S / 10
将操作十分之三万二千七百六十八每次都发生在运行时我用的是TIMER_100_MS宏?
我想避免以下情况:
#define EXTERNAL_CLOCK_FREQUENCY 32768 #define TIMER_1_S EXTERNAL_CLOCK_FREQUENCY #define TIMER_100_MS 3276
编译器需要能够计算常量积分表达式,因为它们是在编译时计算数组大小等内容所必需的.但是,标准只说他们"可以" - 而不是"必须" - 这样做.因此,只有脑死亡编译器不会在编译时评估常量积分表达式,但是对非常规编译器的汇编输出的简单检查将验证每种情况.
宏只是文本替换,因此在您的示例TIMER_100_MS
中,在程序中编写是一种奇特的编写方式32768 / 10
.
因此,问题是编译器何时评估32768 / 10
,这是一个常量积分表达式.我不认为标准在这里需要任何特定的行为(因为运行时和编译时评估在效果上是无法区分的),但任何中途正常的编译器都会在编译时对其进行评估.
这里的大多数答案都集中在宏观替代的影响上.但我想他想知道是否
32768 / 10
在编译时进行评估.首先,这是一个算术常量表达式,另外还有一个整数常量表达式(因为它只有整数类型的文字).该实现可以在运行时自由计算,但它也必须能够在编译时计算它,因为
如果常量表达式在其表达式所具有的类型中无法表示,则它必须提供诊断消息
在翻译时需要值的上下文中允许使用此类表达式,例如,如果用作数组维度的大小.
如果编译器可以在编译时主要计算结果,那么它应该使用该值,而不是在运行时重新计算它.但也许有一些理由仍然这样做.我不知道.
编辑:对不起,我已经回答了这个问题,好像它是关于C++的.今天你注意到了C.表达式中的溢出被认为是C中的未定义行为,无论它是否发生在常量表达式中.当然,第二点在C中也是如此.
编辑:作为评论说明,如果将宏替换为类似的表达式3 * TIMER_100_MS
,那么这将进行评估(3 * 32768) / 10
.因此,简单而直接的答案是"不,它不会在每次运行时发生,因为由于优先级和关联性规则,可能根本不会发生划分".我上面的回答假设宏总是被替换,以便实际发生划分.
我不知道有任何标准可以保证它会被优化.预处理器将替换32768/10替换TIMER_100_MS,您可以通过运行gcc -c看到它.要查看编译器是否进一步优化,请运行gcc -S并检查汇编程序.使用gcc 4.1,即使没有任何优化标志,这也会在编译期间减少到常量:
#include#include #define EXTERNAL_CLOCK_FREQUENCY 32768 #define TIMER_1_S EXTERNAL_CLOCK_FREQUENCY #define TIMER_100_MS TIMER_1_S / 10 int main(int argc, char **argv) { printf("%d\n", TIMER_100_MS); return(0); } gcc -S test.c cat test.s ... popl %ebx movl $3276, 4(%esp) leal LC0-"L00000000001$pb"(%ebx), %eax movl %eax, (%esp) call L_printf$stub ...
编译器应优化该表达式.我不认为这是标准所要求的,但我从未见过不会执行该任务的编译器.
但是,你不应该写:
#define TIMER_100_MS TIMERB_1_S / 10
...因为这是一个等待发生的错误.你应该总是将涉及表达式的#defines括起来.
#define TIMER_100_MS (TIMERB_1_S / 10)
考虑:
i = 10 * TIMER_100_MS;
第一种情况是32768((10*TIMERB_1_S)/ 10),第二种情况是32760(10*(TIMERB_1_S/10)).这里不是一个关键的区别,但你必须意识到它!
每次使用TIMERB_100_MS宏时,操作32768/10都会在运行时发生吗?
代码中您使用的每个位置TIMERB_100_MS
都将由32768 / 10
预处理器替换.
该表达式是否得到进一步优化(它的计算结果为常量)取决于编译器.
来自WG14/N1124委员会草案 - 2005年5月6日ISO/IEC 9899:TC2:
6.6常量表达式
句法
constant-expression:
条件表达式描述
可以在转换期间而不是运行时期间评估常量表达式,并且因此可以在常量可以在任何地方使用.
约束
常量表达式不应包含赋值,递增,递减,函数调用或逗号运算符,除非它们包含在未评估的子表达式中.9)
每个常量表达式应计算为其类型的epresentable值范围内的常量.
伙计们,这种转变被称为"不断折叠",甚至大多数学生编制者也会这样做.只要你有一个由你或你的大学室友建立的编译器并且你正在编译一个静态类型的语言,即使没有打开优化,你也可以依靠它.如果你正在处理一些允许改变其含义的古怪动态语言,那就不一样了/
.