Reputation: 11791
I've got several oldish code bases that initialize variables (to be able to redirect input/output at will) like
FILE *usrin = stdin, *usrout = stdout;
However, gcc (8.1.1, glibc 2.27.9000 on Fedora rawhide) balks at this (initializer is not a compile time constant
). Rummaging in /usr/include/stdio.h
I see:
extern FILE *stdin;
/* C89/C99 say they're macros. Make them happy */
#define stdin stdin
First, it makes no sense to me that you can't initialize variables this (rather natural) way for such use. Sure, you can do it in later code, but it is a nuisance.
Second, why is the macro expansion not a constant?
Third, what is the rationale for having them be macros?
Upvotes: 1
Views: 2238
Reputation: 753695
Classically, the values for stdin
, stdout
and stderr
were variations on the theme of:
#define stdin (&__iob[0])
#define stdout (&__iob[1])
#define stderr (&__iob[2])
These are address constants and can be used in initializers for variables at file scope:
static FILE *def_out = stdout;
However, the C standard does not guarantee that the values are address constants that can be used like that C11 §7.21 Input/output <stdio.h.>
:
stderr
,stdin
,stdout
which are expressions of type ''pointer to FILE'' that point to the FILE objects associated, respectively, with the standard error, input, and output streams.
Sometime a decade or more ago, the GNU C Library changed their definitions so that you could no longer use stdin
, stdout
or stderr
as initializers for variables at file scope, or static
variables with function scope (though you can use them to initialize automatic variables in a function). So, old code that had worked for ages on many systems stopped working on Linux.
The macro expansion of stdin
etc is either a simple identity expansion (#define stdin stdin
) or equivalent (on macOS, #define stdout __stdoutp
). These are variables, not address constants, so you can't copy the value of the variable in the file scope initializer. It is a nuisance, but the standard doesn't say they're address constants, so it is legitimate.
They're required to be macros because they always were macros, so it retains that much backwards compatibility with the dawn of the standard I/O library (circa 1978, long before there was a standard C library per se).
Upvotes: 3
Reputation:
First, it makes no sense to me that you can't initialize variables this (rather natural) way for such use.
Second, why is the macro expansion not a constant?
stdin
, stdout
, and stderr
are pointers which are initialized during C library startup, possibly as the result of a memory allocation. Their values aren't known at compile time -- depending on how your C library works, they might not even be constants. (For instance, if they're pointers to statically allocated structures, their values will be affected by ASLR.)
Third, what is the rationale for having them be macros?
It guarantees that #ifdef stdin
will be true. This might have been added for compatibility with some very old programs which needed to handle systems which lacked support for stdio.
Upvotes: 6