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

C/C++ unused inline function undefined reference

本文关键字:函数 未定义 引用 c++ 未使用      更新时间:2023-10-16

考虑以下代码(这不是特定于pthread的;其他示例,例如那些涉及实时库的示例,也表现出类似的行为):

#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,并尝试构建。

当我在C++中构建时,我没有错误。当我在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没有在主路径中被引用,g++正确地完全忽略了该函数,但gcc没有。

编辑2:让我再试一次。函数foo以及随后对pthread_setaffinity_np的调用都是未使用的。主函数为空。看看吧!不知何故,g++发现foo不需要包括在内,随后构建过程没有绊倒,当我们故意省略-lpthread(并检查与nm导出的符号确认既不需要foo也不需要引用pthread_setaffinity_np)。gcc的结果输出并没有注意到这个事实。

我问这个问题是因为c++和C前端似乎在相同的输入上给出不同的结果。这似乎不是一个ld问题,因为我希望两条路径给出相同的链接错误,这就是为什么我强调这似乎是一个编译器问题。如果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可能不会在您的源代码中使用,但它几乎肯定会在构建过程的其他地方被引用,因此需要编译。

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

如果在内部,链接器决定将整个程序组装为一次,然后在另一次中进行优化,我希望您看到这个错误(它如何组装整个程序?)

此外,如果要导出函数,则必须对其进行编译、链接并最终输出。

听起来你依赖于编译器/链接器的特定行为