C++编译并链接到指向未定义函数的指针

C++ compiles and links with pointer to undefined function

本文关键字:未定义 函数 指针 编译 链接 C++      更新时间:2023-10-16

此代码:

void undefined_fcn();
void defined_fcn() {}
struct api_t {
void (*first)();
void (*second)();
};
api_t api = {undefined_fcn, defined_fcn};

定义全局变量api,其中包含指向不存在函数的指针。然而,令我惊讶的是,它编译的链接绝对没有来自 GCC 的投诉,即使所有这些-Wall -Wextra -Werror -pedantic标志也是如此。

此代码是共享库的一部分。只有当我在运行时加载库时,它才会最终失败。如何在库链接时检查我是否忘记定义任何函数?


更新:这个问题提到了同样的问题,答案是一样的:-Wl,--no-undefined。(顺便说一句,我想这甚至可以标记为重复)。但是,根据下面接受的答案,使用时应小心-Wl,--no-undefined.

此代码是共享库的一部分。

这才是关键。拥有共享库的全部目的是拥有一个"不完整"的共享对象,其中包含未定义的符号,当主可执行文件加载它以及与之链接的所有其他共享库时,必须解析这些符号。此时,运行时加载程序尝试解析所有未定义的符号;并且必须解析所有未定义的符号,否则可执行文件将无法启动。

你说你正在使用gcc,所以你可能正在使用GNUld。由于上述原因,ld将使用未定义的符号链接共享库,但将无法链接可执行文件,除非针对可执行文件链接的共享库解析所有未定义的符号。因此,在运行时,预期的行为是运行时加载程序也应成功解析所有符号;因此,运行时加载程序无法启动可执行文件的唯一情况将指示致命的运行时环境故障(例如共享库被替换为不兼容的版本)。

有一些选项可用于覆盖此行为。--no-undefined选项指示ld在链接共享库时报告未定义符号的链接失败,就像可执行文件一样。当通过gcc间接调用ld时,这变得-Wl,--no-undefined

但是,您可能会发现这将是一个失败的命题。您最好希望共享库中的任何代码都不使用标准 C++ 或 C 库中的任何类。因为,你猜怎么着?-- 这些引用将是未定义的符号,您将无法链接您的共享库!

换句话说,这是你需要处理的必要邪恶。

你不能让编译器告诉你是否忘记在该实现文件中定义函数。 原因是当你定义一个函数时,它在C++中隐式标记为extern。 而且在链接共享库之前,您无法判断共享库中的内容(编译器的链接器不知道是否定义了引用)

如果您不熟悉extern的含义。 标记extern的东西表示外部链接,因此,如果您有一个extern变量,编译器不需要将该变量的定义放在使用它的翻译单元中。 定义可以位于另一个实现文件中,引用在链接时解析(当您与定义变量的翻译单元链接时)。 这同样适用于函数,函数本质上是函数类型的变量。

要获得您想要的行为,请创建函数static,它告诉编译器该函数未extern并且是当前翻译单元的一部分,在这种情况下,必须定义它-Wundefined-internal接受此(-Wundefined-internal-Werror的一部分,所以只需使用它进行编译)