我是编程新手,我从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开始,为什么不这样?
答案 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.c
和x
中的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
您会看到_x
用C
注释(对于Common
)。编译器将int x;
定义为 ,意识到x
是未初始化的全局变量,并将其放置在BSS
中。在它编译时
printf("x = %1f y = %d\n",x , y);
行,它生成了相对于.bss
的重定位记录。
但是,链接器从第二个文件中选择了x
,并将其放置在.data
中,但是按指示执行了相对于.bss
的重定位。这就是x
打印为0
的原因。
免责声明:由于存在UB,因此以上内容纯属猜测(但受过良好教育)。
PS:x
的地址是由于对齐要求或double
而引起的。