【问题标题】:function call in do while loop only execute once.do while 循环中的函数调用只执行一次。
【发布时间】:2015-10-19 02:58:16
【问题描述】:

我有一个程序来检查用户输入并确保它只是整数而不是字符。在我的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);
}

/* 主要 */

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

【问题讨论】:

  • 返回后的break是不可达代码。您可以通过在case 'y': case 'Y': 之后执行return 1; 和在case 'n': case 'N': 之后执行return 0; 来简化代码。但是,这两点都与您当前的问题无关。
  • 编译所有警告和调试信息 (gcc -Wall -Wextra -g) 然后使用调试器 (gdb)

标签: c


【解决方案1】:

修改后的答案

直接的问题是,当doAgain()yn 退出时,它不会读取这些字符之后的换行符,因此当它重新输入getBoundary() 时,它读取的第一个字符是什么在 yn 之后,这可能是一个换行符,它终止了输入行。您需要在有效输入和无效输入上吞噬该行的其余部分。

这段代码大部分都有效——它也没有泄漏(至少在我的随意测试下)。

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

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

顺便说一句,当我第一次编译你的代码时,在我默认的严格选项下,我只收到了 3 个警告——因为我在(非静态)函数定义之前需要原型。这非常好;做得好。很少有人编写发布在 SO 上的代码能够通过这种级别的审查而几乎没有投诉。

如果是我的代码,我将很少有do … while 循环(此代码中没有)。它们偶尔有用,但偶尔是有效的术语。一般来说,最好使用顶级测试while 循环,或显式for 循环。


原答案

真正的问题,但不是立即引起麻烦的问题。

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

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

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

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

还要注意,您的 checkstr() 函数只检测字母字符;标点和控制字符也不会转换为整数。您应该检查每个字符是否是一个数字 (isdigit(check[i])),并且您可能不得不担心普通的 char 被签名 - 所以isdigit((unsigned char)check[i]) 更好。类似的 cmets 适用于其他 isuvwxyz() 函数。

doAgain() 中,您应该使用int c; 而不是char c;,并且您应该检查EOF 和换行符。如果您检测到 EOF,则答案为“否”,您应该返回。

另外,在你的 getBoundary() 函数中,你有:

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

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

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

ptr = realloc(ptr, new_size);

如果分配失败,则会泄漏内存。尽管realloc() 承诺它不会释放旧内存,但您刚刚拥有的唯一指向内存的指针被 NULL 清除。你应该使用:

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

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

【讨论】:

  • 我调整了您推荐的所有内容,但我仍然不明白为什么循环会终止并且只执行一次?我把 continue 语句放错地方了吗???
  • 查看更新。我认为你的情况有一部分是错误的;但是您还遇到了另一个问题,doAgain() 函数在 yn 响应之后没有吃掉换行符,因此 getBoundary() 函数立即读取换行符并终止。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-12
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多