发布于 ,更新于 

C11 中具有安全边界检查的函数

前言

我记得最早加入安全边界检查函数的是微软,在一些涉及缓冲区的基本 C 函数中,一律加上了 _s 后缀的、具有安全边界检查功能的函数。有少部分与之前微软定义的不兼容,这里以 C11 标准为准。

C11 标准引入了这些函数,它们比之前习惯性使用的传统函数更加安全,可以防止出现缓冲区溢出问题错误的发生。但是,能不能做到真正安全,还是需要编码者更细心地正确去编码。

不可理喻的是,C11 标准居然把这些定义的标准设置为可选。既然是标准,就应该是必须实现的才对嘛。这个让使用者情何以堪,到底我们使用它还是不用它 ??

大写的唉

如何确定编译器是否支持安全边界检查函数

如果编译器的标准库实现了安全边界检查函数,就会定义 __STDC_LIB_EXT1__ 的宏。

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main()
{
#ifdef __STDC_LIB_EXT1__
printf("已实现");
#else
printf("未实现");
#endif // ifdef __STDC_LIB_EXT1__
}

如何开启使用安全边界检查函数

好奇葩的一点,如果你想使用安全边界检查函数,还要相应的在 #include 头文件之前定义一个宏 __STDC_WANT_LIB_EXT1__ 的值为 1,才能开启使用这个头文件中的安全边界检查函数。

举例,string.h 里面定义了一些安全边界检查函数,如果要使用它,就需要按照如下的写法:

1
2
3
#define __STDC_WANT_LIB_EXT1__  1      //把这个宏定义为 1 才开启安全边界检查函数
//#define __STDC_WANT_LIB_EXT1__ 0 //把这个宏定义为 0 会禁用安全边界检查函数
#include <string.h>

strlen_s

strlen_s 是 strlen 的安全版本,用于返回字符串的长度。原型:

1
size_t strnlen_s(const char *str, size_t strsz);

第一个参数 str 为字符串指针,第二个参数为缓冲区大小,如果字符串超出缓冲区大小还没有以 \0 结尾则返回缓冲区大小。

strcpy_s

strcpy_s 是 strcpy 的安全版本,用于复制字符串。原型:

1
errno_t strcpy_s(char *restrict dest, rsize_t destsz, const char *restrict src);

第二个参数指出目标缓冲区 dest 的大小。复制成功,返回 0;失败则返回非 0 。

strncpy_s

strncpy_s 是 strncpy 的安全版本,用于复制字符串。原型:

1
2
errno_t strncpy_s(char *restrict dest, rsize_t destsz,
const char *restrict src, rsize_t count);

第二个参数指出目标缓冲区 dest 的大小。第四个参数指出最多从 src 复制多少个字符。如果前 count 个字符中没有 \0,会继续把 \0 添加的目标字符串后面。复制成功,返回 0;失败则返回非 0 。

strcat_s

strcat_s 是 strcat 的安全版本,用于追加复制字符串。原型:

1
errno_t strcat_s(char *restrict dest, rsize_t destsz, const char *restrict src);

第二个参数指出目标缓冲区 dest 的大小。

strncat_s

strncat_s 是 strncat 的安全版本,用于追加复制字符串。原型:

1
2
errno_t strncat_s(char *restrict dest, rsize_t destsz,
const char *restrict src, rsize_t count);

第二个参数指出目标缓冲区 dest 的大小。第四个参数指出最多从 src 追加复制多少个字符。如果前 count 个字符中没有 \0,会继续把 \0 添加的目标字符串后面。复制成功,返回 0;失败则返回非 0 。

gets_s

从stdin流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在 str 指针所指向的字符数组中。原型:

1
char *gets_s( char *str, rsize_t n );

第二个参数指出目标缓冲区 str 的大小。

strtok_s

将字符串进行单元化,分割转换为一系列 token。(这个函数跟微软的不一样)

1
2
char *strtok_s(char *restrict str, rsize_t *restrict strmax,
const char *restrict delim, char **restrict ptr);
  • str : 需要被分割的字符串。执行第一次分割之后,对同一个字符串执行后续的分割时,这个参数是 NULL。
  • strmax : 指向一个 rsize_t 的整数,这个整数表示需要分割的字符串的长度。每次调用 strtok_s 之后,函数会更新这个整数值,表示分割后剩余的字符数。
  • delim : 包含所有可能分界的字符的字符串。
  • ptr : 指向 char* 类型的变量的指针。函数在该变量中存储信息,允许在找到第一个 token 之后继续搜索标记。

返回值:
返回分割的 token,如果没有,返回 NULL。