C語言中內(nèi)存分配

C語言中內(nèi)存分配

在任何程序設(shè)計環(huán)境及語言中,內(nèi)存管理都十分重要点骑。在目前的計算機系統(tǒng)或嵌入式系統(tǒng)中匾委,內(nèi)存資源仍然是有限的。因此在程序設(shè)計中诅炉,有效地管理內(nèi)存資源是程序員首先考慮的問題蜡歹。

第1節(jié)主要介紹內(nèi)存管理基本概念屋厘,重點介紹C程序中內(nèi)存的分配,以及C語言編譯后的可執(zhí)行程序的存儲結(jié)構(gòu)和運行結(jié)構(gòu)月而,同時還介紹了堆空間和椇谷鳎空間的用途及區(qū)別。

第2節(jié)主要介紹C語言中內(nèi)存分配及釋放函數(shù)父款、函數(shù)的功能溢谤,以及如何調(diào)用這些函數(shù)申請/釋放內(nèi)存空間及其注意事項。

內(nèi)存管理基本概念

1.C程序結(jié)構(gòu)

下面列出C語言可執(zhí)行程序的基本情況(Linux 2.6環(huán)境/GCC4.0)憨攒。

[root@localhost Ctest]# ls test -l???? //test為一個可執(zhí)行程序

-rwxr-xr-x? 1 root root 4868 Mar 26 08:10 test

[root@localhost Ctest]# file test?//此文件基本情況

test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),

for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped

[root@localhost Ctest]# size test??//此二進制可執(zhí)行文件結(jié)構(gòu)情況

//代碼區(qū)靜態(tài)數(shù)據(jù)/全局初始化數(shù)據(jù)區(qū) 未初始化數(shù)據(jù)區(qū) 十進制總和 十六進制總和 文件名

text?? data??? ?????bss??? dec??? hex?filename

906??? 284????? ????4?? ?1194??? 4aa?test

可以看出世杀,此可執(zhí)行程序在存儲時(沒有調(diào)入到內(nèi)存)分為代碼區(qū)(text)、數(shù)據(jù)區(qū)(data)和未初始化數(shù)據(jù)區(qū)(bss)3個部分肝集。

(1)代碼區(qū)(text segment)瞻坝。存放CPU執(zhí)行的機器指令(machine instructions)。通常杏瞻,代碼區(qū)是可共享的(即另外的執(zhí)行程序可以調(diào)用它)所刀,因為對于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可捞挥。代碼區(qū)通常是只讀的浮创,使其只讀的原因是防止程序意外地修改了它的指令。另外树肃,代碼區(qū)還規(guī)劃了局部變量的相關(guān)信息蒸矛。

(2)全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(initialized data segment/data segment)。該區(qū)包含了在程序中明確被初始化的全局變量胸嘴、靜態(tài)變量(包括全局靜態(tài)變量和局部靜態(tài)變量)和常量數(shù)據(jù)(如字符串常量)雏掠。例如,一個不在任何函數(shù)內(nèi)的聲明(全局數(shù)據(jù)):

int?? maxcount = 99;

使得變量maxcount根據(jù)其初始值被存儲到初始化數(shù)據(jù)區(qū)中劣像。

static mincount=100;

這聲明了一個靜態(tài)數(shù)據(jù)乡话,如果是在任何函數(shù)體外聲明,則表示其為一個全局靜態(tài)變量耳奕,如果在函數(shù)體內(nèi)(局部)绑青,則表示其為一個局部靜態(tài)變量。另外屋群,如果在函數(shù)名前加上static闸婴,則表示此函數(shù)只能在當前文件中被調(diào)用。

(3)未初始化數(shù)據(jù)區(qū)芍躏。亦稱BSS區(qū)(uninitialized data segment)邪乍,存入的是全局未初始化變量。BSS這個叫法是根據(jù)一個早期的匯編運算符而來,這個匯編運算符標志著一個塊的開始庇楞。BSS區(qū)的數(shù)據(jù)在程序開始執(zhí)行之前被內(nèi)核初始化為0或者空指針(NULL)榜配。例如一個不在任何函數(shù)內(nèi)的聲明:

long? sum[1000];

將變量sum存儲到未初始化數(shù)據(jù)區(qū)。

圖3-1所示為可執(zhí)行代碼存儲時結(jié)構(gòu)和運行時結(jié)構(gòu)的對照圖吕晌。一個正在運行著的C編譯程序占用的內(nèi)存分為代碼區(qū)蛋褥、初始化數(shù)據(jù)區(qū)、未初始化數(shù)據(jù)區(qū)睛驳、堆區(qū)和棧區(qū)5個部分烙心。

(點擊查看大圖)圖3-1 C程序的內(nèi)存布局

(1)代碼區(qū)(text segment)。代碼區(qū)指令根據(jù)程序設(shè)計流程依次執(zhí)行柏靶,對于順序指令弃理,則只會執(zhí)行一次(每個進程),如果反復屎蜓,則需要使用跳轉(zhuǎn)指令痘昌,如果進行遞歸,則需要借助棧來實現(xiàn)炬转。

代碼區(qū)的指令中包括操作碼和要操作的對象(或?qū)ο蟮刂芬茫┝咎ΑH绻橇⒓磾?shù)(即具體的數(shù)值,如5)扼劈,將直接包含在代碼中驻啤;如果是局部數(shù)據(jù),將在棧區(qū)分配空間荐吵,然后引用該數(shù)據(jù)地址骑冗;如果是BSS區(qū)和數(shù)據(jù)區(qū),在代碼中同樣將引用該數(shù)據(jù)地址先煎。

(2)全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(Data Segment)贼涩。只初始化一次。

(3)未初始化數(shù)據(jù)區(qū)(BSS)薯蝎。在運行時改變其值遥倦。

(4)棧區(qū)(stack)。由編譯器自動分配釋放占锯,存放函數(shù)的參數(shù)值袒哥、局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧消略。每當一個函數(shù)被調(diào)用堡称,該函數(shù)返回地址和一些關(guān)于調(diào)用的信息,比如某些寄存器的內(nèi)容艺演,被存儲到棧區(qū)粮呢。然后這個被調(diào)用的函數(shù)再為它的自動變量和臨時變量在棧區(qū)上分配空間婿失,這就是C實現(xiàn)函數(shù)遞歸調(diào)用的方法。每執(zhí)行一次遞歸函數(shù)調(diào)用啄寡,一個新的棧框架就會被使用哩照,這樣這個新實例棧里的變量就不會和該函數(shù)的另一個實例棧里面的變量混淆挺物。

(5)堆區(qū)(heap)。用于動態(tài)內(nèi)存分配飘弧。堆在內(nèi)存中位于bss區(qū)和棧區(qū)之間识藤。一般由程序員分配和釋放,若程序員不釋放次伶,程序結(jié)束時有可能由OS回收痴昧。

之所以分成這么多個區(qū)域,主要基于以下考慮:

一個進程在運行過程中冠王,代碼是根據(jù)流程依次執(zhí)行的赶撰,只需要訪問一次,當然跳轉(zhuǎn)和遞歸有可能使代碼執(zhí)行多次柱彻,而數(shù)據(jù)一般都需要訪問多次豪娜,因此單獨開辟空間以方便訪問和節(jié)約空間。

臨時數(shù)據(jù)及需要再次使用的代碼在運行時放入棧區(qū)中哟楷,生命周期短瘤载。

全局數(shù)據(jù)和靜態(tài)數(shù)據(jù)有可能在整個程序執(zhí)行過程中都需要訪問,因此單獨存儲管理卖擅。

堆區(qū)由用戶自由分配鸣奔,以便管理。

下面通過一段簡單的代碼來查看C程序執(zhí)行時的內(nèi)存分配情況惩阶。相關(guān)數(shù)據(jù)在運行時的位置如注釋所述挎狸。

//main.cpp

int a = 0;????//a在全局已初始化數(shù)據(jù)區(qū)

char *p1;????//p1在BSS區(qū)(未初始化全局變量)

main()

{

int b;????//b在棧區(qū)

char s[] = "abc";?//s為數(shù)組變量,存儲在棧區(qū)琳猫,

//"abc"為字符串常量伟叛,存儲在已初始化數(shù)據(jù)區(qū)

char *p1,p2;??//p1脐嫂、p2在棧區(qū)

char *p3 = "123456";?//123456\0在已初始化數(shù)據(jù)區(qū)统刮,p3在棧區(qū)

static int c =0;??//C為全局(靜態(tài))數(shù)據(jù)账千,存在于已初始化數(shù)據(jù)區(qū)

//另外侥蒙,靜態(tài)數(shù)據(jù)會自動初始化

p1 = (char *)malloc(10);//分配得來的10個字節(jié)的區(qū)域在堆區(qū)

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

free(p1);

free(p2);

}

2.內(nèi)存分配方式

在C語言中,對象可以使用靜態(tài)或動態(tài)的方式分配內(nèi)存空間匀奏。

靜態(tài)分配:編譯器在處理程序源代碼時分配鞭衩。

動態(tài)分配:程序在執(zhí)行時調(diào)用malloc庫函數(shù)申請分配。

靜態(tài)內(nèi)存分配是在程序執(zhí)行之前進行的因而效率比較高,而動態(tài)內(nèi)存分配則可以靈活的處理未知數(shù)目的论衍。

靜態(tài)與動態(tài)內(nèi)存分配的主要區(qū)別如下:

靜態(tài)對象是有名字的變量瑞佩,可以直接對其進行操作;動態(tài)對象是沒有名字的變量坯台,需要通過指針間接地對它進行操作炬丸。

靜態(tài)對象的分配與釋放由編譯器自動處理;動態(tài)對象的分配與釋放必須由程序員顯式地管理蜒蕾,它通過malloc()和free兩個函數(shù)(C++中為new和delete運算符)來完成稠炬。

以下是采用靜態(tài)分配方式的例子。

int a=100;

此行代碼指示編譯器分配足夠的存儲區(qū)以存放一個整型值咪啡,該存儲區(qū)與名字a相關(guān)聯(lián)首启,并用數(shù)值100初始化該存儲區(qū)。

以下是采用動態(tài)分配方式的例子撤摸。

p1 = (char *)malloc(10*sizeof(int));//分配得來得10*4字節(jié)的區(qū)域在堆區(qū)

此行代碼分配了10個int類型的對象毅桃,然后返回對象在內(nèi)存中的地址,接著這個地址被用來初始化指針對象p1愁溜,對于動態(tài)分配的內(nèi)存唯一的訪問方式是通過指針間接地訪問疾嗅,其釋放方法為:

free(p1);

棧和堆的區(qū)別

前面已經(jīng)介紹過,棧是由編譯器在需要時分配的冕象,不需要時自動清除的變量存儲區(qū)代承。里面的變量通常是局部變量、函數(shù)參數(shù)等渐扮。堆是由malloc()函數(shù)(C++語言為new運算符)分配的內(nèi)存塊论悴,內(nèi)存釋放由程序員手動控制,在C語言為free函數(shù)完成(C++中為delete)墓律。棧和堆的主要區(qū)別有以下幾點:

(1)管理方式不同檩咱。

棧編譯器自動管理梢薪,無需程序員手工控制粪般;而堆空間的申請釋放工作由程序員控制秧秉,容易產(chǎn)生內(nèi)存泄漏。

(2)空間大小不同针肥。

棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu)饼记,是一塊連續(xù)的內(nèi)存區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的慰枕,當申請的空間超過棧的剩余空間時具则,將提示溢出。因此具帮,用戶能從棧獲得的空間較小博肋。

堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu)低斋,是不連續(xù)的內(nèi)存區(qū)域。因為系統(tǒng)是用鏈表來存儲空閑內(nèi)存地址的匪凡,且鏈表的遍歷方向是由低地址向高地址膊畴。由此可見,堆獲得的空間較靈活病游,也較大巴比。棧中元素都是一一對應(yīng)的,不會存在一個內(nèi)存塊從棧中間彈出的情況礁遵。

(3)是否產(chǎn)生碎片。

對于堆來講采记,頻繁的malloc/free(new/delete)勢必會造成內(nèi)存空間的不連續(xù)佣耐,從而造成大量的碎片,使程序效率降低(雖然程序在退出后操作系統(tǒng)會對內(nèi)存進行回收管理)唧龄。對于棧來講兼砖,則不會存在這個問題。

(4)增長方向不同既棺。

堆的增長方向是向上的讽挟,即向著內(nèi)存地址增加的方向;棧的增長方向是向下的丸冕,即向著內(nèi)存地址減小的方向耽梅。

(5)分配方式不同。

堆都是程序中由malloc()函數(shù)動態(tài)申請分配并由free()函數(shù)釋放的胖烛;棧的分配和釋放是由編譯器完成的眼姐,棧的動態(tài)分配由alloca()函數(shù)完成,但是棧的動態(tài)分配和堆是不同的佩番,他的動態(tài)分配是由編譯器進行申請和釋放的众旗,無需手工實現(xiàn)。

(6)分配效率不同趟畏。

棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu)贡歧,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行赋秀。堆則是C函數(shù)庫提供的利朵,它的機制很復雜,例如為了分配一塊內(nèi)存沃琅,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大的空間哗咆,如果沒有足夠大的空間(可能是由于內(nèi)存碎片太多),就有需要操作系統(tǒng)來重新整理內(nèi)存空間益眉,這樣就有機會分到足夠大小的內(nèi)存晌柬,然后返回姥份。顯然,堆的效率比棧要低得多年碘。

Linux數(shù)據(jù)類型大小

在Linux操作系統(tǒng)下使用GCC進行編程澈歉,目前一般的處理器為32位字寬,下面是/usr/include/limit.h文件對Linux下數(shù)據(jù)類型的限制及存儲字節(jié)大小的說明屿衅。

/* We don't have #include_next.?? Define ANSI? for standard 32-bit words.? */

/* These assume 8-bit 'char's, 16-bit 'short int's,?? and 32-bit 'int's and 'long int's.? */

1.char數(shù)據(jù)類型

char類型數(shù)據(jù)所占內(nèi)存空間為8位埃难。其中有符號字符型變量取值范圍為?128~127,無符號型字符變量取值范圍為0~255涤久。其限制如下:

/* Number of bits in a 'char'.?*/

#? define CHAR_BIT?8??????????//所占字節(jié)數(shù)

/* Minimum and maximum values a 'signed char' can hold.? */??//有符號字符型范圍

#? define SCHAR_MIN?(-128)

#? define SCHAR_MAX?127

/* Maximum value an 'unsigned char' can hold.? (Minimum is 0.)? */?//無符號字符型范圍

#? define UCHAR_MAX?255

/* Minimum and maximum values a 'char' can hold.? */

#? ifdef __CHAR_UNSIGNED__

#?? define CHAR_MIN?0

#?? define CHAR_MAX?UCHAR_MAX

#? else

#?? define CHAR_MIN?SCHAR_MIN

#?? define CHAR_MAX?SCHAR_MAX

#? endif

2.short int數(shù)據(jù)類型

short int類型數(shù)據(jù)所占內(nèi)存空間為16位涡尘。其中有符號短整型變量取值范圍為?32768~32767,無符號短整型變量取值范圍為0~65535响迂。其限制如下:

/* Minimum and maximum values a 'signed short int' can hold.? */?// 有符號短整型范圍

#? define SHRT_MIN?(-32768)

#? define SHRT_MAX?32767

/* Maximum value an 'unsigned short int' can hold.? (Minimum is 0.)? */// 無符號短整型范圍

#? define USHRT_MAX?65535

3.int數(shù)據(jù)類型

int類型數(shù)據(jù)所占內(nèi)存空間為32位考抄。其中有符號整型變量取值范圍為?2147483648~2147483647,無符號型整型變量取值范圍為0~4294967295U蔗彤。其限制如下:

/* Minimum and maximum values a 'signed int' can hold.? */??//整形范圍

#? define INT_MIN?(-INT_MAX - 1)

#? define INT_MAX?2147483647

/* Maximum value an 'unsigned int' can hold.? (Minimum is 0.)? */?//無符號整形范圍

#? define UINT_MAX?4294967295U

4.long int數(shù)據(jù)類型

隨著宏__WORDSIZE值的改變川梅,long int數(shù)據(jù)類型的大小也會發(fā)生改變。如果__WORDSIZE的值為32然遏,則long int和int類型一樣贫途,占有32位。在Linux GCC4.0-i386版本中待侵,默認情況下__WORDSIZE的值為32丢早。其定義如下:

//come from /usr/include/bits/wordsize.h

#define __WORDSIZE?32

在64位機器上,如果__WORDSIZE的值為64诫给, long int類型數(shù)據(jù)所占內(nèi)存空間為64位香拉。其中有長整型變量取值范圍為-9223372036854775808L~3372036854775807L,無符號長整型變量取值范圍為0~18446744073709551615UL中狂。其限制如下:

/* Minimum and maximum values a 'signed long int' can hold.? */?//有符號長整形范圍

#? if __WORDSIZE == 64

#?? define LONG_MAX?9223372036854775807L

#? else

#?? define LONG_MAX?2147483647L

#? endif

#? define LONG_MIN?(-LONG_MAX - 1L)

/* Maximum value an 'unsigned long int' can hold.? (Minimum is 0.)? *///無符號長整形范圍

#? if __WORDSIZE == 64

#?? define ULONG_MAX?18446744073709551615UL

#? else

#?? define ULONG_MAX?4294967295UL

#? endif

5.long long int數(shù)據(jù)類型

在C99中凫碌,還定義了long long int數(shù)據(jù)類型。其數(shù)據(jù)類型限制如下:

#? ifdef __USE_ISOC99

/* Minimum and maximum values a 'signed long long int' can hold.? *///無符號長長整形范圍

#?? define LLONG_MAX?9223372036854775807LL

#?? define LLONG_MIN?(-LLONG_MAX - 1LL)

/* Maximum value an 'unsigned long long int' can hold.? (Minimum is 0.)? *///有符號長長整形范圍

#?? define ULLONG_MAX?18446744073709551615ULL

#? endif /* ISO C99 */

數(shù)據(jù)存儲區(qū)域?qū)嵗?/b>

此程序顯示了數(shù)據(jù)存儲區(qū)域?qū)嵗搁牛诖顺绦蛑惺⑾眨褂昧薳text、edata和end3個外部全局變量勋又,這是與用戶進程相關(guān)的虛擬地址苦掘。

在程序源代碼中列出了各數(shù)據(jù)的存儲位置,同時在程序運行時顯示了各數(shù)據(jù)的運行位置楔壤,圖3-2所示為程序運行過程中各變量的存儲位置鹤啡。

圖3-2 函數(shù)運行時各數(shù)據(jù)位置

主函數(shù)源代碼如下:

[root@localhost linux_app]# cat mem_add.c

#include

#include

#include

#include

extern void afunc(void);

extern etext,edata,end;

int bss_var;?????????????????????????? ?????//未初始化全局數(shù)據(jù)存儲在BSS區(qū)

int data_var=42;??????????????????????? ????//初始化全局數(shù)據(jù)存儲在數(shù)據(jù)區(qū)

#define SHW_ADR(ID,I) printf("the %8s\t is at adr:%8x\n",ID,&I);?//打印地址宏

int main(int argc,char *argv[])

{

char *p,*b,*nb;

printf("Adr etext:%8x\t Adr edata %8x\t Adr end %8x\t\n",&etext,&edata,&end);

printf("\ntext Location:\n");

SHW_ADR("main",main);????????? ????//查看代碼段main函數(shù)位置

SHW_ADR("afunc",afunc);?????? ????//查看代碼段afunc函數(shù)位置

printf("\nbss Location:\n");

SHW_ADR("bss_var",bss_var);? ????/查看BSS段變量位置

printf("\ndata location:\n");

SHW_ADR("data_var",data_var);?? ??/查看數(shù)據(jù)段變量

printf("\nStack Locations:\n");

afunc();

p=(char *)alloca(32);????????? ????//從棧中分配空間

if(p!=NULL)

{

SHW_ADR("start",p);

SHW_ADR("end",p+31);

}

b=(char *)malloc(32*sizeof(char)); ??//從堆中分配空間

nb=(char *)malloc(16*sizeof(char));??//從堆中分配空間

printf("\nHeap Locations:\n");

printf("the Heap start: %p\n",b);???//堆起始位置

printf("the Heap end:%p\n",(nb+16*sizeof(char)));//堆結(jié)束位置

printf("\nb and nb in Stack\n");

SHW_ADR("b",b);???????//顯示棧中數(shù)據(jù)b的位置

SHW_ADR("nb",nb); ??????//顯示棧中數(shù)據(jù)nb的位置

free(b);?????????//釋放申請的空間,以避免內(nèi)存泄漏

free(nb); ????????//釋放申請的空間蹲嚣,以避免內(nèi)存泄漏

}

子函數(shù)源代碼如下:

void afunc(void)

{

static int long level=0;??????? ??//靜態(tài)數(shù)據(jù)存儲在數(shù)據(jù)段中

int????? stack_var;?????????????? ??//局部變量递瑰,存儲在棧區(qū)

if(++level==5)

{

return;

}

printf("stack_var is at:%p\n",&stack_var);

//????? SHW_ADR("stack_var in stack section",stack_var);

//????? SHW_ADR("Level in data section",level);

afunc();

}

函數(shù)運行結(jié)果如下:

[root@localhost linux_app]# gcc -o mem_add mem_add.c?//編譯

[root@localhost linux_app]# ./mem_add ????//運行結(jié)果

Adr etext: 8048702?????? Adr edata? 8049950????? Adr end? 804995c

text Location:

the???? main???? is at adr: 8048418

the??? afunc???? is at adr: 8048611

bss Location:

the? bss_var???? is at adr: 8049958

data location:

the data_var???? is at adr: 804994c

Stack Locations:

the stack_var in stack section?? ?is at adr:bfbf6c44

the Level in data section??????? ?is at adr: 8049954

the stack_var in stack section?? ?is at adr:bfbf6c24

the Level in data section??????? ?is at adr: 8049954

the stack_var in stack section?? ?is at adr:bfbf6c04

the Level in data section??????? ?is at adr: 8049954

the stack_var in stack section?? ?is at adr:bfbf6be4

the Level in data section??????? ?is at adr: 8049954

the??? start???? is at adr:bfbf6c74

the????? end???? is at adr:bfbf6cf0

Heap Locations:

the Heap start: 0x8453008

the Heap end:0x8453040

b and nb in Stack

the??????? b???? is at adr:bfbf6c70

the?????? nb???? is at adr:bfbf6c6c

如果運行環(huán)境不一樣祟牲,運行程序的地址與此將有差異,但是抖部,各區(qū)域之間的相對關(guān)系不會發(fā)生變化说贝。可以通過readelf命令來查看可執(zhí)行文件的詳細內(nèi)容慎颗。

[root@localhost yangzongde]# readelf -a memadd

3.2 內(nèi)存管理函數(shù)

3.2.1 malloc/free函數(shù)

Malloc()函數(shù)用來在堆中申請內(nèi)存空間乡恕,free()函數(shù)釋放原先申請的內(nèi)存空間。Malloc()函數(shù)是在內(nèi)存的動態(tài)存儲區(qū)中分配一個長度為size字節(jié)的連續(xù)空間俯萎。其參數(shù)是一個無符號整型數(shù)傲宜,返回一個指向所分配的連續(xù)存儲域的起始地址的指針。當函數(shù)未能成功分配存儲空間時(如內(nèi)存不足)則返回一個NULL指針夫啊。

由于內(nèi)存區(qū)域總是有限的蛋哭,不能無限制地分配下去,而且程序應(yīng)盡量節(jié)省資源涮母,所以當分配的內(nèi)存區(qū)域不用時,則要釋放它躁愿,以便其他的變量或程序使用叛本。

這兩個函數(shù)的庫頭文件為:

#include

函數(shù)定義如下:

void *malloc(size_t size)???//返回類型為空指針類型

void free(void *ptr)

例如:

int *p1,*p2;

p1=(int *)malloc(10*sizeof(int));

p2=p1;

……

free(p2) ;??????/*或者free(p1)*/

p1=NULL;???????/*或者p2=NULL */

malloc()函數(shù)返回值賦給p1,又把p1的值賦給p2彤钟,所以此時p1来候,p2都可作為free函數(shù)的參數(shù)。使用free()函數(shù)時逸雹,需要特別注意下面幾點:

(1)調(diào)用free()釋放內(nèi)存后营搅,不能再去訪問被釋放的內(nèi)存空間。內(nèi)存被釋放后梆砸,很有可能該指針仍然指向該內(nèi)存單元转质,但這塊內(nèi)存已經(jīng)不再屬于原來的應(yīng)用程序,此時的指針為懸掛指針(可以賦值為NULL)帖世。

(2)不能兩次釋放相同的指針休蟹。因為釋放內(nèi)存空間后,該空間就交給了內(nèi)存分配子程序日矫,再次釋放內(nèi)存空間會導致錯誤赂弓。也不能用free來釋放非malloc()、calloc()和realloc()函數(shù)創(chuàng)建的指針空間哪轿,在編程時盈魁,也不要將指針進行自加操作,使其指向動態(tài)分配的內(nèi)存空間中間的某個位置窃诉,然后直接釋放杨耙,這樣也有可能引起錯誤赤套。

(3)在進行C語言程序開發(fā)中,malloc/free是配套使用的按脚,即不需要的內(nèi)存空間都需要釋放回收于毙。

下面是使用這兩個函數(shù)的一個例子。

[root@localhost yangzongde]# cat malloc_example.c

#include?????????????? //printf()????//(1)頭文件信息

#include????????????? //malloc()????//(2)

int main(int argc,char* argv[],char* envp[])???//(3)

{

int count;

int* array;

if((array=(int *)malloc(10*sizeof(int)))==NULL) ?//(4)分配空間

{

printf("malloc memory unsuccessful");

exit(1);

}

for (count=0;count<10;count++) ?????//(5) 賦值

{

*array=count;

array++;

}

for(count=9;count>=0;count--)?????????????? ???//(6)賦值

{

array--;

printf("%4d",*array);

}

printf("\n");

free(array); ???????//(7)釋放空間

array=NULL; ??????//(8)將指針置為空辅搬,避免不安全訪問

exit (0);

}

[root@localhost yangzongde]# gcc -o malloc_example malloc_example.c ?//編譯

[root@localhost yangzongde]# ./malloc_example ??????//運行

9?? 8?? 7?? 6?? 5?? 4?? 3?? 2?? 1?? 0

在以上程序中唯沮,(1)句中包含stdio.h頭文件,從而在后面可以調(diào)用printf()函數(shù)堪遂。(2)句中包含stdlib.h頭文件介蛉,其是malloc()函數(shù)的頭文件。(3)句為函數(shù)的入口位置溶褪,此處采用Linux下編程標準币旧,返回值為int型,argc為參數(shù)個數(shù)猿妈, argv[]為參數(shù)吹菱,envp[]存放的是所有環(huán)境變量。(4)句動態(tài)分配了10個整型存儲區(qū)域彭则,此語句可以分為以下幾步鳍刷。

① 分配10個整型的連續(xù)存儲空間,并返回一個指向其起始地址的整型指針俯抖。

② 把此整型指針地址賦給array输瓜。

③ 檢測返回值是否為NULL。

(5)芬萍、(6)句為數(shù)組賦值并打印輸出尤揣,以免內(nèi)存泄漏。(7)句調(diào)用free()函數(shù)釋放內(nèi)存空間柬祠。(8)句將一個NULL指針傳遞給array北戏,雖然在很多情況下可以不用此句,但這樣處理可以避免此指針成為野指針漫蛔。

在C++中最欠,使用new和delete運算符來實現(xiàn)內(nèi)存的分配和釋放,使用new/delete運算符實現(xiàn)內(nèi)存管理比使用malloc/free函數(shù)更有優(yōu)越性惩猫。new/delete運算符定義如下:

static void* operator new(size_t sz);?????//new運算符

static void? operator delete(void* p); ?????//delete運算符

下面是一段C++程序代碼:

void UseNewDelete(void)

{

Obj? *a = new Obj;?????? ????//申請動態(tài)內(nèi)存并且初始化

//…

delete a;??????????????? ???//清除并且釋放內(nèi)存

}

下面詳細介紹C++中new/delete運算符的使用方法芝硬。

class A

{

public:

A() ?{?? cout<<"A is here!"<

int i;

};

A* pA=new A;?????//調(diào)用new運算符申請空間

delete pA;??????//刪除pA

其中,語句new A完成了以下兩個功能:

(1)調(diào)用運算符new轧房,在自由存儲區(qū)分配一個sizeof(A)大小的內(nèi)存空間拌阴。

(2)調(diào)用構(gòu)造函數(shù)A(),在這塊內(nèi)存空間上初始化對象奶镶。

當然迟赃,delete pA完成相反的兩件事:

(1)調(diào)用析構(gòu)函數(shù)~A()陪拘,銷毀對象。

(2)調(diào)用運算符delete纤壁,釋放內(nèi)存左刽。

由此可以看出,運算符new和delete提供了動態(tài)分配和釋放存儲區(qū)的功能酌媒。它們的作用相當于C語言的malloc()和free()函數(shù)欠痴,但是性能更為優(yōu)越。使用new比使用malloc()有以下幾個優(yōu)點:

(1)new自動計算要分配給對象的內(nèi)存空間大小秒咨,不使用sizeof運算符喇辽,簡單,而且可以避免錯誤雨席。

(2)自動地返回正確的指針類型菩咨,不用進行強制類型轉(zhuǎn)換。

(3)用構(gòu)造函數(shù)給分配的對象進行初始化陡厘。

但是抽米,使用malloc函數(shù)和new分配內(nèi)存的時候,本身并沒有對這塊內(nèi)存空間做清零等任何動作糙置。因此缨硝,申請內(nèi)存空間后,其返回的新分配的內(nèi)存是沒有零填充的罢低,程序員需要使用memset()函數(shù)來初始化內(nèi)存。

3.2.2 realloc--更改已經(jīng)配置的內(nèi)存空間

realloc()函數(shù)用來從堆上分配內(nèi)存胖笛,當需要擴大一塊內(nèi)存空間時网持,realloc()試圖直接從堆上當前內(nèi)存段后面的字節(jié)中獲得更多的內(nèi)存空間,如果能夠滿足长踊,則返回原指針功舀;如果當前內(nèi)存段后面的空閑字節(jié)不夠,那么就使用堆上第一個能夠滿足這一要求的內(nèi)存塊身弊,將目前的數(shù)據(jù)復制到新的位置辟汰,而將原來的數(shù)據(jù)塊釋放掉。如果內(nèi)存不足阱佛,重新申請空間失敗帖汞,則返回NULL。此函數(shù)定義如下:

void *realloc(void *ptr,size_t size)

參數(shù)ptr為先前由malloc凑术、calloc和realloc所返回的內(nèi)存指針翩蘸,而參數(shù)size為新配置的內(nèi)存大小。其庫頭文件為:

#include

當調(diào)用realloc()函數(shù)重新分配內(nèi)存時淮逊,如果申請失敗催首,將返回NULL扶踊,此時原來指針仍然有效,因此在程序編寫時需要進行判斷郎任,如果調(diào)用成功秧耗,realloc()函數(shù)會重新分配一塊新內(nèi)存,并將原來的數(shù)據(jù)拷貝到新位置舶治,返回新內(nèi)存的指針分井,而釋放掉原來指針(realloc()函數(shù)的參數(shù)指針)指向的空間,原來的指針變?yōu)椴豢捎茫床恍枰籴尫偶叽膊荒茉籴尫牛┰映椋虼耍话悴皇褂靡韵抡Z句:

ptr=realloc(ptr,new_amount)

如果內(nèi)存減少韩脏,malloc僅僅改變索引信息缩麸,但并不代表被減少的部分還可以訪問,這一部分內(nèi)存將交給系統(tǒng)內(nèi)存分配子程序赡矢。

下面是一個使用relloc函數(shù)的實例杭朱。

[root@localhost yangzongde]# cat realloc_example.c

#include

#include

int main (int argc,char* argv[],char* envp[])???//(1)主函數(shù)

{

int input;

int n;

int *numbers1;

int *numbers2;

numbers1=NULL;

if((numbers2=(int *)malloc(5*sizeof(int)))==NULL)?//(2)numbers2指針申請空間

{

printf("malloc memory unsuccessful");

//free(numbers2);

numbers2=NULL;

exit(1);

}

for (n=0;n<5;n++) ??????//(3)初始化

{

*(numbers2+n)=n;

printf("numbers2's data: %d\n",*(numbers2+n));

}

printf("Enter an integer value you want to remalloc ( enter 0 to stop)\n");//(4)新申請空間大小

scanf ("%d",&input);

numbers1=(int *)realloc(numbers2,(input+5)*sizeof(int));??//(5)重新申請空間

if (numbers1==NULL)

{

printf("Error (re)allocating memory");

exit (1);

}

for(n=0;n<5;n++)???????//(6)這5個數(shù)是從numbers2拷貝而來

{

printf("the numbers1s's data copy from numbers2: %d\n",*(numbers1+n));

}

for(n=0;n

{

*(numbers1+5+n)=n*2;

printf ("nummber1's new data: %d\n",*(numbers1+5+n));?// numbers1++;

}

printf("\n");

free(numbers1);???????//(8)釋放numbers1

numbers1=NULL;

// free(numbers2);?????//(9)不能再釋放numbers2

return 0;

}

[root@localhost yangzongde]# gcc -o realloc_example realloc_example.c

[root@localhost yangzongde]# ./realloc_example

numbers2's data: 0

numbers2's data: 1

numbers2's data: 2

numbers2's data: 3

numbers2's data: 4

Enter an integer value you want to remalloc ( enter 0 to stop)?//重新申請空間

5

the numbers1s's data copy from numbers2: 0

the numbers1s's data copy from numbers2: 1

the numbers1s's data copy from numbers2: 2

the numbers1s's data copy from numbers2: 3

the numbers1s's data copy from numbers2: 4

nummber1's new data: 0

nummber1's new data: 2

nummber1's new data: 4

nummber1's new data: 6

nummber1's new data: 8

此程序是一個簡單的重新申請內(nèi)存空間的實例,(1)為函數(shù)入口吹散,前面已經(jīng)介紹過弧械。(2)從堆空間中申請5個int空間,將返回地址賦給numbers2空民,如果返回值為NULL刃唐,將返回錯誤信息,釋放numbers2并退出界轩。(3)為新申請的空間初始化画饥。(4)輸入需要增加的內(nèi)存數(shù)量。(5)調(diào)用realloc()函數(shù)重新申請內(nèi)存空間浊猾,重新申請內(nèi)存空間大小為原有空間大小加上用戶輸入的內(nèi)存空間數(shù)抖甘。如果申請失敗,將返回NULL葫慎,此時numbers2仍然有效衔彻。如果申請成功,將重新分配一塊大小合適的空間偷办,并將新空間首地址賦給numbers1艰额,同時將numbers2所指向的5個空間的數(shù)據(jù)復制到新的內(nèi)存空間中,釋放掉原來numbers2所指向的內(nèi)存空間椒涯。(6)打印從numbers2所指向的原空間拷貝的數(shù)據(jù)悴晰,(7)句對新增加的空間進行初始化。(8)句釋放number1所指向的新申請空間。(9)為注釋掉的代碼铡溪,提示讀者此時對原空間再次釋放漂辐,因為第(5)已經(jīng)完成了這一操作。

其他內(nèi)存管理函數(shù)calloc和alloca

1.calloc函數(shù)

calloc是malloc函數(shù)的簡單包裝棕硫,它的主要優(yōu)點是把動態(tài)分配的內(nèi)存進行初始化髓涯,全部清零。其操作及語法類似malloc()函數(shù)哈扮。

ptr=(struct data *)calloc (count,sizeof(strunt data))?//申請并初始化空間

下面是這個函數(shù)的實現(xiàn)描述:

void *calloc(size_t nmemb,size_t size)

{

void *p;

size_t total;

total=nmemb *size;

p=malloc(total);?????????//申請空間

if(p!=NULL)

memset(p,'\0',total);??????//將其實始化為\0

return p;

}

2.a(chǎn)lloca函數(shù)

alloca()函數(shù)用來在棧中分配size個字節(jié)的內(nèi)存空間纬纪,因此函數(shù)返回時會自動釋放掉空間。alloca函數(shù)定義及庫頭文件如下:

/* Allocate a block that will be freed when the calling function exits.? */

extern void *alloca (size_t __size) __THROW;???//從棧中申請空間

返回值:若分配成功返回指針滑肉,失敗則返回NULL包各。

它與malloc()函數(shù)的區(qū)別主要在于:

alloca是向棧申請內(nèi)存,無需釋放靶庙,malloc申請的內(nèi)存位于堆中问畅,最終需要函數(shù)free來釋放。

malloc函數(shù)并沒有初始化申請的內(nèi)存空間六荒,因此調(diào)用malloc()函數(shù)之后护姆,還需調(diào)用函數(shù)memset初始化這部分內(nèi)存空間;alloca則將初始化這部分內(nèi)存空間為0掏击。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卵皂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子砚亭,更是在濱河造成了極大的恐慌灯变,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捅膘,死亡現(xiàn)場離奇詭異添祸,居然都是意外死亡,警方通過查閱死者的電腦和手機篓跛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坦刀,“玉大人愧沟,你說我怎么就攤上這事±鹨#” “怎么了沐寺?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盖奈。 經(jīng)常有香客問我混坞,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任究孕,我火速辦了婚禮啥酱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘厨诸。我一直安慰自己镶殷,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布微酬。 她就那樣靜靜地躺著绘趋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颗管。 梳的紋絲不亂的頭發(fā)上陷遮,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音垦江,去河邊找鬼帽馋。 笑死,一個胖子當著我的面吹牛疫粥,可吹牛的內(nèi)容都是我干的茬斧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼梗逮,長吁一口氣:“原來是場噩夢啊……” “哼项秉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慷彤,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤娄蔼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后底哗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岁诉,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年跋选,在試婚紗的時候發(fā)現(xiàn)自己被綠了涕癣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡前标,死狀恐怖坠韩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炼列,我是刑警寧澤只搁,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站俭尖,受9級特大地震影響氢惋,放射性物質(zhì)發(fā)生泄漏洞翩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一焰望、第九天 我趴在偏房一處隱蔽的房頂上張望骚亿。 院中可真熱鬧,春花似錦柿估、人聲如沸循未。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽的妖。三九已至,卻和暖如春足陨,著一層夾襖步出監(jiān)牢的瞬間嫂粟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工墨缘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留星虹,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓镊讼,卻偏偏與公主長得像宽涌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝶棋,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • (JG-2014-08-20)(前半部分經(jīng)過網(wǎng)上多篇文章對比整理)(后半部分根據(jù)ExceptionalCpp卸亮、C+...
    JasonGao閱讀 5,597評論 2 23
  • __block和__weak修飾符的區(qū)別其實是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,284評論 0 6
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,745評論 0 38
  • 他雙腳踏過的土地玩裙, 都陷入深深的貧瘠兼贸。 胯下戰(zhàn)馬嘶鳴, 手中長劍血凝吃溅。 她雙眸望過的天際溶诞。 都染上旖旎的光影。 頸...
    聞道南嘉閱讀 163評論 0 0
  • 微黃的燈光下,我看著形形色色的人赖歌,站在公交站枉圃,著急得等待著回家的車,哦俏站!或許應(yīng)該這樣說讯蒲,他們在著急的等待一輛遲遲未...
    八佰伴閱讀 989評論 0 2