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,例如删除协议头、移动剩余字节。

一句话:先明确复制的是字符串还是字节,再确认容量,最后判断源和目标是否重叠。