直接用 strcpy 或 strncpy 会崩溃,因未检查源指针是否为空或越界;安全做法是先判空、清零目标数组、用 strncpy(dst, src, sizeof(dst)-1) 并依赖初始化保证 '\0'。

直接用 strcpy 或 strncpy 会崩溃?先看源字符串是否为空
不能直接把 const char* 赋值给 char[],因为数组名是常量地址,不支持赋值操作。常见错误是写成 char buf[64] = str; —— 这在编译期就报错:「initializer element is not constant」。真正要做的,是把指针指向的内容**复制进数组**。
但复制前必须检查:str 是否为 nullptr。否则 strcpy(buf, str) 会触发段错误。尤其从 C API(如 getenv、sqlite3_column_text)拿到的 const char*,空值很常见。
- 安全做法:先判空,再复制,且控制长度防止溢出
- 推荐用
strncpy而非strcpy,避免缓冲区溢出 -
strncpy不保证末尾自动加'\0',需手动补零(尤其当源串长度 ≥ 目标大小时)
用 strncpy + 手动补 '\0' 是最稳妥的写法
假设目标是把 const char* src 拷贝到 char dst[128] 中:
char dst[128] = {0}; // 先清零,确保末尾有 '\0'
if (src != nullptr) {
strncpy(dst, src, sizeof(dst) - 1);
}
// 不需要额外判断 strncpy 是否截断 —— 清零已保证安全
这里 sizeof(dst) - 1 留出一个字节给终止符;而开头的 {0} 初始化让整个数组为零,即使 strncpy 没写满,末尾也必有 '\0'。比每次调用后加 dst[sizeof(dst)-1] = '\0'; 更简洁可靠。
- 不要用
strlen(src)做长度判断再 memcpy —— 多一次遍历,且仍要防空指针 - 如果确定
src非空且长度可控,snprintf(dst, sizeof(dst), "%s", src)也可行,但有轻微格式化开销 - C++17 起可用
std::string_view中转,但若目标是纯 C 风格数组,没必要绕路
用 std::string 临时中转反而容易踩坑
有人写 std::string tmp(src); strcpy(dst, tmp.c_str());,看似安全,实则多此一举且隐藏风险:
- 如果
src是空指针,std::string构造会抛std::logic_error(C++11 起标准行为),不是静默失败 - 即使捕获异常,也增加了控制流复杂度,不如原生 C 字符串判空直接
-
tmp.c_str()返回的指针生命周期只在表达式内,但strcpy是立即调用,所以这步本身没问题;问题出在前面的构造阶段
结论:除非你已经在用 std::string 管理该字符串,否则别为了“安全”强行引入 STL 对象做中转。
静态字符数组初始化时只能用字符串字面量
像 char arr[] = "hello"; 是合法的,但 const char* p = "world"; char arr[] = p; 编译不过。C++ 标准禁止用非常量表达式初始化具有自动存储期的数组。
这意味着:函数内定义的 char buf[256],无法在声明时直接“接收”一个运行时才确定的 const char*。必须拆成两步:声明 → 复制。
- 全局或 static 数组允许用 constexpr 表达式初始化,但
const char*通常不是 constexpr - 想一步到位?改用
std::array<char></char>配合std::copy,但本质仍是复制,不是赋值 - 真正省事的做法:如果只是临时使用,直接用
std::string或std::string_view,避开 C 风格数组
最常被忽略的是空指针检查和缓冲区边界处理——这两个点一漏,程序可能在线上跑几天才崩,而且崩在别人代码里,很难回溯。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











