链接器如何解析C中的多定义全局符号

链接器如何解析C中的多定义全局符号,c,linker,operating-system,static-linking,C,Linker,Operating System,Static Linking,我的教科书上说: 函数和初始化的全局变量得到强符号。未初始化的全局变量得到弱符号。给定一个强符号和多个弱符号,请选择强符号 因此,我创建了两个文件以查看: 文件1.c: int number; int main(int argc, char *argv[]) { printf("%d",number); return 0; } file2.c(仅一行): 我运行了gcc-Wall-o程序file1.c file2.c,输出为0,这在我研究链接器之前是可以理解的(file1.

我的教科书上说:

函数和初始化的全局变量得到强符号。未初始化的全局变量得到弱符号。给定一个强符号和多个弱符号,请选择强符号

因此,我创建了两个文件以查看:

文件1.c:

int number;

int main(int argc, char *argv[]) 
{
    printf("%d",number);
    return 0;
}
file2.c(仅一行):


我运行了
gcc-Wall-o程序file1.c file2.c
,输出为0,这在我研究链接器之前是可以理解的(file1.c中的“number”已初始化为0),但在研究链接器的工作原理之后,我开始想为什么输出不是2018,因为file2中的“number”是强符号(初始化的全局变量)文件1中的“数字”是弱符号,因此链接器将选择值为2018的强符号,那么为什么链接器选择弱符号呢?

文件2.c中的
数字是全局的,但仍然局限于该文件的局部范围。如果希望file1.c使用file2.c中的
number
,则需要将其标记为
extern
,如下所示:

extern int number;

int main(int argc, char *argv[]) 
{
    printf("%d",number);
    return 0;
}

int数未取消初始化。请注意,它是在文件范围内声明的,声明时没有初始值设定项,声明时没有存储类说明符(特别是no
extern
static
)。然后,C 2018第6.9.2条规定:

对于具有文件作用域但没有初始值设定项、没有存储类说明符或具有存储类说明符
static
的对象,其标识符的声明构成了一个暂定定义。如果翻译单元包含一个或多个标识符的暂定定义,而翻译单元不包含该标识符的外部定义,则行为与翻译单元包含该标识符的文件范围声明完全相同,复合类型为翻译单元的末尾,具有等于0的初始值设定项

所以,
int数与
int number=0相同。它已初始化


您引用的文本的一个问题是,它使用链接器的术语描述链接器,这与C标准使用的术语不同。C标准没有任何“全局”变量或“强”或“弱”符号。

对不起,我不明白,如果一个全局变量没有静态关键字,那么它是全局范围的,可以被程序访问,否则为什么我们需要静态关键字来限制其他模块的访问?C标准不使用“全局”范围或联系。您的意思可能是file2.c中的
number
具有外部链接,但file scope.不是int number;在全局范围声明?@nicomp:C标准没有任何“全局范围”的范围。有文件范围和外部链接。在任何块或参数列表之外声明的标识符都有文件作用域。@EricPostpischil,但是如果file1.c中的int number与int number=0相同,那么我们有两个强符号,链接器在遇到多个强变量时不应该抛出错误吗?我的意思是“amjad中‘数字’的多重定义:根据C 2018 6.9 5,整个程序中外部对象的定义不得超过一个。根据4.2,违反“应”的行为未被C标准定义。因此,这里允许C实现执行任何操作,包括完成链接。在我的例子中(macOS 10.13.6和Xcode 9.4.1),“2018”被打印出来,我相信这些工具使用的是一种长期存在的Unix行为,其中显式初始化的对象占主导地位(实际上,Unix通过定义C标准没有定义的行为扩展了C)。你应该说明你正在使用的工具。你在使用什么工具链?我认为这是标准的UB,但我很确定大多数链接器都会使用
int-number一个所谓的“通用”符号,在出现强符号时,该符号将被强符号合并替换。我要用tinycc、gcc和叮当声迎接2018年。
extern int number;

int main(int argc, char *argv[]) 
{
    printf("%d",number);
    return 0;
}