<dfn id="w48us"></dfn><ul id="w48us"></ul>
  • <ul id="w48us"></ul>
  • <del id="w48us"></del>
    <ul id="w48us"></ul>
  • C++和操作系統(tǒng)面試問題分類

    時(shí)間:2023-04-03 03:30:03 筆試題目 我要投稿
    • 相關(guān)推薦

    C++和操作系統(tǒng)面試問題分類

      inline的使用是有所限制的,inline只適合函數(shù)體內(nèi)代碼簡(jiǎn)單的函數(shù)使用,不能包含復(fù)雜的結(jié)構(gòu)控制語句例如while switch,并且不能內(nèi)聯(lián)函數(shù)本身不能是直接遞歸函數(shù)(自己內(nèi)部還調(diào)用自己的函數(shù))

    C++和操作系統(tǒng)面試問題分類

      C++多態(tài)實(shí)現(xiàn)機(jī)制:在C++中,對(duì)于有 virtual的類,其sizeof會(huì)比正常情況多處4個(gè)字節(jié)。既在類的最開始四個(gè)字節(jié),放的是VTABLE表的地址(void *類型)。而在VTABLE中,所有虛函數(shù)是以指針數(shù)組的形式存放。 對(duì)于派生的類,即時(shí)沒有重載基類的虛函數(shù),也會(huì)在其VTABLE占用一格。造成空間上的浪費(fèi)。非虛基類沒有VTABLE,VTABLE是在構(gòu)造的時(shí)候編譯器生成的。

      線程和進(jìn)程:進(jìn)程是操作系統(tǒng)資源分配的最小單位,線程是CPU運(yùn)行的最小單位。linux中,使用的是用戶線程(對(duì)應(yīng)核心線程:線程管理由內(nèi)核實(shí)現(xiàn)),而且是1:1形式,既每一個(gè)線程,都對(duì)應(yīng)內(nèi)核中的一個(gè)輕量級(jí)進(jìn)程,調(diào)度由內(nèi)核實(shí)現(xiàn),但是線程的管理(比如產(chǎn)生和結(jié)束),均有一個(gè)管理線程實(shí)現(xiàn)。管理線程在第一次調(diào)用pthread_create的時(shí)候生成。

      軟件開發(fā)流程:

      需求分析和項(xiàng)目計(jì)劃:可行性計(jì)劃,項(xiàng)目計(jì)劃,需求分析,測(cè)試計(jì)劃

      軟件設(shè)計(jì)說明書:功能設(shè)計(jì)說明書,實(shí)現(xiàn)設(shè)計(jì)說明書

      使用手冊(cè)

      測(cè)試報(bào)告

      項(xiàng)目總結(jié)

      C++繼承機(jī)制:

      n類成員的訪問控制方式

      public:類本身、派生類和其它類均可訪問;

      protected:類本身和派生類均可訪問,其它類不能訪問;

      private(默認(rèn)):類本身可訪問,派生類和其它類不能訪問。

      繼承成員的訪問控制規(guī)則

      ——由父類成員的訪問控制方式和繼承訪問控制方式共同決定

      private+public(protectd,private)=>不可訪問

      pubic(protected)+public=>public(protected)

      public(protected)+protected=>protected

      public(protected)+private(默認(rèn))=>private

      C++中的模板和virtual異同? ==>?

      private繼承和public繼承區(qū)別? ==>?

      6. static有什么用途?(請(qǐng)至少說明兩種)

      1.限制變量的作用域

      2.設(shè)置變量的存儲(chǔ)域

      7. 引用與指針有什么區(qū)別?

      1) 引用必須被初始化,指針不必。

      2) 引用初始化以后不能被改變,指針可以改變所指的對(duì)象。

      3) 不存在指向空值的引用,但是存在指向空值的指針。

      8. 描述實(shí)時(shí)系統(tǒng)的基本特性

      在特定時(shí)間內(nèi)完成特定的任務(wù),實(shí)時(shí)性與可靠性

      9. 全局變量和局部變量在內(nèi)存中是否有區(qū)別?如果有,是什么區(qū)別?

      全局變量儲(chǔ)存在靜態(tài)數(shù)據(jù)區(qū),局部變量在堆棧

      10. 什么是平衡二叉樹?

      左右子樹都是平衡二叉樹 且左右子樹的深度差值的絕對(duì)值不大于1

      11. 堆棧溢出一般是由什么原因?qū)е碌?

      沒有回收垃圾資源

      12. 什么函數(shù)不能聲明為虛函數(shù)?

      constructor ==>C++中的類的構(gòu)造函數(shù)聲明

      13. 冒泡排序算法的時(shí)間復(fù)雜度是什么?

      O(n^2)

      14. 寫出float x 與“零值”比較的if語句。

      if(x>0.000001&&x<-0.000001)

      16. Internet采用哪種網(wǎng)絡(luò)協(xié)議?該協(xié)議的主要層次結(jié)構(gòu)?

      tcp/ip 應(yīng)用層/傳輸層/網(wǎng)絡(luò)層/數(shù)據(jù)鏈路層/物理層

      17. Internet物理地址和IP地址轉(zhuǎn)換采用什么協(xié)議?

      ARP (Address Resolution Protocol)(地址解析協(xié)議)

      18.IP地址的編碼分為哪倆部分?

      IP地址由兩部分組成,網(wǎng)絡(luò)號(hào)和主機(jī)號(hào)。不過是要和“子網(wǎng)掩碼”按位與上之后才能區(qū)分哪些是網(wǎng)絡(luò)位哪些是主機(jī)位。

      19.用戶輸入M,N值,從1至N開始順序循環(huán)數(shù)數(shù),每數(shù)到M輸出該數(shù)值,直至全部輸出。寫出C程序。

      循環(huán)鏈表,用取余操作做 ——>??

      20.不能做switch()的參數(shù)類型是:

      SWITH(表達(dá)式),表達(dá)式可以是整型、字符型以及枚舉類型等表達(dá)式。

      switch的參數(shù)不能為實(shí)型。

      淺談C/C++中Static的作用

      1.先來介紹它的第一條也是最重要的一條:隱藏。

      當(dāng)我們同時(shí)編譯多個(gè)文件時(shí),所有未加static前綴的全局變量和函數(shù)都具有全局可見性。為理解這句話,我舉例來說明。我們要同時(shí)編譯兩個(gè)源文件和一個(gè)Makefile,一個(gè)是a.c,另一個(gè)是main.c.下面是a.c的內(nèi)容:#include

      char a = ’A’; //global variable

      void msg()

      { printf(”Hello\n”);}下面是main.c的內(nèi)容:#include

      int main(void)

      { extern char a; // extern variable must be declared before use printf(”%c “, a);(void)msg();return 0;}下面是Makefile的內(nèi)容:CC =gcc

      SRC := $(shell ls *.c)

      OBJS := $(patsubst %.c, %.o, $(SRC))

      TARGET := Main

      $(TARGET): $(OBJS)

      $(CC) $(LIBS) -o $@ $^

      %.o: %.c $(CC) $(CFLAGS) -c -o $@ $<

      clean:rm -f $(TARGET) *.o程序的運(yùn)行結(jié)果是:A Hello你可能會(huì)問:為什么在a.c中定義的全局變量a和函數(shù)msg能在main.c中使用?前面說過,所有未加static前綴的全局變量和函數(shù)都具有全 局可見性,其它的源文件也能訪問。此例中,a是全局變量,msg是函數(shù),并且都沒有加static前綴,因此對(duì)于另外的源文件main.c是可見的。

      如果加了static,就會(huì)對(duì)其它源文件隱藏。例如在a和msg的定義前加上static,main.c就看不到它們了。利用這一特性可以在不同的 文件中定義同名函數(shù)和同名變量,而不必?fù)?dān)心命名沖突。Static可以用作函數(shù)和變量的前綴,對(duì)于函數(shù)來講,static的作用僅限于隱藏,而對(duì)于變 量,static還有下面兩個(gè)作用。

      2.static的第二個(gè)作用是保持變量內(nèi)容的持久。

      存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū)的變量會(huì)在程序剛開始運(yùn)行時(shí)就完成初始化,也是唯一的一次初始化。共有兩種變量存儲(chǔ)在靜態(tài)存儲(chǔ)區(qū):全局變量和static變量,只 不過和全局變量比起來,static可以控制變量的可見范圍,說到底static還是用來隱藏的。雖然這種用法不常見,但我還是舉一個(gè)例子。

      #include int fun(void){ static int count = 10; // 事實(shí)上此賦值語句從來沒有執(zhí)行過return count——;} int count = 1;int main(void)

      { printf(”global\t\tlocal static\n”);for(; count <= 10; ++count)

      printf(”%d\t\t%d\n”, count, fun());return 0;}程序的運(yùn)行結(jié)果是:global local static 1 10 2 9 3 8 4 7 5 6 6 5 7 4 8 3 9 2 10 1 3.static的第三個(gè)作用是默認(rèn)初始化為0.其實(shí)全局變量也具備這一屬性,因?yàn)槿肿兞恳泊鎯?chǔ)在靜態(tài)數(shù)據(jù)區(qū)。

      在靜態(tài)數(shù)據(jù)區(qū),內(nèi)存中所有的字節(jié)默認(rèn)值都是0×00,某些時(shí)候這一特點(diǎn)可以減少程序員的工作量。比如初始化一個(gè)稀疏矩陣,我們可以一個(gè)一個(gè)地把所有 元素都置0,然后把不是0的幾個(gè)元素賦值。如果定義成靜態(tài)的,就省去了一開始置0的操作。再比如要把一個(gè)字符數(shù)組當(dāng)字符串來用,但又覺得每次在字符數(shù)組末 尾加‘\0’太麻煩。如果把字符串定義成靜態(tài)的,就省去了這個(gè)麻煩,因?yàn)槟抢锉緛砭褪?lsquo;\0’。不妨做個(gè)小實(shí)驗(yàn)驗(yàn)證一下。

      #include int a;int main(void)

      { int i;static char str[10];printf(”integer: %d; string: (begin)%s(end)”, a, str);return 0;}程序的運(yùn)行結(jié)果如下integer: 0; string: (begin)(end)

      最后對(duì)static的三條作用做一句話總結(jié)。首先static的最主要功能是隱藏,其次因?yàn)閟tatic變量存放在靜態(tài)存儲(chǔ)區(qū),所以它具備持久性和默認(rèn)值0.

      C++內(nèi)存泄漏的發(fā)生方式

      以發(fā)生的方式來分類,內(nèi)存泄漏可以分為4類:

      1. 常發(fā)性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼會(huì)被多次執(zhí)行到,每次被執(zhí)行的時(shí)候都會(huì)導(dǎo)致一塊內(nèi)存泄漏。比如例二,如果Something()函數(shù)一直返回True,那么pOldBmp指向的HBITMAP對(duì)象總是發(fā)生泄漏。

      2. 偶發(fā)性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過程下才會(huì)發(fā)生。比如例二,如果Something()函數(shù)只有在特定環(huán)境下才返回 True,那么pOldBmp指向的HBITMAP對(duì)象并不總是發(fā)生泄漏。常發(fā)性和偶發(fā)性是相對(duì)的。對(duì)于特定的環(huán)境,偶發(fā)性的也許就變成了常發(fā)性的。所以測(cè)試環(huán)境和測(cè)試方法對(duì)檢測(cè)內(nèi)存泄漏至關(guān)重要。

      3. 一次性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼只會(huì)被執(zhí)行一次,或者由于算法上的缺陷,導(dǎo)致總會(huì)有一塊僅且一塊內(nèi)存發(fā)生泄漏。比如,在類的構(gòu)造函數(shù)中分配內(nèi)存,在析構(gòu)函數(shù)中卻沒有釋放該內(nèi)存,但是因?yàn)檫@個(gè)類是一個(gè)Singleton,所以內(nèi)存泄漏只會(huì)發(fā)生一次。另一個(gè)例子:

    char* g_lpszFileName = NULL;

    void SetFileName( const char* lpcszFileName )
    {
    if( g_lpszFileName ){
    free( g_lpszFileName );
    }
    g_lpszFileName = strdup( lpcszFileName );
    }

      例三

      如果程序在結(jié)束的時(shí)候沒有釋放g_lpszFileName指向的字符串,那么,即使多次調(diào)用SetFileName(),總會(huì)有一塊內(nèi)存,而且僅有一塊內(nèi)存發(fā)生泄漏。

      4. 隱式內(nèi)存泄漏。程序在運(yùn)行過程中不停的分配內(nèi)存,但是直到結(jié)束的時(shí)候才釋放內(nèi)存。嚴(yán)格的說這里并沒有發(fā)生內(nèi)存泄漏,因?yàn)樽罱K程序釋放了所有申請(qǐng)的內(nèi)存。但是對(duì)于一個(gè) 服務(wù)器程序,需要運(yùn)行幾天,幾周甚至幾個(gè)月,不及時(shí)釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所有內(nèi)存。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏。舉一個(gè)例子:

    class Connection
    {
    public:
    Connection( SOCKET s);
    ~Connection();

    private:
    SOCKET _socket;

    };

    class ConnectionManager
    {
    public:
    ConnectionManager(){}
    ~ConnectionManager(){
    list::iterator it;
    for( it = _connlist.begin(); it != _connlist.end(); ++it ){
    delete (*it);
    }
    _connlist.clear();
    }
    void OnClientConnected( SOCKET s ){
    Connection* p = new Connection(s);
    _connlist.push_back(p);
    }
    void OnClientDisconnected( Connection* pconn ){
    _connlist.remove( pconn );
    delete pconn;
    }
    private:
    list _connlist;
    };

      例四

      假設(shè)在Client從Server端斷開后,Server并沒有呼叫OnClientDisconnected()函數(shù),那么代表那次連接的 Connection對(duì)象就不會(huì)被及時(shí)的刪除(在Server程序退出的時(shí)候,所有Connection對(duì)象會(huì)在ConnectionManager的析構(gòu)函數(shù)里被刪除)。當(dāng)不斷的有連接建立、斷開時(shí)隱式內(nèi)存泄漏就發(fā)生了。

      從用戶使用程序的角度來看,內(nèi)存泄漏本身不會(huì)產(chǎn)生什么危害,作為一般的用戶,根本感覺不到內(nèi)存泄漏的存在。真正有危害的是內(nèi)存泄漏的堆積,這會(huì)最終消耗盡系統(tǒng)所有的內(nèi)存。從這個(gè)角度來說,一次性內(nèi)存泄漏并沒有什么危害,因?yàn)樗粫?huì)堆積,而隱式內(nèi)存泄漏危害性則非常大,因?yàn)檩^之于常發(fā)性和偶發(fā)性內(nèi)存泄漏它更難被檢測(cè)到。

      c++內(nèi)存泄漏檢測(cè)

      檢測(cè)內(nèi)存泄漏的方法多種多樣,有使用內(nèi)存泄漏檢測(cè)工具(比如BoundsChecker)檢測(cè)內(nèi)存泄漏;有直接看代碼檢測(cè)代碼邏輯,看那些地方是否沒有釋放內(nèi)存。一般地靜態(tài)內(nèi)存泄漏通過工具與代碼檢查很容易找到泄漏點(diǎn);動(dòng)態(tài)的內(nèi)存泄漏很難查,一般通過在代碼中加斷點(diǎn)跟蹤和 Run-Time內(nèi)存檢測(cè)工具來查找。

      總的來說,要檢查內(nèi)存泄漏分幾個(gè)步驟:

      1、首先寫代碼時(shí)要控制內(nèi)存的釋放,比如new之后要delete,看析構(gòu)函數(shù)是否真的執(zhí)行(很多人編寫釋放內(nèi)存的代碼在析構(gòu)函數(shù)中處理的),如果沒有真正執(zhí)行,就需要?jiǎng)討B(tài)釋放對(duì)象;前段時(shí)間在一個(gè)項(xiàng)目中使用了單例模式對(duì)象,將構(gòu)造函數(shù)和析構(gòu)函數(shù)設(shè)置成保護(hù)類型,在運(yùn)行代碼時(shí)退出時(shí)不執(zhí)行到析構(gòu)函數(shù)里面(具體也不知道什么原因),最后只有手動(dòng)刪除對(duì)象。

      2、其次讓程序長時(shí)間運(yùn)行,看任務(wù)管理器對(duì)應(yīng)程序內(nèi)存是不是一直向上增加;

      3、最后使用常用內(nèi)存泄漏檢測(cè)工具來檢測(cè)內(nèi)存泄漏點(diǎn)。

      文本主要描述一些內(nèi)存泄漏檢測(cè)工具功能介紹與簡(jiǎn)單使用方法。

      一、對(duì)于VS2005/VS2008編譯器自帶的內(nèi)存檢測(cè)工具/函數(shù)。

      在 main() 函數(shù)開頭加上:

      #include “crtdbg.h”

      _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

      二、用BoundsChecker之類的工具。

      BoundsChecker 是一個(gè)Run-Time錯(cuò)誤檢測(cè)工具,它主要定位程序在運(yùn)行時(shí)期發(fā)生的各種錯(cuò)誤。BoundsChecker能檢測(cè)的錯(cuò)誤包括:

      1、指針操作和內(nèi)存、資源泄露錯(cuò)誤,比如:

      內(nèi)存泄露;

      資源泄露;

      對(duì)指針變量的錯(cuò)誤操作。

      2、內(nèi)存操作方面的錯(cuò)誤,比如:

      內(nèi)存讀、寫溢出;

      使用未初始化的內(nèi)存。

      3、API函數(shù)使用錯(cuò)誤

      三、linux下可以用valgrind檢測(cè)內(nèi)存泄露錯(cuò)誤。

      四、purify工具,這個(gè)是專門檢測(cè)內(nèi)存的,包括泄露、越界、指針跑飛等都可以檢查,在VC上使用方便。

      五、用Windbg,試過查句柄泄漏的,比較方便。

      六、Visual Leak Detector

      Visual Leak Detector是一款用于Visual C++的免費(fèi)的內(nèi)存泄露檢測(cè)工具。相比較其它的內(nèi)存泄露檢測(cè)工具,它在檢測(cè)到內(nèi)存泄漏的同時(shí),還具有如下特點(diǎn):

      1、 可以得到內(nèi)存泄漏點(diǎn)的調(diào)用堆棧,如果可以的話,還可以得到其所在文件及行號(hào);

      2、 可以得到泄露內(nèi)存的完整數(shù)據(jù);

      3、 可以設(shè)置內(nèi)存泄露報(bào)告的級(jí)別;

      4、 它是一個(gè)已經(jīng)打包的lib,使用時(shí)無須編譯它的源代碼。而對(duì)于使用者自己的代碼,也只需要做很小的改動(dòng);

      5、 他的源代碼使用GNU許可發(fā)布,并有詳盡的文檔及注釋。對(duì)于想深入了解堆內(nèi)存管理的讀者,是一個(gè)不錯(cuò)的選擇。

      C++培訓(xùn):C/C++中堆和棧的區(qū)別

      一、預(yù)備知識(shí)—程序的內(nèi)存分配

      由C/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分

      1、棧區(qū)(stack): 由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

      2、堆區(qū)(heap): 一般由程序員分配釋放(malloc/free、new/delete), 若程序員不釋放,程序結(jié)束時(shí)可能由操作系統(tǒng)回收。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表。

      3、全局區(qū)(static): 全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域,程序結(jié)束后由系統(tǒng)釋放。

      4、文字常量區(qū): 常量字符串就是放在這里的, 程序結(jié)束后由系統(tǒng)釋放。

      5、程序代碼區(qū): 存放函數(shù)體的二進(jìn)制代碼。

      Example:

      int a = 0; // 全局初始化區(qū)

      char *p1; // 全局未初始化區(qū)

      main()

      {

      int a; // 棧區(qū)

      char s[] = “abc”; // 棧區(qū)

      char *p2; // 棧區(qū)

      char *p3 = “123456″; // 123456\0在常量區(qū),p3在棧上。

      static int c =0; // 全局(靜態(tài))初始化區(qū)

      p1 = (char *)malloc(10);

      p2 = (char *)malloc(20); // 分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。

      strcpy(p1, “123456″); // 123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的”123456″優(yōu)化成一個(gè)地方。

      }

      二、堆和棧的理論知識(shí)

      2.1 申請(qǐng)方式

      棧: 由系統(tǒng)自動(dòng)分配。 例如,聲明在函數(shù)中一個(gè)局部變量 int a; 系統(tǒng)自動(dòng)在棧中為a開辟空間

      堆: 需要程序員自己申請(qǐng),并指明大小,在c中malloc函數(shù):如p1 = (char *)malloc(10); 在C++中用new運(yùn)算符 如p2 = (char *)malloc(10); 但是注意局部變量p1、p2本身是在棧中的,但是他們指向的申請(qǐng)到的內(nèi)存是在堆區(qū),這點(diǎn)要明確!

      2.2 申請(qǐng)后系統(tǒng)的響應(yīng)

      棧:只要棧的剩余空間大于所申請(qǐng)空間,系統(tǒng)將為程序提供內(nèi)存,否則將報(bào)異常提示棧溢出。

      堆:首先應(yīng)該知道操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請(qǐng)時(shí), 會(huì)遍歷該鏈表,尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序,另外,對(duì)于大多數(shù)系統(tǒng),會(huì)在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外,由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請(qǐng)的大小,系統(tǒng)會(huì)自動(dòng)的將多余的那部分重新放入空閑鏈表中。

      2.3 申請(qǐng)大小的限制

      棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。

      堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。

      2.4 申請(qǐng)效率的比較:

      棧:由系統(tǒng)自動(dòng)分配,速度較快。但程序員是無法控制的。

      堆:是由malloc/new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便。

      2.5 堆和棧中的存儲(chǔ)內(nèi)容

      棧: 在函數(shù)調(diào)用時(shí),第一個(gè)進(jìn)棧的是主函數(shù)中的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址,然后是函數(shù)的各個(gè)參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變量。注意靜態(tài)變量是不入棧的。當(dāng)本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令,程序由該點(diǎn)繼續(xù)運(yùn)行。

      堆:一般是在堆的頭部用一個(gè)字節(jié)存放堆的大小。堆中的具體內(nèi)容有程序員安排。

      2.6 存取效率的比較

      char s1[] = “aaaaaaaaaaaaaaa”;

      char *s2 = “bbbbbbbbbbbbbbbbb”;

      aaaaaaaaaaa是在運(yùn)行時(shí)刻賦值的;

      而bbbbbbbbbbb是在編譯時(shí)就確定的;

      但是,在以后的存取中,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快。

      比如:

      #include

      void main()

      {

      char a = 1;

      char c[] = “1234567890″;

      char *p =”1234567890″;

      a = c[1];

      a = p[1];

      return;

      }

      對(duì)應(yīng)的匯編代碼

      10: a = c[1];

      00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]

      0040106A 88 4D FC mov byte ptr [ebp-4],cl

      11: a = p[1];

      0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]

      00401070 8A 42 01 mov al,byte ptr [edx+1]

      00401073 88 45 FC mov byte ptr [ebp-4],al

      第一種在讀取時(shí)直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據(jù)edx讀取字符,顯然慢了。

      三、 小結(jié)

      堆和棧的區(qū)別可以用如下的比喻來看出: 使用棧就象我們?nèi)ワ堭^里吃飯,只管點(diǎn)菜(發(fā)出申請(qǐng))、付錢、和吃(使用),吃飽了就走,不必理會(huì)切菜、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。使用堆就象是自己動(dòng)手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。

      還有就是函數(shù)調(diào)用時(shí)會(huì)在棧上有一系列的保留現(xiàn)場(chǎng)及傳遞參數(shù)的操作。棧的空間大小有限定,VC的缺省是2M。棧不夠用的情況一般是程序中分配了大量數(shù)組和遞歸函數(shù)層次太深。有一點(diǎn)必須知道,當(dāng)一個(gè)函數(shù)調(diào)用完返回后它會(huì)釋放該函數(shù)中所有的棧空間。棧是由編譯器自動(dòng)管理的,不用你操心。堆是動(dòng)態(tài)分配內(nèi)存的,并且你可以分配使用很大的內(nèi)存。但是用不好會(huì)產(chǎn)生內(nèi)存泄漏。并且頻繁地malloc和free會(huì)產(chǎn)生內(nèi)存碎片(有點(diǎn)類似磁盤碎片),因?yàn)镃分配動(dòng)態(tài)內(nèi)存時(shí)是尋找匹配的內(nèi)存的。而用棧則不會(huì)產(chǎn)生碎片。在棧上存取數(shù)據(jù)比通過指針在堆上存取數(shù)據(jù)快些。一般大家說的堆棧和棧是一樣的,就是棧(stack),而說堆時(shí)才是堆heap。棧是先入后出的,一般是由高地址向低地址生長。

      c++內(nèi)存分配的五種方法

      在C++中,內(nèi)存分成5個(gè)區(qū),他們分別是堆、棧、自由存儲(chǔ)區(qū)、全局/靜態(tài)存儲(chǔ)區(qū)和常量存儲(chǔ)區(qū)。

      棧,就是那些由編譯器在需要的時(shí)候分配,在不需要的時(shí)候自動(dòng)清楚的變量的存儲(chǔ)區(qū)。里面的變量通常是局部變量、函數(shù)參數(shù)等。

      堆,就是那些由new分配的內(nèi)存塊,他們的釋放編譯器不去管,由我們的應(yīng)用程序去控制,一般一個(gè)new就要對(duì)應(yīng)一個(gè)delete。如果程序員沒有釋放掉,那么在程序結(jié)束后,操作系統(tǒng)會(huì)自動(dòng)回收。

      自由存儲(chǔ)區(qū),就是那些由malloc等分配的內(nèi)存塊,他和堆是十分相似的,不過它是用free來結(jié)束自己的生命的。

      全局/靜態(tài)存儲(chǔ)區(qū),全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個(gè)區(qū)分了,他們共同占用同一塊內(nèi)存區(qū)。

      常量存儲(chǔ)區(qū),這是一塊比較特殊的存儲(chǔ)區(qū),他們里面存放的是常量,不允許修改(當(dāng)然,你要通過非正當(dāng)手段也可以修改,而且方法很多,在《const的思考》一文中,我給出了6種方法)

      明確區(qū)分堆與棧

      在bbs上,堆與棧的區(qū)分問題,似乎是一個(gè)永恒的話題,由此可見,初學(xué)者對(duì)此往往是混淆不清的,所以我決定拿他第一個(gè)開刀。

      首先,我們舉一個(gè)例子:

      void f() { int* p=new int[5]; }

      這條短短的一句話就包含了堆與棧,看到new,我們首先就應(yīng)該想到,我們分配了一塊堆內(nèi)存,那么指針p呢?他分配的是一塊棧內(nèi)存,所以這句話的意思就是:在棧內(nèi)存中存放了一個(gè)指向一塊堆內(nèi)存的指針p。在程序會(huì)先確定在堆中分配內(nèi)存的大小,然后調(diào)用operator new分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧中,他在VC6下的匯編代碼如下:

      00401028 push 14h

      0040102A call operator new (00401060)

      0040102F add esp,4

      00401032 mov dWord ptr [ebp-8],eax

      00401035 mov eax,dword ptr [ebp-8]

      00401038 mov dword ptr [ebp-4],eax

      這里,我們?yōu)榱撕?jiǎn)單并沒有釋放內(nèi)存,那么該怎么去釋放呢?是delete p么?澳,錯(cuò)了,應(yīng)該是delete []p,這是為了告訴編譯器:我刪除的是一個(gè)數(shù)組,VC6就會(huì)根據(jù)相應(yīng)的Cookie信息去進(jìn)行釋放內(nèi)存的工作。

      好了,我們回到我們的主題:堆和棧究竟有什么區(qū)別?

      主要的區(qū)別由以下幾點(diǎn):

      1、管理方式不同;

      2、空間大小不同;

      3、能否產(chǎn)生碎片不同;

      4、生長方向不同;

      5、分配方式不同;

      6、分配效率不同;

      管理方式:對(duì)于棧來講,是由編譯器自動(dòng)管理,無需我們手工控制;對(duì)于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。

      空間大。阂话銇碇v在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個(gè)角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對(duì)于棧來講,一般是有一定的空間大小的,例如,在VC6下面,默認(rèn)的?臻g大小是1M(好像是,記不清楚了)。當(dāng)然,我們可以修改:

      打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。

      注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁文件里面,它設(shè)置的較大會(huì)使棧開辟較大的值,可能增加內(nèi)存的開銷和啟動(dòng)時(shí)間。

      碎片問題:對(duì)于堆來講,頻繁的new/delete勢(shì)必會(huì)造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對(duì)于棧來講,則不會(huì)存在這個(gè)問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對(duì)應(yīng),以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。

      生長方向:對(duì)于堆來講,生長方向是向上的,也就是向著內(nèi)存地址增加的方向;對(duì)于棧來講,它的生長方向是向下的,是向著內(nèi)存地址減小的方向增長。

      分配方式:堆都是動(dòng)態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動(dòng)態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。

      分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫函數(shù)會(huì)按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會(huì)分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。

      從這里我們可以看到,堆和棧相比,由于大量new/delete的使用,容易造成大量的內(nèi)存碎片;由于沒有專門的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶態(tài)和核心態(tài)的切換,內(nèi)存的申請(qǐng),代價(jià)變得更加昂貴。所以棧在程序中是應(yīng)用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。

      雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時(shí)候分配大量的內(nèi)存空間,還是用堆好一些。

      無論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生(除非你是故意使其越界),因?yàn)樵浇绲慕Y(jié)果要么是程序崩潰,要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運(yùn)行過程中,沒有發(fā)生上面的問題,你還是要小心,說不定什么時(shí)候就崩掉,那時(shí)候debug可是相當(dāng)困難的:)

    【C++和操作系統(tǒng)面試問題分類】相關(guān)文章:

    行為面試的面試問題和答案12-14

    面試疑問和常見面試問題07-31

    經(jīng)典面試問題和回答技巧09-27

    文員面試問題和答案03-17

    面試時(shí)間和地點(diǎn)問題11-19

    常見的面試問題和回答09-14

    DELL的英文面試題(硬件部分和操作系統(tǒng))11-06

    面試問題樣例:決策和分析問題的能力07-31

    計(jì)算機(jī)一級(jí)基礎(chǔ)知識(shí):操作系統(tǒng)的功能和分類11-07

    文藝部面試問題和回答09-22

    主站蜘蛛池模板: 国产精品 91 第一页| 亚洲精品视频在线观看你懂的| 亚洲AV无码乱码精品国产 | 下载天堂国产AV成人无码精品网站| 99re6在线视频精品免费| 人妻VA精品VA欧美VA| 中文字幕亚洲精品| japanese乱人伦精品| 日韩精品人妻系列无码专区免费| 国产微拍精品一区二区| 精品精品国产自在久久高清| 久久精品中文字幕无码绿巨人| 午夜三级国产精品理论三级 | 亚洲色精品aⅴ一区区三区| 国内精品久久久久久久coent| 四虎精品影院4hutv四虎| 国产99视频精品免视看7| 久久久久久国产精品无码超碰| 一区二区国产精品| 日本五区在线不卡精品| 精品国产免费人成网站| 国产成人1024精品免费| 欧美黑人巨大精品| 99精品久久精品一区二区| 无码精品日韩中文字幕| 亚洲精品成人网久久久久久| 精品亚洲一区二区三区在线播放| 999国产精品视频| 91大神精品全国在线观看| 国产精品嫩草影院一二三区入口| 久久青青草原精品国产| 亚洲欧洲国产精品香蕉网| 人妻少妇精品系列| 黑巨人与欧美精品一区 | 国产精品99久久不卡| 99爱在线视频这里只有精品| 久久国产乱子精品免费女| 国产精品天天影视久久综合网| 国产精品免费观看调教网| 国产精品丝袜黑色高跟鞋| 国产成人vr精品a视频|