深入理解指针(6)

一、sizeof和strlen的对比

(一)sizeof

在学习操作符的时候,我们学习了sizeof,说明sizeof是一个操作符,绝对不是函数

sizeof是用来计算变量所占内存空间大小的,单位是字节;如果操作数是类型的话,计算的是适用类型创建的变量所占空间的大小。

sizeof只关注占用内存的大小,不在乎内存中存放什么数据。

而且,sizeof括号中的表达式是不会参与计算的。

cpp 复制代码
int main()
{
	int a = 10;
	short c = 8;
	printf("%zd\n", sizeof(a));
	printf("%zd\n", sizeof a);
	printf("%zd\n", sizeof(int));
	printf("%zd\n", sizeof(c = a - 2));
	printf("%d\n", c);
	return 0;
}

(二)strlen

strlen是C语言的库函数,功能是求字符串长度:

cpp 复制代码
size_t strlen(const char* str);

统计的是从strlen函数的参数str中这个地址开始向后,\0之前字符串中字符的个数。

strlen函数会一直向后找**\0**字符,直到找到为止,所以可能存在越界查找。

由于字符串的后面会自动跟一个\0,所以strlen计算的长度是准确的;但是,如果是由单个字符组成的话,那就有可能出现越界查找了。

cpp 复制代码
int main()
{
	char arr1[] = "abc";
	char arr2[] = { 'a','b','c' };
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));

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

(三)sizeof和strlen的对比

二、数组和指针笔试题解析(1)

在做题目之前,我们再次对数组名的有关知识进行复习:

数组名的理解:

数组名是数组首元素(第一个元素)的地址

但是有2个例外:

  1. sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节

  2. &数组名 - 数组名表示整个数组,取出的是整个数组的地址

除此之外,所以的数组名是数组首元素(第一个元素)的地址。

(一)一维数组

cpp 复制代码
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));
	//16 sizeof()中的数组名代表整个数组
	printf("%d\n", sizeof(a + 0));
	//4 or 8 这里的a是首元素地址,类型是int*,a+0还是首元素地址,只要是地址,大小就是4 or 8
	printf("%d\n", sizeof(*a));
	//4 这里的a是首元素地址,解引用后是整形,整形占4个字节
	printf("%d\n", sizeof(a + 1));
	//4 or 8 这里的a是首元素地址,+1就是跳过一个整型,a+1也就是第二个元素的地址
	printf("%d\n", sizeof(a[1]));
	//4 a[1]就是第二个元素,为整型
	printf("%d\n", sizeof(&a));
	//4 or 8 &a是整个数组的地址,整个数组的地址也是地址,所以是4 or 8
	printf("%d\n", sizeof(*&a));
	//16 1.*&相互抵消,计算的是整个数组的大小;2.&a是数组指针类型,解引用后访问的是数组
	printf("%d\n", sizeof(&a+1));
	//4 or 8 &a是整个数组的地址,+1就是跳过整个数组的地址后那个位置的地址,是地址就是4 or 8
	printf("%d\n", sizeof(&a[0]));
	//4 or 8 首元素地址,大小4 or 8
	printf("%d\n", sizeof(&a[0] + 1));
	//4 or 8 数组第二个元素的地址,大小4 or 8
	return 0;
	//这里要强调一下:arr与&arr的地址数值是一样的,差别就在于二者类型不同,
	//arr是int*,&arr则是(int)(*)[4]
}

16 sizeof()中的数组名代表整个数组

4 or 8 这里的a是首元素地址,类型是int*,a+0还是首元素地址,只要是地址,大小就是4 or 8

4 这里的a是首元素地址,解引用后是整形,整形占4个字节

4 or 8 这里的a是首元素地址,+1就是跳过一个整型,a+1也就是第二个元素的地址

4 a[1]就是第二个元素,为整型

4 or 8 &a是整个数组的地址,整个数组的地址也是地址,所以是4 or 8

16 1.*&相互抵消,计算的是整个数组的大小;2.&a是数组指针类型,解引用后访问的是数组

4 or 8 &a是整个数组的地址,+1就是跳过整个数组的地址后那个位置的地址,是地址就是4 or 8

4 or 8 首元素地址,大小4 or 8

4 or 8 数组第二个元素的地址,大小4 or 8

这里要强调一下:

arr与&arr的地址数值是一样的,差别就在于二者类型不同,

arr是int*,&arr则是(int)(*)[4]

(二)字符数组

1.代码1

cpp 复制代码
//代码一
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	//6 sizeof()中的arr代表整个数组,大小就是整个数组的大小
	printf("%d\n", sizeof(arr + 0));
	//4 or 8 arr是首元素地址,arr+0还是首元素地址,是地址就是4 or 8
	printf("%d\n", sizeof(*arr));
	//1 *arr是首元素,char类型就是1个字节
	printf("%d\n", sizeof(arr[1]));
	//1 第二个元素
	printf("%d\n", sizeof(&arr));
	//4 or 8 整个数组的地址,是地址就是4 or 8
	printf("%d\n", sizeof(&arr + 1));
	//4 or 8 跳过了整个数组,指向了后面的空间,是地址就是4 or 8
	printf("%d\n", sizeof(&arr[0] + 1));
	//4 or 8 第二个元素的地址值 是地址就是4 or 8
	return 0;
}

2.代码2

cpp 复制代码
//代码二
#include<string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));
	//随机 arr是首元素地址,按理说会计算整个字符串长度,但是因为单个字符,
	// 所以无\0,就会导致越界访问,结果是随机的
	printf("%d\n", strlen(arr + 0));
	//随机 arr+0是首元素地址,按理说会计算整个字符串长度,但是因为单个字符,
	// 所以无\0,就会导致越界访问,结果是随机的
	printf("%d\n", strlen(*arr));
	//有问题 *arr就是首元素,'a'的Ascll码值是97,就相当于把97作为地址传递给了strlen
	//函数,strlen得到的是野指针,此代码就是有问题的
	printf("%d\n", strlen(arr[1]));
	//有问题 arr[1]是第二个元素,Ascall码值就是98,就相当于把98作为地址传递给了strlen
	//函数,strlen得到的是野指针,此代码就是有问题的
	printf("%d\n", strlen(&arr));
	//随机值x &arr是整个数组的地址,起始位置是数组的第一个元素的位置,所以会得到随机值x
	printf("%d\n", strlen(&arr + 1));
	//随机值x-6
	printf("%d\n", strlen(&arr[0] + 1));
	//从第二个元素开始向后统计的,得到的也就是随机值x-1
	return 0;
}

随机 arr是首元素地址,按理说会计算整个字符串长度,但是因为单个字符,所以无\0,就会导致越界访问,结果是随机的;

随机 arr+0是首元素地址,按理说会计算整个字符串长度,但是因为单个字符,所以无\0,就会导致越界访问,结果是随机的;

有问题 *arr就是首元素,'a'的Ascll码值是97,就相当于把97作为地址传递给了strlen函数,strlen得到的是野指针,此代码就是有问题的;

有问题 arr[1]是第二个元素,Ascall码值就是98,就相当于把98作为地址传递给了strlen函数,strlen得到的是野指针,此代码就是有问题的;

随机值x &arr是整个数组的地址,起始位置是数组的第一个元素的位置,所以会得到随机值x;

随机值x-6;

从第二个元素开始向后统计的,得到的也就是随机值x-1

3.代码3

cpp 复制代码
//代码三
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));
	//7 arr在sizeof()里面,代表整个数组,sizeof计算时,会计算上字符串最后的\0
	printf("%d\n", sizeof(arr + 0));
	//4 or 8 arr代表首元素地址,arr+0还是首元素地址,是地址就是4 or 8
	printf("%d\n", sizeof(*arr));
	//1 arr是数组首元素地址,*arr就是首元素,char类型是1字节
	printf("%d\n", sizeof(arr[1]));
	//1 arr[1]是数组的第二个元素
	printf("%d\n", sizeof(&arr));
	//4 or 8 &arr是整个数组的地址,是地址就是4 or 8
	printf("%d\n", sizeof(&arr + 1));
	//4 or 8 +1是跳过整个数组,还是地址,是地址就是4 or 8
	printf("%d\n", sizeof(&arr[0] + 1));
	//4 or 8 +1是跳过一个元素的地址,所以现在是该数组第二个元素的地址,是地址就是4 or 8
	return 0;
}

7 arr在sizeof()里面,代表整个数组,sizeof计算时,会计算上字符串最后的\0;

4 or 8 arr代表首元素地址,arr+0还是首元素地址,是地址就是4 or 8

1 arr是数组首元素地址,*arr就是首元素,char类型是1字节

1 arr[1]是数组的第二个元素

4 or 8 &arr是整个数组的地址,是地址就是4 or 8

4 or 8 +1是跳过整个数组,还是地址,是地址就是4 or 8

4 or 8 +1是跳过一个元素的地址,所以现在是该数组第二个元素的地址,是地址就是4 or 8

4.代码4

cpp 复制代码
//代码四
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));
	//6 arr代表首元素地址,从首元素一直计算到\0为止
	printf("%d\n", strlen(arr + 0));
	//6 arr+0还是首元素地址
	printf("%d\n", strlen(*arr));
	//报错 'a'的Ascll码是97,无意义
	printf("%d\n", strlen(arr[1]));
	//报错 'b'的Ascll码是98,无意义
	printf("%d\n", strlen(&arr));
	//6 &arr是整个数组的地址,也就是从该数组第一个元素开始计算到\0为止
	//这里说明一下,&arr的类型是char(*)[7] 但是strlen()中的类型是
	//const char* str 说明发生了强制类型转换
	printf("%d\n", strlen(&arr + 1));
	//随机值 因为+1跳过的是整个数组,那么何时遇到\0是不确定的
	printf("%d\n", strlen(&arr[0] + 1));
	//5 从该数组第二个元素开始计算
	return 0;
}

6 arr代表首元素地址,从首元素一直计算到\0为止

6 arr+0还是首元素地址

报错 'a'的Ascll码是97,无意义

报错 'b'的Ascll码是98,无意义

6 &arr是整个数组的地址,也就是从该数组第一个元素开始计算到\0为止,这里说明一下,&arr的类型是char(*)[7] 但是strlen()中的类型是,const char* str 说明发生了强制类型转换

随机值 因为+1跳过的是整个数组,那么何时遇到\0是不确定的

5 从该数组第二个元素开始计算

5.代码5

cpp 复制代码
//代码五
#include <string.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));
	//4 or 8 p是指针变量
	printf("%d\n", sizeof(p + 1));
	//4 or 8 p代表的是首元素,也就是a的地址,所以p+1是b的地址
	printf("%d\n", sizeof(*p));
	//1 p的类型是char*,解引用完就是char类型
	printf("%d\n", sizeof(p[0]));
	//1 ------1------ p[0]可以转换为*(p+0),所以代表第一个元素a
	//  ------2------ 把字符串想象成数组,p可以理解为数组名,p[0]就是首元素
	printf("%d\n", sizeof(&p));
	//4 or 8 取出的是p的地址,为二级指针类型,是地址就是4 or 8
	printf("%d\n", sizeof(&p + 1));
	//+1是跳过p指针变量后的地址,是地址就是4 or 8
	printf("%d\n", sizeof(&p[0] + 1));
	//代表第二个字符的地址,是地址就是4 or 8
	return 0;
}

4 or 8 p是指针变量

4 or 8 p代表的是首元素,也就是a的地址,所以p+1是b的地址

1 p的类型是char*,解引用完就是char类型

1 ------1------ p[0]可以转换为*(p+0),所以代表第一个元素a

------2------ 把字符串想象成数组,p可以理解为数组名,p[0]就是首元素

4 or 8 取出的是p的地址,为二级指针类型,是地址就是4 or 8

+1是跳过p指针变量后的地址,是地址就是4 or 8

代表第二个字符的地址,是地址就是4 or 8

6.代码6

cpp 复制代码
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));
	//6
	printf("%d\n", strlen(p + 1));
	//5
	printf("%d\n", strlen(*p));
	//报错 'a'的Ascll码是97,无意义
	printf("%d\n", strlen(p[0]));
	//报错 p[0]-->*(p+0)-->*p
	printf("%d\n", strlen(&p));
	//&p是指针变量p的地址,和字符串"abcdef"关系不大
	printf("%d\n", strlen(&p + 1));
	//随机值 而且与&p无关系,因为两者地址中间对于\0的情况是未知的
	printf("%d\n", strlen(&p[0] + 1));
	//5 代表第二个元素的地址,也就是从b开始计算
	return 0;
}

6

5

报错 'a'的Ascll码是97,无意义

报错 p[0]-->*(p+0)-->*p

&p是指针变量p的地址,和字符串"abcdef"关系不大

随机值 而且与&p无关系,因为两者地址中间对于\0的情况是未知的

5 代表第二个元素的地址,也就是从b开始计算

相关推荐
Xの哲學2 小时前
Linux VxLAN深度解析: 从数据平面到内核实现的全面剖析
linux·服务器·算法·架构·边缘计算
TL滕2 小时前
从0开始学算法——第十八天(分治算法练习)
笔记·学习·算法
一杯美式 no sugar2 小时前
数据结构——栈
c语言·数据结构·
月明长歌2 小时前
【码道初阶】【LeetCode 958】判定完全二叉树:警惕 BFS 中的“管中窥豹”陷阱
算法·leetcode·宽度优先
蒙奇D索大3 小时前
【数据结构】考研408 | 冲突解决精讲: 拉链法——链式存储的艺术与优化
数据结构·笔记·考研·改行学it
一直都在5723 小时前
数据结构入门:二叉排序树的构建与相关算法
数据结构·算法
_Minato_3 小时前
数据结构知识整理——复杂度的计算
数据结构·经验分享·笔记·算法·软考
listhi5203 小时前
针对燃油运输和车辆调度问题的蚁群算法MATLAB实现
前端·算法·matlab
月明长歌3 小时前
【码道初阶】【LeetCode 102】二叉树层序遍历:如何利用队列实现“一层一层切蛋糕”?
java·数据结构·算法·leetcode·职场和发展·队列