考虑以下代码(这不是特定于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.c
和testcpp.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
(并用{{1检查导出的符号)时,构建过程没有绊倒确认既不需要nm
也不需要引用foo
。 pthread_setaffinity_np
的结果输出没有发现这一事实。
我问这个问题,因为C ++和C前端似乎在同一输入上给出了不同的结果。这似乎不是一个gcc
问题的原因,因为我希望两个路径都给出相同的链接错误,这就是为什么我强调它似乎是一个编译器问题。如果C ++和C都有问题,那么我会同意它是一个链接问题。
答案 0 :(得分:8)
好吧,显然你的程序包含一个错误:你声明并调用函数pthread_setaffinity_np
,但是你永远不会定义它。显然你忘了提供包含定义的库。这是C和C ++中的错误。
换句话说,这是不 C和C ++中的有效程序。它违反了C ++的One Definition Rule(无论在C中调用类似的规则)。
其余的取决于编译器是否会捕获此错误并为其发出诊断消息。虽然形式上编译器应该捕获它,但实际上链接错误并不总是被编译过程捕获(在术语的扩展意义上,即包括链接)。
他们是否被抓住可能取决于很多因素。在这种特殊情况下,重要的因素显然是C和C ++语言的内联函数的属性之间的差异。 (是的,它们在C和C ++之间确实不同)。我猜想在C ++模式下编译器决定这个内联函数不需要实际的主体,而在C模式下它决定生成主体。
所以,再次,如果这个程序在某些情况下成功编译,那只是因为你很幸运。你似乎相信一个未被调用的函数应该被“完全忽略”。 C和C ++都不能做出这样的保证。假设确实缺少pthread_setaffinity_np
的定义,那么您的程序在C和C ++中都是无效的。因此,拒绝编译它的编译器实际上是具有正确行为的编译器。
考虑到上述情况,您可能想问自己是否真的关心为什么在C和C ++模式下有不同的错误报告。如果这样做,则需要对该特定实现的内部机制进行一些研究,并且与语言本身无关。
答案 1 :(得分:6)
在C中,inline
关键字不会影响该功能的链接。因此foo
具有外部链接,并且无法进行优化,因为它可能是从另一个翻译单元调用的。如果编译器/汇编器将函数放在各自的各个部分中,并且链接器能够在链接时丢弃不需要的函数段,则它可能能够避免链接错误,但是要正确,因为该程序引用pthread_setaffinity_np
,它必须包含某个地方的函数定义,即你必须使用-lpthread
或等价物。
在C ++中,inline
函数默认具有 internal 一些奇怪的伪外部链接,因此gcc对其进行了优化。 有关详细信息,请参阅注释。
简而言之,某些配置中缺少错误是gcc无法诊断无效程序。这不是你应该期待的行为。
你应该从中得到的另一个教训是C和C ++远不是同一个东西。选择你正在写的那个并坚持下去!不要试图编写两者之间“可互换”的代码,否则你可能会在两者中巧妙地做错......
答案 2 :(得分:1)
inline
只是一个建议,而不是编译器有义务监听的内容,所以它不能假设foo
没有在另一个编译单元中使用。
但是,是的,如果您没有发布错误,那么确切地知道哪个是未定义的引用会很好,奇怪的是它出现在C而不是C ++编译中。
答案 3 :(得分:1)
foo
可能不会在您的源代码中使用,但它几乎肯定会在构建过程的其他地方引用,因此需要进行编译。
特别是因为在链接过程中发生了很多优化,因为链接器可以确定函数是“死”并且可以被丢弃。
如果在内部,链接器决定将整个程序组装为一次,然后在另一次传递中进行优化,我希望你能看到这个错误(它如何组装整个程序?)
此外,如果要导出该函数,那么它肯定必须在输出中进行编译,链接和结束。
听起来你依赖于编译器/链接器特定的行为。