先前学习CSAPP中指令的跳转时,书中展示了一段c语言程序编译后的汇编代码,想自己生成汇编代码观察,故留此文记录。
对于一段c语言程序而言,有三种循环实现的方式:do-while循环,while循环,for循环。每个初学者都会编制使用三种循环的代码,但是三种代码对应的汇编程序有何区别,值得研究。
-
do-while 循环
展示以下代码:初始值为10,循环自减2,为0时退出:
#include
int main(){ int i =10; do{ i-=2; }while(i>0); } 使用以下指令编译生成汇编代码
gcc -S do_while.c -Og
展示其结果:
.cfi_startproc endbr64 movl $10, %eax .L2: subl $2, %eax testl %eax, %eax ;test比较指定位(由操作数决定)是否为1,没有任何一位满足的zf=1,其余情况为0. ;在这里可以用生成的0标志位判断jg是否发生跳转 jg .L2 ;指令根据先前一条生成的符号判断是否转移,转移条件为SF=OF 并且 ZF=0 movl $0, %eax ;i赋值为0?是为了返回值为0 ret .cfi_endproc再次尝试,修改循环的退出条件此时c代码改为:
#include
int main(){ int i =10; do{ i-=3; }while(i>0); i = 9; printf("%d",i); } 此时出现的汇编代码为:
.cfi_startproc endbr64 subq $8, %rsp;rsp栈指针寄存器 .cfi_def_cfa_offset 16 movl $10, %eax .L2: subl $3, %eax testl %eax, %eax jg .L2 movl $9, %edx leaq .LC0(%rip), %rsi movl $1, %edi movl $0, %eax call __printf_chk@PLT movl $0, %eax;为何两次? addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc -
以while形式写一个循环
#include
int main(){ int i =10; while(i>0){ i-=2; } return 0; } 其汇编代码为:
.cfi_startproc endbr64 movl $10, %eax .L2: testl %eax, %eax ;先判断 jle .L4;i>0,也就是i<=0时候继续执行 ;若判断i大于等于的话,变为js(i为负数时转移) subl $2, %eax jmp .L2 ;跳转至下一次 .L4: movl $0, %eax ret .cfi_endproc -
以for形式写一个循环:
#include
int main(){ int i ; for(i=10;i>0;i-=2){} return 0; } 其汇编结果如下所示,基本与while形式没有差异。
.cfi_startproc endbr64 movl $10, %eax .L2: testl %eax, %eax jle .L4 subl $2, %eax jmp .L2 .L4: movl $0, %eax ret .cfi_endproc
经过上述分析,在汇编层面上总结三种循环的特点:
- do-while形式的循环只有一次跳转,由于先判断后执行的特点,判断处于靠下位置,判断不满足条件后就直接退出,因此只需要一个跳转语句。
- while 和 for 形式存在两个跳转,这是由于先判断后执行。L2段判断并且执行,L4段退出。



