C/C++的關(guān)鍵字之static你了解嗎
C語言
隱藏
場(chǎng)景演示
當(dāng)我們同時(shí)編譯多個(gè)文件時(shí),所有未加static前綴的全局變量和函數(shù)都具有全局可見性。會(huì)導(dǎo)致符號(hào)表認(rèn)為存在同名全局變量和函數(shù)發(fā)生碰撞。
場(chǎng)景:全局的變量/函數(shù)在.h
中會(huì)在多個(gè).cc
文件中擁有且全局可見有鏈接問題。
a.h
#pragma once #include<stdio.h> void Test() { printf("I am test..\n"); }
b.c
#include"a.h" void call() { Test(); }
c.c
#include"a.h" int main() { Test(); }
makefile
all:c c:c.o b.o gcc -o $@ $^ c.o:c.c gcc -c $^ b.o:b.c gcc -c $^ .PHONY:clean clean: rm -rf *.o c
運(yùn)行結(jié)果
此時(shí)查看b.o
和c.o
符號(hào)表。(readelf -s xxx.o)
可以看到雙方的.o
符號(hào)表中都認(rèn)為有一個(gè)GLOBAL
全局函數(shù)的Test
,兩者匯編階段形成的符號(hào)表此時(shí)在匯總的階段就會(huì)產(chǎn)生同名全局函數(shù)沖突。
此時(shí)在兩者的二進(jìn)制文件里都認(rèn)為各自擁有Test()
函數(shù),且都在全局。而全局函數(shù)只能有一個(gè)名字(注:重載是底層重新命名了)。雖然我們知道兩個(gè)Test()是同一個(gè),但是link的時(shí)候認(rèn)為有兩個(gè)同名函數(shù)實(shí)現(xiàn),因此報(bào)link錯(cuò)。
解決方法
聲明和定義分離
養(yǎng)成聲明和定義分離的習(xí)慣,在.h中只聲明不定義。在.c文件中定義。
a.h
#include<stdio.h> void Test();
a.c
#include"a.h" void Test() { cout<<"I am test..."<<endl; }
makefile
all:c c:c.o b.o a.o gcc -o $@ $^ c.o:c.c gcc -c $^ b.o:b.c gcc -c $^ a.o:a.c gcc -c $^ .PHONY:clean clean: rm -rf *.o c
為什么此時(shí)就可以正常運(yùn)行了?
依然查看符號(hào)表,可以發(fā)現(xiàn)b.o和c.o中此時(shí)只是給Test
聲明留了一個(gè)全局的NOTYPE位置。
而在a.o中定義Test()
,因此a.o中是func類型。
最后三個(gè).o文件鏈接的時(shí)候確定Test()
實(shí)際在最后生成的.out文件中的虛擬內(nèi)存地址。運(yùn)行時(shí)加載到內(nèi)存中,之后的詳細(xì)過程就是linux創(chuàng)建進(jìn)程中的事情。
使用static關(guān)鍵字及缺陷
那如果我就是想要直接在.h中存放一個(gè)公共的全局的對(duì)象來供其他所有文件使用呢?使用static關(guān)鍵字。
a.h
#pragma once #include<stdio.h> static void Test() { printf("I am test..\n"); }
代碼結(jié)構(gòu)
:
此時(shí)為什么又成立呢?兩者.o文件中為什么對(duì)同名的全局函數(shù)包容了呢?可以看到此時(shí)兩者的符號(hào)表中仍然是func,按照?qǐng)鼍把菔局械睦?,?yīng)該報(bào)錯(cuò)的。
此時(shí)反匯編查看Test()函數(shù)地址。我們發(fā)現(xiàn)此時(shí)生成了兩個(gè)test函數(shù),不過函數(shù)地址不同。
結(jié)論:static函數(shù)作用域僅在自己所在文件,其實(shí)是編譯后不同文件下的同名static函數(shù)會(huì)有不同的內(nèi)部命名
不同.c文件include了static變量之后該變量只在各自包含該變量的.c中可見。
既然生成了兩份,我們就可以發(fā)現(xiàn),如果是一個(gè)靜態(tài)的全局變量,我們分別進(jìn)行修改實(shí)際上對(duì)兩個(gè)不同的變量進(jìn)行修改的。如果要解決全局變量統(tǒng)一性訪問,保證全局變量不可變即可。另外一種方式就是使用單例模式。
a.h
#pragma once #include<stdio.h> static int a =0;
b.h
#include"a.h" void call();
b.c
#include"b.h" void call() { a = 1; printf("a=%d\n",a); }
c.c
#include"b.h" int main() { a=2; printf("a=%d\n",a); call(); printf("a=%d\n",a); }
保持變量內(nèi)容的持久
- 全局靜態(tài)變量
在全局變量前加上關(guān)鍵字static,全局變量就定義成一個(gè)全局靜態(tài)變量。
內(nèi)存中位置:靜態(tài)存儲(chǔ)區(qū),在整個(gè)程序運(yùn)行期間移植存在。
初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被自動(dòng)初始化為0(自動(dòng)對(duì)象的值是任意的,除非他是被顯示初始化)。
作用域:全局靜態(tài)變量是從定義指出開始,到文件結(jié)尾,在聲明他的文件之外是不可見的。
- 局部靜態(tài)變量
內(nèi)存位置:靜態(tài)存儲(chǔ)區(qū)
初始化:未經(jīng)初始化的局部靜態(tài)變量會(huì)被自動(dòng)初始化為0(自動(dòng)對(duì)象的值是任意的,除非他是被顯示初始化)。
作用域:為局部作用域,當(dāng)定義他的函數(shù)或者語句塊結(jié)束時(shí),作用域結(jié)束。但是當(dāng)局部靜態(tài)變量離開作用域后,并沒有被銷毀,依然駐留在內(nèi)存中,只不過我們不能再對(duì)它進(jìn)行訪問,直到該函數(shù)再次被調(diào)用,并且值不變。
如下的count
變量作用域在test
函數(shù)中,而生命周期是整個(gè)程序。在第一次進(jìn)入test()的時(shí)候會(huì)初始化,之后進(jìn)入test()就不再執(zhí)行第5行代碼了。
#include<stdio.h> void test() { static int count =0; count++; } int main() { for(int i =0 ; i < 10 ; i++ ) test(); }
默認(rèn)初始化為0
默認(rèn)初始化為0:在靜態(tài)存儲(chǔ)區(qū),內(nèi)存中所有的字節(jié)默認(rèn)值都是0x00。
#include <stdio.h> int a; int main(void) { int i; static char str[10]; printf("integer: %d; string: (begin)%s(end)", a, str); return 0; }
Cpp
static類成員變量
聲明為static的類成員稱為類的靜態(tài)成員,用static修飾的成員變量,稱之為靜態(tài)成員變量;靜態(tài)的成員變量一定要在類外進(jìn)行初始化。
- 靜態(tài)變量屬于整個(gè)類,所有對(duì)象,生命周期在整個(gè)程序間運(yùn)行
- 在類成員函數(shù)中,可以隨便訪問
static類成員方法
用static修飾的成員函數(shù),稱之為靜態(tài)成員函數(shù)。(因?yàn)樵摮蓡T變量沒有this指針)
static成員函數(shù),沒有this指針,不使用對(duì)象就可以調(diào)用–>fun::。
靜態(tài)成員函數(shù)可以調(diào)用非靜態(tài)成員函數(shù)(/成員)嗎?不行。沒有this指針
非靜態(tài)成員函數(shù)可以調(diào)用類的靜態(tài)成員函數(shù)嗎?可以
class Date { public: Date(int year=0,int month=1,int day=1) { } void f1() { } static void f2() { f1();//沒有this指針 } private: }
class Date{ public: void f3() { f4();//突破類域+訪問限定符就可以訪問 Date::f4();/對(duì)象.f4() //類里面是一個(gè)整體都在類域中,類里面不受訪問限定符限制 } static void f4() { } private: };
單例模式
- 單例模式
一個(gè)類只能創(chuàng)建一個(gè)對(duì)象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn),該實(shí)例被所有程序模塊共享。比如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再通過這個(gè)單例對(duì)象獲取這些配置信息,這種方式簡化了在復(fù)雜環(huán)境下的配置管理。
1.如何保證全局(一個(gè)進(jìn)程中)只有一個(gè)唯一的實(shí)例對(duì)象
參考只能在堆上創(chuàng)建對(duì)象和在棧上創(chuàng)建對(duì)象,禁止構(gòu)造和拷貝構(gòu)造及賦值。
提供一個(gè)GetInstance獲取單例對(duì)象。
2.如何提供只有一個(gè)實(shí)例呢?
餓漢模式和懶漢模式。
3.使用場(chǎng)景
由于全局的變量在.h
中會(huì)在多個(gè).cc
文件中擁有且可見容易有鏈接問題。而static
又只能在當(dāng)前文件可見。因此真要處理成全局的就使用單例模式。
具體的單例模式在特殊類設(shè)計(jì)中提及。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語言實(shí)現(xiàn)輸入ascii碼,輸出對(duì)應(yīng)的字符方式
這篇文章主要介紹了C語言實(shí)現(xiàn)輸入ascii碼,輸出對(duì)應(yīng)的字符方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01C++實(shí)現(xiàn)LeetCode(102.二叉樹層序遍歷)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(102.二叉樹層序遍歷),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C/C++數(shù)據(jù)對(duì)齊詳細(xì)解析
通常我們?cè)趯懘a的時(shí)候是不需要考慮對(duì)齊的影響的,都是依賴編譯器來為我們選擇適合的對(duì)齊策略,我們也可以通過傳遞給編譯器預(yù)編譯指令來指定數(shù)據(jù)對(duì)齊的方法2013-10-10C++實(shí)現(xiàn)俄羅斯方塊(linux版本)
這篇文章主要為大家詳細(xì)介紹了linux版本C++實(shí)現(xiàn)俄羅斯方塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07