strcpy (string copy) 是 C 标准库 <string.h> 中的一个函数,用于将源字符串(包括结尾的空字符 \0)复制到目标字符串中。

(图片来源网络,侵删)
strcpy 函数的原型与行为
我们来看一下标准库中 strcpy 的函数原型:
char *strcpy(char *dest, const char *src);
参数说明:
dest: 目标字符串的指针,它必须有足够的空间来容纳源字符串src的内容,否则会导致缓冲区溢出,这是非常危险的。src: 源字符串的指针。const关键字表示这个字符串不会被strcpy函数修改。
返回值:
- 成功时,返回目标字符串
dest的起始地址。
核心行为:

(图片来源网络,侵删)
- 从
src指向的地址开始,逐个字符复制到dest指向的地址。 - 复制过程一直持续,直到遇到源字符串的结尾空字符
\0。 - 将
\0也复制到dest中,以确保目标字符串dest也是一个合法的 C 字符串。 - 返回
dest的值。
手动实现 strcpy
理解了其行为后,我们可以用几种不同的方式来实现它。
使用 while 循环(最经典、最易理解的方式)
这是最常见和最直观的实现方法,其核心思想是:先复制,后判断,直到遇到 \0 为止。
/**
* @brief 实现标准库的 strcpy 函数
* @param dest 目标字符串缓冲区
* @param src 源字符串
* @return 返回目标字符串 dest 的起始地址
*/
char *my_strcpy(char *dest, const char *src)
{
char *ptr = dest; // 保存 dest 的起始地址,用于最后返回
// *src 在遇到 '\0' 时为 0 (假),循环结束
while (*src != '\0')
{
*dest = *src; // 将 src 指向的字符赋值给 dest 指向的位置
dest++; // dest 指针向后移动一位
src++; // src 指针向后移动一位
}
// 循环结束后,*src 已经是 '\0',需要将这个 '\0' 也复制到 dest 中
*dest = '\0';
return ptr; // 返回目标字符串的起始地址
}
代码解析:
char *ptr = dest;:我们创建一个临时指针ptr来记住dest的起始位置,因为dest指针本身会在循环中被移动,最后会指向字符串的末尾,如果我们直接return dest;,返回的将是'\0'的地址,而不是字符串的开头。while (*src != '\0'):循环条件,只要src当前指向的字符不是字符串结尾\0,循环就继续。*dest = *src;:核心的复制操作,获取src指向的字符值,并赋给dest指向的内存单元。dest++;和src++:指针自增,分别指向下一个字符位置。*dest = '\0';:循环结束后,src指向\0,此时我们将这个\0复制到dest的当前位置,完成字符串的结尾。return ptr;:返回我们最初保存的dest的起始地址。
使用 do-while 循环
这种方法逻辑上略有不同,是“先复制,后判断”,可以省去最后的 *dest = '\0'; 这一步。

(图片来源网络,侵删)
char *my_strcpy_do_while(char *dest, const char *src)
{
char *ptr = dest;
// 至少会执行一次循环
do
{
*dest = *src;
dest++;
src++;
} while (*src != '\0'); // 判断条件是 src 是否为 '\0'
// 注意:当 *src 是 '\0' 时,循环已经把 '\0' 赋值给了 *dest,所以不需要额外操作
return ptr;
}
代码解析:
do-while 循环保证循环体至少执行一次,当 src 指向 \0 时,循环体会执行一次,将 \0 复制到 dest,src++ 和 dest++,之后 while 条件 *src != '\0'(*src 是 \0 的下一个字节,不确定是什么)为假,循环结束,虽然指针会越界,但复制 \0 的任务已经完成,这种方法不如 while 循环直观,且存在潜在的指针越界访问风险(尽管通常不会造成问题),因此不推荐。
使用指针运算(更简洁的写法)
这种方法利用了指针的算术运算,代码更紧凑,但可读性稍差。
char *my_strcpy_pointer(char *dest, const char *src)
{
char *ptr = dest;
// 当 *src 不为 '\0' 时,循环继续
while ((*dest++ = *src++) != '\0')
{
// 循环体为空,所有工作都在 while 条件中完成
}
return ptr;
}
代码解析:
这是 C 语言中非常经典和地道的写法,我们重点分析 while 循环的条件:
(*dest++ = *src++) != '\0'
这个复合表达式可以拆解为以下几个步骤(从左到右):
*src:获取src当前指向的字符的值。*dest = ...:将这个值赋给dest当前指向的内存单元。src++:src指针向后移动。dest++:dest指针向后移动。... != '\0':将赋值操作的结果(即被复制的字符的值)与\0进行比较。
执行流程:
- 假设
src指向'a',dest指向某个地址。 'a'被赋值给dest,src和dest都向后移动。- 表达式的结果是
'a','a'不等于'\0',所以条件为真,循环继续。 - ...直到
src指向'\0'。 '\0'被赋值给dest,src和dest都向后移动。- 表达式的结果是
'\0','\0'等于'\0',所以条件为假,循环结束。
这个版本高效且简洁,是很多 C 程序员喜欢的写法。
完整示例与使用
下面是一个完整的 C 程序,演示了如何使用我们自己实现的 my_strcpy 函数。
#include <stdio.h>
// 使用方法三的实现
char *my_strcpy(char *dest, const char *src)
{
char *ptr = dest;
while ((*dest++ = *src++) != '\0')
{
// 空循环体
}
return ptr;
}
int main()
{
// 1. 确保目标缓冲区足够大
char dest1[50]; // 定义一个足够大的字符数组
const char *src1 = "Hello, C Language!";
// 调用我们自己实现的函数
my_strcpy(dest1, src1);
printf("Source string: %s\n", src1);
printf("Copied string: %s\n", dest1);
// 2. 使用指针作为目标
const char *src2 = "This is a test.";
char dest2[20]; // 大小刚好够用(包括 '\0')
char *copied_str = my_strcpy(dest2, src2);
printf("\nSource string: %s\n", src2);
printf("Copied string via return value: %s\n", copied_str); // 使用返回值
printf("Copied string via dest2: %s\n", dest2); // 使用原目标指针
// 3. 危险示例:缓冲区溢出
// char dest_small[5];
// const char *src_long = "This is too long!";
// my_strcpy(dest_small, src_long); // 这行代码会导致未定义行为,可能破坏程序内存
// printf("\nDangerous example: %s\n", dest_small); // 不要运行!
return 0;
}
编译与运行:
将上述代码保存为 strcpy_demo.c,然后使用 gcc 编译并运行:
gcc strcpy_demo.c -o strcpy_demo ./strcpy_demo
输出:
Source string: Hello, C Language!
Copied string: Hello, C Language!
Source string: This is a test.
Copied string via return value: This is a test.
Copied string via dest2: This is a test.
重要注意事项
- 缓冲区溢出:这是
strcpy函数最大的安全隐患,调用者必须确保dest指向的缓冲区足够大,能够容纳src字符串(包括\0)。src比dest长,就会发生缓冲区溢出,覆盖掉dest之后的内存,导致程序崩溃、数据损坏甚至被黑客利用进行攻击。 - 不重叠:标准
strcpy通常要求源内存区域和目标内存区域不能重叠,如果重叠,行为是未定义的。char str[] = "hello"; strcpy(str + 1, str); // 危险!源和目标区域重叠
这种情况下,应该使用
memmove函数,它明确支持重叠区域的复制。 - 安全替代方案:由于
strcpy的危险性,现代 C 编程推荐使用更安全的函数,如strncpy、strcpy_s(C11 标准,但支持度不一) 或snprintf。strncpy(dest, src, n):可以限制最多复制的字符数n,但需要注意它不会自动添加\0,src长度 >=n,dest将不会以\0snprintf(dest, dest_size, "%s", src):这是最推荐的安全方式之一,它会确保不会写入超过dest_size的字符,并自动添加\0。
