
我以前一直有一个问题,为什么数组名的地址和数组名的值相同. 简单地听别人谈论这个事情实际上很难理解. 我认为从编译的角度理解这个问题很容易.
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指令系统中有三种主要类型的操作数: 立即操作数(即常量),寄存器操作数(例如通用寄存器AX,段寄存器DS)和内存操作数(存储在内存中)数据. )
因此,我们编写的变量名称不在编译后生成的机器代码中.
了解这些知识之后,您实际上可以了解上述标识符的替代效果.
基于上述基础,让我们理解为什么数组名和数组名采用相同的地址值.
我们还将数组名称视为标识符. 它也代表一个地址,但是该地址不同于先前的地址. 地址也是一种. 实际上,它不能称为地址. 实际上有地址的类型. 它是一个值,其类型取决于操作他的指令. 操作他的指令从该地址读回一些位,以表示该地址值表示的类型. 因此,所有标识符实际上代表一个公共地址值,但是该值的操作指令不同,显示的类型也不同.
返回数组名称c语言对应汇编语句,该数组名称实际上表示该数组的第一个地址值,但是我们用于地址值的命令与上面由a表示的用于地址值的命令不同,它显示的结果不同的.

我们上面提到的a标识符等效于0xff1122,因此我们在程序中采用标识符a的地址,实际上,它为您提供了由a标识符表示的地址.
直接读取a的值时,它会读取标识符a所表示的地址所指向的值,然后根据不同的操作指令(不同的操作指令确定该指令要处理多长时间),从该地址开始读取不同的数字.
看一段代码: 然后我们使用GCC生成汇编代码c语言对应汇编语句,以查看汇编如何处理该程序
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最初表示此数组,然后将数组的地址偏移量放入%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寄存器中.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-280776-1.html
建议先不升