菜鸡的C学习日志---sizeof和strlen下的指针和数组(2/2)

文章详细分析了C语言中sizeof运算符和strlen函数在处理数组和指针时的不同行为,包括对数组地址、元素、解引用后的值以及指针加法后的大小和字符串长度的计算。通过实例代码解释了可能导致的误解和正确理解这些概念的重要性。
摘要由CSDN通过智能技术生成

叠甲:纯新人学习,讲的都是很简单的内容,就是想让我这种傻瓜也看得懂的,不喜勿喷。有错误或者不好的地方可以帮忙指出,非常感谢!

接着上面的故事

第三板块

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

第三板块 printf打印的结果是什么?整体的答案在这道题结束时公布。现在我们来一条一条看(先代码后解释,别看错位置了)

看之前做个小区分,第二板块的=号后面是{}的,而这里是"",两种是有区别的(看图)

sizeof部分

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

这里的arr代表整个数组,该数组有6个元素且是char类型,输出6。对吗?错误!别忘了字符串还有结束标准符,占一个字节哦,输出7

printf("%d\n", sizeof(arr + 0));//32-4 64-8

这里的arr+0的arr代表首元素地址,首元素+0,还是首元素地址,是地址就32-4 64-8

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

*arr就是解引用首元素地址,解出来是首元素,首元素是‘a' 这里是char类型,输出1

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

第二个元素‘b',且是char类型,输出1

printf("%d\n", sizeof(&arr));//32-4 64-8

取整个数组的地址,是地址就 32-4 64-8

printf("%d\n", sizeof(&arr + 1));//32-4 64-8

这里arr代表整个数组,取整个数组的地址+1,还是个地址,是地址就32-4 64-8

printf("%d\n", sizeof(&arr[0] + 1));//32-4 64-8

取arr[0]的地址+1,还是个地址,是地址就32-4 64-8

strlen部分

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

arr单独使用代表整个数组,这里传进去整个数组的地址,而数组的地址是首元素的地址,于是strlen就从首元素地址开始算,到\0停止,输出6

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

arr+0代表首元素地址+0,还是首元素地址,从首元素开始算,\0结束,输出6

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

 解引用arr,解出来就是arr首元素‘a’,这不是一个合法的传址,编译器会报错

 同理 arr[1]就是arr第二位元素‘b’,这不是一个合法的传址,编译器会报错

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

这里的arr代表整个数组,&arr的就是取整个arr的地址,而arr的地址就是首元素地址,从首元素开始,到\0结束,输出6

printf("%d\n", strlen(&arr + 1));//随机值

 这里的arr代表整个数组,&arr就是取整个arr的地址,&arr+1就是跳到下一个数组去了。那么下一个数组的\0在哪呢?我们不知道,所以是随机值

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

 &arr[0]就是取了arr数组0位的地址,就是arr数组首元素的地址。&arr[0]+1就是第一位的地址+1,这就跳到第二位去了,所以strlen从第二位开始算,到\0结束 输出5
答案(64位平台 就是地址是 8的)

-------------------------------------------------------------------

第四板块 

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

第四板块 printf打印的结果是什么?整体的答案在这道题结束时公布。现在我们来一条一条看(先代码后解释,别看错位置了)

sizeof部分

printf("%d\n", sizeof(p));//32-4 64-8

p指针是个地址,所以 32-4 64-8

printf("%d\n", sizeof(p + 1));//32-4 64-8

 p指针是个地址+1还是个地址,还是32-4 64-8 你懂的

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

p指向的是首元素地址,解引用p,就是解引用首元素地址,得出a,char类型的,在内存占1个字节空间,所以输出1

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

 等价*(p+0) 解引用p+0,还是解引用首元素地址,得出a,char类型的,在内存占1个字节空间,所以输出1

printf("%d\n", sizeof(&p));//32-4 64-8

 取指针p自身地址然后放进sizeof,sizeof就是算操作数在内存上所占空间大小的,地址所占空间大小就 32-4 64-8

printf("%d\n", sizeof(&p + 1));//32-4 64-8 

取指针自身地址+1,不还是地址,是地址就是32-4 64-8

printf("%d\n", sizeof(&p[0] + 1));//32-4 64-8 

依然是取地址+1,还是地址。32-4 64-8

"这是什么情况,怎么和想象的不一样,怎么错那么多"---这是我做完时的心里话,这个确实有点难懂的感觉,当然只是感觉。我画了个图,我敢保证这个图看懂了,你就懂了

 有了这个图,我们下面的strlen部分就简单了

strlen部分

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

 由上图所示,p指向字符串首元素的地址,这里的strlen也就从首元素开始算,遇到\0停止,输出6

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

由上图所示,p指向字符串首元素地址,+1就是字符串首元素地址+1,指向了第二位元素地址,这里strlen就从第二位开始算,遇到\0停止,输出5

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

由上图所示,解引用p,得到的是首元素a,给strlen传这种地址是非法的,编译器报错
 

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

 p[0]就是*(p+0),解出来就是首元素a,这种传址方式是非法的,编译器报错
 

printf("%d\n", strlen(&p));//随机值

取地址指针p,注意是取到了指针自身的地址,这边strlen要遇到\0才会停,我们不知道\0在哪,输出随机值。

printf("%d\n", strlen(&p + 1));//随机值

取地址指针p再+1,就是取指针自身地址+1,还是和上条一样,我们不知道\0在哪,输出随机值

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

可以理解为&(*(p+0))+1, *(p+0)得到首元素,这里取首元素地址再+1,得到第二个元素地址。strlen从第二个元素开始算,遇到\0停止。输出5
答案(64位平台 就是地址是 8的)

 -------------------------------------------------------------------

第五部分 最难的来了

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

第五板块 printf打印的结果是什么?整体的答案在这道题结束时公布。现在我们来一条一条看(先代码后解释,别看错位置了)

开始前请仔细看下面两图,分清楚一二维数组各自的区别和相同点

 开始

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

 a表示整个二维数组  3*4*sizeof(int)=48

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

表示二维数组的第一行第一列元素,int类型,输出4
 

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

表示二维数组的首元素,也可以表示二维数组的第一行。有四个元素,都是int类型,输出16
 

printf("%d\n", sizeof(a[0] + 1));//32-4 64-8 
printf("%d\n", sizeof(*(a[0] + 1)));//4

 a[0]也是二维数组的中第一行一维数组的数组名,这里没有单独使用,也没有取地址。代表第一行第一列地址,+1,就是第一行第二列地址了,是地址就32-4 64-8
同理,解引用第一行第二列地址就得出第一行第二列的元素,int类型,输出4

printf("%d\n", sizeof(a + 1));//32-4 64-8 
printf("%d\n", sizeof(*(a + 1)));//16 

 a是二维数组的数组名,这里没有单独使用,代表首元素地址,+1,就是第二行(能懂吧?)地址

地址就32-4 64-8

a是二维数组名,没有单独使用,代表首元素地址,二维数组首元素是a[0],+1就是a[1]的地址,解引用,4个int得出16

printf("%d\n", sizeof(&a[0] + 1));//32-4 64-8 
printf("%d\n", sizeof(*(&a[0] + 1)));//16 

 a[0]也是二维数组的中第一行一维数组的数组名,这里被取地址,那就代表二维数组中第一行一维数组的整个数组,+1就跳到第二行一维数组去了。由于是取地址,所以输出4

同理,解引用第二行地址得出整个第二行(一维数组),4个int,输出16

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

a是二维数组的数组名,这里没有单独使用也没有取地址,所以代表二维数组首元素,二维数组首元素是a[0],解引用地址得到第一行,4个int类型,输出16
 

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

这是什么东东?怎么有a[3],这不越界访问了吗?

nonono

在C语言中,一条表达式有两种属性。分别是值属性和类型属性

比如int a=1+2这条表达式里面,值属性是3,类型属性是int

越界访问那是要算(被用到)时才会发生的

比如sizeo(1+2),它内部表达式是不计算的,也就是说是不会去算这个1+2等于什么的,而且直接推测他的类型

上面也是,直接根据int和[3][3]推测a[3]是什么类型的空间大小多少,不会真的去访问那块区域

答案(64位平台 就是地址是 8的)

part2结束

以上!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值