Search code examples
clanguage-lawyerc99

Why are stderr, stdin, stdout defined as macros?


C99 defines these three as macros that "are expressions of type ‘‘pointer to FILE’’ that point to the FILE objects associated, respectively, with the standard error, input, and output streams".

Given this, these expressions may or may not be const (i.e. the pointer may or may not be written to redirect to another file or stream); and they may have either internal or external linkage (i.e. writing to redirect to another file or stream may not persist between two separately-compiled C files). Therefore, attempting to redirect stdout from within a C file may have unexpected behavior.

(Note: In gcc-9.4.0, it appears that they are macros mapped to a non-const variable with external linkage having the same name and accompanied by the comment: /* C89/C99 say they're macros. Make them happy. */.)

Can anyone shed some light on whether there is an official reason for this or is it safe to assume that this is another "oversight" in the original standard and that redirecting the streams should be avoided from within C code in order to keep things portable?


Solution

  • these expressions may or may not be const (i.e. the pointer may or may not be written to redirect to another file or stream);

    Correct.

    and they may have either internal or external linkage

    The macro identifiers themselves have no linkage, and only if their replacement texts are identifiers is linkage even meaningful for them.

    [...] Therefore, attempting to redirect stdout from within a C file may have unexpected behavior.

    No. Attempting to assign a new value to stdout may fail or have unexpected results. But yes, you have discovered that performing such an assignment is not a reliable way to change the destination to which stdout is connected.

    Can anyone shed some light on whether there is an official reason for this

    There is an official rationale document for C99. It does not address this point.

    or is it safe to assume that this is another "oversight" in the original standard

    I don't know what you mean. I think it's quite safe to assume that stdin, stdout, and stderr were specified to be macros quite intentionally. And it follows directly from their specifications that you cannot rely on assigning to them, for any purpose.

    I infer that they are specified as they are to allow flexibility to implementations. For example, they could be implemented as function calls or as table lookups. They don't have to be object identifiers.

    and that redirecting the streams should be avoided from within C code in order to keep things portable?

    No. Assigning to stdin, stdout, and stderr must be avoided for strict conformance to the C language specification (which roughly equates to "to keep things portable"). If you want to associate one of the standard streams with a different destination, then that is the primary purpose of the freopen() function, available in all versions of standard C.