我有一个用mingw编译的C ++程序(用于Windows的gcc)。使用包含gcc 4.4.1的mingw的TDM版本。可执行文件链接到两个静态库(.a)文件:它们是用C编写的第三方库;另一个是由我编写的C ++库,它使用C库提供我自己的C ++ API。
C库的功能部分(在我看来,过多)部分是在内联函数中实现的。当你使用C库的API时,你无法避免包含内联函数,但当我尝试将它们全部链接在一起时,我会收到链接错误,说明所有内联函数都有多个定义 - 我都有在我的C ++包装器库中调用了一些我没有的东西,基本上在头文件中内联定义的东西都有一个在C库和C ++库中为它创建的函数。
在同一项目中的不同.c或.cpp文件中多次使用包含文件时,不会导致多个定义错误;问题只是它为每个库生成一个定义。
编译器如何/为什么在两个库中为这些内联函数生成函数和符号?如何强制它停止在我的代码中生成它们?我可以运行一个工具来从.a文件中删除重复的函数,还是一种让链接器忽略多个定义的方法?
(仅供参考,第三方库在其所有标题中都包含#ifdef __cplusplus和extern“C”警卫;无论如何,如果这是问题,它不会导致符号的多重定义,这会导致相反的问题,因为符号将是未定义的或至少是不同的。)
值得注意的是,如果我链接到第三方C库的DLL,则不会发生链接错误;然而,我得到奇怪的运行时故障似乎与我的代码有关,它应该从DLL调用自己的函数版本。 (好像编译器正在创建我没有要求的本地版本的函数。)
之前已经问过这个问题的类似版本,但是,我没有找到任何这些问题的答案:
这个问题的答案是海报多次定义变量,我的问题是内联函数的多重定义:Repeated Multiple Definition Errors from including same header in multiple cpps
这是一个MSVC计划,但我正在使用mingw;另外,海报在这个问题中的问题是在标题中类主体之外定义了一个C ++类构造函数,而我的问题是内联的C函数:Static Lib Multiple Definition Problem
这个傻瓜将他的所有C代码重命名为C ++文件,而他的C代码不是C ++ - 安全:Multiple definition of lots of std:: functions when linking
这个只是想知道为什么违反一个定义规则不是错误:unpredictable behavior of Inline functions with different definitions
答案 0 :(得分:14)
首先,您必须了解C99内联模型 - 您的标头可能有问题。内联函数有两种定义,带有外部(非静态)链接
外部定义
这个函数的定义只能在整个程序中,在指定的TU中出现一次。它提供了可以从其他TU使用的导出功能。
内联定义
它们出现在每个TU中,它们被声明为一个单独的定义。 not 的定义必须彼此相同或与外部定义相同。如果在库中使用内部函数,则可以省略对函数参数的检查,否则这些参数将在外部定义中完成。
函数的每个定义都有自己的局部静态变量,因为它们的本地声明没有链接(它们不像C ++那样共享)。如果
,非静态内联函数的定义将是内联定义inline
和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");
}