C / C ++未使用的内联函数未定义的引用

考虑下面的代码(这不是pthread特定的;其他的例子,例如涉及实时库的那些,performance出类似的行为):

#define _GNU_SOURCE #include <pthread.h> inline void foo() { static cpu_set_t cpuset; pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); } int main(int argc, char *argv[]) { } 

这是C和C ++中的有效程序。 所以我把这个内容保存到testc.ctestcpp.cpp并尝试构build

当我用C++我没有遇到任何错误。 当我build立在C我得到一个未定义的引用错误。 现在,这个错误发生在-O1-O3 。 是否有指示gcc做正确的事情(请参阅foo未使用,并跳过对pthread_setaffinity_np定义的要求)?

编辑:我认为这是显而易见的上下文,但错误信息是:

 /tmp/ccgARGVJ.o: In function `foo': testc.c:(.text+0x17): undefined reference to `pthread_setaffinity_np' 

请注意,因为foo没有在主path中被引用,所以g++完全忽略了这个函数,但gcc却没有。

编辑2:让我再试一次。 函数foo ,以及对pthread_setaffinity_np的后续调用未使用。 主要function是空的。 只要看看它! 不知何故, g++发现foo不需要被包含,随后当我们故意省略-lpthread (并且用nm检查导出的符号确认foopthread_setaffinity_np都不需要引用)时,构build过程并没有发生。 gcc所得到的输出结果并不符合这个事实。

我在问这个问题,因为C ++和C前端在相同的input上似乎给出了不同的结果。 这似乎并不是一个初步问题,因为我希望这两个path都给出相同的链接错误,这就是为什么我强调这似乎是一个编译器问题。 如果C ++和C都给出了问题,那么是的,我会同意它的一个链接问题。

那么,显然你的程序包含一个错误:你声明和调用函数pthread_setaffinity_np ,但你永远不会定义它。 显然你忘了提供包含定义的库。 这在C和C ++中都是错误的。

换句话说,这不是 C和C ++中的有效程序。 它违反了C ++的一个定义规则(以及在C中调用类似的规则)。

剩下的就取决于编译器是否会捕获这个错误并为它发出一个诊断信息。 尽管正式的编译器应该抓住它,但实际上链接错误并不总是被编译过程所捕捉到(在扩展意义上,即包括链接)。

他们是否被抓住可能取决于许多因素。 在这个特定情况下,重要的因素显然是C和C ++语言的内联函数的属性之间的差异。 (是的,它们在C和C ++之间确实是不同的)。 我猜测在C ++模式下,编译器决定这个内联函数不需要实际的主体,而在C模式下,它决定生成主体。

所以,如果这个程序在某些情况下成功编译,那只是因为你运气好。 你似乎认为一个没有被调用的函数应该被“完全忽略”。 C和C ++都没有这样的保证。 假设pthread_setaffinity_np的定义确实缺失,那么您的程序在C和C ++中都是无效的。 因为这个原因,拒绝编译它的编译器实际上是具有正确行为的编译器。

考虑到上面的问题,你可能想问自己,你是否真的在乎你为什么在C和C ++模式下得到不同的错误报告。 如果这样做的话,就需要对这个具体实现的内部机制进行一些研究,并且与语言本身无关。

在C中, inline关键字不影响函数的链接 。 因此foo具有外部联系,并且不能被优化,因为它可能被另一个翻译单元调用。 如果编译器/汇编程序把函数放在它们自己的各个部分中,并且链接器能够在链接时放弃不需要的函数部分,可能可以避免链接错误,但是要正确,因为这个程序引用了pthread_setaffinity_np ,它必须包含无论如何,这个函数的定义,即你必须使用-lpthread或等价的。

在C ++中, inline函数默认内部 有一些奇怪的伪外部链接,所以gcc优化了它。 详情请参阅评论。

简而言之,某些配置中缺少错误是gcc无法诊断无效的程序。 这不是你应该期望的行为。

另一个教训是,C和C ++离得很远, 选择你正在写的一个,坚持下去! 不要试图编写两者之间“可互换”的代码,或者你可能会在两者之间做出微妙的错误。

inline只是一个建议,不是编译器有义务听的东西,所以它不能假定foo在另一个编译单元中不被使用。

但是,是的,如果你没有发布错误,那么确切地知道哪个是未定义的引用是很好的,奇怪的是它是用C语言显示的,而不是C ++编译的。

foo可能不会被用在你的源代码中,但是在构建过程的其他地方几乎肯定会引用它,因此它需要被编译。

特别是由于在链接过程中发生了很多优化,因为链接器可以确定一个函数是“死的”并且可以被丢弃。

如果在内部,链接器决定将整个程序组装成一个通道,然后在另一个通道中进行优化,那么我希望你看到这个错误(怎么能组装整个程序?)

此外,如果要输出函数,那么它肯定要被编译,链接,并在输出中结束。

这听起来像你依赖于编译器/链接器的具体行为。