Search code examples
cgccposixstdint

Include of stdint.h in a header file breaks compiling with clock_gettime()


My C file uses clock_gettime(). For this to work it includes <time.h> and defines _POSIX_C_SOURCE to (200112L), as per the man page:

SYNOPSIS
       #include <time.h>

       int clock_getres(clockid_t clk_id, struct timespec *res);

       int clock_gettime(clockid_t clk_id, struct timespec *tp);

       int clock_settime(clockid_t clk_id, const struct timespec *tp);

       Link with -lrt (only for glibc versions before 2.17).

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       clock_getres(), clock_gettime(), clock_settime():
              _POSIX_C_SOURCE >= 199309L

I compile and link with the following options:

CFLAGS = -g -Wall -Wextra -Werror -O3 -std=c99 -include $(PROJ_SETTINGS_INC) -lrt

, and PROJ_SETTINGS_INC is set to an h file that contains settings.

So far, no problem.

Now I modify my settings file and use uint16_t, so I include <stdint.h> in the settings h file.

The compiler now complains that clock_gettime() is an implicit declaration.

If I change back my settings file to use int instead of uint16_t, and remove the include to <stdint.h>, then compiling works again.

Why does including <stdint.h> in my settings h file break compiling with clock_gettime()?

My best guess is that stdint redefines the POSIX define, but that does not make sense to me, since the -include directive works as if the include was done in the first line of the source.


Here is an example (in the light of John Bollinger's answer, I start to understand what is going wrong, but I thought I'd write this anyway).

bar.h:

#include <stdint.h>

struct s {
    uint16_t a;
};

foo.c

#define _POSIX_C_SOURCE (199309L)

#include <stdio.h>
#include <time.h>

int main(void)
{
    struct s s;
    struct timespec now;

    s.a = 42;
    clock_gettime(CLOCK_REALTIME, &now);

    printf("answer: %d, time: %lld\n", s.a, (long long) now.tv_sec);

    return 0;
}

Build with:

gcc foo.c -include bar.h

Strangely, this gives a useful warning. In my original application I only got the implicit declaration error.

foo.c:1:0: warning: "_POSIX_C_SOURCE" redefined [enabled by default]
 #define _POSIX_C_SOURCE (199309L)
 ^
In file included from /usr/include/stdint.h:25:0,
                 from /usr/lib/gcc/x86_64-linux-gnu/4.8/include/stdint.h:9,
                 from ./bar.h:1,
                 from <command-line>:1:
/usr/include/features.h:230:0: note: this is the location of the previous definition
 # define _POSIX_C_SOURCE 200809L

Solution

  • As rici pointed out, POSIX requires that (re)definitions of any of the feature test macros it specifies precede inclusion of any of the headers it specifies, including by other header files, else behavior is undefined. The _POSIX_C_SOURCE macro is such a feature-test macro, and stdint.h and time.h are both such headers.

    In your particular case, the GNU C library's stdint.h header and many others rely on a common internal header (features.h) that checks which features are explicitly enabled and sets all the feature macros to consistent values (inasmuch as that is possible). It may both check the value of the _POSIX_C_SOURCE macro and set it if it was not already set. It uses a standard guard macro to avoid being processed multiple times. Therefore, if a feature macro is redefined later then you risk inconsistent feature definitions.

    I have not traced down the exact chain of definitions and redefinitions that causes clock_gettime() to fail to be declared in your particular case (and indeed, you have not presented enough information for me to do so), but if you are going to define feature macros then you should ensure that all headers see those definitions. They should appear in your source file before any #include directives, and you should avoid otherwise causing any system headers to be preprocessed before those definitions (e.g. via a directive in the file named in your -include option).

    Note, too, that although we're discussing implementation details, there is no reason to suppose that GNU's implementation is unusual in this respect. Others will be implemented somewhat differently, but it is always wise to ensure that all headers see a consistent set of definitions for any macros that affect them. Even for macros that are not specified by POSIX.