C語言學習
動態內存分配
友情鏈接:C語言專欄
文章目錄
- C語言學習
- 前言:
- 一、為什么要有動態內存分配
- 二、malloc和free
- 2.1 malloc
- 2.2 free
- 三、calloc和realloc
- 3.1 calloc
- 3.2 realloc
- 總結
- 附錄
- 上文鏈接
- 下文鏈接
- 專欄
前言:
在C語言編程中,內存管理是開發者必須掌握的核心技能之一。靜態內存分配雖然簡單易用,但在實際開發中往往無法滿足靈活多變的內存需求。動態內存分配技術為程序提供了運行時按需分配內存的能力,極大地增強了程序的靈活性和資源利用率。本文將深入講解C語言中動態內存分配的四大關鍵函數:malloc、calloc、realloc和free,通過原理分析、代碼示例和常見問題解析,幫助讀者全面掌握動態內存管理的精髓,避免內存泄漏和野指針等常見問題。
一、為什么要有動態內存分配
咱們先來看一下咱們已經掌握的內存開辟方式有:
int val = 10;//在棧空間上開辟四個字節char arr[10] = { 0 };//在棧空間上開辟10個字節的連續空間
但是上述的開辟空間方式有兩個特點:
空間開辟大小是固定的。
數組在申明的時候,必須指定數組的長度,數組空間?旦確定了大小不能調整
但是我們在寫代碼的時候對于空間的需求,不僅僅是上述的情況。有時候我們需要的空間大小在程序運行的時候才能知道,那數組的編譯時開辟空間的方式就不能滿足了。
C語言 引入了動態內存開辟,讓程序員自己可以申請和釋放空間,就比較靈活了。
二、malloc和free
2.1 malloc
C語言提供了?個動態內存開辟的函數:
void* malloc (size_t size);
這個函數向內存申請?塊連續可用的空間,并返回指向這塊空間的指針。
- 如果開辟成功,則返回?個指向開辟好空間的指針。
- 如果開辟失敗,則返回?個 NULL 指針,因此malloc的返回值?定要做檢查。
- 返回值的類型是 void* ,所以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自己來決定。
- 如果參數 size 為0,malloc的行為是標準是未定義的,取決于編譯器。
2.2 free
C語言提供了另外一個函數free
,專門是用來做動態內存的釋放和回收的(動態申請的內存如果不再使用,必須顯式地釋放并歸還給操作系統),函數原型如下:
void free (void* ptr);
free函數用來釋放動態開辟的內存。
- 如果參數 ptr 指向的空間不是動態開辟的,那free函數的行為是未定義的。
- 如果參數 ptr 是NULL指針,則函數什么事都不做。
malloc和free都聲明在 stdlib.h 頭文件中。
代碼示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{int* ptr = NULL;//開辟ptr = (int*)malloc(1000000000000);//動態開辟20個字節的內存if(ptr == NULL)//判斷ptr指針是否為空(是否開辟成功){perror("malloc:");}//使用else{int i = 0;for (i = 0; i < 5; i++){*(ptr + i) = 0;}}//釋放free(ptr);//釋放ptr所指向的動態內存,傳入的指針必須是要釋放的內存空間的起始地址。//注意:free只是將這一塊內存還給操作系統了,而對于ptr指針未作任何改變//此時,ptr就是野指針ptr = NULL;//即將野指針置空return 0;
}
三、calloc和realloc
3.1 calloc
C語言還提供了一個函數叫calloc
, calloc
函數也用來動態內存分配。原型如下:
void* calloc (size_t num, size_t size);
參數解釋:
為 num 個大小為 size 的元素開辟?塊空間,并且把空間的每個字節初始化為0。
與函數 malloc 的區別:
只在于 calloc 會在返回地址之前把申請的空間的每個字節初始化為全0
舉個例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));//開辟10個空間大小為int的內存空間if (NULL != p)//判斷p指針是否為空(是否開辟成功){int i = 0;for (i = 0; i < 10; i++){//咱們并未主動賦值//直接打印printf("%d ", *(p + i));}}free(p);p = NULL;return 0;
}
輸出:
所以如果我們對申請的內存空間的內容要求初始化,那么可以很?便的使?calloc函數來完成任務。
3.2 realloc
realloc函數的出現讓動態內存管理更加靈活。
有時會我們發現過去申請的空間太小了,有時候我們又會覺得申請的空間過大了,那為了合理的時候內存,我們?定會對內存的大小做靈活的調整。那 realloc 函數就可以做到對動態開辟內存大小的調整。
函數原型如下:
void* realloc (void* ptr, size_t size);
參數解釋:
ptr 是要調整的內存地址
size 調整之后新大小
返回值為調整之后的內存起始位置。
那咱們就會有疑問,為什么返回值是調整之后的內存起始位置,起始位置不是一直沒變嗎,其實不是這樣的,
realloc在調整內存空間的是存在兩種情況:
- 情況1:原有空間之后有足夠夠大的空間
- 情況2:原有空間之后沒有足夠大的空間
圖示:
情況1:
當是情況1 的時候,要擴展內存就直接原有內存之后直接追加空間,原來空間的數據不發?變化。
情況2:
當是情況2 的時候,原有空間之后沒有大小夠多的空間時,擴展的方法是:在堆空間上另找?個合適大小的連續空間來使用。這樣函數返回的是?個新的內存地址。
情況2圖示:
知道了上述的兩種情況,realloc函數的使用我們就要注意?些要點了。
看代碼:
#include <stdio.h>
#include <stdlib.h>
int main()
{int* ptr = (int*)malloc(100);if (ptr != NULL){//……}else{perror("malloc");}//擴展容量為1000個字節//完善代碼//……return 0;
}
那咱們可以這樣寫嗎:
ptr = (int*)realloc(ptr, 1000);
不行,堅決不允許。
解釋:
直接將realloc的返回值放到ptr中的話,如果
realloc
函數申請失敗則會返回NULL,此時會使得ptr為NULL。
就是會導致:
- 內存泄漏(原內存無法釋放)
- 無法訪問原有數據
正確怎么寫呢?
我們先將realloc函數的返回值放在p中,當p不為NULL,再放ptr中:
int* p = NULL;p = (int*)realloc(ptr, 1000);if (p != NULL){ptr = p;}
這樣是沒有問題的。
那怎么把上面代碼總結一下,完整的如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{int* ptr = (int*)malloc(100);if (ptr != NULL){//使用……}else{perror("malloc");return 1; // 直接退出,避免后續操作}//擴展容量//先將realloc函數的返回值放在p中,不為NULL,在放ptr中int* p = NULL;p = (int*)realloc(ptr, 1000);if (p != NULL){ptr = p;}//使用……//釋放free(ptr);ptr = NULL;// 防止野指針(良好習慣)return 0;
}
所以,咱們要知道:
malloc、calloc 和 realloc 在內存分配失敗時都會返回 NULL。
總結
良好的內存管理習慣不僅能避免程序崩潰和內存泄漏,還能提高代碼的健壯性和可維護性。記住:動態分配的內存就像借來的書,用完后一定要記得歸還(釋放)!
對于這一部分內容,不難,但是容易出錯,后續我會關于易錯點等等再出一篇文章,幫助大家理解,謝謝觀看至此!!!
附錄
上文鏈接
《聯合體完全指南:內存共享、大小計算與實戰應用》
下文鏈接
《動態內存分配避坑指南:六大易錯點解析與經典筆試題實戰》
專欄
C語言專欄