C 了解组装:-O2如果分支
我刚刚用一个if语句尝试了一个简单的C程序,并分析了它的程序集。然而,当-O2标志用于编译时,它的行为有很大不同 相同的C代码为:-C 了解组装:-O2如果分支,c,assembly,disassembly,C,Assembly,Disassembly,我刚刚用一个if语句尝试了一个简单的C程序,并分析了它的程序集。然而,当-O2标志用于编译时,它的行为有很大不同 相同的C代码为:- #include<stdio.h> int main(int argc, char **argv) { int a; if(a<0) { printf("A is less than 0\n"); } } 我读到test指令基本上只执行两个操作数的逻辑AND。我还了解到,js指令在上一条指令中的符号发
#include<stdio.h>
int main(int argc, char **argv) {
int a;
if(a<0) {
printf("A is less than 0\n");
}
}
我读到test
指令基本上只执行两个操作数的逻辑AND。我还了解到,js
指令在上一条指令中的符号发生变化时执行跳转。因此,test
ing eax with eax将给出0
或1
,跳转将取决于此
我无法理解它是如何被用于分支的。
有人能解释一下它是如何工作的吗?我不知道您期望的是什么,但是您的程序有未定义的行为,因为
a
没有初始化。因此,汇编程序的输出可以是任何形式。如果eax
为负数,则标志将指示在test
指令之后进行跳转。这会将电脑推到执行打印的.L4
。否则,我们离开。测试eax,如果eax=0,eax
将设置零标志
js指令基本上会检查符号标志(当符号发生变化时,ajs不会跳转,如果符号标志为1则跳转 如果最后一次运算的结果为负数,则符号位为on(2的补语中的负数在1中具有最高有效位)
因此,如果AND运算在两个负整数(-1&-1)之间,则最后一位将为1(符号标志),因此进行跳转。如果数字为正,则最后一位将为0,则不会进行跳转。看起来,当您指定-O2时,编译器会将“int a”放入寄存器以进行速度优化 由于从未初始化“int a”,程序集不会显示写入eax的任何内容,而是具有上次分配给它的值
其他答案解释了测试是如何作为一种分支机制工作的。英特尔手册对此很有帮助。以下是它为测试指令编写的文档:
SF是符号标志,由JS操作码测试。它被设置为eax的最高有效位,即符号位。因此,当eax包含负数时,将进行跳转。
test
设置所有算术标志,而不仅仅是零标志。整个程序是否因为a
的值未定义而未定义罚款?我假设它会使用调用时堆栈上的任何内容。程序的输出可以是printf,也可以是nothing,但为什么编译器的输出会是nothing?理论上,输出可以是nothing,但现实世界中的编译器不会输出无意义的内容。@nmichaels:程序的行为是未定义的,是的。这是整个pro的行为例如,优化器可以很好地得出结论,a
从未写入,因此其值未定义,因此可以省略a
的任何后续用法。这将是一个完全有效的优化。@nmichaels:标准不要求调用时堆栈上有任何内容。此uninitializeda
允许不是由RAM支持,而是由一个巨大的混沌空间支持,这会让世界陷入一种老年恐惧。或者,更现实地说,在某些体系结构上,内存可能包含陷阱表示,这会导致硬件在加载到int寄存器时抛出故障。允许这两种情况(尤其是第二种情况),程序的行为是未定义的。x86不需要这么多自由度,但它在标准中有。一个好的编译器应该优化整个程序,因为它有未定义的行为(使用未初始化的变量)。或者,如果初始化了a
,则应根据a
的值优化条件。
main:
push %ebp
mov %ebp, %esp
sub %esp, 8
and %esp, -16
sub %esp, 16
test %eax, %eax
js .L4
leave
ret
.p2align 4,,15
.L4:
sub %esp, 12
push OFFSET FLAT:.LC0
call puts
add %esp, 16
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.6"