C++中不应直接使用strcpy和strncpy,因二者均不检查缓冲区大小且strncpy不保证null终止,易致缓冲区溢出或未定义行为;应优先使用std::string,必要时用snprintf或封装安全函数。

为什么不能直接用 strcpy 和 strncpy
这两个函数在 C++ 中仍可调用(来自 ),但它们不检查目标缓冲区大小,也不保证结果字符串以 '\0' 结尾——strcpy 可能越界写入,strncpy 在源串长度 ≥ 目标大小时不会补 '\0',后续用 strlen 或 printf 就会读越界。常见错误现象是程序崩溃、输出乱码或堆栈破坏。
典型误用:
char dst[5]; strcpy(dst, "hello"); // 写入 6 字节(含 '\0'),dst 只有 5 字节 → 缓冲区溢出
char dst[5]; strncpy(dst, "hello", sizeof(dst)); // 拷贝前 5 字节 "hello",不加 '\0' → dst 不是合法 C 字符串
strlcpy 是更安全的替代,但不是标准 C/C++ 函数
strlcpy 在 BSD 系统和部分 Linux 发行版(如 glibc 2.39+)中可用,它明确要求目标缓冲区大小,并始终以 '\0' 结尾(除非 size == 0)。但它**不是 ISO C++ 标准函数**,跨平台项目中需谨慎:
- Windows 默认不提供,需自行实现或引入兼容层
- 旧版 glibc(如 CentOS 7)不支持
- 参数顺序是
strlcpy(dst, src, size),与strncpy一致,但语义更清晰
简易自实现(仅作示意,生产环境建议用 std::string 或已验证的库):
立即学习“C++免费学习笔记(深入)”;
size_t strlcpy(char* dst, const char* src, size_t size) {
if (size == 0) return strlen(src);
size_t len = strlen(src);
size_t copy_len = (len < size - 1) ? len : size - 1;
memcpy(dst, src, copy_len);
dst[copy_len] = '\0';
return len;
}
真正推荐的 C 风格安全做法:用 snprintf
snprintf 是 ISO C99 起就标准化的函数,C++11 及以后可直接用(包含 ),它天然带长度限制且保证末尾 '\0':
- 总是返回「若缓冲区足够大时本应写入的字符数」,可用于判断是否截断
- 即使
size == 0也安全(只计算长度,不写内存) - 支持格式化,但单纯拷贝字符串时用
"%s"即可
示例:
char dst[10];
int ret = snprintf(dst, sizeof(dst), "%s", "hello world");
// ret == 11 → 实际写入了 "hello wor" + '\0'(共 10 字节),说明被截断
if (ret >= (int)sizeof(dst)) {
// 拷贝不完整,需处理
}
C++ 项目里其实不该用这些 C 风格函数
除非你明确在写系统层、嵌入式驱动或与 C ABI 交互的接口,否则在普通 C++ 代码中坚持用 strcpy 类函数,等于主动放弃类型安全和 RAII。真正该做的:
- 用
std::string存储和传递文本,拷贝就是std::string s2 = s1;,无缓冲区管理负担 - 需要 C 兼容接口时,用
s.c_str()获取只读指针;必须写入 C 缓冲区时,先确认大小,再用snprintf或std::copy_n+ 手动置'\0' - 若必须用 C 风格字符串操作,至少封装一层检查:比如写个
safe_strcpy(dst, sizeof(dst), src)并在 debug 版本做断言
最常被忽略的一点:很多开发者以为「用了 strncpy 就安全了」,却忘了它不自动补 '\0',而这个缺失的空字符,在后续任意一次 strcmp 或 strcat 中都可能引发未定义行为。










