【问题标题】:C - scanf has gone ROGUEC - scanf 已经流氓了
【发布时间】:2014-12-18 06:20:21
【问题描述】:

我有这个 c 程序,我在其中输入一个数字 N,然后再输入 N 个数字。例如,我将输入 100,然后再输入 100 个数字。出于某种原因,经过这么多输入后,scanf 函数将停止正常工作。就好像它已经停止接受输入,只会继续输入 size 中的任何值。

我想出的用例是 100 1 2 3 4 5 6 7 8 9 10...(重复十次)。然后在三四次之后,我将输入 100 10 9 8 7 6 5 4 3 2 1...(重复十次),然后就会出现打印语句的无限循环。

int main(int argc, const char * argv[]) {
  int histogram[10000];
  int i;
  while (1) {
    int *rectPtr = histogram;
    int size;
    scanf("%d", &size);
    if (!size) return 0;
    for (i = 0; i < size; ++i) {
        scanf("%d", rectPtr);
        rectPtr++;
    }
    printf("%d", 1);
    printf("\n");
  }
  return 0;
}

【问题讨论】:

  • 看这里的诀窍。 if (!size) return 0;。当它再次询问元素数量时,您是否尝试在输入循环元素后放置一次0?请注意,它在while(1) 循环中。
  • 你没有测试来自scanf()的返回值,所以你不知道它是否有效。这对printf() 语句很奇怪;为什么不写printf("%d\n", 1); 甚至puts("1");
  • @JonathanLeffler 你是什么意思?当然我知道它不起作用,因为我已经调试过了。双 printf 是不必要的。
  • 澄清一下,在这里,您正在检查 inputsuppossed 将被 scanf() 扫描,但您错过了检查 @987654331 scanf() 本身的 @ 值。
  • 您的代码没有测试或捕获来自scanf()的返回值,所以您不知道scanf()是否报告了问题。作为一般规则,测试输入函数的返回值以确保您认为发生的事情确实发生了。您还可以在读取后打印出读取的值:if (scanf("%d", rectPtr) != 1) { fprintf(stderr, "scanf() failed\n"); return 1; } printf("--&gt; %d\n", *rectPtr); rectPtr++;。同样输入size。也可以考虑if (size &lt;= 0) return 0;。并且使用fgets() 加上sscanf() 可以更轻松地报告错误。

标签: c scanf


【解决方案1】:

不相信无限循环。

在一系列的cmets中,我说:

您没有测试来自scanf() 的返回值,因此您不知道它是否有效。这对printf() 语句很奇怪;为什么不写printf("%d\n", 1); 甚至puts("1");

您的代码没有测试或捕获来自scanf() 的返回值,因此您不知道scanf() 是否报告了问题。作为一般规则,测试输入函数的返回值以确保您认为发生的事情确实发生了。您还可以在读取后打印出读取的值:

if (scanf("%d", rectPtr) != 1)
{
    fprintf(stderr, "scanf() failed\n");
    return 1;
}
printf("--> %d\n", *rectPtr);
rectPtr++;

在输入尺寸时也是如此。也可以考虑if (size &lt;= 0) return 0;。并且使用fgets() 加上`sscanf() 可以使报告错误更容易。

j.will 评论:

很高兴知道 scanf 是否失败,但我想知道它失败的原因并防止它失败。我该怎么做?

我回复了:

我知道你想知道。使用scanf(),在失败后您能做的最好的事情通常是读取跟在换行符或EOF 后面的所有字符,如果您想知道出了什么问题,那么您也可以打印这些字符,因为scanf()保留它在输入缓冲区中读取的最后一个字符,为下一次输入操作做好准备。

void gobble(void)
{
    printf("Error at: <<");
    int c;
    while ((c = getchar()) != EOF && c != '\n')
        putchar(c);
    puts(">>");
    if (c == EOF)
        puts("<<EOF>>");
}

输出中的第一个字符是导致失败的原因。

另见How to use sscanf() in loops?


破解你的代码来匹配这个:

#include <stdio.h>

static void gobble(void)
{
    printf("Error at: <<");
    int c;
    while ((c = getchar()) != EOF && c != '\n')
        putchar(c);
    puts(">>");
    if (c == EOF)
        puts("<<EOF>>");
}

int main(void)
{
    enum { MAX_VALUES = 10000 };
    int histogram[MAX_VALUES];
    int size;
    while (printf("Number of items: ") > 0 && scanf("%d", &size) == 1 &&
           size > 0 && size <= MAX_VALUES)
    {
        int *rectPtr = histogram;
        for (int i = 0; i < size; ++i)
        {
            if (scanf("%d", rectPtr) != 1)
            {
                gobble();
                return 1;
            }
            rectPtr++;
        }
        printf("size %d items read\n", size);
    }
    return 0;
}

【讨论】:

  • 我对 C 失去了信心。这么简单的东西怎么会失败?
  • 它本身不是 C ——或者可能不是。我不知道你是如何输入数据的,但我怀疑你在窗口系统的终端窗口中使用了 copy'n'paste,我暂时将责任归咎于 copy'n'paste 机制炸毁而不是程序中的 C 代码失控了。您可以尝试将数据放入一个文件中,然后使用./your-program &lt; data-file 运行它,如果数据文件本身是干净的,我会感到惊讶。
【解决方案2】:

IMO,您需要检查scanf() 的返回值才能正常运行。请检查以下代码。我添加了一些修改。

要退出程序,您需要按 CTRL+ D 将生成EOF。或者,在输入一些无效输入时[如char 而不是int] 也会导致程序从while() llop 中跳出并终止。

我已将序列先检查scanf()。所有其他的也需要检查。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char * argv[]) {
        int histogram[10000] = {0};
        int i;

        int *rectPtr = histogram;
        int size = 0;
        int retval = 0;


        printf("Enter the number of elements \n");
        while (  (retval = scanf("%d", &size)) != EOF && (retval == 1)) {
                rectPtr = histogram;
                if (!size) return 0;
                printf("Enter %d elements\n", size);
                for (i = 0; i < size; ++i) {
                        scanf("%d", rectPtr);   //check in a simmilar way to above
                        rectPtr++;
                }
                printf("%d\n", 1111111);

                printf("Enter the number of elements: \n");
        }
        return 0;
}

样本运行的输出

[sourav@broadsword temp]$ ./a.out 
Enter the number of elements: 2
Enter 2 elements
1
2
1111111
Enter the number of elements: 3
Enter 3 elements
1
2
3
1111111
Enter the number of elements: 9
Enter 9 elements
0
9
8
7
6
5
4
3
2
1111111
Enter the number of elements: r   
[sourav@broadsword temp]$

【讨论】:

  • while(1) 仍然是一个无限循环,您必须摆脱它。必须有一个目的将它们包含在代码中并温和地分解它,这是我在这里看不到的东西
  • @Gopi 抱歉,您错过了if (retval == EOF) return 0; 声明吗?
  • 输入a(或任何其他字母)并观察代码循环。
  • @JonathanLeffler 啊。我懂了。将更新代码以修复该部分。
  • 你确实有。您可以简化while ( (retval = scanf("%d", &amp;size)) != EOF &amp;&amp; (retval == 1)) {,使其读取为while ((retval = scanf("%d", &amp;size)) == 1) {,这实际上是scanf() 转换了您期望它转换的所有值的惯用测试(此处为1,但您可能一直要求2、3 或更多值,N 值的测试是== N)。如果retval 是 EOF 或 0,则两个循环条件都会终止循环,但在您的版本中有一个针对正常情况的额外测试(一切正常)。
【解决方案3】:

histogram 被声明为大小为 10000。你说你这样做 100 1 2 3 ...重复 10 次。如果我正确理解在 histogram 中使用 1000 个插槽。

如果您重复测试超过 10 次,您会耗尽直方图并开始写入超出数组末尾的内容,从而导致未定义的行为。

所以你必须:

  • 在每次迭代时重置recPtr = histogram
  • 在读取大小后控制recPtr - histogram + size &lt;= sizeof(histogram)(恕我直言更好)

正如其他人所说,您应该始终控制输入操作:任何事情都可能发生在您的程序之外......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 2023-02-16
    • 1970-01-01
    • 2012-03-17
    • 2020-02-18
    • 2015-09-25
    • 2016-01-29
    相关资源
    最近更新 更多