我试着理解如何用一个变量链接两个文件。我知道标准的方法是通过extern关键字,如下所示。
档案1
#include<stdio.h>
int i;
void func1();
int main()
{
i = 10;
func1();
printf("i value with method 1: %d\n", i);
}
档案二
extern int i;
void func1()
{
i = i+1;
}
- 编译并执行=〉**gcc *. c ./a.输出
- 输出=〉i**方法1的值:11
我发现的一件事是通过如下所示的标题:
common.h
#ifndef __COMMON_H
#define __COMMON_H
int i;
#endif
档案1
#include<stdio.h>
#include"common.h"
int main()
{
i = 10;
func1();
printf("i value with method 2: %d\n", i);
}
档案二
#include"common.h"
void func1()
{
i = i+1;
}
- 编译并执行=〉**gcc *. c ./a.输出
- 输出=〉i**方法2的值:11
我的疑问是,方法2是如何工作的?
1条答案
按热度按时间mpgws1up1#
在文件作用域中,
int i;
是一种特殊的声明,称为 * tentative definition *。尽管它的名称是这样,但它不是一个定义。然而,它可以导致创建一个定义。声明和定义在多个翻译单元中使用的对象的一种简洁方法是在标头中使用
extern
声明该对象,标头包含在按名称使用该对象的每个单元中:并且在一个平移单元中定义对象:
在文件范围内,
int i = 0;
是一个定义;用= 0
的初始化使其成为常规定义而不是临时定义。理想情况下,所有源代码都会使用干净的声明和定义。然而,C语言并不是完全事先计划和设计好的。它是通过实验和不同地方的不同人以不同的方式实现事情而发展起来的。当C委员会将C语言标准化时,它们必须处理不同的做法和执行情况。一种常见的做法是声明多个单元中的
int i;
,目的是创建一个i
(这种行为继承自FORTRAN,它有类似的公共对象特性)。为了适应这一点,委员会将文件作用域中的
int i;
描述为一种特殊的声明,一种临时定义。如果在同一翻译单元中有一个定义了同一标识符的规则定义,则临时定义充当普通声明,而不是定义。如果没有规则定义,编译器(或C实现的其它部分)创建标识符的定义,就好像它已经用零初始化一样。C标准将多个试验性定义的协调留给每个C实现;没有定义
int i;
在多个翻译单元中使用时的行为,10版之前GCC默认行为是使用"公共符号"行为;当链接时,多个试探性定义将被协调为单个定义。(为了支持这一点,编译器在创建对象模块时将试探性定义与常规定义标记为不同,因此链接器知道哪个是哪个。)在版本10中,默认值改变了,GCC现在将由试探性定义产生的定义视为常规符号而不是公共符号。这就是为什么你会看到一些人报告他们在链接源代码时遇到了错误,而你和其他人却没有。这只是他们使用的编译器和链接器的版本问题。
您可以使用GCC开关
-fcommon
显式请求通用符号行为,或使用GCC开关-fno-common
显式请求常规符号行为。一般情况下,应使用上述清洁方法;声明标头中带有
extern
的标识符,并在一个源文件中的每个标识符中放置一个定义。