Chinaunix首页 | 论坛 | 博客
  • 博客访问: 640448
  • 博文数量: 171
  • 博客积分: 2246
  • 博客等级: 大尉
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-31 11:45
文章分类

全部博文(171)

文章存档

2018年(3)

2017年(4)

2015年(1)

2014年(20)

2013年(57)

2012年(86)

分类: C/C++

2012-06-14 10:35:36


C语言中extern的用法

一:看到这个,说的很基础,转录于此。

   在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。

 

  1. extern修饰变量的声明。举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern int v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。

 

  2. extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int funint mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int funint mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。 对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。

  3. 此外,extern修饰符可用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同。

二:了解了上面的基本原理,下面深入一下。如下:

转自 
         http://hi.baidu.com/eternity86/blog/item/9f012c153b6a363fdc540175.html

作者:华清远见武汉华嵌 技术支持 曹伟东

内容清单:

1. 用extern声明外部变量

(1)在一个文件内声明的外部变量

(2)在多个文件中声明外部变量

(3)在多个文件中声明外部结构体变量

2. 用extern声明外部函数

3. 总结

1.  用extern声明外部变量

定义:外部变量是指在函数或者文件外部定义的全局变量。外部变量定义必须在所有的函数之外,且只能定义一次。

(1)      在一个文件内声明的外部变量

作用域:如果在变量定义之前要使用该变量,则在用之前加extern声明变量,作用域扩展到从声明开始,到本文件结束。

例子:


点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int max(int x,int y); //函数提前声明

  3. int main(int argc,char *argv[ ] )

  4. {

  5.  int result;

  6. extern int X; //外部变量声明

  7. extern int Y;

  8.         result = max(X,Y);

  9.         printf("the max value is %d\n",result);

  10.         return 0;

  11. }

  12. int X = 10; //定义外部变量

  13. int Y = 20;

  14. int max(int x, int y)

  15. {

  16.         return (x>y ? x : y);

  17. }

其中,用extern声明外部变量时,类型名可以省略。例如,“extern int X;”,可以改写成“extern X;”。

小结:这种用方法简单,实用性不大。

(2)      在多个文件中声明外部变量

作用域:如果整个工程由多个文件组成,在一个文件中想引用另外一个文件中已经定义的外部变量时,则只需在引用变量的文件中用extern关键字加以声明即可。可见,其作用域从一个文件扩展到多个文件了。

例子:

文件a.c的内容:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int BASE=2; //变量定义

  3. int exe(int x); //外部函数提前声明

  4. int main(int argc, char *agrv[])

  5. {

  6.         int a=10;

  7.         printf("%d^%d = %d\n",BASE,a,exe(a));

  8.         return 0;

  9. }

文件b.c的内容:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. extern BASE; //外部变量声明

  3. int exe(int x)

  4. {

  5.         int i;

  6.         int ret=1;

  7.         for(i=0;i<x;i++)

  8.         {

  9.                 ret*=BASE;

  10.         }

  11.         return ret;

  12. }

利用gcc工具编译gcc a.c b.c –o demo,再运行./demo,结果为2^10 = 1024。其中,在a.c文件中定义BASE=2,在b.c中引用BASE时,需要用extern关键字声明其为外部变量,否则编译会找不到该变量。

    小结:对于多个文件的工程,可以采用这种方法来操作。实际工程中,对于模块化的程序文件,在其文件中可以预先留好外部变量的接口,也就是只采用extern声明变量,不定义变量,也通常在模块程序的头文件中声明,在使用该模块时,只需要在使用时定义一下即可,如上述b.c文件,做好相应的函数接口,留好需要改变BASE值的声明,在需要使用该模块时,只需要在调用的文件中定义具体的值即可。

    引用外部变量和通过函数形参值传递变量的区别:用extern引用外部变量,可以在引用的模块内修改其值,而形参值传递的变量则不能修改其值,除非是地址传递。

    因此,如果多个文件同时对需要应用的的变量进行同时操作,可能会修改该变量,类似于形参的地址传递,从而影响其他模块的使用,因此,要慎重使用。

(3)      在多个文件中声明外部结构体变量

前面一节中,只是适合一般变量的外部声明,但是对于声明外部结构体变量时,则有些不同,需要加以注意。

例子:

文件a.c的内容:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. #include "b.h"

  3. #include "c.h"

  4. A_class local_post={1,2,3}; //全局变量

  5. A_class next_post={10,9,8}; //全局变量

  6. int main(int argc,char *argv[])

  7. {

  8.         A_class ret;

  9.         print("first point",local_post);

  10.         print("second point",next_post);

  11.         ret=fun(local_post,next_post);

  12.         printf("the vector is (%d,%d,%d)\n",ret.x,ret.y,ret.z);

  13.         return 0;

  14. }

文件b.h的内容:

点击(此处)折叠或打开

  1. #ifndef __B_H

  2. #define __B_H

  3. #if 1

  4. typedef struct{

  5.         int x;

  6.         int y;

  7.         int z;

  8. }A_class;

  9. #endif

  10. extern A_class local_post; //外部结构体变量声明

  11. extern A_class fun(int x,int y,int z); //接口函数声明

  12. #endif

文件b.c的内容:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. #include "b.h"

  3. A_class fun(A_class first,A_class next)

  4. {

  5.         A_class ret;

  6.         ret.x=next.x-first.x;

  7.         ret.y=next.y-first.x;

  8.         ret.z=next.z-first.z;

  9.         return (ret);

  10. }

文件c.h的内容:

点击(此处)折叠或打开

  1. #ifndef __C_H

  2. #define __C_H

  3. extern int print(char *,A_class post);

  4. #endif

 文件c.c的内容:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. #include "b.h"

  3. int print(char *str,A_class post)

  4. {

  5.         printf("%s : (%d,%d,%d)\n",str,post.x,post.y,post.z);

  6.         return 0;

  7. }

利用gcc工具编译gcc a.c b.c c.c–o demo,再运行./demo,结果为

first point : (1,2,3)

second point : (10,9,8)

the vector is (9,8,5)

   小结:在a.c文件中定义全局变量A_class local_post结构体,并且调用b.c中的接口函数A_class fun(int x,int y,int z)和c.c中int print(char *str,A_class post),在b.h中声明外部结构体变量local_post,同时,需要对其类型A_class进行实现,在b.h文件中,如果屏蔽掉A_class的实现,而在b.h以外的文件中实现,此时编译时就会出错,由于c.c文件中用到A_class定义的类型,所以需要在该文件中包含b.h。

   这里需要说明的是,如果调用外部结构体等多层结构体变量时,需要对这种变量进行实现,使用时,加上模块的头文件即可,否则会报错。

   实际工程中,模块化程序文件,一般提供一个.c和一个.h文件,其中.h文件被.c文件调用,.h文件中实现。

2. 用extern声明外部函数

a.定义函数时,在函数返回值类型前面加上extern关键字,表示此函数时外部函数,可供其他文件调用,如extern int func (int x,int y),C语言规定,此时extern可以省略,隐形为外部函数。

b. 调用此函数时,需要用extern对函数作出声明。

作用域:使用extern声明能够在一个文件中调用其他文件的函数,即把被调用函数的作用域扩展到本文件。C语言中规定,声明时可以省略extern。

例子:

文件a.c的内容:


点击(此处)折叠或打开

  1. #include <stdio.h>

  2. #include "b.h"

  3. int main()

  4. {

  5.         int x=10,y=5;

  6.         printf("x = 10,y = 5\n");

  7.         printf("x + y = %d\n",add(x,y));

  8.         printf("x - y = %d\n",sub(x,y));

  9.         printf("x * y = %d\n",mult(x,y));

  10.         printf("x / y = %d\n",div(x,y));

  11.         return 0;

  12. }

文件b.h的内容:

点击(此处)折叠或打开

  1. #ifndef __F_H

  2. #define __F_H

  3. extern int add(int x,int y);

  4. extern int sub(int x,int y);

  5. extern int mult(int x,int y);

  6. extern int div(int x,int y);

  7. #endif

文件b.c的内容:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int add(int x,int y)

  3. {

  4.         return(x+y);

  5. }

  6. int sub(int x,int y)

  7. {

  8.         return(x-y);

  9. }



  10. int mult(int x,int y)



  11. {

  12.         return(x*y);

  13. }

  14. int div(int x,int y)

  15. {

  16.         if(y!=0)

  17.         {

  18.                 return(x/y);

  19.         }

  20.         printf("mult() fail!the second para can not be zero!\n ");

  21.         return(-1);

  22. }

利用gcc工具编译gcc a.c b.c –o demo,再运行./demo,结果为

x = 10,y = 5

x + y = 15

x - y = 5

x * y = 50

x / y = 2。

小结:由上面简单的例子可以看出,在b.h文件中声明好b.c的函数,使用时,只需要在a.c中包含#include “b.h”头文件即可,这样就可以使用b.c的接口函数了,在实际工程中,通常也是采用这种方式,.c文件中实现函数,.h文件中声明函数借口,需要调用.c文件的函数接口时,只需包含.h头文件即可。

3.  总结

  在实际工程中,有两种情况比较多。一是利用extern只声明外部函数,不需要传递需要外部声明的变量,一个模块化接口文件对应一个声明接口的头文件,需要调用接口函数时,只需要包含其头文件。二是利用用extern声
明外部函数,同时声明需要传递的外部变量,做法和第一种情况一样,声明都放在头文件中,但是,模块文件也需要包含该头文件。另外,如果结构体等比较复杂的
变量,则需要包含其定义的头文件。另外,定义的外部变量属于全局变量,其存储方式为静态存储,生存周期为整个程序的生存周期。

阅读(1558) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~