竹逸 发表于 2022-8-26 23:30:37

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

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

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

数组名本质上是一个基址,是段首地址(一个段中连续内存单元的起始地址),我们可以通过数组名+下标索引的方式来定位各个元素,即通过基址和相对于基址的偏移地址的方式来定位各个元素(内存单元)

竹逸 发表于 2022-8-26 23:33:28

数组元素也是一个变量,我理解的变量是一块内存单元,变量名则是给该内存单元的取名,变量的地址则是该内存单元的地址值

jackz007 发表于 2022-8-26 23:58:58

本帖最后由 jackz007 于 2022-8-27 00:00 编辑

         没错,你的理解很对。
         int a ;
         这条语句分配 4 x 5 = 20 个字节的存储单元,并定义了一个常数指针 a 指向这个存储区的起始地址。
         a = 8 ;
         这条语句会在从地址 a 起算,偏移为 8 的位置处的连续 4 个字节单元中,存入一个整型数 8(08 00 00 00)。

竹逸 发表于 2022-8-28 13:54:38

jackz007 发表于 2022-8-26 23:58
没错,你的理解很对。

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

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

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



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

jackz007 发表于 2022-8-28 14:05:53

竹逸 发表于 2022-8-28 13:54
关于变量和数组的区别我这么理解应该没错吧

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

      没错。

竹逸 发表于 2022-8-28 14:45:58

jackz007 发表于 2022-8-28 14:05
没错。

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

可如下图所示



为啥被标记的内存单元地址和它存储的值(另一个内存地址)是一样的,难道说这个被标记的内存单元里存储的是它本身的地址?

jackz007 发表于 2022-8-28 14:59:36

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

      a 是常量指针,& a 就是 a 本身。

竹逸 发表于 2022-8-28 15:00:37

jackz007 发表于 2022-8-28 14:05
没错。

通过实验,它果然是记录的自身的地址{:5_97:}



这自己记录自己就很狗{:5_99:}

人造人 发表于 2022-8-28 15:03:26

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

忘了吗?
数组的名字是数组第0个元素的地址
就是 a == &a
&a 是整个数组的地址
&a 和 a 的值是一样的,但是类型不一样

竹逸 发表于 2022-8-28 15:08:33

人造人 发表于 2022-8-28 15:03
忘了吗?
数组的名字是数组第0个元素的地址
就是 a == &a


你看我上面的回复,数组名本身是有自己的地址的,这个它本身的地址存储的是数组的首地址,只不过它本身地址里存储的是它本身

人造人 发表于 2022-8-28 15:13:33

看汇编代码么
a&a&a 这三个的值都是一样的,都是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               calll1090 <__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 = {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);
    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               calll1050 <printf@plt>
    11f0:        83 c4 10                     addl   $0x10,%esp
    printf("%x, %p, %x\n", *a, *&a, *&a);
    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               calll1050 <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               calll1230 <__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

人造人 发表于 2022-8-28 15:18:27

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

那么这个地址存储在什么地方?数组第0个元素的位置?
数组第0个位置存储的是数字1,不是数组的地址
这个数组的地址是 不存储的
你非要说存储的话,那就是存储在编译器的内部
    int a = {1, 2, 3, 4, 5};

人造人 发表于 2022-8-28 15:21:15

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

a&a&a
这3个的值不需要内存地址来存储
这3个的值存储在编译器的内部,程序运行的时候,内存中不存储这3个的值

竹逸 发表于 2022-8-28 15:36:32

人造人 发表于 2022-8-28 15:21
a&a&a
这3个的值不需要内存地址来存储
这3个的值存储在编译器的内部,程序运行的时候,内存中不 ...

等等,我先捋捋{:10_266:}

竹逸 发表于 2022-8-28 16:00:22

人造人 发表于 2022-8-28 15:21
a&a&a
这3个的值不需要内存地址来存储
这3个的值存储在编译器的内部,程序运行的时候,内存中不 ...

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

数组名是不是一个指针变量?如果是,那指针变量也是一个变量,在声明的时候CPU就会为它开辟一块内存空间,只不过这块内存空间里存储的是内存地址,假设CPU为指针变量分配的内存空间的地址是18FF34,那这块内存地址里存储的指针是它本身,即18FF34里存储的内存地址是18FF34,*a取的是指针变量18FF34里存储的18FF34的值,因为他里面存储的是自身,也就是取的是18FF34,&a取的是指针变量a本身的地址,*&a取的是它自身地址里存储的值,也是它里面存储的内存地址18FF34,到这里出问题了,那第一个元素的值存储在哪里?既然18FF34里存储的是18FF34,那第一个元素呢{:10_266:}

人造人 发表于 2022-8-28 16:14:45

竹逸 发表于 2022-8-28 16:00
我知道我想错了,按照我的思维逻辑是这样的,

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

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

如果编译器打算把这个数组放在内存地址 18FF34 开始的位置
那么 a   &a&a 这3个的值都是 18FF34,不存在谁保存谁的问题

人造人 发表于 2022-8-28 16:21:14

这样理解也许更合适,如果你在代码中出现了a,那么编译器直接把这个a换成18FF34
你写&a也一样,直接换成18FF34
&a 也是直接换成 18FF34
不存在谁保存谁的问题,谁也不保存谁
只要编译器看见了这3个,直接换成 18FF34

竹逸 发表于 2022-8-28 16:32:57

人造人 发表于 2022-8-28 16:21
这样理解也许更合适,如果你在代码中出现了a,那么编译器直接把这个a换成18FF34
你写&a也一样,直接换成18 ...

令我疑惑的是这个*a和*&a,如果a和&a没区别,那为啥用取值运算符得到的一个是值,另一个却是址{:10_258:}

人造人 发表于 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 = {1, 2, 3, 4, 5};
    printf("%p\n", &a + 1);
    printf("%p\n", a + 1);
    return 0;
}

竹逸 发表于 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是整个数组的地址


我回来了,不知道怎么就睡着了{:5_99:} 我运行了你的代码



得到的结论是,数组名a其实是数组的起始地址,同时也是第一个元素的内存地址,然后a+1,a是指针,1是数值,类型不同的相加运算,发现+1后它指向的是第二个元素的内存地址,那这个+1的操作是下标索引值+1,指向的是下一个元素,所以加了4个字节的长度,最后&a+1,&a取出的是整个数组的内存地址,+1后的值是加14H字节的长度,刚好是整个数组的长度,那这个+1的操作,指向的就是下一个数组的起始地址了
页: [1] 2
查看完整版本: 关于数组的元素,以汇编的角度我这么理解对么?