关于C#:当它只读取换行符时,gets()会保存什么

What does gets() save when it reads just a newline

这是Prata的C Primer Plus中gets()的描述:

It gets a string from your system's standard input device, normally
your keyboard. Because a string has no predetermined length, gets()
needs a way to know when to stop. Its method is to read characters
until it reaches a newline (\
) character, which you generate by
pressing the Enter key. It takes all the characters up to (but not
including) the newline, tacks on a null character (\\0), and gives the
string to the calling program.

我很好奇gets()仅读换行符时会发生什么。所以我这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  int main(void)
  {
    char input[100];

    while(gets(input))
    {
      printf("This is the input as a string: %s\
"
, input);
      printf("Is it the string end character? %d\
"
, input == '\\0');
      printf("Is it a newline string? %d\
"
, input =="\
"
);
      printf("Is it the empty string? %d\
"
, input =="");
    }

    return 0;
  }

这是我与程序的互动:

1
2
3
4
5
6
7
8
9
10
11
$ ./a.out
This is some string
This is the input as a string: This is some string
Is it the string end character? 0
Is it a newline string? 0
Is it the empty string? 0

This is the input as a string:
Is it the string end character? 0
Is it a newline string? 0
Is it the empty string? 0

当我按下所有输入键时,第二个块实际上就是感兴趣的东西。在那种情况下input到底是什么?这似乎不是我的任何猜测:\\0\
""


gets的描述中的这一部分可能令人困惑:

It takes all the characters up to (but not including) the newline

最好说它采用了包括换行符的所有字符,但存储了不包括换行符的所有字符。

因此,如果用户输入some string,则gets函数将从用户终端读取some string和换行符,但仅将some string存储在缓冲区中-换行符会丢失。这很好,因为反正没人希望换行符-它是控制字符,而不是用户想要输入的数据的一部分。

因此,如果仅按enter,则gets会将其解释为空字符串。现在,正如某些人所指出的那样,您的代码有多个错误。

printf("This is the input as a string: %s\
", input);

这里没问题,尽管您可能希望用一些人工字符来分隔字符串以进行更好的调试:

printf("This is the input as a string: '%s'\
", input);

printf("Is it the string end character? %d\
", input == '\\0');

不好:您要在此处检查1个字节,而不是整个缓冲区。如果您尝试将整个缓冲区与0进行比较,答案始终是false,因为编译器会将\\0转换为NULL并像"根本不存在缓冲区?"那样解释比较。

正确的方法是:

printf("Does the first byte contain the string end character? %d\
", input[0] == '\\0');

这与\\0仅比较1个字节。

printf("Is it a newline string? %d\
", input =="\
");

不好:这会将缓冲区的地址与"\
"
的地址进行比较-答案始终是false。在C中比较字符串的正确方法是strcmp

printf("Is it a newline string? %d\
", strcmp(input,"\
") == 0);

请注意特殊用法:strcmp在字符串相等时返回0。

printf("Is it the empty string? %d\
", input =="");

这里是同样的错误。在此也使用strcmp

printf("Is it the empty string? %d\
", strcmp(input,"") == 0);

人们常说的

BTW,gets不能以安全的方式使用,因为它不支持防止缓冲区溢出的保护。因此,即使不太方便,也应该使用fgets

1
2
3
4
5
char input[100];
while (fgets(input, sizeof input, stdin))
{
    ...
}

这可能导致混乱:fgets不会从读取的输入中删除换行符字节。因此,如果将代码中的gets替换为fgets,则会得到不同的结果。幸运的是,您的代码将清楚地说明差异。


它将字符串设置为"",即{'\\0'}。不过,请不要使用gets()。它导致缓冲区溢出。