動態分配內存的意義
定義數組的長度的時候,必須指定長度,這是在編譯期間就要確定的。
當我們無法在編譯期間確定到底需要多大的內存塊,此時就無法定義數組的大小:
- 實際使用的元素數量超過了聲明的長度,程序無法處理。
- 如果程序實際使用的元素數量較少,巨型數組會造成內存空間浪費。
此時就需要在運行的時候根據實際的情況(比如根據輸入的數據的大小),來動態的申請內存空間,然后讓指針指向這塊新申請的內存。
malloc和free
malloc和free是庫函數,不是系統調用
#include "stdlib.h"
void * malloc(size_t size);
void free(void* pointer);
關於malloc
- malloc所分配的是一塊連續的內存,參數size是所分配的內存字節數。
- malloc的返回值是void* ,具體使用的時候需要做強制類型轉換。
- 當請求的動態內存無法滿足的時候,malloc返回NULL,對每個從malloc返回指針都進行檢查,確保它不是NULLL。
- malloc申請的動態內存中的數據是隨機值,不會被初始化為0。
- malloc 實際分配的內存可能有會比請求的多。
關於free
- free的參數要么是NULL,要么是從malloc、
calloc
、realloc
返回的值。 - free用於將申請的動態內存歸還給系統。
- 當 free 的參數為 NULL 時,函數直接返回。
calloc與realloc
#include "stdlib.h"
void* `calloc`(size_t num,size_t size);
void* `realloc`(void* pointer,size_t new_size);
關於calloc
calloc
函數的參數包含了所需元素的數量以及每個元素的字節數,根據這些值可以計算出一共所需的內存大小。calloc
會將申請的內存空間初始化為0。- 如果指向把值存儲到數組中,這種操作會浪費一定的時間。
關於realloc
realloc
用於修改原先已經分配的內存塊大小。- 當
realloc
的第一個參數pointer為NULL時,realloc
相當於malloc。 realloc
用於縮小一個內存塊時,該內存塊的尾部的部分將會被拿掉。realloc
用於擴大內存塊時,原有內容依然保留,新增加的內存塊添加到原先內存塊的后面,新內存並未以任何方式初始化。- 如果原先的內存塊無法改變大小,
realloc
將會分配一個正確大小的內存,並把原有的內容拷貝到新的內存塊上,因此realloc
之后就不能使用指向舊內存的指針,而應該使用realloc
返回的指針。
常見的動態內存分配錯誤
對NULL指針解引用操作
int main(void)
{
int *p = (int *)malloc(sizeof(int));
*p = 20; //這里沒有判斷返回值
//如果返回值為NULL則會出現錯誤
free(p);
system("pause");
return 0;
}
對動態開辟的空間越界訪問
int main(void)
{
int *p = (int *)malloc(10 * sizeof(int));
if (p == NULL)
{
exit(EXIT_FAILURE);
}
int i = 0;
for (i = 0; i <= 10; i++) // 訪問越界
{
p[i] = i;
}
free(p);
p = NULL;
system("pause");
return 0;
}
對非動態開辟的內存使用free釋放
int main(void)
{
int a = 10;
int *p = &a;
free(p); // p不是動態申請的內存
system("pause");
return 0;
}
使用free釋放一塊動態開辟內存的一部分
int main(void)
{
int *p = (int *)malloc(10 * sizeof(int));
if (p == NULL)
{
exit(EXIT_FAILURE);
}
p++;
free(p); // 釋放部分動態申請的內存
system("pause");
return 0;
}
對同一塊內存多次釋放
int main(void)
{
int *p = (int *)malloc(sizeof(int));
if (p == NULL)
{
exit(EXIT_FAILURE);
}
free(p);
free(p); // 第二次釋放
system("pause");
return 0;
}
動態開辟的內存忘記釋放(內存泄漏)
int main(void)
{
int *p = (int *)malloc(sizeof(int) * 10);
if (p == NULL)
{
exit(EXIT_FAILURE);
}
system("pause");
return 0;
}
編寫內存泄漏檢查模塊
頭文件 mleak.h
#pragma once
#include "stdio.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 int line);
void freeEx(void *p);
void PRINT_LEAK_INFO();
實現 mleak.c
#include "mleak.h"
#define MAXSIZE 256
typedef struct
{
void * pointer;
int size;
const char* file;
int line;
}MallocItem;
static MallocItem g_record[MAXSIZE];
void *mallocEx(size_t n, const char *file, const int line)
{
void * p = malloc(n);
int i = 0;
if (p != NULL)
{
for (i = 0; i < MAXSIZE; i++)
{
if (g_record[i].pointer == NULL)
{
g_record[i].pointer = p;
g_record[i].size = n;
g_record[i].file = file;
g_record[i].line = line;
break;
}
}
}
return p;
}
void freeEx(void *p)
{
if (p != NULL)
{
int i = 0;
for (i = 0; i < MAXSIZE; 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;
break;
}
}
}
}
void PRINT_LEAK_INFO()
{ int i = 0;
printf("Potenital Memory Leak Info:\n");
for (i=0; i < MAXSIZE; 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 "stdlib.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;
}
結果: