C语言其实是个绝世高手,它一直隐藏"汇编“的身份,别人以为汇编是汇编,C语言只是默默一笑: 汇编只是我的代理人。
Q: 选择语句if是如何对应汇编的?
A:
int i;
int j;
if (i == 1)
j = 0;
else
j = 1;
0000000100000f92 cmpl $0x1, -0x14(%rbp) // i == 1 ?
0000000100000f96 jne 0x100000fa8
0000000100000f9c movl $0x0, -0x18(%rbp) // j = 0
0000000100000fa3 jmp 0x100000faf
0000000100000fa8 movl $0x1, -0x18(%rbp) // j = 1
0000000100000faf xorl %eax, %eax
判断i是否为1: cmpl指令判断相等; jne代表不为0跳转(cmp为减运算); jmp为无条件跳转,此处为i == 1的情况。
Q: for循环是如何对应汇编的?
A:
int i;
int j;
for(i = 1; i < 10; ++i)
j = i;
0000000100000f82 movl $0x1, -0x14(%rbp) // i = 1
0000000100000f89 cmpl $0xa, -0x14(%rbp) // i < 10
0000000100000f8d jge 0x100000fa7 // when i >= 10, jump out
0000000100000f93 movl -0x14(%rbp), %eax // now, i < 10
0000000100000f96 movl %eax, -0x18(%rbp) // j = i
0000000100000f99 movl -0x14(%rbp), %eax
0000000100000f9c addl $0x1, %eax // ++i
0000000100000f9f movl %eax, -0x14(%rbp)
0000000100000fa2 jmp 0x100000f89
0000000100000fa7 xorl %eax, %eax
对于for循环,比较和跳转指令实现初始化和判断跳转功能。
Q: while循环如何对应汇编?
A:
int i = 1;
int j;
while(i < 10) {
j = i;
++i;
}
0000000100000f82 movl $0x1, -0x14(%rbp) // i = 1
0000000100000f89 cmpl $0xa, -0x14(%rbp) // i >= 10 ?
0000000100000f8d jge 0x100000fa7 // jump out
0000000100000f93 movl -0x14(%rbp), %eax
0000000100000f96 movl %eax, -0x18(%rbp) // j = i
0000000100000f99 movl -0x14(%rbp), %eax
0000000100000f9c addl $0x1, %eax // ++i
0000000100000f9f movl %eax, -0x14(%rbp)
0000000100000fa2 jmp 0x100000f89
0000000100000fa7 xorl %eax, %eax
while和if语句很相近,因为它们表达的含义是相近的。
Q: break对应汇编是什么?
A:
int i = 1;
int j;
while(i < 10) {
if (i > 5)
break;
j = i;
++i;
}
0000000100000f72 movl $0x1, -0x14(%rbp)
0000000100000f79 cmpl $0xa, -0x14(%rbp)
0000000100000f7d jge 0x100000fa6
0000000100000f83 cmpl $0x5, -0x14(%rbp) // i > 5 ?
0000000100000f87 jle 0x100000f92 // i <= 5, continue
0000000100000f8d jmp 0x100000fa6 // i > 5, jump out
0000000100000f92 movl -0x14(%rbp), %eax
0000000100000f95 movl %eax, -0x18(%rbp)
0000000100000f98 movl -0x14(%rbp), %eax
0000000100000f9b addl $0x1, %eax
0000000100000f9e movl %eax, -0x14(%rbp)
0000000100000fa1 jmp 0x100000f79
0000000100000fa6 xorl %eax, %eax
break实际也只是比较和跳转指令的代名词,continue也是类似就不再赘述。
Q: switch/case语句如何对应汇编?
A:
int i; // not initialize it on purpose
int j;
switch(i) {
case 1:
j = 1;
break;
case 2:
j = 2;
break;
default:
j = 0;
break;
}
0000000100000f62 movl -0x14(%rbp), %edi
0000000100000f65 movl %edi, %eax // i is in %eax
0000000100000f67 subl $0x1, %eax // i == 1?
0000000100000f6a movl %edi, -0x1c(%rbp)
0000000100000f6d movl %eax, -0x20(%rbp)
0000000100000f70 je 0x100000f8f // case 1
0000000100000f76 jmp 0x100000f7b
0000000100000f7b movl -0x1c(%rbp), %eax
0000000100000f7e subl $0x2, %eax // i == 2?
0000000100000f81 movl %eax, -0x24(%rbp)
0000000100000f84 je 0x100000f9b // case 2
0000000100000f8a jmp 0x100000fa7
0000000100000f8f movl $0x1, -0x18(%rbp) // case 1: j = 1
0000000100000f96 jmp 0x100000fae
0000000100000f9b movl $0x2, -0x18(%rbp) // case 2: j = 2
0000000100000fa2 jmp 0x100000fae
0000000100000fa7 movl $0x0, -0x18(%rbp) // default: j = 0
0000000100000fae xorl %eax, %eax
又是一堆比较和跳转指令,不过综上所述,好像也没有多少指令就表达了C语言大部分语法概念。
C语言和汇编其实很近,可参考:汇编和c只有一步之近----小话c语言(19)
在计算机性能越来越高的情况下,坚持用汇编也许会稍许提升性能,但很可能已无实在意义,只有纯技术讨论的意义。当然,万物都有局限,当C语言真的没办法直接表达,转用汇编解决问题才是真正吃透了C语言.
作者: 陈曦
环境: MacOS 10.14.5 (Intel i5)
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Linux 3.16.83 (Ubuntu)
转载请注明出处