1. 程式人生 > >C語言動態內存的申請和釋放

C語言動態內存的申請和釋放

== 否則 med 編程 nbsp 配對 強行 越界 初始化

什麽是動態內存的申請和釋放?

當程序運行到需要一個動態分配的變量時,必須向系統申請取得堆中的一塊所需大小的存儲空間,用於存儲該變量。當不再使用該變量時,也就是它的生命結束時,要顯式釋放它所占用的存儲空間,這樣系統就能對該堆空間進行再次分配,做到重復使用有限的資源。

下面將介紹動態內存申請和釋放的函數

1.malloc函數

在C語言中,使用malloc函數來申請內存。函數原型如下:

#include<stdlib.h>

void *malloc(size_t size);

參數size代表需要動態申請的內存的字節數,若內存申請成功,函數返回申請到的內存的起始地址,若申請失敗,返回NULL,

在使用該函數時應註意以下幾點

1.只關心申請內存的大小,該函數的參數很簡單,只有申請內存的大小,單位是字節

2.申請的是一塊連續的內存,該函數一定是申請一塊連續的區間,可能申請到內存比實際申請的大,但也有可能申請不到,若申請失敗,則返回NULL

3.返回值類型是void*,函數的返回值是void*,不是某種具體類型的指針,可以理解成該函數只是申請內存,對在內存中存儲什麽類型的數據,沒有要求,因此,返回值是void*,實際編程中,根據實際情況將void*轉換成需要的指針類型

4.顯示初始化,註意:堆區是不會自動在分配時做初始化的(包括清),所以程序中需要顯示的初始化

2.free 函數

在堆區上分配的內存,需要用free函數顯示釋放。函數原型如下:

#include <stdlib.h>

void free(void *ptr);

函數的參數ptr,指的是需要釋放的內存的起始地址。該函數沒有返回值。使用該函數,也有下面幾點需要註意:

(1)必須提供內存的起始地址。調用該函數時,必須提供內存的起始地址,不能提供部分地址,釋放內存中的一部分是不允許的。因此,必須保存好malloc返回的指針值,若丟失,則所分配的堆空間無法回收,稱內存泄漏。

(2)malloc和free配對使用。編譯器不負責動態內存的釋放,需要程序員顯示釋放。因此,malloc

free是配對使用的,避免內存泄漏。

示例程序如下:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int *get_memory(int n)

{

int *p, i;

if ((p = (int *)malloc(n * sizeof(int))) == NULL)

{

printf("malloc error\n");

return p;

}

memset(p, 0, n * sizeof(int));

for (i = 0; i < n; i++)

p[i] = i+1;

return p;

}

int main()

{

int n, *p, i;

printf("input n:");

scanf("%d", &n);

if ((p = get_memory(n)) == NULL){

return 0;

}

for (i = 0; i < n; i++){

printf("%d ", p[i]);

}

printf("\n");

free(p);

p = NULL;

return 0;

}

程序執行結果如下:

[email protected]:~/book/ch10$ cc malloc.c -Wall

[email protected]:~/book/ch10$./a.out

input n:10

1 2 3 4 5 6 7 8 9 10

該程序演示了動態內存的標準用法。動態內存的申請,通過一個指針函數來完成。內存申請時,判斷是否申請成功,成功後,對內存初始化。在主調函數中,動態內存依然可以訪問,不再訪問內存時,用free函數釋放。

(3)不允許重復釋放。同一空間的重復釋放也是危險的,因為該空間可能已另分配。在上面程序中,如果釋放堆空間兩次(連續調用兩次free(p)),會出現下面的結果。

[email protected]:~/book/ch10$ cc malloc.c –Wall

[email protected]:~/book/ch10$./a.out

input n:1

1

*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x08f1a008 ***

======= Backtrace: =========

/lib/libc.so.6(+0x6c501)[0x687501]

/lib/libc.so.6(+0x6dd70)[0x688d70]

/lib/libc.so.6(cfree+0x6d)[0x68be5d]

./a.out[0x804861e]

/lib/libc.so.6(__libc_start_main+0xe7)[0x631ce7]

./a.out[0x8048471]

======= Memory map: ========

0061b000-00772000 r-xp 00000000 08:01 1048623 /lib/libc-2.12.1.so

00772000-00773000 ---p 00157000 08:01 1048623 /lib/libc-2.12.1.so

00773000-00775000 r--p 00157000 08:01 1048623 /lib/libc-2.12.1.so

00775000-00776000 rw-p 00159000 08:01 1048623 /lib/libc-2.12.1.so

00776000-00779000 rw-p 00000000 00:00 0

008e1000-008fb000 r-xp 00000000 08:01 1048657 /lib/libgcc_s.so.1

008fb000-008fc000 r--p 00019000 08:01 1048657 /lib/libgcc_s.so.1

008fc000-008fd000 rw-p 0001a000 08:01 1048657 /lib/libgcc_s.so.1

00a8f000-00aab000 r-xp 00000000 08:01 1048599 /lib/ld-2.12.1.so

00aab000-00aac000 r--p 0001b000 08:01 1048599 /lib/ld-2.12.1.so

00aac000-00aad000 rw-p 0001c000 08:01 1048599 /lib/ld-2.12.1.so

00b6c000-00b6d000 r-xp 00000000 00:00 0 [vdso]

08048000-08049000 r-xp 00000000 08:01 1079938 /home/linux/book/ch10/a.out

08049000-0804a000 r--p 00000000 08:01 1079938 /home/linux/book/ch10/a.out

0804a000-0804b000 rw-p 00001000 08:01 1079938 /home/linux/book/ch10/a.out

08f1a000-08f3b000 rw-p 00000000 00:00 0 [heap]

b7700000-b7721000 rw-p 00000000 00:00 0

b7721000-b7800000 ---p 00000000 00:00 0

b7815000-b7816000 rw-p 00000000 00:00 0

b7823000-b7827000 rw-p 00000000 00:00 0

bf9a5000-bf9c6000 rw-p 00000000 00:00 0 [stack]

Aborted

(4)free只能釋放堆空間。像代碼區、全局變量與靜態變量區、棧區上的變量,都不需要程序員顯示釋放,這些區域上的空間,不能通過free函數來釋放,否則執行時,會出錯。

示例程序如下:

#include <stdlib.h>

int main()

{

int a[10] = {0};

free(a);

return 0;

}

程序執行結果如下:

[email protected]:~/book/ch10$ cc free.c –o free -Wall

free.c: In function ‘main‘:

free.c:7: warning: attempt to free a non-heap object ‘a‘

可以看到有一個警告,即釋放一個非堆上的空間。如果強行執行程序,會出現下面的結果:

[email protected]:~/book/ch10$./a.out

Segmentation fault

3.野指針

野指針指的是指向“垃圾”內存的指針,不是NULL指針。出現“野指針主要有以下原因:

1)指針變量沒有被初始化。指針變量和其它的變量一樣,若沒有初始化,值是不確定的。也就是說,沒有初始化的指針,指向的是垃圾內存,非常危險。

示例程序如下:

#include <stdio.h>

int main()

{

int *p;

printf("%d\n", *p);

*p = 10;

printf("%d\n", *p);

return 0;

}

程序執行結果如下:

[email protected]:~/book/ch10$ cc p.c –o p -Wall

[email protected]:~/book/ch10$./p

1416572

Segmentation fault

(2)指針pfree之後,沒有置為NULLfree函數是把指針所指向的內存釋放掉,使內存成為了自由內存。但是,該函數並沒有把指針本身的內容清楚。指針仍指向已經釋放的動態內存,這是很危險。程序員稍有疏忽,會誤以為是個合法的指針。就有可能再通過指針去訪問動態內存。實際上,這時的內存已經是垃圾內存了,關於野指針會造成什麽樣的後果,這是很難估計的。若內存仍然是空閑的,可能程序暫時正常運行;若內存被再次分配,又通過野指針對內存進行了寫操作,則原有的合法數據,會被覆蓋,這時,野指針造成的影響將是無法估計的。

示例程序如下:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main()

{

int n = 5, *p, i;

if ((p = (int *)malloc(n * sizeof(int))) == NULL)

{

printf("malloc error\n");

return 0;

}

memset(p, 0, n * sizeof(int));

for (i = 0; i < n; i++)

{

p[i] = i+1;

printf("%d ", p[i]);

}

printf("\n");

printf("p=%p *p=%d\n", p, *p);

free(p);

printf("after free:p=%p *p=%d\n", p, *p);

*p = 100;

printf("p=%p *p=%d\n", p, *p);

return 0;

}

程序執行結果如下:

[email protected]:~/book/ch10$cc test.c –o test -Wall

[email protected]:~/book/ch10$./test

1 2 3 4 5

p=0x92cf008 *p=1

after free:p=0x92cf008 *p=0

p=0x92cf008 *p=100

該程序中,故意在執行了“free(p)”之後,通過野指針p對動態內存進行了讀寫,程序正常執行,也在預料之中。前面已經分析過,內存釋放後,若繼續訪問甚至修改,後果是不可預料的。

3)指針操作超越了變量的作用範圍。指針操作時,由於邏輯上的錯誤,導致指針訪問了非法內存,這種情況讓人防不勝防,只能依靠程序員好的編碼風格,已及紮實的基本功。下面演示一個指針操作越界的情況:

示例程序如下:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main()

{

int a[5] = {1, 9, 6, 2, 10}, *p, i, n;

n = sizeof(a) / sizeof(n);

p = a;

for (i = 0; i <= n; i++)

{

printf("%d ", *p);

p++;

}

printf("\n");

*p = 100;

printf("*p=%d\n", *p);

return 0;

}

程序執行結果如下:

[email protected]:~/book/ch10$ cc test.c –o test -Wall

[email protected]:~/book/ch10$./test

1 9 6 2 10 5

*p=100

該程序故意出了兩個錯誤,一是for循環的條件“i <= n”,p指針指向了數組以外的空間。二是“*p = 100”,對非法內存進行了寫操作。

4不要返回指向棧內存的指針。指針函數會返回一個指針。在主調函數中,往往會通過返回的指針,繼續訪問指向的內存。因此,指針函數不能返回棧內存的起始地址,因為棧內存在函數結束時會被釋放。

C語言動態內存的申請和釋放