加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 链接静态库时内联函数的多重定义

发布时间:2020-12-16 03:06:00 所属栏目:百科 来源:网络整理
导读:我有一个C程序,我用mingw( gcc for Windows)编译.使用mingw的TDM版本,其中包括gcc 4.4.1.可执行文件链接到两个静态库(.a)文件:它们是以C编写的第三方库;另一个是我写的一个C库,它使用C库提供了我自己的C API. C(在我看来,过多的)部分C库的功能是在内联函数
我有一个C程序,我用mingw( gcc for Windows)编译.使用mingw的TDM版本,其中包括gcc 4.4.1.可执行文件链接到两个静态库(.a)文件:它们是以C编写的第三方库;另一个是我写的一个C库,它使用C库提供了我自己的C API.

C(在我看来,过多的)部分C库的功能是在内联函数中实现的.当您使用C库的API时,您不能避免包含内联函数,但是当我尝试将它们链接在一起时,我会收到链接错误,指出所有内联函数都有多重定义 – 我有两个在我的C包装库和我没有的基础上调用,基本上在标题中内联的任何东西都已经在C库和C库中创建了一个函数.

当同一项目中的不同的.c或.cpp文件中使用多个包含文件时,不会导致多个定义错误.问题在于每个库会生成一个定义.

如何/为什么编译器为这两个库中的这些内联函数生成函数和符号?如何强制它在我的代码中停止生成它们?有没有一个可以运行的工具来从.a文件中删除重复的功能,还是使链接器忽略多个定义的方法?

(FYI,第三方图书馆在其所有标题中包含#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 -safe:Multiple definition of lots of std:: functions when linking

这只是想知道为什么违反一个定义规则不是错误:unpredictable behavior of Inline functions with different definitions

解决方法

首先,您必须了解C99内联模型 – 也许您的标题有问题.对于具有外部(非静态)连接的内联函数,有两种定义

>外部定义
一个函数的这个定义只能在整个程序中在指定的TU中出现一次.它提供了可以从其他TU使用的导出功能.
>内联定义
这些出现在每个TU中,其中声明为单独的定义.定义不需要彼此相同或外部定义相同.如果在库中使用内部,则可以省略对外部定义中否则将执行的函数参数的检查.

函数的每个定义都有自己的本地静态变量,因为它们的本地声明没有链接(它们不像C那样共享).非静态内联函数的定义将是一个内联定义

> TU中的每个函数声明都包含说明符inline和
> TU中没有函数声明包含说明符extern.

否则,必须出现在该TU中的定义(因为内联函数必须在声明的同一TU中定义)是外部定义.在调用内联函数时,是否使用外部定义或内联定义是未指定的.然而,由于在所有情况下定义的函数仍然相同(因为它具有外部链接),所以它的地址在所有情况下都是相等的,无论有多少内联定义出现.因此,如果您使用函数的地址,编译器很可能会解析为外部定义(特别是如果优化被禁用).

一个示例显示错误使用内联,因为它在两个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 definitionn");
}

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.cn");
}

// 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.cn");
}

void g(void) {
  f();
}

main2.c

#include <stdio.h>

// external definition!
extern inline void f(void);

inline void f(void) {
  printf("external defn");
}


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 TUn");
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读