试图学习C,但我遇到了奇怪的错误(至少对我来说)

时间:2016-01-19 21:22:17

标签: c char scanf

我试图自己学习C,我有一些Matlab和Python的经验。我试图创建一个简单的猜谜游戏,你给出一个数字,计算机猜测一个数字,你必须告诉它是否更高或更低。我已经编写了一些代码,但它不起作用(例如,尝试输入30)。代码很丑陋,但我只是想了解发生了什么。

我尝试使用case语句编写它,但结果相同。

另外,当您用yy回答y / n问题时会发生什么?这些数字突然上升了吗?

#include "stdio.h"
#include "stdlib.h"

int main(void) {
    int a;
    int i = 1;
    int b = 50;
    int temp;
    int last = 0;
    char ans = ' ';
    printf("Enter value for computer to guess (0-100): ");
    scanf("%d", &a);

    while (a - b) {
        printf("Is the number larger than %i (y/n): ", b);
        scanf("%s", &ans);
        if (ans == 'y') {
            temp = b;
            b = b + ((b + last) / 2);
            last = temp;
        } else {
            temp = b;
            b = b - ((b + last) / 2);
            last = temp;
        }
        i++;
    }
    printf("Your number was: %i. Number of guesses was: %i \n", b, i);
    return 0;
}

3 个答案:

答案 0 :(得分:3)

在您的代码中

 scanf("%s",&ans);

在您超出分配的内存时调用undefined behavior

要使用%s格式说明符,您需要一个数组作为参数(指向数组的第一个元素的指针,具体而言)。

但是,在您的情况下,更改

 scanf("%s",&ans);

scanf(" %c",&ans);  // note the space

可能会解决这个问题。 (可选)要处理 extra 输入(如yyy),您可以在读取每个输入后考虑清除输入缓冲区,例如

while (getchar() != '\n');

或同样。

答案 1 :(得分:0)

您可能希望计算机二进制搜索答案并有效地到达目的地。您需要跟踪您正在搜索的上边界和下边界。试试这样的事情;

int main(void){
    int a;
    int i = 1;
    int b = 50;
    int lower = 0;
    int upper = 100;
    char ans[256] = {0};
    printf("Enter value for computer to guess (0-100): ");
    scanf("%d", &a);

    while (a-b) {
      printf("Is the number larger than %i (y/n): ",b);
      scanf("%s",ans);
      if (ans[0] == 'y') {
        lower = b;
        b = b + ((upper-lower)/2);
        printf("Last: %d - %d\n",lower, upper);
      }
      else {
        upper = b;
        b = b - ((upper-lower)/2);
        printf("Last: %d - %d\n",lower, upper);
      }
      i++;
    }
    printf("Your number was: %i. Number of guesses was: %i \n",b,i);
    return 0;
}

答案 2 :(得分:0)

这就是问题所在。

char ans = ' ';
...
scanf("%s",&ans);

ans只有一个字节的内存,但您已向scanf询问了整个字符串。所以它尽职尽责地将整个字符串填充到ans的单个字节中。由于这包括空字节,即使输入yn也会写入一个额外的字节。 yy写入三个字节。

这会导致ans 静默溢出其内存,破坏相邻变量。这就是yy混淆事物的原因,你将3个字节(2个字符加上一个空字节)放入1个溢出2个字节的字节中。它会使用last字符y覆盖121。您可以通过一些调试printf来看到这一点。

    printf("b = %d, last = %d\n", b, last);
    printf("Is the number larger than %i (y/n): ",b);
    scanf("%s",&ans);
    printf("b = %d, last = %d\n", b, last);

Enter value for computer to guess (0-100): 30
b = 50, last = 0
Is the number larger than 50 (y/n): yy
b = 50, last = 121

相反,您希望使用%c来读取单个字符。但这有其自身的问题,scanf("%d", &a)在STDIN上留下换行符,scanf("%c", &ans)将首先读取该换行符。

这就是为什么scanf是一个问题,应该避免,它可以在流中留下意外的输入。这里有很多关于SO推荐scanf的答案。而是使用fgetsgetline来读取整行,然后使用sscanf来读取字符串。我更喜欢getline因为它处理为你分配行内存。

这是一个如何做的草图。

char *line = NULL;
size_t linelen = 0;

printf("Enter value for computer to guess (0-100): ");
getline(&line, &linelen, stdin);
sscanf(line, "%d", &a);
printf("You entered %d\n", a);

printf("Is the number larger than %i (y/n): ",b);
getline(&line, &linelen, stdin);
sscanf(line, "%c", &ans);

switch(ans) {
    case 'y':
        ...
        break;
    case 'n':
        ...
        break;
    default:
        printf("I don't understand '%c', try again.\n", ans);
}

free(line);