scanf_s 比起 scanf 添加了什么?

有什么变化呢?
关注者
79
被浏览
80,114

4 个回答

scanf_s 和 scanf 几乎是一样的,但是 scanf_s 在字符串的读入上有不同,允许在参数中指定读入字符串的长度上限,以避免读入的内容长度超过已有的内存空间的长度。

比如,如果我们这样写:

char word[10];
scanf("%s",word);

然后运行的时候输入了一个这样的字符串:

tooyoungtoosimplesometimesnaive

那么这么长的字符串就会全部填在 word 数组里,超过它的长度,这样会出偏差的。

为了避免出现这样的偏差,scanf_s 允许你指定读入串的长度上限,于是我们改成下面这样:

scanf_s("%s",word,(rsize_t)sizeof word);

这样输入过长时就会产生错误,执行你之前在 set_constraint_handler_s 中定义的错误处理函数(如果你没有定义的话,系统会给你准备一个,也许是直接忽略,也许是直接停止运行),你的程序就不会在这里搞出什么偏差。

而且,如果你传入的参数有空指针的话,也会产生错误,执行你定义的错误处理函数。

也就是说,使用 scanf_s 比使用 scanf 要更安全。

不过,并不是所有的标准库都实现了 scanf_s,你可以通过检测 __STDC_LIB_EXT1__ 是否被 #define 了,检测标准库是否支持,然后在代码最前面加上下面这一行:

#define __STDC_WANT_LIB_EXT1__ 1

来开启标准库对于 scanf_s 以及其他一些函数的更安全的版本(比如 strcpy_s memset_s 之类的)的支持。

这是 C11 新增的内容。

如有错误,请指出。

scanf_s 几乎没有比 scanf 更加安全,除了空指针的检测。而且 scanf 本身也能做边界检查的。

char buffer[128];
scanf("%127s", buffer); // 安全
scanf_s("%127s", buffer, 128); // 安全
scanf("%s", buffer); // 不安全
scanf_s("%s", buffer, 128); // 安全,当输入过长时能调用报错函数

说实话,若最后一条的 128 被写成过大的数也不安全。

个人看来 _s 系列最大的功能就是调用报错函数,可能可以简化一些报错的流程。

set_constraint_handler_s, constraint_handler_t

有点怀疑 _s 系列的用法是——

别人传来一个莫名其妙的指针/长度/输入内容,然后你直接就把它传到 _s 系列函数里去,不过之前要写对边界。

接下来等着一个统一的报错流程就行了。

另外 qsort_s 和 bsearch_s 有个 context 参数,疑是让人把 C++ 的函数对象传进去的…

就像这样:

extern "C" 
int c_style_cmpr(const void *lhs, const void *rhs, void *p_functor){
    functor_t &functor=(*static_cast<functor_t*>(p_functor));
    const data_t &x=(*static_cast<const data_t*>(lhs)),
                 &y=(*static_cast<const data_t*>(rhs));
    return functor(y, x) - functor(x, y);
}
// 上面的东西或许会在 C 中被译成同义内容
// ...
qsort_s(array, num_sort, sizeof(data_t), c_style_cmpr, &functor);

……纯 C 的话不知有没有这种习惯,C++ 里这么做怎么看都很诡异就是。

为什么?