【问题标题】:why should I use scanf_s here instead of scanf?为什么我应该在这里使用 scanf_s 而不是 scanf?
【发布时间】:2021-11-28 12:38:19
【问题描述】:

交换值的程序:

#include <stdio.h>
#include <conio.h>

void swap(int *a, int *b);

void main()
{
    int a, b;
    printf("Enter two numbers: ");
    scanf_s("%d%d", &a, &b);

    printf("Before swap\n");
    printf("a = %d, b =  %d\n", a, b);
    swap(&a, &b);
    printf("After swap\n");
    printf("a = %d, b = %d", a, b);
}

void swap(int *a, int *b)
{
    printf("Enter the swapped numbers: \n");
    scanf("%d%d", a, b);
}

为什么我应该在这里使用scanf_s 而不是scanf 为什么会这样?

感谢您的帮助。

【问题讨论】:

  • 您可以放心地忽略该警告。 scanf 有很多问题,但总的来说,scanf_s 也好不到哪里去。
  • 你不应该使用scanf_s()Field Experience With Annex K — Bounds Checking Interfaces:“微软为了提高 API 的采用率而弃用标准函数而引发的一个普遍谬误是,对标准函数的每次调用都必然是不安全的,应该用对“更安全”的 API 的调用来代替。 .. Microsoft Visual Studio 实现了 API 的早期版本。但是,实现不完整,既不符合 C11 也不符合原始 TR 24731-1。..."
  • (cont) "...由于与规范有大量偏差,Microsoft 的实现不能被认为是符合规范的或可移植的。"

标签: c syntax


【解决方案1】:

使用scanf,您可以扫描字符串:

char buffer[256];
scanf("%s", buffer);

但如果输入字符串大于目标缓冲区,它可能会导致缓冲区溢出:如strcpy,它将复制直到在输入中找到'\0'。所以你的编译器告诉你使用scanf_s 来指定缓冲区的大小,这样它就不会写过去:

char buffer[256];
scanf_s("%s", buffer, 256);

但在您的情况下,您只是输入单个整数,无论​​如何您都可以使用scanfscanf_s 不是标准函数,因此如果您的代码必须在 Visual Studio 之外编译,请务必小心。

更多:Difference between scanf and scanf_s

【讨论】:

  • scanf( "%255s", buffer ); 完成。 scanf_s() 不会提高安全性和安全性,但会使您的代码不可移植。
  • 我会记得打印一份 scanf 手册页的纸质副本。 ;)
  • Microsoft 版本指定scanf_s("%s", buffer, 256U),而C 标准版本假定scanf_s("%s", buffer, sizeof buffer)。 size 参数一侧的类型为UINT,另一侧的类型为size_t,它们在 32 位目标上是相同的,但在 64 位目标上具有不同的大小。防止缓冲区溢出的简单解决方案一直是scanf("%255s", buffer),这就是警告应该说的。很遗憾*scanfprintf的意义不同,但历史无法改写。
【解决方案2】:

在发布的代码中,没有理由在swap 函数中使用scanfscanf_s

scanf_s 是 Microsoft 引入的一个函数,作为 scanf 的所谓 更安全(1) 的替代品,这常常被毫无戒心的程序员粗心地使用,尤其是对于%s%[%c 转换,导致安全漏洞。

scanf_s 被标准化为具有微妙不同语义的可选扩展。因此使用scanf_s 的程序是不可移植的。

Microsoft 修改了他们的 C 工具链以促使程序员使用 scanf_s,每当遇到 scanf 引用时都会发出警告或错误。这就是屏​​幕截图中显示消息的原因。

在您的程序中,scanf_sscanf 的行为方式基本相同,因为转换 %d%d 对这两个函数具有相同的语义,但 scanf_s 会检测并处理 %d 的空指针参数(导致程序退出)而scanf 只会有未定义的行为(导致程序退出并出现分段错误)。

swap 函数可以这样写:

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

还要注意main 应该有一个int 返回类型。


(1)Andrew Henley 评论说这个主题非常敏感:scanf_s 是 Microsoft 作为 供应商特定的、非-portable, not-any-safer 替代scanf,为您的代码库带来vendor-lock-in。他们内部EEE 策略的图示。

【讨论】:

  • "scanf_s 是 Microsoft 引入的一个函数,作为scanf供应商特定、不可移植、不安全的替代品在您的代码库的供应商锁定中FTFY;-)
  • 我认为交换只是一个例子,如果不是故意的,你不会创建一个要求用户自己交换值的交换函数。 x)
  • @AndrewHenle:同意。答案已更新:)
猜你喜欢
  • 2016-05-05
  • 1970-01-01
  • 2016-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 2014-03-12
  • 2012-12-13
相关资源
最近更新 更多