参考:http://wrchen.blog.sohu.com/71617539.html
(1)编译单元(模块)
    在VC或VS上编写完代码,点击编译按钮准备生成exe文件时,编译器做了两步工作:
第一步,将每个.cpp(.c)和相应的.h文件编译成obj文件;
第二步,将工程中所有的obj文件进行LINK,生成最终.exe文件。
    那么,错误可能在两个地方产生:
一个,编译时的错误,这个主要是语法错误;
一个,链接时的错误,主要是重复定义变量等。
    
    编译单元指在编译阶段生成的每个obj文件。
    一个obj文件就是一个编译单元。
    一个.cpp(.c)和它相应的.h文件共同组成了一个编译单元。
    一个工程由很多编译单元组成,每个obj文件里包含了变量存储的相对地址等。

(2)声明与定义
    函数或变量在声明时,并没有给它实际的物理内存空间,它有时候可保证你的程序编译通过;
    函数或变量在定义时,它就在内存中有了实际的物理空间。
 
    如果你在编译单元中引用的外部变量没有在整个工程中任何一个地方定义的话,那么即使它在编译时可以通过,在连接时也会报错,因为程序在内存中找不到这个变量。
    函数或变量可以声明多次,但定义只能有一次。
(3) extern作用
    作用一:当它与"C"一起连用时,如extern "C" void fun(int a, int b);,则编译器在编译fun这个函数名时按C的规则去翻译相应的函数名而不是C++的。
    作用二:当它不与"C"在一起修饰变量或函数时,如在头文件中,extern int g_nNum;,它的作用就是声明函数或变量的作用范围的关键字,其声明的函数和变量可以在本编译单元或其他编译单元中使用。
    即B编译单元要引用A编译单元中定义的全局变量或函数时,B编译单元只要包含A编译单元的头文件即可,在编译阶段,B编译单元虽然找不到该函数或变量,但它不会报错,它会在链接时从A编译单元生成的目标代码中找到此函数。
(4)全局变量(extern)

有两个类都需要使用共同的变量,我们将这些变量定义为全局变量。比如,res.h和res.cpp分别来声明和定义全局变量,类ProducerThread和ConsumerThread来使用全局变量。(以下是QT工程代码)

[cpp] view plaincopy
  1. /**********res.h声明全局变量************/
  2. #pragma once
  3. #include <QSemaphore>
  4. const int g_nDataSize = 1000; // 生产者生产的总数据量
  5. const int g_nBufferSize = 500; // 环形缓冲区的大小
  6. extern char g_szBuffer[]; // 环形缓冲区
  7. extern QSemaphore g_qsemFreeBytes; // 控制环形缓冲区的空闲区(指生产者还没填充数据的区域,或者消费者已经读取过的区域)
  8. extern QSemaphore g_qsemUsedBytes; // 控制环形缓冲区中的使用区(指生产者已填充数据,但消费者没有读取的区域)
  9. /**************************/
上述代码中g_nDataSize、g_nBufferSize为全局常量,其他为全局变量。

[cpp] view plaincopy
  1. /**********res.cpp定义全局变量************/
  2. #pragma once
  3. #include "res.h"
  4. // 定义全局变量
  5. char g_szBuffer[g_nBufferSize];
  6. QSemaphore g_qsemFreeBytes(g_nBufferSize);
  7. QSemaphore g_qsemUsedBytes;
  8. /**************************/
在其他编译单元中使用全局变量时只要包含其所在头文件即可。

[cpp] view plaincopy
  1. /**********类ConsumerThread使用全局变量************/
  2. #include "consumerthread.h"
  3. #include "res.h"
  4. #include <QDebug>
  5. ConsumerThread::ConsumerThread(QObject* parent)
  6. : QThread(parent) {
  7. }
  8. ConsumerThread::ConsumerThread() {
  9. }
  10. ConsumerThread::~ConsumerThread() {
  11. }
  12. void ConsumerThread::run() {
  13. for (int i = 0; i < g_nDataSize; i++) {
  14. g_qsemUsedBytes.acquire();
  15. qDebug()<<"Consumer "<<g_szBuffer[i % g_nBufferSize];
  16. g_szBuffer[i % g_nBufferSize] = ' ';
  17. g_qsemFreeBytes.release();
  18. }
  19. qDebug()<<"&&Consumer Over";
  20. }
  21. /**************************/

    也可以把全局变量的声明和定义放在一起,这样可以防止忘记了定义,如上面的extern char g_szBuffer[g_nBufferSize]; 然后把引用它的文件中的#include "res.h"换成extern char g_szBuffer[];。
    但是这样做很不好,因为你无法使用#include "res.h"(使用它,若达到两次及以上,就出现重定义错误;注:即使在res.h中加#pragma once,或#ifndef也会出现重复定义,因为每个编译单元是单独的,都会对它各自进行定义),那么res.h声明的其他函数或变量,你也就无法使用了,除非也都用extern修饰,这样太麻烦,所以还是推荐使用.h中声明,.cpp中定义的做法。


(5)静态全局变量(static)
    注意使用static修饰变量,就不能使用extern来修饰,即static和extern不可同时出现。
    static修饰的全局变量的声明与定义同时进行,即当你在头文件中使用static声明了全局变量,同时它也被定义了。
    static修饰的全局变量的作用域只能是本身的编译单元。在其他编译单元使用它时,只是简单的把其值复制给了其他编译单元,其他编译单元会另外开个内存保存它,在其他编译单元对它的修改并不影响本身在定义时的值。即在其他编译单元A使用它时,它所在的物理地址,和其他编译单元B使用它时,它所在的物理地址不一样,A和B对它所做的修改都不能传递给对方。
    多个地方引用静态全局变量所在的头文件,不会出现重定义错误,因为在每个编译单元都对它开辟了额外的空间进行存储。

以下是Windows控制台应用程序代码示例:
[cpp] view plaincopy
  1. /***********res.h**********/
  2. static char g_szBuffer[6] = "12345";
  3. void fun();
  4. /************************/
[cpp] view plaincopy
  1. /***********res.cpp**********/
  2. #include "res.h"
  3. #include <iostream>
  4. using namespace std;
  5. void fun() {
  6. for (int i = 0; i < 6; i++) {
  7. g_szBuffer[i] = 'A' + i;
  8. }
  9. cout<<g_szBuffer<<endl;
  10. }
  11. /************************/

[cpp] view plaincopy
  1. /***********test1.h**********/
  2. void fun1();
  3. /************************/
[cpp] view plaincopy
  1. /***********test1.cpp**********/
  2. #include "test1.h"
  3. #include "res.h"
  4. #include <iostream>
  5. using namespace std;
  6. void fun1() {
  7. fun();
  8. for (int i = 0; i < 6; i++) {
  9. g_szBuffer[i] = 'a' + i;
  10. }
  11. cout<<g_szBuffer<<endl;
  12. }
  13. /************************/
[cpp] view plaincopy
  1. /***********test2.h**********/
  2. void fun2();
  3. /************************/
[cpp] view plaincopy
  1. /***********test2.cpp**********/
  2. #include "test2.h"
  3. #include "res.h"
  4. #include <iostream>
  5. using namespace std;
  6. void fun2() {
  7. cout<<g_szBuffer<<endl;
  8. }
  9. /************************/
[cpp] view plaincopy
  1. /***********main.cpp**********/
  2. #include "test1.h"
  3. #include "test2.h"
  4. int main() {
  5. fun1();
  6. fun2();
  7. system("PAUSE");
  8. return 0;
  9. }
  10. /************************/
运行结果如下:

    按我们的直观印象,认为fun1()和fun2()输出的结果都为abcdef,可实际上fun2()输出的确是初始值。然后我们再跟踪调试,发现res、test1、test2中g_szBuffer的地址都不一样,分别为0x0041a020、0x0041a084、0x0041a040,这就解释了为什么不一样。
    注:一般定义static 全局变量时,都把它放在.cpp文件中而不是.h文件中,这样就不会给其他编译单元造成不必要的信息污染。

(6)全局常量(const)
    const单独使用时,其特性与static一样(每个编译单元中地址都不一样,不过因为是常量,也不能修改,所以就没有多大关系)。
    const与extern一起使用时,其特性与extern一样。

[cpp] view plaincopy
  1. extern const char g_szBuffer[];      //写入 .h中
  2. const char g_szBuffer[] = "123456"; // 写入.cpp中

Fr:http://blog.csdn.net/candyliuxj/article/details/7853938

C++全局变量的声明和定义相关推荐

  1. 基于C++全局变量的声明与定义的详解

    (1)编译单元(模块) 在VC或VS上编写完代码,点击编译按钮准备生成exe文件时,编译器做了两步工作: 第一步,将每个.cpp(.c)和相应的.h文件编译成obj文件: 第二步,将工程中所有的obj ...

  2. 全局变量的声明、定义及用法

    全局变量的声明.定义及用法 文章目录 全局变量的声明.定义及用法 1. 编译单元(模块) 2. 声明和定义 3. extern 作用 4. 全局变量(extern) 4.1 如果直接将声明和定义都放在 ...

  3. 静态全局变量的声明与定义

    先引用一段介绍,原文:https://blog.csdn.net/li15809284891/article/details/54923273 static: 用static修饰的变量,在其所限定的作 ...

  4. C++中的全局变量声明和定义

    1.全局变量 全局变量在整个源文件的作用域都是有效的,只需要在一个源文件中定义全局变量,在其他不包含全局变量定义的源文件中用extern关键字再次声明这个全局变量即可. 也可以在一个源文件中定义这个全 ...

  5. [java] java全局变量 声明和定义

    参考:https://blog.csdn.net/lilil371324/article/details/51241580 JAVA全局变量:静态变量,实例变量(即在类体中定义的变量), 静态变量:s ...

  6. JAVA 全局变量 声明与定义

    JAVA全局变量(或称成员变量)可分两种,一种是静态变量,另一种是实例变量,即在类体中定义的变量,有三点得注意: 一.成员变量不能在类体中先声明(定义)后赋值,但静态变量可以先在类体中声明,然后在方法 ...

  7. C++ : 编译单元、声明和定义、头文件作用、防止头文件在同一个编译单元重复引用、static和不具名空间...

    转 自:http://www.cnblogs.com/rocketfan/archive/2009/10/02/1577361.html 1. 编译单元:一个.cc或.cpp文件作为一个编译单元,生成 ...

  8. [C++再学习系列] 变量的声明、定义与extern关键字

    变量的声明与定义: A definition of a variable allocates storage for thevariable and may also specify an initi ...

  9. 内存四域,变量声明和定义,寄存器,c内嵌汇编,auto,堆栈,常量,静态变量

     1.内存四大区域 2.在程序中,变量的声明可以有多份,定义只能有一份 3.寄存器在cpu里面,没有地址 4.c语言内嵌汇编语言 5.auto变量 自动分配内存,自动释放内存. 6.栈的大小由编译 ...

最新文章

  1. linux怎么和宿主机同步时间,Linux 中设置和同步时间
  2. 递归删除父节点及所有子节点(转)
  3. 【UGV】Mec 麦轮版小车驱动程序示例
  4. 起点计算机网,《零起点计算机》网第5课.pdf
  5. 漫步微积分十二——隐函数、分数指数
  6. android画面传输到电视,如何将手机内容投屏到电视上?
  7. java正则表达式所有字符串_“JAVA”正则表达式如何匹配所有符合要求的子字符串?...
  8. 商业智能数据营销该怎么做?
  9. linux 怎么添加文件类型,如何在Linux/Unix上添加基于文件类型的文件扩展名?
  10. 机房巡检知识点(一)
  11. 数据库系统工程师考试的考点是哪些?
  12. 北斗三号频点_海格通信发布国内首批北斗三号双模应用专用芯片
  13. airflow实现Java定时任务,AirFlow定时调度执行Talend ETL任务
  14. 老版本的linux内核中mtd test 出现 mtd_oobtest: error: verify failed at 0xXXXX 问题
  15. Invalid parameter passed to C runtime function.
  16. 学习webworker
  17. smbd配置windows访问linux centos的共享文件
  18. Linux操作系统学习笔记【入门必备】
  19. 适用场景:All kinds of GCs
  20. 3.PMAC硬件-组成和接口及安装

热门文章

  1. 寻找连通域算法_FPGA实现的连通域识别算法升级
  2. 信安教程第二版-第12章网络安全审计技术原理与应用
  3. java 防止js注入_在WebView中如何让JS与Java安全地互相调用
  4. 添加文字 生成pdf_PDF 文件编辑方法和工具大全
  5. 匿名函数 lambda
  6. Linux 下挂载新硬盘方法(转)
  7. Nginx 的 Location 配置指令块
  8. android小细节
  9. Ubuntu 下面用ibus在opera中输入中文
  10. 【2007-3】【分硬币】