错误输入后程序崩溃 (C)
Program goes berserk after bad input (C)
我有一个函数应该从 stdin
中获取一个数字。它应该检查它是否是一个有效的数字,并有选择地将它设置在一个特定的范围内。如果输入相当长(比如 10 个字符),那么该函数会打印错误消息并重置循环,一切都会按预期进行。但是,如果我输入的内容长得可笑:
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
然后出问题了。循环不断重置,但无论我输入什么,它都不再接受它,即使它完全有效。
我想这可能是因为 stdin
不知何故溢出了?但这不是 fgets()
的重点吗 - 它只读取特定数量的字符并丢弃其他所有字符?我如何在不使用异常的情况下解决这个问题?
相关函数:
int safeinp(int * num, const char *message, int low, int high)
{
long a;
char buf[11]; // 9 digits for the number + "\n[=11=]"
int success; // flag for successful conversion
do
{
puts(message);
if (!fgets(buf, 11, stdin))
{
fprintf(stderr, "Unagle to obtain input.\n");
return 1;
}
// have some input, convert it to integer:
char *endptr;
a = strtol(buf, &endptr, 12);
if (errno == ERANGE)
{
//this if() right here is what gets executed endlessly if the input is bad
fprintf(stderr, "Invalid number.\n");
success = 0;
}
else if (endptr == buf)
{
fprintf(stderr, "Invalid input.\n");
success = 0;
}
else if (*endptr && *endptr != '\n')
{
fprintf(stderr, "Conversion error.\n");
success = 0;
}
else
{
success = 1;
if (low != high) {
a = (a < low) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), low : a;
a = (a > high) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), high : a;
}
*num = a;
}
} while (!success);
return success;
}
问题发生在 Visual Studio 2017 年 Windows 10.
不,fgets()
不会清空 "buffer"。如果您输入一个长字符串,您的代码将一次读取 10 个字符,直到它到达您输入的行尾。下一个循环将再次等待。
精简示例:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char buf[11];
do {
puts("TEST");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
fprintf(stderr, "Unagle to obtain input.\n");
return 1;
}
printf("input: %s\n", buf);
} while (1);
return(0);
}
测试运行:
$ gcc -Wall -o dummy dummy.c
$ ./dummy
TEST
123456789012345678901234567890
input: 1234567890
TEST
input: 1234567890
TEST
input: 1234567890
TEST
input:
TEST
^C
$
更新: 尝试吃掉所有剩余字符直到换行符的提议:
/* replacement for fgets(buf, sizeof(buf), stdin) */
char *p = buf;
char c;
unsigned left = sizeof(buf) - 1;
while ((left-- > 0) && ((c = fgetc(stdin)) != '\n')) {
if (feof(stdin)) {
return(1);
}
*p++ = c;
}
*p++ = '[=12=]';
/* eat the rest until newline */
while (c != '\n') {
c = fgetc(stdin);
if (feof(stdin)) {
return(1);
}
}
新测试运行:
$ gcc -Wall -o dummy dummy.c
$ ./dummy
TEST
123456789012345678901234567890
input: 1234567890
TEST
1
input: 1
TEST
1234567890
input: 1234567890
TEST
^C
您需要在 strtol
的 return 之后检查 a
是否为 LONG_MIN
或 LONG_MAX
。无法保证正确解析输入后 errno 未设置为 ERANGE
。
The strtol() function returns the result of the conversion, unless the
value would underflow or overflow. If an underflow occurs, strtol()
returns LONG_MIN. If an overflow occurs, strtol() returns LONG_MAX.
In both cases, errno is set to ERANGE.
libc 函数不重置 errno
成功!
此外,你在 strtol(..,.., base)
-> 你的基地是 12
?
我有一个函数应该从 stdin
中获取一个数字。它应该检查它是否是一个有效的数字,并有选择地将它设置在一个特定的范围内。如果输入相当长(比如 10 个字符),那么该函数会打印错误消息并重置循环,一切都会按预期进行。但是,如果我输入的内容长得可笑:
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
然后出问题了。循环不断重置,但无论我输入什么,它都不再接受它,即使它完全有效。
我想这可能是因为 stdin
不知何故溢出了?但这不是 fgets()
的重点吗 - 它只读取特定数量的字符并丢弃其他所有字符?我如何在不使用异常的情况下解决这个问题?
相关函数:
int safeinp(int * num, const char *message, int low, int high)
{
long a;
char buf[11]; // 9 digits for the number + "\n[=11=]"
int success; // flag for successful conversion
do
{
puts(message);
if (!fgets(buf, 11, stdin))
{
fprintf(stderr, "Unagle to obtain input.\n");
return 1;
}
// have some input, convert it to integer:
char *endptr;
a = strtol(buf, &endptr, 12);
if (errno == ERANGE)
{
//this if() right here is what gets executed endlessly if the input is bad
fprintf(stderr, "Invalid number.\n");
success = 0;
}
else if (endptr == buf)
{
fprintf(stderr, "Invalid input.\n");
success = 0;
}
else if (*endptr && *endptr != '\n')
{
fprintf(stderr, "Conversion error.\n");
success = 0;
}
else
{
success = 1;
if (low != high) {
a = (a < low) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), low : a;
a = (a > high) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), high : a;
}
*num = a;
}
} while (!success);
return success;
}
问题发生在 Visual Studio 2017 年 Windows 10.
不,fgets()
不会清空 "buffer"。如果您输入一个长字符串,您的代码将一次读取 10 个字符,直到它到达您输入的行尾。下一个循环将再次等待。
精简示例:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char buf[11];
do {
puts("TEST");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
fprintf(stderr, "Unagle to obtain input.\n");
return 1;
}
printf("input: %s\n", buf);
} while (1);
return(0);
}
测试运行:
$ gcc -Wall -o dummy dummy.c
$ ./dummy
TEST
123456789012345678901234567890
input: 1234567890
TEST
input: 1234567890
TEST
input: 1234567890
TEST
input:
TEST
^C
$
更新: 尝试吃掉所有剩余字符直到换行符的提议:
/* replacement for fgets(buf, sizeof(buf), stdin) */
char *p = buf;
char c;
unsigned left = sizeof(buf) - 1;
while ((left-- > 0) && ((c = fgetc(stdin)) != '\n')) {
if (feof(stdin)) {
return(1);
}
*p++ = c;
}
*p++ = '[=12=]';
/* eat the rest until newline */
while (c != '\n') {
c = fgetc(stdin);
if (feof(stdin)) {
return(1);
}
}
新测试运行:
$ gcc -Wall -o dummy dummy.c
$ ./dummy
TEST
123456789012345678901234567890
input: 1234567890
TEST
1
input: 1
TEST
1234567890
input: 1234567890
TEST
^C
您需要在 strtol
的 return 之后检查 a
是否为 LONG_MIN
或 LONG_MAX
。无法保证正确解析输入后 errno 未设置为 ERANGE
。
The strtol() function returns the result of the conversion, unless the
value would underflow or overflow. If an underflow occurs, strtol()
returns LONG_MIN. If an overflow occurs, strtol() returns LONG_MAX.
In both cases, errno is set to ERANGE.
libc 函数不重置 errno
成功!
此外,你在 strtol(..,.., base)
-> 你的基地是 12
?