天天看點

C和彙編(你想知道的C語言 3.6)

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)
 
轉載請注明出處