gets()不适合我

时间:2011-07-14 11:25:01

标签: c

我的代码很快出现问题。请查看函数enter()。当我请求名称,并使用gets()时,它只是跳过它,好像什么都没有,并跳转到请求年龄。我做错了什么?

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

#define RECORDS 30
#define LEN 20

/*Questions
Formatting display() - can we use spaces to format?
Is the patient structure supposed to be global or local in enter()?
Why doesn't printf("name: "); scanf("%s", name); require &name?
Make sure you account for first and last names, use getline or something
*/

void enter();

struct patient
{
    char name[LEN];
    int age;
    double highBP, lowBP, riskFactor;
};

struct patient * db[RECORDS];
int counter = 0;

main()
{
    int flag = 1;

    while (flag == 1)
    {       
        printf("---------------------------------------------------------\n");
        printf("|\t(N)ew record\t(D)isplay db\t(U)pdate record |\n");
        printf("|\t(L)oad disk\t(W)rite disk\t(E)mpty disk    |\n");
        printf("|\t(S)ort db\t(C)lear db\t(Q)uit          |\n");
        printf("---------------------------------------------------------\n");
        printf("choose one: ");

        char selection, selectionstr[3];
        scanf("%s",selectionstr);
        selection = selectionstr[0];        

        //printf("selection %c\n", selection);

        if ((selection == 'n') || (selection == 'N'))
        {
            //New record
            enter();
        }

            // Options omitted...

        else
        {
            printf("not a vaild input\n");  
        }
    }
}

void enter()
{
    if (counter < 30)
    {
        FILE *fptr;
        fptr = fopen("db.txt", "a");

        char name[LEN];
        int age;
        double highBP, lowBP;
        printf("name: "); //scanf("%s", name);
        gets(name);
        printf("age: "); scanf("%d", &age);
        printf("high bp: "); scanf("%lf", &highBP);
        printf("low bp: "); scanf("%lf", &lowBP);
        struct patient temp;
        strcpy(temp.name, name);
        temp.age = age;
        temp.highBP = highBP;
        temp.lowBP = lowBP;

        if ((highBP < 120) && (lowBP < 80))
            temp.riskFactor = 0;

        if (((highBP <= 120) && ((lowBP > 80) && (lowBP <= 90))) || ((highBP <= 80) && ((lowBP > 120) && (lowBP <= 130))))
            temp.riskFactor = 1;

        if (((highBP <= 120) && ((lowBP > 90) && (lowBP <= 100))) || ((highBP <= 80) && ((lowBP > 130) && (lowBP <= 140))))
            temp.riskFactor = 4;

        else
            temp.riskFactor = 5;

        if ((temp.riskFactor > 0) && (temp.age > 50))
            temp.riskFactor += 0.5;

        db[counter] = &temp;
        //fprintf(fptr, "%s, %d, %f, %f, %f\n", db[counter]->name, db[counter]->age, db[counter]->highBP, db[counter]->lowBP, db[counter]->riskFactor);
        printf("%s, %d, %f, %f, %f\n", db[counter]->name, db[counter]->age, db[counter]->highBP, db[counter]->lowBP, db[counter]->riskFactor);
        counter++;

        fclose(fptr);
    }

    else
        printf("database full");


    /*db[counter] = (struct patient *) malloc(sizeof(struct patient));


    printf("name: "); scanf("%s", (*db[counter]).name);
    printf("age: "); scanf("%d", (*db[counter]).age);
    printf("high bp: "); scanf("%d", (*db[counter]).highBP);
    printf("low bp: "); scanf("%d", (*db[counter]).lowBP);


    //db[counter] = &temp;
    printf("%d", sizeof(db[counter]));
    //printf("%s", (db[counter])->name);
    printf("%s, %d, %f, %f\n", db[counter]->name, db[counter]->age, db[counter]->highBP, db[counter]->lowBP);
    counter++;*/
}

4 个答案:

答案 0 :(得分:5)

你这样做:

scanf("%s",selectionstr); /* Leaves '\n' in the input buffer. */

然后你执行gets

gets(name); /* Encounters the leftover '\n'. */

问题是scanf不消耗换行符,换行符保留在输入缓冲区中。当gets出现时,它会遇到换行符并立即得到满足。

这个问题经常出现。它也是C FAQ。并且解释得很好here

有些事情需要考虑:

  • 有人会建议“冲洗stdin”。 This is a bad idea.
  • 有人会提出类似

    的内容
    while((c = getchar()) != '\n' && c != EOF);
    

    这也很麻烦

您最好的选择不是混淆scanfgets

作为旁注,您确实意识到您应该使用fgets代替gets,对吗?

答案 1 :(得分:4)

主要问题

永远不要使用gets()。甚至没有玩具程序。进入是一个坏习惯。它最终无法安全使用,因为您无法判断输入何时溢出您提供的缓冲区。

请改用fgets(),并记住删除它留下的尾随换行符(gets()删除的换行符。)

您的问题是以前使用scanf()的输入未读取换行符,因此gets()会读取第一个换行符 - 为您提供一个空字符串。你也会在下一次迭代中遇到同样的问题,因为你用scanf()来读血压。

辅助问题

您不检查输入是否成功;始终检查scanf()的返回值,以确保获得预期的输入。您将收到错误,因为该名称不能解释为数字。

你应养成编写用原型声明函数的代码的习惯;符号:

void enter();

说“有一个函数enter()不返回任何值并且接受一个不确定的参数列表 - 任何一组参数都可以,但它不是一个变量参数列表函数”。相比之下:

void enter(void);

说“有一个函数enter()没有返回值,也没有参数”。

此外,您应该将main()的返回类型明确定义为int。自C标准在所有函数定义上都需要显式返回类型以来,已有十多年了。

初期问题

一旦您解决了数据扫描问题,您就会发现没有为您的记录正确分配空间。你的数组是:

struct patient *db[RECORDS];

在循环中,你有:

struct patient temp;

db[counter] = &temp;

麻烦是双重的。您正在为每个患者记录重复使用相同的空间,并且当enter()退出时,您存储的指针超出范围。最简单的解决方法是将全局变量更改为:

struct patient db[RECORDS];

然后,您可以取消temp变量,只需在db中初始化相应的条目即可。或者,您可以保留temp但是执行结构赋值而不是指针赋值:

db[counter] = temp;

答案 2 :(得分:1)

问题是在scanf调用之后,输入缓冲区中仍然存在“\ n”(或系统上的任何换行符),并且当您调用gets()时,它将获取换行符并返回。您需要做的是清除输入缓冲区。像这样,例如:

int c;
while ( ( c = getchar() ) != EOF && c != '\n' )
  ;

答案 3 :(得分:0)

过去是什么:

gets(str);

现在替换为:

int c;
while ( (c = getchar()) != EOF && c != '\n' )
    ;
fgets(str, sizeof(str), stdin);
str[strlen(str) - 1] = '\0';

我讨厌这个,但这似乎是做我需要的最小代码。这甚至假设用户点击进入&#39;在输入结束时。它不健壮!