鱼C论坛

 找回密码
 立即注册
查看: 3063|回复: 21

[已解决]关于数组的元素,以汇编的角度我这么理解对么?

[复制链接]
发表于 2022-8-26 23:30:37 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 竹逸 于 2022-8-27 08:25 编辑

a[5],其中[ ]表示一个内存单元,5表示的是该内存单元的偏移地址(相对于基址a的偏移地址)

数组名本质上是一个基址,是段首地址(一个段中连续内存单元的起始地址),我们可以通过数组名+下标索引的方式来定位各个元素,即通过基址和相对于基址的偏移地址的方式来定位各个元素(内存单元)
最佳答案
2022-8-26 23:58:58
本帖最后由 jackz007 于 2022-8-27 00:00 编辑

         没错,你的理解很对。
         int a[5] ; 
         这条语句分配 4 x 5 = 20 个字节的存储单元,并定义了一个常数指针 a 指向这个存储区的起始地址。
         a[2] = 8 ;
         这条语句会在从地址 a 起算,偏移为 8 的位置处的连续 4 个字节单元中,存入一个整型数 8(08 00 00 00)。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-8-26 23:33:28 | 显示全部楼层
数组元素也是一个变量,我理解的变量是一块内存单元,变量名则是给该内存单元的取名,变量的地址则是该内存单元的地址值
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 23:58:58 | 显示全部楼层    本楼为最佳答案   
本帖最后由 jackz007 于 2022-8-27 00:00 编辑

         没错,你的理解很对。
         int a[5] ; 
         这条语句分配 4 x 5 = 20 个字节的存储单元,并定义了一个常数指针 a 指向这个存储区的起始地址。
         a[2] = 8 ;
         这条语句会在从地址 a 起算,偏移为 8 的位置处的连续 4 个字节单元中,存入一个整型数 8(08 00 00 00)。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 13:54:38 | 显示全部楼层
jackz007 发表于 2022-8-26 23:58
没错,你的理解很对。

         这条语句分配 4 x 5 = 20 个字节的存储单元,并定义了一个常数 ...

关于变量和数组的区别我这么理解应该没错吧

变量名是一个标识符,它标记了内存中某快内存单元,通过变量名也就是通过标识符可以定位被标记的内存单元,给变量赋值则是给被标记的内存单元写入数据。变量名可以理解为给该内存单元的取名,变量的地址则是该内存单元的地址值(下标索引)



数组名也是一个标识符,它同样也是标记了内存中某快内存单元,它和变量本质上没有区别,有区别的地方在于它们的值不同,变量的值是一个简单的值,可以是整形,字符型等,而数组名的值是一个内存地址(指针),它指向的是变量的地址,而不是变量的值。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 14:05:53 | 显示全部楼层
竹逸 发表于 2022-8-28 13:54
关于变量和数组的区别我这么理解应该没错吧

变量名是一个标识符,它标记了内存中某快内存单 ...

        没错。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 14:45:58 | 显示全部楼层

既然数组名也是一个标识符,它同样也是标记了内存中某块内存单元,通过数组名也就是通过标识符可以定位被标记的内存单元,那这个被标记的内存单元里存储的应该是另一个内存地址才对

可如下图所示

捕获.JPG

为啥被标记的内存单元地址和它存储的值(另一个内存地址)是一样的,难道说这个被标记的内存单元里存储的是它本身的地址?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 14:59:36 | 显示全部楼层
竹逸 发表于 2022-8-28 14:45
既然数组名也是一个标识符,它同样也是标记了内存中某块内存单元,通过数组名也就是通过标识符可以定位被 ...

      a 是常量指针,& a 就是 a 本身。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 15:00:37 | 显示全部楼层

通过实验,它果然是记录的自身的地址

捕获.JPG

这自己记录自己就很狗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 15:03:26 | 显示全部楼层
竹逸 发表于 2022-8-28 14:45
既然数组名也是一个标识符,它同样也是标记了内存中某块内存单元,通过数组名也就是通过标识符可以定位被 ...

忘了吗?
数组的名字是数组第0个元素的地址
就是 a == &a[0]
&a 是整个数组的地址
&a 和 a 的值是一样的,但是类型不一样
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 15:08:33 | 显示全部楼层
人造人 发表于 2022-8-28 15:03
忘了吗?
数组的名字是数组第0个元素的地址
就是 a == &a[0]

你看我上面的回复,数组名本身是有自己的地址的,这个它本身的地址存储的是数组的首地址,只不过它本身地址里存储的是它本身
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 15:13:33 | 显示全部楼层
看汇编代码么
a  &a  &a[0] 这三个的值都是一样的,都是  leal   -0x20(%ebp),%eax
int main(void) {
    118d:        8d 4c 24 04                  leal   0x4(%esp),%ecx
    1191:        83 e4 f0                     andl   $0xfffffff0,%esp
    1194:        ff 71 fc                     pushl  -0x4(%ecx)
    1197:        55                           pushl  %ebp
    1198:        89 e5                        movl   %esp,%ebp
    119a:        53                           pushl  %ebx
    119b:        51                           pushl  %ecx
    119c:        83 ec 20                     subl   $0x20,%esp
    119f:        e8 ec fe ff ff               calll  1090 <__x86.get_pc_thunk.bx>
    11a4:        81 c3 50 2e 00 00            addl   $0x2e50,%ebx
    11aa:        65 a1 14 00 00 00            movl   %gs:0x14,%eax
    11b0:        89 45 f4                     movl   %eax,-0xc(%ebp)
    11b3:        31 c0                        xorl   %eax,%eax
    int a[5] = {1, 2, 3, 4, 5};
    11b5:        c7 45 e0 01 00 00 00         movl   $0x1,-0x20(%ebp)
    11bc:        c7 45 e4 02 00 00 00         movl   $0x2,-0x1c(%ebp)
    11c3:        c7 45 e8 03 00 00 00         movl   $0x3,-0x18(%ebp)
    11ca:        c7 45 ec 04 00 00 00         movl   $0x4,-0x14(%ebp)
    11d1:        c7 45 f0 05 00 00 00         movl   $0x5,-0x10(%ebp)
    printf("%p, %p, %p\n", a, &a, &a[0]);
    11d8:        8d 45 e0                     leal   -0x20(%ebp),%eax
    11db:        50                           pushl  %eax
    11dc:        8d 45 e0                     leal   -0x20(%ebp),%eax
    11df:        50                           pushl  %eax
    11e0:        8d 45 e0                     leal   -0x20(%ebp),%eax
    11e3:        50                           pushl  %eax
    11e4:        8d 83 14 e0 ff ff            leal   -0x1fec(%ebx),%eax
    11ea:        50                           pushl  %eax
    11eb:        e8 60 fe ff ff               calll  1050 <printf@plt>
    11f0:        83 c4 10                     addl   $0x10,%esp
    printf("%x, %p, %x\n", *a, *&a, *&a[0]);
    11f3:        8b 55 e0                     movl   -0x20(%ebp),%edx
    11f6:        8b 45 e0                     movl   -0x20(%ebp),%eax
    11f9:        52                           pushl  %edx
    11fa:        8d 55 e0                     leal   -0x20(%ebp),%edx
    11fd:        52                           pushl  %edx
    11fe:        50                           pushl  %eax
    11ff:        8d 83 20 e0 ff ff            leal   -0x1fe0(%ebx),%eax
    1205:        50                           pushl  %eax
    1206:        e8 45 fe ff ff               calll  1050 <printf@plt>
    120b:        83 c4 10                     addl   $0x10,%esp
    return 0;
    120e:        b8 00 00 00 00               movl   $0x0,%eax
}
    1213:        8b 55 f4                     movl   -0xc(%ebp),%edx
    1216:        65 2b 15 14 00 00 00         subl   %gs:0x14,%edx
    121d:        74 05                        je     1224 <main+0x97>
    121f:        e8 0c 00 00 00               calll  1230 <__stack_chk_fail_local>
    1224:        8d 65 f8                     leal   -0x8(%ebp),%esp
    1227:        59                           popl   %ecx
    1228:        5b                           popl   %ebx
    1229:        5d                           popl   %ebp
    122a:        8d 61 fc                     leal   -0x4(%ecx),%esp
    122d:        c3                           retl
    122e:        66 90                        xchgw  %ax,%ax
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 15:18:27 | 显示全部楼层
竹逸 发表于 2022-8-28 15:08
你看我上面的回复,数组名本身是有自己的地址的,这个它本身的地址存储的是数组的首地址,只不过它本身地 ...

那么这个地址存储在什么地方?数组第0个元素的位置?
数组第0个位置存储的是数字1,不是数组的地址
这个数组的地址是 不存储的
你非要说存储的话,那就是存储在编译器的内部
    int a[5] = {1, 2, 3, 4, 5};
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 15:21:15 | 显示全部楼层
竹逸 发表于 2022-8-28 15:08
你看我上面的回复,数组名本身是有自己的地址的,这个它本身的地址存储的是数组的首地址,只不过它本身地 ...

a  &a  &a[0]
这3个的值不需要内存地址来存储
这3个的值存储在编译器的内部,程序运行的时候,内存中不存储这3个的值
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 15:36:32 From FishC Mobile | 显示全部楼层
人造人 发表于 2022-8-28 15:21
a  &a  &a[0]
这3个的值不需要内存地址来存储
这3个的值存储在编译器的内部,程序运行的时候,内存中不 ...

等等,我先捋捋
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 16:00:22 From FishC Mobile | 显示全部楼层
人造人 发表于 2022-8-28 15:21
a  &a  &a[0]
这3个的值不需要内存地址来存储
这3个的值存储在编译器的内部,程序运行的时候,内存中不 ...

我知道我想错了,按照我的思维逻辑是这样的,

数组名是不是一个指针变量?如果是,那指针变量也是一个变量,在声明的时候CPU就会为它开辟一块内存空间,只不过这块内存空间里存储的是内存地址,假设CPU为指针变量分配的内存空间的地址是18FF34,那这块内存地址里存储的指针是它本身,即18FF34里存储的内存地址是18FF34,*a取的是指针变量18FF34里存储的18FF34的值,因为他里面存储的是自身,也就是取的是18FF34,&a取的是指针变量a本身的地址,*&a取的是它自身地址里存储的值,也是它里面存储的内存地址18FF34,到这里出问题了,那第一个元素的值存储在哪里?既然18FF34里存储的是18FF34,那第一个元素呢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 16:14:45 | 显示全部楼层
竹逸 发表于 2022-8-28 16:00
我知道我想错了,按照我的思维逻辑是这样的,

数组名是不是一个指针变量?如果是,那指针变量也是一个 ...

    int a[5] = {1, 2, 3, 4, 5};

如果编译器打算把这个数组放在内存地址 18FF34 开始的位置
那么 a   &a  &a[0] 这3个的值都是 18FF34,不存在谁保存谁的问题
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 16:21:14 | 显示全部楼层
这样理解也许更合适,如果你在代码中出现了a,那么编译器直接把这个a换成18FF34
你写&a也一样,直接换成18FF34
&a[0] 也是直接换成 18FF34
不存在谁保存谁的问题,谁也不保存谁
只要编译器看见了这3个,直接换成 18FF34
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 16:32:57 From FishC Mobile | 显示全部楼层
人造人 发表于 2022-8-28 16:21
这样理解也许更合适,如果你在代码中出现了a,那么编译器直接把这个a换成18FF34
你写&a也一样,直接换成18 ...

令我疑惑的是这个*a和*&a,如果a和&a没区别,那为啥用取值运算符得到的一个是值,另一个却是址
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-28 16:45:48 | 显示全部楼层
竹逸 发表于 2022-8-28 16:32
令我疑惑的是这个*a和*&a,如果a和&a没区别,那为啥用取值运算符得到的一个是值,另一个却是址

如果a和&a没区别
谁说a和&a没区别,a和&a是有区别的
a是数组第0个元素的地址,&a是整个数组的地址
这个区别体现在对这个地址进行运算的时候
他们的值是一样的,但是类型不一样
记住,类型不一样
#include <stdio.h>

int main(void) {
    int a[5] = {1, 2, 3, 4, 5};
    printf("%p\n", &a + 1);
    printf("%p\n", a + 1);
    return 0;
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-28 19:14:50 | 显示全部楼层
本帖最后由 竹逸 于 2022-8-28 19:18 编辑
人造人 发表于 2022-8-28 16:45
如果a和&a没区别
谁说a和&a没区别,a和&a是有区别的
a是数组第0个元素的地址,&a是整个数组的地址


我回来了,不知道怎么就睡着了 我运行了你的代码

捕获.JPG

得到的结论是,数组名a其实是数组的起始地址,同时也是第一个元素的内存地址,然后a+1,a是指针,1是数值,类型不同的相加运算,发现+1后它指向的是第二个元素的内存地址,那这个+1的操作是下标索引值+1,指向的是下一个元素,所以加了4个字节的长度,最后&a+1,&a取出的是整个数组的内存地址,+1后的值是加14H字节的长度,刚好是整个数组的长度,那这个+1的操作,指向的就是下一个数组的起始地址了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-28 18:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表