函数调用do while循环只执行一次。

时间:2015-10-19 02:58:16

标签: c

我有一个检查用户输入的程序,并确保它只是整数而不是字符。在我的main函数中,do while循环仅在输入不正确时执行一次。但我想让它继续执行,直到用户输入有效的输入。我的 doAgain()功能是询问用户是否想再次尝试。使用 doAgain()功能问题。如果将其留在if语句中,它只执行一次。除了这个故障,一切正常。但是,当我删除它时,循环继续执行,直到用户输入我想要的有效输入,但是然后 doAgain()功能将无用

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

/ *获得边界* /

 char* getBoundary(char str[]){
    int i;
    char c;

     str = (char*) malloc(sizeof(char));

    for (i = 0; (c = getchar()) != '\n'; i++) // The loop stop running after the second time
    {
        str = (char *) realloc(str, sizeof(char) + i);
        str[i] = c;
    }

    str[i] = '\0';
    return str;
}

/ *检查有效字符串* /

 int checkStr(const char *check)

{
    unsigned i;
    size_t len = strlen(check);

    for (i = 0; i < len; i++)
        if(isalpha(check[i]))
        {
            printf("Invalid integer formatt!!!");
            return 0;
        }
    return 1;
}

/ *询问是否再次* /

int doAgain(void)
{
    char ans, c;

    do {
        printf("Do you want to try again?: ");
        scanf(" %c", &ans);

        switch (ans) 
        {   
            case 'y':
            case 'Y':
            case 'n':
            case 'N':
                return (ans == 'y') || (ans == 'Y') ? 1 : 0;
                break;
            default:
                printf("Invalid answer!!! answer 'y' and 'Y' or 'n' and 'N' only\n");
                do { /* flush input stream */
                    c = getchar();
                }while (c != '\n'); 
        }   
    }while (1);
}

/ * Main * /

int main(void)
{

    char *l_boundRow;
    l_boundRow = NULL;


    do {
        printf("Enter lower bound row: ");
        l_boundRow  = getBoundary(l_boundRow);

        if (!checkStr(l_boundRow) && doAgain())  // problem start here, it works if I remove doAgain() function
            continue; // if the string is invalid, the program asks user if they want to try again
        else
            break;
    }while (1);

    free(l_boundRow);
    return 0;
}

1 个答案:

答案 0 :(得分:2)

修订答案

当前问题是当doAgain()退出yn时,它不会在这些字符后读取换行符,因此当它重新进入时{{1}它读取的第一个字符是getBoundary()y之后的任何字符,它可能是一个新行,它终止了输入行。您需要在有效输入和无效输入上吞噬剩余的行。

这段代码大部分都有效 - 它也是无泄漏的(至少在我的临时测试中)。

n

如果您选择在输入无效后再试一次,则最后一个无效值将打印为“最终绑定行”#39;。您可以轻松破解代码以避免此问题。

顺便说一下,当我第一次编译你的代码时,我在默认的严格选项下只得到3个警告 - 因为我需要原型(非静态)函数定义。这非常好;做得好。很少有人会编写那些在SO上发布的代码,这些代码通过了那么多的投诉来进行审查。

如果是我的代码,我会有很少的#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> char* getBoundary(void); int checkStr(const char *check); int doAgain(void); /* get boundary */ char* getBoundary(void) { int i; int c; char *str = (char*) malloc(sizeof(char)); for (i = 0; (c = getchar()) != '\n' && c != EOF; i++) { str = (char *) realloc(str, 2 + i); str[i] = c; } str[i] = '\0'; return str; } /* check for valid string */ int checkStr(const char *check) { unsigned i; size_t len = strlen(check); for (i = 0; i < len; i++) { if (!isdigit(check[i])) { printf("Invalid integer format (%s)!!!\n", check); return 0; } } return 1; } static int gobble(void) { int c; while ((c = getchar()) != EOF && c != '\n') ; return c; } /* Ask if do again */ int doAgain(void) { char ans; int c; do { printf("Do you want to try again?: "); scanf(" %c", &ans); switch (ans) { case 'y': case 'Y': c = gobble(); return 1; case 'n': case 'N': c = gobble(); return 0; default: { printf("Invalid answer!!! answer 'y' and 'Y' or 'n' and 'N' only\n"); c = gobble(); if (c == EOF) { printf("EOF detected\n"); return 0; } } } } while (1); } /* Main */ int main(void) { char *l_boundRow; l_boundRow = NULL; do { printf("Enter lower bound row: "); l_boundRow = getBoundary(); if (checkStr(l_boundRow)) break; if (!doAgain()) break; free(l_boundRow); }while (1); printf("Final bound row: %s\n", l_boundRow); free(l_boundRow); return 0; } 循环(此代码中没有)。它们偶尔会有用,但偶尔也是有效的术语。通常,最好使用顶级测试do … while循环或显式while循环。

原始答案

一个真正的问题,但不是那个立即造成麻烦的问题。

for的代码中,首先分配一个字符。然后,在循环体中,重新分配getBoundary()个字符。在第一次迭代中,您重新分配1个字节;那么当你退出循环时,你会在分配的最后一个字符之外写一个,这会导致未定义的行为。您需要使用i + 1作为重新分配的大小。 (有些人会因使用i + 2而阻止您使用sizeof(char),因此保证1

这可能是你麻烦的根源;写入超出分配缓冲区的末尾很容易导致崩溃。

如果你在valgrind下运行代码,它会告诉你这个错误。

另外,每次在循环周围再分配一个字节不是一个好主意。最好分配20个字节(足够大以容纳任何64位整数值),或者在需要更多空间时将每次迭代的大小加倍。在这种情况下,它不是时间关键,但它可能成为更大程序中的一个问题。

另请注意,您的checkstr()功能仅检测字母字符;标点符号和控制字符也不会转换为整数。您应该检查每个字符是否为数字(isdigit(check[i])),并且您可能不得不担心明确char被签名 - 因此isdigit((unsigned char)check[i])更好。类似的评论适用于其他isuvwxyz()函数。

doAgain()中,您应该使用int c;代替char c;,并且应该检查EOF和换行符。如果您检测到EOF,答案是“不”。你应该归还。

此外,再次在getBoundary()功能中,您有:

str = (char *) realloc(str, sizeof(char) + i);

有些人会谴责你为演员;我不是这样做的心态。但请注意,你会因为很多人在SO上回答关于C的问题而受到批评。

更重要的是,您不应该以这种方式编写realloc()代码。成语:

ptr = realloc(ptr, new_size);
如果分配失败,

会泄漏内存。你只有唯一一个指向用NULL清除的内存的指针,即使realloc()承诺它没有释放旧内存。你应该使用:

void *new_ptr = realloc(ptr, new_size);
if (new_ptr == NULL)
    …handle out of memory condition…ptr is still valid!
ptr = new_ptr;

您还应始终检查内存分配是否成功。如果它们失败,你最终会取消引用空指针,这会导致崩溃。