共计 3644 个字符,预计需要花费 10 分钟才能阅读完成。
一、头文件
首先说下头文件,其实头文件对计算机而言没什么作用,她只是在预编译时在 #include 的中央开展一下,没别的意义了,其实头文件次要是给他人看的。
我做过一个试验,将头文件的后缀改成 xxx.txt,而后在援用该头文件的中央用 #include “xxx.txt”, 编译,链接都很顺利的过来了,由此可知,头文件仅仅为浏览代码作用,没其余的作用了!
不论是 C 还是 C ++,你把你的函数,变量或者构造体,类啥的放在你的.c 或者.cpp 文件里。而后编译成 lib,dll,obj,.o 等等,而后他人用的时候,最根本的 gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。
但对于咱们程序员而言,他们怎么晓得你的 lib,dll… 外面到底有什么货色?要看你的头文件。你的头文件就是对用户的阐明。函数,参数,各种各样的接口的阐明。
那既然是阐明,那么头文件外面放的天然就是对于函数,变量,类的 ” 申明 ”(对函数来说,也叫函数原型)了。记着,是 ” 申明 ”,不是 ” 定义 ”。
那么,我假如大家晓得申明和定义的区别。所以,最好不要傻嘻嘻的在头文件里定义什么货色。比方全局变量:
/xx 头文件/
ifndef _XX_头文件.H
define _XX_头文件.H
int A;
endif
那么,很蹩脚的是,这里的 int A 是个全局变量的定义,所以如果这个头文件被屡次援用的话,你的 A 会被反复定义,显然语法上错了。只不过有了这个 #ifndef 的条件编译,所以能保障你的头文件只被援用一次,不过兴许还是不会出岔子,但若多个 c 文件蕴含这个头文件时还是会出错的,因为宏名无效范畴仅限于本 c 源文件,所以在这多个 c 文件编译时是不会出错的,但在链接时就会报错,说你多处定义了同一个变量:
Linking…
incl2.obj : error LNK2005: “int glb” (?glb@@3HA) already defined in incl1.obj
Debug/incl.exe : fatal error LNK1169: one or more multiply defined symbols found
二、extern
这个关键字真的比拟可恶,在定义变量的时候,这个 extern 竟然能够被省略(定义时,默认均省略);在申明游戏变量的时候,这个 extern 必须增加在变量前,所以有时会让你搞不清楚到底是申明还是定义。或者说,变量前有 extern 不肯定就是申明,而变量前无 extern 就只能是定义。注:定义要为变量分配内存空间;而申明不须要为变量分配内存空间。
上面分变量和函数两类来说:
(1)变量
尤其是对于变量来说:
extern int a; // 申明一个全局变量 a
int a; // 定义一个全局变量 a
extern int a =0 ; // 定义一个全局变量 a 并给初值。
int a =0; // 定义一个全局变量 a, 并给初值,
第四个等于第三个,都是定义一个能够被内部应用的全局变量,并给初值。
糊涂了吧,他们看上去可真像。然而定义只能呈现在一处。也就是说,不论是 int a;还是 www.sangpi.comextern int a=0;还是 int a=0; 都只能呈现一次,而那个 extern int a 能够呈现很屡次。
当你要援用一个全局变量的时候,你就必须要申明,extern int a; 这时候 extern 不能省略,因为省略了,就变成 int a; 这是一个定义,不是申明。注:extern int a; 中类型 int 可省略,即 extern a; 但其余类型则不能省略。
(2)函数
函数,对于函数也一样,也是定义和申明,定义的时候用 extern,阐明这个函数是能够被内部援用的,申明的时候用 extern 阐明这是一个申明。但因为函数的定义和申明是有区别的,定义函数要有函数体,申明函数没有函数体(还有以分号结尾),所以函数定义和申明时都能够将 extern 省略掉,反正其余文件也是晓得这个函数是在其余中央定义的,所以不加 extern 也行。两者如此不同,所以省略了 extern 也不会有问题。
比方:
/某 cpp 文件/
int fun(void)
{
return 0;
}
很好,咱们定义了一个全局函数:
/另一 cpp 文件 /
int fun(void);
咱们对它做了个申明,而后前面就能够用了, 加不加 extern 都一样, 咱们也能够把对 fun 的申明放在一个头文件里,最初变成这样:
/fun.h/
int fun(void); // 函数申明,所以省略了 extern,残缺些是 extern int fun(void);
/对应的 fun.cpp 文件/
int fun(void)
{
return 0;
}// 一个残缺的全局函数定义,因为有函数体,extern 同样被省略了。
而后,一个客户,一个要应用你的 fun 的客户,把这个头文件蕴含进去,ok,一个全局的申明。没有问题。
然而,对应的,如果是这个客户要应用全局变量,那么要 extern 某某变量;不然就成了定义了。
总结:
对变量而言,如果你想在本源文件 (例如文件名 A) 中应用另一个源文件 (例如文件名 B) 的变量,办法有 2 种:(1)在 A 文件中必须用 extern 申明在 B 文件中定义的变量 (当然是全局变量);(2) 在 A 文件中增加 B 文件对应的头文件,当然这个头文件蕴含 B 文件中的变量申明,也即在这个头文件中必须用 extern 申明该变量,否则,该变量又被定义一次。
对函数而言,如果你想在本源文件 (例如文件名 A) 中应用另一个源文件 (例如文件名 B) 的函数,办法有 2 种:(1)在 A 文件中用 extern 申明在 B 文件中定义的函数 (其实,也可省略 extern,只需在 A 文件中呈现 B 文件定义函数原型即可);(2) 在 A 文件中增加 B 文件对应的头文件,当然这个头文件蕴含 B 文件中的函数原型,在头文件中函数能够不必加 extern。
对上述总结换一种说法:
(a)对于一个文件中调用另一个文件的全局变量,因为全局变量个别定义在原文件.c 中,咱们不能用 #include 蕴含源文件而只能蕴含头文件,所以罕用的办法是用 extern int a 来申明内部变量。另外一种办法是能够是在 a.c 文件中定义了全局变量 int global_num,能够在对应的 a.h 头文件中写 extern int global_num,这样其余源文件能够通过 include a.h 来申明她是内部变量就能够了。
(b)还有变量和函数的不同举例 int fun(); 和 extern int fun(); 都是申明(定义要有实现体)。用 extern int fun() 只是更明确指明是申明而已。而 int a; 是定义 extern int a; 是申明。
(3)此外,extern 修饰符可用于 C++程序中调用 c 函数的标准问题。
比方在 C++中调用 C 库函数,就须要在 C++程序中用 extern “C” 申明要援用的函数。这是给链接器用的,通知链接器在链接的时候用 C 函数标准来链接。次要起因是 C++和 C 程序编译实现后在指标代码中命名规定不同。
C++语言在编译的时候为了解决的多态问题,会将名和参数联结起来生成一个两头的名称,而 c 语言则不会,因而会造成链接时找不到对应的状况,此时 C 就须要用 extern “C” 进行链接指定,这通知编译器,请放弃我的名称,不要给我生成用于链接的两头名。
三、extern 和头文件的分割
这种分割也解决了最后提出的 2 个问题:
(a)用 #include 能够蕴含其余头文件中变量、函数的申明,为什么还要 extern 关键字?
(b)如果我想援用一个全局变量或函数 a,我只有间接在源文件中蕴含 #include (xxx.h 蕴含了 a 的申明)不就能够了么,为什么还要用 extern 呢??
答案:如果一个文件 (假如文件名 A) 要大量援用另一个文件 (假如文件名 B) 中定义的变量或函数,则应用头文件效率更高,程序结构也更标准。其余文件 (例如文件名 C、D 等) 要援用文件名 B 中定义的变量或函数,则只需用 #include 蕴含文件 B 对应的头文件 (当然,这个头文件只有对变量或函数的申明,绝不能有定义) 即可。
那是一个被忘记的年代,那时,编译器只意识.c(或.cpp)文件,而不晓得.h 是何物的年代。
那时的人们写了很多的.c(或.cpp)文件,慢慢地,人们发现在很多.c(或.cpp)文件中的申明变量或函数原型是雷同的,但他们却不得不一个字一个字地反复地将这些内容敲入每个.c(或.cpp)文件。但更为恐怖的是,当其中一个申明有变更时,就须要查看所有的.c(或.cpp)文件,并批改其中的申明,啊~,几乎是世界末日来临!
终于,有人(或者是一些人)再不能忍耐这样的折磨,他(们)将反复的局部提取进去,放在一个新文件里,而后在须要的.c(或.cpp)文件中敲入 #include XXXX 这样的语句。这样即便某个申明产生了变更,也再不须要到处寻找与批改了 — 世界还是那么美妙!
因为这个新文件,常常被放在.c(或.cpp)文件的头部,所以就给它起名叫做 ” 头文件 ”,扩展名是.h。
从此,编译器(其实是其中预处理器)就晓得世上除了.c(或.cpp)文件,还有个.h 的文件,以及一个叫做 #include。