14. sizeof 与 strlen 的区别:它们到底在算什么?
说实话,这个问题我面试过不少人,十有八九会答错。有人觉得「sizeof就是算长度的」,有人觉得「strlen就是sizeof的简化版」——嗯,这两种理解都不对。今天咱们就把这两个家伙彻底掰扯清楚。
14.1 先看一个让人懵圈的代码
我直接上代码,你先猜猜输出是什么:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
char *p = str;
printf("sizeof(str) = %zu\n", sizeof(str));
printf("strlen(str) = %zu\n", strlen(str));
printf("sizeof(p) = %zu\n", sizeof(p));
printf("strlen(p) = %zu\n", strlen(p));
return 0;
}
在64位系统上,输出是这样的:
sizeof(str) = 6
strlen(str) = 5
sizeof(p) = 8
strlen(p) = 5
看到了吗?sizeof(str) 是6,strlen(str) 是5。差在哪?差在结尾那个看不见的 '\0'。而 sizeof(p) 是8,因为指针本身占8个字节(64位系统),跟字符串长度半毛钱关系都没有。
核心区别一句话:
sizeof是运算符,算的是「变量或类型在内存中占了多少字节」strlen是函数,算的是「字符串从开头到'\0'之间有多少个字符」
14.2 sizeof 到底在算什么?
sizeof 是编译时求值的。编译器看到 sizeof,直接查类型信息,不需要运行程序就能知道结果。所以它算的是「内存布局的大小」,不是「内容的长短」。
举个例子:
int arr[10];
printf("%zu\n", sizeof(arr)); // 40,因为 int 占4字节,10个就是40
printf("%zu\n", sizeof(arr[0])); // 4,一个int的大小
数组名在这里代表整个数组,所以 sizeof(arr) 返回整个数组的字节数。但一旦数组名退化成指针,情况就变了:
void func(int arr[]) {
printf("%zu\n", sizeof(arr)); // 8,arr已经退化成指针了!
}
我在项目中遇到过好几次这种坑。有个同事在函数里用 sizeof(arr) 去算数组长度,结果传进来的数组永远「只有8个字节」——调试了一下午才发现是数组退化的问题。
注意:sizeof 对数组和指针的行为完全不同。数组名在 sizeof 中不会退化,但在函数参数中会退化成指针。这是C语言里最容易踩的坑之一。
14.3 strlen 到底在算什么?
strlen 是运行时函数。它从你给的地址开始,一个字节一个字节地往后数,直到碰到 '\0' 为止。它不关心你分配了多少内存,只关心 '\0' 在哪里。
看这个例子:
char buf[100] = "Hello";
printf("%zu\n", strlen(buf)); // 5,只数到'\0'为止
printf("%zu\n", sizeof(buf)); // 100,整个数组的大小
buf 有100个字节的空间,但 strlen 只数了5个字符就停了,因为第6个字符是 '\0'。sizeof 则老老实实告诉你:这个数组占了100个字节。
我曾经接手过一个项目,里面有一段代码:
char *msg = malloc(256);
strcpy(msg, "OK");
printf("length = %zu\n", strlen(msg)); // 2,没问题
printf("size = %zu\n", sizeof(msg)); // 8,指针大小,不是256!
那个程序员用 sizeof(msg) 去判断缓冲区是否够用,结果可想而知。嗯,后来我改成了 #define BUF_SIZE 256,用宏来管理缓冲区大小。
14.4 一张图看懂核心逻辑
下面这张图展示了 sizeof 和 strlen 在处理同一个字符串时的不同路径:
14.5 常见误区与避坑指南
我总结了几个最常见的错误,你看看自己中过几个:
| 误区 | 错误写法 | 正确做法 |
|---|---|---|
| 用 sizeof 算字符串长度 | sizeof("Hello") → 6 |
用 strlen("Hello") → 5 |
| 在函数内用 sizeof 算数组长度 | void f(int a[]) { sizeof(a); } |
把数组长度作为参数传进来 |
| 对指针用 sizeof 以为能拿到缓冲区大小 | char *p = malloc(100); sizeof(p); |
用宏或变量记录分配的大小 |
| 混淆 sizeof 和 strlen 的返回值类型 | 两者都返回 size_t,但含义不同 |
sizeof 是字节数,strlen 是字符数 |
我的个人习惯:
- 需要知道缓冲区大小时,用 sizeof(仅限数组)
- 需要知道字符串有效长度时,用 strlen
- 动态分配的内存,一定用宏或变量记住大小,别指望 sizeof 指针
14.6 一个容易忽略的细节
strlen 返回的是 size_t,这是个无符号类型。如果你这么写:
if (strlen(s) - 10 > 0) {
// 你以为长度大于10才进来?
}
当 strlen(s) 小于10时,无符号数减10会变成一个巨大的正数,条件永远为真。我曾经在代码审查时看到过这种写法,当时就提醒了那位同事。正确的写法是:
if (strlen(s) > 10) {
// 这样写才安全
}
14.7 总结一下
sizeof 和 strlen,一个编译时,一个运行时;一个算内存布局,一个算字符个数。搞混了,轻则逻辑错误,重则缓冲区溢出。你想想看,如果拿 sizeof 的结果去分配内存,或者拿 strlen 的结果去拷贝整个数组,后果都不堪设想。
记住一句话:sizeof 问的是「你有多大空间」,strlen 问的是「你有多长内容」。两者各司其职,别让它们干对方的活。
公众号:蓝海资料掘金营,微信deep3321