前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C指针】深入理解指针(最终篇)数组&&指针&&指针运算题解析(一)

【C指针】深入理解指针(最终篇)数组&&指针&&指针运算题解析(一)

作者头像
学习起来吧
发布2024-02-29 21:37:42
920
发布2024-02-29 21:37:42
举报
文章被收录于专栏:学习C/++学习C/++

前言

通过前几节我们学了前面五节指针和学习了sizeof操作符知识,本小节,阿森继续和你一起做一维数组和指针笔试题,?当然还有关于指针运算的笔试题,干货满满!让我们开始做题? !(?当然,宝子们,天气变化大,记得多注意保暖?)


1?? sizeof和strlen的对?

??sizeof

C语言中sizeof不是一个函数,而是一个编译期计算类型大小的内置操作符,所以它没有函数原型。 sizeof没有函数原型,它的语法形式是:

代码语言:javascript
复制
sizeof(类型名)
或
sizeof 变量名

类型名需要括号,变量名可以不需要括号。直接写类型名后面会报错。 sizeof 只关注占?内存空间的??,不在乎内存中存放什么数据。

代码语言:javascript
复制
int main()
{
	int a = 66;
	printf("%zd\n", sizeof(a));//4
	printf("%zd\n", sizeof(int));//4
	printf("%zd\n", sizeof a);//这里可以省略括号
	printf("%zd\n", sizeof int);//err,类型名本身不能直接跟在sizeof后面,如sizeof int是错误的
	return 0;
}

当然,sizeof也可以求数组的大小

数组名就是数组?元素(第?个元素)的地址。 sizeof(数组名)---->sizeof中单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??,单位是字节. &数组名(如&a),这?的数组名表?整个数组,取出的是整个数组的地址(整个数组的地址和数组?元素 的地址是有区别的) 除此之外,任何地?使?数组名,数组名都表??元素的地址。

代码语言:javascript
复制
int main()
{
	int arr1[4] = { 0 };
	char arr2[4] = { 0 };
	printf("%zd\n", sizeof(arr1));//16    //单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??
	printf("%zd\n", sizeof(int [4]));//16  //int占用4个字节,int[4]数组占用4 * 4 = 16字节

	printf("%zd\n", sizeof(arr2));//4        //单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??
	printf("%zd\n", sizeof(char [4]));//4  //arr2是char类型的数组,char占1字节,char[4]数组占用4 * 1 = 4字节。

	return 0;
}

?? ?? strlen

strlen 是C语?库函数,功能是求字符串?度。函数原型如下:

代码语言:javascript
复制
 size_t strlen ( const char * str );

统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会?直向后找 \0 字符,直到找到为?,所以可能存在越界查找。

代码语言:javascript
复制
int main()
{
	char arr1[20] = "abcdef";
	size_t len = strlen(arr1);//不包含结尾'\0',所以长度是6。
	printf("len = %zd\n", len);

	size_t sz = sizeof(arr1);//arr1这个数组变量占用的内存大小,即20字节。
	printf("sz = %zd\n", sz);

	char arr2[] = { 'a', 'b','c' };
	printf("%zd\n", strlen(arr2));//随机值,因为找不到\0
	
	char arr3[6] = "abcdef";//err
    //因为"abcdef"包含7个字符及结尾'\0',大于arr3的空间6字节,会导致越界。
	return 0;
}

?? ?? ??sizeof 和 strlen的对?

2?? 数组和指针笔试题解析

? ?维数组

数组名就是数组?元素(第?个元素)的地址。 sizeof(数组名)---->sizeof中单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??,单位是字节. &数组名(如&a),这?的数组名表?整个数组,取出的是整个数组的地址(整个数组的地址和数组?元素 的地址是有区别的) 除此之外,任何地?使?数组名,数组名都表??元素的地址。

代码语言:javascript
复制
char* arr1[10];     //存放字符指针的指针数组
int* arr2[10];      //存放整型指针的指针数组
int(*arr3[5])[6]  ;//存放数组指针的数组指针数组
int(*arr4[5])(int);//存放函数指针的函数指针数组
代码语言:javascript
复制
int main()
{
	int a[] = { 6,7,8,9,10 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a + 0));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(a[1]));
	printf("%d\n", sizeof(&a));
	printf("%d\n", sizeof(*&a));
	printf("%d\n", sizeof(&a + 1));
	printf("%d\n", sizeof(&a[0]));
	printf("%d\n", sizeof(&a[0] + 1));
}

细解:

  1. printf(“%d\n”, sizeof(arr));//20 sizeof中单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??,单位是字节.
  2. printf(“%zd\n”, sizeof(a + 0));4/8 a没有单独放在sizeof内部,也没有&取地址,因此a表示的就是数组首元素的地址,a+0还是首元素的地址 ,是地址,那么大小就是 4/8,地址大小只与环境有关,32位是4个字节,64位就是8个字节
  3. printf(“%zd\n”, sizeof(*a)); //4 a表示的就是数组首元素的地址,*a 就是首元素,首元素是6,6的类型是int,大小就是4个字节
  4. printf(“%zd\n”, sizeof(a + 1));//4/8 a表示的就是数组首元素的地址,a+1就是第二个元素的地址,这里的计算的是第二个元素的地址的大小,地址大小只与环境有关,32位是4个字节,64位就是8个字节4/8
  5. printf(“%zd\n”, sizeof(a[1]));//4 a[1]是数组的第二个元素(7),大小是4个字节
  6. printf(“%zd\n”, sizeof(&a));//4/8 &a - 取出的是数组的地址,但是数组的地址也是地址,是地址,大小就是4/8个字节
  7. printf(“%zd\n”, sizeof(*&a));//20 两种方法:1. &* 抵消 2.&a 的类型是数组指针,int(*)[5]*&a就是对数组指针解引用访问一个数组的大小,是20个字节
  8. printf(“%zd\n”, sizeof(&a + 1));//4/8 &a+1是跳过整个数组后的一个地址,是地址,大小就是4/8个字节
  9. printf(“%zd\n”, sizeof(&a[0]));//4/8 &a[0]是数组第一个元素的地址,是地址,大小就是4/8个字节
  10. printf(“%zd\n”, sizeof(&a[0] + 1));//4/8 &a[0] + 1 是第二个元素的地址,大小就是4/8个字节

?字符数组

?? sizeof计算字符数组
代码语言:javascript
复制
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	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));
	return 0;
}

细解:

  1. printf(“%d\n”, sizeof(arr)); //6 sizeof中单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??,单位是字节.,6个字节
  2. printf(“%d\n”, sizeof(arr + 0));// 4/8 arr+0 是数组第一个元素的地址,是地址,大小就是4/8个字节
  3. printf(“%d\n”, sizeof(* arr));// 1* *arr是首元素,计算的是首元素的大小,字符a的类型是char,是1个字节
  4. printf(“%d\n”, sizeof(arr[1]));// 1 arr[1] 是第二个元素字符b,类型是char是1个字节
  5. printf(“%d\n”, sizeof(&arr));//4/8 &arr取得是整个数组的地址,是地址,大小就是4/8个字节。
  6. printf(“%d\n”, sizeof(&arr + 1));//4/8 &arr取得是整个数组的地址,&arr+1跳过整个数组,跳过了6个数组的元素,指向f后面,也是地址,大小就是4/8个字节。

???strlen计算字符数组
代码语言:javascript
复制
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	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));
	
}
  1. printf(“%d\n”, strlen(arr));//随机值 arr为数组名,也就是数组首元素的地址,但是字符数组中没有\0,strlen将会继续查找\0,但是\0是不知道在什么位置出现,所以无法计算,为随机值。
  2. printf(“%d\n”, strlen(arr + 0));//随机值 arr+0也是数组首元素的地址,但是字符数组中没有\0,strlen将会继续查找\0,但是\0是不知道在什么位置出现,所以无法计算,为随机值。并且与前面的随机值一样
  3. printf(“%d\n”, strlen(*arr));//非法访问
代码语言:javascript
复制
	                     //'a'-97
	//printf("%d\n", strlen(*arr));//err
	//                   //'b'-98
	//printf("%d\n", strlen(arr[1]));//err

注意 *arr取出来的是字符a,字符a对应的ASCII码值为97,而97为内存空间里的一个地址, size_t strlen ( const char * str );,内存空间这块97地址传给strlen,strlen能直接访问这块不属于他的空间吗? 答案是不可以,因为它不属于strlen的作用域==一旦访问了就成非法访问了。(当然,arr[1]也是如此)

  1. printf(“%d\n”, strlen(arr[1]));//非法访问 注意 *arr1[1]取出来的是字符b,字符a对应的ASCII码值为98,而98为内存空间里的一个地址, size_t strlen ( const char * str );,内存空间这块97地址传给strlen,strlen能直接访问这块不属于他的空间吗? 答案是不可以,因为它不属于strlen的作用域一旦访问了就成非法访问了,编译器就会报错–>访问冲突
  1. printf(“%d\n”, strlen(&arr));//随机值 &arr虽然是数组的地址,但是也是指向数组的起始位置,那就是从数组首元素开始找嘛,没有\0,找不到,那就是随机值了
  2. printf(“%zd\n”, strlen(&arr + 1));//随机值-6 &arr+1跳过整个数组,也就是跳过6个char类型的元素从f后面的地址开始查找\0,因此相比从头开始找少了6个元素
  1. printf(“%d\n”, strlen(&arr[0] + 1));//随机值-1 &a[0] + 1 是第二个元素的地址,比随机值少一个元素a

? 字符串数组

? ? sizeof计算字符串数组
代码语言:javascript
复制
int main()
{
	char arr[] = "abcdef";

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

	return 0;
}

解析:char arr[] = “abcdef\0”//字符串别忘记有\0,也占用一个char空间

  1. printf(“%zd\n”, sizeof(arr));//7 数组名arr单独放,计算的是整个数组的大小,7个字节
  2. printf(“%zd\n”, sizeof(arr + 0));//4/8 arr+0是数组首元素的地址,地址的大小是4/8个字节
  3. printf(“%zd\n”, sizeof(*arr));//1 *arr是数组的首元素,这里计算的是首元素(a)的大小 ,类型为char,大小为1
  4. printf(“%zd\n”, sizeof(arr[1]));//1 *arr是数组的首元素,这里计算的是第二个元素(b)的大小 ,大小为1
  5. printf(“%zd\n”, sizeof(&arr));//4/8 &arr - 是数组的地址,数组的地址也是地址,是地址就是4/8个字节
  6. printf(“%zd\n”, sizeof(&arr + 1));//4/8 &arr+1,跳过整个数组,指向了数组的后边,4/8
  7. printf(“%zd\n”, sizeof(&arr[0] + 1));//4/8 &arr[0] + 1是第二个元素的地址 是地址,那就是4/8</kbd>

小结

  1. sizeof计算变量或类型的大小,单位是字节(byte)%zd格式说明符用于打印size_t类型的值,这也是sizeof的返回类型。
  2. 字符串常量"abcdef"实际上是一个char数组,最后一个元素是'\0'。所以sizeof(arr)算出的是整个字符串占用的空间,包含结束符,即7个字节。
  3. sizeof操作数是数组时,它计算的是整个数组占用的字节数。当操作数是指针时,它计算的是指针变量本身占用的字节数,与它指向的对象大小无关。
? ? ? strlen计算字符串数组
代码语言:javascript
复制
int main()
{
	char arr[] = "abcdef";

	printf("%zd\n", strlen(arr));//arr也是数组首元素的地址 6
	printf("%zd\n", strlen(arr + 0));//arr + 0是数组首元素的地址,6
	//printf("%zd\n", strlen(*arr));//?传递是'a'-97,//err
	//printf("%zd\n", strlen(arr[1]));//?'b'-98//err
	printf("%zd\n", strlen(&arr));//6, &arr虽然是数组的地址,但是也是指向数组的起始位置
	printf("%zd\n", strlen(&arr + 1));//随机值
	printf("%zd\n", strlen(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址 - 5

	return 0;
}
  1. printf(“%zd\n”, strlen(arr));// 6 arr也是数组首元素的地址,从a地址开始,直到\0停止,并且\0不计算在内。
  2. printf(“%zd\n”, strlen(arr + 0));//6 arr + 0是数组首元素的地址,也是从a地址开始,直到\0停止,并且\0不计算在内。
  3. printf(“%zd\n”, strlen(*arr));//访问冲突 *arr传递是'a'-97,字符a对应的ASCII码值为97,而97为内存空间里的一个地址,不属于数组arr的空间,那么就会访问冲突
  4. printf(“%zd\n”, strlen(arr[1]));//访问冲突 注意: (同样在字符串中,该结论同样适用)arr[1],因为arr[1]取出来的是字符'b',字符'b'的ASCII码值98同样是一个地址,但不是字符串起始地址。 传递*arrarr[1]strlen,strlen是无法直接访问这些地址处的内存空间的,因为这些地址都不属于strlen的作用域。 而arr名本身,以及arr地址&arr,都是字符串的起始地址,所以可以作为strlen函数的参数。

?小结:

strlen的参数必须是字符串的起始地址,才能正确计算长度。

  • arrarr+0都是数组首元素的地址,传给strlen计算长度都是6
  • *arrarr[1]访问到的是字符'a''b',传给strlen会报错
  • &arr虽然是数组的地址,但也指向数组第一个元素,所以strlen(&arr)可以正确计算长度
  • &arr+1会指向字符串的其他位置,传给strlen会产生随机值或者错误结果

? 指针变量

?? sizeof计算指针变量
代码语言:javascript
复制
int main()
{
	char* p = "abcdef";
	printf("%zd\n", sizeof(p));
	printf("%zd\n", sizeof(p + 1));
	printf("%zd\n", sizeof(*p));
	printf("%zd\n", sizeof(p[0]));
	printf("%zd\n", sizeof(&p));
	printf("%zd\n", sizeof(&p + 1));
	printf("%zd\n", sizeof(&p[0] + 1));
	return 0;
}
  1. printf(“%zd\n”, sizeof§);//4/8 pchar*类型的指针变量,存放的是a的地址,是地址,那就是4或8字节。
  1. printf(“%zd\n”, sizeof(p + 1));4/8 p+1计算的是'b'字符的地址,依然是地址,大小也是4/8个字节。
  1. printf(“%zd\n”, sizeof(*p));// 1字节 p访问的是'a'字符,sizeof计算的是字符类型大小,是1字节。
  1. printf(“%zd\n”, sizeof(p[0]));//1字节 p[0]等价于*(p+0),(p+0)也等价pp[0]-- > *(p+0) - >*p也是访问'a'字符是1字节。
  1. printf(“%zd\n”, sizeof(&p));//4/8 &p是p指针变量的地址,是地址,大小也是4/8个字节。
  1. printf(“%zd\n”, sizeof(&p + 1));//4/8个字节 &p + 1是指向p指针变量后面的空间,也是地址,是4/8个字节
  1. printf(“%zd\n”, sizeof(&p[0] + 1));//4/8 &p[0]+1访问'b'字符的地址,是地址就是4/8个字节。

小结: sizeof运算符计算:

  • 指针变量时,计算的是指针变量本身占用的内存空间,4或8字节。
  • 字符或整数类型时,计算的是该类型变量占用的内存大小,如char是1字节。
  • 地址时,地址也是指针类型,计算的还是指针类型占用的内存大小,4或8字节。
???strlen计算指针变量
代码语言:javascript
复制
int main()
{
	char* p = "abcdef";
	
	printf("%zd\n", strlen(p));
	printf("%zd\n", strlen(p + 1));
	//printf("%zd\n", strlen(*p));
	//printf("%zd\n", strlen(p[0]));
	printf("%zd\n", strlen(&p));
	printf("%zd\n", strlen(&p + 1));
	printf("%zd\n", strlen(&p[0] + 1));

	return 0;
}

运行该代码,打印strlen的结果解释:

  1. printf(“%zd\n”, strlen(p ));//6 因为p指向字符串"abcdef"的首地址,strlen计算字符串长度就是6
  2. printf(“%zd\n”, strlen(p + 1));//5 结果是5,因为p+1指向从'b'开始的字符串,长度是5
  3. printf(“%zd\n”, strlen(*p));//访问冲突 *p'a'字符, size_t strlen ( const char * str );strlen接受一个地址,‘a’`字符不是地址,如果是的话,那又是访问ASCII码值然后报错。
  4. printf(“%zd\n”, strlen(p[0])); //访问冲突 同理,p[0]也是字符,不能用strlen。
  5. printf(“%zd\n”, strlen(&p));随机值 &p是指针p的地址,strlen无法识别,结果是随机值。
  6. printf(“%zd\n”, strlen(&p + 1))随机值; 同理,&p+1找的是p后面地址,然后开始在p后面不断找\0,也是随机值。
  1. printf(“%zd\n”, strlen(&p[0] + 1));//5 因为&p[0]+1指向从'b'开始的字符串。

strlen只能计算字符串常量或以'\0'结束的字符串长度,其他情况如字符,地址都无法识别长度,可能导致随机值或报错。


3?? 总结

本小节我们学习了sizeof和strlen对一维字符数组,字符串数组和指针变量进行练习,我们应该对指针有了更多的理解,但是他们都离不开我们以下的知识点。sizeof用于计算变量或类型占用的内存空间(以字节为单位),它是编译期就能确定的常量。 strlen用于计算字符串长度,但仅限于以'\0'作为字符串结束标志的字符串。它需要在运行期扫描字符串计算长度,他接收地址,不能接收数值,否则就会访问编译器就会报错,造成访问冲突。

数组名就是数组?元素(第?个元素)的地址。 sizeof(数组名)---->sizeof中单独放数组名,这?的数组名表?整个数组,计算的是整个数组的??,单位是字节. &数组名(如&a),这?的数组名表?整个数组,取出的是整个数组的地址(整个数组的地址和数组?元素 的地址是有区别的) 除此之外,任何地?使?数组名,数组名都表??元素的地址。

如果你看到这里,让我们把掌声送给我们自己 感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞?

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1?? sizeof和strlen的对?
    • ??sizeof
      • ?? ?? strlen
        • ?? ?? ??sizeof 和 strlen的对?
        • 2?? 数组和指针笔试题解析
          • ? ?维数组
            • ?字符数组
              • ?? sizeof计算字符数组
              • ???strlen计算字符数组
            • ? 字符串数组
              • ? ? sizeof计算字符串数组
              • ? ? ? strlen计算字符串数组
            • ? 指针变量
              • ?? sizeof计算指针变量
              • ???strlen计算指针变量
          • 3?? 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com