C語言之動態內存管理
文章目錄
- C語言之動態內存管理
- 1. 為什么要有動態內存管理
- 2. malloc 和 free
- 2.1 malloc
- 2.2 free
- 2.3 例子
- 3. calloc 和 realloc
- 3.1 calloc
- 3.2 realloc
- 4. 常見的動態內存錯誤
- 4.1 對NULL指針的解引?操作
- 4.2 對動態開辟空間的越界訪問
- 4.3 對?動態開辟內存使?free釋放
- 4.4 使?free釋放?塊動態開辟內存的?部分
- 4.5 對同?塊動態內存多次釋放
- 4.6 動態開辟內存忘記釋放(內存泄漏)
- 5. 總結
1. 為什么要有動態內存管理
我們已經掌握的內存開辟?式有:
#include <stdio.h>int main()
{int val = 20;int arr[10] = { 0 };return 0;
}
上述的開辟空間的?式有兩個特點:
? 空間開辟大小是固定的
?數組在申明的時候,必須指定數組的?度,數組空間?旦確定了??不能調整
所以C語?引?了動態內存開辟,讓程序員??可以申請和釋放空間
2. malloc 和 free
malloc和free函數都是在stdlib.h頭文件中聲明的
2.1 malloc
C語言中提供了一個動態內存開辟的函數:
void* malloc (size_t size);
其中size為要開辟的內存空間的大小,單位為字節
這個函數向內存申請?塊連續可?的空間,并返回指向這塊空間的指針
?如果開辟成功,則返回一個指向開辟好的內存空間的指針
?如果開辟失敗,則返回一個NULL指針
?返回類型為void *,因為malloc函數不知道要開辟什么類型的內存空間,只知道要開辟的大小
?如果參數為0,malloc函數的行為標準是未定義的,取決于編譯器
2.2 free
C語言還提供了一個的函數,專門用來做動態內存的釋放和回收的:
void free (void* ptr);
ptr為要釋放內存空間的指針
?如果參數ptr指向的內存空間不是動態開辟的,那么free函數的行為是未定義的
?如果參數ptr是NULL,則函數什么都不做
如果不對malloc calloc realloc 開辟的空間進行釋放,即使出了作用域也不會銷毀,有可能導致內存泄漏
釋放的方式
1. free
2. 直到程序結束,由操作系統釋放
2.3 例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(10 * sizeof(int)); //開辟40個字節的空間//判斷是否為NULL指針if (p == NULL){perror("malloc fail\n"); //perror為錯誤信息打印return 1;}//使用int i = 0;for (i = 0; i < 10; i++){*(p + i) = i;//*p = i; //如果使用這種方法,p指針向后走了,在下面打印時,就找不到首元素的地址了//p++;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p + i));}free(p); //釋放p = NULL; //將指針置NULL,如果不置NULL,下面解引用p時,p就是野指針return 0;
}
代碼運行結果:>
0 1 2 3 4 5 6 7 8 9
malloc開辟空間時,是不會給空間初始化的,如果直接打印,會打印出隨機值
3. calloc 和 realloc
3.1 calloc
C語?還提供了?個函數叫 calloc , calloc 函數也?來動態內存分配
void* calloc (size_t num, size_t size);
num為要開辟的元素個數
size為開辟元素的元素大小,單位為字節
?calloc為開辟num個大小為size元素的內存空間,并且將內存中每個字節初始化為0
?calloc的使用方法和malloc一樣,主要區別在于calloc會初始化元素
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)calloc(10 ,sizeof(int));//判斷是否為NULL指針if (p == NULL){perror("calloc fail\n");return 1;}int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}return 0;
}
代碼運行結果:>
0 0 0 0 0 0 0 0 0 0
3.2 realloc
C語言中有一個函數用來調整動態內存開辟后的大小
void* realloc (void* ptr, size_t size);
ptr為要調整的內存地址
size為調整后的內存大小
? realloc函數的出現讓動態內存管理更加靈活
? 當我們發現我們使用malloc calloc realloc申請的內存空間不夠時,我們可以使用realloc進行擴容
?返回值為調整之后的內存的起始位置(不一定是原內存地址)
?如果開辟失敗則返回一個NULL
?如果開辟成功則分以下兩個情況:
情況1:原有空間之后有?夠?的空間
情況2:原有空間之后沒有?夠?的空間
情況1:在原有內存后邊直接追加空間,原來的空間的數據不變
情況2:原有內存之后的空間不足以最加空間,那么realloc會在堆區中找到一塊足夠開辟新大小的空間,將舊空間中的數據拷貝到新空間,并且將舊空間釋放,同時返回新空間起始位置的地址
realloc的用法除了為開辟的內存進行擴容,也可以和malloc一樣
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//判斷是否為NULL指針if (p == NULL){perror("malloc fail\n");return 1;}int* tmp = (int*)realloc(p, 100*sizeof(int));if (tmp != NULL){p = tmp;}else{perror("relloc fail\n");return 1;}//使用//..........free(p);p = NULL;return 0;
}
這次的運行結果就是情況2(開辟100個字節的大小時,可能會出現),當后面的空間不夠時, realloc就會找一塊新的空間
這次只開辟了40個字節的空間,屬于情況1,后面的空間足夠時, realloc會直接在后面追加空間
4. 常見的動態內存錯誤
4.1 對NULL指針的解引?操作
#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(40);*p = 20; //如果malloc開辟空間失敗,p可能是NULL,此時p為野指針return 0;
}
在VS2022中,編譯器會進行提示,我們得對可能出現NULL的情況進行處理
在使用malloc calloc realloc開辟空間時,最好對返回值進行判斷,當不為NULL再使用
4.2 對動態開辟空間的越界訪問
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}int i = 0;for (i = 0; i <= 10; i++) //只有10個元素的空間,卻訪問了第11個元素,訪問越界了{*(p + i) = i;}free(p);p = NULL;return 0;
}
4.3 對?動態開辟內存使?free釋放
#include <stdio.h>
#include <stdlib.h>int main()
{int a = 10;int* p = &a;free(p);return 0;
}
當用free釋放了不是由malloc calloc realloc開辟的空間時,就會報錯
4.4 使?free釋放?塊動態開辟內存的?部分
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int a = 10;int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}//使用//......p++;free(p);p = NULL;return 0;
}
當用free釋放了開辟空間的一部分時,就會報錯
4.5 對同?塊動態內存多次釋放
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int a = 10;int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}//使用//......free(p);free(p);p = NULL;return 0;
}
對一塊動態開辟的內存進行多次free釋放
在上述代碼中如果free釋放NULL,則沒有問題,因為free的參數為NULL時,則什么都不做
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int a = 10;int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}//使用//......free(p);p = NULL;free(p);p = NULL;return 0;
}
4.6 動態開辟內存忘記釋放(內存泄漏)
#include <stdio.h>
#include <stdlib.h>void test()
{int* p = (int*)malloc(100);if (p != NULL){*p = 20;}
}
int main()
{test();while (1); //死循環,讓程序不結束return 0;
}
當動態開辟的內存不釋放時,就會一存在,在上述代碼中,調用了test函數,開辟了100個字節的空間,同時賦值,出函數時,p被銷毀了,但是開辟的空間并沒有被銷毀,沒人可以使用,也沒人可以釋放,就會導致內存泄漏
5. 總結
一丶
在使用malloc calloc realloc開辟的空間時,要對其進行判斷,當不為NULL的再進行使用
二丶
當不使用動態開辟的內存時,將其free釋放,同時將指針置NULL,防止可能出現的內存泄露和野指針
三丶
不對不是動態開辟的空間free,不連續對動態開辟的空間free,同時free動態開辟的空間時,要給開辟的起始地址,不能free部分空間