strcpy函数如何安全使用?

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

strcpy() 是什么?

strcpy() 是 C 标准库 <string.h> 中的一个函数,它的全称是 string copy(字符串复制),它的核心功能是将源字符串(source string)的内容完整地复制到目标字符串(destination string)中,并在目标字符串的末尾自动添加一个空字符 '\0'

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

函数原型

在使用 strcpy() 之前,你需要在代码文件的开头包含头文件 <string.h>

#include <string.h> // 必须包含的头文件
char *strcpy(char *dest, const char *src);

参数详解:

  1. dest (destination - 目标):

    • 这是一个指向字符数组的指针,也就是目标字符串的起始地址。
    • 必须有足够的空间来容纳源字符串的内容,否则会导致缓冲区溢出,这是非常严重的安全漏洞。
    • 它的类型是 char *,并且没有被 const 修饰,意味着函数可以修改这个指针指向的内容。
  2. src (source - 源):

    • 这是一个指向常量字符数组的指针,也就是源字符串的起始地址。
    • 函数会从这个地址开始读取字符,直到遇到空字符 '\0' 为止。
    • 它的类型是 const char *,意味着函数承诺不会修改这个指针指向的内容。

返回值:

  • strcpy() 函数会返回目标字符串 dest 的起始地址(也就是 dest 指针的值)。
  • 这个返回值的主要用途是允许函数调用被链接在一起(链式调用),strcpy(strcpy(dest1, src), src2)

工作原理

strcpy() 的工作流程非常简单:

c语言strcpy(
(图片来源网络,侵删)
  1. src 指向的地址开始读取字符。
  2. 将读取到的字符依次写入到 dest 指向的地址中。
  3. 每写入一个字符,srcdest 指针都会自动向后移动一个位置。
  4. 当从 src 读取到空字符 '\0' 时,复制过程停止。
  5. 将这个 '\0' 也写入到 dest 的当前位置。

图示:

初始状态:
      dest:  [  ][  ][  ][  ][  ]  (未初始化的内存)
      src:   H  e  l  l  o  \0
复制过程:
步骤1: dest[0] = 'H'
      dest:  [H][  ][  ][  ][  ]
      src:   e  l  l  o  \0
步骤2: dest[1] = 'e'
      dest:  [H][e][  ][  ][  ]
      src:   l  l  o  \0
... (继续)
步骤5: dest[4] = 'o'
      dest:  [H][e][l][l][o]
      src:   \0
步骤6: dest[5] = '\0' (复制结束)
      dest:  [H][e][l][l][o][\0]
      src:   (已到达末尾)

代码示例

示例 1:基本用法

#include <stdio.h>
#include <string.h>
int main() {
    char src[] = "Hello, World!"; // 源字符串
    char dest[20];                // 目标字符串,确保空间足够
    // 将 src 的内容复制到 dest 中
    strcpy(dest, src);
    printf("源字符串 src: %s\n", src);
    printf("目标字符串 dest: %s\n", dest);
    return 0;
}

输出:

源字符串 src: Hello, World!
目标字符串 dest: Hello, World!

重要的注意事项(⚠️ 警告)

这是 strcpy() 最关键的部分,也是初学者最容易犯错的地方。

缓冲区溢出

strcpy() 不会检查目标缓冲区 dest 的大小,如果源字符串 src 的长度(包括 '\0')超过了目标缓冲区 dest 的容量,多出来的字符就会溢出,覆盖掉 dest 之后内存空间的数据,导致程序崩溃、数据损坏或严重的安全漏洞。

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

错误示例:

#include <stdio.h>
#include <string.h>
int main() {
    char src[] = "This is a very long string that will cause a buffer overflow!";
    char dest[10]; // 目标缓冲区太小了!
    // 危险!这行代码会引发缓冲区溢出
    strcpy(dest, src);
    printf("目标字符串 dest: %s\n", dest); // 输出是不可预测的,程序可能已经崩溃
    return 0;
}

后果:

  • 程序可能直接崩溃(段错误)。
  • 程序可能继续运行,但 dest 之后的数据(如其他局部变量)被破坏。
  • 攻击者可以利用此漏洞执行任意代码。

目标缓冲区必须可写

dest 必须是一个字符数组(分配在栈上或堆上),或者是一个指向动态分配内存的指针。不能是一个字符串字面量,因为字符串字面量通常存储在只读的内存区域(如程序的代码段)。

错误示例:

#include <stdio.h>
#include <string.h>
int main() {
    // "Hello" 是一个字符串字面量,存储在只读内存区
    char *dest = "Hello"; 
    char src[] = "World";
    // 错误!尝试修改只读内存,会导致程序崩溃(段错误)
    strcpy(dest, src); 
    printf("dest: %s\n", dest);
    return 0;
}

正确做法:

int main() {
    // 使用字符数组,它存储在可写的栈内存中
    char dest[] = "Hello"; 
    char src[] = "World";
    strcpy(dest, src); // 正确
    printf("dest: %s\n", dest); // 输出: World
    return 0;
}

目标缓冲区必须足够大

即使 dest 是一个字符数组,也必须确保其大小足够容纳 src 的内容。

错误示例:

int main() {
    char src[] = "12345";
    char dest[4]; // 大小是4,只能存3个字符 + '\0'
    strcpy(dest, src); // 危险!需要6个字节的空间('1','2','3','4','5','\0')
    return 0;
}

更安全的替代方案

正是因为 strcpy() 存在缓冲区溢出的风险,C 标准库提供了更安全的版本。

strncpy()

strncpy() 允许你指定最大复制的字符数,从而防止溢出。

char *strncpy(char *dest, const char *src, size_t n);
  • n: 是要复制的最大字符数。
  • 优点:可以防止缓冲区溢出。
  • 缺点
    1. src 的长度小于 ndest 的剩余部分不会被自动用 '\0' 填充,可能会包含垃圾数据。
    2. src 的长度大于或等于 ndest 的字符串将不会以 '\0',这会导致后续使用字符串的函数(如 printf("%s", dest))越界读取,同样是危险的。

strncpy() 的正确用法(手动添加 '\0'):

#include <stdio.h>
#include <string.h>
int main() {
    char src[] = "Hello";
    char dest[10];
    // 最多复制 9 个字符,为 '\0' 留出空间
    strncpy(dest, src, 9); 
    // 重要!src 非常长,strncpy 不会添加 '\0',所以必须手动添加
    dest[9] = '\0'; 
    printf("dest: %s\n", dest);
    return 0;
}

snprintf() (推荐)

这是现代 C 编程中最推荐使用的安全函数之一。

int snprintf(char *str, size_t size, const char *format, ...);
  • str: 目标缓冲区。
  • size: 目标缓冲区的大小(包括为 '\0' 预留的空间)。
  • format: 格式字符串,后面跟着要写入的变量。
  • 优点
    1. 绝对安全:它会确保写入的字符数(包括 '\0')永远不会超过 size
    2. 功能更强大,不仅可以复制字符串,还可以格式化输出。

snprintf() 示例:

#include <stdio.h>
int main() {
    char src[] = "Hello, snprintf!";
    char dest[10];
    // 安全地将 src 复制到 dest,最多写入 9 个字符
    // snprintf 会自动在末尾添加 '\0'
    snprintf(dest, sizeof(dest), "%s", src);
    printf("dest: %s\n", dest); // 输出: Hello, sn
    return 0;
}

在这个例子中,sizeof(dest) 是 10。snprintf 最多会写入 9 个字符(从 src 复制过来的字符),然后第 10 个位置会自动放上 '\0',完全避免了溢出风险。


函数 原型 安全性 特点
strcpy() char *strcpy(char *dest, const char *src); 不安全 简单易用,但极易导致缓冲区溢出。
strncpy() char *strncpy(char *dest, const char *src, size_t n); 相对安全 可限制长度,但需手动处理 '\0',否则仍有风险。
snprintf() int snprintf(char *str, size_t size, ...); 非常安全 强烈推荐,自动处理长度和 '\0',功能强大。

核心建议:

  1. 永远不要在新的代码中使用 strcpy(),除非你 100% 确定源字符串的长度绝对安全,并且你完全清楚其风险。
  2. 优先使用 snprintf(),它是现代 C 语言中最安全、最灵活的字符串复制/格式化函数。
  3. 如果必须使用 strncpy(),请务必记得手动在目标字符串的末尾添加 '\0'
-- 展开阅读全文 --
头像
dede用户名密码忘了怎么找回?
« 上一篇 昨天
织梦模板如何仿家庭在线网?
下一篇 » 昨天
取消
微信二维码
支付宝二维码