【问题标题】:Include of stdint.h in a header file breaks compiling with clock_gettime()在头文件中包含 stdint.h 会中断使用 clock_gettime() 进行编译
【发布时间】:2015-02-17 15:24:06
【问题描述】:

我的 C 文件使用clock_gettime()。为此,它包括 <time.h> 并将 _POSIX_C_SOURCE 定义为 (200112L),根据手册页:

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

我使用以下选项进行编译和链接:

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

PROJ_SETTINGS_INC 设置为包含设置的 h 文件。

到目前为止,没问题。

现在我修改我的设置文件并使用uint16_t,所以我在设置h 文件中包含&lt;stdint.h&gt;

编译器现在抱怨clock_gettime() 是一个隐式声明。

如果我将我的设置文件改回使用int 而不是uint16_t,并将包含删除到&lt;stdint.h&gt;,然后再次编译。

为什么在我的设置 h 文件中包含 &lt;stdint.h&gt; 会破坏使用 clock_gettime() 进行编译?

我的最佳猜测是 stdint 重新定义了 POSIX 定义,但这对我来说没有意义,因为 -include 指令的工作方式就像在源代码的第一行中完成了包含一样。


这是一个例子(根据 John Bollinger 的回答,我开始明白出了什么问题,但我想我还是会写这个)。

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;
}

构建:

gcc foo.c -include bar.h

奇怪的是,这给出了一个有用的警告。在我的原始应用程序中,我只得到了隐式声明错误。

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

【问题讨论】:

  • 可能需要将#define 放在all #include 之前。
  • 查看预处理器输出会有所帮助 - stackoverflow.com/questions/19443520/…
  • 可以显示$(PROJ_SETTINGS_INC)文件的内容吗?
  • 一个最小的工作示例会有所帮助。我无法通过提供的信息重现该问题。
  • 你到底在哪里定义_POSIX_C_SOURCE?

标签: c gcc posix stdint


【解决方案1】:

正如 rici 指出的,POSIX 要求(重新)定义它指定的任何功能测试宏,然后才能包含它指定的任何头文件,包括其他头文件,否则行为未定义。 _POSIX_C_SOURCE 宏就是这样一个功能测试宏,stdint.htime.h 都是这样的标头。

在您的特定情况下,GNU C 库的 stdint.h 标头和许多其他标头依赖于一个通用的内部标头 (features.h),它检查哪些功能已明确启用并将所有功能宏设置为一致的值(因为是可能的)。它既可以检查_POSIX_C_SOURCE 宏的值,也可以设置它(如果尚未设置)。它使用标准的保护宏来避免被多次处理。因此,如果稍后重新定义功能宏,您将面临功能定义不一致的风险。

我没有找到导致clock_gettime() 在您的特定情况下无法声明的确切定义和重新定义链(实际上,您没有提供足够的信息让我这样做),但如果您是要定义功能宏,那么您应该确保 all 标题看到这些定义。它们应该出现在您的源文件中任何 #include 指令之前,并且您应该避免以其他方式导致在这些定义之前对任何系统标头进行预处理(例如,通过在您的 -include 选项中命名的文件中的指令)。

还要注意,尽管我们正在讨论实现细节,但没有理由认为 GNU 的实现在这方面是不寻常的。其他的实现方式会有所不同,但总是明智的做法是确保所有标题都看到影响它们的任何宏的一致定义集。即使是 POSIX 未指定的宏。

【讨论】:

  • 这不仅仅是可取的。这是强制性的。在 Posix 指定的标头(包括标准库标头)的 #include 之后定义 Posix 功能测试宏是未定义的行为。见pubs.opengroup.org/onlinepubs/9699919799/functions/…第三段
  • 谢谢,@rici。我已经更新了我的答案,以引起对 POSIX 规范的特别关注,并将其余答案放在该上下文中。
  • 是的,正确!看起来您不需要更多信息来查明问题,但我还是用代码编辑了我的问题,谢谢。作为后续,我不确定使用clock_gettime 的最佳方式是什么。我仍然想从构建命令中包含一个文件(以便能够使用不同的文件构建,使用PROJ_SETTINGS_INC=my_file.h make),但是如果这个文件需要stdint.h,我就有麻烦了。对此的通用解决方案是什么,这必须经常发生?我应该在CFLAGS 中用-D 定义_POSIX_C_SOURCE 吗?
  • @Gauthier:我发现将 _POSIX_C_SOURCE 定义放在我的 CFLAGS 中最容易,因为这样可以保证它在任何包含之前定义;通过将它添加到我的 makefile 中,我避免了再次考虑它。除非您依赖某些已弃用的行为,否则没有理由不使用功能测试宏。
  • @Gauthier:如果您的源代码的正确编译取决于标准功能测试宏的特定值,那么我不明白您为什么要在 in 以外的任何地方定义该宏您的来源。方便的问题是次要的;宏定义的放置是正确性的问题。
猜你喜欢
  • 2012-01-04
  • 2012-05-15
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多