竹逸 发表于 2022-11-22 20:33:57

关于数组的一个疑问

#include<stdio.h>

void main()
{

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

        //数组名的值是数组中第一个元素的内存地址,即a == &a,通过取值运算符"*"证明a == &a

        printf("%d\n", *(&a));
        printf("%d\n", *a);

        printf("%d\n", sizeof(a));
       
        //求数组名的长度 sizeof(a) 得到的结果为何与第一个元素地址的长度 sizeof(&a) 不一样?地址的长度是4个字节,数组名的值也是一个地址,可为啥它的长度却是整个数组的长度?

        printf("%d\n", sizeof(&a));

        //自我尝试解释一下,数组名其实是一个特殊的变量名,跟结构体一样,数组中的各个元素就相当于结构体中的各个成员,只不过数组类型是C语言定义好的结构体类型,不需要我们再次自定义一个结构类型,数组名这个特殊的变量就相当于结构体的变量,这个特殊的变量的长度就和结构体变量的长度一样,是其元素(成员)长度之和,问题是,结构体变量的值是各个成员的值,可数组名这个特殊变量的值为何是一个地址?

}


jackz007 发表于 2022-11-22 20:33:58

本帖最后由 jackz007 于 2022-11-22 21:53 编辑

      int a = {1, 2, 3, 4, 5} ;
      a 属于局部变量,在进入函数 main() 时,系统会在堆栈区为 a 开辟 sizeof(int) * 5 = 20 个字节的空间用于储存 a 的 5 个元素,并进行初始化,a 就是这片存储区的代表,它有两个重要的属性,一个是起始地址,另一个是内存容量(字节数),所以,我们可以用 sizeof(a) 来获取 a 占用的字节数,可以通过 a、a 等来访问数组的元素。这两个属性只是在其作用域,也就是 main() 函数内是完整可见的。当把 a 作为实参,用于调用函数的时候,实际传入函数的,只能是 a 两个属性中的一个,那就是内存地址(指针),而占用存储区的字节数的属性则无法同时传入。所以,在函数内,我们不能通过 sizeof(a) 来获取其实际容量,得到的值只是作为一个指针变量所占用的存储空间的数量,这也是很多人强调指针不是数组的重要原因。
       作为数组的 a 确实是一个指针,但是,这个指针很特殊,因为 a 和 & a 的值是完全一样的,这个特性很有用,可以通过取址操作,使 a 升级为多级指针。

竹逸 发表于 2022-11-22 21:02:12

本帖最后由 竹逸 于 2022-11-22 21:18 编辑

我发现结构体变量的值也是一个地址,不过为啥我这个结构体变量的长度是16?不应该是14吗{:5_94:} 我记得课件里好像说结构体变量的长度是其成员长度之和,难道我听错了{:5_94:}

xiaotubie 发表于 2022-11-22 21:10:55

本帖最后由 xiaotubie 于 2022-11-22 22:01 编辑

数组名代表的是数组,不是地址,代表了数组这个结构体。
但是它又在大多数的表达式中会隐式转换为指向首元素的指针(打印输出就是一个首元素的地址),除了参与&和sizeof以外。所以你刚好用到了sizeof这个时候没有转换的,还是代表了数组,所以获得数组整个大小。
&数组名时也不会发生转换,所以&a就是数组的指针(因为这个时候a还是数组的意思),所以&a+1使得地址值会前进整个数组大小的地址。&a打印输出是数组的地址,虽然等于首元素地址,但是意义不一样。(a与&a打印出来地址这个数值一样,代表的不一样, a是int*类型,&a是int(*)类型,说他们一样的,你可以直接无视)
a==&a这两个a已经转换成了数组首元素指针。后面那个a先参与[]运算相当于*(a+0)也就是获得了首元素,再进行前面的&运算获得首元素的指针,所以前后相等。
*(&a)上面分析了&a是首元素指针所以*运算获取首元素1
*a 也直接获取了首元素1(a转换成了首元素的指针)
sizeof(&a) 打印首元素指针的大小

很多书或者文章会直接说数组名是地址或者指针,所以导致看的人摸不着头脑:为什么sizeof一个地址却是数组的大小?为什么&a输出来还会等于a自己?还错误的理解了二维数组名就是二级指针!

为什么是16,因为涉及到结构体对齐,你这里按4的整数倍来占用空间。从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,
这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

两手空空儿 发表于 2022-11-22 21:35:49

本帖最后由 两手空空儿 于 2022-11-22 21:38 编辑

试着回答一下,不一定准确,更深层的东西我也不知道
先抄来一段
关于sizeof的两个精巧的宏实现。
非数组的sizeof:
#defne _sizeof(T) ( (size_t)((T*)0 + 1))
数组的sizeof:
#define array_sizeof(T)   ( (size_t)(&T+1)- (size_t)(&T))

声明的时候编译器就知道a是一个数组,那对一个数组求长度,给出整个数组的长度才合理,当年的程序员肯定就是这么设计的。
从上面的代码可以看出,求第一个元素的长度和求a的长度用的是不一样的代码
上面用了指针+1的特性, 令int *p = &a, p是数组的指针, p+1会跨过整个数组,(P +1 ) - p 就是整个数组的长度

两手空空儿 发表于 2022-11-22 21:51:39

竹逸 发表于 2022-11-22 21:02
我发现结构体变量的值也是一个地址,不过为啥我这个结构体变量的长度是16?不应该是14吗 我记得课 ...

结构体的总大小不是简单的相加,看一下小甲鱼老师的视频,结构体部分,有说明,有个对齐的问题
你这个最大的元素是int,那结构体大小要是4(int)的倍数,所以不是14,是16


https://blog.csdn.net/a10201516595/article/details/95465601

竹逸 发表于 2022-11-22 22:10:51

xiaotubie 发表于 2022-11-22 21:10
数组名代表的是数组,不是地址,代表了数组这个结构体。
但是它又在大多数的表达式中会隐式转换为指向首元 ...

#include<stdio.h>

void main()
{
        int a = {1, 2, 3, 4, 5};

        printf("%d\n", sizeof(a));             // 结果20
        printf("%d\n", sizeof(&a));         // 结果20
}

既然数组名a与&参与运算时,a并不会转换成地址而是代表了整个数组,那 &a 就是取这个整个数组的地址,那既然是地址,为啥结果依然还是整个数组的长度,而不是一个地址的长度(4个字节)呢?

竹逸 发表于 2022-11-22 22:13:39

jackz007 发表于 2022-11-22 21:24
a 属于局部变量,在进入函数 main() 时,系统会在堆栈区为 a 开辟 sizeof(int) * 5 = 20 个字节 ...

a作为实际参数传递给函数时只能传递a的地址,而不能传递a的字节数,那为啥函数sizeof(a)是个例外{:5_94:}

竹逸 发表于 2022-11-22 22:17:06

本帖最后由 竹逸 于 2022-11-22 22:24 编辑

jackz007 发表于 2022-11-22 21:24
a 属于局部变量,在进入函数 main() 时,系统会在堆栈区为 a 开辟 sizeof(int) * 5 = 20 个字节 ...

#include<stdio.h>

void main()
{
        void test(int []);
        int a = {1, 2, 3, 4, 5};

        printf("%d\n", sizeof(a));         //打印结果20
        test(a);
}

void test(int arr)
{
        printf("%d\n", sizeof(arr));      //打印结果4
        printf("%d\n", sizeof(&arr));      //打印结果4
}

除了sizeof函数是个例外,其他函数(例如函数test)真的是只能接收数组a的起始地址,打印形参数组名arr得到的是指针的长度

jackz007 发表于 2022-11-22 22:19:18

竹逸 发表于 2022-11-22 22:17
除了sizeof函数是个例外,其他函数真是按指针的长度计算

         很显然,4 个字节是你的系统一个指针(32位)所占用的存储空间。

jackz007 发表于 2022-11-22 22:21:13

竹逸 发表于 2022-11-22 22:13
a作为实际参数传递给函数时只能传递a的地址,而不能传递a的字节数,那为啥函数sizeof(a)是个例外

         我已经说了,用 sizeof(a) 获取 a 占用的存储空间仅限于其作用域,也就是在 main() 函数内部。

xiaotubie 发表于 2022-11-22 22:30:51

本帖最后由 xiaotubie 于 2022-11-22 22:33 编辑

竹逸 发表于 2022-11-22 22:10
既然数组名a与&参与运算时,a并不会转换成地址而是代表了整个数组,那 &a 就是取这个整个数组的地址 ...

&a是指针。或者你认为是地址也行,打印sizeof(&a)是指针的大小,你可以试试,你搞混了 sizeof(a)才是数组大小

竹逸 发表于 2022-11-22 22:42:24

jackz007 发表于 2022-11-22 22:21
我已经说了,用 sizeof(a) 获取 a 占用的存储空间仅限于其作用域,也就是在 main() 函数内部 ...

原来是这样,a是个局部数组,所以在main函数内,sizeof能得到a的长度,当a作为实参传递给其他函数时,sizeof只能得到a的指针的长度

竹逸 发表于 2022-11-22 22:54:27

本帖最后由 竹逸 于 2022-11-22 23:00 编辑

两手空空儿 发表于 2022-11-22 21:51
结构体的总大小不是简单的相加,看一下小甲鱼老师的视频,结构体部分,有说明,有个对齐的问题
你这个 ...

我看的是小甲鱼旧版本的C课件,里面好像有说过结构体变量的长度是各个成员长度之和{:5_99:},当然也可能是我听错了{:5_99:}

你这个链接的讲解很详细, 原来是这么回事{:5_106:}

桃花飞舞 发表于 2022-11-22 23:34:35

本帖最后由 桃花飞舞 于 2022-11-22 23:38 编辑

看完你们的讨论我来总结下
int a;
数组名
1)数组类型的变量,作为变量a ---- in   数组名当做变量表示数组的一整个空间, a代表整个数组,&a自然指向整个数组的指针,&a是数组指针,是int (*)类型
2) 数组名表示数组的首元素地址-----指针作为指针,固定指向数组的首元素,指针常量,a --- int *
还有就是函数传参jackz007已经讲的比较清楚。
//===================================================================================================

sizeof是最冤枉的关键字,是关键字不是函数。

//===================================================================================================
最后结构体类型   先和相邻的下一个元素字节对齐在和成员基本数据类型所占的最大的空间进行字节对齐

再来比较下数组和结构体
数组:是一系列具有相同数据类型的数据的集合,数据彼此独立,互不影响。
结构体:成员,集合里面的数据,成员类型可以相同,可以不同,通过成员名访问




竹逸 发表于 2022-11-23 00:04:08

xiaotubie 发表于 2022-11-22 22:30
&a是指针。或者你认为是地址也行,打印sizeof(&a)是指针的大小,你可以试试,你搞混了 sizeof(a)才是 ...

#include<stdio.h>

void main()
{
        int a = {1,2,3,4,5};

        printf("%d\n", sizeof(a));       //打印结果20
        printf("%d\n", sizeof(&a));   //打印结果20
}
这两个结果都是一样的

竹逸 发表于 2022-11-23 00:30:16

桃花飞舞 发表于 2022-11-22 23:34
看完你们的讨论我来总结下
int a;
数组名

总结:除了&和sizeof外,数组名都作为首元素地址来用{:5_108:}

xiaotubie 发表于 2022-11-23 07:21:52

本帖最后由 xiaotubie 于 2022-11-23 07:26 编辑

竹逸 发表于 2022-11-23 00:04
这两个结果都是一样的

不可能的,如果你说这两个一样,你肯定没测试,&a是指针,sizeof &a是指针的大小,32位系统下,答案就是4。 你想想就知道&是取指针(地址)的,所以&a肯定是指针。 我刚又测试完,用vs和gcc都是4。如果你那确实是20,只能说你不要用这样的编译器了。

竹逸 发表于 2022-11-23 09:36:00

xiaotubie 发表于 2022-11-23 07:21
不可能的,如果你说这两个一样,你肯定没测试,&a是指针,sizeof &a是指针的大小,32位系统下,答案就 ...

确实是20,我用的是vc6{:10_250:}

竹逸 发表于 2022-11-23 09:47:12

本帖最后由 竹逸 于 2022-11-23 09:53 编辑

xiaotubie 发表于 2022-11-23 07:21
不可能的,如果你说这两个一样,你肯定没测试,&a是指针,sizeof &a是指针的大小,32位系统下,答案就 ...

我发现了个秘密{:5_97:}

这是VC6.0编译器



这是DEV-C编译器



同样的代码,既然结果不一样, VC6看来是用不了了{:5_99:}
页: [1] 2
查看完整版本: 关于数组的一个疑问