In C a variable inside a file say example.c is given local scope. The compiler expects that the variable would have its definition inside the same file example.c and when it does not find the same , it would throw an error.A function on the other hand has by default global scope . Thus you do not have to explicitly mention to the compiler "look dude...you might find the definition of this function here". For a function including the file which contains its declaration is enough.(The file which you actually call a header file). For example consider the following 2 files :
example.c
#include<stdio.h>
extern int a;
main(){
printf("The value of a is <%d>\n",a);
}
example1.c
int a = 5;
Now when you compile the two files together, using the following commands :
step 1)cc -o ex example.c example1.c step 2)./ex
You get the following output : The value of a is <5>
用#include可以包含其他头文件中变量、函数的声明,为什么还要extern关键字?
如果我想引用一个全局变量或函数a,我只要直接在源文件中包含#include<xxx.h> (xxx.h包含了a的声明)不就可以了么,为什么还要用extern呢??
这个问题一直也是似是而非的困扰着我许久,经过实践和查找资料,有如下总结:
一、头文件
首先说下头文件,其实头文件对计算机而言没什么作用,她只是在预编译时在#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;还是extern 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> (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命令。
refer : http://lpy999.blog.163.com/blog/static/117372061201182051413310/ ==============
最近写了一段C程序,编译时出现变量重复定义的错误,自己查看没发现错误。使用Google发现,自己对extern理解不透彻,我搜到了这篇文章,写得不错。我拙劣的翻译了一下。(原文:http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/) 我确定这篇文章对c语言的初学者会有很大的帮助,因为这将使他们更好更熟练的使用c语言。所以就让我先来说说extern关键字在变量和函数上的应用。最基本的extern关键字扩展了变量和函数的可见度。这可能就是它为什么命名为extern的原因。
几乎所有人都知道声明和定义变量(函数)的意义,但是为了这篇文章的完整性,我想弄清楚它们。声明一个变量(函数)只是表明这个变量(函数)存在于程序的某个地方,并没有为它们分配内存。但是,声明变量(函数)具有重要作用,那就是说明变量(函数)的类型。因此,当一个变量声明,程序知道变量的类型,在函数声明的情况下,程序知道函数的参数和返回类型。这就是所谓的声明。来到定义,当我们定义一个变量(函数),除了声明的作用,它也为该变量(函数)分配内存。因此,我们可以认为声明是定义的子集。从上面的说明,显而易见,变量(函数)可以声明多次,只能定义一次。
现在回到我们的主要目标:理解C语言中的关键字extern。我已经解释过声明和定义的作用了,因为我们必须借助它们来理解关键字extern。我们先来了解一种简单的情况,extern对函数的作用。默认情况下,声明和定义一个函数,都有一个extern的前缀,这意味着在声明和定义函数时,前面不写extern,它也是默认存在的。例如下面的代码。
int foo(int arg1, char arg2); 声明的函数前面没有extern,但编译器会在前面加上关键字extern,如下
extern int foo(int arg1, char arg2); 定义函数的情况和上面一样(函数的定义是指带有函数体)。因此,每当我们定义一个函数的时候,前面总会有默认的关键字extern。由于声明可以多次重复,定义只能完成一次,我们可以看到一个函数的声明可以在多个C/H文件或单个的C/H文件中重复多次。但是该函数实际只定义了一次(仅在一个文件中)。这样的函数在整个程序中都是可见的,任何文件任何地方都可以调用。(通过函数的声明,编译器在编译时就知道函数定义在哪里)。
第二种情况,extern对变量的作用。如何声明一个变量而不定义它?这是一个理解extern对变量作用的重要问题,答案如下:
extern int var; 在上面,一个整型变量var被声明(没有定义,没有分配内存)。并且,根据需要,我们可以声明多次。
现在,如何定义一个变量?答案如下:
int var; 在这里,声明及定义了一个整型变量var(声明是定义的子集,定义一个变量就包含了声明),这里还为变量var分配了内存。现在,让我们感到惊讶的是,当我们定义或声明一个函数时,前面会有默认的extern,但对于变量的情况就不一样了。如果变量也和函数一样,那么它们永远不会分配内存,它们仅仅被声明。因此,当我们想要声明而不定义一个变量就需要加上关键字extern。此外extern将变量的能见度延伸到了整个程序,我们知道变量声明和定义的地方,可以在整个程序的任何地方使用它们。下面借助几个例子来理解extern
int var; int main(void) { var = 10; return 0; } 分析:程序编译成功,var是一个全局变量(隐势声明)
extern int var; int main(void) { return 0; } 分析:程序编译成功,这里var只被声明,其他地方没有使用var,所以没问题。
extern int var; int main(void) { var = 10; return 0; } 分析:程序编译错误,由于声明了变量var,而没有在其他地方定义,从本质上讲,没有为var分配内存,程序又要给它赋值。
#include "somefile.h" extern int var; int main(void) { var = 10; return 0; } 分析:如果文件somefile.h文件中有定义变量var, 程序将会编译成功。
extern int var = 0; int main(void) { var = 10; return 0; } 分析:猜猜上面的代码能否编译成功?在这里按照C的标准是可以编译通过的。声明一个变量并给它初始化一个值,则该变量的内存将被分配,即该变量被定义。(有的编译器会报告一个警告:warning: 'extern' variable has an initializer)
所以,我们可以得出结论:
声明可以多次,定义只能一次。
关键字extern用于扩展变量和函数的可见性。
由于函数默认存在extern,不需要再定义和声明的时候使用extern。
当变量使用extern时,它只是声明没有定义。
当变量用extern声明并且有初始化时,和变量的定义一样。===============================================================``` 这里输入代码