深入理解指针(4)

sizeof和strlen的比较

sizeof

在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。 sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据

复制代码
#include<stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof a);
	printf("%d\n", sizeof(int));
	return 0;
}

所以不管我们的数据是不是数据类型,编译器都会认为sizeof后面的是数据类型来进行处理

strlen

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

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

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

复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[3] = { 'a', 'b', 'c' };
	char arr2[] = "abc";
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));
	printf("%d\n", sizeof(arr1));
	printf("%d\n", sizeof(arr2));
	return 0;
}

这里strlen(arr1)意思就是计算arr1的字符串长度,但是arr1是字符串嘛?很显然不是,这是一个字符数组,数组中每一个元素都是字符,而字符是没有'\0'的,所以第一个就会一直计算下去,导致strlen越界访问栈区其他内存,直到碰巧找到 '\0'才停止,最终长度取决于栈区随机的内存内容,但每次运行代码时第一个返回值会有所不同

strlen(arr2)意思是计算arr2的字符串长度,arr2是一个字符串数组,字符串末尾默认有'\0',所以就返回字符串的个数,但是'\0'不算在其中,返回值为3

sizeof(arr1)意思是计算arr1的总字节数,有三个元素就返回3

sizeof(arr2)计算字符串的总字节数,返回字节个数,但'\0'要包含在其中,三个字符再加上一个'\0'就是4个总元素

数组和指针解析

一维数组

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n",sizeof(a));//16
	printf("%zd\n", sizeof(a+0));//首元素地址,类型是int*
	printf("%zd\n", sizeof(*a));//首元素地址,*a就是首元素==a[0]
	printf("%zd\n", sizeof(a+1));//第二个地址,类型是int*
	printf("%zd\n", sizeof(a[1]));//第二个元素
	printf("%zd\n", sizeof(&a)); //数组地址,地址大小是4/8
	printf("%zd\n", sizeof(*&a));//取出地址后解引用就相互抵消了,类型是int(*)[4],就是数组的地址
	printf("%zd\n", sizeof(&a+1));//整个数组地址后加1后的第一个地址
	printf("%zd\n", sizeof(&a[0]));//首元素地址
	printf("%zd\n", sizeof(&a[0]+1));//数组第二个元素的地址,大小4/8
	return 0;
}

首先来看sizeof(a),因为sizeof是计算操作数所占内存的大小,单位是字节,a[]是类型为int型的四个元素的数组,所以大小就是16个字节,

sizeof(a+0)是首元素地址,我们前面说了只有sizeof(数组名)和&数组名才表示整个数组大小,而sizeof(a+0)显然不是,所以 他就是首元素地址,并且类型为int*,那为什么类型是int*,而不是int呢?因为a表示的是首元素的"地址",对地址加减编译器会隐式转化为指针运算,只有指针才能对地址进行操作,所以类型是int*,大小是4或8个字节

sizeof(*a)是首元素地址,还是回到之前的内容,只有sizeof(数组名)和&数组名才表示整个数组大小,而第三个前面有一个"*"号,所以就是对首地址进行解引用,大小是4个字节

sizeof(a+1)就是第二个元素的地址,类型也是int*,大小是4或8个字节

sizeof(a[1])第二个元素的大小,4个字节

sizeof(&a),&数组名表示整个数组的地址,类型是int*,所以大小就是4或8个字节

sizeof(*&a)取出地址后解引用就相互抵消了,类型是int(*)[4],就是数组的地址,所以大小就是16个字节

sizeof(&a+1),&a取出整个数组的大小然后再对整个数组的地址加1,大小就是4或8个字节

sizeof(&a[0]),取出首元素地址,大小就是4或8个字节

sizeof(&a[0]+1)数组第二个元素的地址,大小4/8个字节

字符数组

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#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;
}

sizeof(arr)取出整个数组大小,大小是6个字节

sizeof(arr+0),我们分开来看,首先看"arr+0"表示首元素地址,所以它是地址,然后sizeof对地址进行计算大小,所以大小是4或8个字节

sizeof(*arr)和上面的表示一样,表示对首元素地址进行解引用,大小就是1个字节

sizeof(arr[1])是第二个元素,大小1个字节

sizeof(&arr),取出整个数组的地址,地址大小就是4或8个字节

sizeof(&arr+1),"&arr+1"是取出整个数组的地址后加1就跳过整个数组,大小是4或8个字节

sizeof(&arr[0]+1)取出第二个元素地址,大小是4或8个字节

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{       //strlen的参数是const char*,返回值是size_t      size_t strlen(const char*)
	char arr[] = { 'a','b','c','d','e','f' };
		printf("%zd\n", strlen(arr));//arr首元素地址,数组中没有\0就会导致绝界访问,会生成随机值
		printf("%zd\n", strlen(arr+0));//arr+0首元素地址,数组中没有\0就会导致绝界访问,会生成随机值
		//printf("%zd\n", strlen(*arr));//arr+0首元素地址,*arr是首元素,就是a的ascii值,就相当与吧97传给了strlen,就会得到野指针,代码会出问题
		//printf("%zd\n", strlen(arr[1]));//将第二个元素ascii值传给strlen也是错的
		printf("%zd\n", strlen(&arr));//数组地址,起始位置是数组的第一个元素的位置,随机值
		printf("%zd\n", strlen(&arr+1));//随机值
		printf("%zd\n", strlen(&arr[0]+1));//第二个元素向后统计,得到的也是随机值
	return 0;
}

strlen(arr),arr是首元素地址,数组中没有\0就会导致绝界访问,会生成随机值

strlen(arr+0)arr+0首元素地址,数组中没有\0就会导致绝界访问,会生成随机值

strlen(*arr),首先来看一下这个代码

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

strlen的参数是const char*,返回值是size_t,而这里我们写的是*arr,就是首元素'a',而a的ascii值是97,就相当与把97作为地址传给了strlen,但97这个地址里面不一定是我的值,所以就会得到野指针,代码报错,大家可以试一下

strlen(arr[1]),将第二个元素ascii值传给strlen也是错的,arr[1]==*(arr+1)

strlen(&arr),数组地址,起始位置是数组的第一个元素的位置,数组中没有"\0",所以就会生成随机值

strlen(&arr+1),指的是第二个元素'b',将b的ascii值作为地址传给strlen,生成随机值

strlen(&arr[0]+1)是取出第二个元素地址后向后统计,没有"\0"就生成随机值

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	char arr[] = "abvdef";
	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;
}

sizeof(arr),求整个数组的占用字节数,大小为7,因为sizeof包含字符串后面的'\0'

sizeof(arr+0),求首元素的地址大小,地址大小为4或8个字节

sizeof(*arr),对首地址解引用就是arr的大小,为1个字节'

sizeof(arr[1]),求第二个元素的大小,1个字节

sizeof(&arr),求整个数组的地址大小,为4或8个字节

sizeof(&arr+1),对整个数组的地址加1就是跳过整个数组地址,大小为4或8个字节

sizeof(&arr[0]+1),求第二个地址的大小,为4或8个字节

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "abvdef";
	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));

	return 0;
}

strlen(arr):arr首元素地址,从首元素开始往后计算,直到遇到"\0"结束,字符串默认在最后带有"\0",所以返回6

strlen(arr + 0):首元素地址开始计算,答案也是6

strlen(*arr):将首元素的字符的ascii值作为地址传给strlen,代码报错

strlen(arr[1]):第二个元素的字符的ascii值作为地址传给strlen,代码报错

strlen(&arr):取出整个数组的地址,从地址的第一个位置开始计算,答案也是6

strlen(&arr + 1):取出整个数组的地址加1,生成随机值

strlen(&arr[0] + 1):从第二个元素计算,答案是5

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));//p是指针变量
	printf("%d\n", sizeof(p+1));//p+1是b的地址
	printf("%d\n", sizeof(*p));//p的类型是const char*,*p就是char类型,1个字节
	printf("%d\n", sizeof(p[0]));//p[0]==*(p+0)==*p=='a',大小就是1个字节
	printf("%d\n", sizeof(&p));//取出的是P的地址
	printf("%d\n", sizeof(&p+1));//跳过p的指针变量皇后的地址
	printf("%d\n", sizeof(&p[0]+1));
	return 0;
}

sizeof(p):p是一个指针变量,求指针变量的大小4或8

sizeof(p+1):p同样是指向首元素'a',而p+1就是指向'b'的指针变量,大小4或8

sizeof(*p):对p指针进行解引用指向首元素'a',大小就是1个字节

sizeof(p[0]):是取出首元素,大小就是1个字节

sizeof(&p):取出的是p的地址

sizeof(&p+1):对取出p的地址加1,大小还是指针变量

sizeof(&p[0]+1):取出首元素的地址往后加1后就是第二个元素的地址,大小为4或8个字节

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", strlen(p));
	printf("%d\n", strlen(p + 1));
	printf("%d\n", strlen(*p));// *p就是'a'的ascii值,err
	printf("%d\n", strlen(p[0]));//p[0]==*(p+0)==*p=='a'
	printf("%d\n", strlen(&p));//取出的是P的地址,但字符串有自己的地址,所以没有太大关系,随机值
	printf("%d\n", strlen(&p + 1));//跳过p的指针变量皇后的地址
	printf("%d\n", strlen(&p[0] + 1));
	return 0;
}

里面的有一部分我们在前面说过,这里就不再说了,大家尝试一下自己理解

二维数组

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n",sizeof(a));//48
	printf("%d\n", sizeof(a[0][0]));//第一行第一个元素地址
	printf("%d\n", sizeof(a[0]));//第一行的地址,大小为16
	printf("%d\n", sizeof(a[0]+1));//没有单独的a[0],所以就是第一行第二个元素地址
	printf("%d\n", sizeof(*(a[0] + 1)));//第一行第二个元素地址解引用的内容
	printf("%d\n", sizeof(a+1));//第二行的地址,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));// a表示二维数组首元素地址,也就是第一行的地址,*a就是第一行,大小16字节,*a==*(a+0)==a[0]
	printf("%d\n", sizeof(a[3]));
	return 0;
}

sizeof(a):48

sizeof(a[0][0]):第一行第一个元素,大小为4

sizeof(a[0]):"a[0]"表示第一行的数组,将第一行的数组单独放在sizeof里面,大小为16

sizeof(a[0]+1):这里没有将数组名单独放在sizeof里面,就表示首元素地址加1,就是第一行的第二个元素地址,大小为4或8个字节

sizeof(*(a[0] + 1)):对第一行的第二个地址解引用得到元素大小就是4个字节

sizeof(a+1):a+1表示首元素的地址二维数组的首元素地址是第一行的地址,加1后就是第二行的地址,大小为4或8个字节

sizeof(*(a + 1)):对第二行的地址进行解引用就是16个字节

sizeof(&a[0]+1):取出第二行的地址大小,4或8个字节

sizeof(*(&a[0] + 1)):取出第二行的数组大小,就是16个字节

sizeof(*a):二维数组首元素地址就是第一行的地址,大小为16个字节

sizeof(a[3]):第四行的数组名,计算第四行的数组大小,但我们这里并没有第四行,但sizeof前面说过不需要内容,只需要类型,类型为int的数组a,所以大小为16

相关推荐
小白狮ww2 小时前
Matlab 教程:基于 RFUAV 系统使用 Matlab 处理无人机信号
开发语言·人工智能·深度学习·机器学习·matlab·无人机·rfuav
A24207349302 小时前
JavaScript学习
前端·javascript·学习
奋斗吧程序媛2 小时前
动态组件驱动的标签页架构(简单来说:一个页面包含许多Tabs页面,这些Tabs页面渲染逻辑)
前端·javascript·vue.js
Felix_Fly2 小时前
用 Vue3 + naive-cron 开发 Cron 表达式工具:从 0 到 1 实现生成 + 反解析
前端·javascript·vue.js·vue·cron·naive
开发者小天2 小时前
react中useReducer的使用
前端·javascript·react.js
阿蒙Amon2 小时前
JavaScript学习笔记:1.JavaScript简介
javascript·笔记·学习
penngo2 小时前
Golang使用Fyne开发桌面应用
开发语言·后端·golang
while(1){yan}2 小时前
JAVA中如何操作文件
java·开发语言·面试
啊森要自信2 小时前
【C语言】 C语言文件操作
c语言·开发语言·汇编·stm32·单片机