07 字符串复制与缓冲区安全
1. C 字符串需要 \0
C 字符串以 \0(值为 0 的字节)结束。字符串 "ABC" 有 3 个可见字符,但需要 4 字节:A B C \0。
- 存储完整字符串:目标容量至少为 字符数 + 1。
- 原始二进制数据不一定有
\0,不能直接当字符串处理。
2. strcpy、memcpy、memmove 的区别
| 函数 | 复制规则 | 自动复制/补充 \0? | 支持区域重叠? | 是否检查目标容量? |
|---|---|---|---|---|
strcpy(dst, src) | 从 src 复制到遇到 \0,包含这个终止符 | 复制源字符串的 \0 | 否 | 否 |
memcpy(dst, src, n) | 精确复制 n 字节 | 不会额外补 \0(若 n 覆盖源中的 \0,也会原样复制) | 否 | 否 |
memmove(dst, src, n) | 精确复制 n 字节 | 同 memcpy | 是 | 否 |
核心记忆:strcpy 处理字符串;memcpy 处理不重叠内存;memmove 处理可能重叠的内存。
char src[] = "ABC";
char dst[4] = {0};
strcpy(dst, src); // 复制 A B C \0
memcpy(dst, src, 3); // 只复制 A B C;dst[3] 原本为 0
char data[] = "ABCDE";
memmove(data + 1, data, 3); // 得到 "AABCE"
// memcpy(data + 1, data, 3); // 区域重叠:未定义行为
3. 常见安全问题
- 目标容量不足:
strcpy不知道目标大小,可能越界;memcpy和memmove也不会检查容量。 - 漏掉终止符:
memcpy(dst, src, n)只复制指定字节,后续如果把dst当字符串使用,必须确保有效范围内有\0。 - 重叠复制:源和目标区域有重叠时,选
memmove,不要使用memcpy或strcpy。 - 读取越界:三个函数都要求源区域包含足够的可读数据;
strcpy还要求源字符串能够在有效范围内找到\0。
4. 实际开发中的选择
- 复制完整 C 字符串:确保目标容量不小于
strlen(src) + 1,再用strcpy;若长度来自不可信输入,还需验证源和目标的有效范围。 - 复制蓝牙协议包等二进制数据:使用
memcpy,同时明确源数据长度和目标容量。 - 在同一缓冲区中移动数据:使用
memmove,例如删除协议头、移动剩余字节。
一句话:先明确复制的是字符串还是字节,再确认容量,最后判断源和目标是否重叠。