链接器如何解析在多个位置定义的相同名称的全局符号

时间:2018-06-20 22:17:50

标签: c

我是编程新手,我从c开始。我正在学习链接器如何解析在多个位置定义的全局符号这一点。所以我做了一个小程序,上面写着:

// main.c

#include <stdio.h>

void b(void);


int x;
int a=10;
int y =500;
int x1 = 2016;

int main()
{
b();


printf("x = %1f y = %d\n",x , y);
return 0;
}

// second_file.c

double x=100.0;
int g=100;
extern int y;
void b()
{
x = -100.0;

}

我从链接器遵循的规则中知道可以解决这种情况,链接器将选择强符号,在这种情况下(双倍x = 100.0;)不是main.c中定义的整数。所以我希望输出是x = -100.0 y = 500, 但是输出为x = 0.000000 y = 89,谁能解释为什么我找到了这个输出,或者我错了?

我试图在cmd(用于Windows)中使用objdump命令查看符号表,也许我发现了一些使我理解的东西,但又发现了另一件事,很奇怪,我发现了变量的地址,如下所示:

a ------ 0x00000000

y ------ 0x00000004

x1 ----- 0x00000008

x ------ 0x00000010

g ------ 0x00000018

我认为x应该从0x0000000c开始,为什么不这样?

3 个答案:

答案 0 :(得分:0)

main.c中的声明

int x;

与second_file中的那个无关。c

double x=100.0;

因为它们的范围(可见性)仅限于其编译单元。也就是说,它们是全局变量,但(大致)仅在同一文件中的函数中可见。 如果您希望两个文件共享相同的全局变量,则必须使用extern关键字来指定它:

extern int x;

在其中一个文件中,这将强制链接器在实现该声明的链接时匹配符号。

不要忘记:您可以多次声明一个变量(类型一致),但只能定义一次。为了避免花太长时间回答这个问题,请尝试在stackoverflow中搜索在多个文件中使用全局变量的最佳方法。最好通过* .h进行声明,当然要在一个* .c文件中实现它。

最后,在此示例中,链接器无论如何都不会将int x声明与double x定义匹配,并且在链接时会引发错误。

答案 1 :(得分:0)

您可能知道或可能不知道,您需要小心使用多个定义。理想情况下,每个全局符号将被精确定义一次。在各种情况下,您都可以避开多个定义,但是您必须要小心。

在这种情况下,您依赖于“公共分配模型”,该模型在(并且至今仍然)在C编译器中广泛存在,这是因为无论是否相信FORTRAN都会对其产生早期影响。该模型表明,您最多可以具有多个定义,只要其中一个给出一个初始化值即可。但是-这是最大的问题-所有定义都必须具有相同的类型。 (我不确定我是否曾经看过这个规则;我不确定我什至没有明确考虑过它,因为它最终显然必须是这样。)

C使用单独编译的概念。每个.c文件都会编译成一个单独的独立“目标文件”。后来,一个名为 linker 的单独程序将目标文件链接在一起。链接程序将最终地址分配给全局变量,并最终解决了公共分配。

但是链接程序仅解析地址。它将安排x中的main.cx中的second_file.c位于同一地址。但是,如果main.c认为在位置int上有一个x,这就是它的解释方式。并且如果second_file.c认为位置double处有一个x,并且确实在其中存储了一个类型为double的值,那么当main.c试图执行以下操作时,它将变得很乱将该位模式解释为int

@ user58697在另一个答案中讨论,您在printf调用中遇到了其他问题,试图使用%f打印(编译器认为是)int值,永远也无法正常工作。

答案 2 :(得分:0)

首先,在一行中

    printf("x = %1f y = %d\n",x , y);

您撒谎了printf。您告诉编译器压入一个int(价值4个字节),但指示printf拉一个double(显然8个字节)。这是一个UB。所有赌注都关闭了。没有人知道printf将从哪里y撤出。

x会发生这种情况:

vnp$ nm -n main.o
                 U _b
                 U _printf
0000000000000000 T _main
0000000000000004 C _x
0000000000000040 D _a
0000000000000044 D _y
0000000000000048 D _x1

您会看到_xC注释(对于Common)。编译器将int x;定义为 ,意识到x是未初始化的全局变量,并将其放置在BSS中。在它编译时

    printf("x = %1f y = %d\n",x , y);

行,它生成了相对于.bss的重定位记录。

但是,链接器从第二个文件中选择了x,并将其放置在.data中,但是按指示执行了相对于.bss的重定位。这就是x打印为0的原因。

免责声明:由于存在UB,因此以上内容纯属猜测(但受过良好教育)。

PS:x的地址是由于对齐要求或double而引起的。