概念:声明如何链接到专有定义

Concept: How are declarations linked to apropriate definitions

本文关键字:定义 链接 声明 何链接 概念      更新时间:2023-10-16

头文件或任何前向声明如何确切地知道它指的是哪个定义?

我知道.cpp文件是独立编译的,我们需要一个头文件或转发声明来访问另一个.cpp文件的成员。但是当我们声明一个成员时,我们不会显式告诉编译器从哪里获取定义。

这是我能想到的一个案例: 假设我有两个 cpp 文件"一.cpp"和"二.cpp"。 "one.cpp"和"two.cpp都有一个成员"int func(int x)",它们具有不同的实现(但具有确切的名称和格式)。 如果我们在这两个文件之外的某个地方有一个头文件或这个函数的声明,编译器如何知道要采用哪个定义?

解析每个声明的定义由链接器执行。每个声明必须具有唯一的定义。虽然一个函数可以声明多次,但每个函数必须在要链接的所有编译单元中精确定义一次。如果具有相同签名的函数有多个定义,则链接器将引发错误并拒绝完成可执行文件的生成。

我建议您创建您描述的示例文件,并尝试将它们构建为单个可执行文件。您将看到我在此处描述的错误。

If we have a header file or declaration of this function somewhere outside these two files, how does the compiler know which definition to take?

它是一个链接器,它将一个或多个目标文件或库作为输入,并将它们组合在一起以生成可执行文件。在这样做的过程中,它解析了对外部符号的引用,即它从其他".obj"文件和外部库中查找所有外部函数和全局变量的定义,将最终地址分配给过程/函数和变量,并修改代码和数据以反映新地址。

让我们考虑一下您提到的相关示例:

Say I have two cpp files 'one.cpp' and 'two.cpp'. Both 'one.cpp' and 'two.cpp' have a member 'int func(int x)' that have different implementations....

说,one.cpp

int func(int x)
{
return x+1;
}

two.cpp

int func(int x)
{
return x+2;
}

以及声明func()函数的头文件,例如myinc.h

int func(int x);

main()在召唤func(),说main.cpp

#include <iostream>
#include <myinc.h>
int main()
{
int res;
res = func(10);
std::cout << res << std::endl;
return 0;
}

我可以创建main.cpp的对象文件,因为对象文件可以引用未定义的符号。

>g++ -I . -c main.cpp

现在让我们使用nm命令检查对象文件main.o,输出为:

Symbols from main.o:
Name                  Value           Class        Type         Size             Line  Section
_GLOBAL__I_main     |0000000000000078|   t  |              FUNC|0000000000000015|     |.text    
_Z41__static_initialization_and_destruction_0ii|0000000000000038|   t  |              FUNC|0000000000000040|     |.text    
_Z4funci            |                |   U  |            NOTYPE|                |     |*UND*
.......
.......<SNIP>

func()函数ClassU,表示未定义。 编译器不介意它找不到特定函数的定义,它只是假设该函数是在另一个文件中定义的。

另一方面,链接器可能会查看多个文件,并尝试查找对未提及的函数的引用。

因此,当我们尝试从目标文件创建可执行文件时one.otwo.omain.o

>g++ two.o one.o main.o -o outexe
one.o: In function `func(int)':
one.cpp:(.text+0x0): multiple definition of `func(int)'
two.o:two.cpp:(.text+0x0): first defined here
collect2: ld returned 1 exit status

在这里,您可以看到链接器为func()抛出multiple definition错误,因为它找到了func()的两个定义。

C++ 中有一个定义规则,它指出:

在整个程序中,一个对象或

非内联函数不能有多个定义;如果使用一个对象或函数,它必须只有一个定义。您可以声明从未使用过的对象或函数,在这种情况下,您不必提供定义。在任何情况下都不能有多个定义。

因此,该程序违反了 ODR,因为它包含同一函数声明的两个定义。

如果我们不向链接器提供one.otwo.o对象文件,则意味着,如果我们只提供一个func()定义,它将生成 exe:

>g++ one.o main.o -o outexe

如果我们检查outexe,我们会得到:

Symbols from outexe:
Name                  Value           Class        Type         Size             Line  Section
.....<SNIP>    
_Z41__static_initialization_and_destruction_0ii|000000000040082c|   t  |              FUNC|0000000000000040|     |.text
_Z4funci            |00000000004007e4|   T  |              FUNC|000000000000000f|     |.text
.......
.......<SNIP>

符号func()的类型为 -FUNC-T,表示 -The symbol is in the text (code) section