帮助理解汇编级别的C和C++中#define,const和enum之间的差异

mar*_*tin 5 c x86 assembly gcc

最近,我正在研究#define,const和enum的汇编代码:

C代码(#define):

3   #define pi 3  
4   int main(void)
5   {
6      int a,r=1;             
7      a=2*pi*r;
8      return 0;
9   }
Run Code Online (Sandbox Code Playgroud)

GCC生成的汇编代码(用于C代码中的第6行和第7行):

6   mov $0x1, -0x4(%ebp)
7   mov -0x4(%ebp), %edx
7   mov %edx, %eax
7   add %eax, %eax
7   add %edx, %eax
7   add %eax, %eax
7   mov %eax, -0x8(%ebp)
Run Code Online (Sandbox Code Playgroud)

C代码(枚举):

2   int main(void)
3   {
4      int a,r=1;
5      enum{pi=3};
6      a=2*pi*r;
7      return 0;
8   }
Run Code Online (Sandbox Code Playgroud)

GCC生成的汇编代码(用于c代码中的第4行和第6行):

6   mov $0x1, -0x4(%ebp)
7   mov -0x4(%ebp), %edx
7   mov %edx, %eax
7   add %eax, %eax
7   add %edx, %eax
7   add %eax, %eax
7   mov %eax, -0x8(%ebp)
Run Code Online (Sandbox Code Playgroud)

C代码(常量):

4   int main(void)
5   {
6      int a,r=1;  
7      const int pi=3;           
8      a=2*pi*r;
9      return 0;
10  }
Run Code Online (Sandbox Code Playgroud)

GCC生成的汇编代码(用于c代码中的第7行和第8行):

6   movl $0x3, -0x8(%ebp)
7   movl $0x3, -0x4(%ebp)
8   mov  -0x4(%ebp), %eax
8   add  %eax, %eax
8   imul -0x8(%ebp), %eax
8   mov  %eax, 0xc(%ebp)
Run Code Online (Sandbox Code Playgroud)

我发现使用#defineenum汇编代码是一样的.编译器使用3个添加指令来执行乘法.但是,在使用时,使用constimul指令.谁知道背后的原因?

Joh*_*ler 7

不同之处在于,#define或者enum值3不需要作为代码中的显式值存在,因此编译器决定使用两个添加指令而不是为常量3分配空间.add reg,reg指令是每个指令2个字节,所以6个字节的指令和0个字节的常数乘以3,这比imul使用4个字节的常数更小.再加上使用添加指令的方式,它可以实现*2*3的非常直译,因此这可能不是大小优化,只要乘以2或3,它就可以是默认的编译器输出.通常是比乘法更快的指令).

#define并且enum不声明实例,它们只提供了为值3赋予符号名称的方法,因此编译器可以选择制作更小的代码.

  mov $0x1, -0x4(%ebp)    ; r=1
  mov -0x4(%ebp), %edx    ; edx = r
  mov %edx, %eax          ; eax = edx
  add %eax, %eax          ; *2
  add %edx, %eax          ; 
  add %eax, %eax          ; *3
  mov %eax, -0x8(%ebp)    ; a = eax
Run Code Online (Sandbox Code Playgroud)

但是当你声明时const int pi = 3,你告诉编译器为整数值分配空间并用3初始化它.它使用4个字节,但是该常量现在可用作imul指令的操作数.

 movl $0x3, -0x8(%ebp)     ; pi = 3
 movl $0x3, -0x4(%ebp)     ; r = 3? (typo?)
 mov  -0x4(%ebp), %eax     ; eax = r
 add  %eax, %eax           ; *2
 imul -0x8(%ebp), %eax     ; *pi
 mov  %eax, 0xc(%ebp)      ; a = eax
Run Code Online (Sandbox Code Playgroud)

顺便说一下,这显然不是优化代码.因为a从未使用过该值,所以如果打开优化,编译器就会执行

xor eax, eax  ; return 0
Run Code Online (Sandbox Code Playgroud)

在所有3个案例中.

附录:

我尝试使用MSVC,在调试模式下,我得到所有3种情况的相同输出,MSVC总是使用imul的文字6.即使在情况3时,它创建const int = 3它实际上并没有在imul中引用它.

我不认为这个测试真的告诉你有关const vs define vs enum的任何信息,因为这是非优化代码.

  • +1; 关于最后一段; 这就是C++在`const`语义中与C的不同之处.除非你使用它的地址,否则C++的行为就像pi是文字一样,所以他应该相信在所有三种情况下为C++编译获得相同的代码. (3认同)