memcpy与strcpy,何时用哪个更安全高效?

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 C语言 正文
  • strcpy (String Copy):专门用于复制\0 (空字符) 结尾的字符串,它会复制源字符串的所有字符,直到遇到 \0 为止,并在目标空间的末尾自动添加 \0
  • memcpy (Memory Copy):通用的内存块复制函数,它按字节复制指定大小的内存内容,是什么(可以是字符串、整数、结构体等),也不关心 \0

strcpy (String Copy)

strcpy 是字符串操作的基石之一。

c语言 memcpy strcpy
(图片来源网络,侵删)

函数原型

#include <string.h>
char *strcpy(char *dest, const char *src);

功能

将源字符串 src(Source)的内容复制到目标字符串 dest(Destination)中,直到遇到源字符串的结束符 \0,并在 dest 的末尾也添加一个 \0

参数

  • dest: 目标字符数组(字符串)的指针,必须有足够的空间来容纳源字符串,包括结尾的 \0
  • src: 源字符数组(字符串)的指针,通常是一个字符串字面量或另一个字符数组。

返回值

返回指向目标字符串 dest 的指针。

优点与缺点

  • 优点:专门为字符串设计,使用简单直观。
  • 致命缺点不安全strcpy 不会检查目标缓冲区 dest 的大小src 字符串的长度超过了 dest 的大小,就会发生缓冲区溢出,这可能导致程序崩溃、数据损坏,甚至被黑客利用进行恶意攻击。

代码示例

#include <stdio.h>
#include <string.h>
int main() {
    char src[] = "Hello, World!";
    // 必须确保 dest 的大小足够容纳 src 的内容 + '\0'
    // "Hello, World!" 有 13 个字符,加上 '\0' 共 14 个
    char dest[14]; 
    strcpy(dest, src);
    printf("源字符串: %s\n", src);
    printf("目标字符串: %s\n", dest);
    return 0;
}

安全替代方案:strncpy

为了解决 strcpy 的不安全问题,C 标准库提供了 strncpy

char *strncpy(char *dest, const char *src, size_t n);

strncpy 会从 src 复制最多 n 个字符到 dest

c语言 memcpy strcpy
(图片来源网络,侵删)
  • src 的长度小于 ndest 的剩余部分会用 \0 填充。
  • src 的长度大于或等于 ndest 不会自动添加 \0,这可能导致 dest 不是合法的字符串,使用 strncpy 时需要手动确保 dest\0

memcpy (Memory Copy)

memcpy 是更底层、更通用的内存操作函数。

函数原型

#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);

功能

从源内存地址 src 处开始,复制 n 个字节的数据到目标内存地址 dest 处,它是一个纯粹的内存拷贝,对数据类型一无所知。

参数

  • dest: 目标内存块的指针。
  • src: 源内存块的指针。
  • n: 要复制的字节数。这个参数是必须的,也是 memcpy 安全的关键

返回值

返回指向目标内存块 dest 的指针。

优点与缺点

  • 优点
    • 高效:现代编译器对 memcpy 有高度优化。
    • 安全:因为需要指定要复制的字节数,只要 n 不超过 dest 的大小,就不会发生缓冲区溢出。
    • 通用:可以复制任何类型的数据,而不仅仅是字符串。
  • 缺点:对于字符串,使用起来比 strcpy 稍显繁琐,需要手动计算长度或确定要复制的字节数。

代码示例

示例 1:复制字符串

c语言 memcpy strcpy
(图片来源网络,侵删)
#include <stdio.h>
#include <string.h>
int main() {
    char src[] = "Hello, Memory!";
    char dest[20];
    // sizeof(src) 会计算字符串长度 + 1 (给 '\0' 的空间)
    memcpy(dest, src, sizeof(src));
    printf("源字符串: %s\n", src);
    printf("目标字符串: %s\n", dest);
    return 0;
}

示例 2:复制整数数组 strcpy 无法完成这个任务,但 memcpy 可以。

#include <stdio.h>
#include <string.h>
int main() {
    int src_arr[] = {10, 20, 30, 40, 50};
    int dest_arr[5];
    // sizeof(src_arr) 得到整个数组占用的字节数
    memcpy(dest_arr, src_arr, sizeof(src_arr));
    printf("源数组: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", src_arr[i]);
    }
    printf("\n");
    printf("目标数组: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest_arr[i]);
    }
    printf("\n");
    return 0;
}

strcpy vs memcpy 对比总结

特性 strcpy memcpy
全称 String Copy (字符串拷贝) Memory Copy (内存拷贝)
目的 专门用于复制 C 风格字符串 通用的内存块拷贝
复制单位 字符 (直到遇到 \0) 字节
结束条件 遇到源字符串的 \0 复制了指定的 n 个字节
安全性 不安全,易发生缓冲区溢出 相对安全,只要 n 不越界
参数 dest, src (两个指针) dest, src, n (两个指针 + 一个大小)
数据类型 只能处理 char 数组 可以处理任何数据类型 (int, float, struct 等)
性能 可能比 memcpy 稍慢 (需要检查 \0) 通常非常快,编译器优化得好
使用场景 确定源和目标都是字符串,且对性能要求不高,或者代码可读性优先时 处理非字符串数据、处理固定大小的数据块、追求高性能和安全性时

安全性警告与最佳实践

在现代 C/C++ 编程中,直接使用 strcpy强烈不推荐的,即使是 memcpy,也需要程序员自己确保 n 的正确性。

更安全的现代替代方案:

  1. 对于 C++ (推荐 std::stringstd::vector)

    • 字符串:使用 std::string,它会自动管理内存,从根本上杜绝了缓冲区溢出的风险。
      #include <string>
      std::string src = "Hello, C++";
      std::string dest = src; // 安全、简单
  2. 对于 C (使用安全函数)

    • 字符串:使用 strncpy,并记得手动添加 \0,或者更安全的 snprintf
      char dest[10];
      snprintf(dest, sizeof(dest), "%s", src); // 更安全,会自动截断
    • 内存块:使用 memcpy,但要确保 n 正确,或者使用 C11 标准引入的 memcpy_s (在支持的环境下)。
      // memcpy_s 是 C11 的安全版本,在 Windows (MSVC) 上支持较好
      // errno_t memcpy_s(void *dest, rsize_t dest_size, const void *src, rsize_t n);
  • strcpy:一个历史悠久但危险的函数,在新的代码中应尽量避免使用。
  • memcpy:一个强大、高效且相对安全的通用内存拷贝函数,当你需要复制一块固定大小的内存时,它是首选。
  • 核心思想strcpy 是“按逻辑拷贝”(遇到 \0 停止),而 memcpy 是“按字节拷贝”(拷贝你指定的字节数),理解这个区别是掌握它们的关键。
-- 展开阅读全文 --
头像
dede吊用jquery为何需身份验证?
« 上一篇 昨天
织梦为何不生成index.html?
下一篇 » 昨天

相关文章

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