strcpy (string copy) 是 C 标准库 <string.h> 中的一个函数,用于将一个字符串(以 '\0' 结尾的字符数组)复制到另一个字符数组中。

(图片来源网络,侵删)
strcpy 函数的原型与行为
我们来看一下它的标准原型:
char *strcpy(char *dest, const char *src);
参数说明:
dest(destination): 目标字符数组的指针,函数会将src字符串的内容复制到这个地址开始的空间中。src(source): 源字符串的指针,这个字符串必须是'\0'结尾的。const关键字表示strcpy函数不会修改源字符串的内容。
返回值:
- 函数返回
dest的首地址,即一个指向目标字符串的char*指针。
行为与注意事项:

(图片来源网络,侵删)
- 复制到
'\0'为止:strcpy会复制src字符串中的所有字符,包括结尾的'\0'(空字符),直到遇到'\0'才停止。 - 缓冲区溢出: 这是最重要的安全注意事项!
strcpy不会检查目标缓冲区dest的大小。src字符串的长度(包括'\0')超过了dest数组的容量,就会发生缓冲区溢出,导致程序崩溃或安全漏洞(例如被恶意代码利用)。 - 内存重叠:
dest和src所指向的内存区域有重叠,strcpy的行为是未定义的,这可能导致复制的数据不正确。
strcpy 的基本实现
strcpy 的核心逻辑非常简单:循环地从 src 逐个字符复制到 dest,直到遇到 src 的结尾 '\0'。
使用 while 循环和指针
这是最经典、最易于理解的实现方式,我们使用指针来遍历字符串。
#include <stdio.h>
/**
* @brief 自定义 strcpy 函数实现
* @param dest 目标字符串缓冲区
* @param src 源字符串
* @return 返回 dest 的首地址
*/
char *my_strcpy(char *dest, const char *src) {
// 保存 dest 的起始地址,用于最后返回
char *ptr = dest;
// 循环条件:只要 *src 不是 '\0',循环就继续
while (*src != '\0') {
// 将 src 指向的字符赋值给 dest 指向的位置
*dest = *src;
// 同时移动 dest 和 src 指针,指向下一个字符
dest++;
src++;
}
// 循环结束后,src 指向 '\0',我们需要手动将 '\0' 复制到 dest 的当前位置
*dest = '\0';
// 返回保存好的起始地址
return ptr;
}
代码解析:
char *ptr = dest;: 我们创建一个临时指针ptr来记住dest的起始地址,因为在循环中dest指针会不断移动,最后指向的是字符串的末尾,我们需要返回的是字符串的开头。while (*src != '\0'): 这是循环的终止条件,只要src当前指向的字符不是字符串结束符'\0',循环就继续。*dest = *src;: 这是核心的复制操作,将src指针解引用(获取其指向的字符)的值,赋给dest指针解引用的地址。dest++; src++;: 将两个指针都向前移动一个字节,指向下一个字符。*dest = '\0';: 当循环结束时,src指向了'\0',而dest指向了目标字符串的末尾的下一个位置,我们需要在这里手动写入一个'\0',以确保目标字符串也是正确结尾的。return ptr;: 返回保存好的起始地址。
更简洁的实现(利用 '\0' 的值为 0)
在 C 语言中,'\0' 的 ASCII 码值就是 0。while (*src != '\0') 可以简化为 while (*src),因为当 *src 是 '\0' 时,表达式 *src 的值就是 0(假),循环终止。
#include <stdio.h>
char *my_strcpy_v2(char *dest, const char *src) {
char *ptr = dest;
// 循环条件:只要 *src 不是 0 (即 '\0'),循环就继续
while (*src) {
*dest++ = *src++;
}
// 循环结束后,*src 的值是 '\0',所以这个赋值操作会将 '\0' 写入 dest
*dest = '\0';
return ptr;
}
这个版本更紧凑,但功能与版本一完全相同。
最精简的实现(不推荐用于生产,但常见于面试)
这个版本利用了赋值表达式的返回值,赋值操作 a = b 的值就是 b 的值。
#include <stdio.h>
char *my_strcpy_v3(char *dest, const char *src) {
char *ptr = dest;
// *dest++ = *src++ 的执行流程:
// 1. 获取 *src 的值
// 2. 将该值赋给 *dest
// 3. 将 src 和 dest 指针都向后移动
// 4. 整个表达式的值为 *src 的值(即被复制的字符)
// 当复制到 '\0' 时,表达式的值为 0,循环终止
while ((*dest++ = *src++)) {
// 循环体为空,所有逻辑都在 while 条件中
}
return ptr;
}
这个版本非常简洁,但可读性稍差,对于初学者可能不太好理解,在实际开发中,版本一或版本二的可读性更好。
完整示例代码
下面是一个完整的 C 程序,演示了如何使用我们自己实现的 my_strcpy 函数。
#include <stdio.h>
// 使用版本一的实现
char *my_strcpy(char *dest, const char *src) {
char *ptr = dest;
while (*src != '\0') {
*dest = *src;
dest++;
src++;
}
*dest = '\0';
return ptr;
}
int main() {
// 定义源字符串和目标字符数组
const char *source_str = "Hello, C Language!";
char destination_str[50]; // 确保目标缓冲区足够大
printf("源字符串: %s\n", source_str);
printf("复制前,目标字符串: %s\n", destination_str);
// 调用我们自己实现的 strcpy 函数
// 返回值被我们忽略,但我们也可以接收它
my_strcpy(destination_str, source_str);
printf("复制后,目标字符串: %s\n", destination_str);
// 也可以接收返回值
char another_dest[50];
char *ret_ptr = my_strcpy(another_dest, "This is a test.");
printf("使用返回值打印: %s\n", ret_ptr);
return 0;
}
编译与运行:
$ gcc strcpy_example.c -o strcpy_example
$ ./strcpy_example
源字符串: Hello, C Language!
复制前,目标字符串:
复制后,目标字符串: Hello, C Language!
使用返回值打印: This is a test.
安全的替代方案:strncpy
由于 strcpy 存在缓冲区溢出的风险,C 标准库还提供了 strncpy 函数,它允许你指定最多复制的字符数。
char *strncpy(char *dest, const char *src, size_t n);
strncpy 会从 src 复制最多 n 个字符到 dest。
strncpy 的行为:
strlen(src) < n:它会复制整个src字符串(包括'\0'),然后在剩余的空间里用'\0'填充,直到总共复制了n个字符。strlen(src) >= n:它会复制src的前n个字符到dest。但不会自动添加'\0'结尾符! 这会导致dest可能不是一个合法的 C 字符串。
使用 strncpy 时,需要格外小心,最好总是手动确保 dest 是以 '\0' 结尾的。
示例:
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello";
char dest[10];
// 复制最多 3 个字符
strncpy(dest, src, 3);
// 因为 src 的长度 (5) > 3,dest 不会自动添加 '\0'
// 必须手动添加!
dest[3] = '\0';
printf("strncpy 结果: %s\n", dest); // 输出: Hel
return 0;
}
| 特性 | strcpy |
strncpy |
|---|---|---|
| 功能 | 复制整个字符串(到 '\0' 为止) |
复制最多 n 个字符 |
| 安全性 | 不安全,容易导致缓冲区溢出 | 更安全,但需要小心处理 '\0' |
| 原型 | char *strcpy(char *dest, const char *src); |
char *strncpy(char *dest, const char *src, size_t n); |
| 优点 | 简单易用 | 可控制复制的最大长度,防止溢出 |
| 缺点 | 危险,不检查目标缓冲区大小 | n 小于源字符串长度,不会自动添加 '\0' |
| 实现核心 | while (*src) { *dest++ = *src++; } |
for (size_t i = 0; i < n && src[i]; i++) { dest[i] = src[i]; } |
在编写现代 C 代码时,如果可能,应优先使用更安全的函数,如 strncpy、snprintf 或 C 标准库中 <string.h> 提供的其他安全函数。
