1

我正在用 C 编写一个超级简单的基于命令行的程序。这只是一个小测试,代码非常简单。所以它的意思是向用户询问他们的姓名、数学成绩、英语成绩、计算成绩。然后它会计算出他们的平均成绩,并告诉他们输入的名字。是的,我知道这是一个非常简单的程序,但我仍然做错了什么。

问题是,我的代码的一部分将首先运行,告诉用户输入他们的姓名,然后一旦他们这样做并按 enter,我的其余代码将立即运行,然后停止工作。很奇怪,我只是不明白出了什么问题。

#include <stdio.h>

int main(int argc, const char * argv[])
{
    char chr;
    char firstname;
    int mathsmark, englishmark, computingmark, averagemark;

    printf("What is your name?\n");
    scanf("%c", &firstname);
    printf("\n");

    printf("What is your maths mark?\n");
    scanf("%d", &mathsmark);
    printf("\n");

    printf("What is your english mark?\n");
    scanf("%d", &englishmark);
    printf("\n");

    printf("What is your computing mark?\n");
    scanf("%d", &computingmark);
    printf("\n");

    printf("Your name is: %c", firstname);
    printf("\n");

    averagemark = (mathsmark + englishmark + computingmark) / 3;
    printf("%d", averagemark);
    printf("\n");

    chr = '\0';

    while (chr != '\n') {

        chr = getchar ();
    }

    return 0;
}
4

4 回答 4

5

一个主要问题是您已声明firstname为单个字符长,当您尝试从控制台读取名称时,您使用的是%c转换说明符,它从输入流中读取下一个单个字符并将其存储到firstname. 名称的其余部分留在输入流中以混淆剩余的scanf调用。

例如,如果您键入“Jacob”作为名字,那么第一个scanf调用将分配Jfirstname,留"acob\n"在输入流中。

下一次scanf调用尝试转换"acob\n"为整数值并将其保存到mathsmark,但失败("acob\n"不是有效的整数字符串)。scanf接下来的两个电话 也会发生同样的事情。

最后一个循环

while (chr != '\n')
{
   chr = getchar();
}

finally 消耗剩下的"acob\n",其中包含换行符(因为您在输入名称后按 Enter ),导致循环和程序退出。

你如何解决这个问题?

首先,您需要声明firstname为一个数组char

char firstname[SOME_SIZE] = {0};

whereSOME_SIZE大到足以处理您的所有案件。您需要将scanf呼叫更改为

scanf("%s", firstname);

这告诉scanf从输入流中读取字符直到下一个空白字符,并将结果存储到firstname数组中。注意这里不需要使用&操作符;在大多数情况下,数组类型的表达式将被转换(“衰减”)为指针类型的表达式,表达式的值将是数组中第一个元素的地址。

请注意,这scanf不是很安全,也不是很健壮。如果您输入的字符多于缓冲区的大小以容纳,scanf则会很乐意将这些额外的字符存储到数组后面的内存中,这可能会破坏一些重要的东西。您可以通过在转换说明符中使用显式字段宽度来防止这种情况,例如

scanf(*%29s", firstname);

但总的来说这是一种痛苦。

scanf也不是很擅长检测不良输入。如果您输入“12er”作为您的标记之一,scanf将转换并分配"12",将 留"er"在流中以破坏下一次读取。

scanf返回成功分配的数量,因此防止错误输入的一种方法是检查返回值,如下所示:

if (scanf("%d", &mathmarks) != 1)
{
  printf("Bad input detected for math marks\n");
}

不幸的是,scanf不会从流中删除坏字符;你必须自己使用getchar或类似的方法。

于 2012-10-09T15:53:56.677 回答
1

这是新 C/C++ 开发人员的常见错误。该scanf函数检测到您ENTER/RETURN按下键以表示输入结束,但它也会\n在输入字符串的末尾捕获字符,因此您基本上会RETURNS检测到两个字符。

请在此处阅读使用fgets示例sscanf

http://www.linuxforums.org/forum/programming-scripting/67560-problem-scanf.html


它将很快为您解决这个问题。与此同时,我强烈建议你看看这本书:

http://www.amazon.com/Primer-Plus-5th-Stephen-Prata/dp/0672326965


这是北美高中和大学最常用的C编程书籍,有大量示例供您使用通过,包括您在上面演示的这个特定程序。印刷版比电子书有更多的例子,所以我会为印刷版支付 30.00 美元。

祝你好运!

于 2012-10-09T15:48:06.493 回答
1

您可能想看一些教程。也许一个关于格式说明符,一个关于C 中的字符串

scanf()stdin从格式说明符指定的读取数据并存储它们。在这种情况下:

char firstname;    
scanf("%c", &firstname); 

读取 1 个字符stdin并将其存储到firstname

>> What is your first name?
Mike

现在firstname == 'M'因为scanf()按照我们的要求读取 1 个字符。

你想要做的是读取一个字符串(一堆字符):

char firstname[5];        // an array of characters
scanf("%s", firstname);  // store as a string
firstname[4] = '\0';      // Truncate the result with a NULL to insure no overflow

>> What is your first name?
Mike

现在firstname是 [M][i][k][e][\0] 因为scanf()按照我们的要求读取了 1 个字符串。注意同样适用于printf(), a printfwith a%c会给你一个字符,而 as a printf()with a%s会给你所有字符,直到 NULL 终止符。

于 2012-10-09T15:54:06.450 回答
1

你有(至少)两个选择。

char firstname[number_big_enough_to_hold_long_name];
/*or */

char *firstname = malloc(sizeof(char) * number_big_enough_to_hold_long_name);
/* ... code ... */
free(firstname);

此外,最好限制读取的宽度。scanf()不知道 的大小(可用空间)firstname

scanf("%number_big_enough_to_hold_long_names", ...
/* i.e. */

char firstname[32];
if(scanf("%31s", firstname) == EOF) {
     perror("bad");
     return 1;
}

此外,在尝试下一次阅读之前,您应该检查是否还有任何东西。即,如果有人输入“我的名字”,那么只有“我的”最终会出现,firstname而“名字”将留在输入流中。

getchar()返回一个int非字符。

于 2012-10-09T16:01:46.963 回答