動態內存分配


動態分配內存的意義

定義數組的長度的時候,必須指定長度,這是在編譯期間就要確定的。

當我們無法在編譯期間確定到底需要多大的內存塊,此時就無法定義數組的大小:

  • 實際使用的元素數量超過了聲明的長度,程序無法處理。
  • 如果程序實際使用的元素數量較少,巨型數組會造成內存空間浪費。

此時就需要在運行的時候根據實際的情況(比如根據輸入的數據的大小),來動態的申請內存空間,然后讓指針指向這塊新申請的內存。

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、callocrealloc返回的值。
  • 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;
}

結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM