我有一个检查用户输入的程序,并确保它只是整数而不是字符。在我的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;
}
答案 0 :(得分:2)
当前问题是当doAgain()
退出y
或n
时,它不会在这些字符后读取换行符,因此当它重新进入时{{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;
您还应始终检查内存分配是否成功。如果它们失败,你最终会取消引用空指针,这会导致崩溃。