这是我的代码:
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
int main(int argc,char *argv[])
{
int oldfd;
int newfd;
if(argc!=2)
{
printf("Usgae : %s file_name\n",argv[0]);
exit(0);
}
oldfd=open(argv[1],O_RDWR|O_APPEND,S_IRWXU); // Opening the file in Read/Write mode
if (-1 == oldfd)
{
perror("Error opening file");
exit(0);
}
close(1); // closing stdout
newfd=dup(oldfd); //Now this newfd holds the value 1
close(oldfd); //closing the oldfd
printf("\nStack Overflow"); //Now this printf will print content into the file as stdout closed already
close(newfd);// closing newfd
return 0;
}
我实际上要做的只是使用printf()而不是write()系统调用将“Stack Overflow”打印到文件中。
它不会将内容打印到文件中。但我观察到的一件事是,如果我删除代码:
close(newfd)
它按预期将内容打印到文件中。但我不明白为什么。我打印了内容,然后才关闭newfd。
是什么原因?
答案 0 :(得分:5)
这里实际发生的是printf
的输出被缓冲而不立即发送到fd 1;而是从main
返回后,C运行时将缓冲区刷新到文件描述符1。如果你close(newfd)
,你已经有效地阻止了退出时运行时执行的自动刷新。
如果您在fflush(stdout)
之前明确close(newfd)
,则您的输出应显示在文件中。
顺便说一下,如果你想重定向一个特定的文件描述符,就会有一个备用系统调用dup2(oldfd, 1)
,它使fd 1成为oldfd
的副本,如果以前打开则关闭fd 1。
答案 1 :(得分:0)
当您直接使用文件描述符时,您将希望避免使用诸如printf
之类的C stdio函数。从stdio层下面更改底层文件描述符似乎充满了危险。
如果您将printf
更改为以下内容:
write(newfd, "\nStack Overflow", 15);
那么您可能会得到您期望的输出(无论您是否close(newfd)
)。
答案 2 :(得分:0)
close
,write
,open
system calls主要在linux kernel内完成;所以从应用的角度来看,它们是基本的原子操作。
printf
和fprintf
是在这些(和其他)系统调用之上构建的标准库函数。
在exit之前(例如从main
返回),标准库和环境(特别是调用crt*.o
的{{1}}中的代码)正在执行这些功能由atexit注册;并且标准I / O是(排序)在退出时注册对fflush的调用。因此main
在退出时是stdout
。如果你在main中fflush
它的描述符,则刷新失败并且什么都不做。
我认为你不应该将close
和raw stdio
- s混合到同一个写描述符中。考虑使用fdopen
or freopen