C++中extern与static使用注意事项
本文最后更新于 894 天前,其中的信息可能已经有所发展或是发生改变。

本文为个人理解,若哪里讲的不妥,欢迎大佬指出

使用static或extern关键字定义变量的注意事项

这里开门见山,注意事项就是static关键字定义的全局变量只能作用于这个变量所在文件,若其他地方使用extern引用该全局变量,就会出现编译错误,下面是这句话的详细解释

再详细解释说明注意事项之前,先引用网上对这两个关键字的解释

static声明一个变量的作用:
对局部变量使用static声明,则该为该变量分配的空间在整个程序的执行过程中都存在。
对于局部变量无论是否使用static关键字声明,都不能使用extern关键字进行外部变量引用
对于全局变量,不实用static关键字声明,可以通过extern关键字进行外部变量的引用。
对全局变量使用static声明,则该变量只作用于本文件模块,不能通过extern关键字进行引用。
原因:
对于局部变量(未加static声明)来说,只有在函数调用时才为其分配存储空间,函数调用结束后,其存储空间被释放,extern关键字声明的变量找不到该的定义,所以程序报错;对于添加static声明的局部变量来说,虽然其变量的存储空间一直持续到程序执行结束,但是,添加static关键字后,使得该变量只对变量所定义的文件可见,extern 声明变量亦不能找到变量所定义的地方。
对于全局变量(未加static声明)来说,只要该变量定义,即存在存储空间,可以通过extern声明来找到该变量定义的地方;对于添加static声明的全局变量来说,该全局变量即变成其所定义的文件内部可见的变量,不能通过extern声明来找到该变量的定义位置,故函数会报错。
————————————————
版权声明:本文为CSDN博主「Doveal呢」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/h522955926/article/details/82026152

感谢这位博主的解释,已经相当详细和系统了,但是为了便于理解,我要对原文在做进一步的解释

通常所说的变量应该是所有变量的总称,变量可以以多种方式进行划分

而按照作用域(即一个变量的生命周期),变量大致可分为以下几种

  • 全局变量(陪伴程序从运行到最后的变量,这是真爱啊)
  • 局部变量(一般存在于函数内部,只在函数调用时才为其分配空间的变量)
  • 局部静态变量(存在于函数内部,却也陪伴程序从运行到最后的变量,估计也是个真爱)
  • 成员变量(对象内的成员)

注意局部变量不一定一定要存在于函数内部,解释这个得先理解C++作用域是怎么定义的

我们经常能在C或C++代码中看到一对一对的花括号(花括号的成双成对的,我裂开了),这让初学者看到可能会有莫名的恐惧。。

但这一对一对的花括号就可以说是一个个的作用域,哪一对花括号离局部变量最近且局部变量恰好被这一对花括号包裹着,那么这对花括号包裹的区域就是该局部变量的作用域,离开作用域,局部变量便不复存在

举个例子有以下代码:

void add()
{
int x,y;
x=1;
y=2;
cout << x+y<< endl;
}

那么x,y只在这个函数体内有效

再例如:

void add()
{
int x,y;
x=1;
y=2;
{
int z =  x + y;
}
cout << z << endl;
}

此时就出现了错误,C++中花括号出现嵌套情况时,里面可以看到外面,外面的看不到里面,就像一扇无形的屏蔽窗,阻断了你我之间的联系

结合代码,就是花括号里的z可以访问x,y,当这段代码运行出包裹着z的花括号时,z就不复存在了,那么也就是说cout输出的是一个根本不存在的变量,细思极恐。。

说完作用域,就来讲讲static关键字对局全局和对局部变量的不同作用

通过上面,我们知道全局变量是贯穿程序运行始终的。在实际的编程中,我们一般都将程序分割为多个文件来存储,那么难免会遇到跨文件访问变量的情况。全局变量也就自然衍生出了文件内全局和全文件全局变量。默认定义的全局变量都是全文件全局变量

那如果我将程序分成很多个模块,每一个模块都有一个全局变量来标注模块版本,且都以g_iVersion命名,在最后的编译过程中不就会出现重定义错误了嘛。编译器才不管你的g_iVersion是指哪个文件的,因此这时,我们就要手动告诉编译器,这个全局变量,我只想要在这个文件内使用,那如何告诉它呢?使用static关键字重新定义 g_iVersion 就可以了,最后的代码就像这个样子

static int g_iVersion; //定义当前模块版本

终于回到文章之初的注意事项了,extern变量可以引用其他文件定义的全局变量,解释了这么多,现在终于能理解为何在其它文件用extern引用当前文件中被static修饰了的全局变量,会出现错误了吧

继续说说static对局部变量的作用,还是从上面可知,局部变量一出作用域便不复存在,但如果我想要这个变量再出作用域时继续存在呢?C++考虑到了这种情况,你要的功能static还是能满足你,真是一步到位的关键字啊!

还是来举个例子吧

如果我想调试一个函数在程序中被执行了多少次,我们不难想到使用全局变量可以很好地解决这个问题,那如果我懒,只想写在函数体内呢?

void func()
{
static int s_iCount;//函数调用计次
cout << "[调试]函数第" << s_iCount << "次调用" << endl;
//...Details
}

上面的s_iCount就是函数的调用计数了,我们输出它,就可以得到函数的调用计数

全文总结,static对于全局和局部变量,具有不同的作用。若用extern引用其他文件里static修饰了的全局变量,就会引发编译错误

分享一下自己最近栽在static和extern两关键字上的经历

故事还得从一只蝙蝠说起

哦哦,扯远了哈!

武汉加油,中国加油

故事还得从一个程序说起,由于疫情的影响,寒假延长了,苦逼的高二党也终于有时间泡在代码里了,为此我准备为我的点名软件添加更新功能,那自然少不了手写一个服务端,恰好最近学了C++网络编程,准备拿这个程序开开刀,于是……

刚开始真是一帆风顺,神挡杀神佛挡杀佛,BUG挡…..就修嘛

既然是服务端,少不了日志系统,为此我专门写了一个CLogger类来将日志输出到控制台

我的服务端采用了多线程处理客户端任务,每个线程都会输出消息,那少不了线程同步技术,来保证日志系统中日志的有序性

为了保证主线程子线程都能使用同一个日志系统,我在头文件里定义了一个静态变量s_logger(不规范写法,头文件一般不直接定义变量)

然后就遇到了s_logger初始化两次的奇葩情况

失败的输出

除此之外还可以看出因为s_logger的两次初始化(主线程和子线程各用各的s_logger),日志的输出格式也乱了

经过后期调试,我发现是因为头文件被两次包含,才导致了s_logger重复初始化,于是我将定义迁移至cpp文件,很不幸,直接给我个编译错误

编译器错误

我太难了

又经过了N次寻求帮助无果后,我才发现就是本篇文章所讲的问题导致了编译错误。。。。哎,有的时候就是不经意间才会发现,那出错的地方就在你眼前…

我在类的cpp文件里用static定义了s_logger,并在头文件里用extern声明,到这里并没有错,错就错在我妄想在main文件里使用s_logger……当然会出现无法解析的外部命令。s_logger被类文件独享了,你main文件还想要分杯羹,一边凉快去吧

经过修改后运行:

最后的成功输出

算是成功修复日志器初始化两次和输出异常这两个bug了吧

从日志日期不难看出,这个问题困扰了我将近六天时间!!!

此外我在C++吧里求助过这个问题,不过被我自己解决了,哈哈

帖子传送门:

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
Source: https://github.com/zhaoolee/ChineseBQB
Source: https://github.com/zhaoolee/ChineseBQB
Source: https://github.com/zhaoolee/ChineseBQB
颜文字
Emoji
小恐龙
花!
滑稽大佬
演奏
程序员专属
上一篇
下一篇