嵌入式c面試試題

2021-05-15 12:01:12 字數 3708 閱讀 5251

c++中的堆與棧

1 基本概念

也不知道是什麼原因,很多人總是把堆和棧混合一起,在寫程式時,總是經常脫口而出地說堆疊。網上的一些資料說堆疊的叫法是有歷史原因的,至於具體是什麼歷史原因,這不是本文所要討論的問題。

堆:在資料結構中,堆是一種滿足「堆性質」(至於什麼是堆性質可以查閱任何一本資料結構的書)的資料結構。然而,通常我們所指的堆都是指二叉堆,即一種使用陣列來模擬完全二叉樹的結構。

當然,也存在其它形式的堆,包括斐波拉契堆、二項堆、楊氏表等,想獲得有關這些特殊堆的性質可以查閱演算法導論。然而,在編譯器中,堆是乙個儲存區,通常用於動態分配儲存空間,一般堆具有不連續性(在下文中將講到堆的不連續性)。

棧:在資料結構中,棧是一種按照資料項先進後出的順序排列的資料結構,我們只能在棧頂來對棧中的資料項進行操作。然而在編譯器中,棧通常是用來為函式中的臨時變數分配儲存空間,通常棧空間的分配具有連續性。

2 相關知識

通常乙個由c++編譯的程式占用的記憶體分為以下五個部分(這些知識對理解下文至關重要,這些是對乙個基本的c++程式的儲存方式的認識):

1)棧區(stack)

是由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。

2)堆區(heap)

一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由作業系統**(如果**的不及時有可能會造成記憶體洩露)。堆空間的分配方式類似於資料結構中的鍊錶。

3)全域性區(靜態區)(static)

全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。在程式結束後由系統釋放。

4)文字常量區

用於存放常量資料,程式結束後由系統釋放。

5)程式**區

存放函式體的二進位制**。

3 堆和棧的區別

在it面試中,通常有人會問哪個變數是堆變數,哪個變數又是棧變數,作業系統中的棧是向上(從低位址向高位址的方向)申請空間還是向下申請空間等等問題。我想只要掌握了堆和棧的區別,以及它們的工作原理,這些問題都會迎刃而解。本節將分以下幾個方面來講述它們之間的差別。

3.1 儲存物件的不同

這個問題其實在第2節已經初步提到,在本小節中再次詳細說明一下,因為這對下文的理解至關重要。

3.1.1堆區的儲存物件

主要儲存動態申請的空間。在c++中,儲存「new出來」的物件,如下程式段

int *a;

a = new int;

*a = 1;

那麼,變數a儲存的值為1,1的儲存位址在堆區,即指標a所指向的那個物件的儲存位址是在堆區,但是要注意的是指標a本身所儲存的區域是在棧區(嘿嘿,暈乎了把,可以看以下例子)。

exp01:

#include

int main ()

exp01的輸出結果如下:

指標a所指向物件的位址(堆區位址)為:0x00371100

儲存指標a本身的位址(棧區位址)為:0x0012ff7c

3.1.2 棧區的儲存物件

主要儲存程式中的臨時變數,這些臨時變數包括函式的引數變數、函式內的臨時變數、指標變數(指的是指標本身)、陣列變數等。注意:全域性變數和靜態變數不在棧區,它們是放在全域性區。

3.2 儲存空間的分配方式

3.2.1 堆區的空間分配方式

堆區的空間分配是由程式管理,而不是由系統管理。堆空間通常是由程式動態申請的。通常作業系統中有乙個記錄空閒記憶體位址的鍊錶,當系統收到程式的申請時,會遍歷該鍊錶,根據某種記憶體管理演算法,尋找乙個空間大於所申請空間的堆結點,然後將該結點從空閒結點鍊錶中刪除,並將該結點的空間分配給程式。

對於這種申請方式,需要在程式中使用delete語句釋放空間,否則容易導致記憶體洩露。

堆空間的分配一般都是向高位址擴充套件,並且具有不連續性。這是由於系統是用鍊錶來管理空閒記憶體位址的,當然也就不連續了,而系統中煉表的遍歷方向通常是由低位址向高位址遍歷。我們可以通過以下例子可以看到,

exp02:

#include

int main ()

exp02的輸出結果:

0x00371100 0x00371138 0x00371170

由前文我們可以知道*a,*b,*c均為堆變數(注意指標本身為棧變數),再由輸出結果我們可以看到,a的位址小於b,b小於c,並且a,b,c之間的差不是4,而是差值較大,由此可以說明堆分配的特點是向高位址擴充套件的、不連續的。

3.2.2 棧區的空間分配方式

棧通常是由系統自動分配空間的。只要系統剩餘的空間大於程式所申請的空間,那麼空間申請操作一般都會成功,否則就會出現緩衝棧溢位的錯誤。windows系統中c++編譯器的棧區空間的分配有以下一些性質:

1) 在windows系統中,棧空間的分配是從高位址向低位址擴充套件的,並且棧空間的分配一般

具有連續性,棧頂的位址和棧的最大容量是由系統預先規定。可以通過以下例子來檢視這一性質。

exp03:

#include

int main ()

exp03的輸出結果為:

0x0012ff7c 0x0012ff78 0x0012ff74

顯然,a的位址大於b和c的位址,並且a,b,c的位址間隔均為4個位元組,這可以說明2個問題:1 棧空間的分配是由高位址向低位址擴充套件的;2 棧空間的分配一般具有連續性(即相鄰變數之間的位址是不間斷的,我做了多次實驗,均證實了這點,不過仍然不能代表正確,所以只能說一般具有連續性)。

2)c++中函式引數的空間分配

函式引數的位址分配是根據引數列表中,從左到右的方向來分配的。我們根據下面這個例子來分析:

exp04:

#include

int p(int a, int b, int *h)

int main ()

exp04的輸出結果為:

變數a的位址:0x0012ff7c

指標變數h的位址:0x0012ff78

0x0012ff14 0x0012ff18 0x0012ff1c 0x0012ff08 0x0012ff04

0x0012ff14 0x0012ff18 0x0012ff1c 0x0012ff08 0x0012ff04

0x00401028 0x0012ff74

堆變數位址:0x00371280 0x003712b8

由上面的輸出結果我們可以得到以下結論:

1)進一步證實棧區的分配位址方式是由高位址向低位址擴充套件(根據主函式中變數a的位址大於指標變數h的位址);

2)函式引數變數的位址分配是由右向左的方式進行的(根據函式p中引數變數a的位址小於引數變數b的位址,引數變數b的位址小於指標引數變數h的位址,此處還發現了乙個現象就是:臨時變數c的位址比引數變數a的位址小了12個位元組,那麼編譯器需要這12個位元組是做什麼用的呢?莫非是用於儲存斷點等資訊,這些東西我們不得而知);

3)函式指標儲存在另外乙個區域(由函式指標p的位址為0x00401028,我們可以知道,它並不是儲存在一般的棧區,因為根據輸出結果,棧區的位址一般都是0x0012ffxx左右,也不是儲存在一般的堆區,因為根據輸出結果,堆區的位址一般為0x003712xx左右,那到底編譯器是如何給函式指標分配空間的呢?是另外開一塊區域嗎?這些問題我們也不得而知,不過我個人認為函式指標仍然是儲存在乙個「特殊的棧區」,這一點下文會有乙個說明);

5)乙個函式在呼叫結束後,所有的臨時變數都會由系統釋放,並且再次呼叫該函式時,仍然是從第一次呼叫的位址開始分配空間,這也說明了棧空間是由系統管理的,而不必程式設計師手工釋放(這一點可以由2次呼叫函式p的輸出結果一樣來說明)。

嵌入式 面試題

1.求下面函式的返回值 微軟 1.int func x 2.9.return countx 10.假定x 9999.答案 8思路 將x轉化為2進製,看含有的1的個數。2.什麼是 引用 申明和使用 引用 要注意哪些問題?答 引用就是某個目標變數的 別名 alias 對應用的操作與對變數直接操作效果完全...

嵌入式面試題

區域性變數能否和全域性變數重名?答 能,區域性會遮蔽全域性。要用全域性變數,需要使用 區域性變數可以與全域性變數同名,在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。對於有些編譯器而言,在同乙個函式內可以定義多個同名的區域性變數,比如在兩個迴圈體內都定義乙個同名的區域性變數,而...

華為嵌入式面試題

華為嵌入式系統面試題 1 1 什麼是預編譯,何時需要預編譯 答案 總是使用不經常改動的大型 體。程式由多個模組組成,所有模組都使用一組標準的包含檔案和相同的編譯選項。在這種情況下,可以將所有包含檔案預編譯為乙個預編譯頭。2 char const p char const p const char p...