2012軟考軟件設(shè)計(jì)師輔導(dǎo):C++內(nèi)存分配詳解

字號(hào):

一、關(guān)于內(nèi)存
    1、內(nèi)存分配方式
    內(nèi)存分配方式有三種:
    (1)從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。
    例如全局變量,static變量。
    (2)在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
    (3)從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用free或delete釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最多。
    2、內(nèi)存使用錯(cuò)誤發(fā)生內(nèi)存錯(cuò)誤是件非常麻煩的事情。編譯器不能自動(dòng)發(fā)現(xiàn)這些錯(cuò)誤,通常是在程序運(yùn)行時(shí)才能捕捉到。而這些錯(cuò)誤大多沒有明顯的癥狀,時(shí)隱時(shí)現(xiàn),增加了改錯(cuò)的難度。有時(shí)用戶怒氣沖沖地把你找來,程序卻沒有發(fā)生任何問題,你一走,錯(cuò)誤又發(fā)作了。常見的內(nèi)存錯(cuò)誤及其對策如下:
    * 內(nèi)存分配未成功,卻使用了它。
    編程新手常犯這種錯(cuò)誤,因?yàn)樗麄儧]有意識(shí)到內(nèi)存分配會(huì)不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL.如果是用malloc或new來申請內(nèi)存,應(yīng)該用if(p==NULL) 或if(p!=NULL)進(jìn)行防錯(cuò)處理。
    * 內(nèi)存分配雖然成功,但是尚未初始化就引用它。
    犯這種錯(cuò)誤主要有兩個(gè)起因:一是沒有初始化的觀念;二是誤以為內(nèi)存的缺省初值全為零,導(dǎo)致引用初值錯(cuò)誤(例如數(shù)組)。內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)一的標(biāo)準(zhǔn),盡管有些時(shí)候?yàn)榱阒?,我們寧可信其無不可信其有。所以無論用何種方式創(chuàng)建數(shù)組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
    * 內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界。
    例如在使用數(shù)組時(shí)經(jīng)常發(fā)生下標(biāo)“多1”或者“少1”的操作。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯(cuò),導(dǎo)致數(shù)組操作越界。
    * 忘記了釋放內(nèi)存,造成內(nèi)存泄露。
    含有這種錯(cuò)誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存。剛開始時(shí)系統(tǒng)的內(nèi)存充足,你看不到錯(cuò)誤。終有一次程序突然死掉,系統(tǒng)出現(xiàn)提示:內(nèi)存耗盡。
    動(dòng)態(tài)內(nèi)存的申請與釋放必須配對,程序中malloc與free的使用次數(shù)一定要相同,否則肯定有錯(cuò)誤(new/delete同理)。
    * 釋放了內(nèi)存卻繼續(xù)使用它。
    有三種情況:
    (1)程序中的對象調(diào)用關(guān)系過于復(fù)雜,實(shí)在難以搞清楚某個(gè)對象究竟是否已經(jīng)釋放了內(nèi)存,此時(shí)應(yīng)該重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),從根本上解決對象管理的混亂局面。
    (2)函數(shù)的return語句寫錯(cuò)了,注意不要返回指向“棧內(nèi)存”的“指針”或者“引用”,因?yàn)樵搩?nèi)存在函數(shù)體結(jié)束時(shí)被自動(dòng)銷毀。
    (3)使用free或delete釋放了內(nèi)存后,沒有將指針設(shè)置為NULL.導(dǎo)致產(chǎn)生“野指針”。
    「規(guī)則1」用malloc或new申請內(nèi)存之后,應(yīng)該立即檢查指針值是否為NULL.防止使用指針值為NULL的內(nèi)存。
    「規(guī)則2」不要忘記為數(shù)組和動(dòng)態(tài)內(nèi)存賦初值,防止將未被初始化的內(nèi)存作為右值使用。
    「規(guī)則3」避免數(shù)組或指針的下標(biāo)越界,特別要當(dāng)心發(fā)生“多1”或者“少1”操作。
    「規(guī)則4」動(dòng)態(tài)內(nèi)存的申請與釋放必須配對,防止內(nèi)存泄漏。
    「規(guī)則5」用free或delete釋放了內(nèi)存之后,立即將指針設(shè)置為NULL,防止產(chǎn)生“野指針”。
    二、詳解new,malloc,GlobalAlloc
    1.new
    new和delete運(yùn)算符用于動(dòng)態(tài)分配和撤銷內(nèi)存的運(yùn)算符
    new用法:
    1>開辟單變量地址空間
    1)new int; //開辟一個(gè)存放數(shù)組的存儲(chǔ)空間,返回一個(gè)指向該存儲(chǔ)空間的地址。int *a = new int 即為將一個(gè)int類型的地址賦值給整型指針a.
    2)int *a = new int(5) 作用同上,但是同時(shí)將整數(shù)賦值為5
    2>開辟數(shù)組空間
    一維:int *a = new int[100];開辟一個(gè)大小為100的整型數(shù)組空間
    一般用法: new 類型 [初值]
    delete用法:
    1> int *a = new int;
    delete a; //釋放單個(gè)int的空間
    2>int *a = new int[5];
    delete [] a; //釋放int數(shù)組空間
    要訪問new所開辟的結(jié)構(gòu)體空間,無法直接通過變量名進(jìn)行,只能通過賦值的指針進(jìn)行訪問。
    用new和delete可以動(dòng)態(tài)開辟,撤銷地址空間。在編程序時(shí),若用完一個(gè)變量(一般是暫時(shí)存儲(chǔ)的數(shù)組),下次需要再用,但卻又想省去重新初始化的功夫,可以在每次開始使用時(shí)開辟一個(gè)空間,在用完后撤銷它。
    2. malloc原型:extern void *malloc(unsigned int num_bytes);用法:#i nclude 或#i nclude 功能:分配長度為num_bytes字節(jié)的內(nèi)存塊說明:如果分配成功則返回指向被分配內(nèi)存的指針,否則返回空指針NULL.當(dāng)內(nèi)存不再使用時(shí),應(yīng)使用free()函數(shù)將內(nèi)存塊釋放。
    malloc的語法是:指針名=(數(shù)據(jù)類型*)malloc(長度),(數(shù)據(jù)類型*)表示指針。
    說明:malloc 向系統(tǒng)申請分配指定size個(gè)字節(jié)的內(nèi)存空間。返回類型是 void* 類型。void* 表示未確定類型的指針。C、C++規(guī)定,void* 類型可以強(qiáng)制轉(zhuǎn)換為任何其它類型的指針。
    malloc()函數(shù)的工作機(jī)制malloc函數(shù)的實(shí)質(zhì)體現(xiàn)在,它有一個(gè)將可用的內(nèi)存塊連接為一個(gè)長長的列表的所謂空閑鏈表。調(diào)用malloc函數(shù)時(shí),它沿連接表尋找一個(gè)大到足以滿足用戶請求所需要的內(nèi)存塊。然后,將該內(nèi)存塊一分為二(一塊的大小與用戶請求的大小相等,另一塊的大小就是剩下的字節(jié))。接下來,將分配給用戶的那塊內(nèi)存?zhèn)鹘o用戶,并將剩下的那塊(如果有的話)返回到連接表上。調(diào)用free函數(shù)時(shí),它將用戶釋放的內(nèi)存塊連接到空閑鏈上。到最后,空閑鏈會(huì)被切成很多的小內(nèi)存片段,如果這時(shí)用戶申請一個(gè)大的內(nèi)存片段,那么空閑鏈上可能沒有可以滿足用戶要求的片段了。于是,malloc函數(shù)請求延時(shí),并開始在空閑鏈上翻箱倒柜地檢查各內(nèi)存片段,對它們進(jìn)行整理,將相鄰的小空閑塊合并成較大的內(nèi)存塊。
    和new的不同從函數(shù)聲明上可以看出。malloc 和 new 至少有兩個(gè)不同: new 返回指定類型的指針,并且可以自動(dòng)計(jì)算所需要大小。比如:int *p;p = new int; //返回類型為int* 類型(整數(shù)型指針),分配大小為 sizeof(int);或:int* parr;parr = new int [100]; //返回類型為 int* 類型(整數(shù)型指針),分配大小為 sizeof(int) * 100;而 malloc 則必須由我們計(jì)算要字節(jié)數(shù),并且在返回后強(qiáng)行轉(zhuǎn)換為實(shí)際類型的指針。
    int* p;p = (int *) malloc (sizeof(int));第一、malloc 函數(shù)返回的是 void * 類型,如果你寫成:p = malloc (sizeof(int)); 則程序無法通過編譯,報(bào)錯(cuò):“不能將 void* 賦值給 int * 類型變量”。所以必須通過 (int *) 來將強(qiáng)制轉(zhuǎn)換。
    第二、函數(shù)的實(shí)參為 sizeof(int) ,用于指明一個(gè)整型數(shù)據(jù)需要的大小。如果你寫成:int* p = (int *) malloc (1);代碼也能通過編譯,但事實(shí)上只分配了1個(gè)字節(jié)大小的內(nèi)存空間,當(dāng)你往里頭存入一個(gè)整數(shù),就會(huì)有3個(gè)字節(jié)無家可歸,而直接“住進(jìn)鄰居家”!造成的結(jié)果是后面的內(nèi)存中原有數(shù)據(jù)內(nèi)容全部被清空。
    3. GlobalAlloc
    VC中關(guān)于GlobalAlloc,GlobalLock,GlobalUnLock,GlobalFree調(diào)用GlobalAlloc函數(shù)分配一塊內(nèi)存,該函數(shù)會(huì)返回分配的內(nèi)存句柄。調(diào)用GlobalLock函數(shù)鎖定內(nèi)存塊,該函數(shù)接受一個(gè)內(nèi)存句柄作為參數(shù),然后返回一個(gè)指向被鎖定的內(nèi)存塊的指針。 您可以用該指針來讀寫內(nèi)存。調(diào)用GlobalUnlock函數(shù)來解鎖先前被鎖定的內(nèi)存,該函數(shù)使得指向內(nèi)存塊的指針無效。調(diào)用GlobalFree函數(shù)來釋放內(nèi)存塊。您必須傳給該函數(shù)一個(gè)內(nèi)存句柄。
    GlobalAlloc說明分配一個(gè)全局內(nèi)存塊返回值Long,返回全局內(nèi)存句柄。零表示失敗。會(huì)設(shè)置GetLastError參數(shù)表參數(shù) 類型及說明wFlags Long,對分配的內(nèi)存類型進(jìn)行定義的常數(shù)標(biāo)志,如下所示:GMEM_FIXED 分配一個(gè)固定內(nèi)存塊GMEM_MOVEABLE 分配一個(gè)可移動(dòng)內(nèi)存塊GMEM_DISCARDABLE 分配一個(gè)可丟棄內(nèi)存塊GMEM_NOCOMPACT 堆在這個(gè)函數(shù)調(diào)用期間不進(jìn)行累積GMEM_NODISCARD 函數(shù)調(diào)用期間不丟棄任何內(nèi)存塊GMEM_ZEROINIT 新分配的內(nèi)存塊全部初始化成零dwBytes Long,要分配的字符數(shù)
    GlobalLock函數(shù)功能描述:鎖定一個(gè)全局的內(nèi)存對象,返回指向該對象的第一個(gè)字節(jié)的指針函數(shù)原型:LPVOID GlobalLock( HGLOBAL hMem )
    參數(shù):hMem:全局內(nèi)存對象的句柄。這個(gè)句柄是通過GlobalAlloc或GlobalReAlloc來得到的返回值:調(diào)用成功,返回指向該對象的第一個(gè)字節(jié)的指針調(diào)用失敗,返回NULL,可以用GetLastError來獲得出錯(cuò)信息注意:調(diào)用過GlobalLock鎖定一塊內(nèi)存區(qū)后,一定要調(diào)用GlobalUnlock來解鎖
    GlobalUnlock函數(shù)功能描述:解除被鎖定的全局內(nèi)存對象函數(shù)原型:BOOL GlobalUnlock( HGLOBAL hMem );參數(shù):hMem:全局內(nèi)存對象的句柄返回值:非零值,指定的內(nèi)存對象仍處于被鎖定狀態(tài)0,函數(shù)執(zhí)行出錯(cuò),可以用GetLastError來獲得出錯(cuò)信息,如果返回NO_ERROR,則表示內(nèi)存對象已經(jīng)解鎖了注意:這個(gè)函數(shù)實(shí)際上是將內(nèi)存對象的鎖定計(jì)數(shù)器減一,如果計(jì)數(shù)器不為0,則表示執(zhí)行過多個(gè)GlobalLock函數(shù)來對這個(gè)內(nèi)存對象加鎖,需要對應(yīng)數(shù)目的GlobalUnlock函數(shù)來解鎖。如果通過GetLastError函數(shù)返回錯(cuò)誤碼為ERROR_NOT_LOCKED,則表示未加鎖或已經(jīng)解鎖。
    示例:// Malloc memory hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nSize);// Lock memory pMem = (BYTE *) GlobalLock(hMem);……
    // Unlock memory GlobalUnlock(hMem);GlobalFree(hMem);
    三 總結(jié)
    靈活自由是C/C++語言的一大特色,而這也為C/C++程序員出了一個(gè)難題。當(dāng)程序越來越復(fù)雜時(shí),內(nèi)存的管理也會(huì)變得越加復(fù)雜,稍有不慎就會(huì)出現(xiàn)內(nèi)存問 題。內(nèi)存泄漏是最常見的內(nèi)存問題之一。內(nèi)存泄漏如果不是很嚴(yán)重,在短時(shí)間內(nèi)對程序不會(huì)有太大的影響,這也使得內(nèi)存泄漏問題有很強(qiáng)的隱蔽性,不容易被發(fā)現(xiàn)。 然而不管內(nèi)存泄漏多么輕微,當(dāng)程序長時(shí)間運(yùn)行時(shí),其破壞力是驚人的,從性能下降到內(nèi)存耗盡,甚至?xí)绊懙狡渌绦虻恼_\(yùn)行。另外內(nèi)存問題的一個(gè)共同特點(diǎn) 是,內(nèi)存問題本身并不會(huì)有很明顯的現(xiàn)象,當(dāng)有異?,F(xiàn)象出現(xiàn)時(shí)已時(shí)過境遷,其現(xiàn)場已非出現(xiàn)問題時(shí)的現(xiàn)場了,這給調(diào)試內(nèi)存問題帶來了很大的難度。
    下載Windows Debug 工具安裝后,使用其中的gflags.exe工具打開PageHeap,gflags -p /enable MainD.exe /full重新使用VS用調(diào)試方式運(yùn)行,很快就找到了出錯(cuò)位置,因?yàn)樵谀硞€(gè)靜態(tài)函數(shù)中筆誤導(dǎo)致。在編寫穩(wěn)定的服務(wù)器程序時(shí),這個(gè)工具尤為有用。
    1. 首先我們來看HeapAlloc:MSDN上的解釋為:HeapALloc是從堆上分配一塊內(nèi)存,且分配的內(nèi)存是不可移動(dòng)的(即如果沒有連續(xù)的空間能滿足分配的大小,程序不能將其他零散的空間利用起來,從而導(dǎo)致分配失?。?,該分配方法是從一指定地址開始分配,而不像GloabalAlloc是從全局堆上分配,這個(gè)有可能是全局,也有可能是 局部。函數(shù)原型為:LPVOID HeapAlloc(HANDLE hHeap,DWORD dwFlags,SIZE_T dwBytes);hHeap是進(jìn)程堆內(nèi)存開始位置。
    dwFlags是分配堆內(nèi)存的標(biāo)志。包括HEAP_ZERO_MEMORY,即使分配的空間清零。dwBytes是分配堆內(nèi)存的大小。其對應(yīng)的釋放空間函數(shù)為HeapFree.
    2. 再看GlobalAlloc:該函數(shù)用于從全局堆中分配出內(nèi)存供程序使用,函數(shù)原型為:HGLOBAL GlobalAlloc(UINT uFlags,SIZE_T dwBytes);uFlags參數(shù)含義GHND GMEM_MOVEABLE和GMEM_ZEROINIT的組合GMEM_FIXED 分配固定內(nèi)存,返回值是一個(gè)指針GMEM_MOVEABLE 分配活動(dòng)內(nèi)存,在Win32中,內(nèi)存塊不能在物理內(nèi)存中移動(dòng),但能在默認(rèn)的堆中移動(dòng)。返回值是內(nèi)存對象的句柄,用函數(shù)GlobalLock可將句柄轉(zhuǎn)化為指針GMEM_ZEROINIT 將內(nèi)存內(nèi)容初始化為零GPTR GMEM_FIXED和GMEM_ZEROINIT的組合一般情況下我們在編程的時(shí)候,給應(yīng)用程序分配的內(nèi)存都是可以移動(dòng)的或者是可以丟棄的,這樣能使有限的內(nèi)存資源充分利用,所以,在某一個(gè)時(shí)候我們分配的那塊內(nèi)存的地址是不確定的,因?yàn)樗强梢砸苿?dòng)的,所以得先鎖定那塊內(nèi)存塊,這兒應(yīng)用程序需要調(diào)用API函數(shù)GlobalLock函數(shù)來鎖定句柄。如下: lpMem=GlobalLock(hMem); 這樣應(yīng)用程序才能存取這塊內(nèi)存。所以我們在使用GlobalAllock時(shí),通常搭配使用GlobalLock,當(dāng)然在不使用內(nèi)存時(shí),一定記得使用 GlobalUnlock,否則被鎖定的內(nèi)存塊一直不能被其他變量使用。
    GlobalAlloc對應(yīng)的釋放空間的函數(shù)為GlobalFree.
    3. LocalAlloc:該函數(shù)用于從局部堆中分配內(nèi)存供程序使用,函數(shù)原型為:HLOCAL LocalAlloc(UINT uFlags,SIZE_T uBytes);參數(shù)同GlobalAlloc.在16位Windows中是有區(qū)別的,因?yàn)樵?6位windows用一個(gè)全局堆和局部堆來管理內(nèi)存,每一個(gè)應(yīng)用程序或dll裝入內(nèi)存時(shí),代碼段被裝入全局 堆,而系統(tǒng)又為每個(gè)實(shí)例從全局堆中分配了一個(gè)64kb的數(shù)據(jù)段作為該實(shí)例的局部堆,用來存放應(yīng)用程序的堆棧和所有全局或靜態(tài)變量。而 LocalAlloc/GlobalAlloc就是分別用于在局部堆或全局堆中分配內(nèi)存。
    由于每個(gè)進(jìn)程的局部堆很小,所以在局部堆中分配內(nèi)存會(huì)受到空間的限制。但這個(gè)堆是每個(gè)進(jìn)程私有的,相對而言分配數(shù)據(jù)較安全,數(shù)據(jù)訪問出錯(cuò)不至于影響到整個(gè)系統(tǒng)。而在全局堆中分配的內(nèi)存是為各個(gè)進(jìn)程共享的,每個(gè)進(jìn)程只要擁有這個(gè)內(nèi)存塊的句柄都可以訪問這塊內(nèi)存,但是每個(gè)全局內(nèi)存空間需要額外的內(nèi)存開銷,造成分配浪費(fèi)。而且一旦發(fā)生嚴(yán)重錯(cuò)誤,可能會(huì)影響到整個(gè)系統(tǒng)的穩(wěn)定。
    不過在Win32中,每個(gè)進(jìn)程都只擁有一個(gè)省缺的私有堆,它只能被當(dāng)前進(jìn)程訪問。應(yīng)用程序也不可能直接訪問系統(tǒng)內(nèi)存。所以在Win32中全局堆和局部堆都指向進(jìn)程的省缺堆。用LocalAlloc/GlobalAlloc分配內(nèi)存沒有任何區(qū)別。甚至LocalAlloc分配的內(nèi)存可以被 GlobalFree釋放掉。所以在Win32下編程,無需注意Local和Global的區(qū)別,一般的內(nèi)存分配都等效于 HeapAlloc(GetProcessHeap(),……)。
    LocalAlloc對應(yīng)的釋放函數(shù)為LockFree.
    4. VirtualAlloc:該函數(shù)的功能是在調(diào)用進(jìn)程的虛地址空間,預(yù)定或者提交一部分頁,如果用于內(nèi)存分配的話,并且分配類型未指定MEM_RESET,則系統(tǒng)將自動(dòng)設(shè)置為0;其函數(shù)原型:LPVOID VirtualAlloc(LPVOID lpAddress, // region to reserve or commit SIZE_T dwSize, // size of region DWORD flAllocationType, // type of allocation DWORD flProtect // type of access protection);VirtualAlloc可以通過并行多次調(diào)用提交一個(gè)區(qū)域的部分或全部來保留一個(gè)大的內(nèi)存區(qū)域。多重調(diào)用提交同一塊區(qū)域不會(huì)引起失敗。這使得一個(gè)應(yīng)用程 序保留內(nèi)存后可以隨意提交將被寫的頁。當(dāng)這種方式不在有效的時(shí)候,它會(huì)釋放應(yīng)用程序通過檢測被保留頁的狀態(tài)看它是否在提交調(diào)用之前已經(jīng)被提交。
    VirtualAlloc對應(yīng)的釋放函數(shù)為VirtualFree.
    5.Malloc:malloc與free是C++/C語言的標(biāo)準(zhǔn)庫函數(shù),可用于申請動(dòng)態(tài)內(nèi)存和釋放內(nèi)存。對于非內(nèi)部數(shù)據(jù)類型的對象而言,光用 malloc/free無法滿足動(dòng)態(tài)對象的要求。對象在創(chuàng)建的同時(shí)要自動(dòng)執(zhí)行構(gòu)造函數(shù),對象在消亡之前要自動(dòng)執(zhí)行析構(gòu)函數(shù)。由于malloc/free是 庫函數(shù)而不是運(yùn)算符,不在編譯器控制權(quán)限之內(nèi),不能夠把執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)的任務(wù)強(qiáng)加于malloc/free.
    6.New:new/delete是C++的運(yùn)算符??捎糜谏暾垊?dòng)態(tài)內(nèi)存和釋放內(nèi)存。C++語言需要一個(gè)能完成動(dòng)態(tài)內(nèi)存分配和初始化工作的運(yùn)算符new, 以一個(gè)能完成清理與釋放內(nèi)存工作的運(yùn)算符delete.注意new/delete不是庫函數(shù)。C++程序經(jīng)常要調(diào)用C函數(shù),而C程序只能用malloc /free管理動(dòng)態(tài)內(nèi)存。new 是個(gè)操作符,和什么“+”,“-”,“=”……有一樣的地位。
    malloc是個(gè)分配內(nèi)存的函數(shù),供你調(diào)用的。
    new是保留字,不需要頭文件支持。
    malloc需要頭文件庫函數(shù)支持。new 建立的是一個(gè)對象,malloc分配的是一塊內(nèi)存。
    new建立的對象你可以把它當(dāng)成一個(gè)普通的對象,用成員函數(shù)訪問,不要直接訪問它的地址空間malloc分配的是一塊內(nèi)存區(qū)域,就用指針訪問好了,而且還可以在里面移動(dòng)指針。
    內(nèi)存泄漏對于malloc或者new都可以檢查出來的,區(qū)別在于new可以指明是那個(gè)文件的那一行,而malloc沒有這些信息。new可以認(rèn)為是malloc加構(gòu)造函數(shù)的執(zhí)行。new出來的指針是直接帶類型信息的。而malloc返回的都是void指針。