从汇编的角度来看C语言标识符以及对数组名取地址为什么与数组名的值相同

我之前一直有一个疑问,对数组名取地址和数组名的值为什么是一样的。单纯听别人口头讲述这个东西其实我觉得还是有点难以理解,我觉得从汇编的角度来看待这个问题,非常好理解。

int main()
{
    int a = 1;
    int c[10] = {1,2,3,4,5,6};
    return 0;
}

上面这样的一段简单的代码,我们从汇编的角度来看一些,首先要说明的是,c语言中的标识符(也就是变量名),其实是没有地址空间的,也就是说他其实是不占空间的,只是他代表的值占据了空间。为什么叫标识符,因为他只是一个标识的作用,它就相当于一个地址,该地址的值是该标识符代表的是的地址。

就拿上面的a来说,我们画图进行理解

上面的a = 0xff1122并不是说a的值等于0xff1122,而是说a这个标识符是等同于这个地址值,他是一个替代作用,这一点需要理解。然后我们对变量a的访问对应到汇编上就等同于

MOV AL,[ff1122H]

就是将ff1122这个地址保存的值传送到AL寄存器中,所以说a这个标识符其实就是一个助计的作用,他代表的就是一个地址。

mov指令的作用就是将[]内的地址值位置中保存的值拿出来。

注意,汇编语言中其实也是有变量名的,为了避免这个问题继续循环下去,我们进行了简化,尽量站在机器的角度去看待问题。

你总不能让我给你写机器码吧。

CPU进行运算的时候是看不懂什么变量名的,对他来说一个指令中只能有操作数,而操作数具有不同的类型。

8086/8088的指令系统中的操作数主要有3种类型:立即数操作数(即常数)、寄存器操作数(如通用寄存器AX、段寄存器DS)和存储器操作数(存放在内存中的数据)。

所以我们写的变量名什么的,编译之后生成的机器码中没有这样的东西。

知道这些之后其实对于上面标识符的替代作用就能理解清楚了。

 

有了上面的基础之后,我们再来理解为什么数组名和数组名取地址的值相同。

我们现在也把数组名想象成标识符,他也是代表一个地址,但是这个地址与之前的地址是不同的,地址也是有类型的,其实也不能叫地址有类型,其实地址本身来说就是一个值,他的类型是由操作他的指令来确定的,操作他的指令在该地址上往后读几个位,就代表了该地址值所代表的类型。所以说,所有的标识符其实都是代表一个普通的地址值,但是对于该值的操作指令不同,也就展现出了不同的类型。

回到数组名,数组名代表的其实就是数组的首地址值,只是我们对于该地址值的指令与对上面a这个表示符所代表的地址值的指令不一样,就显示出了不同的结果。

上面我们说的a标识符等同于0xff1122,那么我们在程序中对a这个标识符取地址,其实就是将a标识符代表的地址给了你。

然后直接读取a的值的时候,就是读取a这个标识符代表的地址指向的值,然后根据不同的操作指令(不同的操作指令决定了指令要处理的数据有多长),从该地址开始往后读不同的位数。

看一段代码:然后我们利用GCC生成汇编代码,来看汇编是如何处理这段程序的

int a = 1;
int c[10] = {1,2,3,4,5,6};
printf("%d",a);
printf("%d",&a);
printf("%d",c);
printf("%d",&c);

 

生成的汇编代码,8,9这些有数字的行不用管,代表下面的汇编在c语言中代表的是哪一行,这只是codeBlock为了帮助我们对照代码进行的对应处理。 

;8  :	    printf("%d",a);
0x4013d2	mov    0x3c(%esp),%eax
0x4013d6	mov    %eax,0x4(%esp)
0x4013da	movl   $0x408024,(%esp)
0x4013e1	call   0x401350 <printf>
;9  :	    printf("%d",&a);
0x4013e6	lea    0x3c(%esp),%eax
0x4013ea	mov    %eax,0x4(%esp)
0x4013ee	movl   $0x408024,(%esp)
0x4013f5	call   0x401350 <printf>
;10 :	    printf("%d",c);
0x4013fa	lea    0x14(%esp),%eax
0x4013fe	mov    %eax,0x4(%esp)
0x401402	movl   $0x408024,(%esp)
0x401409	call   0x401350 <printf>
;11 :	    printf("%d",&c);
0x40140e	lea    0x14(%esp),%eax
0x401412	mov    %eax,0x4(%esp)
0x401416	movl   $0x408024,(%esp)
0x40141d	call   0x401350 <printf>

esp是一个指针寄存器,其内部的值指向栈顶的位置。0x3c就是偏移地址,最终操作的地址就是esp+0x3c。

lea可以将有效地址传送到指定的的寄存器,也就是将上面的esp+0x3c这个地址值传送到指定的寄存器。

mov 0x3c(%esp),%eax的意思就是将栈顶偏移0x3c位置的值保存到%eax寄存器中,我们就可以看出,直接输出a的时候,是将a这个标识符所代表的地址位置保存的值给输出。

lea 0x3c(%esp),%eax的意思就是将a标识符代表地址的偏移值保存到%eax寄存器中,也就是将a所代表的地址保存到%eax寄存器中,然后进行输出,最终输出的就是变量a的地址。

然后再看下面对于数组c的操作,我们发现我们直接输出a的时候,其也是用的lea指令,为什么,因为不能直接将c标识符所代表的地址位置的值进行输出,这是一个数组,他要怎么输出,这是有语义错误的,但是帮助我们进行了转换,也就是直接把c所代表的地址值进行了输出。

&c就好理解,c本来就代表的是这个数组,lea就是将数组的地址偏移量放到了%eax寄存器中。然后后面就进行了输出。

 

在来看一段代码,我们直接输出c[0],看汇编是什么样子

;12 :	    printf("%d",c[0]);
0x401422	mov    0x14(%esp),%eax
0x401426	mov    %eax,0x4(%esp)
0x40142a	movl   $0x408024,(%esp)
0x401431	call   0x401350 <printf>

看到了没有,这次就用上的是mov而不是上面的lea,mov就是取 esp+0x14 地址指向的值放到%eax寄存器。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值