深入理解指针(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开始计算

相关推荐
AlenTech2 分钟前
198. 打家劫舍 - 力扣(LeetCode)
算法·leetcode·职场和发展
Z1Jxxx6 分钟前
0和1的个数
数据结构·c++·算法
ldccorpora6 分钟前
Chinese News Translation Text Part 1数据集介绍,官网编号LDC2005T06
数据结构·人工智能·python·算法·语音识别
重生之后端学习7 分钟前
21. 合并两个有序链表
java·算法·leetcode·链表·职场和发展
源代码•宸7 分钟前
Leetcode—1266. 访问所有点的最小时间【简单】
开发语言·后端·算法·leetcode·职场和发展·golang
朔北之忘 Clancy25 分钟前
2020 年 6 月青少年软编等考 C 语言二级真题解析
c语言·开发语言·c++·学习·青少年编程·题解·尺取法
YuTaoShao30 分钟前
【LeetCode 每日一题】712. 两个字符串的最小ASCII删除和——(解法一)记忆化搜索
算法·leetcode·职场和发展
HABuo43 分钟前
【Linux进程(五)】进程地址空间深入剖析-->虚拟地址、物理地址、逻辑地址的区分
linux·运维·服务器·c语言·c++·后端·centos
知乎的哥廷根数学学派1 小时前
基于物理信息嵌入与多维度约束的深度学习地基承载力智能预测与可解释性评估算法(以模拟信号为例,Pytorch)
人工智能·pytorch·python·深度学习·算法·机器学习