林少华 失乐园:从汇编的角度分析C语言(一)char *p与char p[]的区别

来源:百度文库 编辑:偶看新闻 时间:2024/05/01 07:10:54

C语言代码:

view plaincopy to clipboardprint?
  1. #include    
  2. #include    
  3.   
  4. void main()  
  5. {  
  6.     char array1[] = { "123456" };  
  7.     char *pointer1 = "123456";  
  8. }  

汇编代码:

view plaincopy to clipboardprint?
  1. (gdb) disassemble main   
  2. Dump of assembler code for function main:  
  3.    0x08048394 <+0>:     push   %ebp  
  4.    0x08048395 <+1>:     mov    %esp,%ebp  
  5.    0x08048397 <+3>:     sub    $0x10,%esp  
  6.    0x0804839a <+6>:     movl   $0x34333231,-0xb(%ebp)  
  7.    0x080483a1 <+13>:    movw   $0x3635,-0x7(%ebp)  
  8.    0x080483a7 <+19>:    movb   $0x0,-0x5(%ebp)  
  9.    0x080483ab <+23>:    movl   $0x8048484,-0x4(%ebp)  
  10.    0x080483b2 <+30>:    leave    
  11.    0x080483b3 <+31>:    ret      
  12. End of assembler dump.  
  13. (gdb) x/7xb 0x8048484  
  14. 0x8048484 <__dso_handle+4>:     0x31    0x32    0x33    0x34    0x35    0x36    0x00  
  15. (gdb)   

从<+6><+13><+19>三行可以看出,程序为array1分配了7bytes的内存空间,用来存储“123456”+‘\0’。

而<+23>行表示将地址0x8048484赋给了pointer1,我们可以查看内存0x8048484之后内容,7bytes正好为“123456”+‘\0’,这里pointer1只是一个指针,并没有为其分配内存单元。
那么下面的这段代码就不难理解了。

view plaincopy to clipboardprint?
  1. #include    
  2. #include    
  3.   
  4. void main()  
  5. {  
  6.     char array1[] = { "123456" };  
  7.     char array2[] = { "123456" };  
  8.     char *pointer1 = "123456";  
  9.     char *pointer2 = "123456";  
  10. }  
汇编代码:

view plaincopy to clipboardprint?
  1. (gdb) disassemble main   
  2. Dump of assembler code for function main:  
  3.    0x08048394 <+0>:     push   %ebp  
  4.    0x08048395 <+1>:     mov    %esp,%ebp  
  5.    0x08048397 <+3>:     sub    $0x20,%esp  
  6.    0x0804839a <+6>:     movl   $0x34333231,-0xf(%ebp)  
  7.    0x080483a1 <+13>:    movw   $0x3635,-0xb(%ebp)  
  8.    0x080483a7 <+19>:    movb   $0x0,-0x9(%ebp)  
  9.    0x080483ab <+23>:    movl   $0x34333231,-0x16(%ebp)  
  10.    0x080483b2 <+30>:    movw   $0x3635,-0x12(%ebp)  
  11.    0x080483b8 <+36>:    movb   $0x0,-0x10(%ebp)  
  12.    0x080483bc <+40>:    movl   $0x8048494,-0x4(%ebp)  
  13.    0x080483c3 <+47>:    movl   $0x8048494,-0x8(%ebp)  
  14.    0x080483ca <+54>:    leave    
  15.    0x080483cb <+55>:    ret      
  16. End of assembler dump.  
  17. (gdb) x/7xb 0x8048494  
  18. 0x8048494 <__dso_handle+4>:     0x31    0x32    0x33    0x34    0x35    0x36    0x00  
  19. (gdb)  

接着这段代码也就顺其自然了! view plaincopy to clipboardprint?
  1. (gdb) disassemble  main   
  2. Dump of assembler code for function main:  
  3.    0x08048394 <+0>:     push   %ebp  
  4.    0x08048395 <+1>:     mov    %esp,%ebp  
  5.    0x08048397 <+3>:     sub    $0x20,%esp  
  6.    0x0804839a <+6>:     movl   $0x34333231,-0xf(%ebp)  
  7.    0x080483a1 <+13>:    movw   $0x3635,-0xb(%ebp)  
  8.    0x080483a7 <+19>:    movb   $0x0,-0x9(%ebp)  
  9.    0x080483ab <+23>:    movl   $0x34333231,-0x16(%ebp)  
  10.    0x080483b2 <+30>:    movw   $0x3635,-0x12(%ebp)  
  11.    0x080483b8 <+36>:    movb   $0x0,-0x10(%ebp)  
  12.    0x080483bc <+40>:    movl   $0x8048494,-0x4(%ebp)  
  13.    0x080483c3 <+47>:    movl   $0x804849b,-0x8(%ebp)  
  14.    0x080483ca <+54>:    leave    
  15.    0x080483cb <+55>:    ret      
  16. End of assembler dump.  
  17. (gdb) x/7xb 0x8048494  
  18. 0x8048494 <__dso_handle+4>:     0x31    0x32    0x33    0x34    0x35    0x36    0x00  
  19. (gdb) x/9xb 0x804849b  
  20. 0x804849b <__dso_handle+11>:    0x31    0x32    0x33    0x34    0x35    0x36    0x37    0x38  
  21. 0x80484a3 <__dso_handle+19>:    0x00  
  22. (gdb)   

 

 

 

 

 

从汇编的角度分析C语言(二)gcc代码的优化

  1. #include    
  2. #include    
  •   
  • int main()  
  • {  
  •     int m = 1;  
  •     int n = 2;  
  •     int r = m + n;  
  •     return r;  
  • }  
  • 未使用优化:

    [root@ShiGuang test]# gcc -S main.c

    view plaincopy to clipboardprint?

    1.     .file   "main.c"  
    2.     .text  
    3. .globl main  
    4.     .type   main, @function  
    5. main:  
    6.     pushl   %ebp  
    7.     movl    %esp, %ebp  
    8.     subl    $16, %esp  
    9.     movl    $1, -4(%ebp)  
    10.     movl    $2, -8(%ebp)  
    11.     movl    -8(%ebp), %eax  
    12.     movl    -4(%ebp), %edx  
    13.     leal    (%edx,%eax), %eax  
    14.     movl    %eax, -12(%ebp)  
    15.     movl    -12(%ebp), %eax  
    16.     leave  
    17.     ret  
    18.     .size   main, .-main  
    19.     .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"  
    20.     .section    .note.GNU-stack,"",@progbits  

    优化处理:

    [root@ShiGuang test]# gcc -S main.c -O2

    view plaincopy to clipboardprint?

    1. .file   "main.c"  
    2. .text  
    3. .p2align 4,,15  
    4. globl main  
    5. .type   main, @function  
    6. ain:  
    7. pushl   %ebp  
    8. movl    $3, %eax  
    9. movl    %esp, %ebp  
    10. popl    %ebp  
    11. ret  
    12. .size   main, .-main  
    13. .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"  
    14. .section    .note.GNU-stack,"",@progbits  

    优化结果还是很明显的!gcc对常数的合并与常数的传播,具有较好的优化处理。

    我们从上面的两段汇编代码就能看出。


     

    从汇编的角度分析C语言(三)指针的赋值

    1. #include    
    2. #include    
  •   
  • int main()  
  • {  
  •     int m = 1;  
  •     int * p;  
  •     int * q;  
  •     p = &m;  
  •     q = p;  
  •     *p = 2;  
  •     *q = 3;  
  •     return 0;  
  • }  
  • view plaincopy to clipboardprint?
    1. (gdb) disassemble main   
    2. Dump of assembler code for function main:  
    3.    0x08048394 <+0>:     push   %ebp  
    4.    0x08048395 <+1>:     mov    %esp,%ebp  
    5.    0x08048397 <+3>:     sub    $0x10,%esp  
    view plaincopy to clipboardprint?
    1. 0x0804839a <+6>:     movl   $0x1,-0xc(%ebp)  
    view plaincopy to clipboardprint?
    1. 0x080483a1 <+13>:    lea    -0xc(%ebp),%eax  
    2. 0x080483a4 <+16>:    mov    %eax,-0x4(%ebp)  
    view plaincopy to clipboardprint?
    1. 0x080483a7 <+19>:    mov    -0x4(%ebp),%eax  
    2. 0x080483aa <+22>:    mov    %eax,-0x8(%ebp)  
    view plaincopy to clipboardprint?
    1. 0x080483ad <+25>:    mov    -0x4(%ebp),%eax  
    2. 0x080483b0 <+28>:    movl   $0x2,(%eax)  
    view plaincopy to clipboardprint?
    1. 0x080483b6 <+34>:    mov    -0x8(%ebp),%eax  
    2. 0x080483b9 <+37>:    movl   $0x3,(%eax)  
    view plaincopy to clipboardprint?
    1. 0x080483bf <+43>:    mov    $0x0,%eax  
    view plaincopy to clipboardprint?
    1.    0x080483c4 <+48>:    leave    
    2.    0x080483c5 <+49>:    ret      
    3. End of assembler dump.  
    4. (gdb)   


       从汇编的角度分析C语言(四)函数的调用过程
    1. #include    
    2. #include    
    3.   
    4. int add(int a, int b)  
    5. {  
    6.   int c;  
    7.   c = a;  
    8.   c += b;  
    9.   return c;  
    10. }  
    11.   
    12. int main()  
    13. {  
    14.     int a, b, c;  
    15.     a = 0x12;  
    16.     b = 0x34;  
    17.     c = add(a, b);  
    18.     return 0;  
    19. }  
    main.s

    view plaincopy to clipboardprint?

    1.     .file   "main.c"  
    2.     .text  
    3. .globl add  
    4.     .type   add, @function  
    5. add:  
    6.     pushl   %ebp  
    7.     movl    %esp, %ebp  
    8.     subl    $16, %esp  
    9.     movl    8(%ebp), %eax  
    10.     movl    %eax, -4(%ebp)  
    11.     movl    12(%ebp), %eax  
    12.     addl    %eax, -4(%ebp)  
    13.     movl    -4(%ebp), %eax  
    14.     leave  
    15.     ret  
    16.     .size   add, .-add  
    17. .globl main  
    18.     .type   main, @function  
    19. main:  
    20.     pushl   %ebp  
    21.     movl    %esp, %ebp  
    22.     subl    $24, %esp  
    23.     movl    $18, -4(%ebp)  
    24.     movl    $52, -8(%ebp)  
    25.     movl    -8(%ebp), %eax  
    26.     movl    %eax, 4(%esp)  
    27.     movl    -4(%ebp), %eax  
    28.     movl    %eax, (%esp)  
    29.     call    add  
    30.     movl    %eax, -12(%ebp)  
    31.     movl    $0, %eax  
    32.     leave  
    33.     ret  
    34.     .size   main, .-main  
    35.     .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"  
    36.     .section    .note.GNU-stack,"",@progbits  
    gdb调试:

    view plaincopy to clipboardprint?

    1. (gdb) disassemble main  
    2. Dump of assembler code for function main:  
    3.    0x080483ab <+0>:     push   %ebp  
    4.    0x080483ac <+1>:     mov    %esp,%ebp  
    5.    0x080483ae <+3>:     sub    $0x18,%esp  
    6.    0x080483b1 <+6>:     movl   $0x12,-0x4(%ebp)  
    7.    0x080483b8 <+13>:    movl   $0x34,-0x8(%ebp)  
    8.    0x080483bf <+20>:    mov    -0x8(%ebp),%eax  
    9.    0x080483c2 <+23>:    mov    %eax,0x4(%esp)  
    10.    0x080483c6 <+27>:    mov    -0x4(%ebp),%eax  
    11.    0x080483c9 <+30>:    mov    %eax,(%esp)  
    12.    0x080483cc <+33>:    call   0x8048394   
    13.    0x080483d1 <+38>:    mov    %eax,-0xc(%ebp)  
    14.    0x080483d4 <+41>:    mov    $0x0,%eax  
    15.    0x080483d9 <+46>:    leave    
    16.    0x080483da <+47>:    ret      
    17. End of assembler dump.  
    18. (gdb) list  
    19. 6         int c;  
    20. 7         c = a;  
    21. 8         c += b;  
    22. 9         return c;  
    23. 10      }  
    24. 11  
    25. 12      int main()  
    26. 13      {  
    27. 14          int a, b, c;  
    28. 15          a = 0x12;  
    29. 16          b = 0x34;  
    30. 17          c = add(a, b);  
    31. 18          return 0;  
    32. 19      }  
    33. (gdb) break 17  
    34. Breakpoint 1 at 0x80483bf: file main.c, line 17.  
    35. (gdb) run  
    36. Starting program: /home/my_project/test/main   
    37.   
    38. Breakpoint 1, main () at main.c:17  
    39. 17          c = add(a, b);  
    40. Missing separate debuginfos, use: debuginfo-install glibc-2.12.90-17.i686  
    41. (gdb) info registers esp  
    42. esp            0xbffff1e0       0xbffff1e0  
    43. (gdb) info registers ebp  
    44. ebp            0xbffff1f8       0xbffff1f8  
    45. (gdb) x/6xw 0xbffff1e0  
    46. 0xbffff1e0:     0x080483e0      0x080482e0      0x080483eb      0x00394ff4  
    47. 0xbffff1f0:     0x00000034      0x00000012  
    48. (gdb) step  
    49. add (a=18b=52) at main.c:7  
    50. 7         c = a;  
    51. (gdb) x/6xw 0xbffff1e0  
    52. 0xbffff1e0:     0x00000012      0x00000034      0x080483eb      0x00394ff4  
    53. 0xbffff1f0:     0x00000034      0x00000012  
    54. (gdb) info registers esp  
    55. esp            0xbffff1c8       0xbffff1c8  
    56. (gdb) info registers ebp  
    57. ebp            0xbffff1d8       0xbffff1d8  
    58. (gdb) tep  
    59. Undefined command: "tep".  Try "help".  
    60. (gdb) step  
    61. 8         c += b;  
    62. (gdb) step  
    63. 9         return c;  
    64. (gdb) x/16xw 0xbffff1c0  
    65. 0xbffff1c0:     0x00235f95      0x08049624      0xbffff1f8      0x080483f9  
    66. 0xbffff1d0:     0x08048215      0x00000046      0xbffff1f8      0x080483d1  
    67. 0xbffff1e0:     0x00000012      0x00000034      0x080483eb      0x00394ff4  
    68. 0xbffff1f0:     0x00000034      0x00000012      0xbffff278      0x0021ce16  
    69. (gdb)step  
    70. 10      }  
    71. (gdb) step  
    72. main () at main.c:18  
    73. 18          return 0;  
    74. (gdb) info registers esp  
    75. esp            0xbffff1e0       0xbffff1e0  
    76. (gdb) info registers ebp  
    77. ebp            0xbffff1f8       0xbffff1f8  
    78. (gdb)   
    具体堆栈情况如下图所示:


    红色是main函数分配的堆栈,绿色是add函数分配的堆栈
          从汇编的角度分析C语言(五)函数指针与指针的指针

    函数指针:

    view plaincopy to clipboardprint?
    1. #include    
    2. #include    
    3.   
    4. void add()  
    5. {  
    6.   printf("12\n");  
    7. }  
    8.   
    9. int main()  
    10. {  
    11.     int * func;  
    12.     func = (int *)add;  
    13.     return 0;  
    14. }  
    view plaincopy to clipboardprint?
    1.     .file   "main.c"  
    2.     .section    .rodata  
    3. .LC0:  
    4.     .string "12"  
    5.     .text  
    6. .globl add  
    7.     .type   add, @function  
    8. add:  
    9.     pushl   %ebp  
    10.     movl    %esp, %ebp  
    11.     subl    $24, %esp  
    12.     movl    $.LC0, (%esp)  
    13.     call    puts  
    14.     leave  
    15.     ret  
    16.     .size   add, .-add  
    17. .globl main  
    18.     .type   main, @function  
    19. main:  
    20.     pushl   %ebp  
    21.     movl    %esp, %ebp  
    22.     subl    $16, %esp  
    23.     movl    $add, -4(%ebp)  
    24.     movl    $0, %eax  
    25.     leave  
    26.     ret  
    27.     .size   main, .-main  
    28.     .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"  
    29.     .section    .note.GNU-stack,"",@progbits  
    view plaincopy to clipboardprint?
    1. (gdb) disassemble main   
    2. Dump of assembler code for function main:  
    3.    0x080483c8 <+0>:     push   %ebp  
    4.    0x080483c9 <+1>:     mov    %esp,%ebp  
    5.    0x080483cb <+3>:     sub    $0x10,%esp  
    6.    0x080483ce <+6>:     movl   $0x80483b4,-0x4(%ebp)  
    7.    0x080483d5 <+13>:    mov    $0x0,%eax  
    8.    0x080483da <+18>:    leave    
    9.    0x080483db <+19>:    ret      
    10. End of assembler dump.  
    11. (gdb) p add  
    12. $1 = { variable, no debug info>} 0x80483b4   
    13. (gdb)   

    指针的指针:

    view plaincopy to clipboardprint?
    1. #include    
    2. #include    
    3.   
    4. void main()  
    5. {  
    6.     int a = 0x12;  
    7.     int *p1 = &a;  
    8.     int **p2 = &p1;  
    9.     int ***p3 = &p3;  
    10. }  
    view plaincopy to clipboardprint?
    1. (gdb) disassemble main   
    2. Dump of assembler code for function main:  
    3.    0x08048394 <+0>:     push   %ebp  
    4.    0x08048395 <+1>:     mov    %esp,%ebp  
    5.    0x08048397 <+3>:     sub    $0x10,%esp  
    6.    0x0804839a <+6>:     movl   $0x12,-0x8(%ebp)  
    7.    0x080483a1 <+13>:    lea    -0x8(%ebp),%eax  
    8.    0x080483a4 <+16>:    mov    %eax,-0xc(%ebp)  
    9.    0x080483a7 <+19>:    lea    -0xc(%ebp),%eax  
    10.    0x080483aa <+22>:    mov    %eax,-0x4(%ebp)  
    11.    0x080483ad <+25>:    lea    -0x10(%ebp),%eax  
    12.    0x080483b0 <+28>:    mov    %eax,-0x10(%ebp)  
    13.    0x080483b3 <+31>:    leave    
    14.    0x080483b4 <+32>:    ret      
    15. End of assembler dump.  
    16. (gdb)   


        从汇编的角度分析C语言(六)switch和if else效率分析

    switch语句分析:

    view plaincopy to clipboardprint?

    1. #include    
    2. #include    
    3.   
    4. void main()  
    5. {  
    6.     int m = 0x12;  
    7.     switch(m)  
    8.     {  
    9.       case 0x12:   
    10.     m = m + 1;  
    11.     break;  
    12.       case 0x13:  
    13.     m = m + 2;  
    14.     break;  
    15.       default:  
    16.     break;  
    17.     }  
    18. }  
    view plaincopy to clipboardprint?
    1. (gdb) disassemble main   
    2. Dump of assembler code for function main:  
    3.    0x08048394 <+0>:     push   %ebp  
    4.    0x08048395 <+1>:     mov    %esp,%ebp  
    5.    0x08048397 <+3>:     sub    $0x10,%esp  
    6.    0x0804839a <+6>:     movl   $0x12,-0x4(%ebp)  
    7.    0x080483a1 <+13>:    mov    -0x4(%ebp),%eax  
    8.    0x080483a4 <+16>:    cmp    $0x12,%eax  
    9.    0x080483a7 <+19>:    je     0x80483b0 +28>  
    10.    0x080483a9 <+21>:    cmp    $0x13,%eax  
    11.    0x080483ac <+24>:    je     0x80483b6 +34>  
    12.    0x080483ae <+26>:    jmp    0x80483bb +39>  
    13.    0x080483b0 <+28>:    addl   $0x1,-0x4(%ebp)  
    14.    0x080483b4 <+32>:    jmp    0x80483bb +39>  
    15.    0x080483b6 <+34>:    addl   $0x2,-0x4(%ebp)  
    16.    0x080483ba <+38>:    nop  
    17.    0x080483bb <+39>:    leave    
    18.    0x080483bc <+40>:    ret      
    19. End of assembler dump.  
    20. (gdb)   
    if else语句分析:

    view plaincopy to clipboardprint?

    1. #include    
    2. #include    
    3.   
    4. void main()  
    5. {  
    6.     int m = 0x12;  
    7.     if( m == 0x12)   
    8.     m = m + 1;  
    9.     else if( m == 0x12)   
    10.     m = m + 1;  
    11.     else  
    12.       ;  
    13. }  
    view plaincopy to clipboardprint?
    1. (gdb) disassemble main   
    2. Dump of assembler code for function main:  
    3.    0x08048394 <+0>:     push   %ebp  
    4.    0x08048395 <+1>:     mov    %esp,%ebp  
    5.    0x08048397 <+3>:     sub    $0x10,%esp  
    6.    0x0804839a <+6>:     movl   $0x12,-0x4(%ebp)  
    7.    0x080483a1 <+13>:    cmpl   $0x12,-0x4(%ebp)  
    8.    0x080483a5 <+17>:    jne    0x80483ad +25>  
    9.    0x080483a7 <+19>:    addl   $0x1,-0x4(%ebp)  
    10.    0x080483ab <+23>:    jmp    0x80483b7 +35>  
    11.    0x080483ad <+25>:    cmpl   $0x12,-0x4(%ebp)  
    12.    0x080483b1 <+29>:    jne    0x80483b7 +35>  
    13.    0x080483b3 <+31>:    addl   $0x1,-0x4(%ebp)  
    14.    0x080483b7 <+35>:    leave    
    15.    0x080483b8 <+36>:    ret      
    16. End of assembler dump.  
    17. (gdb)  
    我们可以看到,switch和if else的执行效率差不多。