转自:chao_yu

extern 函数声明
  常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?
  答案与分析:
  如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:
extern int f(); 和int f();
  当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。关于这样做的原因和利弊可见下面的这个例子:“用extern修饰的全局变量”

(1) 在test1.h中有下列声明:
    #ifndef TEST1H
    #define TEST1H
    extern char g_str[]; // 声明全局变量g_str
    void fun1();
    #endif
    (2) 在test1.cpp中
    #include "test1.h"
        char g_str[] = "123456"; // 定义全局变量g_str
        void fun1() { cout << g_str << endl; }
    (3) 在test1.cpp中。以上是test1模块, 它的编译和连接都可以通过,如果我们还有test2模块也想使用g_str,只需要在原文件中引用就可以了
    #include "test1.h"

void fun2()    { cout << g_str << endl;    }
    以上test1和test2可以同时编译连接通过,如果你感兴趣的话可以用ultraEdit打开test1.obj,你可以在里面找到"123456"这个字符串,但是你却不能在test2.obj里面找到,这是因为g_str是整个工程的全局变量,在内存中只存在一份,test2.obj这个编译单元不需要再有一份了,不然会在连接时报告重复定义这个错误!
    (4) 有些人喜欢把全局变量的声明和定义放在一起,这样可以防止忘记了定义,如把上面test1.h改为
    extern char g_str[] = "123456"; // 这个时候相当于没有extern
    然后把test1.cpp中的g_str的定义去掉,这个时候再编译连接test1和test2两个模块时,会报连接错误,这是因为你把全局变量g_str的定义放在了头文件之后,test1.cpp这个模块包含了test1.h所以定义了一次g_str,而test2.cpp也包含了test1.h所以再一次定义了g_str,这个时候连接器在连接test1和test2时发现两个g_str。如果你非要把g_str的定义放在test1.h中的话,那么就把test2的代码中#include "test1.h"去掉 换成:
    extern char g_str[];
    void fun2()   {  cout << g_str << endl;   }
   这个时候编译器就知道g_str是引自于外部的一个编译模块了,不会在本模块中再重复定义一个出来,但是我想说这样做非常糟糕,因为你由于无法在test2.cpp中使用#include "test1.h",那么test1.h中声明的其他函数你也无法使用了,除非也用都用extern修饰,这样的话你光声明的函数就要一大串,而且头文件的作用就是要给外部提供接口使用的,所以 请记住, 只在头文件中做声明,真理总是这么简单

extern 和 static

(1) extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
 (2) static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.

static 作用范围是内部连接的关系, 和extern有点相反.它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern 引用,而static 不可以,只允许对象本身用它. 具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:
    (1) test1.h:
    #ifndef TEST1H
    #define TEST1H
    static char g_str[] = "123456"; 
    void fun1();
    #endif

(2) test1.cpp:
    #include "test1.h"
    void fun1()  {   cout << g_str << endl;  }
    (3) test2.cpp
    #include "test1.h"
    void fun2()  {   cout << g_str << endl;  }
    以上两个编译单元可以连接成功, 当你打开test1.obj时,你可以在它里面找到字符串"123456",同时你也可以在test2.obj中找到它们,它们之所以可以连接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。 也许你比较较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址相同,于是你下结论static修饰的变量也可以作用于其他模块,但是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,执行效率更高,当编译器在连接各个编译单元的时候,它会把相同内容的内存只拷贝一份,比如上面的"123456", 位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就只会存在一份了,如果你把上面的代码改成下面的样子,你马上就可以拆穿编译器的谎言:
    (1) test1.cpp:
    #include "test1.h"
    void fun1()
    {
        g_str[0] = ''a'';
        cout << g_str << endl;
    }

(2) test2.cpp
    #include "test1.h"
    void fun2()  {  cout << g_str << endl;  }
    (3) void main()     {
        fun1(); // a23456
        fun2(); // 123456
    }
    这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,所以编译器被强行的恢复内存的原貌,在内存中存在了两份拷贝给两个模块中的变量使用。正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!

extern 和const

C++中const修饰的全局常量据有跟static相同的特性,即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中, 如extern const char g_str[];
    然后在原文件中别忘了定义:     const char g_str[] = "123456";

所以当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了!所以对const我没有什么可以过多的描述,我只是想提醒你,const char* g_str = "123456" 与 const char g_str[] ="123465"是不同的, 前面那个const 修饰的是char *而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char* g_str遵守const的全局常量的规则,最好这么定义const char* const g_str="123456".

extern函数声明(转)的更多相关文章

  1. C++函数声明和定义深度解析

    概述: 声明是将一个名称引入一个程序. 定义提供了一个实体在程序中的唯一描述. 声明在单个作用域内可以重复多次(类成员除外),定义在一个给定的作用域内只能出现一次. 一个定义就是一个声明,除非: 它定 ...

  2. Js 变量声明提升和函数声明提升

    Js代码分为两个阶段:编译阶段和执行阶段 Js代码的编译阶段会找到所有的声明,并用合适的作用域将它们关联起来,这是词法作用域的核心内容 包括变量声明(var a)和函数声明(function a(){ ...

  3. JavaScript 中对变量和函数声明的“提前”

    变量声明“被提前” JavaScript 的语法和 C .Java.C# 类似,统称为 C 类语法.有过 C 或 Java 编程经验的同学应该对“先声明.后使用”的规则很熟悉,如果使用未经声明的变量或 ...

  4. js函数表达式和函数声明的区别

    我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义"隐 藏"起来,外部作用域无法访问包装函数内部的任何内容. 例如: var a = 2; function ...

  5. C语言中函数声明实现的位置

    在学习C语言的时候我遇到了这么个事情,因为之前先学习的C#,在C#编译器中,函数的声明位置不会影响编译的结果,但是在C语言中却发生了错误 先看一段代码: #include <stdio.h> ...

  6. javascript中函数声明和函数表达式浅析

    记得在面试腾讯实习生的时候,面试官问了我这样一道问题. //下述两种声明方式有什么不同 function foo(){}; var bar = function foo(){}; 当初只知道两种声明方 ...

  7. JS中的函数声明错误

      这个问题是JS的函数声明形参部分不带var. ( 额,基础不扎实.)

  8. JavaScript 函数声明,函数表达式,匿名函数,立即执行函数之区别

    函数声明:function fnName () {-};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 函数表达式 var fnName = function () {-};使 ...

  9. JS中函数声明与函数表达式的不同

    Js中的函数声明是指下面的形式: function functionName(){   } 这样的方式来声明一个函数,而函数表达式则是类似表达式那样来声明一个函数,如 var functionName ...

随机推荐

  1. CXGRID用法(取行、列值;定位选中某行等等)

    Delphi Cxgrid获取选中行列,排序规则,当前正在编辑的单元格内的值 cxGrid1DBTableView1.Controller.FocusedRowIndex 当前行号 cxGrid1DB ...

  2. CentOS下Neo4j安装教程

    本文记录一下在CentOS 6.7上,安装neo4j图数据库,本文安装的版本为neo4j-community-2.3.9-unix.tar.gz. 下载Neo4j安装包 使用wget命令获取Neo4j ...

  3. yii 验证码 CCaptcha的总结(转)

    今天用到yii的验证码 ccaptcha,经过在网上搜寻 找到以下例子: 1.在controller中加入代码 (1)启用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 &l ...

  4. SOAP 缓存问题

    今天在进行soap调用老是出错,去其他人的机器上试下,就好了,下面是从网上找到的原因 一开始不知道还有SOAP缓存.因为类文件改变了,重新生成了WSDL文件,测试运行,竟然不能通过.给我的第一感觉是W ...

  5. Linux_Nginx 安装

    官网:http://nginx.org/ 1.下载http://nginx.org/download/nginx-1.14.0.tar.gz 2.查看详情 [zwesy@localhost ~]$ l ...

  6. es6 字符串String的扩展

    新特性:模板字符串 传统字符串 let name = "Jacky"; let occupation = "doctor"; //传统字符串拼接 let str ...

  7. Centos7 pip 安装MySQLdb(mysql-python)出错

    租了个阿里云的Centos7的ECS,需要使用pip安装MySQLdb模块.也就是mysql-python模块. 但是遇到问题 Collecting mysql-python Downloading ...

  8. SGU438_The Glorious Karlutka River =)

    好题,有一些人在河的一边,想通过河里的某些点跳到对岸去.每个点最多只能承受一定数量的人,每人跳跃一次需要消耗一个时间.求所有人都过河的最短时间. 看网上说是用了什么动态流的神奇东东.其实就是最大流吧, ...

  9. 数据库事物 jdbc事物 spring事物 隔离级别:脏幻不可重复读

    1.数据库事物: 事物的概念 a给b打100块钱的例子 2.jdbc事物: 通过下面代码实现 private Connection conn = null; private PreparedState ...

  10. python参数传递方式

    原文地址:http://www.cnblogs.com/zhaopengcheng/p/5492183.html python中一切皆对象,函数中参数传递的是对象的引用. 1在函数中改变变量指向的对象 ...