在编译时计算宏中的数学吗?

时间:2014-04-11 17:59:29

标签: c macros build-process

例如,下面的MIN_N_THINGIES编译为2吗?或者我每次在代码中使用宏时都会重新计算除法(例如,每次迭代重新计算for循环的结束条件)。

#define MAX_N_THINGIES  (10)
#define MIN_N_THINGIES  ((MAX_N_THINGIES) / 5)

uint8_t i;
for (i = 0; i < MIN_N_THINGIES; i++) {
  printf("hi");
}

这个问题源于我仍在学习构建过程的事实。谢谢!

5 个答案:

答案 0 :(得分:2)

预处理器会将MIN_N_THINGIES替换为((10)/5),然后由编译器来优化(或不使用)表达式。

答案 1 :(得分:2)

如果将-E传递给gcc,它将显示预处理器阶段输出的内容。

gcc -E test.c | tail -n11

输出:

# 3 "test.c" 2

int main() {
uint8_t i;
for (i = 0; i < ((10) / 5); i++) {
  printf("hi");
}
return 0;
}

然后,如果您将-s标志传递给gcc,您将看到该分区已经过优化。如果你也传递了-o标志,你可以设置输出文件并对它们进行区分,看它们是否生成了相同的代码。

gcc -S test.c -o test-with-div.s
edit test.c to make MIN_N_THINGIES equal a const 2
gcc -S test.c -o test-constant.s
diff test-with-div.s test-constant.s
// for educational purposes you should look at the .s files generated.

然后如另一条评论所述,您可以使用-O ...

更改优化标记

gcc -S test.c -O2 -o test-unroll-loop.s

即使没有循环,也会展开for循环。

答案 2 :(得分:1)

没有。预处理器不计算宏,它们由编译器处理。预处理器可以在#if条件中计算算术表达式(无浮点值)。

宏只是文本替换。

请注意,扩展的宏仍然可以由编译器计算和优化,只是它不是由预处理器完成的。

答案 3 :(得分:0)

也许。该标准并未强制规定它是否存在。在大多数编译器上,它会在传递优化标志后执行(例如,带-O0的gcc不执行此操作,而-O2它甚至会展开循环)。

现代编译器执行更复杂的技术(矢量化,循环偏移,阻塞......)。但是,除非你真的关心表现,否则你编程HPC,编程实时系统等,你可能不应该关心编译器的输出 - 除非你只是感兴趣(是的 - 编译器可能是一个引人入胜的主题)。

答案 4 :(得分:0)

标准要求在编译时评估某些表达式。但请注意,当调用宏时,预处理器只进行文本拼接(好吧,差不多),所以如果你这样做:

#define A(x) ((x) / (S))

#define S  5

A(10) /* Gives ((10) / (5)) == 2 */

#undef S
#define S  2

A(20)  /* Gives ((20) / (2)) == 10 */

这些笔记本可以避免像:

这样的愚蠢
#define square(x) x * x

square(a + b)  /* Gets you a + b * a + b, not the expected square */

在预处理之后,结果将传递给编译器本身,该编译器执行标准请求的源中的(大部分)计算。大多数编译器会执行很多常量折叠,即由已知常量组成的计算(子)表达式,因为这很容易做到。

要查看扩展,编写几行的*.c文件非常有用,只需要检查宏,然后通过预处理器运行它(通常类似于cc -E file.c)和检查输出。