• 快速识别汇编中等价的C语言语句(if, while, for, switch)


    可能经常看汇编的朋友会一眼就认出跟C语言中一些语句等价的汇编代码, 经验使然也. 而不经常接触汇编的同学, 可能就对相对繁琐的寄存器操作指令有点云里雾里了.

    汇编是编译器翻译中级语言(也可以把C语言称作高级语言, 呵呵)的结果, 只要是机器做的事儿,一般都有规律可循. 那我们现在就来看看一下这些基本语句的汇编规律吧.

    注意:本文使用的汇编格式为GAS(Gnu ASembler GNU汇编器). 它同Intel文档中的格式以及微软编译器使用的格式差异很大,

    具体请看文章AT&T汇编格式与Intel汇编格式的比较.

    条件转移语句- if

    ============================

    C语言中的if-else语句的通用形式如下

    1. if(test-expr)
    2.     then-statement;
    3. else
    4.     else-statement;

    对于这种通用形式, 汇编实现通常会使用下面这种形式

    1.     t= test-expr;
    2.     if (t)
    3.         goto true;
    4.     else-statement
    5.         goto done;
    6. true:
    7.     then-statement
    8. done:

    也就是汇编器为then-statement 和else-statement各自产生代码块, 并插入条件和无条件分支, 以保证正确代码块的执行.

    下面我们来看C语言源代码, 汇编结构的C代码, 和汇编代码的比较.

    Code Snippet
    1. //----------Classic C code------------
    2. int absdiff(int x, int y)
    3. {
    4.     if (x < y)
    5.         return y - x;
    6.     else
    7.         return x - y;
    8. }
    9. //----------Classic C code------------

    Code Snippet
    1. //----------Equivalent Goto Version------------
    2. int gotodiff(int x, int y)
    3. {
    4.     int rval;
    5.  
    6.     if (x < y)
    7.         goto less;
    8.     rval = x - y;
    9.     goto done;
    10. less:
    11.     rval = y - x;
    12. done:
    13.     return rval;
    14. }
    15. //----------Equivalent Goto Version------------

    Code Snippet
    1. //----------Equivalent assembly Version------------
    2.     movl 8(%ebp),%edx          ;Get x
    3.     movl 12(%ebp),%eax         ;Get y
    4.     cmpl %eax,%edx             ;Compare x:y
    5.     jl .L3                     ;If <, goto less:
    6.     subl %eax,%edx             ;Compute y-x
    7.     movl %edx,%eax             ;Set as return value
    8.     jmp .L5                    ;Goto done:
    9. .L3:                           ;less:
    10.     subl %edx,%eax             ;Compute x-y as return value
    11. .L5:                           ;done:Begin completion code
    12. //----------Equivalent assembly Version------------

    do-while循环

    ========================

    do-while循环的通用形式是这样的:

    1. do
    2. {body-statement}
    3. while (test-expr);

    循环的效果就是重复执行body-statement, 对test-expr求值, 如果不是0, 就继续循环. 注意, 循环体至少执行一次.

    通常, do-while 的实现有下面的通用形式:

    1. loop:
    2.     body-statement
    3.     t= test-expr;
    4.     if (t)
    5.         goto loop;

    下面是一个例子, 找找感觉吧.

    Code Snippet
    1. //----------Original C Version------------
    2. do{
    3.     int t = val + nval;
    4.     val = nval;
    5.     nval = t;
    6.     i++;
    7. } while (i < n);
    8. //----------Original C Version------------
    Code Snippet
    1. //----------Corresponding assembly code------------
    2. .L6: loop:
    3.     leal (%edx,%ebx),%eax ;Compute t = val + nval
    4.     movl %edx,%ebx        ;copy nval to val
    5.     movl %eax,%edx        ;Copy t to nval
    6.     incl %ecx             ;Increment i
    7.     cmpl %esi,%ecx        ;Compare i:n
    8.     jl .L6 If less,       ;goto loop
    9. //---------Corresponding assembly code------------

    while循环

    ========================

    while语句循环的通用形式是这样的

    1. while(test-expr)
    2.     body-statement

    与do-while的不同之处在于对test-expr求值, 在第一次执行body-statement之前, 循环就可能终止了. 翻译成goto语句的形式就是

    1. loop:
    2.     t= test-expr;
    3.     if (!t)
    4.         goto done;
    5.     body-statement
    6.         goto loop;
    7. done:

    这种翻译需要在内循环(也就是执行次数最多的代码部分)中, 有两条goto语句. 大多数的编译器将这段代码转换成do-while循环, 把一个条件分支语句从循环体中拿到外面来.

    1.     if (!test-expr)
    2.         goto done;
    3.     do
    4.     body-statement
    5.         while (test-expr);
    6. done:

    然后, 再把这段代码换成带goto的语句的代码, 如下

    1.     t= test-expr;
    2.     if (!t)
    3.         goto done;
    4. loop:
    5.     body-statement
    6.         t= test-expr;
    7.     if (t)
    8.         goto loop;
    9. done:

    for循环

    ========================

    for循环的通用形式是这样的:

    1. for (init-expr; test-expr; update-expr)
    2.     body-statement

    C语言的标准说明, 这样的一个循环的行为与下面这段使用while循环的代码的行为一样:

    1. init-expr;
    2. while (test-expr){
    3.     body-statement
    4.     update-expr;
    5. }

    然后再用前面讲过的从while到do-while的转换. 首先给出do-while形式

    1.     init-expr;
    2.     if (!test-expr)
    3.         goto done;
    4.     do{
    5.         body-statement
    6.         update-expr;
    7.     }while (test-expr);
    8. done:

    再转换成goto代码

    1.     init-expr;
    2.     t= test-expr;
    3.     if (!t)
    4.         goto done;
    5. loop:
    6.     body-statement
    7.         update-expr;
    8.     t= test-expr;
    9.     if (t)
    10.         goto loop;
    11. done:

    相信现在, 你已经对汇编中的循环指令簇有点模式的感觉了吧? 呵呵. 我们再来看一个switch语句, 然后收工.

    switch语句

    ======================

    switch语句提供了一个整数索引值, 通过它来进行多重分支. 那么switch语句和一组很长的if-else语句相比, 有什么优势呢? 我先把答案说出来, 然后看看汇编, 就知道了.

    优势就是: 执行开关语句的时间与开关情况的数量无关.

    能做到这样的原因是跳转表. 跳转表是一个数组, 表项i是一个代码段的地址, 这个代码段实现的就是开关索引值等于i的时候应该采取的动作.

    让我们来看一个例子, 这个例子包含一些很有意思的特征, 情况标号(case label)不连续, 比如101, 105; 一个情况有多个标号, 比如104, 106; 有些情况会落入其他情况(102), 因为该情况没有用break结尾.

    1. //----------Original C code------------
    2. int switch_eg(int x)
    3. {
    4.     int result = x;
    5.  
    6.     switch (x) {
    7.  
    8.         case 100:
    9.             result *= 13;
    10.             break;
    11.  
    12.         case 102:
    13.             result += 10;
    14.             /* Fall through */
    15.  
    16.         case 103:
    17.             result += 11;
    18.             break;
    19.  
    20.         case 104:
    21.         case 106:
    22.             result *= result;
    23.             break;
    24.  
    25.         default:
    26.             result = 0;
    27.     }
    28.  
    29.     return result;
    30. }
    31. //----------Original C code------------

    说明问题的C的伪代码

    1. /* Next line is not legal C */
    2. code *jt[7] = {
    3.     loc_A, loc_def, loc_B, loc_C,
    4.     loc_D, loc_def, loc_D
    5. };
    6. int switch_eg_impl(int x)
    7. {
    8.     unsigned xi = x - 100;
    9.     int result = x;
    10.     if (xi > 6)
    11.         goto loc_def;
    12.     /* Next goto is not legal C */
    13.     goto jt[xi];
    14. loc_A: /* Case 100 */
    15.     result *= 13;
    16.     goto done;
    17. loc_B: /* Case 102 */
    18.     result += 10;
    19.     /* Fall through */
    20. loc_C: /* Case 103 */
    21.     result += 11;
    22.     goto done;
    23. loc_D: /* Cases 104, 106 */
    24.     result *= result;
    25.     goto done;
    26. loc_def: /* Default case*/
    27.     result = 0;
    28. done:
    29.     return result;
    30. }
    1. //----------Corresponding assembly code------------
    2. //***********
    3. // Code that Set up the jump table access
    4. //***********
    5.     leal -100(%edx),%eax         ;Compute xi = x-100
    6.     cmpl $6,%eax                 ;Compare xi:6
    7.     ja .L9                       ;if >, goto done
    8.     jmp *.L10(,%eax,4)           ;Goto jt[xi]
    9. //Case 100
    10. L4:                              ;loc A:
    11.     leal (%edx,%edx,2),%eax      ;Compute 3*x
    12.     leal (%edx,%eax,4),%edx      ;Compute x+4*3*x
    13.     jmp .L3                      ;Goto done
    14. //Case 102
    15. L5:                              ;loc B:
    16.     addl $10,%edx                ;result += 10, Fall through
    17. //Case 103
    18. L6:                              ;loc C:
    19.     addl $11,%edx                ;result += 11
    20.     jmp .L3                      ;Goto done
    21. //Cases 104, 106
    22. L8:                              ;loc D:
    23.     imull %edx,%edx              ;result *= result
    24.     jmp .L3                      ;Goto done
    25. //Default case
    26. L9:                              ;loc def:
    27.     xorl %edx,%edx               ;result = 0
    28. //Return result
    29. L3:                              ;done:
    30.     movl %edx,%eax               ;Set result as return value
    31. //----------Corresponding assembly code------------

    参考资料<深入理解计算机系统>

  • 相关阅读:
    哈希表(hash)
    并查集
    trie树(字典树)
    单调队列(滑动窗口)
    单调栈
    用数组实现栈与队列
    数组实现双链表
    数组实现单链表
    区间合并
    离散化
  • 原文地址:https://www.cnblogs.com/awpatp/p/1600792.html
Copyright © 2020-2023  润新知