(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)
(5)静态全局变量(static)
有两个类都需要使用共同的变量,我们将这些变量定义为全局变量。比如,res.h和res.cpp分别来声明和定义全局变量,类ProducerThread和ConsumerThread来使用全局变量。(以下是QT工程代码)
/**********res.h声明全局变量************/ #pragma once #include <QSemaphore> const int g_nDataSize = 1000; // 生产者生产的总数据量 const int g_nBufferSize = 500; // 环形缓冲区的大小 extern char g_szBuffer[]; // 环形缓冲区 extern QSemaphore g_qsemFreeBytes; // 控制环形缓冲区的空闲区(指生产者还没填充数据的区域,或者消费者已经读取过的区域) extern QSemaphore g_qsemUsedBytes; // 控制环形缓冲区中的使用区(指生产者已填充数据,但消费者没有读取的区域) /**************************/上述代码中g_nDataSize、g_nBufferSize为全局常量,其他为全局变量。
/**********res.cpp定义全局变量************/ #pragma once #include "res.h" // 定义全局变量 char g_szBuffer[g_nBufferSize]; QSemaphore g_qsemFreeBytes(g_nBufferSize); QSemaphore g_qsemUsedBytes; /**************************/在其他编译单元中使用全局变量时只要包含其所在头文件即可。
/**********类ConsumerThread使用全局变量************/ #include "consumerthread.h" #include "res.h" #include <QDebug> ConsumerThread::ConsumerThread(QObject* parent) : QThread(parent) { } ConsumerThread::ConsumerThread() { } ConsumerThread::~ConsumerThread() { } void ConsumerThread::run() { for (int i = 0; i < g_nDataSize; i++) { g_qsemUsedBytes.acquire(); qDebug()<<"Consumer "<<g_szBuffer[i % g_nBufferSize]; g_szBuffer[i % g_nBufferSize] = ' '; g_qsemFreeBytes.release(); } qDebug()<<"&&Consumer Over"; } /**************************/也可以把全局变量的声明和定义放在一起,这样可以防止忘记了定义,如上面的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控制台应用程序代码示例:
/***********res.h**********/ static char g_szBuffer[6] = "12345"; void fun(); /************************/
/***********res.cpp**********/ #include "res.h" #include <iostream> using namespace std; void fun() { for (int i = 0; i < 6; i++) { g_szBuffer[i] = 'A' + i; } cout<<g_szBuffer<<endl; } /************************/ /***********test1.h**********/ void fun1(); /************************/ /***********test1.cpp**********/ #include "test1.h" #include "res.h" #include <iostream> using namespace std; void fun1() { fun(); for (int i = 0; i < 6; i++) { g_szBuffer[i] = 'a' + i; } cout<<g_szBuffer<<endl; } /************************/ /***********test2.h**********/ void fun2(); /************************/ /***********test2.cpp**********/ #include "test2.h" #include "res.h" #include <iostream> using namespace std; void fun2() { cout<<g_szBuffer<<endl; } /************************/ /***********main.cpp**********/ #include "test1.h" #include "test2.h" int main() { fun1(); fun2(); system("PAUSE"); return 0; } /************************/
结果:
ABCDEF
abcdef
12345
按我们的直观印象,认为fun1()和fun2()输出的结果都为abcdef,可实际上fun2()输出的确是初始值。然后我们再跟踪调试,发现res、test1、test2中g_szBuffer的地址都不一样,分别为0x0041a020、0x0041a084、0x0041a040,这就解释了为什么不一样。
注:一般定义static 全局变量时,都把它放在.cpp文件中而不是.h文件中,这样就不会给其他编译单元造成不必要的信息污染。
(6)全局常量(const)
const单独使用时,其特性与static一样(每个编译单元中地址都不一样,不过因为是常量,也不能修改,所以就没有多大关系)。
const与extern一起使用时,其特性与extern一样。
extern const char g_szBuffer[]; //写入 .h中 const char g_szBuffer[] = "123456"; // 写入.cpp中
转自:http://blog.csdn.net/candyliuxj/article/details/7853938
相关推荐
在C++中,声明一个类,并且定义一个全局变量,练习全局变量的使用。注意全局变量的使用技巧和方法。
C++中,全局变量只能声明、初始化,而不能赋值 也就是说,下面这样是不被允许的: #include using namespace std; int a; a = 2; int main() { return 0; } 错误提示是: C++ requires a type specifier for all...
宏定义、结构体定义、函数声明以及全局变量定义放到一个head.h头文件中 函数的定义放到head.cpp中 main函数放到main.cpp中 然而却报错了,提示xxx变量在*.obj文件中已定义 问题出现的原因 为什么会出现这种...
本篇文章是对C++全局变量的声明与定义进行了详细的分析介绍,需要的朋友参考下
1、声明全局变量 2、声明函数 今天我们只谈extern,什么const、static之类等等与之相关或不相关的一律忽略,下面就分别对以上两种情况一一讲解 声明和定义 既然提到extern声明变量,那我们就必须搞清楚声明和定义的...
一个,链接时的错误,主要是重复定义变量等。 编译单元指在编译阶段生成的每个obj文件。 一个obj文件是一个编译单元。 一个.cpp(.c)和它相应的.h文件共同组成了一个编译单元。 一个工程由很多编译...
本文详细介绍了C++程序中的全局变量的声明、引用以及如何在头文件中定义全局变量
在global.h声明和定义变量 int sharedData = 9; 编译出错 Building target: CTest Invoking: GCC C++ Linker g++ -o "CTest" ./src/CTest.o ./src/global.o ./src/global.o:(.data+0x0): `...
函数或变量在声明时,并没有给它实际的物理内存空间,它有时候可以保证你的程序编译通过, 但是当函数或变量定义的时候,它就在内存中有了实际的物理空间与区别。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错 4、语句for( ;1 ;)有什么问题?它是什么意思? 微软亚洲技术中心的面试题 慧通面试题
4.2 C++ 中的变量声明 4.3 C++ 中的左值(Lvalues)和右值(Rvalues) 5. 变量作用域 5.1 局部变量 5.2 全局变量 5.3 初始化局部变量和全局变量 6. 常量 6.1 整数常量 6.2 浮点常量 6.3 布尔常量 6.4 字符常量 6.5 ...
本章我们先来讲解什么是局部变量和全局变量。 局部变量 在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。下面的实例使用了局部变量: #include using namespace ...
作为一种欲与C兼容的语言,C++保留了一部分过程 式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设计语言,为了支 持函数的重载...
2.5 C++中的变量声明 2.5.1 C++的命名规则 2.5.2 在哪里声明变量 2.6 C++中的运算符 2.6.1 计算路程的程序 2.6.2 从键盘输入程序所需数据 2.6.3 赋值运算符 2.6.4 运算符的优先级 2.6.5 数据类型及其存储的值 2.6.6...
38. 变量定义和变量声明有什么区别? 10 39. 引入结构体有什么好处? 10 40. 单链表中为什么要引入头结点? 10 41. 结构体类型定义的作用是什么? 结构体类型的变量定义的作用是什么? 10 42. 用自己的话描述逐步...
11.10.1 枚举类型的定义和枚举变量的说明 184 11.10.2 枚举类型变量的赋值和使用 185 11.11 类型定义符typedef 12 位运算 12.1 位运算符C语言提供了六种位运算符: 189 12.1.1 按位与运算 191 12.1.2 按位或运算 ...
全局变量与局部变量的区别及其生命周期。 每个问题都配有详细的解释和示例代码,帮助读者理解C++中的复杂概念和面试中可能遇到的技术问题。通过学习这份文档,读者可以提升自己的C++编程技能,为面试和实际开发工作...