strcpy函数如何实现字符串拷贝?

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

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

c语言strcpy函数实现
(图片来源网络,侵删)

strcpy 函数的原型与行为

我们来看一下标准库中 strcpy 的函数原型:

char *strcpy(char *dest, const char *src);

参数说明:

  • dest: 目标字符串的指针,它必须有足够的空间来容纳源字符串 src 的内容,否则会导致缓冲区溢出,这是非常危险的。
  • src: 源字符串的指针。const 关键字表示这个字符串不会被 strcpy 函数修改。

返回值:

  • 成功时,返回目标字符串 dest 的起始地址。

核心行为:

c语言strcpy函数实现
(图片来源网络,侵删)
  1. src 指向的地址开始,逐个字符复制到 dest 指向的地址。
  2. 复制过程一直持续,直到遇到源字符串的结尾空字符 \0
  3. \0 也复制到 dest,以确保目标字符串 dest 也是一个合法的 C 字符串。
  4. 返回 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; // 返回目标字符串的起始地址
}

代码解析:

  1. char *ptr = dest;:我们创建一个临时指针 ptr 来记住 dest 的起始位置,因为 dest 指针本身会在循环中被移动,最后会指向字符串的末尾,如果我们直接 return dest;,返回的将是 '\0' 的地址,而不是字符串的开头。
  2. while (*src != '\0'):循环条件,只要 src 当前指向的字符不是字符串结尾 \0,循环就继续。
  3. *dest = *src;:核心的复制操作,获取 src 指向的字符值,并赋给 dest 指向的内存单元。
  4. dest++;src++:指针自增,分别指向下一个字符位置。
  5. *dest = '\0';:循环结束后,src 指向 \0,此时我们将这个 \0 复制到 dest 的当前位置,完成字符串的结尾。
  6. return ptr;:返回我们最初保存的 dest 的起始地址。

使用 do-while 循环

这种方法逻辑上略有不同,是“先复制,后判断”,可以省去最后的 *dest = '\0'; 这一步。

c语言strcpy函数实现
(图片来源网络,侵删)
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 复制到 destsrc++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'

这个复合表达式可以拆解为以下几个步骤(从左到右):

  1. *src:获取 src 当前指向的字符的值。
  2. *dest = ...:将这个值赋给 dest 当前指向的内存单元。
  3. src++src 指针向后移动。
  4. dest++dest 指针向后移动。
  5. ... != '\0':将赋值操作的结果(即被复制的字符的值)与 \0 进行比较。

执行流程:

  • 假设 src 指向 'a'dest 指向某个地址。
  • 'a' 被赋值给 destsrcdest 都向后移动。
  • 表达式的结果是 'a''a' 不等于 '\0',所以条件为真,循环继续。
  • ...直到 src 指向 '\0'
  • '\0' 被赋值给 destsrcdest 都向后移动。
  • 表达式的结果是 '\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.

重要注意事项

  1. 缓冲区溢出:这是 strcpy 函数最大的安全隐患,调用者必须确保 dest 指向的缓冲区足够大,能够容纳 src 字符串(包括 \0)。srcdest 长,就会发生缓冲区溢出,覆盖掉 dest 之后的内存,导致程序崩溃、数据损坏甚至被黑客利用进行攻击。
  2. 不重叠:标准 strcpy 通常要求源内存区域和目标内存区域不能重叠,如果重叠,行为是未定义的。
    char str[] = "hello";
    strcpy(str + 1, str); // 危险!源和目标区域重叠

    这种情况下,应该使用 memmove 函数,它明确支持重叠区域的复制。

  3. 安全替代方案:由于 strcpy 的危险性,现代 C 编程推荐使用更安全的函数,如 strncpystrcpy_s (C11 标准,但支持度不一) 或 snprintf
    • strncpy(dest, src, n):可以限制最多复制的字符数 n,但需要注意它不会自动添加 \0src 长度 >= ndest 将不会以 \0
    • snprintf(dest, dest_size, "%s", src):这是最推荐的安全方式之一,它会确保不会写入超过 dest_size 的字符,并自动添加 \0
-- 展开阅读全文 --
头像
dede如何添加子菜单?操作步骤是怎样的?
« 上一篇 昨天
织梦代码乱码是编码问题还是文件损坏?
下一篇 » 昨天

相关文章

取消
微信二维码
支付宝二维码