【问题标题】:C check user input error [duplicate]C检查用户输入错误[重复]
【发布时间】:2016-05-07 19:39:54
【问题描述】:

我要做的就是要求输入密码并在输入长度超过 10 个字符时打印出错误消息。如果第一个输入少于 10 个字符,它会起作用。它只是打印出输入并退出程序。如果输入超过 10 个字符,则会打印错误消息并要求输入新密码,但如果第二次尝试输入少于 10 个字符,则会打印出输入,然后程序会以“Thread:1 信号”中断SIGABRT”错误。我知道我不应该使用gets,但我正试图找到一种方法让我的代码使用它。

#include <stdio.h>

#define BUFFER_LENGTH   11

int main() {
    int cont;
    while (1) {
        char line[BUFFER_LENGTH];
        char *p;
        printf("Enter Password: ");
        p = gets (line);
        if (strlen(p)>10) {
            printf("Error! Password must be shorter than 10 characters! \n");
        }else{
            printf(p);
            printf("\n");
            break;
        }
    }
}

【问题讨论】:

  • 不要使用已从 C 标准中删除的 gets()。请改用fgets()
  • 您已为gets 分配了一个最低限度的缓冲区。如果用户输入的字符超过 10 个,则gets 将写到line 的末尾。如果你使用fgets,那么你可以告诉它你的缓冲区有多大,这是防止这个问题的唯一方法(使用更大的缓冲区只会降低错误的可能性)。
  • 当我使用 fgets() 并且输入超过 10 个字符时,它会打印错误消息,然后将前 10 个字符之后的字符打印出来。那不是我想要的。有没有办法让 fgets() 为我的程序工作?
  • printf(p); 从来没有这样做! printf("%s", p); 代替。

标签: c runtime-error


【解决方案1】:

如果用户输入超过 10 个字符,您最终会使用超出有效限制的内存。这正是您必须避免使用gets 的原因。有关该主题的更多信息,请参阅Why is the gets function so dangerous that it should not be used?

gets 行更改为:

fgets(line, sizeof(line), stdin);

那么,您不必担心用户输入的字符数超过 10 个。它们将被简单地忽略。

如果您想将该用例作为用户错误处理,请更改line 的大小,但仍使用fgets

更新,感谢@chux

如果用户输入的字符少于您的情况下的 11 个字符,则该行

 fgets(line, sizeof(line), stdin);

不仅会读取字符,还会在其中包含结束换行符。您必须添加一些代码才能从line 中删除换行符。

 // Trim the newline character from user input.
 size_t len = strlen(line);
 if ( len > 0 && line[len-1] == '\n' )
 {
    line[len-1] = '\0';
 }

【讨论】:

  • 如果你使用它并且输入的长度超过 10 个字符(例如:abcdefghijkl),那么它会打印出错误消息,然后在前 10 个字符(例如:kl)之后获取剩余的字符并打印那些作为有效输入输出。有没有办法擦除“行”中的数据,使其不寻找剩余的字符?
  • 当然,您可以写int c; while ( (c = fgetc(stdin)) != EOF &amp;&amp; c != '\n'); 忽略该行的其余部分。
  • 不,多余的字符不会被忽略,它们会留下来被下次读取。
  • fgets(line, sizeof(line), stdin); 需要额外的工作以确保密码不会太长。它确实可以防止用户输入溢出,但line 可能包含最终的'\n'
  • 注意:line[len-1] 是一个黑客攻击,因为fgets() 读取的第一个char 可能是一个空字符,导致line[0-1] 和UB。最好check len &gt; 0 firststackoverflow.com/a/2693827/2410359
【解决方案2】:

要检测输入的字符是否超过n,代码必须

  1. 至少读取n+1非控制字符。

  2. 处理过多的字符。

OP 的代码不应使用gets()。这是一个过时的函数,不能防止缓冲区溢出。

void GetPrintPW(void) {
  // +1 for the null character.
  char pw[PASSWORD_MAX_LENGTH + 1];

  size_t i = 0;
  bool too_long = false;
  int bad_char = EOF;
  int ch;

  // Note: All characters in the line are consumed. Only the first `n` are saved.
  while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {

    // This would be a good place to add code to check if the character is "good".      
    if (!isprint(ch)) bad_char = ch;

    if (i < PASSWORD_MAX_LENGTH) pw[i++] = ch;
    else too_long = true;
  }
  pw[i] = '\0';

  if (bad_char != EOF) {
     printf("Error! Bad character, code %d\n", bad_char);
  }  else if (too_long) {
     // Avoid `printf(only_some_string)`
     puts("%Error! Password must be shorter than 10 characters!");
  } else {
    // this is BAD! as a % in pw will cause UB with `printf()` 
    // printf(pw);
    printf("'%s'\n", pw);
  }

  // Always a good idea to scrub data after using a password to prevent memory snooping.
  memset(pw, 0, sizeof pw);
}

PW 注释:不要使用getline() 读取密码,因为代码会失去对存储密码的缓冲区的控制。使用一个普通的 char 数组,然后擦洗。使用malloc()realloc()等也会有类似的问题。

良好的操作系统将具有读取密码的特殊功能,因为在每个功能级别,任何缓冲区数据都需要清理。

【讨论】:

    【解决方案3】:

    如果您使用getsline 需要足够大以容纳用户在回车之前可以输入的所有可能字符。那是多少?极限接近无穷大。请改用fgets

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-26
      • 2015-06-20
      • 1970-01-01
      • 2021-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多