C++ 使用指向未定义函数的指针进行编译和链接
C++ compiles and links with pointer to undefined function
此代码:
void undefined_fcn();
void defined_fcn() {}
struct api_t {
void (*first)();
void (*second)();
};
api_t api = {undefined_fcn, defined_fcn};
定义了一个全局变量api
,并带有一个指向不存在的函数的指针。然而,令我惊讶的是,它编译 links 完全没有来自 GCC 的抱怨,即使有所有这些 -Wall -Wextra -Werror -pedantic
标志。
此代码是共享库的一部分。只有当我在 运行 加载库时,它才最终失败。我如何在库 link 时检查我没有忘记定义任何函数?
更新:this question提到了同样的问题,答案是一样的:-Wl,--no-undefined
。 (顺便说一句,我想这甚至可以标记为重复)。但是,根据下面接受的答案,使用 -Wl,--no-undefined
.
时应小心
您不能让编译器告诉您是否忘记在该实现文件中定义该函数。原因是当你定义一个函数时,它在 C++ 中被隐式标记为 extern
。在共享库被 linked 之前,你无法分辨共享库中的内容(编译器的 linker 不知道引用是否已定义)
如果您不熟悉 extern
的含义。标记为 extern
的东西表示外部 linkage,因此如果您有一个 extern
的变量,编译器不需要在使用它的翻译单元中定义该变量。该定义可以在另一个实现文件中,并且在 link 时解析引用(当您 link 使用定义变量的翻译单元时)。这同样适用于函数,函数本质上是函数类型的变量。
为了获得你想要的行为,使函数 static
告诉编译器该函数不是 extern
并且是当前翻译单元的一部分,在这种情况下必须定义它-Wundefined-internal
接受这个(-Wundefined-internal
是 -Werror
的一部分,所以只用那个编译)
This code is part of a shared library.
这是关键。拥有共享库的全部目的是拥有一个 "incomplete" 共享对象,其中包含在主可执行文件加载它时必须解析的未定义符号以及它获得 link 编辑的所有其他共享库。那时,运行时加载程序会尝试解析所有未定义的符号;并且必须解析所有未定义的符号,否则可执行文件将无法启动。
您说您正在使用 gcc
,因此您可能正在使用 GNU ld
。由于上述原因,ld
将 link 具有未定义符号的共享库,但将无法 link 可执行文件,除非针对可执行文件获取的共享库解析所有未定义符号 link编辑。因此,在运行时,预期的行为是运行时加载程序也有望成功解析所有符号;因此,运行时加载程序无法启动可执行文件的唯一情况将指示致命的运行时环境故障(例如共享库被不兼容的版本替换)。
有一些选项可用于覆盖此行为。 --no-undefined
选项指示 ld
在 link 共享库时报告未定义符号的 link 失败,就像可执行文件一样。当通过 gcc
间接调用 ld
时,这变成 -Wl,--no-undefined
.
但是,您可能会发现这将是一个失败的提议。您最好希望共享库中的 none 代码使用标准 C++ 或 C 库中的 any class .因为,你猜怎么着? -- 这些引用将是未定义的符号,您将无法 link 您的共享库!
换句话说,这是你需要处理的必要之恶。
此代码:
void undefined_fcn();
void defined_fcn() {}
struct api_t {
void (*first)();
void (*second)();
};
api_t api = {undefined_fcn, defined_fcn};
定义了一个全局变量api
,并带有一个指向不存在的函数的指针。然而,令我惊讶的是,它编译 links 完全没有来自 GCC 的抱怨,即使有所有这些 -Wall -Wextra -Werror -pedantic
标志。
此代码是共享库的一部分。只有当我在 运行 加载库时,它才最终失败。我如何在库 link 时检查我没有忘记定义任何函数?
更新:this question提到了同样的问题,答案是一样的:-Wl,--no-undefined
。 (顺便说一句,我想这甚至可以标记为重复)。但是,根据下面接受的答案,使用 -Wl,--no-undefined
.
您不能让编译器告诉您是否忘记在该实现文件中定义该函数。原因是当你定义一个函数时,它在 C++ 中被隐式标记为 extern
。在共享库被 linked 之前,你无法分辨共享库中的内容(编译器的 linker 不知道引用是否已定义)
如果您不熟悉 extern
的含义。标记为 extern
的东西表示外部 linkage,因此如果您有一个 extern
的变量,编译器不需要在使用它的翻译单元中定义该变量。该定义可以在另一个实现文件中,并且在 link 时解析引用(当您 link 使用定义变量的翻译单元时)。这同样适用于函数,函数本质上是函数类型的变量。
为了获得你想要的行为,使函数 static
告诉编译器该函数不是 extern
并且是当前翻译单元的一部分,在这种情况下必须定义它-Wundefined-internal
接受这个(-Wundefined-internal
是 -Werror
的一部分,所以只用那个编译)
This code is part of a shared library.
这是关键。拥有共享库的全部目的是拥有一个 "incomplete" 共享对象,其中包含在主可执行文件加载它时必须解析的未定义符号以及它获得 link 编辑的所有其他共享库。那时,运行时加载程序会尝试解析所有未定义的符号;并且必须解析所有未定义的符号,否则可执行文件将无法启动。
您说您正在使用 gcc
,因此您可能正在使用 GNU ld
。由于上述原因,ld
将 link 具有未定义符号的共享库,但将无法 link 可执行文件,除非针对可执行文件获取的共享库解析所有未定义符号 link编辑。因此,在运行时,预期的行为是运行时加载程序也有望成功解析所有符号;因此,运行时加载程序无法启动可执行文件的唯一情况将指示致命的运行时环境故障(例如共享库被不兼容的版本替换)。
有一些选项可用于覆盖此行为。 --no-undefined
选项指示 ld
在 link 共享库时报告未定义符号的 link 失败,就像可执行文件一样。当通过 gcc
间接调用 ld
时,这变成 -Wl,--no-undefined
.
但是,您可能会发现这将是一个失败的提议。您最好希望共享库中的 none 代码使用标准 C++ 或 C 库中的 any class .因为,你猜怎么着? -- 这些引用将是未定义的符号,您将无法 link 您的共享库!
换句话说,这是你需要处理的必要之恶。