这个语句会变成 nop 吗?

Does this statement turn into a nop?

我想知道下面的代码在 #define PRINT_STATEMENT 行被注释掉后是否会在 ARM 微控制器上执行许多 CPU 个周期:

#define PRINT_STATEMENT       1

#if PRINT_STATEMENT
   #define PRINT_DBG(...)     printf(__VA_ARGS__)
#else
   #define PRINT_DBG(...)
#endif

int main(){
   PRINT_DBG("Hello World\n");
   return 0;
}

我测试了在在线 C 编译器上注释掉 #define PRINT_STATEMENT 行,可以看到打印语句没有被执行。但是,我仍然想知道如果将程序闪存到 ARM 微控制器上(arm_gcc),那行会变成 nop 操作吗?

谢谢!

编译器可以自由决定是否产生 nop。生成任何不打印任何内容的程序几乎是免费的。

在现代编译器中,预处理和编译是同时进行的。但理论上,编译器在预处理器之后得到代码。在这种情况下,假设您已将 PRINT_STATEMENT 设置为 0,编译器将只会看到带有单条 return 0; 语句的主函数。

该标准并未规定在此级别上应该发生什么。如果你想知道你的编译器和你的目标在你的特定情况下发生了什么,你将不得不看一下程序集。

最有可能的情况是它被完全删除。一方面是因为编译器甚至看不到它,另一方面是因为当你什么都不能产生时为什么要产生一个 nop?

你可以在编译器资源管理器中查看:https://gcc.godbolt.org/z/39Koxz

这里可以看到主函数编译到return 0;部分

如果您将 #define PRINT_STATEMENT 注释掉,则 printf 永远不会进入后期编译阶段。

我们可以使用 gcc -E 运行预处理器并打印结果来验证这一点:

$ gcc -E test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 9 "test.c"
int main(){
   ;
   return 0;
}

如您所见,最终代码只是一个带有 return 0 的空主代码。

Since there is no code to generate an operation, why does it not turn into an empty operation (as in consuming no CPU cycles)? If it is a nop, that would mean that it would take 1 CPU cycle?

空语句没有可观察到的效果 - 因此没有生成代码。

However, I am still wondering if the program were to be flashed onto an ARM microcontroller (with arm_gcc), will that line be turned into a nop operation?

没有比测试更简单的了!

$ cat main.c
#include <stdio.h>
#if PRINT_STATEMENT
    #define PRINT_DBG(...)     printf(__VA_ARGS__)
#else
    #define PRINT_DBG(...)
#endif
int main(){
   PRINT_DBG("Hello World\n");
   return 0;
}

让我们看看 printf 调用:

$ arm-none-eabi-gcc -specs=nosys.specs -DPRINT_STATEMENT=1 main.c -o a.elf
$ arm-none-eabi-objdump -D a.elf | sed '/<main>:/,/^$/!d'
0000820c <main>:
    820c:   e92d4800    push    {fp, lr}
    8210:   e28db004    add fp, sp, #4
    8214:   e59f0014    ldr r0, [pc, #20]   ; 8230 <main+0x24>
    8218:   eb0000cc    bl  8550 <puts>
    821c:   e3a03000    mov r3, #0
    8220:   e1a00003    mov r0, r3
    8224:   e24bd004    sub sp, fp, #4
    8228:   e8bd4800    pop {fp, lr}
    822c:   e12fff1e    bx  lr
    8230:   0000b5fc    strdeq  fp, [r0], -ip

哎呀!看起来 printf 已优化为 puts 甚至 已禁用优化。好吧,不管怎样,让我们​​看看没有那个讨厌的东西printf:

$ arm-none-eabi-gcc -specs=nosys.specs main.c -o b.elf
$ arm-none-eabi-objdump -D b.elf | sed '/<main>:/,/^$/!d'
0000820c <main>:
    820c:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
    8210:   e28db000    add fp, sp, #0
    8214:   e3a03000    mov r3, #0
    8218:   e1a00003    mov r0, r3
    821c:   e28bd000    add sp, fp, #0
    8220:   e49db004    pop {fp}        ; (ldr fp, [sp], #4)
    8224:   e12fff1e    bx  lr

里面没有nop指令。只是应该存在的说明。

will that line be turned into a nop operation?

没有。该行将“不存在”。