如何使用extern在源文件之间共享变量?

我知道C中的全局变量有时有extern关键字。什么是extern变量?宣言是什么样的?它的范围是什么

这与跨源文件共享变量有关,但这究竟是如何工作的呢?我在哪里使用extern

使用extern仅在构建程序时才有意义
由链接在一起的多个源文件组成,其中
例如,在源文件file1.c中定义的变量需要
在其他源文件中引用,例如file2.c

理解定义a之间的区别很重要
变量和声明
变量:

  • 当编译器被告知
    变量存在(这是它的类型);它不分配资源
    此时变量的存储

  • 当编译器为变量分配存储时,定义了一个变量
    变量

可以多次声明变量(尽管一次就足够了);
在给定范围内只能定义一次。
变量定义也是一种声明,但不是所有变量
声明是定义

声明和定义全局变量的最佳方法

声明和定义全局变量的干净、可靠的方法是使用
包含变量的外部声明的头文件

标头包含在定义变量的一个源文件中
以及引用该变量的所有源文件。
对于每个程序,一个源文件(并且只有一个源文件)定义
变量
类似地,一个头文件(并且只有一个头文件)应该声明
变量
头文件至关重要;它可以在不同的服务器之间进行交叉检查
独立的TUs(翻译单元-思考源文件)并确保
一致性

尽管有其他方法可以做到这一点,但这种方法简单易行
可信赖的
它可以通过file3.hfile1.cfile2.c进行演示:

文件3.h

extern int global_variable;/*变量的声明*/

文件1.c

“include”;文件3.h"/*声明可在此查阅*/
#包括「;项目1.h"/*函数声明*/
/*此处定义的变量*/
int全局_变量=37;/*根据声明检查定义*/
int增量(void){return global_variable++;}

文件2.c

“include”;文件3.h“;
#包括「;项目1.h“;
#包括<标准h>
作废使用它(作废)
{
printf(“全局变量:%d\n”,全局变量++);
}

这是声明和定义全局变量的最佳方法


接下来的两个文件完成了prog1

显示的完整程序使用函数,因此函数声明
潜入。
C99和C11都需要先声明或定义函数,然后才能使用它们
使用了(而C90没有使用,原因很好)。
我在标题中的函数声明前面使用关键字extern
一致性-匹配变量前面的extern
标题中的声明。
许多人不喜欢在函数前面使用extern
声明;编译器不在乎——最终,我也不在乎
只要是一致的,至少在源文件中是一致的

项目1.h

extern void使用它(void);
外部内部增量(无效);

项目1.c

“include”;文件3.h“;
#包括「;项目1.h“;
#包括<标准h>
内部主(空)
{
使用它();
全局_变量+=19;
使用它();
printf(“增量:%d\n”,增量());
返回0;
}
  • prog1使用prog1.cfile1.cfile2.cfile3.hprog1.h

文件prog1.mk仅是prog1的生成文件。
它将适用于从大约本回合开始生产的大多数版本的make
千禧年。
它不是专门与GNU Make绑定的

prog1.mk

#prog1的最小生成文件
程序=程序1
FILES.c=prog1.cfile1.cfile2.c
FILES.h=prog1.h file3.h
FILES.o=${FILES.c:.c=.o}
CC=gcc
SFLAGS=-std=c11
GFLAGS=-g
OFLAGS=-O3
WFLAG1=-Wall
WFLAG2=-Wextra
WFLAG3=-Werror
WFLAG4=-Wstrict原型
WFLAG5=-Wmissing原型
WFLAGS=${WFLAG1}${WFLAG2}${WFLAG3}${WFLAG4}${WFLAG5}
UFLAGS=#仅在命令行上设置
CFLAGS=${SFLAGS}${GFLAGS}${OFLAGS}${WFLAGS}${UFLAGS}
LDFLAGS=
低密度脂蛋白=
全部:${PROGRAM}
${PROGRAM}:${FILES.o}
${CC}[email protected]${CFLAGS}${FILES.o}${LDFLAGS}${LDLIBS}
prog1.o:${FILES.h}
file1.o:${FILES.h}
file2.o:${FILES.h}
#如果存在,则prog1.dSYM是macOS上的一个目录
碎屑=a.out堆芯*~*.dSYM
RM_FR=RM-FR
清洁:
${RM_FR}${FILES.o}${PROGRAM}${particles}

指导方针

只有专家才能违反规则,而且必须有充分的理由:

  • 头文件仅包含变量的extern声明-从不
    静态或非限定变量定义

  • 对于任何给定的变量,只有一个头文件声明它(SPOT)-
    单点真理)

  • 源文件从不包含变量的extern声明-
    源文件始终包含声明它们的(唯一)头文件

  • 对于任何给定变量,只有一个源文件定义该变量,
    最好也初始化它。(虽然没有必要
    显式初始化为零,这样做没有坏处,也有好处,
    因为一个特定对象只能有一个初始化定义
    程序中的全局变量)

  • 定义变量的源文件还包括要
    确保定义和声明一致

  • 函数不需要使用extern声明变量

  • 尽可能避免使用全局变量-改用函数

此答案的源代码和文本可在我的
SOQ(堆栈溢出问题)
中的GitHub上的存储库
src/so-0143-3204
子目录

如果你不是一个有经验的C程序员,你可以(也许
应该)停止在这里阅读

这不是定义全局变量的好方法

有了一些(事实上,很多)C编译器,您就可以不受任何影响了
也被称为变量的“公共”定义。
这里的“Common”是指Fortran中用于共享的一种技术
源文件之间的变量,使用(可能命名的)公共块。
这里发生的事情是,许多文件中的每一个都提供了一个暂定的
变量的定义。
只要不超过一个文件提供初始化定义,
然后,各种文件最终共享一个通用的
变量:

文件10.c

“include”;prog2.h"
长l;/*不要在可移植代码中执行此操作*/
void inc(void){l++;}

文件11.c

“include”;prog2.h"
长l;/*不要在可移植代码中执行此操作*/
void dec(void){l--;}

文件12.c

“include”;prog2.h"
#包括<标准h>
长l=9;/*不要在可移植代码中执行此操作*/
void put(void){printf("l=%ld\n",l);}

此技术不符合C标准的字母和
“一个定义规则”-这是官方未定义的行为:

J.2未定义的行为

使用带有外部链接的标识符,但在程序中
标识符的外部定义不存在,或者
未使用该标识符,并且存在多个外部标识符
标识符的定义(6.9)

§6.9外部定义5

外部定义是一个外部声明,也是一个
函数的定义(内联定义除外)或
对象
如果在
表达式(不是作为sizeof的操作数的一部分)或
\u Alignof运算符,其结果为整数常量),位于
整个程序应有一个完整的外部定义
标识符;否则,不得超过
一个。161)

161)因此,如果使用外部链接声明标识符
未在表达式中使用,则不需要外部定义

然而,C标准在资料性附录J中也将其列为
常见的扩展

J.5.11多重外部定义

的标识符可能有多个外部定义
对象,无论是否显式使用关键字extern;如果
定义不一致,或初始化了多个定义
行为未定义(6.9.2)

因为这种技术并不总是受支持的,所以最好避免使用
使用它,尤其是当您的代码需要可移植时。
使用此技术,还可能导致意外类型
双关语

如果上述其中一个文件将l声明为double而不是
long,C类型的不安全链接器可能不会发现不匹配。
如果您在一台64位的机器上,您甚至不会
得到警告

发表评论