C++:存儲(chǔ)持續(xù)性抖甘、作用域和鏈接性(第九章)

相關(guān)概念:

1.自動(dòng)存儲(chǔ)持續(xù)性:聲明的變量的存儲(chǔ)持續(xù)性是自動(dòng)的,在開始執(zhí)行其函數(shù)或代碼塊時(shí)被創(chuàng)建闷尿,執(zhí)行完畢后他們使用的內(nèi)存被釋放塑径。2種存儲(chǔ)持續(xù)性為自動(dòng)變量
2.靜態(tài)存儲(chǔ)持續(xù)性:在函數(shù)外定義變量或使用關(guān)鍵字static定義的變量。在程序整個(gè)運(yùn)行過程中都存在悠砚。3種存儲(chǔ)持續(xù)性為靜態(tài)的變量晓勇。
3.線程存儲(chǔ)持續(xù)性:多核處理器,CPU同時(shí)執(zhí)行多個(gè)任務(wù)灌旧。暫時(shí)不學(xué)绑咱。
4.動(dòng)態(tài)存儲(chǔ)持續(xù)性:用new運(yùn)算符分配的內(nèi)存將一直存在,直到delete將其刪除枢泰。存儲(chǔ)在heap中描融。
5.作用域:簡(jiǎn)單來說就是valid的范圍,分為局部作用域和全局作用域衡蚂。
6.鏈接性(linkage):名稱如何在不同單元間共享窿克。鏈接性為外部的名稱可在文件間共享,為內(nèi)部的名稱只能有一個(gè)文件中的函數(shù)共享毛甲。自動(dòng)變量名稱無linkage年叮。

1. 自動(dòng)存儲(chǔ)持續(xù)性

1.默認(rèn)情況下,函數(shù)中聲明的函數(shù)參數(shù)和變量的存儲(chǔ)持續(xù)性為自動(dòng)玻募,作用域?yàn)榫植恐凰穑瑳]有l(wèi)inkage。若有相同的名稱且作用域范圍并不全不相同七咧,則有最近代碼塊優(yōu)先原則跃惫。新定義的名稱將隱藏以前的定義。但是跳出新定義名稱的作用域之后艾栋,以前定義的名稱又可以使用爆存。兩個(gè)定義互相獨(dú)立
2.自動(dòng)變量和棧
棧的默認(rèn)長(zhǎng)度取決于實(shí)現(xiàn),程序使用兩個(gè)指針來跟蹤棧蝗砾,一個(gè)指針指向棧底先较,一個(gè)指向棧頂(下一個(gè)可用單元)携冤。當(dāng)函數(shù)被調(diào)用時(shí),自動(dòng)變量加入到棧闲勺,棧頂指針指向變量后面的下一個(gè)可用內(nèi)存單元噪叙。函數(shù)結(jié)束時(shí),棧頂指針被重置為函數(shù)被調(diào)用前的值霉翔。注意新值沒有被刪除,只是不再被標(biāo)記苞笨。下次使用這個(gè)空間的時(shí)候他們會(huì)被覆蓋债朵。
3.寄存器變量
關(guān)鍵字register,建議編譯器使用cpu寄存器來存儲(chǔ)自動(dòng)變量瀑凝,旨在提高訪問變量的速度序芦。

//關(guān)于自動(dòng)變量相同名稱和作用域
int num=10;
{ 
        cout<<"1.  num is "<<num<<endl;
        int num=5, num2=9;
        cout<<"2.   num is "<<num<<endl
        <<"    num2 is "<<num2<<endl;
}
cout<<"3.   num is "<<num<<endl;
//此時(shí)若打印cout<<num2<<endl;將發(fā)生錯(cuò)誤
return 0;
}

輸出結(jié)果為:


輸出結(jié)果

在代碼塊內(nèi)部,新定義的變量覆蓋了原來定義的變量粤咪,但是執(zhí)行完代碼塊跳出谚中,原來定義的變量又上位了,而且不能使用num2了寥枝,已經(jīng)被銷毀了宪塔。

2. 靜態(tài)持續(xù)變量:

3種鏈接性:

  • 外部鏈接性(可在其他文件訪問),
  • 內(nèi)部鏈接性(只能在當(dāng)前文件訪問)
  • 無鏈接性(只能在當(dāng)前函數(shù)或代碼塊訪問)囊拜。
    由于靜態(tài)變量的數(shù)目在程序運(yùn)行期間是不變的某筐,所以不需要特殊裝置如棧來管理。如果沒有顯示初始化靜態(tài)變量冠跷,系統(tǒng)默認(rèn)設(shè)置為0.
    聲明方式:
    1.外部鏈接性變量:在代碼塊外面聲明南誊。
    2.內(nèi)部鏈接性變量:代碼塊外部用static聲明。
    3.無鏈接性變量:代碼塊內(nèi)部static聲明蜜托。
    4.靜態(tài)變量初始化:分為常量表達(dá)式初始化(靜態(tài))和動(dòng)態(tài)初始化抄囚。

①. 靜態(tài)持續(xù)性、外部鏈接性:

1.單定義規(guī)則(One Definition Rule, ODR):變量只能有一次定義橄务,但可能多個(gè)文件要使用同一個(gè)變量幔托,所以C++提供了2種變量聲明:一是定義聲明,給變量分配內(nèi)存空間仪糖;另一個(gè)是引用聲明柑司,不給變量分配內(nèi)存空間。referencing declaration使用關(guān)鍵字extern锅劝,且不進(jìn)行初始化攒驰,否則將聲明為定義。
2.全局變量寫出來的程序并不總是可靠故爵。正常情況下應(yīng)使用局部變量玻粪,在知曉的情況下才傳遞數(shù)據(jù)隅津。但全局變量也有advantage,尤其適用于表示常量數(shù)據(jù)如十二個(gè)月等劲室,使用const來防止篡改伦仍。

下面演示聲明外部鏈接全局變量、動(dòng)態(tài)初始化很洋、常量表達(dá)式初始化充蓝、引用聲明、外部鏈接性設(shè)置為內(nèi)部鏈接性

// text1.cpp 
#include<iostream>
using namespace std;
int x=3; 
int mul(int num1,int num2); 
int y = 10; // const-expression initialization
void show();

int main()
{
    const int z = mul(x,y); // dynamic-initialization 
    cout<<"x is "<<x<<endl
    <<"y is "<<y<<endl
    <<"z is "<<z<<endl;
    show();
    return 0;
}
#include<iostream>
using namespace std;
extern int x;    //引用聲明
static int y=4;    //設(shè)置為內(nèi)部鏈接
int mul(int num1,int num2); 
void show();

int mul(int num1,int num2)
{
    return x*(num1+num2);
}
 
void show()
{
    cout<<"internal y is "<<y<<endl;
}

PS 函數(shù)原型在兩個(gè)源代碼文件中都要有喉磁,因?yàn)闆]有head file谓苟。
如果報(bào)錯(cuò)說編譯不成功,試著在項(xiàng)目中移除文件并重新添加协怒。
而且要注意盡量編寫完成再編譯涝焙。

運(yùn)行結(jié)果如下:
靜態(tài)變量

②靜態(tài)持續(xù)性、內(nèi)部鏈接性:

將static限定符用于作用于為整個(gè)文件的變量時(shí)孕暇,該變量的鏈接性為內(nèi)部仑撞。如果要在不同文件中使用相同的名稱來表示變量(這個(gè)名稱變量在其他文件已經(jīng)定義為外部鏈接變量),則聲明方法為static妖滔,指明該標(biāo)識(shí)符的鏈接性為內(nèi)部隧哮。
(聲明方法上文圖已寫出)

③靜態(tài)持續(xù)性、無鏈接性:

在代碼塊中用static限定符定義變量座舍,將導(dǎo)致局部變量的存儲(chǔ)持續(xù)性為靜態(tài)近迁。當(dāng)代碼塊不處于活動(dòng)狀態(tài)時(shí),該變量也存在簸州。非常適用于兩次函數(shù)調(diào)用之間鉴竭,其值是不會(huì)變的。初始化過一次該變量岸浑,以后將不會(huì)像自動(dòng)變量一樣被初始化了搏存。

#include<iostream>
using namespace std;
void func();
int main()
{
    for(int i=0;i<10;i++)
        func();
    return 0;
}

void func()
{
    static int times=0;  //靜態(tài)持續(xù)變量 ,無鏈接性
    cout<<"times is now: "<<times++<<endl;
}

運(yùn)行結(jié)果為
無鏈接性靜態(tài)持續(xù)變量

3. 說明符和限定符

①存儲(chǔ)說明符(C++11)
register矢洲、 static璧眠、 extern、 thread_local(線程)读虏、 mutable
cv限定符:const和volatile
volatile表示即使程序代碼沒有對(duì)內(nèi)存單元進(jìn)行修改责静,其值也可能發(fā)生變化【暫時(shí)不學(xué)】
mutable(可變的)
用來指出即使結(jié)構(gòu)或類的變量為const,某個(gè)成員也可以被修改盖桥,定義在被修改成員之前灾螃。
const
全局const int x=10 equal to static int x=10;
因?yàn)榧僭O(shè)在頭文件中有如上聲明,然后每個(gè)源代碼文件預(yù)處理器都將頭文件的內(nèi)容包含到源代碼文件中揩徊,如果const聲明的鏈接性是外部的腰鬼,則違反了ODR嵌赠。
如果程序員希望某個(gè)常量的鏈接性為external,則用extern關(guān)鍵字來覆蓋默認(rèn)的內(nèi)部linkage熄赡,
extern const int y=50;

4.函數(shù)和鏈接性:

所有函數(shù)的存儲(chǔ)持續(xù)性都自動(dòng)為靜態(tài)姜挺。默認(rèn)情況下,函數(shù)的鏈接性為外部彼硫。也可以使用static將函數(shù)的鏈接性設(shè)置為內(nèi)部炊豪,但必須同時(shí)在原型和定義中使用該關(guān)鍵字。非內(nèi)聯(lián)函數(shù)也適用于ODR拧篮,但內(nèi)聯(lián)函數(shù)不受這項(xiàng)規(guī)則約束溜在。

5.語言鏈接性(重載函數(shù)的原理)

concept:同一個(gè)名稱可能對(duì)應(yīng)多個(gè)函數(shù),必須將這些函數(shù)翻譯成不同的符號(hào)名稱他托。如C++可能將spiff(int)翻譯成_spiff_i,而將spiff(double仆葡,double)翻譯成_spiff_d_d赏参,這就叫做語言鏈接性。要使用C語言鏈接來查找函數(shù)或者是C++(兩種鏈接性不同)沿盅,有如下約定:
extern “C” void spiff(int);
extern void spiff(int) = extern “C++” void spiff(int) (顯示指出)

6.存儲(chǔ)方案和動(dòng)態(tài)分配

由new分配適當(dāng)?shù)淖止?jié)的內(nèi)存將一直保存在內(nèi)存中把篓,直到使用delete運(yùn)算符將其釋放。但當(dāng)包含該聲明的語句塊執(zhí)行完畢時(shí)腰涧,指向該內(nèi)存的指針將消失韧掩。如果希望下一個(gè)函數(shù)能使用到該內(nèi)容,則必須將其地址傳遞或返回給該函數(shù)窖铡。但如果聲明的指針為外部的疗锐,則可以一直使用。

關(guān)于new的初始化:

int *pi = new int (6); //初始化*pi為6
int *pt = new int [4] {1,2,3,4}; //初始化數(shù)組

struct where{double x, double y, double z};
where * pr = new where {1.0,2.0,3.0}; //初始化結(jié)構(gòu)
delete pi;
delete [] pt; //刪除數(shù)組內(nèi)存
delete pr;

定位運(yùn)算符:

new負(fù)責(zé)在堆heap中找到一個(gè)足以滿足要求的內(nèi)存塊费彼,也可以讓你能夠指定要使用的內(nèi)存塊(定位new運(yùn)算符)滑臊。
要使用此特性首先要包含new頭文件。
使用定位new箍铲,指定的內(nèi)存是靜態(tài)內(nèi)存雇卷,而delete只能用于指向常規(guī)new運(yùn)算符分配的heap memory,所以不能夠delete定位new運(yùn)算符

下面寫一段代碼颠猴,通過比較地址來認(rèn)識(shí)常規(guī)new運(yùn)算符和定位new運(yùn)算符:

#include<iostream>
#include<new>
using namespace std;
const int BUF=600;
char buff [BUF];  //這是定位new運(yùn)算符將要指定的地址
int main()
{
    const int n=5;
    int * pt1, *pt2,i;
    pt1 = new int[n];
    pt2 = new (buff) int[n]; //指定地址用這樣的形式聲明
    cout<<"內(nèi)存地址:\n"<<"heap address: "<<pt1
    <<"; static address: "<<(void *)buff<<endl;
    for(i=0;i<n;i++)
    {
        pt1[i] = pt2[i]= i;
        cout<<pt1[i]<<" at "<<&pt1[i]<<"; ";
        cout<<pt2[i]<<" at "<<&pt2[i]<<"; \n";
    }
    delete []pt1;
    int *pt3, *pt4;
    pt3 = new int[n];
    pt4 = new (buff+1) int[n];
    cout<<"內(nèi)存地址:\n"<<"heap address: "<<pt3
    <<"; static address: "<<(void *)(buff+1)<<endl;
    for(i=0;i<n;i++)
    {
        pt3[i] = pt4[i]= i;
        cout<<pt3[i]<<" at "<<&pt3[i]<<"; ";
        cout<<pt4[i]<<" at "<<&pt4[i]<<"; \n";
    }
    delete [] pt3;  
    return 0;
}

運(yùn)行結(jié)果如下:


定位new運(yùn)算符和new存放內(nèi)存理解

可以看到heap的地址為1520結(jié)尾关划,指定的地址為7040結(jié)尾,每個(gè)int占4字節(jié)翘瓮。再第一次分配完之后delete pt1之后贮折,堆同樣的地址又被占領(lǐng)。(下面是第一次分配完之后不釋放pt1內(nèi)存單元)

image.png

發(fā)現(xiàn)沒有釋放內(nèi)存單元资盅,heap將繼續(xù)往下堆砌pile脱货。

總結(jié):

image.png
  • 存儲(chǔ)持續(xù)性就是表示變量在程序生存的狀態(tài)岛都,
    要么執(zhí)行完一個(gè)范圍(代碼塊或函數(shù))就見光死(Auto)
    要么在此程序運(yùn)行期間都堅(jiān)強(qiáng)地活著或多個(gè)文件間自由穿梭(Static)
    要么我要你有你才有,我要你亡say 拜拜(dynamic)
  • 作用域就是變量可以發(fā)揮作用的區(qū)域
    小明星就在小區(qū)域(局部振峻,代碼塊和函數(shù))
    大明星就在大區(qū)域(多個(gè)文件)
  • 鏈接性就是你能否有足夠的權(quán)利去走到更遠(yuǎn)的地方
    無鏈接性那你只能在代碼塊或函數(shù)內(nèi)活動(dòng)
    內(nèi)部鏈接性那你可以在整個(gè)程序內(nèi)活動(dòng)
    外部鏈接性則可以穿梭于多個(gè)文件
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末臼疫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扣孟,更是在濱河造成了極大的恐慌烫堤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤价,死亡現(xiàn)場(chǎng)離奇詭異鸽斟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)利诺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門富蓄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慢逾,你說我怎么就攤上這事立倍。” “怎么了侣滩?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵口注,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我君珠,道長(zhǎng)寝志,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任策添,我火速辦了婚禮材部,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唯竹。我一直安慰自己败富,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布摩窃。 她就那樣靜靜地躺著兽叮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猾愿。 梳的紋絲不亂的頭發(fā)上鹦聪,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蒂秘,去河邊找鬼泽本。 笑死,一個(gè)胖子當(dāng)著我的面吹牛姻僧,可吹牛的內(nèi)容都是我干的规丽。 我是一名探鬼主播蒲牧,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赌莺!你這毒婦竟也來了冰抢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤艘狭,失蹤者是張志新(化名)和其女友劉穎挎扰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巢音,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡遵倦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了官撼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梧躺。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖傲绣,靈堂內(nèi)的尸體忽然破棺而出掠哥,到底是詐尸還是另有隱情,我是刑警寧澤斜筐,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蛀缝,受9級(jí)特大地震影響顷链,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屈梁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一嗤练、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧在讶,春花似錦煞抬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至曙强,卻和暖如春残拐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碟嘴。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工溪食, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娜扇。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓错沃,卻偏偏與公主長(zhǎng)得像栅组,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子枢析,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355