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

2020-04-15  本文已影响0人  故事观察日记

指针是一种保存变量地址的变量。下面代码声明了指针变量p ,它的值是x 的地址:

int x = 1;
int *p = &x;

一元运算符&可用于取一个对象的地址。一元运算符*是间接寻址或间接引用运算符,当它作用于指针时,将访问指针所指向的对象。
下面看一个示例代码:

/*
1.指针的声明与定义
*/

int z = 3;
int *q = &z;
main(){ 
    int x = 1;
    int y = 2; 
    int *ip = &x;
    y = *ip;  /* 现在 y=1 */
    *ip = 0;  /* 现在 x=0 */      
}

编译后的汇编相关代码如下:

.globl _z
    .data
_z:
    .long   3
.globl _q
_q:
    .long   _z
    .text
_main:
    push    ebp
    mov ebp, esp
    sub esp, 24
    mov DWORD PTR [ebp-4], 1
    mov DWORD PTR [ebp-8], 2
    lea eax, [ebp-4]
    mov DWORD PTR [ebp-12], eax
    mov eax, DWORD PTR [ebp-12]
    mov eax, DWORD PTR [eax]
    mov DWORD PTR [ebp-8], eax
    mov eax, DWORD PTR [ebp-12]
    mov DWORD PTR [eax], 0
    leave
    ret

可以看到,外部变量z 被编译成了一个标号_z ,标号后面是它的值。而外部指针变量q 也被编译成一个标号_q ,它的值就是标号_z。也就是说,指针变量与普通变量编译后名字看起来没什么区别,都是一个标号或地址,只不过指针变量中存的是另一个地址。
lea 指令指的是加载有效地址(load effective address),与mov 指令进行对比:

lea eax, [ebp-4]
mov eax, [ebp-4]

第一行的lea 指令是将[ebp-4] 的地址也就是[ebp-4] 本身的值赋给了eax ;
第二行的mov 指令则是将[ebp-4] 地址的内存单元中存储的值赋给了eax。
所以以下两行代码即是原代码中的“int *ip = &x;” 语句:

lea eax, [ebp-4]
mov DWORD PTR [ebp-12], eax

这两行代码执行后,栈内存中各变量的值参考以下表格3:


表格3

接下来执行代码:

    mov eax, DWORD PTR [ebp-12]
    mov eax, DWORD PTR [eax]
    mov DWORD PTR [ebp-8], eax
    mov eax, DWORD PTR [ebp-12]
    mov DWORD PTR [eax], 0

第1行代码执行后,寄存器eax 的值为[ebp-12] 地址中存的值,对照表格3,也就是ebp-4 ;
所以第2行代码中的[eax] 也就相当于[ebp-4] ,第2行代码执行后eax 的值为[ebp-4] 地址中存的值,即1;
第3行代码将eax 的值也就是1存入[ebp-8] 中,[ebp-8]之前存的是y 的值2,此行执行完值变为了1;
这3行代码完成了原代码中“y = * ip; ”语句的功能。
第4行代码与第1行相同,执行后eax 的值为ebp-4;
第5行代码中的[eax] 也就相当于[ebp-4],[ebp-4]之前存的是x 的值1,执行之后[ebp-4]的值变为0,所以变量x 的值也变成了0。
这2行代码完成了原代码中“*ip = 0;”语句的功能。
经过以上分析,明白了C语言的指针变量编译后汇编语言是如何实现的。因为汇编中也有lea 这种取地址的指令,所以通过汇编代码似乎并没有比直接理解C语言更深刻更本质,只是多加了一层翻译而已。就当是从另一个角度学习指针吧。
好了,这篇先写到这里,下一篇继续学习指针与数组的内容。

上一篇下一篇

猜你喜欢

热点阅读