1、堆棧空間分配不同。棧由操作系統自動分配釋放,存放函數的參數值,局部變量的值等。堆區一般由程序員分配釋放。
2、堆棧緩存方式不同。棧使用的是一級緩存,它通常都是在被調用時處于存儲空間中,調
用完畢後立即釋放;堆則存放在二級緩存中,速度要慢些。
3、堆棧數據結構不同。堆類似數組結構;棧類似棧結構,先進後出。
簡述C 的内存管理1、内存分配方式:
在C 中,内存分成5個區,他們分别是堆、棧、自由存儲區、全局/靜态存儲區和常量存儲區。
棧:在執行函數時,函數内局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。
堆:就是那些由new分配的内存塊,一般一個new就要對應一個delete。
區自由存儲:就是那些由malloc等分配的内存塊,和堆是十分相似的,不過是用free來結束自己的生命。
全局/靜态存儲區:全局變量和靜态變量被分配到同一塊内存中。
常量存儲區:這是一塊比較特殊的存儲區,裡面存放的是常量,不允許修改。
2、常見的内存錯誤及其對策:
(1)内存分配未成功,卻使用了它。
(2)内存分配雖然成功,但是尚未初始化就引用它。
(3)内存分配成功并且已經初始化,但操作越過了内存的邊界。
(4)忘記了釋放内存,造成内存洩露。
(5)釋放了内存卻繼續使用它。
對策:
(1)定義指針時,先初始化為NULL。
(2)用malloc或new申請内存之後,應該立即檢查指針值是否為NULL。防止使用指針值為NULL的内存。
(3)不要忘記為數組和動态内存賦初值。防止将未被初始化的内存作為右值使用。
(4)避免數字或指針的下标越界,特别要當心發生“多1”或者“少1”操作。
(5)動态内存的申請與釋放必須配對,防止内存洩漏。
(6)用free或delete釋放了内存之後,立即将指針設置為NULL,防止“野指針”。
(7)使用智能指針。
3、内存洩露及解決辦法:
什麼是内存洩露?
簡單地說就是申請了一塊内存空間,使用完畢後沒有釋放掉。
(1)new和malloc申請資源使用後,沒有用delete和free釋放
(2)子類繼承父類時,父類析構函數不是虛函數。
(3)Windows句柄資源使用後沒有釋放。
malloc和局部變量分配在堆還是棧?malloc是在堆上分配内存,需要程序員自己回收内存;局部變量是在棧中分配内存,超過作用域就自動回收。
程序有哪些section,分别的作用?程序啟動的過程?怎麼判斷數據是分配在棧還是堆上?
一個程序有哪些section:
從低地址到高地址,一個程序由代碼段、數據段、BSS段、堆、共享區、棧等組成
代碼段: 存放程序執行代碼的一塊内存區域。隻讀,代碼段的頭部還會包含一些隻讀的常數變量。
數據段:存放程序中已初始化的全局變量和靜态變量的一塊内存區域
BSS 段:存放程序中未初始化的全局變量和靜态變量的一塊内存區域。
可執行程序在運行時又會多出兩個區域:堆區和棧區:
堆區:動态申請内存用。堆從低地址向高地址增長。
棧區:存儲局部變量、函數參數值。棧從高地址向低地址增長。是一塊連續的空間。
共享區:位于堆和棧之間。
程序啟動的過程:
1、操作系統首先創建相應的進程并分配私有的進程空間,然後操作系統的加載器負責把可執行文件的數據段和代碼段映射到進程的虛拟内存空間中。
2、加載器讀入可執行程序的導入符号表,根據這些符号表可以查找出該可執行程序的所有依賴的動态鍊接庫。
3、加載器針對該程序的每一個動态鍊接庫調用LoadLibrary (1)查找對應的動态庫文件,加載器為該動态鍊接庫确定一個合适的基地址。 (2)加載器讀取該動态鍊接庫的導入符号表和導出符号表,比較應用程序要求的導入符号是否匹配該庫的導出符号。 (3)針對該庫的導入符号表,查找對應的依賴的動态鍊接庫,如有跳轉,則跳到3 (4)調用該動态鍊接庫的初始化函數。
4、始化應用程序的全局變量,對于全局對象自動調用構造函數。
5、進入應用程序入口點函數開始執行。
怎麼判斷數據分配在棧上還是堆上:
首先局部變量分配在棧上;而通過malloc和new申請的空間是在堆上。
初始化為0的全局變量在bss還是dataBSS段通常是指用來存放程序中未初始化的或者初始化為0的全局變量和靜态變量的一塊内存區域。特點是可讀寫的,在程序執行之前BSS段會自動清0。
簡述C 中内存對齊的使用場景内存對齊應用于三種數據類型中:struct/class/union
struct/class/union内存對齊原則有四個:
1、數據成員對其規則:
結構(struct)或聯合(union)的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大小的整數倍開始。
2、結構體作為成員:
如果一個結構裡有某些結構體成員,則結構體成員要從其内部"最寬基本類型成員"的整數倍地址開始存儲。(struct a裡存有struct b,b裡有char,int ,double等元素,那b應該從8的整數倍開始存儲)。
3、收尾工作:
結構體的總大小,也就是sizeof的結果,必須是其内部最大成員的"最寬基本類型成員"的整數倍。不足的要補齊。(基本類型不包括struct/class/uinon)。4、sizeof(union):
以結構裡面size最大元素為union的size,因為在某一時刻,union隻有一個成員真正存儲于該地址。
解析:
1、什麼是内存對齊?
那麼什麼是字節對齊?在C語言中,結構體是一種複合數據類型,其構成元素既可以是基本數據類型(如int、long、float等)的變量,也可以是一些複合數據類型(如數組、結構體、聯合體等)的數據單元。在結構體中,編譯器為結構體的每個成員按其自然邊界(alignment)分配空間。各個成員按照它們被聲明的順序在内存中順序存儲,第一個成員的地址和整個結構體的地址相同。
為了使CPU能夠對變量進行快速的訪問,變量的起始地址應該具有某些特性,即所謂的“對齊”,比如4字節的int型,其起始地址應該位于4字節的邊界上,即起始地址能夠被4整除,也即“對齊”跟數據在内存中的位置有關。如果一個變量的内存地址正好位于它長度的整數倍,他就被稱做自然對齊。
比如在32位cpu下,假設一個整型變量的地址為0x00000004(為4的倍數),那它就是自然對齊的,而如果其地址為0x00000002(非4的倍數)則是非對齊的。現代計算機中内存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定類型變量的時候經常在特定的内存地址訪問,這就需要各種類型數據按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
2、為什麼要字節對齊?
需要字節對齊的根本原因在于CPU訪問數據的效率問題。假設上面整型變量的地址不是自然對齊,比如為0x00000002,則CPU如果取它的值的話需要訪問兩次内存,第一次取從0x00000002-0x00000003的一個short,第二次取從0x00000004-0x00000005的一個short然後組合得到所要的數據,如果變量在0x00000003地址上的話則要訪問三次内存,第一次為char,第二次為short,第三次為char,然後組合得到整型數據。
而如果變量在自然對齊位置上,則隻要一次就可以取出數據。一些系統對對齊要求非常嚴格,比如sparc系統,如果取未對齊的數據會發生錯誤,而在x86上就不會出現錯誤,隻是效率下降。
各個硬件平台對存儲空間的處理上有很大的不同。一些平台對某些特定類型的數據隻能從某些特定地址開始存取。比如有些平台每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那麼一個讀周期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀周期,并對兩次讀出的結果的高低字節進行拼湊才能得到該32bit數據。顯然在讀取效率上下降很多。
3、字節對齊實例
union example { int a[5]; char b; double c; }; int result = sizeof(example); /* 如果以最長20字節為準,内部double占8字節,這段内存的地址0x00000020并不是double的整數倍,隻有當最小為0x00000024時可以滿足整除double(8Byte)同時又可以容納int a[5]的大小,所以正确的結果應該是result=24 */ struct example { int a[5]; char b; double c; }test_struct; int result = sizeof(test_struct); /* 如果我們不考慮字節對齊,那麼内存地址0x0021不是double(8Byte)的整數倍,所以需要字節對齊,那麼此時滿足是double(8Byte)的整數倍的最小整數是0x0024,說明此時char b對齊int擴充了三個字節。所以最後的結果是result=32 */ struct example { char b; double c; int a; }test_struct; int result = sizeof(test_struct); /* 字節對齊除了内存起始地址要是數據類型的整數倍以外,還要滿足一個條件,那就是占用的内存空間大小需要是結構體中占用最大内存空間的類型的整數倍,所以20不是double(8Byte)的整數倍,我們還要擴充四個字節,最後的結果是result=24 */
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!