sizeof和strlen的区别
sizeof和strlen的区别
- 一、本质不同:运算符 vs 库函数
- 二、求值时机:编译期常量 vs 运行时计算
- 三、测量核心:总内存大小 vs 有效字符长度
- 32 位系统输出
- 四、职责分离:`sizeof` 问结构,`strlen` 问内容
- 五、常见陷阱:函数参数中的数组退化
- 六、安全性差异
- 七、全面对照总结表
一、本质不同:运算符 vs 库函数
sizeof是 C/C++ 语言内置的运算符(关键字),由编译器直接处理,无需包含任何头文件。strlen是 C 标准库提供的函数,声明在<string.h>中,使用时必须包含该头文件。
| 维度 | sizeof | strlen |
|---|---|---|
| 本质 | 运算符(关键字) | 库函数 |
| 所需头文件 | 无 | <string.h>(C++ 中为<cstring>) |
| 作用对象 | 类型名、变量名、表达式 | const char*(以'\0'结尾的字符串) |
二、求值时机:编译期常量 vs 运行时计算
sizeof:在编译阶段计算结果,等价于一个常量,不依赖程序实际运行。strlen:在程序运行时从传入地址逐个扫描内存,直到遇见'\0'才停止。
#include<stdio.h>#include<string.h>intmain(void){chars[]="abc";// 编译器分配 4 字节:'a','b','c','\0'printf("sizeof(s) = %zu\n",sizeof(s));// 编译时已知printf("strlen(s) = %zu\n",strlen(s));// 运行时遍历return0;}输出
sizeof(s) = 4 strlen(s) = 3讲解
- 数组
s被初始化为"abc",编译器分配 4 个字节(含结尾'\0')。sizeof(s)在编译时就被替换为常数4。 strlen(s)在运行时从s的首地址向后扫描,数到'\0'前一共 3 个字符,返回3。
三、测量核心:总内存大小 vs 有效字符长度
sizeof:测量类型或对象占用的总内存字节数。若对象是字符数组,会包含结尾的'\0'。strlen:测量字符串中有效字符的个数,绝不包含结尾的'\0'。
#include<stdio.h>#include<string.h>intmain(void){charstr[]="hello";// 数组 —— 实际占据 6 字节char*p="hello";// 指针 —— p 只存一个地址printf("=== 对字符数组 str ===\n");printf("sizeof(str) = %zu\n",sizeof(str));// 整个数组大小printf("strlen(str) = %zu\n",strlen(str));// 有效字符数printf("\n=== 对指针 p ===\n");printf("sizeof(p) = %zu\n",sizeof(p));// 指针变量自身大小printf("strlen(p) = %zu\n",strlen(p));// 指针所指字符串长度return0;}输出(64 位系统)
=== 对字符数组 str === sizeof(str) = 6 strlen(str) = 5 === 对指针 p === sizeof(p) = 8 strlen(p) = 532 位系统输出
=== 对字符数组 str === sizeof(str) = 6 strlen(str) = 5 === 对指针 p === sizeof(p) = 4 strlen(p) = 5讲解
- 数组
str:sizeof(str)返回6(5 个字母 + 1 个'\0'),32 位与 64 位系统结果完全相同。strlen(str)返回5(遇'\0'停止,不计入结束符)。 - 指针
p:sizeof(p)在64 位系统下返回 8,在32 位系统下返回 4,只与系统位数有关,与p指向什么内容完全无关。strlen(p)始终返回5,因为它沿着指针地址计算有效字符长度,至'\0'停止,不受系统位数影响。
四、职责分离:sizeof问结构,strlen问内容
sizeof只关心“这个变量 / 类型本身占多大空间”。不读取内存,不关心里面存的是什么。strlen只关心“从这个地址开始,存放着一个多长的字符串”。必须读取内存,以'\0'为唯一终止标记。
因此:- 对数组名用
sizeof→ 得到整个数组的字节数。 - 对指针变量用
sizeof→ 得到指针变量自身的大小(4 或 8 字节)。 strlen对数组名和指针一视同仁,都是从给定地址向后数非'\0'字符。
五、常见陷阱:函数参数中的数组退化
数组作为函数参数传递时,会退化为指针。此时在函数内部使用sizeof(形参名)将无法获得原数组大小,只能得到指针大小。而strlen可以继续正常工作。
#include<stdio.h>#include<string.h>voidexamine(chararr[]){// 形参等价于 char *arrprintf("在函数内部:\n");printf(" sizeof(arr) = %zu <-- 这是指针的大小,不是数组的大小\n",sizeof(arr));printf(" strlen(arr) = %zu <-- 这才是字符串的真正长度\n",strlen(arr));}intmain(void){charstr[]="hello";printf("在 main 中 sizeof(str) = %zu\n\n",sizeof(str));examine(str);return0;}输出(64 位系统)
在 main 中 sizeof(str) = 6 在函数内部: sizeof(arr) = 8 <-- 这是指针的大小,不是数组的大小 strlen(arr) = 5 <-- 这才是字符串的真正长度讲解
main中sizeof(str)正确反映数组总大小6。- 传入函数后,形参
arr虽写作数组形式,但本质是指针。sizeof(arr)返回指针变量的大小8,丢失了原数组的长度。 strlen(arr)不受影响,依然从地址扫描,得到正确长度5。
🔑编程铁律:函数内处理字符串时,要长度请用
strlen,或显式传入长度参数,不可依赖sizeof。
六、安全性差异
strlen有越界风险:必须扫描到'\0'才停止。若传入的指针不是指向以'\0'结尾的有效字符串,将导致未定义行为,可能读取非法内存甚至崩溃。sizeof不访问内存:仅根据类型信息在编译期推算大小,没有越界风险,极其安全。
char*dangerous=(char*)0x100;// 任意地址// strlen(dangerous); // 未定义行为,极可能崩溃// sizeof(*dangerous); // 等价于 sizeof(char),安全得到 1七、全面对照总结表
| 比较维度 | sizeof | strlen |
|---|---|---|
| 是什么 | 运算符(关键字) | 库函数 |
| 头文件 | 无 | <string.h>/<cstring> |
| 计算时机 | 编译时(VLA 除外) | 运行时 |
| 计算对象 | 类型、变量、表达式 | 以'\0'结尾的字符串(const char*) |
| 测量内容 | 内存占用总字节数 | 有效字符个数(不含'\0') |
是否包含'\0' | 数组:包含; 指针:与内容无关 | 不包含 |
| 对数组 | 返回整个数组大小 | 返回内部字符串长度 |
| 对指针 | 返回指针变量自身大小(4/8 字节) | 返回指针所指字符串长度 |
| 在函数参数中 | 数组退化 → 返回指针大小 | 仍可正确求字符串长度 |
| 安全性 | 不访问内存,无越界 | 依赖正确的'\0',否则可能越界 |
| 结果是否为编译期常量 | 多数情况是 | 否,运行时值 |
✨记忆口诀
sizeof:问 “变量 / 类型 占多大坑” ——编译时定,不读数据。strlen:问 “坑里字符串有多长 (别把末尾\0当字符)” ——运行时数,认\0为止。
