本节主要是为大家讲解一些之前欠考虑的地方,可以自行选择学习。
一.sizeof与strlen的区别
在初学阶段大家很容易将这两个东西搞混,不都是测量大小和长度用的吗?他俩究其根本有啥区别呢?
首先,sizeof是个操作符,只算占多大空间。而strlen是个库函数,只数到\0为止。
下面我们来具体说一说区别。
1.1 sizeof操作符
sizeof 是 C 语言的操作符,不是函数。它做的事非常纯粹:告诉你一个变量或者一个类型在内存里占多少字节,单位是字节。它只关心占多大地方,完全不管那块内存里装的是什么数据。在我看来,把 sizeof 理解为求大小而非长度比较好。
下面再来介绍一下用法:
int a = 10; printf("%d\n", sizeof(a)); // a 是 int printf("%d\n", sizeof a); // 括号可以省掉 printf("%d\n", sizeof(int)); // 直接写类型在这里我们要注意一个细节:sizeof 括号里放的是类型时,括号不能省(sizeof(int));放变量时括号可以省(sizeof a)。
1.2 strlen库函数
strlen 是标准库函数,要包含头文件 string.h,它的功能是求字符串长度。它从你给的地址开始,一个字符一个字符往后数,直到碰到 \0 就停下,返回中间数过了多少个字符。在我看来,把 strlen 理解为求长度而非大小比较好。
重点是:它根本不看这块空间有多大,只关注哪里有 \0。如果一直找不到 \0,它就会一直往后越界找下去。
见以下代码:
char arr1[3] = {'a', 'b', 'c'}; // 没有 \0 char arr2[] = "abc"; // 自动带一个 \0 printf("%d\n", strlen(arr1)); // 随机值,越界找 \0 printf("%d\n", strlen(arr2)); // 3 printf("%d\n", sizeof(arr1)); // 3,占 3 字节 printf("%d\n", sizeof(arr2)); // 4,abc 加 \0 共 4 字节我们来讲解一下:arr1 占 3 字节,但因为没有 \0,strlen 遍历它时根本不知道该在哪停,结果就是个随机值;arr2 虽然只看得到 abc,但字符串自带 \0,所以 sizeof 是 4、strlen 是 3。
1.3 小总结
| 名称 | sizeof | strlen |
| 身份 | 操作符 | 库函数 |
| 作用 | 求占内存的字节数,不关注里面放了什么 | 求 \0 之前的字符个数 |
相信通过这次讲解,应该给大家彻底讲明白了 strlen 和 sizeof 的区别。
二.数组名的意义
这里先给大家简单提一下之前讲过的数组名的意义。
1. sizeof(数组名):这里的数组名代表整个数组,算的是整个数组的大小。
2. &数组名:这里的数组名代表整个数组,取出来的是整个数组的地址,它 +1 会跳过整整一个数组。
3. 除此之外的所有情况,数组名都代表“首元素的地址”,它 +1 只跳过一个元素。
2.1 一维整型数组名
int main() { int a[] = { 1,2,3,4 }; printf("%zu\n", sizeof(a)); printf("%zu\n", sizeof(a + 0)); printf("%zu\n", sizeof(*a)); printf("%zu\n", sizeof(a + 1)); printf("%zu\n", sizeof(a[1])); printf("%zu\n", sizeof(&a)); printf("%zu\n", sizeof(*&a)); printf("%zu\n", sizeof(&a + 1)); printf("%zu\n", sizeof(&a[0])); printf("%zu\n", sizeof(&a[0] + 1)); return 0; }大家看看这些输出应该是啥?(x64 环境下)
这里最容易错的是最后一行:&a[0]+1 只是首元素地址往后跳一个 int,还是地址,所以是 8;而 &a+1 虽然也是地址(8 字节),但它一跳就跳过整个 16 字节的数组。大家不要只觉得自己做对了答案就是会了,一定要知道具体所代表的含义是什么。
2.2 字符数组名
int main() { char arr[] = { 'a','b','c','d','e','f' }; printf("%zu\n", sizeof(arr)); printf("%zu\n", sizeof(arr + 0)); printf("%zu\n", sizeof(*arr)); printf("%zu\n", sizeof(arr[1])); printf("%zu\n", sizeof(&arr)); printf("%zu\n", sizeof(&arr + 1)); printf("%zu\n", sizeof(&arr[0] + 1)); return 0; }大家看看这些输出应该是啥?(x64 环境下)
如果换成 strlen 再看一遍就更清楚了:strlen(arr) 是随机值,因为这组字符没有 \0;而 sizeof(arr) 永远是 6,跟里面有没有 \0 一点关系都没有。
2.3 字符串指针
int main() { char* p = "abcdef"; printf("%zu\n", sizeof(p)); printf("%zu\n", sizeof(p + 1)); printf("%zu\n", sizeof(*p)); printf("%zu\n", sizeof(p[0])); printf("%zu\n", sizeof(&p)); printf("%zu\n", sizeof(&p + 1)); printf("%d\n", strlen(p)); printf("%d\n", strlen(p + 1)); printf("%d\n", strlen(*p)); printf("%d\n", strlen(p[0])); return 0; }大家来看看这些输出应该是啥?(依旧 x64 环境)
我来给大家讲解一下:
sizeof(p) // 8,p 本身是个指针 sizeof(p+1) // 8,还是指针 sizeof(*p) // 1,*p 是字符 'a' sizeof(p[0]) // 1,等价于 *p sizeof(&p) // 8,指向指针的指针 sizeof(&p+1) // 8,地址 strlen(p) // 6 strlen(p+1) // 5,从 b 开始数 strlen(*p) // 报错,*p 是字符不是地址 strlen(p[0]) // 报错,p[0] 是字符不是地址关键区别:char arr[] = “abcdef” 里,arr 是数组,sizeof 它是 7,含 \0;而 char *p = “abcdef” 里,p 是指针,sizeof 它只是 8 字节的地址,字符串常量本身才是 7 字节。
2.4 二维数组名
int main() { int a[3][4]; printf("%zu\n", sizeof(a)); printf("%zu\n", sizeof(a[0][0])); printf("%zu\n", sizeof(a[0])); printf("%zu\n", sizeof(a[0] + 1)); printf("%zu\n", sizeof(a + 1)); printf("%zu\n", sizeof(*(a + 1))); printf("%zu\n", sizeof(*a)); printf("%zu\n", sizeof(a[3])); return 0; }大家来看看这些输出应该是啥?(依旧 x64 环境)
在这里我们要把每一行看成一个整体:a[0]、a[1] 分别就是第一行、第二行,它们的 sizeof 都是 16(一行的大小);而 a[0]+1 这种行地址加偏移,就退化成了普通元素地址,sizeof 就变成 8。