关于printf:C-第3个scanf修改了第2个scanf中的变量

C - 3rd scanf modifies a variable from 2nd scanf

我想我已经尝试了任何方法(刷新标准输入,使用scanf消耗换行符等),但是没有任何工作如我所希望的那样。 由于某些原因,第三个scanf在以下代码中修改了第二个scanf中的变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>

int main()
{
  char first_name[16], last_name[21];
  char filename[11];
  FILE *opening;

  printf("The program saves your first and last name into a file.\
"
);

  printf("Enter your first name:");
  scanf("%s", first_name);
  getchar();

  printf("Enter your last name:");
  scanf(" %s", last_name);
  getchar();

  printf("File where you want to save your name:");
  scanf(" %s", filename);

  opening = fopen(filename,"wb");

  fprintf(opening,"%s %s", first_name, last_name);
  printf("\
Successfully saved the data!"
);

  fclose(opening);

  return 0;
}

输出:

1
2
3
4
5
6
The program saves your first and last name into a file.
Enter your first name: John
Enter your last name: Doe
File where you want to save your name: filename.txt

Successfully saved the data!

一切正常,但文件名.txt的内容是这样的:

John t

我猜想't'字符以某种方式来自'txt',但是我刚开始学习C语言,我不知道如何解决这段代码。 上师可以帮我吗?


您的filename缓冲区太小。

您编写的filename.txt是12个字符,再加上零以完成它,使结果为13。您仅分配11。请尝试如下操作:

1
char filename[20];

它应该工作。

尽管使用scanf还是要小心,因为它可能会导致非常讨厌的问题,就像您现在遇到的那样。实验和学习C语言非常有用,因为它向您展示了正确的内存处理的重要性。对于任何实际项目,您应该考虑使用不同的功能或框架。


在字符串上使用scanf()是危险的,因为与缓冲区提供的内存相比,它可能将更多的数据读入缓冲区。

如果扫描字符串,则应始终通过将此数字添加到传递给scanf()的格式中来告诉scanf()要读取多少字符:

1
2
3
4
5
6
7
8
char file_name[11];

...

scanf("%10s", file_name); /* As file_name provides memor for 11 characters, read a
                             maximum of 10 characters into file_name leaving 1
                             character room for the necessary `0-`terminator indicating
                             the end of the"string". */

此外,您的代码还会错过fopen系统调用上的错误检查。

最好做这样的事情:

1
2
3
4
5
6
opening = fopen(filename,"wb");
if (NULL == opening)
{
  perror("fopen() failed");
  exit(EXIT_FAILURE);
}

如果要输入filename.txt作为文件名,那么您将超出filename的缓冲区。这是未定义的行为,是导致奇怪结果的原因。

要解决此问题,请将char filename[11];增大,记住要为NULL终止符增加1个字符。在您的特定情况下,将是char filename[14];,允许在scanf调用中的%s之前有错误的空间。

否则,一切看起来都很好。