美文网首页
C语言到汇编-指针与数组2

C语言到汇编-指针与数组2

作者: 故事观察日记 | 来源:发表于2020-04-16 21:53 被阅读0次

    本章剩下的内容主要有:

    1.指针与数组 & 地址算术运算
    2.字符指针与函数
    3.多维数组 & 指针与多维数组
    4.指向函数的指针
    

    每个内容举一个例子,第一个例子:

    int a[10] = {1,2,3,4,5,6};
    int *pa = &a[0];
    
    main(){ 
        pa = &a[0]; 
        pa = a;
        int x = *pa;
        int i;
        for(i=0; i<5; i++){
            printf("%d", a[i]);
            printf("%d", pa[i]);
            printf("%d", *(pa+i));
        }
    }
    

    指针类型的pa 指向了a[0],即pa 的值是a[0]的地址。数组变量a 的值也是数组的首地址,所以pa 的两种赋值方式完全相同。x 的值是a[0],即1。pa[i]*(pa+i)指向的也是同一个元素。对照编译后的汇编代码,如下:

    .globl _a
        .data
    _a:
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .space 16
    .globl _pa
    _pa:
        .long   _a
        .section .rdata,"dr"
    LC0:
        .ascii "%d\0"
        .text
    .globl _main
    _main:
        mov DWORD PTR _pa, OFFSET FLAT:_a
        mov DWORD PTR _pa, OFFSET FLAT:_a
        mov eax, DWORD PTR _pa
        mov eax, DWORD PTR [eax]
        mov DWORD PTR [ebp-4], eax
        mov DWORD PTR [ebp-8], 0
    L2:
        cmp DWORD PTR [ebp-8], 4
        jg  L3
        mov eax, DWORD PTR [ebp-8]
        mov eax, DWORD PTR _a[0+eax*4]
        mov DWORD PTR [esp+4], eax
        mov DWORD PTR [esp], OFFSET FLAT:LC0
        call    _printf
        mov eax, DWORD PTR [ebp-8]
        lea edx, [0+eax*4]
        mov eax, DWORD PTR _pa
        mov eax, DWORD PTR [edx+eax]
        mov DWORD PTR [esp+4], eax
        mov DWORD PTR [esp], OFFSET FLAT:LC0
        call    _printf
        mov eax, DWORD PTR [ebp-8]
        lea edx, [0+eax*4]
        mov eax, DWORD PTR _pa
        mov eax, DWORD PTR [edx+eax]
        mov DWORD PTR [esp+4], eax
        mov DWORD PTR [esp], OFFSET FLAT:LC0
        call    _printf
        lea eax, [ebp-8]
        inc DWORD PTR [eax]
        jmp L2
    L3:
        leave
        ret
    

    可以看到_pa 的值就是标号_a ,而后面两个printf 的代码也完全一样。
    第二个例子,字符指针与函数:

    char amessage[] = "now is the time"; /* 定义一个数组 */
    char *pmessage = "now is the time"; /* 定义一个指针 */
    char *b[] = {"xxx","yyy"};
    main(){
        char *a[] = {amessage,pmessage};
    }
    

    编译后的汇编代码:

    .globl _amessage
        .data
    _amessage:
        .ascii "now is the time\0"
        .section .rdata,"dr"
    LC0:
        .ascii "now is the time\0"
    .globl _pmessage
        .data
    _pmessage:
        .long   LC0
        .section .rdata,"dr"
    LC1:
        .ascii "xxx\0"
    LC2:
        .ascii "yyy\0"
    .globl _b
        .data
    _b:
        .long   LC1
        .long   LC2
        .text
    .globl _main
    _main:
        mov DWORD PTR [ebp-8], OFFSET FLAT:_amessage
        mov eax, DWORD PTR _pmessage
        mov DWORD PTR [ebp-4], eax
        leave
        ret
    
    

    变量ab 是指针数组,在汇编代码中与其他变量其实并无不同,在函数内部都是一个内存空间,在函数外部都是一个标号。
    第三个例子,指针与多维数组:

    static char daytab[2][13] = { 
     {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
     {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} 
     };
     
    int a[10][20]; 
    int *w[10];
     
     main(){
         int a = daytab[1][10];
         f(daytab);
     }
     
     f(char daytab[][13])
     {
         char b = daytab[1][10];
     }
    

    编译后的汇编代码:

        .data
    _daytab:
        .byte   0
        .byte   31
        .byte   28
        .byte   31
        .byte   30
        .byte   31
        .byte   30
        .byte   31
        .byte   31
        .byte   30
        .byte   31
        .byte   30
        .byte   31
        .byte   0
        .byte   31
        .byte   29
        .byte   31
        .byte   30
        .byte   31
        .byte   30
        .byte   31
        .byte   31
        .byte   30
        .byte   31
        .byte   30
        .byte   31
        .text
    .globl _main
    _main:
        movsx   eax, BYTE PTR _daytab+23
        mov DWORD PTR [ebp-4], eax
        mov DWORD PTR [esp], OFFSET FLAT:_daytab
        call    _f
        leave
        ret
    .globl _f
    _f:
        push    ebp
        mov ebp, esp
        sub esp, 4
        mov eax, DWORD PTR [ebp+8]
        add eax, 23
        movzx   eax, BYTE PTR [eax]
        mov BYTE PTR [ebp-1], al
        leave
        ret
        .comm   _a, 800  # 800
        .comm   _w, 48   # 40
    

    可以发现,汇编代码中并不区分一维还是二维数组,而是调用时根据行和列的值计算出具体位置。
    最后一个例子,指向函数的指针:

     int f(int i){
         return i+1;
     }
     
     int w(int i){
         return i+2;
     }
     
    int useforw(int (*x)(int));
     
     main(){
         int (*pf)(int);
         pf = f;
         int a = 1;
         int b = 2;
         b = (int (*)(int)) a;
         useforw(f);
         useforw(w);
         useforw((int (*)(int))(1+1==2 ? f : w));
     }
     
     int useforw(int (*x)(int)){
         int y = (*x)(9);
     }
    

    main 函数上面定义了两个函数fw ,省去了声明。useforw 函数的参数是一个指向函数的指针,也即函数指针。函数指针其实就是指向了函数代码的首地址,而函数名也是代码的首地址,这和数组很类似,在汇编代码中,数组名和函数名都是一个标号。
    main 函数中第一行:

         int (*pf)(int);
    

    声明了一个函数指针变量,变量名为pf ,类型为int (*)(int)。接着把函数f 的地址赋给了pf 。其实不管pf 是什么类型,int 型还是看起来很复杂的指针型,在汇编代码中都只是一个没有姓名的内存空间而已。
    后面的代码将变量a 强制转换为int (*)(int)类型,也就是函数指针类型,这种写法是可以的,但是不能用(int (*)(int)) a;这种方式来将a 声明为此种类型的变量。
    编译后的汇编代码如下:

        .text
    .globl _f
    _f:
        push    ebp
        mov ebp, esp
        mov eax, DWORD PTR [ebp+8]
        inc eax
        pop ebp
        ret
    .globl _w
    _w:
        push    ebp
        mov ebp, esp
        mov eax, DWORD PTR [ebp+8]
        add eax, 2
        pop ebp
        ret
    .globl _main
    _main:
        mov DWORD PTR [ebp-4], OFFSET FLAT:_f
        mov DWORD PTR [ebp-8], 1
        mov DWORD PTR [ebp-12], 2
        mov eax, DWORD PTR [ebp-8]
        mov DWORD PTR [ebp-12], eax
        mov DWORD PTR [esp], OFFSET FLAT:_f
        call    _useforw
        mov DWORD PTR [esp], OFFSET FLAT:_w
        call    _useforw
        mov DWORD PTR [esp], OFFSET FLAT:_f
        call    _useforw
        leave
        ret
    .globl _useforw
    _useforw:
        push    ebp
        mov ebp, esp
        sub esp, 8
        mov DWORD PTR [esp], 9
        mov eax, DWORD PTR [ebp+8]
        call    eax
        mov DWORD PTR [ebp-4], eax
        leave
        ret
    

    可以看到,函数指针作为参数时,虽然写法看起来复杂,实际只是把函数名/标号的地址传递了过去。
    好了,第五章就到这里,下一篇开始学习第六章——结构。

    相关文章

      网友评论

          本文标题:C语言到汇编-指针与数组2

          本文链接:https://www.haomeiwen.com/subject/hmsdvhtx.html