The Standard C Library consists of various headers files. Often only a few select ones are needed for given code.
Other times it is simply convenient coding to include them all in a .c file, even if that makes the compilation time a bit slower.
Including all standard <*.h>
is useful to help detect naming collisions of a .c
file with an existing standard function, object, type, macro, etc.
Review Goal
How well does std.h
accomplish the goal of including all standard header files via one .h
file given that the set varies amongst C versions and implementations?
/*
* std.h
* Created on: Mar 16, 2019, Author: chux
*/
#ifndef STD_H_
#define STD_H_
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined __STDC__ && defined __STDC_VERSION__
#if __STDC_VERSION__ >= 199409
#include <iso646.h>
#include <wchar.h>
#include <wctype.h>
#endif
#if __STDC_VERSION__ >= 199901
#ifndef __STDC_NO_COMPLEX__
#include <complex.h>
#endif
#include <fenv.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <tgmath.h>
#endif
#if __STDC_VERSION__ >= 201112
#include <stdalign.h>
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#endif
#include <stdnoreturn.h>
#ifndef __STDC_NO_THREADS__
#include <threads.h>
#endif
#include <uchar.h>
#endif
#if __STDC_VERSION__ >= 201710
/* None added */
#endif
#endif
#endif /* STD_H_ */
In making a set of functions and types called say foo
, I do not recommend including all header files in a foo.h
, yet perhaps in foo.c
.
Sample usage:
// foo.h (no std.h here)
#ifndef FOO_H_
#define FOO_H_
#include <stdint.h>
#include <time.h>
typedef struct {
time_t t;
uint32_t u32;
} foo;
void foo_this(foo *f);
void foo_that(foo *f);
#endif /* FOO_H_ */
foo.c
or main.c
#include "foo.h"
#include "std.h"
int main(void) {
foo f;
foo_this(&f);
foo_that(&f);
printf("Hello World!\n");
return 0;
}
The alternative spellings afforded in <iso646.h>
seem to be a solution to a regional character set problem of years ago. I reluctantly included <iso646.h>
here but do see that as a good candidate to exclude. It defines macros for and
, or
, xor
and others.
4 Answers 4
Your code doesn't cover "freestanding implementations" (embedded systems or OS). You could check for
#if __STDC_HOSTED__ == 0
and reduce the headers. Freestanding implementations need not provide all headers except a minimal subset, see C11 4/6:A conforming freestanding implementation shall accept any strictly conforming program in which the use of the features specified in the library clause (clause 7) is confined to the contents of the standard headers
<float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>,
and<stdnoreturn.h>
Though of course freestanding implementations may provide other headers too, making this hard to fulfil without a specific implementation in mind.
Style issue: you should indent everything within
#if
...#endif
just as you would for regularif
statements. It is also good practice to leave a comment /* */ after each#endif
to document which#if
is belongs to.
EDIT: proposed indention fix
#ifndef STD_H_
#define STD_H_
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined __STDC__ && defined __STDC_VERSION__
#if __STDC_VERSION__ >= 199409
#include <iso646.h>
#include <wchar.h>
#include <wctype.h>
#endif
#if __STDC_VERSION__ >= 199901
#ifndef __STDC_NO_COMPLEX__
#include <complex.h>
#endif
#include <fenv.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <tgmath.h>
#endif
#if __STDC_VERSION__ >= 201112
#include <stdalign.h>
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#endif
#include <stdnoreturn.h>
#ifndef __STDC_NO_THREADS__
#include <threads.h>
#endif
#include <uchar.h>
#endif
#if __STDC_VERSION__ >= 201710
/* None added */
#endif
#endif /* #if defined __STDC__ && defined __STDC_VERSION__ */
#endif /* STD_H_ */
-
\$\begingroup\$ Please post a sample
#
indent. I have tried various styles, ` #if bar` (my auto formatter (Eclipse) keeps shifting them left ) and# if bar
which looks odd. Perhaps you have another? \$\endgroup\$chux– chux2019年03月25日 13:29:17 +00:00Commented Mar 25, 2019 at 13:29 -
1\$\begingroup\$ @chux Posted. After trying some 30+ different, magic IDEs including GNU indent, I'm convinced that mankind will never develop intelligent AI. So I tend to fall back to good ole spacebar :) \$\endgroup\$Lundin– Lundin2019年03月25日 14:10:08 +00:00Commented Mar 25, 2019 at 14:10
-
\$\begingroup\$ Thanks for the
__STDC_HOSTED__
idea. I'll need to dig deeper into that. \$\endgroup\$chux– chux2019年08月03日 15:11:53 +00:00Commented Aug 3, 2019 at 15:11
With this suggested usage:
#include "foo.h" #include "std.h"
then any macros defined in "std.h"
(and not used in the rest of the main
) won't break the program. Swapping these round:
#include "std.h"
#include "foo.h"
solves that problem, but then fails to diagnose missing includes in "foo.h"
.
To use any of the extensions offered by POSIX (on many OSes including Linux or Mac), you would need to define a feature-test macro before including any system header. To use POSIX 1-2024 with the XSI extension, on a system that supports them, you would prepend:
#define _POSIX_C_SOURCE 202405L // POSIX 1-2024
#define _XOPEN_SOURCE 800 // With XSI extensions
Technically, you only need _XOPEN_SOURCE
, but I always define _POSIX_C_SOURCE
too, so that any inconsistent definition in the codebase will raise an error.
If you want to declare a default version but let a project override it on the command line (e.g. with -D_XOPEN_SOURCE=700
), make the declaration conditional.
#if !defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)
# define _POSIX_C_SOURCE 202405L // Default to POSIX 1-2024
# define _XOPEN_SOURCE 800 // With XSI extensions
#endif
On Windows, you would set _WIN32_WINNT
and NTDDI_VERSION
, and possibly include <SdkDdkVer.h>
. I’m not aware of any functionality they enable in the C standard headers, however.
The next-most-likely feature-test macro a codebase is likely to need is _GNU_SOURCE
.
[Update due to C23 release]
Update also incorporates __STDC_HOSTED__
idea.
(I like @Lundin's idea of indenting the # tree, yet my autoformatter defeated it.)
Note the C23 update and future version detection code.
/*
* stdc.h
*/
#ifndef STD_H_
#define STD_H_
#if defined __STDC_HOSTED__ && __STDC_HOSTED__ == 0
#include <float.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#else
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#endif
#if defined __STDC__ && defined __STDC_VERSION__
#if __STDC_VERSION__ >= 199409
#if defined __STDC_HOSTED__ && __STDC_HOSTED__ == 0
#include <iso646.h>
#else
#include <iso646.h>
#include <wchar.h>
#include <wctype.h>
#endif
#endif
#if __STDC_VERSION__ >= 199901
#if __STDC_HOSTED__ == 0
#include <stdbool.h>
#include <stdint.h>
#else
#ifndef __STDC_NO_COMPLEX__
#include <complex.h>
#endif
#include <fenv.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <tgmath.h>
#endif
#endif
#if __STDC_VERSION__ >= 201112
#if __STDC_HOSTED__ == 0
#include <stdalign.h>
#include <stdnoreturn.h>
#else
#include <stdalign.h>
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#endif
#include <stdnoreturn.h>
#ifndef __STDC_NO_THREADS__
#include <threads.h>
#endif
#include <uchar.h>
#endif
#endif
#if __STDC_VERSION__ >= 201710
/* None added */
#endif
#if __STDC_VERSION__ >= 202311
// https://en.wikipedia.org/wiki/C23_(C_standard_revision)
#if __STDC_HOSTED__ == 0
#include <stdbit.h>
#else
#include <stdbit.h>
#include <stdckdint.h>
#endif
#endif
// Future version detection:
// This file updated in December 2024.
// Report a warning for even greater __STDC_VERSION__ values than the one this file is coded for.
#if __STDC_VERSION__ >= 202412
#warning stdc.h may need updating as __STDC_VERSION__ >= 202412
#endif
#endif
#endif
#endif /* STD_H_ */
Hmmm, maybe:
#warning __FILE__ may need updating as __STDC_VERSION__ >= 202412
instead of:
#warning stdc.h may need updating as __STDC_VERSION__ >= 202412
-
\$\begingroup\$ Note that compilers where
__STDC_HOSTED__
is not defined generally did not support short-circuiting of preprocessor directives. So, there could be a compiler where#if defined __STDC_HOSTED__ && __STDC_HOSTED__ == 0
produces a warning that__STDC_HOSTED__
is undefined. Nested conditionals don’t have this problem. \$\endgroup\$Davislor– Davislor2024年12月05日 22:38:26 +00:00Commented Dec 5, 2024 at 22:38 -
\$\begingroup\$ @Davislor Interesting. Was "not support short-circuiting of preprocessor directives" C compliant at some point? \$\endgroup\$chux– chux2024年12月05日 22:47:45 +00:00Commented Dec 5, 2024 at 22:47
-
\$\begingroup\$ Still doesn’t!
#if defined(foo) && foo()
does not compile on GCC, Clang or ICX and gives a warning on MSVC. \$\endgroup\$Davislor– Davislor2024年12月06日 01:23:50 +00:00Commented Dec 6, 2024 at 1:23 -
1\$\begingroup\$ However,
#if defined FOO && FOO == 0
works fine on all of them with lots of warnings enabled. Any undefined macro has "always" been required to expand to 0 in an#if
directive. \$\endgroup\$Davislor– Davislor2024年12月06日 01:26:11 +00:00Commented Dec 6, 2024 at 1:26 -
\$\begingroup\$ @Davislor " there could be a compiler where
#if defined __STDC_HOSTED__ && __STDC_HOSTED__ == 0
produces a warning that__STDC_HOSTED__
is undefined." --> By spec or by example, can you provide an example that warns? \$\endgroup\$chux– chux2024年12月15日 11:07:20 +00:00Commented Dec 15, 2024 at 11:07
<*.h>
will not certainly detect all name collision and future ones, yet is a reasonable test today to potential discern them. \$\endgroup\$#if __has_include(<foo.h>)
. \$\endgroup\$