先问你一道送分题:char s[] = "hello";那么sizeof(s)和strlen(s)分别是多少?
答案是6和5。可别小看这一题,据这些年我面试新人、帮网友改代码的经验,能在五分钟内把这两个东西讲清楚的人,连三分之一都不到。很多人字符串处理写得飞起,一问sizeof到底算的是哪块内存、strlen为什么需要头文件、数组名传进函数后为什么就“变了”,立刻卡壳。
这篇文章我就把sizeof和strlen的底层原理、使用场景、笔试高频陷阱一次性讲透,再带你把网上流传的经典真题和易错选项逐题拆解一遍。内容覆盖数组、指针、字符串字面量、结构体对齐、动态内存申请这些 C 语言基础核心知识点。适合三类人看:刚学完 C 语法想补基础入门知识的同学;准备校招笔试、需要刷sizeof和strlen相关习题的应届生;还有工作几年但遇到类似题目依然凭感觉猜答案的在职工程师。
建议你找个能敲代码的环境,边看边跑一遍,印象会深得多。
1. 先分清身份:一个是运算符,一个是库函数
1.1 sizeof 是关键字,不是函数,也不需要头文件
很多初学 C 的朋友被网上的写法误导,以为sizeof是库函数,还要问“sizeof 函数需要头文件吗”。这里我明确告诉你:sizeof在 C 语言里是关键字,是运算符,不是函数。
它长得像函数,是因为我们习惯写成sizeof(类型)或sizeof(变量),括号看起来像函数调用。但 C 标准规定sizeof是单目运算符,优先级和++、-这类符号相同。对普通变量操作时,括号甚至可以不写,比如:
int x = 10; printf("%zu\n", sizeof x); // 不写括号也能编译我在真实项目代码里见过有人写sizeof x,语法上合法,但我个人不建议你在工程里这样写,可读性太差。笔试如果碰到这种写法,你要能看懂,它和sizeof(x)等价。
既然是关键字,它就不需要任何头文件。这点一定要记牢:sizeof是编译器认识的符号,不是运行时从某个库里调出来的函数。
sizeof的核心运作机制是编译期求值。编译器在编译阶段就能算出结果,生成的可执行代码里,sizeof(x)已经被替换成了一个整数常量。所以它不消耗运行时间,也不执行括号里的表达式。下一章我会用sizeof(i++)这个经典例子给你看它到底有多“懒”。
1.2 strlen 是标准库函数,必须包含 <string.h>
strlen就完全是另一回事了。它是 C 标准库里的函数,原型长这样:
size_t strlen(const char *s);使用它之前必须包含头文件:
#include <string.h>strlen的工作原理可以理解为:从你传给它的指针地址开始,一个字节一个字节地往下读,直到遇到第一个'\0'为止,然后把走过的字节个数返回给你。它属于 C 语言字符串函数家族,跟strcpy、strcmp、strcat这些是同一批成员,都在<string.h>里。
这里要引出第一个重要区分:sizeof问的是“这块内存占了多大地方”,strlen问的是“这个字符串到结束符为止有多长”。一个是地盘的大小,一个是内容的长度。
1.3 一张对照表说清本质区别
把两者最关键的区别拉成一张表,建议你直接存下来:
| 对比项 | sizeof | strlen |
|---|---|---|
| 本质 | 运算符 / 关键字 | C 标准库函数 |
| 头文件 | 不需要 | 需要<string.h> |
| 求值时机 | 编译期 | 运行期 |
| 参数 | 类型名或变量名 | 以'\0'结尾的字符串指针 |
| 返回值 | 对象或类型占用的字节数 | 到第一个'\0'之前的字符个数 |
是否包含'\0' | 如果是字符数组,包含 | 不包含 |
| 能否作用于未分配初始化的内存 | 可以,只算类型大小 | 不能,会越界读到不确定的字节 |
| 对数组名的处理 | 数组名在 sizeof 中保留“整个数组”信息 | 数组名作为参数时退化为指针 |
这张表只看可能还有点抽象,后面每一章我都配了具体代码示例,把表的每一行对应到真实场景里去理解。
用一个生活类比帮助记忆:sizeof相当于看房产证上的建筑面积,厕所、阳台、墙体全给你算进去;strlen相当于拿着卷尺从大门口往里量,走到第一堵墙('\0')就停下,只算这段可见路径的长度。所以同一个字符数组"hello",sizeof报的是 6 平米的完整结构,strlen量出来的是 5 米的可见路长,剩下 1 平米是藏在墙后的终止符。
2. 字符串与数组:最容易踩的 sizeof 坑
2.1 数组名和指针变量,sizeof 结果差远了
这道题是笔试绝对的高频题:
char s[] = "hello"; char *p = "hello"; printf("%zu %zu\n", sizeof(s), sizeof(p)); printf("%zu %zu\n", strlen(s), strlen(p));在 64 位环境下,输出结果是:
6 8 5 5如果你答错成6 5,说明你把p的sizeof结果当成了字符串长度。这就是最大的坑:s是数组,p是指针变量,两者类型完全不同。
对于数组s,sizeof(s)取的是整块数组内存的大小。"hello"在 C 语言里是一个char[6]类型,除了h、e、l、l、o五个字母外,末尾还自动补了一个'\0',所以整块数组占 6 个字节。这里的6是“整个容器的体积”,不是“容器里有效内容的长度”。
对于指针p,它只是一个保存地址的变量。64 位系统上地址宽度是 8 字节,所以sizeof(p)是 8。指针本身不关心你指向的内容是谁、有多长,它只负责存一个地址。你把p指向"hello"也好,指向"hello world"也好,sizeof(p)都是 8,因为指针变量所占的字节数与它指向什么东西毫无关系。
strlen就不同了,它接收参数时只看指针指向的内容,从p指向的地址开始找'\0',找到'o'后面的终止符,返回 5。所以strlen(p)结果和strlen(s)一样,都是 5。
记住这一点:sizeof作用于数组名时,还算你整块地盘;sizeof作用于指针时,只算你用来存地址的那个小格子。两者不要混为一谈。
2.2 函数传参后,数组名“退化”为指针
现在做一道更高频的笔试/面试改编题:
void foo(char s[]) { printf("%zu\n", sizeof(s)); } int main() { char s[100] = "hello"; foo(s); return 0; }函数内部sizeof(s)输出多少?答案是 8(64 位环境),不是 100。
这就是 C 语言里著名的“数组参数退化”规则。虽然函数签名写的是char s[],看着像数组,但在 C 语言中,函数形参里的数组声明会被编译器自动调整为指针,等价于void foo(char *s)。所以在函数内部,s只是一个指针变量,sizeof(s)也就变成了指针的大小。
这个知识点会直接牵出一个笔试题高频解法错误:很多同学喜欢在函数内部写sizeof(arr) / sizeof(arr[0])来求数组元素个数,比如:
int sum_of_array(int arr[]) { size_t n = sizeof(arr) / sizeof(arr[0]); // 错!arr 是指针 ... }这里sizeof(arr)是指针大小,sizeof(arr[0])是 int 大小,两个一除在 64 位 Linux 下是 2,根本不是数组元素个数。正确做法是:数组定义在哪个作用域,就在哪个作用域用sizeof算大小;函数内部要拿数组长度,必须通过参数显式传进来。
2.3 字符串字面量的隐藏字符与转义陷阱
再看一个细节:sizeof("hello")为什么是 6?因为字符串字面量在 C 语言中的类型是char[6],它真实地包含末尾的'\0'。strlen("hello")扫到'o'之后的'\0'停下,所以返回 5。
这个差异放在转义字符上会更隐蔽,笔试也爱考。举个例子:
char s[] = "a\tb\n"; printf("%zu %zu\n", sizeof(s), strlen(s));很多人看到\t和\n,以为这是两个字符,于是猜是 6。实际上\t和\n在 C 语言源码里是转义序列,分别表示一个制表符和一个换行符,每个只占 1 个字符。整个字符串内容是:'a'、'\t'、'b'、'\n'、'\0',一共 5 个字节。所以sizeof(s)输出 5,strlen(s)输出 4。
还有更恶心的:
char s[] = "\0hello"; printf("%zu %zu\n", sizeof(s), strlen(s));字符串字面量"\0hello"实际存储的字符序列是:'\0'、'h'、'e'、'l'、'l'、'o'、'\0',共 7 个字节。所以sizeof(s)是 7;而strlen(s)从头开始数,第一个字节就是'\0',直接返回 0。
这类题考的其实不是你能不能背出答案,而是你有没有意识到'\0'是真实存在于内存中的一个字节,sizeof会把它的位置也算进数组体积,而strlen见到它就立刻收工。
2.4 实际场景:fgets 读完的字符串怎么安全地取长度
字符串长度计算最常见的工程场景,就是读取一行用户输入后判断内容。比如网上流传的“字符串逆序”练习题,很多同学在 PTA 和翁恺老师的练习里都遇到过,正确思路就是先用strlen拿到输入长度,再做首尾交换。
用fgets读入字符串时,缓冲区里通常会带一个换行符,需要先处理掉:
#include <stdio.h> #include <string.h> int main(void) { char s[101]; if (fgets(s, sizeof(s), stdin) == NULL) { return 1; } size_t len = strlen(s); if (len > 0 && s[len - 1] == '\n') { s[len - 1] = '\0'; len--; } // 字符串逆序 for (size_t i = 0; i < len / 2; i++) { char tmp = s[i]; s[i] = s[len - 1 - i]; s[len - 1 - i] = tmp; } printf("%s\n", s); return 0; }注意这里我用sizeof(s)作为fgets的最大读取长度参数,这恰好是sizeof在工程中最经典的正向应用:在数组定义作用域里,用sizeof(s)得到缓冲区真实容量,防止越界写。但因为数组传参给函数后会退化为指针,所以如果你把fgets封装到另一个函数里,就必须把容量作为独立参数传进去,绝不能在函数内部再用sizeof。
3. 进阶陷阱:表达式求值、二维数组、结构体对齐
3.1 经典送命题:sizeof(i++) 到底加不加
来看一道能把不少人打懵的题目:
int i = 0; printf("%zu %d\n", sizeof(i++), i);输出是什么?答案是4 0。
很多人想都不想就答4 1,因为他们以为sizeof(i++)会执行i++,让i变成 1。但sizeof的运算发生在编译期,它只需要知道i++这个表达式的类型是int,就能算出结果是 4 个字节,根本不需要在运行时把这个表达式真正执行一遍。
因此i的值不会变,还是 0。编译器连生成i++的指令都省了,直接把4当常量写进程序。这个题目是验证“sizeof 是编译期运算符”的最强证据。
顺便提醒一句,C 语言标准里有一条例外:当sizeof作用于变长数组(VLA)时,因为数组长度要运行时才知道,sizeof确实会在运行时求值。但这不是入门笔试的重点,大多数题目不会涉及,你只需要知道有这么个例外存在即可,答题时默认按编译期处理。
3.2 二维数组的 sizeof 分解:一层一层剥开
二维数组的sizeof是笔试的难点,但只要你记住一条原则就不怕:sizeof里没有“退化”这回事,数组名是什么类型就是什么类型。
看例子:
int b[3][4];整个数组b的类型是int[3][4],共 12 个 int 元素,64 位 Linux 下 int 是 4 字节,所以:
sizeof(b) // 3 * 4 * 4 = 48 sizeof(b[0]) // b[0] 是第一行,类型是 int[4],大小 16 sizeof(b[0][0]) // 单个元素 int,大小 4再组合一下:
sizeof(b) / sizeof(b[0]) // 48 / 16 = 3,得到行数 sizeof(b[0]) / sizeof(b[0][0]) // 16 / 4 = 4,得到列数这里面隐藏着一个高频考点:b[0]在绝大多数表达式里作为数组名会退化成int *,比如你写func(b[0])传进去的是首元素地址。但在sizeof里,b[0]依然保留第一行数组的类型int[4],所以sizeof(b[0])才能算出 16。你要是用sizeof(&b[0]),那又不一样了,&b[0]是数组指针,大小是 8(64 位),因为它是指针。分清“数组名本身用于 sizeof”和“对数组名取地址后的类型”,是二维数组题型的核心。
3.3 结构体字节对齐:为什么不是简单相加
sizeof作用在结构体上时,很多人以为把成员大小加起来就行,于是就有了下面的经典笔试题:
struct A { char c1; int x; char c2; };直觉答案:1 + 4 + 1 = 6。实际在默认对齐规则下,输出通常是12。
原因是在这个结构体里,int x要求按 4 字节对齐,所以c1占完第 0 字节后,1、2、3 三个字节被编译器填充成空隙,x从第 4 字节开始放,占 4、5、6、7 字节,然后c2放在第 8 字节。整个结构体的大小还要对齐到成员中最大对齐数的整数倍,这里是 4 的倍数,所以尾部还要补到 12 字节。
如果把成员顺序调换:
struct B { char c1; char c2; int x; };结果是 8:c1和c2各自占 1 字节,连续放完后,x从第 4 字节开始,整体大小 8。
笔试里这种变式考得非常多,因为它不仅考sizeof,还考你对内存对齐机制的掌握程度。我建议你计算结构体大小时分三步走:
- 找出对齐要求最大的成员,决定整体对齐边界;
- 按声明顺序给每个成员分配起始位置,注意是否要“对齐到该成员大小整数倍”;
- 成员放完后再把整个大小补到“最大对齐数整数倍”。
这个知识在嵌入式领域尤其重要,因为很多硬件工程师是在用结构体映射通信协议帧结构。偏移量一旦算错,接收方解出的字段就会错位。所以遇到“硬件工程师笔试题”里出现结构体大小计算,请务必用这套流程,而不是简单相加。
3.4 malloc 出来的缓冲区,千万别一上来就 strlen
再看一道跟内存管理相关的坑题:
char *p = (char *)malloc(20); printf("%zu\n", strlen(p));这段代码属于未定义行为。malloc(20)只申请了一块 20 字节的未初始化内存,里面是什么没人知道。strlen(p)从这块内存的起始地址一路找'\0',可能在 3 个字节后碰到,也可能在 19 个字节后碰到,甚至可能直接撞出越界区域才碰到。结果完全不可预测,甚至可能因为越界访问触发崩溃。
正确做法是先给缓冲区内容做初始化,再调strlen:
char *p = (char *)malloc(20); if (p == NULL) { return 1; } p[0] = '\0'; // 先把它变成空字符串 strcpy(p, "hello"); size_t len = strlen(p); // 此时才安全 free(p);顺便聊一个网上经常出现的写法:prt=(char*)malloc(10.2*1024*sizeof(char))这类代码。它的本意是申请大概 10.2 KB 的字符空间。但实际工程里我们不建议把容量写成一个带小数的浮点表达式,10.2 * 1024在 C 里会先按浮点数算出 10444.8,再转成整数做参数,中间可能发生截断,代码表达意图也不清晰。直接写成整数容量更安全:
char *prt = (char *)malloc(10 * 1024); // 10 KB,去掉 sizeof(char) 完全没问题 char *prt2 = (char *)malloc(10445); // 或者直接严格写成整数sizeof(char)在 C 标准里恒等于 1,乘上去只是形式上的冗余,不是错误,但别被这类写法绕进去。另外在 C 语言里malloc返回的void *可以隐式转换为任意对象指针类型,不需要强转;但在 C++ 里必须强转。很多教材示例喜欢写(char *)malloc(...),这不算错,不过你最好知道原因,免得被问到时候支支吾吾。
4. 经典笔试题实战:答案和坑位一条条过
这一章我把网上流传比较广的sizeof/strlen笔试题整理成一组高频列表,每一题给出结论、解析和易错点。题目不算难,但非常典型。
4.1 高频题组:逐个拆解
题 1:
char s[] = "hello"; printf("%zu %zu\n", sizeof(s), strlen(s));输出:6 5。s是数组,sizeof 把'\0'算进去;strlen 数到终止符前停下。
题 2:
char *p = "hello"; printf("%zu %zu\n", sizeof(p), strlen(p));输出:8 5(64 位环境)。sizeof 算的是指针变量大小,strlen 算的是字符串内容长度。
题 3:
char s[20] = "hello"; printf("%zu %zu\n", sizeof(s), strlen(s));输出:20 5。数组容量是 20,不管你实际放了多少内容;strlen 拿到的是有效字符数量。
题 4:
void foo(char s[]) { printf("%zu\n", sizeof(s)); } char str[100] = "hello"; foo(str);输出:8(64 位环境)。数组形参退化为指针,sizeof 结果与实参数组容量无关。
题 5:
int a[10]; printf("%zu\n", sizeof(a) / sizeof(a[0]));输出:10。这是sizeof最正统的求数组元素个数用法,只能用在数组定义作用域内。
题 6:
char s[] = {'h', 'e', 'l', 'l', 'o'}; printf("%zu %zu\n", sizeof(s), strlen(s));输出:sizeof(s)是 5,strlen(s)是未定义行为。这里一定要瞪大眼睛:s是用字符列表初始化的数组,并没有自动补'\0'。strlen 会从第 5 字节继续向后读,直到碰到某个碰巧为 0 的内存字节,结果不可预测。这个题目是区分“懂原理”和“背答案”的试金石。
题 7:
int i = 0; printf("%zu %d\n", sizeof(i++), i);输出:4 0。sizeof是编译期运算符,不执行副作用。
题 8:
struct A { char c1; int x; char c2; }; printf("%zu\n", sizeof(struct A));输出:常见环境为12。按成员对齐规则分配并补齐,不能简单相加。
题 9:
int b[3][4]; printf("%zu %zu %zu\n", sizeof(b), sizeof(b[0]), sizeof(b[0][0]));输出:48 16 4(64 位 Linux)。注意b[0]在 sizeof 里保持第一行数组类型。
4.2 笔试答题速查表
为了方便你考试前快速过一遍,我把常见场景整理成一张速查表:
| 代码场景 | sizeof 结果 | strlen 结果 |
|---|---|---|
char s[] = "hello" | 6 | 5 |
char s[20] = "hello" | 20 | 5 |
char *p = "hello" | 8(64 位指针) | 5 |
char s[] = {'a','b','c'} | 3 | 未定义行为 |
char s[] = "\0hello" | 7 | 0 |
int a[10] | 40 | 不可用 |
struct{char c; int x;} | 8 取决于对齐规则 | 不可用 |
最后强调一句:表里的指针大小、int 大小、结构体对齐结果都与平台相关。考试时如果题目没有特别说明,一般默认按主流 64 位平台作答;如果题干写了 32 位环境,指针大小就要改成 4。很多人在答案里写“sizeof(p) = 8”却不说明平台,容易和面试官产生歧义。你答题时主动补一句“64 位下是 8”,反而显得基本功扎实。
5. 动手实测:用 VSCode + GCC 搭一个验证环境
5.1 环境准备与第一个测试程序
网上关于“vscode 配置 c 语言环境”的帖子很多,实际可以很简单。你需要两样东西:
- 一个 C 编译器,推荐 GCC。Windows 上可以装 MinGW-w64,macOS 上可以用
clang(它是默认编译器,命令略有不同但兼容),Linux 上直接sudo apt install gcc即可。 - VSCode 的 C/C++ 扩展,由微软发布的那个
ms-vscode.cpptools。它负责智能提示、语法高亮和调试。
简单到可以在终端直接编译运行,不一定非要折腾 IDE 配置。写完代码存成main.c,然后在终端执行:
gcc main.c -o main && ./main如果只是想验证sizeof/strlen的各种输出,把测试代码写好,敲这一条命令就够了。VSCode 对于这类小程序的意义,主要是查看函数定义、类型信息比较方便,调试时还能用图形界面断点。
下面这个程序几乎覆盖了上面所有考点:
#include <stdio.h> #include <string.h> #include <stddef.h> struct Test { char c1; int x; char c2; }; void foo(char s[]) { printf("in foo: sizeof(s)=%zu\n", sizeof(s)); } int main(void) { char s1[] = "hello"; char *p1 = "hello"; char s2[20] = "hello"; char s3[] = {'h', 'e', 'l', 'l', 'o'}; char s4[] = "\0hello"; char s5[] = "a\tb\n"; int a[10]; int b[3][4]; int i = 0; printf("sizeof(s1)=%zu, strlen(s1)=%zu\n", sizeof(s1), strlen(s1)); printf("sizeof(p1)=%zu, strlen(p1)=%zu\n", sizeof(p1), strlen(p1)); printf("sizeof(s2)=%zu, strlen(s2)=%zu\n", sizeof(s2), strlen(s2)); printf("sizeof(s3)=%zu, strlen(s3)=%zu <-- 注意 s3 没有 '\\0'\n", sizeof(s3), strlen(s3)); printf("sizeof(s4)=%zu, strlen(s4)=%zu\n", sizeof(s4), strlen(s4)); printf("sizeof(s5)=%zu, strlen(s5)=%zu\n", sizeof(s5), strlen(s5)); printf("sizeof(a)=%zu, 元素个数=%zu\n", sizeof(a), sizeof(a) / sizeof(a[0])); printf("sizeof(b)=%zu, sizeof(b[0])=%zu, sizeof(b[0][0])=%zu\n", sizeof(b), sizeof(b[0]), sizeof(b[0][0])); printf("sizeof(i++)=%zu, i=%d\n", sizeof(i++), i); printf("struct Test size=%zu\n", sizeof(struct Test)); char buf[100] = "hello"; foo(buf); return 0; }因为strlen(s3)是未定义行为,不同编译器可能给出不同结果,甚至可能崩溃。你运行后会发现它基本能返回一个看起来“正常”的数字,比如 6 或 7,那是因为所在内存区域恰好有零字节。千万别把这个结果当成标准答案,它只是运气好而已。
5.2 打印 size_t 的格式符踩坑
上面代码里我用的是%zu,这是 C99 标准专门为size_t提供的格式占位符。sizeof和strlen的返回值类型都是size_t,在 64 位系统上它是 8 字节无符号整数。
很多同学习惯写%d,在 64 位平台上编译可能会报警告,因为%d期望一个int,而size_t是unsigned long,这个不匹配属于未定义行为。轻则输出错误,重则栈上参数读取异常。如果你用的编译器比较老,不支持%zu,可以手动强转:
printf("%lu\n", (unsigned long)sizeof(s)); printf("%llu\n", (unsigned long long)sizeof(s));推荐写%zu,既干净又不丢信息。考试如果出到打印题,默认环境通常支持,放心用。
5.3 调试技巧:用调试器看 sizeof 干扰结果
还有一种更直观的验证方法,直接进调试器看。VSCode 里配上调试器后,在printf那行打断点,鼠标悬停在sizeof(s1)上,调试插件可能会显示6。如果你把s1传进foo函数再中断,会看到形参s的类型已经被推理成指针。
命令行选手可以这样:
gcc -g main.c -o main gdb ./main break main print sizeof(s1)用 GDB 打印sizeof(s1),输出$1 = 6。再下一行打印strlen(s1),输出$2 = 5。亲眼看到这些数字可比死背表格有说服力得多。
6. 面试官视角:这类题到底在考什么
6.1 结构化答题话术
面试时如果被问到“说下 sizeof 和 strlen 的区别”,我不建议你上来就背列表。比较讨巧的回答方式是分三层展开:
第一层说身份:sizeof是运算符、关键字,编译期求值,不需要头文件;strlen是函数,运行期执行,需要<string.h>。
第二层说语义:sizeof计算对象或类型占用的内存字节数,不在乎里面存的是什么;strlen计算以'\0'结尾的字符串中有效字符个数,碰不到终止符就永远不知道什么时候停。
第三层举例子:比如char s[] = "hello",sizeof(s)是 6,strlen(s)是 5;但换成char *p = "hello",sizeof(p)在 64 位环境下是 8,strlen(p)还是 5。这个例子能同时说明“数组和指针的区别”“sizeof 对数组名保留整体类型”“strlen 只关心指向内容”三个知识点。
这样回答逻辑清晰,面试官基本没法再追问到你卡壳。
6.2 常见误区快查
把容易错的认知整理成一个误区表,考前最后瞄一眼:
| 常见误区 | 正确理解 |
|---|---|
| sizeof 是函数,要加头文件 | sizeof 是关键字,不需要头文件 |
| sizeof 返回字符串长度 | sizeof 返回内存占用字节数,与字符串长度无关 |
| strlen 会计算数组全部内容 | strlen 只计算到'\0'前的内容,数组中没有'\0'就会越界 |
| 数组传入函数后还能用 sizeof 求大小 | 数组形参退化为指针,sizeof 只能拿到指针大小 |
| sizeof(i++) 会执行自增 | sizeof 在编译期求值,不执行表达式副作用 |
| 结构体大小等于成员大小之和 | 要考虑内存对齐、填充字节、尾部补齐 |
| 结果在 32 位和 64 位平台完全一样 | 指针大小、部分数据类型大小、结构体对齐结果会随平台变化 |
6.3 最后一个小技巧:先看类型,再看结束符
我个人在实际面试和辅导中积累的经验是,所有关于sizeof和strlen的题目,都可以用十个字概括解题步骤:“先看类型,再看结束符。”
看到sizeof,先问自己这个操作数是什么类型:是数组名、指针、结构体,还是普通变量?类型确定后,sizeof的本质就是返回这个类型的字节数。看到strlen,先问这个内存区域是不是真的以'\0'结尾:数组初始化方式决定了它是否隐藏着终止符,指针指向的字符串是否合法。只要这两个问题有答案,题目基本就不会错。
我当时学这个知识点时,也经历过“背了三天题,换个新题又错”的阶段。后来把所有情况写成一个printf全家桶程序,在本地跑了一遍,亲眼看到输出结果,才真正把这些知识点内化。所以在这里也建议你花一个晚上把上面清单敲一遍、跑一遍、故意猜错几回。踩过坑的印象,永远比死记硬背深刻得多。