相關(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é)果為:
在代碼塊內(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;
}
運(yùn)行結(jié)果如下:PS 函數(shù)原型在兩個(gè)源代碼文件中都要有喉磁,因?yàn)闆]有head file谓苟。
如果報(bào)錯(cuò)說編譯不成功,試著在項(xiàng)目中移除文件并重新添加协怒。
而且要注意盡量編寫完成再編譯涝焙。
②靜態(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é)果為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é)果如下:
可以看到heap的地址為1520結(jié)尾关划,指定的地址為7040結(jié)尾,每個(gè)int占4字節(jié)翘瓮。再第一次分配完之后delete pt1之后贮折,堆同樣的地址又被占領(lǐng)。(下面是第一次分配完之后不釋放pt1內(nèi)存單元)
發(fā)現(xiàn)沒有釋放內(nèi)存單元资盅,heap將繼續(xù)往下堆砌pile脱货。
總結(jié):
- 存儲(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è)文件