sizeof和strlen辨析、数组和指针的运算(加深理解)(自用!!)

sizeof和strlen辨析

sizeof()strlen()

1、sizeof是操作符

2、sizeof计算操作数所占内存大小,单位是字节

3、不关注内存中存放什么数据

1、strlen是库函数,使用需要包含头文件<string.h>

2、strlen是求字符串长度的,统计的是“ \0 ”之前的字符个数

3、关注内存中是否有“ \0 ”,如果没有“ \0 ”,就会持续往后找,直到读到“ \0 ”为止(因此可能会越界

(在不同的环境下,地址的大小是不同的:X86:地址大小为4字节;X64:地址大小为8字节)

再提一嘴:

数组名的意义: 1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。 2. &数组名,这⾥的数组名表示整个数组,取出的是整个数组的地址。 3. 除此之外所有的数组名都表示首元素的地址

size_t strlen( const char *str );   //要包含头文件<string.h>

我们可以看到strlen的返回值是“size_t”,因此我们要用“%zd”来接收strlen的返回值;

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

sizeof----一维数组

下面的代码可以加深你对sizeof和一维数组的理解:

	int a[] = { 1,2,3,4 };

	printf("%d\n", sizeof(a));//sizeof内部单独放数组名,表示的是整个数组,因此这里表示的是整个数组的大小;16字节
	printf("%d\n", sizeof(a + 0));//sizeof内部不是单独放数组名,那么数组名就表示数组首元素地址,“ + 0”表示跳过0个元素,
	                              //因此这里表示的就是第一个元素的地址;4/8字节 
	printf("%d\n", sizeof(*a));//sizeof内部不是单独放数组名,所以这里表示首元素地址,那么*a == *&a[0] == a[0];4字节
	printf("%d\n", sizeof(a + 1));//sizeof内部没有单独放数组名,表示首元素地址,这里就是跳过1个元素,
	                              // 表示的是第二个元素的地址;4/8字节
	printf("%d\n", sizeof(a[1]));//表示下标为1的元素的大小;4字节
	printf("%d\n", sizeof(&a));//&a就表示取出整个数组的地址,但它始终表示的是地址;4/8字节
	printf("%d\n", sizeof(*&a));//理解1:“&a”取出整个数组的地址后再“*”解引用获得整个数组;16字节
	                            //理解2:“&”和“*”相互抵消,*&a == a
	printf("%d\n", sizeof(&a + 1));//“&a”取出整数组的地址,+1后跳过整个数组,但+1后表示的还是地址;4/8字节
	printf("%d\n", sizeof(&a[0]));//取出下标为0的元素的地址,既然是地址那就是4/8个字节
	printf("%d\n", sizeof(&a[0] + 1));//取出首元素的地址并+1,此时指向的就是第二个元素的地址;4/8字节

不想翻代码可以看截图:

接下来看一组关于字符数组的代码

	char arr[] = { 'a','b','c','d','e','f' };

	printf("%d\n", sizeof(arr));//数组名单独放在sizeof内部;6字节
	printf("%d\n", sizeof(arr + 0));//数组名没单独放在sizeof内部,这里arr就表示首元素地址,跳过0个元素;
	                                //既然是地址那就是4/8字节
	printf("%d\n", sizeof(*arr));//数组名不单独放在sizeof内部,那就表示首元素地址,等价于“*&arr[0]”,
	                             //表示下标为0的元素;1字节
	printf("%d\n", sizeof(arr[1]));//表示下标为1的元素;1字节
	printf("%d\n", sizeof(&arr));//数组名虽然没单独放在sizeof内部,但这里是“&arr”表示取出整个数组的地址,
	                             // 既然是地址那就是4/8字节
	printf("%d\n", sizeof(&arr + 1));//+1 表示跳过整个数组的地址,“&arr + 1”表示的依旧是地址;4/8
	printf("%d\n", sizeof(&arr[0] + 1));//“&arr[0]”表示取出下标为0的元素的地址,+1跳过一个元素,
	                                    //所以这里表示的是下标为1的元素的地址;4/8

不想翻代码可以看截图:

再来一段:

	char* p = "abcdef";
	//char arr[] = "abcdef";----
	//char* p = &arr;-----------这两行代码和上面一行等价

	printf("%d\n", sizeof(p));//p是指针(地址),里面放的是字符串首字符'a'的地址;4/8字节
	printf("%d\n", sizeof(p + 1));//p指向的是字符'b'的地址;4/8字节
	printf("%d\n", sizeof(*p));//p指向'a'的地址,解引用后就表示字符a;1字节
	printf("%d\n", sizeof(p[0]));//我们可以将242行的代码分解成243、244行的代码:因为p里放的是首字符a的地址,而数组名
    //arr也表示首元素a的地址,因此可以将p和arr等价,那么p[0] == arr[0] == 'a',所以计算结果为1字节
	printf("%d\n", sizeof(&p));//&p表示取出指针p的地址,既然是地址,那大小就是4/8字节
	printf("%d\n", sizeof(&p + 1));//&p表示的还是地址,+1后它表示的依旧是地址;4/8字节
	printf("%d\n", sizeof(&p[0] + 1));//等价于“&arr[0] + 1”,跳过下标为0的元素的地址,表示的是第二个元素的地址;4/8字节

不想翻代码可以看截图:

strlen函数-----字符数组

	char arr[] = { 'a','b','c','d','e','f' };

	printf("%zd\n", strlen(arr));//这里arr表示数组首元素地址(注意和sizeof(arr)区分开),由于arr字符数组里面没有“\0”.
	                             //所以strlen会一直向后访问内存空间,直到读到“\0”为止;随机值
	printf("%zd\n", strlen(arr + 0));//等价于“(&arr[0] + 0) == &arr[0]”,和上面一样是随机值
	printf("%zd\n", strlen(*arr));//这里arr表示首元素地址,等价于“*&arr[0] == arr[0]”也就是字符“a”,a的ASCII值是97
	                              //所以这里会现将97转为十六进制:61,然后把61作为地址来访问,
								  //但是这块空间可能不属于当前程序,因此编译器可能会报错;error
	printf("%zd\n", strlen(arr[1]));//arr[1] == 'b',b的ASCII值是98,转为十六进制是62,然后将62作为地址来访问,
	                                //因此可能会出现和上面一样的情况;error
	printf("%zd\n", strlen(&arr));//“&arr”的类型为char(*)[6],是一个数组指针,注意strlen函数的参数为const char* str,
	//因此在&arr将数组的地址传给strlen时,会将char(*)[6]强制类型转换为const char*的类型,那么strlen函数依旧会从首元素的
	//地址开始读取字符,直到读到“\0”为止;随机值
	printf("%zd\n", strlen(&arr + 1));//“&arr + 1”表示跳过整个数组的地址,此时strlen函数就从arr数组地址后面的那一个
	                                  //地址开始读取;随机值
	printf("%zd\n", strlen(&arr[0] + 1));//跳过下标为0的元素的地址,此时指向的就是第二个元素的地址,从第二个元素开始
	                                     //读取,还是和前面的一样是随机值

不想翻代码可以看截图:

再来看一段代码:

​​​​​​​

	char* p = "abcdef";//这种字符串有隐藏的\0: (a,b,c,d,e,f,\0)
	//char arr[] = "abcdef";----
	//char* p = &arr;-----------这两行代码和上面一行等价

	printf("%d\n", strlen(p));//传过去的是字符串中首字符的地址,也就是'a'的地址,因为隐藏有\0,所以值为6
	printf("%d\n", strlen(p + 1));//+1跳过一个字符,所以传过去的是'b'的地址;5
	printf("%d\n", strlen(*p));//传过去的p就是a的地址,“*”解引用后得到字符'a',它的ACSII值是97,
	//这里会现将97转为十六进制:61,然后把61作为地址来访问,但是这块空间可能不属于当前程序,
	//因此编译器可能会报错;error
	printf("%d\n", strlen(p[0]));//我们可以将220行的代码分解成221、222行的代码:因为p里放的是首字符的地址,而数组名
    //arr也表示首元素地址,因此可以将p和arr等价,那么p[0] == 'a',就会出现和上面相同的错误;error
	printf("%d\n", strlen(&p));//这里传的是指针p的地址,那么就会从p的地址读起,直到读到\0;随机值
	printf("%d\n", strlen(&p + 1));//这行代码和上一行代码基本一样,只是strlen读取的其实地址不同,还是随机值
	printf("%d\n", strlen(&p[0] + 1));//经过第229行代码的分析,“&p[0]+1”== “&arr[0]+1”,也就是从'b'的地址开始读起;5

sizeof-----二维数组

来看一段比较困难的代码:

	int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };

	printf("%d\n", sizeof(a));//数组名单独放在sizeof内部,表示整个数组;48字节
	printf("%d\n", sizeof(a[0][0]));//下标为0行0列的元素;4字节
	printf("%d\n", sizeof(a[0]));//二维数组是一维数组的数组,所以二维数组的首元素是第0行的一维数组(也就是“1,2,3,4”
	//这个一维数组);16字节
	printf("%d\n", sizeof(a[0] + 1));//a[0]是二维数组首元素地址,它+1后并不表示第1行的一维数组的地址,而是表示从第0行的
	//一维数组的首元素向后跳过一个元素的地址(也就是“2”的地址);4/8字节
	printf("%d\n", sizeof(*(a[0] + 1)));//上面分析出(a[0]+1)是“2”的地址,*解引用后就是2;4字节
	printf("%d\n", sizeof(a + 1));//这里数组名没单独放在sizeof内,所以表示二维数组首元素地址,等价于(a[0]+1)和第268行的
	//代码相同;4/8字节
	printf("%d\n", sizeof(*(a + 1)));//依托前面的代码分析可以知道:a+1 == a[0]+1 == 元素“2”的地址,*解引用后就是2;4字节
	printf("%d\n", sizeof(&a[0] + 1));//&a[0]表示取出二维数组中下标为0行的一维数组,+1后跳过一个一维数组,那么此时的地址
	//就是下标为1行的一维数组的地址(也就是“5,6,7,8”这个一维数组);4/8字节
	printf("%d\n", sizeof(*(&a[0] + 1)));//上面分析过:&a[0]+1表示“5,6,7,8”这个一维数组的地址
	//(也可以认为是该数组首元素的地址),*解引用后就是首元素“5”;4字节
	printf("%d\n", sizeof(*a));//数组名未单独放在sizeof内,那么数组名表示的就是二维数组首元素地址(这里的首元素地址恰好是
	//第0行一维数组的首元素的地址,也就是“1”的地址),*解引用后得到的就是元素1;4字节
	printf("%d\n", sizeof(a[3]));//下标为3行的一维数组,按理说这是越界行为,但是对于sizeof()来说是没有越界这一说法的,
	//因为sizeof计算的是数据大小,不会真实访问数组或内存空间,所以这里的a[3]和a[0]、a[1]是一样的

不想翻代码可以看截图:

(错必纠)

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值