Why does this C code compile in C99? What should I read to learn more?
I can't post unless I add more text so here's some nonsense text because I don't think there's anything else to say
$ cat m.c
#include <stdio.h>
#include <time.h>
int main() {
struct timespec time;
int res = clock_gettime(CLOCK_REALTIME, &time);
printf("%d %ld %ld\n", res, time.tv_sec, time.tv_nsec);
return 0;
}
$ clang m.c && ./a.out && rm ./a.out
0 1631386905 774654955
$ clang -std=c99 m.c && ./a.out && rm ./a.out
m.c:4:18: error: variable has incomplete type 'struct timespec'
struct timespec time;
^
m.c:4:9: note: forward declaration of 'struct timespec'
struct timespec time;
^
m.c:5:12: warning: implicit declaration of function 'clock_gettime' is invalid in C99 [-Wimplicit-function-declaration]
int res = clock_gettime(CLOCK_REALTIME, &time);
^
m.c:5:26: error: use of undeclared identifier 'CLOCK_REALTIME'
int res = clock_gettime(CLOCK_REALTIME, &time);
^
1 warning and 2 errors generated.
It’s because using the -std=c99
option defines a macro that causes the declaration of clock_gettime
to be hidden.
The GNU C library can expose different versions of its functions, or hide them altogether, depending on what is expected by user code. User code declares those expectations by defining certain specially-named macros (called feature test macros, though this is something of a misnomer) with pre-determined values recognised by the GNU C library headers. One such macro is __STRICT_ANSI__
, which is implicitly defined if any of the -std=cXX
options are used.
Quoting feature_test_macros(7)
:
__STRICT_ANSI__
ISO Standard C. This macro is implicitly defined by
gcc(1)
when invoked with, for example, the-std=c99
or-ansi
flag.[…]
If any of
__STRICT_ANSI__
,_ISOC99_SOURCE
,_ISOC11_SOURCE
(since glibc 2.18),_POSIX_SOURCE
,_POSIX_C_SOURCE
,_XOPEN_SOURCE
,_XOPEN_SOURCE_EXTENDED
(in glibc 2.11 and earlier),_BSD_SOURCE
(in glibc 2.19 and earlier), or_SVID_SOURCE
(in glibc 2.19 and earlier) is explicitly defined, then_BSD_SOURCE
,_SVID_SOURCE
, and_DEFAULT_SOURCE
are not defined by default.
Using -std=gnuXX
option instead of -std=cXX
does not cause __STRICT_ANSI__
to be defined; it also enables GNU extensions to the C language. If you want to keep those language extensions disabled, you can instead put #define _DEFAULT_SOURCE 1
at the very top of the file to expose all the symbols the C library otherwise would expose (or perhaps another feature test macro to adjust the library interface to your liking). An explicit feature test macro will take precedence over __STRICT_ANSI__
.