在关于http://man7.org/linux/man-pages/man3/setbuf.3.html给出的缓冲区使用功能的手册中,下面的一段代码被声明为无效。但是,当我在我的机器上尝试时,事情进展顺利:
#include <stdio.h>
int
main(void)
{
char buf[BUFSIZ];
setbuf(stdin, buf);
printf("Hello, world!\n");
return 0;
}
该年龄给出的理由是:
您必须确保 buf 指向的空间仍然存在 时间流已关闭,也会在程序终止时发生。
我在这段代码中看不到任何错误。它也很顺利。是对还是不对?任何人都可以向我解释一下吗?
################################################ 2 @Lingxi已经回答如下,主函数返回后缓冲区将被销毁。但stdin
幸存下来。
但是,是否有stdin
的默认缓冲区?
而且,我们可以假设stdin
是一个流,并且有一些方法可以销毁或关闭该流吗?
答案 0 :(得分:2)
错误消息已经非常清楚地解释了它。在您的代码段中,buf
在main
返回时被销毁,但stdin
的生命周期超出main
。在main
返回但stdin
关闭之前的时间段内,您将打开一个带有无效缓冲区的stdin
。这有时会引起问题。举个例子,如果存在一个析构函数使用stdin
进行输入的全局类对象,您认为会发生什么?
您可能还想查看this question。
答案 1 :(得分:2)
这是未定义行为的示例。未定义的行为不是弹出的神奇错误消息,并告诉您程序错误。这意味着你的程序很危险。 UB可能发生的最糟糕的事情就是发生在你身上的事情 - 根本没有明显的症状。这是因为该bug未被发现,并且可能在以后导致极其危险的事情。例如,如果在main
返回(atexit
处理程序,{+ 1}}处理程序,C ++ dtors,退出时发生的stdio文件关闭等运行的退出代码期间重新填充读取缓冲区,则返回其他某些地址的返回地址堆栈上的函数可能被破坏,这可能导致任意代码执行漏洞。
当C界面的文档告诉您无法执行某些操作时,信任它。测试它是否“有效”并不是一项有意义的活动,除非你试图弄清楚如何利用这些错误。
答案 2 :(得分:1)
此代码失败的原因仅仅是因为缓冲区是在atexit
函数内声明的,当此函数结束时,因为缓冲区是main函数的本地缓冲区,并且在堆栈中它被释放。这会导致stream
setbuf
刷新和关闭不符合有效缓冲区的#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct Record Record;
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
char zipcode[51];
char phoneNumber[51];
Record *next;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = '\0';
// int size = 1;
// int i = 0;
Record *records = NULL;
Record *records_first = NULL;
Record *records_previous = NULL;
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL) {
for( ; ; ) {
records = (Record*) malloc(sizeof(Record));
if(records_first == NULL)
records_first = records;
if(records_previous != NULL)
records_previous->next = records;
records = records_first;
printf("First Name: \n");
scanf("%s", records->fname);
fprintf(fileWriter,"%s\t",records->fname);
printf("Last Name: \n");
scanf("%s", records->lname);
fprintf(fileWriter,"%s\t",records->lname);
printf("Address: \n");
scanf(" %[^\n]", records->address);
fprintf(fileWriter,"%s\t",records->address);
printf("City: \n");
scanf("%s", records->city);
fprintf(fileWriter,"%s\t",records->city);
printf("State: \n");
scanf("%s", records->state);
fprintf(fileWriter,"%s\t",records->state);
printf("Zipcode: \n");
scanf("%s", records->zipcode);
fprintf(fileWriter,"%s\t",records->zipcode);
printf("Phone Number: \n");
scanf("%s", records->phoneNumber);
fprintf(fileWriter,"%s\t\n\n",records->phoneNumber);
records->next = NULL;
records_previous = records;
printf("Are there anymore records? [y/n] ");
scanf(" %c", &answer);
if(tolower(answer) != 'y') {
free(records);
fclose(fileWriter);
break;
}
}
} else
printf("Error opening file.");
return 0;
}
功能要求。
答案 3 :(得分:1)
点是,main()实际上既不是为程序执行的第一个代码,也不是最后一个代码。有一些“启动”代码在执行一些处理后调用main()
并在main()返回后清理。
此清理的一个步骤实际上是刷新缓冲区并关闭尚未被应用程序关闭的文件(这些文件包括stdin等)。所以,如果你离开main(),缓冲区就不再是你的了,但是清理可能仍然想要访问它。试着把缓冲区给别人,其他东西放在里面。任何事情都可能发生,因为你不知道他对记忆的所作所为。
简单地说:C标准中用于“任何可能发生的事情”的术语是“未定义的行为”。从字面上理解这两个短语。 @R。指出相当不错。想想一辆刹车不起作用的汽车,但你没有得到任何通知,开着200公里/小时(是 - 高速公路;-)下坡。