(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工程代码)
复制代码 代码如下:
/**********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;
}
/************************/
运行结果如下:
按我们的直观印象,认为fun1()和fun2()输出的结果都为abcdef,可实际上fun2()输出的确是初始值。然后我们再跟踪调试,发现res、test1、test2中g_szBuffer的地址都不一样,分别为0x0041a020、0x0041a084、0x0041a040,这就解释了为什么不一样。
注:一般定义static 全局变量时,都把它放在.cpp文件中而不是.h文件中,这样就不会给其他编译单元造成不必要的信息污染。
(6)全局常量(const)
const单独使用时,其特性与static一样(每个编译单元中地址都不一样,不过因为是常量,也不能修改,所以就没有多大关系)。
const与extern一起使用时,其特性与extern一样。
[code]
extern const char g_szBuffer[]; //写入 .h中
const char g_szBuffer[] = "123456"; // 写入.cpp中
[/code
相关推荐:
ChatGPT页面空白无法登录?如何解决这一困扰?,温州ai字幕生成
ChatGPT恢复正常使用时间,提升你的工作与生活效率,联想电脑AI0510
ChatGPT服务异常:为何影响到你的工作和生活?如何有效解决?,ai怎么保持圆角不变
360AI写作怎样?助力创作的新风尚,ai到访
seo需要学习什么语言,做seo需要懂什么技术 ,ai软件制作教程
SEO联系:如何通过SEO优化提升您的网络营销效果,企业营销推广获客
ChatGPT不登录,如何畅享AI的智能服务?,手信ai
AI提炼主要内容:如何让信息更精准、高效、易懂,女军人ai
AI文章概括缩写:让内容高效获取的智能工具,ai 处理文件
seo经验是什么,seo进阶 ,ai矿机
AI自动生成:开启智能时代的无限可能,ai熊熊图片
SEO有点:揭秘优化之道,提升网站排名的秘诀,广州抖音seo厂家地址
ChatGPT发生故障,背后隐藏着哪些不为人知的原因与挑战?,硬件ai和软件ai
ChatGPT为什么访问不了?全面解析及解决方案,ai首页设计
软件我在AI:改变未来的智能助手
seo类文章是什么,seo技术文章 ,ai3.5-ai聊天
ChatGPT连了外网也登不了?如何解决这一问题,重新畅享AI助力!,ai少女大瓜
ChatGPT为什么访问不了了?了解背后的原因与解决方法,AI怎么拖入路径
SEO优化与SEM广告:提升品牌曝光与流量的双重利器,ai接回头
SEO排位:如何通过精准策略提升网站排名,获得流量与转化,林海网络推广营销
BingAdapter设置数据后没有显示数据?解决方案在这里!,ai中如何把图变形
seo自己做什么,自己做seo需要花钱吗 ,ai850775
什么是seo运营,什么是seo营销 ,ai绘画沙地
AI生成网页模板,轻松打造专业网站,ai网格画法
用AI写文章会不会查重率高?破解写作困扰的真相
企业营销:如何在竞争激烈的市场中脱颖而出,美装网站Seo优化
ChatGPT中文版下载,开启智能对话新体验,婚纱ai男
优化投入,让企业更具竞争力,怎样推广营销方案
ChatGPT中显示已进行一处编辑,但看不到内容?你需要了解的隐藏问题!,ai敲
专业关键词助力SEO优化,让你的内容脱颖而出,东营响应式网站优化
一键创作,助你轻松实现创意梦想,最低价刷粉网站推广
ChatGPT美区要梯子吗?轻松畅享全球智能聊天体验,ai音位
360优化关键词-提升网站流量与排名的秘密武器,美拍ai
SEO收集:如何通过有效数据收集提升网站排名,鞍山商城网站建设报价
乘风SEO:突破困境,领航网络营销新时代,武汉靠谱的关键词排名
2025年SEO最新技术:让你的网站脱颖而出!,ai少女特色
seo运营经理是什么,seo和运营的区别 ,皖妍ai宁慕晴o
“标题制造机”:颠覆内容创作的秘密武器,助你轻松打造吸引力十足的标题,景区线上推广用哪些网站
智能AI写作生成:如何借助人工智能提升创作效率与质量
seo跟sem是什么,seo和sem的概念 ,惠威的ai功能
SEO手段:提升网站流量的制胜法宝,idc网站怎么推广
怎么识别是AI写的文章
SEO优化的话题:助力企业成功的关键,夏杰ai智能管家
ChatGPT回答问题,网页无法线下滚动?解决方案轻松get!,谷歌AI铃声
SEO要求:如何优化网站提升搜索引擎排名,助力企业获取更多流量,鹤山区网站运营推广
SEO新手必看:从零开始SEO优化的终极指南,随州网站优化技巧
SEO领先-如何借助SEO技术让您的网站脱颖而出,seo是黑客吗
ChatGPT常见问题汇总:解答你关于AI的一切疑惑,松鼠ai智能教适应教育
为什么选择收录网站是企业在线营销的必备策略,全网营销与推广
ChatGPT的诞生,预示着人工智能大规模应用的时代已经来临,ai此生不渝