c语言输出11258循环,c/c++内存机制(一)(转)
一:C語言中的內(nèi)存機制
在C語言中,內(nèi)存主要分為如下5個存儲區(qū):
(1)棧(Stack):位于函數(shù)內(nèi)的局部變量(包括函數(shù)實參),由編譯器負責分配釋放,函數(shù)結束,棧變量失效。
(2)堆(Heap):由程序員用malloc/calloc/realloc分配,free釋放。如果程序員忘記free了,則會造成內(nèi)存泄露,程序結束時該片內(nèi)存會由OS回收。
(3)全局區(qū)/靜態(tài)區(qū)(Global Static Area):?全局變量和靜態(tài)變量存放區(qū),程序一經(jīng)編譯好,該區(qū)域便存在。并且在C語言中初始化的全局變量和靜態(tài)變量和未初始化的放在相鄰的兩個區(qū)域(在C++中,由于全局變量和靜態(tài)變量編譯器會給這些變量自動初始化賦值,所以沒有區(qū)分了)。由于全局變量一直占據(jù)內(nèi)存空間且不易維護,推薦少用。程序結束時釋放。
(4)C風格字符串常量存儲區(qū):?專門存放字符串常量的地方,程序結束時釋放。
(5)程序代碼區(qū):存放程序二進制代碼的區(qū)域。
二:C++中的內(nèi)存機制
在C++語言中,與C類似,不過也有所不同,內(nèi)存主要分為如下5個存儲區(qū):
(1)棧(Stack):位于函數(shù)內(nèi)的局部變量(包括函數(shù)實參),由編譯器負責分配釋放,函數(shù)結束,棧變量失效。
(2)堆(Heap):這里與C不同的是,該堆是由new申請的內(nèi)存,由delete或delete[]負責釋放
(3)自由存儲區(qū)(Free Storage):由程序員用malloc/calloc/realloc分配,free釋放。如果程序員忘記free了,則會造成內(nèi)存泄露,程序結束時該片內(nèi)存會由OS回收。
(4)全局區(qū)/靜態(tài)區(qū)(Global Static Area): 全局變量和靜態(tài)變量存放區(qū),程序一經(jīng)編譯好,該區(qū)域便存在。在C++中,由于全局變量和靜態(tài)變量編譯器會給這些變量自動初始化賦值,所以沒有區(qū)分了初始化變量和未初始化變量了。由于全局變量一直占據(jù)內(nèi)存空間且不易維護,推薦少用。程序結束時釋放。
(5)常量存儲區(qū): 這是一塊比較特殊的存儲區(qū),專門存儲不能修改的常量(如果采用非正常手段更改當然也是可以的了)。
三:堆和棧的區(qū)別
3.1 棧(Stack)
具體的講,現(xiàn)代計算機(馮諾依曼串行執(zhí)行機制),都直接在代碼低層支持棧的數(shù)據(jù)結構。這體現(xiàn)在有專門的寄存器指向棧所在的地址(SS,堆棧段寄存器,存放堆棧段地址);有專門的機器指令完成數(shù)據(jù)入棧出棧的操作(匯編中有PUSH和POP指令)。
這種機制的特點是效率高,但支持數(shù)據(jù)的數(shù)據(jù)有限,一般是整數(shù)、指針、浮點數(shù)等系統(tǒng)直接支持的數(shù)據(jù)類型,并不直接支持其他的數(shù)據(jù)結構(可以自定義棧結構支持多種數(shù)據(jù)類型)。因為棧的這種特點,對棧的使用在程序中是非常頻繁的 。對子程序的調(diào)用就是直接利用棧完成的。機器的call指令里隱含了把返回地址入棧,然后跳轉(zhuǎn)至子程序地址的操作,而子程序的ret指令則隱含從堆棧中彈出返回地址并跳轉(zhuǎn)之的操作。
C/C++中的函數(shù)自動變量就是直接使用棧的例子,這也就是為什么當函數(shù)返回時,該函數(shù)的自動變量自動失效的原因,因而要避免返回棧內(nèi)存和棧引用,以免內(nèi)存泄露。
3.2 堆(Heap)
和棧不同的是,堆得數(shù)據(jù)結構并不是由系統(tǒng)(無論是機器硬件系統(tǒng)還是操作系統(tǒng))支持的,而是由函數(shù)庫提供的。基本的malloc/calloc/realloc/free函數(shù)維護了一套內(nèi)部的堆數(shù)據(jù)結構(在C++中則增加了new/delete維護)。
當程序用這些函數(shù)去獲得新的內(nèi)存空間時,這套函數(shù)首先試圖從內(nèi)部堆中尋找可用的內(nèi)存空間(常見內(nèi)存分配算法有:首次適應算法、循環(huán)首次適應算法、最佳適應算法和最差適應算法等。os的基本內(nèi)容!!)。如果沒有可用的內(nèi)存空間,則試圖利用系統(tǒng)調(diào)用來動態(tài)增加程序數(shù)據(jù)段的內(nèi)存大小,新分配得到的空間首先被組織進內(nèi)部堆中去,然后再以適當?shù)男问椒祷亟o調(diào)用者。當程序釋放分配的內(nèi)存空間時,這片內(nèi)存空間被返回到內(nèi)部堆結構中,可能會被適當?shù)奶幚?比如空閑空間合并成更大的空閑空間),以更適合下一次內(nèi)存分配申請。 這套復雜的分配機制實際上相當于一個內(nèi)存分配的緩沖池(Cache),使用這套機制有如下幾個原因:
(1)系統(tǒng)調(diào)用可能不支持任意大小的內(nèi)存分配。有些系統(tǒng)的系統(tǒng)調(diào)用只支持固定大小及其倍數(shù)的內(nèi)存請求(按頁分配);這樣的話對于大量的小內(nèi)存分配來說會造成浪費。
(2)系統(tǒng)調(diào)用申請內(nèi)存可能是代價昂貴的。 系統(tǒng)調(diào)用可能涉及到用戶態(tài)和核心態(tài)的轉(zhuǎn)換。
(3)沒有管理的內(nèi)存分配在大量復雜內(nèi)存的分配釋放操作下很容易造成內(nèi)存碎片。
3.3 棧和堆的對比
從以上介紹中,它們有如下區(qū)別:
(1)棧是系統(tǒng)提供的功能,特點是快速高效,缺點是由限制,數(shù)據(jù)不靈活;
堆是函數(shù)庫提供的功能,特點是靈活方便,數(shù)據(jù)適應面廣,但是效率有一定降低。
(2)棧是系統(tǒng)數(shù)據(jù)結構,對于進程/線程是唯一的;
堆是函數(shù)庫內(nèi)部數(shù)據(jù)結構,不一定唯一,不同堆分配的內(nèi)存無法互相操作。
(3)棧空間分靜態(tài)分配和動態(tài)分配,一般由編譯器完成靜態(tài)分配,自動釋放,棧的動態(tài)分配是不被鼓勵的;
堆得分配總是動態(tài)的,雖然程序結束時所有的數(shù)據(jù)空間都會被釋放回系統(tǒng),但是精確的申請內(nèi)存/釋放內(nèi)存匹配是良好程序的基本要素。
(4)碎片問題
對于堆來講,頻繁的new/delete等操作勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序的效率降低;對于棧來講,則不會存在這個問題,因為棧是后進先出(LIFO)的隊列。
(5)生長方向
堆的生長方向是向上的,也就是向這內(nèi)存地址增加的方向;對于棧來講,生長方向卻是向下的,是向著內(nèi)存地址減少的方向增長。
(6)分配方式
堆都是動態(tài)分配的,沒有靜態(tài)分配的堆;
棧有兩種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配則由alloca函數(shù)進行分配,但是棧的動態(tài)分配和堆不同,它的動態(tài)分配是由編譯器進行釋放,無需我們手工實現(xiàn)。
(7)分配效率
棧是機器系統(tǒng)提供的數(shù)據(jù)結構,計算機在底層提供支持,分配有專門的堆棧段寄存器,入棧出棧有專門的機器指令,這些都決定了棧的高效率執(zhí)行。
堆是由C/C++函數(shù)庫提供的,機制比較復雜,有不同的分配算法,易產(chǎn)生內(nèi)存碎片,需要對內(nèi)存進行各種管理,效率比棧要低很多。
四:具體實例分析
例子(一)
看下面的一小段C程序,仔細體會各種內(nèi)存分配機制。
int a = 0; //全局初始化區(qū),a的值為0
char *p1; //全局未初始化區(qū)(C++中則初始化為NULL) intmain() { int b; //b分配在棧上,整型 char s[] = "abc"; //s分配在棧上,char *類型;"abc\0"分配在棧上,運行時賦值,函數(shù)結束銷毀 char *p2; //p2分配在棧上,未初始化 char *p3 = "123456"; //p3指向"123456"分配在字符串常量存儲區(qū)的地址,編譯時確定 static int c = 0; //c在全局(靜態(tài))初始化區(qū),可以多次跨函數(shù)調(diào)用而保持原值 p1 = (char *)malloc(10); //p1在全局未初始化區(qū),指向分配得來得10字節(jié)的堆區(qū)地址 p2 = (char *)malloc(20); //p2指向分配得來得20字節(jié)的堆區(qū)地址 strcpy(p1, "123456"); //"123456"放在字符串常量存儲區(qū),編譯器可能會將它與p3所指向的"123456"優(yōu)化成一塊 return 0; }
例子(二)
看下面的一小段代碼,體會堆與棧的區(qū)別:
intfoo()
{
//其余代碼
int *p = new int[5]; //其余代碼 return 0; }
其中的語句int *p = new int[5];就包含了堆與棧。其中new關鍵字分配了一塊堆內(nèi)存,而指針p本身所占得內(nèi)存為棧內(nèi)存(一般4個字節(jié)表示地址)。這句話的意思是在棧內(nèi)存中存放了一個指向一塊堆內(nèi)存的指針p。在程序中先確定在堆中分配內(nèi)存的大小,然后調(diào)用new關鍵字分配內(nèi)存,最后返回這塊內(nèi)存首址,放入棧中。匯編代碼為:
intfoo()
{
008C1520 push ebp
008C1521 mov ebp,esp
008C1523 sub esp,0D8h
008C1529 push ebx
008C152A push esi
008C152B push edi
008C152C lea edi,[ebp-0D8h]
008C1532 mov ecx,36h
008C1537 mov eax,0CCCCCCCCh
008C153C rep stos dword ptr es:[edi]
int *p = new int[5]; 008C153E push 14h 008C1540 call operator new[] (8C1258h) 008C1545 add esp,4008C1548 mov dword ptr [ebp-0D4h],eax 008C154E mov eax,dword ptr [ebp-0D4h] 008C1554 mov dword ptr [p],eax return 0; 008C1557 xor eax,eax } 008C1559 pop edi 008C155A pop esi 008C155B pop ebx 008C155C add esp,0D8h 008C1562 cmp ebp,esp 008C1564 call @ILT+395(__RTC_CheckEsp) (8C1190h) 008C1569 mov esp,ebp 008C156B pop ebp 008C156C ret
如果需要釋放內(nèi)存,這里我們需要使用delete[] p,告訴編譯器,我要刪除的是一個數(shù)組。
例子(三)
看下面的一小段代碼,試著找出其中的錯誤:
#include
using namespacestd;
intmain() { char a[] = "Hello"; //分配在棧上 a[0] = 'X'; cout << a <
發(fā)現(xiàn)問題了嗎?是的,字符數(shù)組a的容量是6個字符,其內(nèi)容為"hello\0"。a的內(nèi)容時可以改變的,比如a[0]='X',因為其是在棧上分配的,也就是在運行時確定的內(nèi)容。但是指針p指向的字符串"world"分配在字符串常量存儲區(qū),內(nèi)容為"world\0",常量字符串的內(nèi)容時不可以修改的。從語法上來說,編譯器并不覺得語句p[0]='X'有什么問題,但是在運行時則會出現(xiàn)"access violation"非法內(nèi)存訪問的問題。
以下幾個函數(shù)的變化要看清楚了:
char *GetString1(void)
{
char p[] = "hello,world"; //結果:h。由于數(shù)組指針指向第一元素的地址,所以調(diào)用之后是h returnp; } char *GetString2(void) { char *p = "hello,world"; //結果:hello,world。由于p指向“hello,world”字符串常量區(qū)域地址 returnp; } char *GetString3(void) { char *p = (char *)malloc(20); //指向p所分配的堆上的內(nèi)存空間。 returnp; } char *GetString4(void) { char *p = new char[20]; //指向p所分配的內(nèi)存空間,p本身在棧上的,p所指向的空間是堆上的。 returnp; }
附錄:內(nèi)存管理注意事項
【規(guī)則1】用malloc或new申請內(nèi)存之后,應該立即檢查指針值是否為NULL,防止使用指針值為NULL的內(nèi)存,可以在函數(shù)入口處斷言檢測。
【規(guī)則2】不要忘記為數(shù)組或動態(tài)內(nèi)存賦初值(比如calloc比malloc就要好),指針初始化為NULL(c++中為0)。
【規(guī)則3】避免數(shù)組或指針下標越界,特別
當心發(fā)生“多1”或者"少1"
的操作。
【規(guī)則4】動態(tài)內(nèi)存的申請和釋放必須配對,防止內(nèi)存泄露,具體為malloc/calloc/realloc和free配對,new和delete以及delete[]配對。
【規(guī)則5】用free或者delete釋放內(nèi)存后,應立即將指針設置為NULL(C++中為0),防止產(chǎn)生“野指針”、"懸垂指針"。
【規(guī)則6】遇到不懂得問題及時debug,一般的蟲子
debug一下就灰飛煙滅了,一切bug都是浮云而已
總結
以上是生活随笔為你收集整理的c语言输出11258循环,c/c++内存机制(一)(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 私家车托运多少钱啊?
- 下一篇: 计算机二级c语言题库缩印,计算机二级C语