所以我想编写一个程序来检查用户是否输入。说的要求是4位数字,如果用户输入5,则程序会不断要求用户租用4位数字。
我得到了这样的工作代码:基本上使用scanf
读取字符串值,然后使用strlen
计数位数。如果用户输入正确的数字,那么我将使用atoi
将该字符串转换为int
,稍后将使用它。说的要求是4位数字:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int digit, mynumber;
int digit = 5;
char str[5];
/* Checking if enter the correct digit */
do {
printf("Enter a %d digit number\n", digit);
scanf("%s", &str);
if (strlen(str) != digit) {
printf("You entered %d digits. Try again \n", strlen(str));
} else {
printf("You entered %d digits. \n", strlen(str));
printf("Converting string to num.....\n");
mynumber = atoi(str);
printf("The number is %d\n", mynumber);
}
} while (strlen(str) != digit);
return 0;
}
我想对此做些修改。而不是对5位数的字符串执行char str[5]
。我想尝试一个动态数组。
所以代替char str[5]
,我这样做:
char *str;
str = malloc(sizeof(char) * digit);
通过代码运行此操作会产生段错误。谁能帮我这个?
这是有问题的完整代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int mynumber;
int digit = 5;
char *str;
str = malloc(sizeof(char) * digit);
/* Checking if enter the correct digit */
do {
printf("Enter a %d digit number\n", digit);
scanf("%s", &str);
if (strlen(str) != digit) {
printf("You entered %d digits. Try again \n", strlen(str));
} else {
printf("You entered %d digits. \n", strlen(str));
printf("Converting string to num.....\n");
mynumber = atoi(str);
printf("The number is %d\n", mynumber);
}
} while (strlen(str) != digit);
return 0;
}
在您的代码中,您有5个字节的数组。如果用户输入的数字多于4位,则scanf
会很高兴地使数组溢出并损坏内存。在两种情况下,它都会破坏内存,作为数组和malloc
。但是,并非每个内存损坏都会导致崩溃。
因此,您需要限制scanf
可以读取的字节数。这样做的方法是在格式字符串中使用%4s
。
但是,在这种情况下,您将无法检测到用户输入的数字多于4位。您至少还需要1个字节:str[6]
和%5s
;
我建议不要使用scanf
,而要使用getchar
。它可以让您逐个字符地阅读并在途中对它们进行计数。
虽然您可以使用格式化的输入函数scanf
将输入作为字符串,但是scanf
充满了许多陷阱,这些陷阱可能会在输入流(stdin
)中留下杂散字符,具体取决于< [匹配失败发生。使用"%s"
转换说明符的限制还在于只能读取第一个空白。如果您的用户滑倒并输入"123 45"
,则您读取"123"
,测试失败,并且"45"
留在stdin
中未读取,除非您手动清空stdin
,否则您将等到下次尝试读取时咬您。
field-width
修饰符的"%s"
,则最好使用gets()
,因为scanf
会很高兴地将无限数量的字符读入5或6个字符数组,超出您的数组范围,调用Undefined Behavior。更合理的方法是提供足够大的字符缓冲区以处理用户可能输入的任何内容。 (不要在缓冲区大小上跳过
)。用fgets()
一次读取整行,它具有足够大的缓冲区,可确保消耗整行,从而消除了字符在stdin
中不被读取的机会。唯一带有fgets
(以及每个line-bound输入功能,例如POSIX getline
)的警告是,'\n'
也将被读取并包含在已填充的缓冲区中。您只需使用'\n'
作为一种方便的方法从尾部修剪strcspn()
,即可获得同时输入的字符数。([note:如果调整测试以使'\n'
包含在您验证的长度中,则可以放弃修剪'\n'
,因为转换为int
会忽略尾随的'\n'
)]] >您的逻辑缺少其他需要的检查。如果用户输入"123a5"
,该怎么办?输入了所有5个字符,但不是全数字。 atoi()
没有错误报告功能,并且会很乐意将字符串无提示地转换为123
,而没有提供任何剩余字符的指示。您有两种选择,要么使用strtol
进行转换并验证是否没有剩余字符,要么简单地循环遍历字符串中的字符,并使用isdigit()
检查每个字符以确保输入了所有数字。
将所有内容放在一起,您可以执行以下操作:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NDIGITS 5 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void) {
int mynumber;
size_t digit = NDIGITS;
char buf[MAXC]; /* buffer to hold MAXC chars */
/* infinite loop until valid string entered, or manual EOF generated */
for (;;) {
size_t len;
printf("\nEnter a %zu digit number: ", digit); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) { /* read entire line */
fputs ("(user canceled input)\n", stdout);
break;
}
buf[(len = strcspn(buf, "\n"))] = 0; /* trim \n, get len */
if (len != digit) { /* validate length */
fprintf(stderr, " error: %zu characters.\n", len);
continue;
}
for (size_t i = 0; i < len; i++) { /* validate all digits */
if (!isdigit(buf[i])) {
fprintf (stderr, " error: buf[%zu] is non-digit '%c'.\n",
i, buf[i]);
goto getnext;
}
}
if (sscanf (buf, "%d", &mynumber) == 1) { /* validate converstion */
printf ("you entered %zu digits, mynumber = %d\n", len, mynumber);
break; /* all criteria met, break loop */
}
getnext:;
}
return 0;
}
[无论何时编写输入例程,都应尝试将其中断。验证它会执行您需要做的事情,并捕获您要防止的情况(您仍然可以添加更多验证)。在这里,它涵盖了最常见的滥用行为:示例使用/输出
$ ./bin/only5digits
Enter a 5 digit number: no
error: 2 characters.
Enter a 5 digit number: 123a5
error: buf[3] is non-digit 'a'.
Enter a 5 digit number: 123 45
error: 6 characters.
Enter a 5 digit number: ;alsdhif aij;ioj34 ;alfj a!%#$%$ ("cat steps on keyboard...")
error: 61 characters.
Enter a 5 digit number: 1234
error: 4 characters.
Enter a 5 digit number: 123456
error: 6 characters.
Enter a 5 digit number: 12345
you entered 5 digits, mynumber = 12345
用户在Linux上使用取消输入(在Windows上使用ctrl + z取消输入,从而生成手册ctrl + d
EOF
:$ ./bin/only5digits
Enter a 5 digit number: (user canceled input)
(<< [note:,您可以添加其他检查以查看是否输入了1024个或更多字符-留给您]
这是一种读取输入的方法,但略有不同,但是从一般规则的角度来看,在接受用户输入时,如果确保消耗了整行输入,则可以避免与为此目的使用scanf
相关的许多陷阱。 。