I am currently writing a piece of code whose intended usage is this:
program input.txt output.txt
or
program input.txt
in which case it defaults to stdout
.
This is the code I have now (within main()
):
FILE *outFile;
if (argc < 3) {
outFile = stdout;
} else {
fprintf(stdout, "Will output to file %s\n", argv[2]);
outFile = fopen(argv[2], "w");
if (outFile == NULL) {
fprintf(stderr, "ERR: Could not open file %s. Defaulting to stdout\n", argv[2]);
outFile = stdout;
}
}
/* ... write stuff to outFile... */
if (argc < 3 && outFile != stdout) {
fclose(outFile);
}
These are my concerns: first of all, will this successfully open and close outFile when provided? Also, will this successfully not close stdout? Can anything bad happen if I close stdout?
Also, is this portable? I compile with gcc
but this project will be evaluated by a professor using Windows.
Apologies if this is a bit of a mess of a question. I come from Python and am not a CS major (I'm studying mathematics).
是的,它是便携式的,还可以。
Yes, it's portable. You assigned outfile = stdout
, so they will be equal as long as you don't reassign either of them elsewhere in the program.
You don't really need the argc < 3
test as well -- the two conditions should always be the same, since you only do the assignment when that's true.
In any program that writes significant data to stdout
, you should close stdout
immediately before exiting, so that you can check for and report delayed write errors. (Delayed write errors are a design mistake; it ought to be impossible for fclose
or close
to fail. But we are stuck with them.)
The usual construct is, at the very end of main
,
if (ferror(stdout) || fclose(stdout)) {
perror("stdout: write error");
return 1;
}
return 0;
Some programs stick an fflush
in there too, but ISO C requires fclose
to perform a fflush
, so it shouldn't be necessary. This construct is entirely portable.
It's important for this to be the very last thing you do before exiting. It is relatively common for libraries to assume that stdout
is never closed, so they may malfunction if you call into them after closing stdout
. stdin
and stderr
are also troublesome that way, but I've yet to encounter a situation where one wanted to close those.
It does sometimes happen that you want to close stdout
before your program is completely done. In that case you should actually leave the FILE
open but close the underlying "file descriptor" and replace it with a dummy.
int rfd = open("/dev/null", O_WRONLY);
if (rfd == -1) perror_exit("/dev/null");
if (fflush(stdout) || close(1)) perror_exit("stdout: write error");
dup2(rfd, 1);
close(rfd);
This construct is NOT portable to Windows. There is an equivalent, but I don't know what it is. It's also not thread-safe: another thread could call open
in between the close
and dup2
operations and be assigned fd 1, or it could attempt to write something to stdout
in that window and get a spurious write error. For thread safety you have to duplicate the old fd 1 and close it via that handle:
// These allocate new fds, which can always fail, e.g. because
// the program already has too many files open.
int new_stdout = open("/dev/null", O_WRONLY);
if (new_stdout == -1) perror_exit("/dev/null");
int old_stdout = dup(1);
if (old_stdout == -1) perror_exit("dup(1)");
flockfile(stdout);
if (fflush(stdout)) perror_exit("stdout: write error");
dup2 (new_stdout, 1); // cannot fail, atomically replaces fd 1
funlockfile(stdout);
// this close may receive delayed write errors from previous writes
// to stdout
if (close (old_stdout)) perror_exit("stdout: write error");
// this close cannot fail, because it only drops an alternative
// reference to the open file description now installed as fd 1
close (new_stdout);
Order of operations is critical: the open
, dup
and fflush
calls must happen before the dup2
call, both close
calls must happen after the dup2
call, and stdout must be locked from before the fflush
call until after the dup2
call.
Additional possible complications, dealing with which is left as an exercise:
fork
and execve
mid-operation
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.