C程序內存分配

內存管理及相關工具

在任何程序設計環境及語言中,內存管理都十分重要。在目前的計算機系統或嵌入式系統中,內存資源仍然是有限的。因此在程序設計中,有效地管理內存資源是程序員首先考慮的問題。

第1節主要介紹內存管理基本概念,重點介紹C程序中內存的分配,以及C語言編譯後的可執行程序的存儲結構和運行結構,同時還介紹了堆空間和棧空間的用途及區別。

第2節主要介紹C語言中內存分配及釋放函數、函數的功能,以及如何調用這些函數申請/釋放內存空間及其注意事項。

第3節主要介紹了在Linux下除GDB之外的內存調試及管理工具,包括glibc提供的內存管理函數、MemWatch內存錯誤檢測工具和valgrind內存檢測工具。重點介紹使用這三類工具進行內存檢查的原理及相關示例。

3.1 內存管理基本概念

3.1.1 C程序內存分配

1.C程序結構

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

[root@localhost Ctest]# ls test -l     //test爲一個可執行程序
-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  //此二進制可執行文件結構情況
//代碼區靜態數據/全局初始化數據區 未初始化數據區 十進制總和 十六進制總和 文件名
text   data         bss    dec    hex filename
906    284          4    1194    4aa test

可以看出,此可執行程序在存儲時(沒有調入到內存)分爲代碼區(text)、數據區(data)和未初始化數據區(bss)3個部分。

(1)代碼區(text segment)。存放CPU執行的機器指令(machine instructions)。通常,代碼區是可共享的(即另外的執行程序可以調用它),因爲對於頻繁被執行的程序,只需要在內存中有一份代碼即可。代碼區通常是隻讀的,使其只讀的原因是防止程序意外地修改了它的指令。另外,代碼區還規劃了局部變量的相關信息。

(2)全局初始化數據區/靜態數據區(initialized data segment/data segment)。該區包含了在程序中明確被初始化的全局變量、靜態變量(包括全局靜態變量和局部靜態變量)和常量數據(如字符串常量)。例如,一個不在任何函數內的聲明(全局數據):

int   maxcount = 99;

使得變量maxcount根據其初始值被存儲到初始化數據區中。

static mincount=100;

這聲明瞭一個靜態數據,如果是在任何函數體外聲明,則表示其爲一個全局靜態變量,如果在函數體內(局部),則表示其爲一個局部靜態變量。另外,如果在函數名前加上static,則表示此函數只能在當前文件中被調用。

(3)未初始化數據區。亦稱BSS區(uninitialized data segment),存入的是全局未初始化變量。BSS這個叫法是根據一個早期的彙編運算符而來,這個彙編運算符標誌着一個塊的開始。BSS區的數據在程序開始執行之前被內核初始化爲0或者空指針(NULL)。例如一個不在任何函數內的聲明:

long  sum[1000];

將變量sum存儲到未初始化數據區。

圖3-1所示爲可執行代碼存儲時結構和運行時結構的對照圖。一個正在運行着的C編譯程序佔用的內存分爲代碼區、初始化數據區、未初始化數據區、堆區和棧區5個部分。

(點擊查看大圖)圖3-1 C程序的內存佈局

(1)代碼區(text segment)。代碼區指令根據程序設計流程依次執行,對於順序指令,則只會執行一次(每個進程),如果反覆,則需要使用跳轉指令,如果進行遞歸,則需要藉助棧來實現。

代碼區的指令中包括操作碼和要操作的對象(或對象地址引用)。如果是立即數(即具體的數值,如5),將直接包含在代碼中;如果是局部數據,將在棧區分配空間,然後引用該數據地址;如果是BSS區和數據區,在代碼中同樣將引用該數據地址。

(2)全局初始化數據區/靜態數據區(Data Segment)。只初始化一次。

(3)未初始化數據區(BSS)。在運行時改變其值。

(4)棧區(stack)。由編譯器自動分配釋放,存放函數的參數值、局部變量的值等。其操作方式類似於數據結構中的棧。每當一個函數被調用,該函數返回地址和一些關於調用的信息,比如某些寄存器的內容,被存儲到棧區。然後這個被調用的函數再爲它的自動變量和臨時變量在棧區上分配空間,這就是C實現函數遞歸調用的方法。每執行一次遞歸函數調用,一個新的棧框架就會被使用,這樣這個新實例棧裏的變量就不會和該函數的另一個實例棧裏面的變量混淆。

(5)堆區(heap)。用於動態內存分配。堆在內存中位於bss區和棧區之間。一般由程序員分配和釋放,若程序員不釋放,程序結束時有可能由OS回收。

之所以分成這麼多個區域,主要基於以下考慮:

一個進程在運行過程中,代碼是根據流程依次執行的,只需要訪問一次,當然跳轉和遞歸有可能使代碼執行多次,而數據一般都需要訪問多次,因此單獨開闢空間以方便訪問和節約空間。

臨時數據及需要再次使用的代碼在運行時放入棧區中,生命週期短。

全局數據和靜態數據有可能在整個程序執行過程中都需要訪問,因此單獨存儲管理。

堆區由用戶自由分配,以便管理。

下面通過一段簡單的代碼來查看C程序執行時的內存分配情況。相關數據在運行時的位置如註釋所述。

//main.cpp 
int a = 0;    //a在全局已初始化數據區
char *p1;    //p1在BSS區(未初始化全局變量)
main()
{
int b;    //b在棧區
char s[] = "abc"; //s爲數組變量,存儲在棧區,
//"abc"爲字符串常量,存儲在已初始化數據區
char *p1,p2;  //p1、p2在棧區
char *p3 = "123456"; //123456\0在已初始化數據區,p3在棧區
static int c =0;  //C爲全局(靜態)數據,存在於已初始化數據區
//另外,靜態數據會自動初始化
p1 = (char *)malloc(10);//分配得來的10個字節的區域在堆區
p2 = (char *)malloc(20);//分配得來的20個字節的區域在堆區
free(p1);
free(p2);
}

2.內存分配方式

在C語言中,對象可以使用靜態或動態的方式分配內存空間。

靜態分配:編譯器在處理程序源代碼時分配。

動態分配:程序在執行時調用malloc庫函數申請分配。

靜態內存分配是在程序執行之前進行的因而效率比較高,而動態內存分配則可以靈活的處理未知數目的。

靜態與動態內存分配的主要區別如下:

靜態對象是有名字的變量,可以直接對其進行操作;動態對象是沒有名字的變量,需要通過指針間接地對它進行操作。

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

以下是採用靜態分配方式的例子。

int a=100;

此行代碼指示編譯器分配足夠的存儲區以存放一個整型值,該存儲區與名字a相關聯,並用數值100初始化該存儲區。

以下是採用動態分配方式的例子。

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

此行代碼分配了10個int類型的對象,然後返回對象在內存中的地址,接着這個地址被用來初始化指針對象p1,對於動態分配的內存唯一的訪問方式是通過指針間接地訪問,其釋放方法爲:

free(p1);

3.1.2 棧和堆的區別

前面已經介紹過,棧是由編譯器在需要時分配的,不需要時自動清除的變量存儲區。裏面的變量通常是局部變量、函數參數等。堆是由malloc()函數(C++語言爲new運算符)分配的內存塊,內存釋放由程序員手動控制,在C語言爲free函數完成(C++中爲delete)。棧和堆的主要區別有以下幾點:

(1)管理方式不同。

棧編譯器自動管理,無需程序員手工控制;而堆空間的申請釋放工作由程序員控制,容易產生內存泄漏。

(2)空間大小不同。

棧是向低地址擴展的數據結構,是一塊連續的內存區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩餘空間時,將提示溢出。因此,用戶能從棧獲得的空間較小。

堆是向高地址擴展的數據結構,是不連續的內存區域。因爲系統是用鏈表來存儲空閒內存地址的,且鏈表的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。棧中元素都是一一對應的,不會存在一個內存塊從棧中間彈出的情況。

(3)是否產生碎片。

對於堆來講,頻繁的malloc/free(new/delete)勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低(雖然程序在退出後操作系統會對內存進行回收管理)。對於棧來講,則不會存在這個問題。

(4)增長方向不同。

堆的增長方向是向上的,即向着內存地址增加的方向;棧的增長方向是向下的,即向着內存地址減小的方向。

(5)分配方式不同。

堆都是程序中由malloc()函數動態申請分配並由free()函數釋放的;棧的分配和釋放是由編譯器完成的,棧的動態分配由alloca()函數完成,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實現。

(6)分配效率不同。

棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行。堆則是C函數庫提供的,它的機制很複雜,例如爲了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考數據結構/操作系統)在堆內存中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由於內存碎片太多),就有需要操作系統來重新整理內存空間,這樣就有機會分到足夠大小的內存,然後返回。顯然,堆的效率比棧要低得多。

3.1.3 Linux數據類型大小

在Linux操作系統下使用GCC進行編程,目前一般的處理器爲32位字寬,下面是/usr/include/limit.h文件對Linux下數據類型的限制及存儲字節大小的說明。

/* 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數據類型

char類型數據所佔內存空間爲8位。其中有符號字符型變量取值範圍爲?128~127,無符號型字符變量取值範圍爲0~255。其限制如下:

/* Number of bits in a 'char'. */
#  define CHAR_BIT 8          //所佔字節數
/* 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數據類型

short int類型數據所佔內存空間爲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數據類型

int類型數據所佔內存空間爲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數據類型

隨着宏__WORDSIZE值的改變,long int數據類型的大小也會發生改變。如果__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類型數據所佔內存空間爲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數據類型

在C99中,還定義了long long int數據類型。其數據類型限制如下:

#  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 */

3.1.4 數據存儲區域實例

此程序顯示了數據存儲區域實例,在此程序中,使用了etext、edata和end3個外部全局變量,這是與用戶進程相關的虛擬地址。

在程序源代碼中列出了各數據的存儲位置,同時在程序運行時顯示了各數據的運行位置,圖3-2所示爲程序運行過程中各變量的存儲位置。

圖3-2 函數運行時各數據位置

主函數源代碼如下:

[root@localhost linux_app]# cat mem_add.c
#include
#include
#include
#include
extern void afunc(void);
extern etext,edata,end;
int bss_var;                                //未初始化全局數據存儲在BSS區
int data_var=42;                            //初始化全局數據存儲在數據區
#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函數位置
SHW_ADR("afunc",afunc);           //查看代碼段afunc函數位置
printf("\nbss Location:\n");
SHW_ADR("bss_var",bss_var);      /查看BSS段變量位置
printf("\ndata location:\n");
SHW_ADR("data_var",data_var);     /查看數據段變量
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)));//堆結束位置
printf("\nb and nb in Stack\n");
SHW_ADR("b",b);       //顯示棧中數據b的位置
SHW_ADR("nb",nb);       //顯示棧中數據nb的位置
free(b);         //釋放申請的空間,以避免內存泄漏
free(nb);         //釋放申請的空間,以避免內存泄漏
}

子函數源代碼如下:

void afunc(void)
{
static int long level=0;          //靜態數據存儲在數據段中
int      stack_var;                 //局部變量,存儲在棧區
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();
}

函數運行結果如下:

[root@localhost linux_app]# gcc -o mem_add mem_add.c //編譯
[root@localhost linux_app]# ./mem_add     //運行結果
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

如果運行環境不一樣,運行程序的地址與此將有差異,但是,各區域之間的相對關係不會發生變化。可以通過readelf命令來查看可執行文件的詳細內容。

[root@localhost yangzongde]# readelf -a memadd

3.2 內存管理函數

3.2.1 malloc/free函數

Malloc()函數用來在堆中申請內存空間,free()函數釋放原先申請的內存空間。Malloc()函數是在內存的動態存儲區中分配一個長度爲size字節的連續空間。其參數是一個無符號整型數,返回一個指向所分配的連續存儲域的起始地址的指針。當函數未能成功分配存儲空間時(如內存不足)則返回一個NULL指針。

由於內存區域總是有限的,不能無限制地分配下去,而且程序應儘量節省資源,所以當分配的內存區域不用時,則要釋放它,以便其他的變量或程序使用。

這兩個函數的庫頭文件爲:

#include

函數定義如下:

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()函數返回值賦給p1,又把p1的值賦給p2,所以此時p1,p2都可作爲free函數的參數。使用free()函數時,需要特別注意下面幾點:

(1)調用free()釋放內存後,不能再去訪問被釋放的內存空間。內存被釋放後,很有可能該指針仍然指向該內存單元,但這塊內存已經不再屬於原來的應用程序,此時的指針爲懸掛指針(可以賦值爲NULL)。

(2)不能兩次釋放相同的指針。因爲釋放內存空間後,該空間就交給了內存分配子程序,再次釋放內存空間會導致錯誤。也不能用free來釋放非malloc()、calloc()和realloc()函數創建的指針空間,在編程時,也不要將指針進行自加操作,使其指向動態分配的內存空間中間的某個位置,然後直接釋放,這樣也有可能引起錯誤。

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

下面是使用這兩個函數的一個例子。

[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頭文件,從而在後面可以調用printf()函數。(2)句中包含stdlib.h頭文件,其是malloc()函數的頭文件。(3)句爲函數的入口位置,此處採用Linux下編程標準,返回值爲int型,argc爲參數個數, argv[]爲參數,envp[]存放的是所有環境變量。(4)句動態分配了10個整型存儲區域,此語句可以分爲以下幾步。

① 分配10個整型的連續存儲空間,並返回一個指向其起始地址的整型指針。

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

③ 檢測返回值是否爲NULL。

(5)、(6)句爲數組賦值並打印輸出,以免內存泄漏。(7)句調用free()函數釋放內存空間。(8)句將一個NULL指針傳遞給array,雖然在很多情況下可以不用此句,但這樣處理可以避免此指針成爲野指針。

在C++中,使用new和delete運算符來實現內存的分配和釋放,使用new/delete運算符實現內存管理比使用malloc/free函數更有優越性。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;           //申請動態內存並且初始化
//…
delete a;                   //清除並且釋放內存
}

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

class A
{
public:
A()  {   cout<<"A is here!"<<endl;   }  ="" 構造函數
~A() {   cout<<"A is dead!"<<endl;   }  ="" 析構函數
private:
int i;
};
A* pA=new A;     //調用new運算符申請空間
delete pA;      //刪除pA

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

(1)調用運算符new,在自由存儲區分配一個sizeof(A)大小的內存空間。

(2)調用構造函數A(),在這塊內存空間上初始化對象。

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

(1)調用析構函數~A(),銷燬對象。

(2)調用運算符delete,釋放內存。

由此可以看出,運算符new和delete提供了動態分配和釋放存儲區的功能。它們的作用相當於C語言的malloc()和free()函數,但是性能更爲優越。使用new比使用malloc()有以下幾個優點:

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

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

(3)用構造函數給分配的對象進行初始化。

但是,使用malloc函數和new分配內存的時候,本身並沒有對這塊內存空間做清零等任何動作。因此,申請內存空間後,其返回的新分配的內存是沒有零填充的,程序員需要使用memset()函數來初始化內存。

3.2.2 realloc--更改已經配置的內存空間

realloc()函數用來從堆上分配內存,當需要擴大一塊內存空間時,realloc()試圖直接從堆上當前內存段後面的字節中獲得更多的內存空間,如果能夠滿足,則返回原指針;如果當前內存段後面的空閒字節不夠,那麼就使用堆上第一個能夠滿足這一要求的內存塊,將目前的數據複製到新的位置,而將原來的數據塊釋放掉。如果內存不足,重新申請空間失敗,則返回NULL。此函數定義如下:

void *realloc(void *ptr,size_t size)

參數ptr爲先前由malloc、calloc和realloc所返回的內存指針,而參數size爲新配置的內存大小。其庫頭文件爲:

#include<stdlib.h>

當調用realloc()函數重新分配內存時,如果申請失敗,將返回NULL,此時原來指針仍然有效,因此在程序編寫時需要進行判斷,如果調用成功,realloc()函數會重新分配一塊新內存,並將原來的數據拷貝到新位置,返回新內存的指針,而釋放掉原來指針(realloc()函數的參數指針)指向的空間,原來的指針變爲不可用(即不需要再釋放,也不能再釋放),因此,一般不使用以下語句:

ptr=realloc(ptr,new_amount)

如果內存減少,malloc僅僅改變索引信息,但並不代表被減少的部分還可以訪問,這一部分內存將交給系統內存分配子程序。

下面是一個使用relloc函數的實例。

[root@localhost yangzongde]# cat realloc_example.c 
#include <stdio.h>
#include <stdlib.h>
int main (int argc,char* argv[],char* envp[])   //(1)主函數
{
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個數是從numbers2拷貝而來
{
printf("the numbers1s's data copy from numbers2: %d\n",*(numbers1+n));
}
     for(n=0;n<input;n++)       //(7)新數據初始化
{
*(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) //重新申請空間

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

此程序是一個簡單的重新申請內存空間的實例,(1)爲函數入口,前面已經介紹過。(2)從堆空間中申請5個int空間,將返回地址賦給numbers2,如果返回值爲NULL,將返回錯誤信息,釋放numbers2並退出。(3)爲新申請的空間初始化。(4)輸入需要增加的內存數量。(5)調用realloc()函數重新申請內存空間,重新申請內存空間大小爲原有空間大小加上用戶輸入的內存空間數。如果申請失敗,將返回NULL,此時numbers2仍然有效。如果申請成功,將重新分配一塊大小合適的空間,並將新空間首地址賦給numbers1,同時將numbers2所指向的5個空間的數據複製到新的內存空間中,釋放掉原來numbers2所指向的內存空間。(6)打印從numbers2所指向的原空間拷貝的數據,(7)句對新增加的空間進行初始化。(8)句釋放number1所指向的新申請空間。(9)爲註釋掉的代碼,提示讀者此時對原空間再次釋放,因爲第(5)已經完成了這一操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章