欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > C/C++安全函数替代指南

C/C++安全函数替代指南

2025/9/24 23:01:26 来源:https://blog.csdn.net/weixin_45821611/article/details/148609124  浏览:    关键词:C/C++安全函数替代指南

在 C/C++ 编程中,很多传统的字符串和内存操作函数(如 strcpysprintfgets 等)存在缓冲区溢出等安全隐患,容易导致程序崩溃或安全漏洞。为此,C 标准库和编译器厂商提供了一些安全版本的函数,用于替代这些不安全函数。

下面是一些常见的安全函数及其参数说明,以及它们与原始函数的区别。

一、字符串操作函数

原始函数安全函数参数说明说明
strcpy(dest, src)strncpy(dest, src, n)

dest: 目标缓冲区

src: 源字符串

n: 最多复制的字符数

限制复制长度,防止溢出。但不会自动添加 \0,需手动补零。
strcat(dest, src)strncat(dest, src, n)

dest: 目标缓冲区

src: 源字符串

n: 最多追加的字符数

限制追加长度,防止溢出。自动添加 \0
gets(dest)fgets(dest, size, stdin)

dest: 目标缓冲区

size: 缓冲区大小

stdin: 输入流

限制读取长度,防止溢出。推荐使用 fgets 替代 gets
sprintf(dest, format, ...)snprintf(dest, size, format, ...)

dest: 目标缓冲区

size: 缓冲区大小

format: 格式字符串

...: 参数列表

限制写入长度,防止溢出。返回实际需要的字符数(不包括 \0)。
vsprintf(dest, format, args)vsnprintf(dest, size, format, args)同上,支持 va_list 参数与 snprintf 类似,支持可变参数。

二、内存操作函数

原始函数安全函数参数说明说明
memcpy(dest, src, n)memcpy_s(dest, destSize, src, n)(C11)

dest: 目标缓冲区

destSize: 目标缓冲区大小

src: 源地址

n: 要复制的字节数

防止缓冲区溢出,返回错误码。仅部分编译器支持(如 MSVC)。
memmove(dest, src, n)memmove_s(dest, destSize, src, n)(C11)同上支持重叠内存区域的安全复制。

三、文件操作函数

原始函数安全函数参数说明说明
fopen(filename, mode)fopen_s(fp, filename, mode)(MSVC)

fp: 指向 FILE* 的指针filename: 文件名

mode: 打开模式

避免空指针访问,返回错误码。MSVC 特有。
freopen(filename, mode, stream)freopen_s(fp, filename, mode, stream)(MSVC)类似 fopen_s替换现有流的目标文件。

四、输入/输出函数

原始函数安全函数参数说明说明
scanf(format, ...)scanf_s(format, ...)(MSVC)

format: 格式字符串

...: 参数列表

限制字符串长度,防止缓冲区溢出。MSVC 特有。
sscanf(str, format, ...)sscanf_s(str, format, ...)(MSVC)同上字符串输入的安全版本。
vscanf(format, args)vscanf_s(format, args)(MSVC)同上支持可变参数的安全版本。

五、Windows 平台安全函数(MSVC)

原始函数安全函数参数说明说明
strcpystrcpy_s(dest, size, src)

dest: 目标缓冲区

size: 缓冲区大小

src: 源字符串

检查缓冲区大小,返回错误码。
strcatstrcat_s(dest, size, src)

dest: 目标缓冲区

size: 缓冲区大小

src: 源字符串

防止溢出,自动检查长度。
scanfscanf_s("%s", str, (unsigned)_countof(str))与 scanf 类似,但需指定缓冲区大小防止缓冲区溢出。
sprintfsprintf_s(dest, size, format, ...)

dest: 目标缓冲区

size: 缓冲区大小

format: 格式字符串

限制写入长度,防止溢出。

六、C++ 标准库推荐方式(避免使用 C 风格)

在 C++ 中,推荐使用标准库中的安全类和函数代替 C 风格字符串操作:

替代方式说明
std::string使用 std::string 替代 char[],避免手动管理缓冲区。
std::array<char, N>固定大小数组,避免动态分配。
std::vector<char>动态数组,适用于不确定大小的缓冲区。
std::getline(std::cin, str)替代 fgets,用于安全地读取一行文本。
std::stringstream替代 sprintf/sscanf,避免格式化错误。

七、使用建议

  1. 优先使用标准库:如 std::stringstd::vector,避免手动管理缓冲区。
  2. 启用编译器警告:如 -Wdeprecated-declarations-Wformat-security 等,帮助发现不安全函数。
  3. 使用安全函数:如 strncpysnprintffgets 等,替代 strcpysprintfgets
  4. 注意边界检查:即使使用安全函数,也应确保传入的缓冲区大小正确。
  5. 使用静态分析工具:如 Clang-Tidy、Coverity、Valgrind 等,帮助发现潜在问题。

八、示例代码对比

1. strncpy 替代 strcpy

不安全写法:
char dest[10];
strcpy(dest, "This is a long string");  // 潜在缓冲区溢出
  • 问题strcpy 不检查目标缓冲区大小,若源字符串长度超过 dest 容量(10 字节),会导致缓冲区溢出,覆盖栈上其他数据(如返回地址),可能引发程序崩溃或安全漏洞。
安全写法:
char dest[10];
strncpy(dest, "This is a long string", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';  // 手动补空字符
  • 参数说明
    • dest: 目标缓冲区。
    • "This is a long string": 源字符串。
    • sizeof(dest) - 1: 最多复制的字符数(保留 \0 空间)。
  • 原因
    • strncpy 的第三个参数限制复制长度,防止溢出。
    • 注意strncpy 不会自动添加 \0,需手动补零以确保字符串完整性。

2. strncat 替代 strcat

不安全写法:
char dest[10] = "Hello";
strcat(dest, " World!");  // 潜在缓冲区溢出
  • 问题strcat 不检查目标缓冲区容量,若拼接后的字符串长度超过 dest 容量(10 字节),会导致缓冲区溢出。
安全写法:
char dest[10] = "Hello";
strncat(dest, " World!", sizeof(dest) - strlen(dest) - 1);
  • 参数说明
    • dest: 目标缓冲区。
    • " World!": 源字符串。
    • sizeof(dest) - strlen(dest) - 1: 最多追加的字符数(保留 \0 空间)。
  • 原因
    • strncat 的第三个参数限制追加长度,防止溢出。
    • 自动添加 \0,确保字符串完整性。

3. memcpy_s 替代 memcpy

不安全写法:
char src[100] = "Hello World";
char dest[50];
memcpy(dest, src, strlen(src) + 1);  // 潜在缓冲区溢出
  • 问题memcpy 不检查目标缓冲区大小,若复制长度超过 dest 容量(50 字节),会导致缓冲区溢出。
安全写法:
#include <string.h>  // C11 标准char src[100] = "Hello World";
char dest[50];
errno_t err = memcpy_s(dest, sizeof(dest), src, strlen(src) + 1);
if (err != 0) {// 处理错误(如缓冲区不足)
}
  • 参数说明
    • dest: 目标缓冲区。
    • sizeof(dest): 目标缓冲区大小。
    • src: 源地址。
    • strlen(src) + 1: 要复制的字节数。
  • 原因
    • memcpy_s 的第二个参数 destSize 显式指定目标缓冲区大小。
    • 若复制长度超过目标容量,函数返回错误码而非直接溢出。
    • 注意memcpy_s 是 C11 标准的一部分,部分编译器(如 MSVC)支持,GCC/Clang 可能需要启用 _FORTIFY_SOURCE

4. memmove_s 替代 memmove

不安全写法:
char src[100] = "Hello World";
char dest[50];
memmove(dest, src, strlen(src) + 1);  // 潜在缓冲区溢出
  • 问题memmove 不检查目标缓冲区大小,若复制长度超过 dest 容量(50 字节),会导致缓冲区溢出。
安全写法:
#include <string.h>  // C11 标准char src[100] = "Hello World";
char dest[50];
errno_t err = memmove_s(dest, sizeof(dest), src, strlen(src) + 1);
if (err != 0) {// 处理错误
}
  • 参数说明:与 memcpy_s 相同。
  • 原因
    • memmove_s 支持重叠内存区域的复制,防止缓冲区溢出。
    • 返回错误码而非直接溢出。

5. fopen_s 替代 fopen

不安全写法:
FILE* fp = fopen("data.txt", "r");
if (fp == NULL) {// 错误处理
}
  • 问题fopen 不检查 fp 是否为 NULL,且在多线程环境下可能引发竞争条件(TOCTOU 攻击)。
安全写法:
FILE* fp;
errno_t err = fopen_s(&fp, "data.txt", "r");
if (err != 0 || fp == NULL) {// 处理错误
}
  • 参数说明
    • &fp: 指向 FILE* 的指针。
    • "data.txt": 文件名。
    • "r": 打开模式。
  • 原因
    • fopen_s 的第一个参数是 FILE**,直接传递指针地址,避免空指针访问。
    • 返回错误码而非依赖 errno,提高可读性和安全性。
    • 注意fopen_s 是 Microsoft 特有扩展,GCC/Clang 推荐使用 fopen 加强错误检查。

6. freopen_s 替代 freopen

不安全写法:
FILE* fp = freopen("data.txt", "w", stdout);
if (fp == NULL) {// 错误处理
}
  • 问题freopen 不检查 fp 是否为 NULL,且在多线程环境下可能引发竞争条件。
安全写法:
FILE* fp;
errno_t err = freopen_s(&fp, "data.txt", "w", stdout);
if (err != 0 || fp == NULL) {// 处理错误
}
  • 参数说明:与 fopen_s 类似。
  • 原因
    • freopen_s 替换现有流的目标文件,增强错误处理。

7. scanf_s 替代 scanf

不安全写法:
char name[10];
scanf("%s", name);  // 潜在缓冲区溢出
  • 问题scanf 不限制输入长度,若用户输入超过 name 容量(10 字节),会导致缓冲区溢出。
安全写法:
char name[10];
scanf_s("%s", name, (unsigned)_countof(name));  // 限制最大输入长度
  • 参数说明
    • name: 目标缓冲区。
    • (unsigned)_countof(name): 缓冲区大小。
  • 原因
    • scanf_s 的第三个参数指定缓冲区大小,防止溢出。
    • 注意scanf_s 是 Microsoft 特有扩展,GCC/Clang 推荐使用 fgets

8. fgets 替代 gets

不安全写法:
char buffer[10];
gets(buffer);  // 无边界检查,绝对禁止使用!
  • 问题gets 不检查输入长度,用户输入任意长度都可能溢出缓冲区。
安全写法:
char buffer[10];
fgets(buffer, sizeof(buffer), stdin);  // 限制最大读取长度
buffer[strcspn(buffer, "\n")] = '\0';  // 去除换行符
  • 参数说明
    • buffer: 目标缓冲区。
    • sizeof(buffer): 缓冲区大小。
    • stdin: 输入流。
  • 原因
    • fgets 的第二个参数指定最大读取字节数,防止溢出。
    • 保留换行符需手动去除(通过 strcspn 查找 \n 位置)。

9. snprintf 替代 sprintf

不安全写法:
char buffer[10];
sprintf(buffer, "%s", "This is a long string");  // 缓冲区溢出
  • 问题sprintf 不限制输出长度,若格式化结果超过缓冲区容量(10 字节),会导致溢出。
安全写法:
char buffer[10];
snprintf(buffer, sizeof(buffer), "%s", "This is a long string");
  • 参数说明
    • buffer: 目标缓冲区。
    • sizeof(buffer): 缓冲区大小。
    • "%s": 格式字符串。
    • "This is a long string": 参数列表。
  • 原因
    • snprintf 的第二个参数指定缓冲区大小,限制写入长度。
    • 自动添加 \0,确保字符串完整性。
    • 返回值为实际需要的字符数(不包括 \0),可用于判断是否被截断。

10. vsnprintf 替代 vsprintf

不安全写法:
char buffer[10];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);  // 潜在缓冲区溢出
va_end(args);
  • 问题vsprintf 不限制输出长度,若格式化结果超过缓冲区容量(10 字节),会导致溢出。
安全写法:
char buffer[10];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);  // 限制写入长度
va_end(args);
  • 参数说明:与 snprintf 类似,支持 va_list 参数。
  • 原因
    • vsnprintf 的第二个参数指定缓冲区大小,防止溢出。

11. std::string 替代 char[]

不安全写法(C 风格):
char buffer[100];
strcpy(buffer, "Hello");  // 手动管理缓冲区
安全写法(C++):
#include <string>std::string str = "Hello";  // 自动管理内存
str += " World";            // 动态扩展
  • 原因
    • std::string 自动处理内存分配和释放,避免缓冲区溢出。
    • 支持动态扩展,无需手动计算长度。
    • 优势:结合 std::vectorstd::array 等容器,进一步提升安全性

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词