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

2023-12-27

我有一个用 mingw (gcc for Windows)编译的 C++ 程序。使用 mingw 的 TDM 版本,其中包括 gcc 4.4.1。可执行文件链接到两个静态库(.a)文件:其中一个是用 C 编写的第三方库;另一个是我编写的 C++ 库,它使用 C 库在上面提供了我自己的 C++ API。

C 库的功能的一部分(在我看来,过多的)是在内联函数中实现的。当您使用 C 库的 API 时,您无法避免包含内联函数,但是当我尝试将它们全部链接在一起时,我收到链接错误,指出所有内联函数都有多个定义 - 这两个函数我都有在我的 C++ 包装器库中调用的以及我没有调用的库,基本上标头中内联定义的任何内容都已在 C 库和 C++ 库中为其创建了一个函数。

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

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

(仅供参考,第三方库确实在其所有标头中包含 #ifdef __cplusplus 和 extern "C" 保护;无论如何,如果这是问题,它不会导致符号的多重定义,它会导致相反的问题,因为符号会未定义或至少不同。)

值得注意的是,如果我链接到第三方 C 库的 DLL,则不会发生链接错误;然而,后来我遇到了奇怪的运行时故障,这似乎与我的代码有自己的函数版本有关,它应该从 DLL 调用。 (好像编译器正在创建我没有要求的函数的本地版本。)

以前曾问过这个问题的类似版本,但是,我在以下任何一个中都没有找到我的情况的答案:

这个问题的答案是海报具有多重定义变量,我的问题是内联函数的多重定义:由于在多个 cpp 中包含相同标头而导致重复的多个定义错误 https://stackoverflow.com/questions/223771/repeated-multiple-definition-errors-from-including-same-header-in-multiple-cpps

这是一个 MSVC 程序,但我使用的是 mingw;另外,发帖者在这个问题中的问题是头文件中类主体之外的 C++ 类构造函数的定义,而我的问题是内联的 C 函数:静态库多重定义问题 https://stackoverflow.com/questions/717622/static-lib-multiple-definition-problem

这个傻瓜将他所有的 C 代码重命名为 C++ 文件,并且他的 C 代码不是 C++ 安全的:链接时大量 std:: 函数的多重定义 https://stackoverflow.com/questions/2208834/multiple-definition-of-lots-of-std-functions-when-linking

这个只是想知道为什么违反单一定义规则不是错误:具有不同定义的内联函数的不可预测行为 https://stackoverflow.com/questions/2025380/unpredictable-behavior-of-inline-functions-with-different-definitions


首先,您必须了解 C99 内联模型 - 也许您的标头有问题。具有外部(非静态)链接的内联函数有两种定义

  • 外部定义
    函数的这个定义在整个程序中只能在指定的 TU 中出现一次。它提供了可以从其他 TU 使用的导出函数。

  • 内联定义
    这些出现在作为单独定义声明的每个 TU 中。定义确实not需要彼此相同或与外部定义相同。如果在库内部使用,它们可以省略对函数参数的检查,否则这些检查将在外部定义中完成。

函数的每个定义都有自己的局部静态变量,因为它们的本地声明没有链接(它们不像 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");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

链接静态库时内联函数的多重定义 的相关文章

  • 数据模板绑定垃圾邮件输出窗口出现错误:找不到管理 FrameworkElemen

    我有问题 System Windows Data 错误 2 找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement BindingExpression 无路径 数据项 空 目标元素是 So
  • 通过 SocketCAN 进行 boost::asio

    我正在考虑利用升压阿西奥 http www boost org doc libs 1 49 0 doc html boost asio html从a读取数据套接字CAN http en wikipedia org wiki SocketCA
  • 有没有快速创建集合的方法?

    目前我正在创建一个像这样的新集 std set a s s insert a1 s insert a2 s insert a3 s insert a10 有没有办法创建s在一行 int myints 10 20 30 40 50 std s
  • 我如何理解这个 C 类型声明?

    double bar int double double double double 在查看讲座幻灯片时 我发现了留给学生的练习 用简单的英语来说 什么是类型bar在这个 C 声明中 Please帮助我解决这个问题 我什至不知道从哪里开始
  • 使用 Enumerable.OfType() 或 LINQ 查找特定类型的所有子控件

    Existed MyControl1 Controls OfType
  • 更改 Qt OpenGL 窗口示例以使用 OpenGL 3.3

    我正在尝试更改 Qt OpenGL 示例以使用更现代的 opengl 版本 330 似乎合适 所以我做了 在 main cpp 上设置版本和配置文件 设置着色器版本 更改着色器以使用统一 它现在构建没有任何错误 但我只看到一个空白窗口 我错
  • 指向特征矩阵的指针数组

    我在代码中使用 Eigen 的 MatrixXd 矩阵 在某个时刻我需要一个 3D 矩阵 由于 Eigen 没有三维矩阵类型 因为它仅针对线性代数进行了优化 因此我创建了一个 MatrixXd 类型的指针数组 Eigen MatrixXd
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • vs2008 c#:Facebook.rest.api如何使用它来获取好友列表?

    如何在此基础上取得进一步的进步 获取好友列表的下一步是什么 string APIKey ConfigurationManager AppSettings API Key string APISecret ConfigurationManag
  • 在 JSQMessagesViewController 中显示 LocationMediaItem

    我刚刚尝试实施LocationMediaItem in my Xamarin iOS应用程序使用JSQMessagesViewController 一切都很顺利 唯一的问题是UICollectionView应该显示位置的单元格永远停留在加载
  • 从 WebBrowser 控件 C# 获取滚动值

    我试图在 WebBrowser 控件中获取网页的 Y 滚动索引 但无法访问内置滚动条的值 有任何想法吗 对于标准模式下的 IE 使用文档类型 正如你所说 scrollTop是的财产元素 而不是 HtmlDocument htmlDoc th
  • 如何在服务器端按钮点击时关闭当前标签页?

    我尝试在确认后关闭当前选项卡 因此我将以下代码放在确认按钮的末尾 但选项卡没有关闭 string jScript ClientScript RegisterClientScriptBlock this GetType keyClientBl
  • 运行选定的代码生成器时出错:“未将对象引用设置到对象的实例。”错误?

    我已经尝试了所有解决方案 例如修复 VS 2013 但没有用 当您通过右键单击控制器文件夹来创建控制器并添加控制器时 然后右键单击新创建的控制器的操作并选择添加视图 当我尝试创建视图时 就会发生这种情况 它不是一个新项目 而是一个现有项目
  • 在 EnvDTE 中调试时捕获 VS 局部变量

    是否可以使用 EnvDTE 进行 vsix Visual Studio 扩展来捕获本地和调试窗口使用的调试数据 或者可以通过其他方法吗 我想创建一个自定义的本地窗口 我们可以修改它以根据需要显示一些较重的内容 而无需为高级用户牺牲原始的本地
  • .NET Core 中的跨平台文件名处理

    如何处理文件名System IO以跨平台方式运行类以使其在 Windows 和 Linux 上运行 例如 我编写的代码在 Windows 上完美运行 但它不会在 Ubuntu Linux 上创建文件 var tempFilename Dat
  • IEnumerable.Except 不起作用,那么我该怎么办?

    我有一个 linq to sql 数据库 非常简单 我们有 3 个表 项目和用户 有一个名为 User Projects 的连接表将它们连接在一起 我已经有了一个获得的工作方法IEnumberable
  • 每个数据库多个/单个 *.edmx 文件

    我有一个通过 ADO net 数据服务与数据库交互的项目 数据库很大 近 150 个具有依赖关系的表 该项目几年前开始 当时使用的是数据集 现在我们正在转向实体模型关系 由于我们添加了更多需要使用的表 该模型正在不断增长 这是管理这一切的正
  • 更改 Windows Phone 系统托盘颜色

    有没有办法将 Windows Phone 上的系统托盘颜色从黑色更改为白色 我的应用程序有白色背景 所以我希望系统托盘也是白色的 您可以在页面 XAML 中执行此操作
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 使我的 COM 程序集调用异步

    我刚刚 赢得 了在当前工作中维护用 C 编码的遗留库的特权 这个dll 公开使用 Uniface 构建的大型遗留系统的方法 除了调用 COM 对象之外别无选择 充当此遗留系统与另一个系统的 API 之间的链接 在某些情况下 使用 WinFo

随机推荐