【问题标题】:why gets() is not working?为什么gets()不起作用?
【发布时间】:2010-06-04 12:19:18
【问题描述】:

我在 Unix 中用 C 语言编程, 我正在使用gets 从键盘读取输入。 我总是收到此警告并且程序停止运行:

warning: this program uses gets(), which is unsafe.

谁能告诉我为什么会这样?

【问题讨论】:

  • 这是一个不安全的函数;如果程序使用的函数无法检查缓冲区大小,则可能会发出缓冲区溢出攻击
  • 如果我使用 scanf() 而不是 gets() 它可以工作,但我必须从键盘读取 2 个输入,并且在我读取第一个后程序停止。为什么?这是代码:字符用户;字符通行证; printf("Iser ID"); scanf("%s",user); printf("密码:"); scanf("%s",pass);
  • @peiska:程序在第一个之后停止,因为它正在等待您进入第二个。你自己把第二个scanf放在那里。为什么程序“停止”然后让您感到惊讶?
  • userpass 需要声明为字符的数组,而不是单个字符。 "%s" 转换说明符期望相应的参数具有 char * 类型,并且该参数需要指向一个足够长的缓冲区以容纳输入字符串加上 0 终止符(未修饰的 "%s" 转换说明符暴露了在这方面与gets() 相同的安全漏洞,但这里没有足够的空间详细介绍)。将 userpass 的声明更改为 char user[N]; char pass[M] 其中 N 和 M 是两个数组所需的大小。

标签: c


【解决方案1】:

gets 是不安全的,因为你给它一个缓冲区,但你没有告诉它缓冲区有多大。输入可能会超出缓冲区的末尾,从而相当壮观地炸毁您的程序。使用fgets 会更好一些,因为你告诉它缓冲区有多大,就像这样:

const int bufsize = 4096; /* Or a #define or whatever */
char buffer[bufsize];

fgets(buffer, bufsize, stdin);

...所以只要你给它正确的信息,它就不会写到缓冲区的末尾并把事情搞砸。

有点OT,但是:

您不必使用 const int 来表示缓冲区大小,但我强烈建议您不要只在两个地方都输入文字数字,因为您不可避免地会改变一个,但不是另一个。编译器可以提供帮助:

char buffer[4096];
fgets(buffer, (sizeof buffer / sizeof buffer[0]), stdin);

该表达式在编译时解析,而不是运行时解析。打字很痛苦,所以我曾经在我通常的标题集中使用宏:

#define ARRAYCOUNT(a) (sizeof a / sizeof a[0])

...但是我的纯 C 已经过时了几年,现在可能有更好的方法。

【讨论】:

  • 如果你处理的是char的数组,则不需要将sizeof buffer[0]除以定义为sizeof (char) == 1,即函数调用可以简化为fgets(buffer, sizeof buffer, stdin)跨度>
  • @Christoph:您是否确定 char 在所有平台上都是一个字节,和/或如果它(比如说)是两个字节,那么 buf 大小参数到 fgets 将被应用为 chars 而不是 bytes ?我见过的大多数文档都说过字节。我在 C 游戏中已经有一段时间了,我知道这些东西变得更加复杂,所以我在代码中有点防御性。但是,是的,@OP,if 那些假设是安全的,那么您可以使用 char 数组省略计算(但与 intlong 等类似的成语应用除外.).
  • @T.J. Crowder:C 标准定义 char 为 1 个字节。
  • @T.J. Crowder:参见 C99 第 3.7.1 节,其中指出 C 字符是单字节的,而 6.5.3.4 §3 明确提到 sizeof (char) 始终为 1
  • 它是 C。不是 C++,所以使用 #define 或 enum。
【解决方案2】:

如前面的答案中所述,使用fgets 而不是gets

但这并不是说gets 根本不起作用,它只是非常非常不安全。我的猜测是您的代码中存在一个错误,该错误也会与 fgets 一起出现,因此请发布您的源代码。

编辑 根据您在评论中提供的更新信息,我有一些建议。

  • 我建议用你的母语搜索一个好的 C 教程,谷歌是你的朋友。作为一本书,我会推荐The C Programming Language

  • 如果您有新信息,最好将它们编辑到您的原始帖子中,特别是如果它是代码,这将使人们更容易理解您的意思。

  • 您试图将一个字符串(基本上是一个字符数组)读入单个字符,这当然会失败。您想要做的事情类似于以下内容。

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

    请随意评论/编辑,即使是基本的 C 语言我也很生疏。

编辑 2 正如 jamesdlin 警告的那样,使用 scanfgets 一样危险。

【讨论】:

  • 您的scanf 使用情况与gets 一样糟糕。 (也避免使用scanfc-faq.com/stdio/scanfprobs.html
  • @jamesdlin 感谢您的提醒,我不知道缓冲区溢出问题,尽管我之前遇到过 scanf 的其他问题。
【解决方案3】:

man gets 说:

永远不要使用gets()。因为它是 不知道是不可能的 数据提前多少个字符 gets() 将读取,并且因为 gets() 将继续存储 超出缓冲区末尾的字符, 使用起来非常危险。它 已经被用来破解电脑 安全。请改用 fgets()。

【讨论】:

    【解决方案4】:

    gets() 是不安全的。它有一个参数,一个指向 char 缓冲区的指针。问问自己,你必须使缓冲区有多大,以及用户在不按返回键的情况下可以输入多长时间。

    基本上,没有办法用gets()来防止缓冲区溢出——使用fgets()。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-07
      • 2019-08-06
      相关资源
      最近更新 更多