【问题标题】:strcmp returns wrong valuestrcmp 返回错误值
【发布时间】:2019-02-14 12:51:45
【问题描述】:

有人能解释一下为什么strcmp 会返回相同的值,即使密码正确/不正确?我在包含部分下方定义了有效密码,并在程序结束时输入了一个来检查它。

这是我的代码:

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>

#define TIME 10
#define MAXPASSWORD 12
#define PASSWORD "pass123"

void sigalrm_handler() {
    printf("\nERR: Time is up...\n");
}

int getch() {
    struct termios oldtc, newtc;
    int ch;
    tcgetattr(STDIN_FILENO, &oldtc);
    newtc = oldtc;
    newtc.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newtc);
    ch=getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldtc);
    return ch;
}

int main(int argc, char * argv[]) {
    char password[MAXPASSWORD] = {0};
    printf("Enter correct password. You have %d seconds: ", TIME);
    signal(SIGALRM, sigalrm_handler);
    alarm(TIME);
    fflush(stdout);

    for(int i=0; i<MAXPASSWORD; i++)
    {
      password[i] = getch();
      if (password[i] == '\n')
        break;
      printf("*");
    }

    if (strcmp(password, PASSWORD) == 0) {
        printf("\nValid password\n");
    } else {
        printf("\nInvalid password\n");
    }
}

【问题讨论】:

  • 相同的值是多少?
  • 在代码末尾添加printf("&lt;%s&gt; &lt;%s&gt;\n", password, PASSWORD);,检查输出,你可能会发现。
  • 你是不是总是在用户输入缓冲区中存储一个额外的换行符(就像 fgets() 一样)?
  • 当getchar() 已经可用时,为什么还要在代码中定义getch()? (getch() is a nonstandard function . It is not part of the C standard library or ISO C....)
  • 这不起作用,因为 '\n' 始终是字符串输入的一部分,而不是宏的一部分。如果你添加密码[i] = '\0';在 if 检查内部和中断之前;这个程序可以正常工作。

标签: c strcmp


【解决方案1】:

问题是您将\n 存储到输入缓冲区中,因此pass123 和pass123\n 将不匹配。

因此,如果您发现\n,则null 终止输入,如下所示。

  if (password[i] == '\n')
  {
     password[i] = '\0';
     break;
  }

【讨论】:

  • 谢谢老兄,成功了!我不知道我必须将 '\0' 添加到数组中。你解释得简单明了!
【解决方案2】:

您忘记将 \n 替换为 NUL

...
for(int i=0; i<MAXPASSWORD; i++)
{
  password[i] = getch();
  if (password[i] == '\n')
  {
    password[i] = 0;   // <<<< add this line
    break;
  }
  printf("*");
}
...

还有另一个问题:考虑一下如果用户在点击 Enter 之前输入了超过 11 个字符会发生什么。我让你发现自己作为一个练习。

【讨论】:

  • 我也想说这个,但他们对缓冲区进行了零初始化。因此,除非读取所有 12 个字符,否则会意外出现空终止,在这种情况下它将丢失。
  • @Lundin 实际上 \n 需要替换为 NUL。
【解决方案3】:

您已经说过strcmp() 表示两个字符串 相等,但当您知道它们不相等时...

其他答案中提到的\n 的问题没有得到解决,如果您确实看到strcmp() 返回错误的指示,那么问题就变成了为什么?

在 C 中,string 定义为以null 结尾的字符数组。因此,例如,如果您有以下情况:

char password[MAXPASSWORD] = {0};//where MAXPASSWORD == 12

|p|a|s|s|1|2|3|\0|\0|\0|\0|\0| // `PASSWORD ` - legal string
|s|o|m|e|p|a|s|s|1|2|3|4|\n|  // 'password' 

即使替换了\n 字符,这个数组还是太长了一个字符:

|s|o|m|e|p|a|s|s|1|2|3|4|\0|  // 'password' - too long by 1 character
                        ^     // end of legal definition of `password`

如果password 数组的字符太多,即使在这种情况下,在将最后一个char \n 替换为字符串合法定义的位置中的NULL 之后,代码成为受制于undefined behavior。

字符串函数专门用于strings。当出现非空终止字符数组时,函数(在本例中为 strcmp()),因为它正在寻找 nul 终止符以知道字符串的结尾在哪里,因此不能期望其行为具有可预测性。 (在这种情况下,nul 字符的位置将是未定义行为的原因。)

为防止这种情况发生,即使用户能够在password 中输入太多字符,也始终以如下语句终止:

password[MAXPASSWORD-1] = 0;  //for a properly initialized array, (as your code indicates)
                              //this guarantees termination occurs 
                              //within legal memory area of defined variable. 

这样,就不会出现未定义的行为,如果字符串不同,strcmp() 会指出这一点。

【讨论】:

  • @Jabberwocky - 问题的标题断言 OP 看到 strcmp 表示相等,而已知 strings 确实不相等。我正在解决这个问题。您的答案似乎解决了如何使字符串相等的问题。没有人(我还没有注意到)询问输入我的用户的数组有多长。如果它等于sizeof char 数组定义,那么您有未定义的行为。
  • 我认为这段代码不是UB。这不起作用,因为断点是在将“\n”字符存储在数组中并进行比较之后。所以 '\n' 不存在于定义的宏中。所以这始终是预期的行为。如果他只是在休息前分配密码[i] = '\0',这应该可行。如果我理解你提到的 UB 不正确,请纠正我
  • @ryyker 我想问题的措辞是错误的(这不是第一次发生)。 \n 确实存在 问题。如果用户输入这个:ABC 然后按 Enter 键,缓冲区肯定包含 "ABC\n" (NUL 终止),因为缓冲区是零初始化的(char password[MAXPASSWORD] = {0};)
  • @ryyker:我只是想了解更多,想知道我是否遗漏了什么,这个评论并不是要说任何反对。对不起,如果有这种感觉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-30
  • 1970-01-01
相关资源
最近更新 更多