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