【问题标题】:Whats wrong with my SIMPLE C program?我的 SIMPLE C 程序有什么问题?
【发布时间】:2012-09-29 23:23:23
【问题描述】:

我正在用 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;
}

【问题讨论】:

  • 如果我们所有的名字都是一个字母,那就太棒了。
  • 停下手头的工作,找一本关于 C 编程的好书。了解字符串和字符数组的工作原理。
  • 但是 %c 仅仅意味着字符。我没有将名称定义为一个字符长吗?
  • @Supertecnoboff, char firstname; = 一个字符。
  • @chris 但“char firstname”只是定义一个字符并将其称为“firstname”...??

标签: c char int printf


【解决方案1】:

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

例如,如果您键入“Jacob”作为名字,则第一个 scanf 调用会将 J 分配给 firstname,而在输入流中留下 "acob\n"

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

最后一个循环

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

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

你如何解决这个问题?

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

char firstname[SOME_SIZE] = {0};

SOME_SIZE 足够大,可以处理所有案件。您需要将scanf 调用更改为

scanf("%s", firstname);

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

请注意,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 或类似方法来执行此操作。

【讨论】:

    【解决方案2】:

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

    请在此处阅读使用fgetssscanf 的示例:

    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 美元。

    祝你好运!

    【讨论】:

      【解决方案3】:

      您可能想查看一些教程。也许一个在Format specifiers 和一个在strings in C

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

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

      stdin读取1个字符并将其存储到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() 也是如此,带有 %cprintf 将为您提供一个字符,而带有 %sprintf() 将为您提供所有字符,直到 NULL 终止符。

      【讨论】:

      • scanf %s 上没有和号 - 或使用 &firstname[0](这有点吵)
      【解决方案4】:

      你有(至少)两个选择。

      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;
      }
      

      在尝试下一次阅读之前,您还应该检查是否还有任何内容。 IE。如果有人输入“我的名字”,那么只有“我的”会以firstname 结尾,而“名字”将留在输入流中。

      getchar() 返回一个int 而不是一个字符。

      • getchar
      • scanf

      • 并搜索“ansi c char arrays tutorial”或类似内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多