c語言深度剖析(38)—C語言中的動態內存分配

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用於重置內存大小
發佈了222 篇原創文章 · 獲贊 226 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章