假设我在 C 或C ++中有以下内容:
#include <math.h>
#define ROWS 15
#define COLS 16
#define COEFF 0.15
#define NODES (ROWS*COLS)
#define A_CONSTANT (COEFF*(sqrt(NODES)))
然后,我去许多嵌套循环深处使用NODES
和A_CONSTANT
(即多次使用)。显然,两者都有可以在编译时确定的数值,但编译器实际上是这样做的吗?在运行时,CPU每次看到15*16
时都必须评估NODES
,还是编译器静态放置240
?同样,每次看到A_CONSTANT
时,CPU都必须评估平方根吗?
我的猜测是ROWS*COLS
乘法被优化了,但没有别的。整数乘法内置于语言中,但sqrt是一个库函数。确实如此,是否有任何方法可以获得相当于A_CONSTANT
的幻数,以便在运行时仅对平方根进行一次评估?
答案 0 :(得分:20)
宏定义通过简单的文本替换扩展到源代码中,然后再转交给编译器,这可能会进行优化。编译器将为表达式NODES
,ROWS*COLS
和15*16
生成完全相同的代码(我不能想到每次循环时都会进行乘法运算的单个代码已启用优化)。
至于A_CONSTANT
,它再次成为一个宏的事实并不重要;重要的是编译器是否足够聪明,以确定常量的sqrt
是常量(假设来自sqrt
的{{1}})。我知道GCC足够聪明,我希望其他生产质量的编译器也足够聪明。
答案 1 :(得分:11)
#define中的任何内容都作为预编译步骤插入到源代码中,这意味着一旦编译代码,宏基本上就消失了,代码就像往常一样编译。是否优化取决于您的代码,编译器和编译器设置。
答案 2 :(得分:9)
这取决于你的编译器。
#include <math.h>
#define FOO sqrt(5);
double
foo()
{
return FOO;
}
我的编译器(gcc 4.1.2)为此代码生成以下程序集:
.LC0:
.long 2610427048
.long 1073865591
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB2:
movsd .LC0(%rip), %xmm0
ret
.LFE2:
所以它确实知道sqrt(5)
是一个编译时常量。
如果您的编译器不那么聪明,我不知道在编译时计算平方根的任何可移植方法。 (当然,您可以计算一次结果并将其存储在全局或其他内容中,但这与编译时常量不同。)
答案 3 :(得分:8)
这里真的有两个问题:
sqrt()
?(1)很简单:是的,确实如此。预处理器与C编译器分开,并在C编译器启动之前完成它。所以,如果你有
#define ROWS 15
#define COLS 16
#define NODES (ROWS*COLS)
void foo( )
{
int data[ROWS][COLS];
printf( "I have %d pieces of data\n", NODES );
for ( int *i = data; i < data + NODES ; ++i )
{
printf("%d ", *i);
}
}
编译器实际上会看到:
void foo( )
{
int data[15][16];
printf( "I have %d pieces of data\n", (15*16) );
for ( int *i = data; i < data + (15*16) ; ++i )
{
printf("%d ", *i);
}
}
这受所有常规编译时常量优化的影响。
sqrt()
比较复杂,因为它从编译器到编译器各不相同。在大多数现代编译器中,sqrt()实际上是compiler intrinsic而不是库函数 - 它看起来像一个函数调用,但它实际上是编译器中的一个特殊情况,它具有基于数学定律,硬件操作的额外启发式算法在sqrt()
是如此特殊情况的智能编译器中,常量值的sqrt()
将在内部转换为常数。在愚蠢的编译器中,每次都会产生一个函数调用。知道你得到的唯一方法是编译代码并查看发出的程序集。
从我所看到的,MSVC,现代GCC,英特尔,IBM和SN都将sqrt
作为内部处理。旧的GCC和一些供应商提供的用于嵌入式芯片的糟糕的编译器没有。
答案 4 :(得分:3)
#defines
处理,只需简单的文本替换。然后将生成的文本文件传递给实际的编译步骤。
如果您正在使用gcc,请尝试使用-E
开关编译源文件,该文件将执行预处理然后停止。查看生成的文件以查看编译步骤的实际输入。
答案 5 :(得分:1)
宏将被替换,然后代码编译就像其余的代码一样。如果你已经启用了优化(并且你正在使用的编译器进行正常优化),你可能希望在编译时计算这样的事情。
从正确的角度来看,相对较少的C ++编译器已经足够老了,你会期望它们缺乏这样的优化。编程器足够老,缺乏简单的优化通常只有C(即便如此,不要依赖它 - 绝对像MS C 5.0 / 5.1 / 6.0,Datalight / Zortech C,Borland等,这样做了从我记得的情况来看,在CP / M上运行的C编译器大多没有。