头文件中的静态内联函数

时间:2017-12-14 18:14:20

标签: c

最近我一直在尝试阅读更多开源C代码。我在业余爱好项目中采用的一种常见模式如下: -

在我的C文件中,我有静态或导出的函数。只有导出的函数才会放在头文件中。仅在对象范围内使用的全局变量也用作静态全局变量。

我的问题涉及在头文件中使用“静态内联”函数的有用性和动机。从我在线阅读的内容来看,不使用static关键字会导致多重定义错误,这就是不仅仅将函数定义为“内联”的原因。

但是,这是否意味着导出此函数以供其他对象使用? 如果是,那么为什么不在C文件中定义此函数并通过头文件导出它? 如果不是,为什么要将它放在头文件中,而不是只将它放在C文件中?

这种编码风格背后有原因吗?我错过了什么?

可以在hashmap.h

中的git代码库中找到一个这样的示例
/*
 * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
 * for use in hash tables. Cryptographic hashes are supposed to have
 * uniform distribution, so in contrast to `memhash()`, this just copies
 * the first `sizeof(int)` bytes without shuffling any bits. Note that
 * the results will be different on big-endian and little-endian
 * platforms, so they should not be stored or transferred over the net.
 */
static inline unsigned int sha1hash(const unsigned char *sha1)
{
    /*
     * Equivalent to 'return *(unsigned int *)sha1;', but safe on
     * platforms that don't support unaligned reads.
     */
    unsigned int hash;
    memcpy(&hash, sha1, sizeof(hash));
    return hash;
}

1 个答案:

答案 0 :(得分:13)

实际上,static inline函数可能可能(但不确定)由一些优秀的优化编译器inlined(例如GCC在其大多数呼叫站点都有-O2)。

它在头文件中定义,因为它可以在大多数调用站点(可能都是所有这些站点)内联。如果它只是声明(并简单地"导出"),则内联不太可能发生(除非您使用link-time optimizations编译并链接 ,也就是LTO,例如,编译和链接gcc -flto -O2,并且增加很多构建时间。)

实际上,编译器需要知道函数的主体才能内联它。所以一个合适的地方是在一些公共头文件中定义它(否则,它可以仅在定义它的同一个翻译单元中内联,除非你启用LTO),这样每个翻译单元都会知道该无限函数的主体。 / p>

如果编译器没有内联它(例如,当你使用它的地址时),则声明static以避免多个定义(在链接时)。

实际上,在C99或C11代码中(除了LTO,我很少使用),我总是把我想要的短函数作为static inline定义在公共头文件中。

请务必了解C preprocessor的工作方式和时间。请注意,您原则上可以(但实践和恶心的风格)会避免在公共头文件中定义一些static inline函数,而是将其相同的定义复制并粘贴到多个.c文件中。 (但是,这可能对生成的 .c文件有意义,例如,如果您设计compiler emitting C code)。

FYI LTO实际上是由最近的GCC编译器实现的,它通过在目标文件中嵌入一些内部编译器表示(一些GIMPLE),并重做一些"编译"步骤 - using the lto1 frontend - at" link"时间。在实践中,整个程序几乎被编译了两次"。

(实际上,我总是想知道为什么C标准化委员会没有决定所有明确的inline函数都是静态的)