【问题标题】:Very strange error regarding gets(string)关于获取(字符串)的非常奇怪的错误
【发布时间】:2013-12-26 19:53:31
【问题描述】:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
   int a;
   char c[1];
   printf("\n%d\n", a);
   gets(c);
   printf("\n%d\n", a);
   return 0;
}

当使用gets 读取c 时,之前打印的a 的值被打印为具有以下值:0;当使用scanf("%c", &amp;c); 代替gets(c); 时,a 的值在整个代码中保持不变。

我似乎无法弄清楚为什么会这样,有人可以解释一下这是怎么可能的吗?

【问题讨论】:

  • gets 本身就很奇怪。
  • '(使用 C,而不是 C++)' 那么为什么要标记 c++?重新标记...
  • 打印未初始化的值是不好的。
  • when using scanf("%c",&amp;c); as a replacement for gets(c); the value of a stays the same throughout the code.。 a 和 c 有什么关系?
  • 为什么有这么多反对票?当然,这是一个初学者的错误,但问题是具体的,措辞清晰,包含一个(诚然缩进严重的)示例案例,并且 OP 显然至少尝试修复它(他谈到了当他使用 scanf() 时会发生什么)。向我 +1 以抵消不当的反对票。

标签: c string gets string.h


【解决方案1】:

您可能遇到堆栈损坏。 gets() 将输入字符写入您传入的内存地址(指向c 的第一个元素的指针)。但是,您只分配了 1 个字符,这对于一个空字符串(包括空终止符)来说已经足够了。

这就是为什么永远不应该使用gets()。它不安全,因为它可以覆盖其目标数组的末尾。请改用更安全的函数,例如fgets()。

【讨论】:

  • 哦,明白了,我想,tnx :)。
【解决方案2】:

您在字符串a 中只有\0 的空间,而gets() 不在乎没有足够的空间。它显然覆盖了导致您的问题的部分内存。代替gets(),使用fgets()。

char c[100];
if (fgets(c, sizeof c, stdin) == NULL) Handle_EOForIOError();
size_t len = strlen(c);
if (len && c[len-1] == '\n') c[--len] = '\0';

C 标准 (2011) 已从其规范中删除 gets()。


OP:...当使用 scanf("%c",&amp;c); 代替 gets(c); 时,a 的值在整个代码中保持不变。

这很好因为scanf("%c",&amp;c); 不应该影响a。

如果您希望通过使用gets(c); 来覆盖较小的c 而覆盖a,您可以尝试char c[1]; int a;,但任何此类代码都会调用未定义的行为。

【讨论】:

    【解决方案3】:

    您的程序正在调用未定义的行为
    1. 除非您没有传递空字符串 (\n)。
    2. a 未初始化。

    在这种情况下,任何事情都可能发生。您可能会得到预期或意外的结果、分段错误甚至程序崩溃。

    【讨论】:

    • 肯定不是 2º 的:D.
    • 是的。当然。但我不明白你的意思是什么:)
    【解决方案4】:

    只是为了解决这个问题,永远不要永远不要使用gets:它会在您的程序中引入一个故障点。它在 C99 标准中已被弃用,并已从 C2011 标准中删除。这是邪恶的。

    说了这么多,让我们来看看代码中的所有问题。

    请记住,在 C 中,字符串 是一个以 0 值字节结尾的字符序列。这意味着要存储 N 个字符的字符串,您必须留出 N+1 个char 存储元素。您的 c 数组的大小可以容纳 1 个元素,这意味着它可以存储的唯一 string 是空字符串。

    gets 的问题是它不知道目标缓冲区有多大;当您将数组作为参数传递时,所有被调用函数接收到的都是指向第一个元素的指针。如果您输入 10 个非空白字符,或 100 或 1000 个,gets 会很乐意将多余的字符存储到数组后面的内存中;这就是您的 a 变量被覆盖的原因。这也是为什么 gets 最终从语言标准中删除的原因;这种行为导致了大量的恶意软件利用。

    您的scanf 调用没有 导致问题的原因是因为您使用了%c 转换说明符,它只从输入流中读取单个字符。如果您使用了%s 转换说明符,您会看到与使用gets 调用相同的结果。

    因此,您需要做两件事:首先,您需要声明 c 足够大,以容纳您期望的最大字符串加上 1 作为 0 终止符:

    #define MAX_SIZE 10 // or however large the string needs to be
    ...
    char c[MAX_SIZE+1];
    

    那么你需要使用fgets来读取输入:

    if ( fgets( c, sizeof c, stdin ) != NULL )
    {
      // work with c
    }
    

    【讨论】:

    • 1.同意gets()的邪恶。 2. 不知道为什么你在“如果你输入 10 个非空白字符”中提到 non-whitespace,因为gets() 读取的是非\n 字符。 3. 注意:gets() 会丢弃结尾的\n 而fgets() 不会。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多