在 C 中的两个文件之间共享一个变量
Share a variable between two files in C
我正在尝试了解如何 link 两个带有变量的文件。我知道标准方法是通过 extern 关键字,如下所示。
文件 1
#include<stdio.h>
int i;
void func1();
int main()
{
i = 10;
func1();
printf("i value with method 1: %d\n", i);
}
文件 2
extern int i;
void func1()
{
i = i+1;
}
编译执行=> gcc *.c ./a.out
输出=> i 方法 1 的值:11
我发现的一件事是通过 headers 如下所示:
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);
}
文件 2
#include"common.h"
void func1()
{
i = i+1;
}
编译执行=> gcc *.c ./a.out
输出=> i 方法 2 的值:11
我的疑问是,方法 2 是如何工作的?
在文件范围内,int i;
是一种特殊的声明,称为暂定定义。尽管它的名字,它不是一个定义。但是,它可能会导致创建定义。
声明和定义在多个翻译单元中使用的 object 的一种简洁方法是在每个使用 object 的单元中包含的 header 中声明它按姓名:
extern int i;
并在一个翻译单元中定义object:
int i = 0;
在文件范围内,int i = 0;
是一个定义;使用 = 0
的初始化使其成为常规定义而不是暂定定义。
理想情况下,所有源代码都将使用干净的声明和定义。但是,C并没有完全事先计划和设计。它是通过实验和不同的人在不同的地方以不同的方式实现的。当 C 委员会对 C 进行标准化时,他们不得不处理不同的实践和实现。一种常见的做法是在多个单元中声明 int i;
,旨在创建单个 i
。 (此行为是从 FORTRAN 继承而来的,后者具有共同的 object 作为类似功能。)
为了适应这一点,委员会在文件范围内将 int i;
描述为一种特殊的声明,一种暂定定义。如果同一翻译单元中存在定义相同标识符的常规定义,则暂定定义充当普通声明,而不是定义。如果没有常规定义,编译器(或 C 实现的其他部分)会为标识符创建一个定义,就好像它已用零初始化一样。
C 标准将多个暂定定义的协调留给每个 C 实现;它没有定义在多个翻译单元中使用 int i;
时的行为。在版本 10 之前,GCC 的默认行为是使用“公共符号”行为;链接时,多个暂定定义将协调为一个定义。 (为了支持这一点,编译器在创建 object 模块时将暂定定义标记为与常规定义不同,因此链接器知道哪个是哪个。)在版本 10 中,默认值已更改,GCC 现在处理暂定定义产生的定义作为常规符号而不是普通符号。
这就是为什么您会看到有些人报告他们在将源与暂定定义链接时出错,而您和其他人却没有。这只是他们使用哪个版本的编译器和链接器的问题。
您可以使用 GCC 开关 -fcommon
明确请求通用符号行为或 -fno-common
常规符号行为。
一般应该使用上面的clean方法;在 headers 中用 extern
声明标识符,并在一个源文件中的每个标识符中准确地放置一个定义。
我正在尝试了解如何 link 两个带有变量的文件。我知道标准方法是通过 extern 关键字,如下所示。
文件 1
#include<stdio.h>
int i;
void func1();
int main()
{
i = 10;
func1();
printf("i value with method 1: %d\n", i);
}
文件 2
extern int i;
void func1()
{
i = i+1;
}
编译执行=> gcc *.c ./a.out
输出=> i 方法 1 的值:11
我发现的一件事是通过 headers 如下所示:
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);
}
文件 2
#include"common.h"
void func1()
{
i = i+1;
}
编译执行=> gcc *.c ./a.out
输出=> i 方法 2 的值:11
我的疑问是,方法 2 是如何工作的?
在文件范围内,int i;
是一种特殊的声明,称为暂定定义。尽管它的名字,它不是一个定义。但是,它可能会导致创建定义。
声明和定义在多个翻译单元中使用的 object 的一种简洁方法是在每个使用 object 的单元中包含的 header 中声明它按姓名:
extern int i;
并在一个翻译单元中定义object:
int i = 0;
在文件范围内,int i = 0;
是一个定义;使用 = 0
的初始化使其成为常规定义而不是暂定定义。
理想情况下,所有源代码都将使用干净的声明和定义。但是,C并没有完全事先计划和设计。它是通过实验和不同的人在不同的地方以不同的方式实现的。当 C 委员会对 C 进行标准化时,他们不得不处理不同的实践和实现。一种常见的做法是在多个单元中声明 int i;
,旨在创建单个 i
。 (此行为是从 FORTRAN 继承而来的,后者具有共同的 object 作为类似功能。)
为了适应这一点,委员会在文件范围内将 int i;
描述为一种特殊的声明,一种暂定定义。如果同一翻译单元中存在定义相同标识符的常规定义,则暂定定义充当普通声明,而不是定义。如果没有常规定义,编译器(或 C 实现的其他部分)会为标识符创建一个定义,就好像它已用零初始化一样。
C 标准将多个暂定定义的协调留给每个 C 实现;它没有定义在多个翻译单元中使用 int i;
时的行为。在版本 10 之前,GCC 的默认行为是使用“公共符号”行为;链接时,多个暂定定义将协调为一个定义。 (为了支持这一点,编译器在创建 object 模块时将暂定定义标记为与常规定义不同,因此链接器知道哪个是哪个。)在版本 10 中,默认值已更改,GCC 现在处理暂定定义产生的定义作为常规符号而不是普通符号。
这就是为什么您会看到有些人报告他们在将源与暂定定义链接时出错,而您和其他人却没有。这只是他们使用哪个版本的编译器和链接器的问题。
您可以使用 GCC 开关 -fcommon
明确请求通用符号行为或 -fno-common
常规符号行为。
一般应该使用上面的clean方法;在 headers 中用 extern
声明标识符,并在一个源文件中的每个标识符中准确地放置一个定义。