C - 一个函数声明,多个定义,并且仍然有效

C++ - One Function Declaration, Multiple definitions, And it still works?

本文关键字:定义 有效 声明 函数 一个      更新时间:2023-10-16

好吧,我有3个文件,一个标头,所述标头的来源和一个主。在标题文件中,我定义具有功能的类。在源文件中,我定义该函数。但是,在主文件中,i 重新定义该函数,然后在主函数中进行类的实例并调用函数。这可以很好地编译 - 没有任何警告。至少可以说,输出很可怕。

标题:testme.h

#ifndef testme_h_
#include <iostream>
using namespace std;
class wtf {
public:
  string getStr();
};
#endif

来源:testme.cpp

#include "testme.h"
string wtf::getStr() {
  return "Hello World!";
};

main:main.cpp

#include <iostream>
using namespace std;
#include "testme.h"
string wtf::getStr()
{
  return "God is Dead.";
}
int main()
{
  wtf f;
  cout << f.getStr() << endl;
}

输出:

God is Dead.

为什么这起作用?为什么关于多个定义没有错误?为什么souce文件的定义被忽略了?为什么没有警告?

部分答案当将其重新编译为" g main.cpp testme.cpp -o sanity.o",实际上确实会产生链接器错误。

让我得到的,但是,我写了一个镜像我在库中定义一个函数的镜像的小案例,但是我们在另一个"测试套件"程序中重新定义了一个函数方式。为什么这样?在图书馆中它允许它覆盖ODR?

为什么这起作用?为什么关于多个定义没有错误?为什么souce文件的定义被忽略了?为什么没有警告?

部分答案是在将其重新编译为" g main.cpp testme.cpp -o sanity.o"时。实际上,它确实会产生链接器错误。

在C 的设计和演变中,Stroustrup说,在设计功能时,他偶尔会在(1((1(复杂规则之间进行选择,即编译器能够使用警告和错误消息执行,或(2(一个简单的规则,即编译器在所有情况下都无法执行。他试图选择简单的规则,他希望编译器最终能够执行/检测错误。C和C 的几个部分有效地显示了1970年代,1980年代,1990年代等的最新状态(例如inlineregistervolatile(。可能还有更多。

大致来说,这种权衡是标准中未定义行为的来源。如果不能执行一个简单的规则(或委员会认为执行该规则很昂贵(,则该规则停留在标准中,违反行为是不确定的。从不触发未定义的行为是程序员的责任。编译器和相关工具有可能在某些时候执行其中一些规则。某些编译器或相关工具甚至可能始终执行其中的某些规则。但是,总的来说,您是一个人。

设计和进化甚至包括对您的特定问题的讨论。编写编译器后,Stroustrup也不想写一个链接器。他想出了一种简单的方法来依靠系统链接器,但是在早期,许多连接器对符号可能长时间有严格的限制。他努力说服那些编写系统接头的人,以使他们与Cfront一起工作。最终,他成功了。

今天,链接器通常带有编译器,但是标准委员会仍然将其视为单独的工具,可能在编写编译器的人们的控制之外。一个定义规则的存在很大程度上是为了确保编译器的输出与大多数标准链接器兼容,但是委员会不需要这些链接者能够检测到违反ODR的行为。此外,可以逐步编译和链接程序,因此无法保证链接器将有足够的信息来检测违反ODR的行为。

值得注意的是,许多编译器将为inline功能和template函数生成多个符号,希望链接器抛出冗余定义。因此,链接器不一定要查看多个定义是错误的。(就ODR而言,这些定义必须相同,以便链接器可以扔掉所有的定义。(

(

在库中它允许它覆盖ODR?

我不会说"覆盖"。听起来这种行为是故意的。我会说"违反未被发现的违法"。答案只是"标准委员会不期望链接者始终发现违规行为,而且许多链接者都不会。"避免违规是您的责任。但是,您不是一个人:一部分名称空间是为了使这个问题更容易解决。