【问题标题】:Using malloc in C to make sure that user enter certain digits在 C 中使用 malloc 确保用户输入某些数字
【发布时间】:2020-01-26 03:26:55
【问题描述】:

所以我想编写一个程序来检查用户是否输入。假设要求是 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;
}

【问题讨论】:

  • 你能显示有问题的代码吗?我怀疑您将str 的地址传递给scanf,但没有代码,这只是猜测。
  • 你需要malloc(digit+1)。字符串 NUL 终止符需要一个额外的字节。
  • 我刚刚添加了给出问题的完整代码。
  • 你需要scanf("%s", str);&amp; 因为str 已经是一个指针。
  • char str[5]; ... scanf("%s", &amp;str); 应该使用scanf("%4s", str);。如果代码需要读取超过 4 个字符,请使用更大的缓冲区。

标签: c string integer malloc atoi


【解决方案1】:

虽然您可以使用格式化输入函数 scanf 将输入作为字符串,但 scanf 充满了许多陷阱,可能会在输入流 (stdin) 中留下杂散字符,具体取决于是否匹配失败。它还具有使用"%s" 转换说明符 的限制,只能读取到第一个空白。如果您的用户滑倒并输入了"123 45",那么您读取了"123",您的测试失败,并且"45" 留在stdin 中未读,除非您手动清空stdin,否则等待下次尝试读取时咬您。

此外,如果您使用不带 field-width 修饰符的"%s" —— 您不妨使用gets(),因为scanf 会很乐意将无限数量的字符读入您的5 或 6 个字符数组,写入超出数组边界调用 Undefined Behavior

更合理的方法是提供一个足够大的字符缓冲区来处理用户可能输入的任何内容。 (不要吝啬缓冲区大小)。使用fgets() 一次读取整行,它具有足够大小的缓冲区确保消耗整行,从而消除字符在stdin 中保持未读的机会。 fgets(以及每个面向行的输入函数,如POSIX getline)的唯一警告是'\n'也被读取并包含在填充的缓冲区中。您只需使用strcspn() 从末尾修剪'\n',作为获取同时输入的字符数的便捷方法。

(注意:如果您调整测试以在您验证的长度中包含'\n',则可以放弃修剪'\n',因为转换为int 将忽略尾随@ 987654343@)

您的逻辑缺少另一项需要的检查。如果用户输入"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 上的 ctrl+d(或 Windows 上的 ctrl+z)取消输入,生成手册 EOF:

$ ./bin/only5digits

Enter a 5 digit number: (user canceled input)

注意:您可以添加额外的检查以查看是否输入了 1024 个或更多字符 - 留给您)

这是读取输入的一种稍微不同的方法,但从一般规则的角度来看,在获取用户输入时,如果您确保使用整行输入,您可以避免许多与使用 scanf 相关的陷阱目的。

检查一下,如果您还有其他问题,请告诉我。

【讨论】:

    【解决方案2】:

    在您的代码中,您有一个 5 个字节的数组。如果用户输入超过 4 位,scanf 将愉快地溢出数组并破坏内存。在这两种情况下,它都会破坏内存,作为一个数组和一个malloc。但是,并非所有内存损坏都会导致崩溃。

    因此,您需要限制scanf 可以读取的字节数。方法是在格式字符串中使用%4s

    但是,在这种情况下,您将无法检测到用户输入超过 4 位的数字。您至少需要多 1 个字节:str[6]%5s

    我建议不要使用scanf,而是使用getchar。它可以让您逐字阅读并在途中数数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-08
      • 2017-02-02
      • 1970-01-01
      • 2022-10-22
      • 2012-04-01
      相关资源
      最近更新 更多