1. 基本概念与用途
1.1 sizeof 的定义与场景
sizeof 的核心是返回给定对象或类型在程序中的字节数,属于编译时信息,常用于确定静态数组的容量、对齐和内存布局的推导。
strlen 是一个运行时函数,按字节遍历字符直到遇到空字符 '\0',因此结果是有效字符数量,不包含结束符。
#include <iostream>
#include <cstddef>int main() {int a[10];std::cout << "sizeof(a) = " << sizeof(a) << std::endl;std::cout << "sizeof(a)/sizeof(a[0]) = " << (sizeof(a) / sizeof(a[0])) << std::endl;return 0;
}
示例输出与要点:对于静态数组,sizeof 给出元素个数乘以元素类型大小的总字节数,这在编写数组遍历、内存对齐和缓冲区分配时非常有用。
1.2 strlen 的定义与应用场景
strlen 的返回值来自遍历至结束符的字符数量,适用于 C 风格字符串的长度计算,在模板元编程或编译期大小推断方面并不直接可用。
两者的时序差异 是理解要点:sizeof 可在编译阶段确定对象的字节数,而 strlen 必须在运行时读取内存内容判断有效字符数量,因此它不能在编译期工作。
#include <iostream>
#include <cstring>int main() {char s[] = "hello";std::cout << "sizeof(s) = " << sizeof(s) << std::endl; // 输出 6(包含结束符)std::cout << "strlen(s) = " << std::strlen(s) << std::endl; // 输出 5return 0;
}
要点提醒:将 sizeof 用于获取字符串长度时会产生误解,因为它返回字节数且可能包含或不包含结束符,具体取决于字符串的定义形式。
2. sizeof 在数组与指针上的区别
2.1 数组名与指针变量的 sizeof 行为差异
数组名在表达式中通常表现为整块内存对象;对静态数组,sizeof 会返回整个数组的字节数。
指针变量本身只是一个地址,它的大小由平台位宽决定(如 4 字节或 8 字节),sizeof 对指针返回的是指针本身的大小,而不是它所指向的对象大小。
#include <iostream>void show(char (&arr)[4]) {std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl;
}int main() {char a[4] = {'a','b','c','d'};char* p = a;std::cout << "sizeof(a) = " << sizeof(a) << std::endl;std::cout << "sizeof(p) = " << sizeof(p) << std::endl;show(a);return 0;
}
实战要点:当需要获取数组长度时,直接对数组对象使用 sizeof 是正确的;对指针变量使用 sizeof 时得到的是指针大小,需通过其他信息来推断实际容量。
2.2 指针与动态分配对象的大小
指针变量大小与其指向对象的大小是无关的,无论指针指向的是一个静态数组、堆中分配的内存,还是字符串常量,sizeof 只告诉你指针本身占用的字节数。
动态分配的内存区域的容量需要在分配时明确指定(例如 new、malloc),而且不受指针本身大小的影响;因此要对这部分内存进行边界管理。
#include <iostream>
#include <cstring>int main() {int a[8];int* p = a;std::cout << "sizeof(a) = " << sizeof(a) << std::endl;std::cout << "sizeof(p) = " << sizeof(p) << std::endl;return 0;
}
结论要点:若要获取数组的长度,请在编译期直接对数组对象使用 sizeof;若处理指针,请额外带上长度信息或传递数组大小作为参数。
3. strlen 的工作原理与局限
3.1 以空字符结束的 C 风格字符串
strlen 的内部实现依赖于逐字节扫描直到遇到结束符,因此结果仅反映有效字符数量,不包含结尾的 '\0'。
编码场景需注意:在使用 UTF-8 或其他变长编码时,单个字符可能由多个字节组成,因此长度单位和字节数可能不一致。

#include <cstring>
#include <iostream>int main() {const char* s = "hello";std::cout << "strlen(s) = " << std::strlen(s) << std::endl;return 0;
}
干扰因素:如果字符串没有以 NULL 结束,strlen 可能导致越界读取,产生未定义行为,因此必须确保传入函数的字符串是以 NULL 结尾的。
3.2 多字节字符集下的注意要点
多字节字符集(如 UTF-8)下,长度(字符数)与字节数之间的映射可能并非一一对应。
对比分析:若需要按字符计数而非字节,需要在应用层对字节序列进行解析,或使用更高级的字符串类型如 std::wstring、std::u8string 等。
#include <iostream>
#include <cstring>int main() {const char* s = "こんにちは"; // 注意:这里包含多字节字符std::cout << "strlen(s) = " << std::strlen(s) << std::endl;return 0;
}
4. 内存占用的全解析
4.1 静态字符串字面值与运行时分配的区别
字符串字面值存放在只读数据段,其大小由 sizeof(\"...\") 决定,通常包含结束符。
对堆上分配的字符数组,内存占用取决于分配方式与实现;如果使用 std::string,对象本身在栈上所占的大小仅代表字符串对象的元数据,实际可存放的字符在堆上分配。
#include <iostream>
#include <cstring>
#include <string>int main() {const char* lit = "hello";std::cout << "strlen(lit) = " << std::strlen(lit) << std::endl;std::cout << "sizeof(\"hello\") = " << sizeof("hello") << std::endl;std::string s = "hello";std::cout << "sizeof(s) = " << sizeof(s) << std::endl;return 0;
}
要点总结:字面值的内存大小与区域分配方式直接相关;std::string 的实际缓冲区在实现中可能在堆上,sizeof(std::string) 并不等于字符串内容的字节数。
4.2 内存占用对性能的影响
遍历与长度计算的成本,当使用 strlen 逐字节扫描字符串时,若字符串很长且在热点路径重复调用,会带来显著的运行時成本。
对于固定大小的缓冲区,优先通过 sizeof + 辅助信息进行边界检查,以避免在运行时进行额外的长度推导。
#include <iostream>
#include <cstring>int main() {const char* s = "hello world";// 通过已知常量时,尽量避免重复调用 strlenconst std::size_t len = std::strlen(s);std::cout << "len = " << len << std::endl;std::cout << "size of literal including null = " << sizeof("hello world") << std::endl;return 0;
}
5. 常见误区与实践细节
5.1 用 sizeof 求字符串长度的误区
误区之一是在函数外部对字符串常量使用 sizeof 来获取真实字符数;实质上,sizeof 返回的是编译期的字节数,包含结束符,而非字符数。
正确用法分析:如果要获得字符串中的字符数,应使用 strlen,但需确保是以 NULL 结束的 C 字符串;对于字面值常量,sizeof(\"...\") 提供的是常量表达式的大小。
#include <iostream>
#include <cstring>int main() {const char* s = "abc";std::cout << "sizeof(s) = " << sizeof(s) << std::endl; // 指针大小std::cout << "strlen(s) = " << std::strlen(s) << std::endl; // 字符数量std::cout << "sizeof(\"abc\") = " << sizeof("abc") << std::endl; // 包含结尾的字节数return 0;
}
5.2 调用场景中的 sizeof 的误导
在函数参数中,对数组参数声明为指针类型时,sizeof 无法反映原始数组长度,因为函数参数完成了类型转化。
正确的做法:在函数接口中通过模板参数或显式传入数组大小来保留长度信息,减小运行时错误风险。
#include <iostream>void g(char arr[]) {std::cout << "sizeof(arr) in function = " << sizeof(arr) << std::endl; // 实际输出指针大小
}int main() {char a[20];std::cout << "sizeof(a) in main = " << sizeof(a) << std::endl;g(a);return 0;
}


