当前位置: 首页 > article >正文

【C语言】指针(6)

前言:上期我们介绍了回调函数的实现,qsort快速排序函数的使用以及qsort的模拟实现。这期我们主要通过一些小练习和面试题来巩固之前所学的知识。
往期文章:
指针1

指针2

指针3

指针4

指针5

文章目录

  • 一,sizeof和strlen的辨析
    • 1,sizeof
    • 2,strlen
    • 3,sizeof与strlen的对比总结

一,sizeof和strlen的辨析

1,sizeof

前面讲操作符的时候我们也介绍了sizeof,sizeof是用来计算变量所占内存空间大小的操作符,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

#include<stdio.h>
int main()
{
	int x = 1, z = 5;
	double y = 0;
	printf("%zd\n", sizeof(x));//打印sizeof的返回值需使用%zd格式来打印
	printf("%zd\n", sizeof(int));
	printf("%zd\n", sizeof(y));
	printf("%zd\n", sizeof(double));
	printf("%zd\n", sizeof(x + z));
	return 0;
}

在这里插入图片描述
从运行结果来看我们知道sizeof不在乎内存中放什么数据只关注其所占用空间的大小。还有一点需要注意的是sizeof里面如果有表达式是不会进行计算的! 这一点我们可以从最后一行printf的运行结果看出来。

2,strlen

strlen是C语言中的库函数使用它时需要包含<string.h>这个头文件,它的功能是求字符串长度,是统计一个字符串中\0之前的字符个数的函数。它的函数原型如下:size_t strlen ( const char * str );
但要注意一点,strlen会从字符串的首地址开始依次向后查找\0直到找到为止,如果没有\0就会一直查找下去直至程序奔溃。

#include<stdio.h>
int main()
{
	char arr1[3]={'a','b','c'};//没有存放\0
	char arr2[]="abc";//实质存放的是"a b c \0"
	printf("%zd\n",strlen(arr1));
	printf("%zd\n",strlen(arr2));

	printf("%zd\n",sizeof(arr1));
	printf("%zd\n",sizeof(arr2));
	return 0;
}

在这里插入图片描述
我们来分析一下运行结果:

首先 char arr1[3]={'a','b','c'};
这个数组中是没有\0的所以在我们使用strlenarr1的时候strlen会从字符a开始往后查找\0arr1里面确实没有\0那么这个时候编译器也不知道到底是多少所以就给出了一个随机值35

而如果是sizeof去求arr1的话这里就要注意了arr1是数组首元素的地址,将首元素的地址放在sizeof里面求得的是整个数组得大小;整个数组的大小就等于数组元素个数*数组的类型。上面的例子就是3*1=3所以结果为3

接着我们来看看:char arr2[]="abc";//实质存放的是"a b c \0"这个数组
首先使用strlen去求arr2的时候由于arr2中实质上存放的是a,b,c,\0;strlen统计的是\0之前的字符个数所以是3这毫无疑问。而使用sizeof去求arr2的时候与上面使用sizeof去求arr1的时候一样求的是整个数组的大小,而为什么这里是4而上面求得的是3呢?其实就是多了一个\0。这就是为什么我们前面说arr2实质上存放的是a,b,c\0。

对于数组名的含义还不了解的话可以去看我的指针3那篇文章!

3,sizeof与strlen的对比总结

sizeofstrlen
1,sizeof是操作符1,strlen是库函数需要包含头文件<string.h>
2,sizeof计算的是操作数所占内存的大小,单位是字节2. srtlen是求字符串长度的,统计的是 \0 之前字符的字符个数
3,sizeof不关注内存中存放什么数据,只关注其所占空间的大小3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能越界

有了上面的知识来些笔试题练练手:

#include<stdio.h>
int main()
{
	int a[] = {1,2,3,4}; 
	printf("%zd\n",sizeof(a)); 
	printf("%zd\n",sizeof(a+0)); 
	printf("%zd\n",sizeof(*a)); 
	printf("%zd\n",sizeof(a+1)); 
	printf("%zd\n",sizeof(a[1])); 
	printf("%zd\n",sizeof(&a)); 
	printf("%zd\n",sizeof(*&a)); 
	printf("%zd\n",sizeof(&a+1));
	printf("%zd\n",sizeof(&a[0])); 
	printf("%zd\n",sizeof(&a[0]+1));
return 0;
}

分析如下:

  1. a是数组首元素地址,单独放在sizeof里面计算整个数组大小 16
  2. a+0拿到的是首元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节 这与你的平台有关x64 8 x86 4
  3. *a是对首元素解引用 放在sizeof里面计算元素的类型大小 4
  4. a+1拿到的就是第二个元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  5. a[1]拿到的是数组中的第二个元素 计算的是元素的大小 所以是 4
  6. &a取地址a取出的是整个数组的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  7. *&a取完地址再解引可以抵消就相当于sizeof(a)与第一种情况一样 16
  8. &a+1取出整个数组的地址再加1就相当于跳过一个数组指向该数组的末尾 但其本质拿到的还是一个地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  9. &a[0]取出首元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  10. &a[0]+1 取出第二个元素的地址 情况同上

以上的关键就是知道求的是地址的大小 整个元素的大小 还是元素大小
由于我使用的是x64平台 所以地址打印的都为8
答案如下:
在这里插入图片描述

#include<stdio.h>
int main()
{
//字符数组
	char arr[] = {'a','b','c','d','e','f'}; 
	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;
}

分析如下

  1. arr是数组的首元素的地址 单独放在sizeof里面计算整个数组大小 6
  2. arr+0拿到的是首元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  3. *arr是对首元素解引用 放在sizeof里面计算元素的类型大小 所以是1
  4. arr[1]拿到的是数组中的第二个元素 计算的是元素的大小 所以是 1
  5. &arr取地址a取出的是整个数组的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  6. &arr+1取出整个数组的地址再加1就相当于跳过一个数组指向该数组的末尾 但其本质拿到的还是一个地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  7. &arr[0]+1拿到的是第二个元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节

答案如下:
在这里插入图片描述

#include<stdio.h>
int main()
{
	char arr[] = {'a','b','c','d','e','f'}; 
	printf("%zd\n", strlen(arr)); 
	printf("%zd\n", strlen(arr+0)); 
	printf("%zd\n", strlen(*arr)); 
	printf("%zd\n", strlen(arr[1])); 
	printf("%zd\n", strlen(&arr)); 
	printf("%zd\n", strlen(&arr+1)); 
	printf("%zd\n", strlen(&arr[0]+1)); 
    return 0;
}

分析如下:

  1. arr是数组首元素的地址,意味着是从首元素开始统计直至\0,但这个数组内部是没有\0的 所以计算出来的结果就是随机值
  2. arr+0拿到的是首元素的地址,情况同上结果是随机值
  3. *arr是对首元素解引用拿到的是第一个元素 这个时候就要注意了!strlen接受的参数应该是指针是地址,而这里解引用拿到的是一个字符 字符的本质是ASCAII码值;首元素是字符a对应的ASCAII码码值为97 将97这个地址传给strlen;而97处的地址对应的空间是不属于当前程序的,也就相当于给strlen里传入了一个野指针 这时程序就会报错!
  4. arr[1]拿到的是数组中的第二个元素 strlen接受的不是地址 所以结果同上会报错!
  5. &arr取地址a取出的是整个数组的地址 整个元素的地址是从首元素开始的但这个数组内是没有\0的所以情况跟第一种是一样的!产生随机值。
  6. &arr+1取出整个数组的地址再加1就相当于跳过一个数组指向该数组的末尾 只要数组内部无\0再怎么去查找都是找不到的 所以也是随机值但是会与从第一个元素开始查找得到得随机值相差6(6为元素个数)
  7. &arr[0]+1拿到的是第二个元素的地址 是随机值 会与从第一个元素开始查找得到得随机值差5

答案如下:
在这里插入图片描述

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abcdef";//实质存放着"a,b,c,d,e,f,\0"
	printf("%zd\n", strlen(arr)); 
	printf("%zd\n", strlen(arr+0)); 
	printf("%zd\n", strlen(*arr)); 
	printf("%zd\n", strlen(arr[1])); 
	printf("%zd\n", strlen(&arr)); 
	printf("%zd\n", strlen(&arr+1)); 
	printf("%zd\n", strlen(&arr[0]+1));
    return 0;
}

分析如下:

  1. arr是数组首元素的地址,意味着是从首元素开始统计直至\0 这次数组是含有\0的了 所以结果为6
  2. arr+0拿到的是首元素的地址 情况同上 结果为6
  3. *arr是对首元素解引用拿到的是第一个元素 会报错!
  4. arr[1]拿到的是数组中的第二个元素 strlen接受的不是地址 所以结果同上会报错!
  5. &arr取地址a取出的是整个数组的地址 整个元素的地址是从首元素开始的从第一个元素开始直到碰到\0 所以结果为6
    6.&arr+1取出整个数组的地址再加1就相当于跳过一个数组指向该数组的末尾 该函数本身含有\0但是跳过了整个数组之后再想找\0就不知道能不能找到了 所以是随机值。但是会与从第一个元素开始查找得到得随机值相差6(6为元素个数)
  6. &arr[0]+1拿到的是第二个元素的地址那就是从第二个元素开始统计 所以结果为5

答案如下:
在这里插入图片描述
同样将strlen换成sizeof会怎样呢?

#include<stdio.h>
int main()
{
	char arr[] = "abcdef";//实质存放着"a,b,c,d,e,f,\0"
	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;
}

分析如下:

  1. arr是数组的首元素的地址 单独放在sizeof里面计算整个数组大小 所以为7
  2. arr+0拿到的也是首元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  3. *arr是对首元素解引用 放在sizeof里面计算元素的类型大小 所以是1
  4. arr[1]是对首元素解引用 放在sizeof里面计算元素的类型大小 所以是1
  5. &arr取地址a取出的是整个数组的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  6. &arr+1取出整个数组的地址再加1就相当于跳过一个数组指向该数组的末尾 但其本质拿到的还是一个地址 所以是 4或8个字节
  7. &arr[0]+1拿到的是第二个元素’b’的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节

答案如下:
在这里插入图片描述
改成指针又会有什么变化呢?

#include<stdio.h>
int main()
{
	char *p = "abcdef"; //本质存放着"a,b,c,d,e,f,\0"
	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;
}

分析如下:

  1. p拿到的是首元素a的地址 从首元素开始统计直到\0 所以结果为6
  2. p+1拿到的是第二个元素的地址 从第二个元素开始统计 所以所以结果为5
  3. *p对首元素进行解引用不是一个地址 所以会报错
  4. p[0] 拿到首元素 情况同上不是一个地址 所以会报错
  5. &p 取地址p这时候取出来的就不是整个数组的地址了,因为p为指针变量它不是数组名 拿到p的地址后往后统计直到遇到\0拿编译器也不知道什么时候遇到\0 所以是随机值
  6. &p+1 跳过一个char *类型的指针变量p的地址,往后统计仍然不知道什么时候碰到\0 所以是随机值
  7. &p[0]+1相当于&*(p+0)+1==p+1 相当于拿到第二个元素的地址 从第二个元素开始统计 所以所以结果为5

答案如下:
在这里插入图片描述
注意!第3个结果是随机值:
&pp这个变量的地址,strlen就是从p这块空间的起始地址开始向后找\0p中存放的地址是不确定的,所有有没有\0,什么时候会遇到\0都不确定。

最后一个二维数组:

#include<stdio.h>
int main()
{
	int a[3][4] = {0}; 
	printf("%zd\n",sizeof(a)); 
	printf("%zd\n",sizeof(a[0][0])); 
	printf("%zd\n",sizeof(a[0])); 
	printf("%zd\n",sizeof(a[0]+1)); 
	printf("%zd\n",sizeof(*(a[0]+1))); 
	printf("%zd\n",sizeof(a+1)); 
	printf("%zd\n",sizeof(*(a+1))); 
	printf("%zd\n",sizeof(&a[0]+1)); 
	printf("%zd\n",sizeof(*(&a[0]+1))); 
	printf("%zd\n",sizeof(*a)); 
	printf("%zd\n",sizeof(a[3]));
    return 0;
}

分析如下:

  1. a是二维数组的数组名 将数组名单独放在sizeof里面计算整个数组大小 所以为3*4*4=48
  2. a[0][0]拿到的是第0行第0个元素 放在sizeof里面计算元素的类型大小 所以是4
  3. a[0]是二维数组的第一个元素 也就是第一行这个一维数组的数组名 将数组名 单独放在sizeof里面计算的是这一行的一维数组的大小 所以为4*4=16
  4. a[0]+1首先a[0]是数组名是第一行第一个元素的地址 +1跳过一个元素指向下一个元素的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  5. *(a[0]+1))相当于a[0][1]是计算一个元素大小 所以是4
  6. a+1a是二维数组的首元素地址,也就是二维数组中整个第一行的地址+1就跳到第二行 指向第二行的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节这里要和第二行第一个元素的地址区分开 区分行地址 和单个元素的地址 行地址就是整个一维数组的地址!
  7. *(a+1)是第二行的地址,*(a+1)得到的就是第二行
    int(*)[4] 对数组指针解引用,放一个数组,就是一行的一维数组
    *(a+1) == a[1], a[1]是第二行的数组名,sizeof(arr[1])计算的是第二行的大小所以为4*4=16
  8. &a[0]+1首先&a[0]就是拿到第一行的地址 +1偏移1就拿到了第二行的地址 是地址那么使用sizeof计算得到的大小就是 4或8个字节
  9. *(&a[0]+1) 首先&a[0]+1拿到第二行的地址 即a[1] a[1]是第二行的一维数组的数组名 放在sizeof里面计算一行的大小 所以为4*4=16
  10. *a首先a为二维数组首元素的地址 代表第一行的地址 再解引用拿到的就是第一行 *a==*(a+0) 所以sizeof(*(a+0))计算的是一行的大小 所以为4*4=16
  11. a[3]看到a[3]有人就会不自觉的想到数组越界(包括博主也一样),认为它是非法访问 但是放在这就不一样了 这里的a[3]根本就没有访问 这是由于sizeof内部的表达式是不计算的 所以求的还是一行的大小所以为4*4=16

最后我们给出答案:
在这里插入图片描述

到这strlen与sizeof相关的笔试题就讲完了,还有指针运算相关的笔试题由于篇幅放到下期。
感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!
在这里插入图片描述


http://www.kler.cn/a/559103.html

相关文章:

  • 《Keras 3 :使用 Vision Transformers 进行物体检测》:此文为AI自动翻译
  • Node.js中如何修改全局变量的几种方式
  • git,bash - 从一个远端git库只下载一个文件的方法
  • 架构对比分析
  • QT中经常出现的用法:组合
  • 【Linux系统】—— 冯诺依曼体系结构与操作系统初理解
  • vue3 elementUi Table 数据下拉懒加载
  • 37、深度学习-自学之路-自己搭建深度学习框架-2、自动梯度计算
  • Seata 分布式事务框架:从下载到实战配置全解析
  • 网页制作07-html,css,javascript初认识のhtml添加多媒体
  • 【Linux网络】认识协议(TCP/UDP)、Mac/IP地址和端口号、网络字节序、socket套接字
  • Blaze RangePartitioning 算子Native实现全解析
  • 现场可以通过手机或者pad实时拍照上传到大屏幕的照片墙现场大屏电子照片墙功能
  • Lua | 每日一练 (3)
  • vue文件没有name属性怎么被调用
  • 游戏开发 游戏开始界面
  • 【Blender】二、建模篇--06,曲线建模/父子级和蒙皮修改器
  • 简识MQ之Kafka、ActiveMQ、RabbitMQ、RocketMQ传递机制
  • MQTT实现智能家居------3、源码分析(超详细)
  • AI性能极致体验:通过阿里云平台高效调用满血版DeepSeek-R1模型