我知道C中的全局变量有时有extern关键字。什么是extern变量?宣言是什么样的?它的范围是什么
这与跨源文件共享变量有关,但这究竟是如何工作的呢?我在哪里使用extern
使用extern仅在构建程序时才有意义
由链接在一起的多个源文件组成,其中
例如,在源文件file1.c中定义的变量需要
在其他源文件中引用,例如file2.c
理解定义a之间的区别很重要
变量和声明
变量:
-
当编译器被告知
变量存在(这是它的类型);它不分配资源
此时变量的存储 -
当编译器为变量分配存储时,定义了一个变量
变量
可以多次声明变量(尽管一次就足够了);
在给定范围内只能定义一次。
变量定义也是一种声明,但不是所有变量
声明是定义
声明和定义全局变量的最佳方法
声明和定义全局变量的干净、可靠的方法是使用
包含变量的外部声明的头文件
标头包含在定义变量的一个源文件中
以及引用该变量的所有源文件。
对于每个程序,一个源文件(并且只有一个源文件)定义
变量
类似地,一个头文件(并且只有一个头文件)应该声明
变量
头文件至关重要;它可以在不同的服务器之间进行交叉检查
独立的TUs(翻译单元-思考源文件)并确保
一致性
尽管有其他方法可以做到这一点,但这种方法简单易行
可信赖的
它可以通过file3.h、file1.c和file2.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.c,file1.c,file2.c,file3.h和prog1.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位长和双的机器上,您甚至不会
得到警告