您好,登錄后才能下訂單哦!
在一個C語言程序中,能夠獲取的內(nèi)存就是三種情況:棧(stack)、堆(heap)、數(shù)據(jù)區(qū)(.data)
棧的詳解
運行時自動分配&自動回收:棧是自動管理的,程序員不需要手工干預。方便簡單。
反復使用:棧內(nèi)存在程序中其實就是那一塊空間,程序反復使用這一塊空間。
臟內(nèi)存:棧內(nèi)存由于反復使用,每次使用后程序不會去清理,因此分配到時保留原來的值。
臨時性:(函數(shù)不能返回棧變量的指針,因為這個空間是臨時的)
棧會溢出:因為操作系統(tǒng)事先給定了棧的大小,如果在函數(shù)中無窮盡的分配棧內(nèi)存總能用完。
堆內(nèi)存詳解
操作系統(tǒng)堆管理器管理:堆管理器是操作系統(tǒng)的一個模塊,堆管理內(nèi)存分配靈活,按需分配。
大塊內(nèi)存:堆內(nèi)存管理者總量很大的操作系統(tǒng)內(nèi)存塊,各進程可以按需申請使用,使用完釋放。
程序手動申請&釋放:手工意思是需要寫代碼去申請malloc和釋放free。
臟內(nèi)存:堆內(nèi)存也是反復使用的,而且使用者用完釋放前不會清除,因此也是臟的。
臨時性:堆內(nèi)存只在malloc和free之間屬于我這個進程,而可以訪問。在malloc之前和free之后都不能再訪問,否則會有不可預料的后果。
堆內(nèi)存使用范例
(1)void *是個指針類型,malloc返回的是一個void *類型的指針,實質(zhì)上malloc返回的是堆管理器分配給我本次申請的那段內(nèi)存空間的首地址(malloc返回的值其實是一個數(shù)字,這個數(shù)字表示一個內(nèi)存地址)。為什么要使用void *作為類型?主要原因是malloc幫我們分配內(nèi)存時只是分配了內(nèi)存空間,至于這段空間將來用來存儲什么類型的元素malloc是不關心的,由我們程序自己來決定。
(2)什么是void類型。早期被翻譯成空型,這個翻譯非常不好,會誤導人。void類型不表示沒有類型,而表示萬能類型。void的意思就是說這個數(shù)據(jù)的類型當前是不確定的,在需要的時候可以再去指定它的具體類型。void *類型是一個指針類型,這個指針本身占4個字節(jié),但是指針指向的類型是不確定的,換句話說這個指針在需要的時候可以被強制轉(zhuǎn)化成其他任何一種確定類型的指針,也就是說這個指針可以指向任何類型的元素。
(3)malloc的返回值:成功申請空間后返回這個內(nèi)存空間的指針,申請失敗時返回NULL。所以malloc獲取的內(nèi)存指針使用前一定要先檢驗是否為NULL。
(4)malloc申請的內(nèi)存時用完后要free釋放。free(p);會告訴堆管理器這段內(nèi)存我用完了你可以回收了。堆管理器回收了這段內(nèi)存后這段內(nèi)存當前進程就不應該再使用了。因為釋放后堆管理器就可能把這段內(nèi)存再次分配給別的進程,所以你就不能再使用了。
(5)再調(diào)用free歸還這段內(nèi)存之前,指向這段內(nèi)存的指針p一定不能丟(也就是不能給p另外賦值)。因為p一旦丟失這段malloc來的內(nèi)存就永遠的丟失了(內(nèi)存泄漏),直到當前程序結(jié)束時操作系統(tǒng)才會回收這段內(nèi)存。
malloc的一些細節(jié)表現(xiàn)
malloc(0)
malloc申請0字節(jié)內(nèi)存本身就是一件無厘頭事情,一般不會碰到這個需要。
如果真的malloc(0)返回的是NULL還是一個有效指針?答案是:實際分配了16Byte的一段內(nèi)存并且返回了這段內(nèi)存的地址。這個答案不是確定的,因為C語言并沒有明確規(guī)定malloc(0)時的表現(xiàn),由各malloc函數(shù)庫的實現(xiàn)者來定義。
malloc(4)
gcc中的malloc默認最小是以16B為分配單位的。如果malloc小于16B的大小時都會返回一個16字節(jié)的大小的內(nèi)存。malloc實現(xiàn)時沒有實現(xiàn)任意自己的分配而是允許一些大小的塊內(nèi)存的分配。
代碼段、數(shù)據(jù)段、bss段
(1)編譯器在編譯程序的時候,將程序中的所有的元素分成了一些組成部分,各部分構(gòu)成一個段,所以說段是可執(zhí)行程序的組成部分。
(2)代碼段:代碼段就是程序中的可執(zhí)行部分,直觀理解代碼段就是函數(shù)堆疊組成的。
(3)數(shù)據(jù)段(也被稱為數(shù)據(jù)區(qū)、靜態(tài)數(shù)據(jù)區(qū)、靜態(tài)區(qū)):數(shù)據(jù)段就是程序中的數(shù)據(jù),直觀理解就是C語言程序中的全局變量。(注意:全局變量才算是程序的數(shù)據(jù),局部變量不算程序的數(shù)據(jù),只能算是函數(shù)的數(shù)據(jù))
(4)bss段(又叫ZI(zero initial)段):bss段的特點就是被初始化為0,bss段本質(zhì)上也是屬于數(shù)據(jù)段,bss段就是被初始化為0的數(shù)據(jù)段。
注意區(qū)分:數(shù)據(jù)段(.data)和bss段的區(qū)別和聯(lián)系:二者本來沒有本質(zhì)區(qū)別,都是用來存放C程序中的全局變量的。區(qū)別在于把顯示初始化為非零的全局變量存在.data段中,而把顯式初始化為0或者并未顯式初始化(C語言規(guī)定未顯式初始化的全局變量值默認為0)的全局變量存在bss段。
有些特殊數(shù)據(jù)會被放到代碼段
(1)C語言中使用char *p = "linux";定義字符串時,字符串"linux"實際被分配在代碼段,也就是說這個"linux"字符串實際上是一個常量字符串而不是變量字符串。
(2)const型常量:C語言中const關鍵字用來定義常量,常量就是不能被改變的量。const的實現(xiàn)方法至少有2種:第一種就是編譯將const修飾的變量放在代碼段去以實現(xiàn)不能修改(普遍見于各種單片機的編譯器);第二種就是由編譯器來檢查以確保const型的常量不會被修改,實際上const型的常量還是和普通變量一樣放在數(shù)據(jù)段的(gcc中就是這樣實現(xiàn)的)。
顯式初始化為非零的全局變量和靜態(tài)局部變量放在數(shù)據(jù)段
(1)放在.data段的變量有2種:第一種是顯式初始化為非零的全局變量。第二種是靜態(tài)局部變量,也就是static修飾的局部變量。(普通局部變量分配在棧上,靜態(tài)局部變量分配在.data段)
未初始化或顯式初始化為0的全局變量放在bss段
(1)bss段和.data段并沒有本質(zhì)區(qū)別,幾乎可以不用明確去區(qū)分這兩種。
C語言中所有變量和常量所使用的內(nèi)存無非以上三種情況。
(1)相同點:三種獲取內(nèi)存的方法,都可以給程序提供可用內(nèi)存,都可以用來定義變量給程序用。
(2)不同點:棧內(nèi)存對應C中的普通局部變量(別的變量還用不了棧,而且棧是自動的,由編譯器和運行時環(huán)境共同來提供服務的,程序員無法手工控制);堆內(nèi)存完全是獨立于我們的程序存在和管理的,程序需要內(nèi)存時可以去手工申請malloc,使用完成后必須盡快free釋放。(堆內(nèi)存對程序就好象公共圖書館對于人);數(shù)據(jù)段對于程序來說對應C程序中的全局變量和靜態(tài)局部變量。
(3)如果我需要一段內(nèi)存來存儲數(shù)據(jù),我究竟應該把這個數(shù)據(jù)存儲在哪里?(或者說我要定義一個變量,我究竟應該定義為局部變量還是全局變量還是用malloc來實現(xiàn))。不同的存儲方式有不同的特點,簡單總結(jié)如下:
* 函數(shù)內(nèi)部臨時使用,出了函數(shù)不會用到,就定義局部變量
* 堆內(nèi)存和數(shù)據(jù)段幾乎擁有完全相同的屬性,大部分時候是可以完全替換的。但是生命周期不一
堆內(nèi)存的生命周期是從malloc開始到free結(jié)束,而全局變量是從整個程序一開始執(zhí)行就開始,
直到整個程序結(jié)束才會消滅,伴隨程序運行的一生。啟示:如果你這個變量只是在程序的一個
階段有用,用完就不用了,就適合用堆內(nèi)存;如果這個變量本身和程序是一生相伴的,那就
適合用全局變量。(堆內(nèi)存就好象租房、數(shù)據(jù)段就好象買房。堆內(nèi)存就好象圖書館借書,數(shù)
據(jù)段就好象自己書店買書)你以后會慢慢發(fā)現(xiàn):買不如租,堆內(nèi)存的使用比全局變量廣泛。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。