最近我一直在尝试阅读更多开源C代码。我在业余爱好项目中采用的一种常见模式如下: -
在我的C文件中,我有静态或导出的函数。只有导出的函数才会放在头文件中。仅在对象范围内使用的全局变量也用作静态全局变量。
我的问题涉及在头文件中使用“静态内联”函数的有用性和动机。从我在线阅读的内容来看,不使用static关键字会导致多重定义错误,这就是不仅仅将函数定义为“内联”的原因。
但是,这是否意味着导出此函数以供其他对象使用? 如果是,那么为什么不在C文件中定义此函数并通过头文件导出它? 如果不是,为什么要将它放在头文件中,而不是只将它放在C文件中?
这种编码风格背后有原因吗?我错过了什么?
可以在hashmap.h
:
/*
* 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;
}
答案 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
函数都是静态的)