1.動態內存分配的意義
- C語言中的—切操作都是基於內存的
- 變量和數組都是內存的別名
- 內存分配由編譯器在編譯期間決定
- 定義數組的時候必須指定數組長度
- 數組長度是在編譯期就必須確定的
- 需求:程序運行的過程中,可能需要使用—些額外的內存空間。
2.malloc和free
- malloc和free用於執行動態內存分配和釋放
- malloc所分配的是—塊連續的內存(虛擬內存)
- malloc以字節爲單位,並且不帶任何的類型信息
- free用於將動態內存歸還系統
- void *malloc(size_t size);
- void free(void *pointer);
3.注意
- malloc和free是庫函數,而不是系統調用
- malloc實際分配的內存可能會比請求的多
- 不能依賴於不同平臺下的malloc行爲
- 當請求的動態內存無法滿足時malloc返回NULL
- 當free的參數爲NULL時,函數直接返回
4.思考
- malloc(0); 將返回什麼?
#include <stdio.h>
#include <malloc.h>
int main()
{
int *p=(int *)malloc(0);
printf("%p\n",p);
free(p);
return 0;
}
- 運行結果:
- 可以看到,返回了具體的地址。
- 我們所說的內存包括起始地址和長度。我們平時說內存的時候更多的是關注起始地址,而忽略了長度。
- 如果動態的綜合這兩部,我們使用malloc(0)返回了一個地址就不會奇怪了,因爲這塊內存的長度是0。這塊內存我們可能無法正常使用,因爲長度是0。
- 我們如果不停的malloc(0),會使系統的內存耗盡嗎?
- 答案是會的,因爲我們malloc的時候,得到的內存往往要比實際申請的大。
- 現在的內存申請一般都是四字節對齊的。
- 因此,malloc(0)也要使用free來釋放這片內存。
5.實例分析
- 內存泄漏檢測模塊
- mleak.h
#ifndef _MLEAK_H_
#define _MLEAK_H_
#include <malloc.h>
#define MALLOC(n) mallocEx(n, __FILE__, __LINE__)
#define FREE(p) freeEx(p)
void *mallocEx(size_t n, const char *file, const line);
void freeEx(void *p);
void PRINT_LEAK_INFO();
#endif
- mleak.c
#include "mleak.h"
#define SIZE 256
/* 動態內存申請參數結構體 */
typedef struct
{
void *pointer;
int size;
const char *file;
int line;
} MItem;
static MItem g_record[SIZE]; /* 記錄動態內存申請的操作 */
void *mallocEx(size_t n, const char *file, const line)
{
void *ret = malloc(n); /* 動態內存申請 */
if (ret != NULL)
{
int i = 0;
/* 遍歷全局數組,記錄此次操作 */
for (i = 0; i < SIZE; i++)
{
/* 查找位置 */
if (g_record[i].pointer == NULL)
{
g_record[i].pointer = ret;
g_record[i].size = n;
g_record[i].file = file;
g_record[i].line = line;
break;
}
}
}
return ret;
}
void freeEx(void *p)
{
if (p != NULL)
{
int i = 0;
/* 遍歷全局數組,釋放內存空間,並清除操作記錄 */
for (i = 0; i < SIZE; i++)
{
if (g_record[i].pointer == p)
{
g_record[i].pointer = NULL;
g_record[i].size = 0;
g_record[i].file = NULL;
g_record[i].line = 0;
free(p);
break;
}
}
}
}
void PRINT_LEAK_INFO()
{
int i = 0;
printf("Potential Memory Leak Info:\n");
/* 遍歷全局數組,打印未釋放的空間記錄 */
for (i = 0; i < SIZE; i++)
{
if (g_record[i].pointer != NULL)
{
printf("Address: %p, size:%d, Location: %s:%d\n", g_record[i].pointer, g_record[i].size, g_record[i].file, g_record[i].line);
}
}
}
- 主程序如下:
#include <stdio.h>
#include "mleak.h"
void f()
{
MALLOC(100);
}
int main()
{
int *p = (int*)MALLOC(3 * sizeof(int));
f();
p[0] = 1;
p[1] = 2;
p[2] = 3;
FREE(p);
PRINT_LEAK_INFO();
return 0;
}
- 運行結果如下:
- 我們將第19行釋放p的語句也註釋掉,再次運次:
6.calloc和realloc
- malloc的同胞兄弟
- void *calloc(size_t num, size_t size);
- void *realloc(void *pointer, size_t new_size);
- calloc的參數代表所返回內存的類型信息
- calloc會將返回的內存初始化爲0
- realloc用於修改—個原先已經分配的內存塊大小
- 在使用realloc之後應該使用其返回值
- 當pointer的第—個參數爲NULL時,等價於malloc
#include <stdio.h>
#include <malloc.h>
#define SIZE 5
int main()
{
int i = 0;
int *pI = (int*)malloc(SIZE * sizeof(int));
short *pS = (short*)calloc(SIZE, sizeof(short));
for (i = 0; i < SIZE; i++)
{
printf("pI[%d] = %d, pS[%d] = %d\n", i, pI[i], i, pS[i]);
}
printf("Before: pI = %p\n", pI);
pI = (int *)realloc(pI, 2 * SIZE * sizeof(int));
printf("After: pI = %p\n", pI);
for (i = 0; i < 10; i++)
{
printf("pI[%d] = %d\n", i, pI[i]);
}
free(pI);
free(pS);
return 0;
}
- 運行結果如下:
- realloc重置之後地址值會發生變化。
- 可以得到結論:calloc得到的內存是絕對會初始化的,malloc不會初始化。realloc會重置內存空間的大小,重置後擴大的內存部分可能是隨機值。
7.小結
- 動態內存分配是C語言中的強大功能
- 程序能夠在需要的時候有機會使用更多的內存
- malloc單純的從系統中申請固定字節大小的內存
- calloc能以類型大小爲單位申請內存並初始化爲0
- realloc用於重置內存大小