C 编译器能生成无用的汇编代码吗?

C 编译器能生成无用的汇编代码吗?,c,gcc,assembly,x86,C,Gcc,Assembly,X86,我试图找到c程序生成的汇编代码的含义。以下是C语言的程序: int* a = &argc; int b = 8; a = &b; 下面是生成的汇编代码,并附有说明。有一部分我不明白: 主要报告的序言: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $36, %esp 加载%eax中argc的地址: mo

我试图找到c程序生成的汇编代码的含义。以下是C语言的程序:

int* a = &argc;
int b = 8;
a = &b;
下面是生成的汇编代码,并附有说明。有一部分我不明白:

主要报告的序言:

leal    4(%esp), %ecx
andl    $-16, %esp
pushl   -4(%ecx)
pushl   %ebp
movl    %esp, %ebp
pushl   %ecx
subl    $36, %esp
加载%eax中argc的地址:

movl    %ecx, %eax
我没有得到的部分是:

movl    4(%eax), %edx
movl    %edx, -28(%ebp)
堆叠粉碎保护器代码(设置):

a和b中的荷载值(见main.c):

修改a的值(a=&b):

堆栈粉碎保护器代码(验证堆栈是否正常):

如果堆栈正常:

.L7:
    addl    $36, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret

因此,我不理解的部分是修改-28(%ebp)中的值,这是一个从未使用过的地址。有人知道为什么会生成此部分吗?

这是查看编译器功能的好方法。我假设您有一个名为main.c的文件:

int main(int argc, char **argv) 
{
    int* a = &argc;
    int b = 8;
    a = &b;
}
使用调试信息编译到对象文件:

$ gcc -c -g main.c
查看部件:

$ objdump -S main.o

main.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
int main(int argc, char **argv)
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d ec                mov    %edi,-0x14(%rbp)
   7:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
    int* a = &argc;
   b:   48 8d 45 ec             lea    -0x14(%rbp),%rax
   f:   48 89 45 f8             mov    %rax,-0x8(%rbp)
    int b = 8;
  13:   c7 45 f4 08 00 00 00    movl   $0x8,-0xc(%rbp)
    a = &b;
  1a:   48 8d 45 f4             lea    -0xc(%rbp),%rax
  1e:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  22:   b8 00 00 00 00          mov    $0x0,%eax
}
  27:   5d                      pop    %rbp
  28:   c3                      retq   
然后再次查看部件:

$ objdump -S main.o

main.o:     file format elf64-x86-64


Disassembly of section .text.startup:

0000000000000000 <main>:
int main(int argc, char **argv)
{
    int* a = &argc;
    int b = 8;
    a = &b;
}
   0:   31 c0                   xor    %eax,%eax
   2:   c3                      retq   
$objdump-S main.o
main.o:文件格式elf64-x86-64
分解.text.startup节:
0000000000000000 :
int main(int argc,字符**argv)
{
int*a=&argc;
int b=8;
a=&b;
}
0:31 c0异或%eax,%eax
2:c3 retq

所以答案是肯定的。编译器可以生成不需要的指令。这就是为什么要启用优化。当它们被关闭时,编译器会以一种非常通用的方式完成它的工作,根本不需要思考。例如,它为未使用的变量保留空间。

如果编译时未进行优化(默认),则编译器输出将包含内存中的所有加载/存储,即使它们不需要。将您的代码放入一个不是
main
的函数中,并在(
-O3
)上进行优化编译。不要使用main(),请使用其他函数名测试您的代码。main()问题已经被问了无数次,回答了无数次……你告诉编译器通过不指定优化标志来关闭它的大脑,现在你想知道它会生成奇怪的代码?如果我使用-O1进行优化,唯一留在main中的就是mov$0,%eax,然后ret@DJ_Joe他妈的没错。如果要强制编译器发出无用代码,请使用
volatile
。或者编写一些测试用例,这些测试用例实际上做了一些编译器无法简单删除的有意义的工作。好的,谢谢。我发现非常令人惊讶的是,汇编部分与C源代码的任何部分都不对应。但是,由于程序什么也不做,汇编的每一行都应该被认为是无用的。制作C函数进行编译和反汇编以查看编译器在做什么是一门艺术。低优化级别编译器将所有内容保存到堆栈/内存中,并在那里检索表单,然后如果您有死代码,它可能会编写一些它从未读取过的内容,这在一定程度上是因为您的程序可能已经这样做了。编写非死代码或优化为非有趣内容的测试函数需要一些练习。@DJ_乔:看,尤其是到的链接,这正是@old_time所说的。遗憾的是,OP标题问题的答案是“是”,即使是在优化代码中,有了足够复杂的源代码,编译器最终将不得不停止优化计算,并发出经过合理删减的正确代码。创建最佳代码将花费太多的时间,对于现代软件项目的任何大型源代码库来说,它可能会直接超过宇宙的生命周期,或者至少需要数千年。获得接近最优的解决方案是完全不同的(NP与P问题),而当前的编译器在这方面非常擅长。@Ped7g甚至不可能创建最优代码。主要是因为我们对“最优”没有一个好的定义。这是最小的代码吗?最快的代码?最小内存消耗?什么是“最快”?最快的响应时间或吞吐量?
int main(int argc, char **argv) 
{
    int* a = &argc;
    int b = 8;
    a = &b;
}
$ gcc -c -g main.c
$ objdump -S main.o

main.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
int main(int argc, char **argv)
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d ec                mov    %edi,-0x14(%rbp)
   7:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
    int* a = &argc;
   b:   48 8d 45 ec             lea    -0x14(%rbp),%rax
   f:   48 89 45 f8             mov    %rax,-0x8(%rbp)
    int b = 8;
  13:   c7 45 f4 08 00 00 00    movl   $0x8,-0xc(%rbp)
    a = &b;
  1a:   48 8d 45 f4             lea    -0xc(%rbp),%rax
  1e:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  22:   b8 00 00 00 00          mov    $0x0,%eax
}
  27:   5d                      pop    %rbp
  28:   c3                      retq   
$ gcc -c -g -O3 main.c 
$ objdump -S main.o

main.o:     file format elf64-x86-64


Disassembly of section .text.startup:

0000000000000000 <main>:
int main(int argc, char **argv)
{
    int* a = &argc;
    int b = 8;
    a = &b;
}
   0:   31 c0                   xor    %eax,%eax
   2:   c3                      retq