首先,您必须了解 C99 内联模型 - 也许您的标头有问题。具有外部(非静态)链接的内联函数有两种定义
函数的每个定义都有自己的局部静态变量,因为它们的本地声明没有链接(它们不像 C++ 那样共享)。非静态内联函数的定义将是内联定义,如果
- TU 中的每个函数声明都包含说明符
inline
, and
- TU 中的函数声明不包含说明符
extern
.
否则,必须出现在该 TU 中的定义(因为内联函数必须在声明的同一 TU 中定义)是外部定义。在调用内联函数时未指定是使用外部定义还是内联定义。但是,由于在所有情况下定义的函数仍然相同(因为它具有外部链接),因此无论出现多少个内联定义,它的地址在所有情况下都比较相等。因此,如果您获取函数的地址,编译器很可能会解析为外部定义(特别是在禁用优化的情况下)。
一个展示错误使用的例子inline
,因为它在两个TU中包含两次函数的外部定义,导致多重定义错误
// included into two TUs
void f(void); // no inline specifier
inline void f(void) { }
下面的程序是危险的,因为编译器可以自由使用外部定义,但该程序没有提供
// main.c, only TU of the program
inline void g(void) {
printf("inline definition\n");
}
int main(void) {
g(); // could use external definition!
}
我使用 GCC 制作了一些测试用例,进一步演示了该机制:
main.c
#include <stdio.h>
inline void f(void);
// inline definition of 'f'
inline void f(void) {
printf("inline def main.c\n");
}
// defined in TU of second inline definition
void g(void);
// defined in TU of external definition
void h(void);
int main(void) {
// unspecified whether external definition is used!
f();
g();
h();
// will probably use external definition. But since we won't compare
// the address taken, the compiler can still use the inline definition.
// To prevent it, i tried and succeeded using "volatile".
void (*volatile fp)() = &f;
fp();
return 0;
}
main1.c
#include <stdio.h>
inline void f(void);
// inline definition of 'f'
inline void f(void) {
printf("inline def main1.c\n");
}
void g(void) {
f();
}
main2.c
#include <stdio.h>
// external definition!
extern inline void f(void);
inline void f(void) {
printf("external def\n");
}
void h(void) {
f(); // calls external def
}
现在,程序输出了我们所期望的结果!
$ gcc -std=c99 -O2 main.c main1.c main2.c
inline def main.c
inline def main1.c
external def
external def
查看符号表,我们将看到内联定义的符号没有导出(来自main1.o
),同时导出外部定义(来自main2.o
).
现在,如果您的静态库每个都有其内联函数的外部定义(正如它们应该的那样),它们自然会相互冲突。解决方案是将内联函数设为静态或仅重命名它们。这些将始终提供外部定义(因此它们是成熟的定义),但它们不会导出,因为它们具有内部链接,因此不会发生冲突
static inline void f(void) {
printf("i'm unique in every TU\n");
}