C语言进阶——3(对于数组名的进一步理解)

1. 导读:

在前几篇关于指针的文章中多次提到一句话,即:数组名一般情况下=数组首元素地址。因为指针是一个地址,而在C语言中,数组是用来用来存放不同类型内容。可见,数组和指针的联系紧密,以及上面提到的话的重要性。在这篇文章中,将通过给出不同情况下的若干例子,并将这些例子分为一维数组、字符数组、二维数组进行解读来加深对数组和指针二者之间关系的理解。

(文章链接:C语言——对于简单指针性质的介绍_爱写代码的粉毛护理的博客-CSDN博客

指针——(进阶)_爱写代码的粉毛护理的博客-CSDN博客

2.  题目解析:

(注:为了便于区分,本文对于问题的解析使用蓝色字体,且下列得出的答案均实在64位背景下进行的)

2.1 一维数组:

定义下列数组,对下列数组在不同的情况下的打印进行解答;

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

题目一:


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


 在前面的文章提到过,一般情况下,数组名 = 首元素地址,但是在两个特殊情况下,这句话是不成立的。

第一种情况: sizeof(数组名)此时的数组名,代表了整个数组。此时sizeof计算的是整个数组的大小。单位是字节

第二种情况:&+数组名。此时的数组名,也是代表了整个数组,&取出来的是整个数组的地址。

 可以看到,给出的代码恰好是对应了第一种情况,所以,第一种情况打印出的结果应该是:4*4,即16个字节。

题目二:

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

前面说到,对于sizeof(数组名),此时的数组名表示整个数组,但是,当sizeof中的内容不是单独的数组名时,此时的数组名,表示数组首元素地址。

所以,此时的a表示数组首元素地址,且a+0依然表示数组首元素地址,所以答案是8个字节

题目三:

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

和题目二一样,此时sizeof中的内容不再是单纯的a,而是解引用操作符+a,所以,这里的a依然表示数组首元素地址,解引用操作符+a表示:首元素。因为数组的类型是Int,所以打印结果为4个字节。

题目四:
 

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

依旧和题目二、题目三相似,此时的a表示数组首元素的地址,且a的类型是Int*,在前面的文章中说过,对于整型类型的指针,加减整数n表示跳过n*4的字节的内容,所以,这里是跳过4个字节大小的内容,而Int类型的内容本来大小就是四个字节,所以a+1表示数组中,第二个元素的地址,此时,打印的结果是8字节。

题目五:

printf("%d\n",sizeof(a[1]));

这里的a[1]和上一个题目相似,这里表示数组中的第二个元素,大小为4个字节。

题目六:

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

对于这个题目需要注意,a的前面有一个&符号,&+数组名的组合,代表了取出整个数组的地址,但是,就算是整个数组的地址,本质也是个地址,地址的大小只会根据计算机地址线的不同而改变,所以,题目的 打印结果是8个字节。

题目七:

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

对于*&a,相当于先对a取地址,表示取出整个数组的地址,再进行解引用,相当于二者的效果相互抵消,于是,代码就等于题目一中的sizeof(a),打印结果依然是16字节。

对于这个题目,也可以从指针类型的角度考虑,&a表示取出数组的地址,这个地址的类型正是数组指针,即:int(*)[4],对这个类型的指针解引用,则访问内容是一整个数组,所以,对一整个数组进行sizeof求大小,是16字节

题目八:

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

上面的题目中说道,对于&+数组名这样的组合,代表取出整个数组的地址,此时再对整个组合进行加1,则不再表示跳过1个数组中的元素,而是直接跳过整个数组。但是即使跳过了整个数组,&a+1依旧表示地址,大小为8字节

题目九:

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

题目九则比较简单,sizeof中的内容表示对数组的首元素取地址,所以,打印结果依旧是8字节。

题目十:

printf("%d\n",sizeof(&a[0]+1));

和上面的题目不一样的是,这里的+1并不是跳过整个数组,而是先取了数组首元素的地址,再加一,表示取数组的下一个地址,即数组的第二个数组,打印结果是8字节。

2.2字符数组:

定义如下字符数组:

char arr[] = {'a','b','c','d','e','f'};

题目一:

printf("%d\n", sizeof(arr));

题目一为sizeof+数组名单独使用的组合,此时的数组名表示整个数组的地址,表达式计算的是整个数组的大小,单位是字节。所以,结果为6字节。

题目二:

printf("%d\n", sizeof(arr+0));

与一维数组中的一道题类似,在sizeof中的arr并没有单独使用,所以这里的arr表示数组首元素地址,arr+0依然表示数组首元素地址。所以sizeof计算的是这个地址的大小,为8字节。

题目三:

printf("%d\n", sizeof(*arr));

sizeof中的arr并没有单独使用,此时arr表示数组首元素地址,对arr进行解引用操作,表示数组arr的首元素。打印结果为1字节。

题目四:
 

printf("%d\n", sizeof(arr[1]));

与一维数组中的题目基本相同,此时的arr[1]表示数组中的 第二个元素。打印结果1字节。

题目五:

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

&+数组名的组合,此时的arr表示整个数组的地址,但是整个数组的地址也是一个地址,打印结果为8字节。

题目六:

printf("%d\n", sizeof(&arr+1));

对于&+arr这样的组合进行加1时,表示跳过这个数组,来到数组最后,因此,表达式整体的结果就是数组最后的地址,所以打印结果依旧为8字节。

题目七:

printf("%d\n", sizeof(&arr[0]+1));

 &+arr[0]的组合,表示取数组首元素的地址,+1表示取数组第二个元素的地址,所以,打印结果为8字节。

下面的题目,依旧是参考上面创建的字符数组,但是将打印函数中的sizeof改为strlen

char arr[] = {'a','b','c','d','e','f'};

题目一:

printf("%d\n", strlen(arr));

对上面的题目进行解析之前,首先说明一下,sizeof和strlen二者的区别,sizeof是通过类型来计算的,strlen是通过给予的初始地址向后找,统计‘\0'之前的元素的大小。

对于题目一,上面创建的字符数组中并没有’\0',所以strlen会一直向后寻找,答案为随机值,

题目二:

printf("%d\n", strlen(arr+0));

与一维数组中的题目相同,此时的arr+0依然表示首元素地址,所以,打印结果为随机值

题目三:

printf("%d\n", strlen(*arr));

题目中,strlen中的数是数组首元素。这里就要牵扯到strlen的参数传递:

 

 即strlen的参数部分需要传递一个地址。而题目把首元素传给了strlen,首元素为字符‘a',此时,strlen函数会把字符a的ASCII值,即97作为地址,所以,此地址非法,打印结果非法访问。

题目四:

printf("%d\n", strlen(arr[1]));

 与上面的题目相同,arr[1]表示数组中的第二个元素,打印结果依旧是非法访问。

题目五:

printf("%d\n", strlen(&arr));

&+数组名arr表示取数组的地址,不过需要注意这里的一个性质,即数组地址和数组首元素的地址的值是一样的。传递给strlen函数后,依然是从首元素向后计算,知道出现'\0'为止。打印结果为随机值

题目六:

printf("%d\n", strlen(&arr+1));

同样,这里strlen函数的内容表示跳过该数组。由于数组之后没有'\0',所以打印的答案依旧是随机值

题目七:

printf("%d\n", strlen(&arr[0]+1));

与上面的题目相同,此时strlen函数内部的内容表示,该数组的第二个元素,打印结果仍然为随机值

上面的题目是创建一个字符数组用于存放多个字符,下面将创建一个字符数组,来存放一个字符串。

char arr[] = "abcdef";

题目一:

printf("%d\n", sizeof(arr));

与前面的几个题目相同,属于是sizeof+arr数组单独使用的组合,计算的是数组中内容的大小,单位是字节。不过需要注意,对于字符串来说,最后面会带上’\0’代表结束,而sizeof并不会像strlen那样,读取到'\0'就截止,即不包含'\0',sizeof读取字符串时,会包含上最后的‘\0’,所以打印结果为7字节

题目二:

printf("%d\n", sizeof(arr+0));

sizeof中的数组名arr不再单独使用,arr+0依然表示数组首元素的地址,打印结果为8字节。

题目三:

printf("%d\n", sizeof(*arr));

同理,此时的arr不再单独使用,表示数组的首元素的地址,对arr进行解引用表示数组的首个元素。打印结果为1字节。

题目四:

printf("%d\n", sizeof(arr[1]));

此时,sizeof中的内容表示数组的第二个元素,打印结果为1字节。

题目五:
 

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

与上面的题目相同,&+arr的组合表示取出整个数组的地址,打印结果为8字节。

题目六:

printf("%d\n", sizeof(&arr+1));

sizeof中的内容表示跳过整个数组来到数组最后面的地址,打印结果为8字节

题目七:

printf("%d\n", sizeof(&arr[0]+1));

与上面的题目相同,sizeof中的内容表示数组的第二个元素的地址,打印结果为8字节。

下面的题目同样是对于上面的字符串,但是将sizeof换成strlen:

char arr[] = "abcdef";

题目一:

printf("%d\n", strlen(arr));

arr代表了首元素的地址,strlen函数通过首元素的地址对字符串进行打印,打印结果为6字节。

题目二:

printf("%d\n", strlen(arr+0));

同样,此时arr+0依然代表了数组首元素的地址,打印结果为6字节

题目三:

printf("%d\n", strlen(*arr));

*arr表示数组的首个元素,救是把字符'a‘的ASCII值作为参数给strlen函数,属于非法访问,打印错误。

题目四:

printf("%d\n", strlen(arr[1]));

此时arr[1]表示数组第二个元素,也就是将第二个元素的ASCII值作为参数传给strlen函数,非法访问,打印结果错误

题目五:
 

printf("%d\n", strlen(&arr));

在上面的题目中提到,数组的地址和数组首元素的地址值是相同的,这里&+arr表示取整个数组的地址,将作为参数传给strlen,依然是从头开始打印字符串,结果为7字节。

题目六:

printf("%d\n", strlen(&arr+1));

&arr+1,表示跳过整个数组,即来到了字符串结束标志'\0'的后面,用strlen对后面的内容进行计算,结果为随机值

题目七:

printf("%d\n", strlen(&arr[0]+1));

&arr[0]表示取数组首元素的地址,+1表示跳过首元素,所以,strlen内的内容表示取数组第二个元素的地址,再通过strlen从数组的第二个地址开始向后计算,结果为5字节。

下面的题目,将列举这样一个例子:创建一个字符指针,指针保存一个字符串的地址,对这个例子在不同情况下的值进行分析。

char *p = "abcdef";

题目一:

printf("%d\n", sizeof(p));

p是一个指针变量,用来存放字符串的地址(首字母a的地址),所以,打印结果为8字节。

题目二;

printf("%d\n", sizeof(p+1));

对于p+1,在前面的文章对于不同类型的指针变量的意义的探讨中说过,此时p的类型是char*,对此类型的指针+1,表示跳过一个字节的内容,所以p+1表示b的地址。打印结果为8字节

题目三:

printf("%d\n", sizeof(*p));

p代表了数组的首元素地址,*p代表了数组的首元素,此时元素的类型为char型,所以,打印结果为1字节

题目四:

printf("%d\n", sizeof(p[0]));

arr[0]表示数组的首个元素,对于p[0]同样,也可以把p[0]看成:*(p+0),也就等于*p,表示该字符数组的首个元素,打印结果为1字节。 

题目五:

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

p是用来存放地址的指针变量,&p就是一个二级指针,所以,打印结果是8字节

题目六:

printf("%d\n", sizeof(&p+1));

&p+1代表跳过整个p的内容,所以本质还是地址,打印结果为8字节

题目七:

printf("%d\n", sizeof(&p[0]+1));

与上面的一个题目相似,&p[0]表示取数组中第一个元素的地址,+1表示跳过该数组的首元素。打印结果为8字节

下面,将sizeof改为strlen再次分析:

题目一:

printf("%d\n", strlen(p));

p中存储着字符串首字符的地址,也就是'a'的地址,所以,将a的地址传给strlen函数,打印结果为字符串结束标志’\0之前的内容,打印结果为6字节。

题目二:

printf("%d\n", strlen(p+1));

p+1表示字符串中第二个字符的地址,即b的地址,打印结果为5字节。

题目三:

printf("%d\n", strlen(*p));

*p相当于字符串的首元素,所以,非法访问

题目四:

printf("%d\n", strlen(p[0]));

p[0]表示字符串的首个元素,打印结果依旧非法访问

题目五:

printf("%d\n", strlen(&p));

&p表示对p指针变量再次取地址,所以strlen中的参数是一个二级指针,计算的是这个地址的内容,因为地址中不知道有没有'\0',所以随机值。

题目六:

printf("%d\n", strlen(&p+1));

&p+1表示跳过整个地址,所以打印结果为随机值

题目七:

printf("%d\n", strlen(&p[0]+1));

&p[0]+1表示取数组第二个元素的地址,打印结果为 5字节

2.3 二维数组:

创建一个二维数组,对不同情况下的打印结果进行解析,创建的二维数组如下所示:

int a[3][4] = {0};

题目一:

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

此时数组名a单独与sizeof组合,所以这里的a表示首元素地址,数组是一个三行四列的数组,所以,打印结果为:3*4*4=48字节

题目二:

printf("%d\n",sizeof(a[0][0]));

a[0][0]表示数组的第一个元素,所以,打印结果为4字节。

题目三:

printf("%d\n",sizeof(a[0]));

在之前对于二维数组解析的文章中提到,可以将二维数组看成是一维数组的数组,对这里,可以将a[0]看作二维数组的第一行的一维数组的数组名,此时,数组名单独放在sizeof中,计算的是整个数组的大小,所以打印结果是4*4=16字节。

题目四:

printf("%d\n",sizeof(a[0]+1));

a[0]作为第一行数组的数组名,但是这里并没有和sizeof单独使用,所以,这里的a[0]表示的是第一行数组的首元素地址,也就是a[0][0]的地址,+1第二个元素,所以,a[0]+1表示二维数组第一行的第二个元素的地址,打印结果为8字节。

题目五:

printf("%d\n",sizeof(*(a[0]+1)));

上个题目的a[0]+1表示二维数组第一行第二个元素的地址,加上*后表示,二维数组第一行第二个元素,所以,打印结果为4字节。

题目六:

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

a是数组首元素的地址,a+1表示数组第二行的地址,这是因为,此时的指针类型是一个数组指针,即int(*)[4]+1后,跳过的内容是数组中的四个元素,所以来到第二行。结果为8字节

题目七:

printf("%d\n",sizeof(*(a+1)));

a+1表示第二行数组的数组名,此时的指针的类型是数组指针:int(*)[4],对其进行解引用,则事项的内容应该是一个数组,即指向第二行数组,所以,sizeof计算的是第二行数组的大小,为16字节。

题目八:

printf("%d\n",sizeof(&a[0]+1));

a[0]是二维数组第一行的数组名,&a[0]表示第一行数组的地址,所以&a[0]+1表示第二行的地址,因为是地址,所以打印结果是8字节

题目九:

printf("%d\n",sizeof(*(&a[0]+1)));

与上一个题目相比多了一个*,表示的意思是第二行的数组,所以打印结果是16字节。

题目十:

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

a并没有单独和sizeof放在一起,所以,a表示的是二维数组首元素的地址,即二维数组第一行一维数组的地址,解引用则表示二维数组的第一行,也就是一维数组,所以打印结果为16字节。

题目十一:

printf("%d\n",sizeof(a[3]));

此时,虽然a[3]已经造成了越界,但是a[4]的类型就是int [4],所以打印结果为16字节,这是因为,sizeof并不会真正去计算参数表达式的 值,只会关注参数的类型。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起床写代码啦!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值