【C語言】動態內存管理全解析:malloc、calloc、realloc與free的正確使用

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語言還提供了一個函數叫calloccalloc 函數也用來動態內存分配。原型如下:

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。
就是會導致:

  1. 內存泄漏(原內存無法釋放)
  2. 無法訪問原有數據

正確怎么寫呢?
我們先將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語言專欄

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89581.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89581.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89581.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于Arduino智能家居環境監測系統—以光照強度檢測修改

2 相關技術與理論 2.1 Arduino 技術 Arduino 是一款廣受歡迎的開源電子原型平臺&#xff0c;由硬件和軟件組成&#xff0c;為開發者提供了便捷且低成本的解決方案&#xff0c;尤其適用于快速搭建交互式電子項目&#xff0c;在本智能家居環境監測系統中擔當核心角色。? 硬件方…

前端上傳 pdf 文件 ,前端自己解析出來 生成界面 然后支持編輯

要在前端解析 PDF 文件并生成可編輯界面&#xff0c;我們可以使用 PDF.js 庫來解析 PDF 內容&#xff0c;然后將其轉換為可編輯的 HTML 元素。 主要特點和工作原理如下&#xff1a; PDF 解析&#xff1a; 使用 Mozilla 的 PDF.js 庫解析 PDF 文件內容&#xff0c;提取文本信息。…

Linux“一切皆文件“設計哲學 與 Linux文件抽象層:struct file與file_operations的架構解析

在Linux系統中&#xff0c;“一切皆文件”&#xff08;Everything is a file&#xff09;是一個核心設計哲學&#xff0c;它抽象了系統資源的訪問方式&#xff0c;使得幾乎所有硬件設備、進程、網絡連接等都可以通過統一的文件接口&#xff08;如open()、read()、write()、clos…

藍橋杯零基礎到獲獎-第3章 C++ 變量和常量

藍橋杯零基礎到獲獎-第3章 C 變量和常量 文章目錄一、變量和常量1.變量的創建2.變量初始化3.變量的分類4.常量4.1 字?常量4.2 #define定義常量4.3 const 定義常量4.4 練習練習1&#xff1a;買票https://www.nowcoder.com/practice/0ad8f1c0d7b84c6d8c560298f91d5e66練習2&…

物理AI是什么技術?

當英偉達CEO黃仁勛在鏈博會上明確提出“物理AI將是AI的下一浪潮”時&#xff0c;這個看似陌生的概念瞬間引發了科技圈的廣泛關注。究竟什么是物理AI&#xff1f;它與我們熟悉的人工智能有何不同&#xff1f;又將如何重塑我們與物理世界的交互方式&#xff1f; 物理AI&#xff1…

GRIB數據處理相關指令

GRIB 數據格式簡介 GRIB(General Regularly distributed Information in Binary form)&#xff0c;是由世界氣象組織&#xff08;WMO&#xff09;設計和維護的一種用于存儲和傳輸網格數據的標準數據格式&#xff0c;它是一種自描述的二進制壓縮格式&#xff0c;通常具有擴展名…

微服務學習(六)之分布式事務

微服務學習&#xff08;六&#xff09;之分布式事務一、認識Seata二、部署TC服務1、準備數據庫表2、準備配置文件3、docker部署三、微服務集成seata1、引入依賴2、改造配置3、添加數據庫表4、測試四、XA模式1、兩階段提交2、seata的XA模型3、優缺點4、實現步驟五、AT模式1、Sea…

Go實現用戶登錄小程序

寫一個用戶登錄注冊的小程序 運行程序&#xff0c;給出提示1. 注冊輸入用戶名、密碼、年齡、性別 {"用戶名": "root", "passwd": "123456", "age": 18, "sex": "男"}注冊前要判斷是否存在此用戶2. 登錄…

鴻蒙藍牙通信

https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-bluetooth-low-energy 藍牙權限 module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.ACCESS_BLUETOOTH","reason": "…

Java:Map

文章目錄Map常用方法Map遍歷的三種方法先獲取Map集合的全部鍵&#xff0c;再通過遍歷來找值Entry對象forEach結合lambda表達式Map 案例分析需求我的代碼&#xff08;不好&#xff09;老師的代碼&#xff08;好&#xff09;好在哪里另外集合分為Collection和MapMap常用方法 代碼…

fastjson2 下劃線字段轉駝峰對象

在對接第三方或查詢數據庫時&#xff0c;返回的字段是下劃線分隔的&#xff0c;而在業務中需要轉成java對象&#xff0c;java對象的字段是駝峰的&#xff0c;使用fastjson2時&#xff0c;有兩種方法可以實現&#xff1a; 比如數據格式是&#xff1a; {"item_id": &q…

【硬件】藍牙音頻協議

1. 無線音頻傳輸的工作原理 在無線傳輸的過程中&#xff0c;音源設備首先將MP3、FLAC等音頻文件還原為PCM格式。通過藍牙音頻編碼轉為藍牙無線傳輸的文件&#xff0c;發送到音頻設備段。將藍牙無線傳輸的文件再次還原為PCM格式&#xff0c;之后轉為模擬信號并放大&#xff0c;通…

【宇樹科技:未來1-3年,機器人可流水線打螺絲】

在第三屆中國國際供應鏈促進博覽會上&#xff0c;宇樹科技工作人員表示&#xff0c;未來1到3年內&#xff0c;機器人產品有望從單一工業化產品&#xff0c;發展至復合化工業場景&#xff0c;如機器人搬完箱子后&#xff0c;換個 “手” 就能在流水線上打螺絲。在3到10年內&…

Spring AI 1.0版本 + 千問大模型之 文本記憶對話

上篇文章&#xff0c;主要是簡單講解了一下文本對話的功能。由于模型不具備上下文記憶功能&#xff0c;只能一問一答。因此我們需要實現記憶對話功能&#xff0c;這樣大模型回答信息才能夠更加準確。 1、pom依賴 項目構建就不詳細說了&#xff0c;大家可以參考上篇 文本對話 文…

測試學習之——Pytest Day2

一、Pytest配置框架Pytest的配置旨在改變其默認行為&#xff0c;以適應不同的測試需求和項目結構。理解其配置層級和常用參數&#xff0c;是高效使用Pytest的基礎。1. 配置的意義與層級配置的本質在于提供一種機制&#xff0c;允許用戶根據項目特點、團隊規范或特定測試場景&am…

Go-Redis × RediSearch 全流程實踐

1. 連接 Redis ctx : context.Background()rdb : redis.NewClient(&redis.Options{Addr: "localhost:6379",Password: "",DB: 0,Protocol: 2, // 推薦 RESP2// UnstableResp3: true, // 若要體驗 RESP3 Raw* })2. 準備示例數據 u…

深入理解指針(指針篇2)

在指針篇1我們已經了解了整型指針&#xff0c;當然還有很多其他類型的指針&#xff0c;像字符指針、數組指針、函數指針等&#xff0c;他們都有他們的特別之處&#xff0c;讓我們接著學習。1. 指針類型介紹和應用1.1 字符指針變量字符指針變量類型為char*&#xff0c;一般這樣使…

Python+Selenium自動化爬取攜程動態加載游記

1. 引言 在旅游行業數據分析、輿情監測或競品研究中&#xff0c;獲取攜程等平臺的游記數據具有重要價值。然而&#xff0c;攜程的游記頁面通常采用動態加載&#xff08;Ajax、JavaScript渲染&#xff09;&#xff0c;傳統的**<font style"color:rgb(64, 64, 64);backg…

ESP8266服務器建立TCP連接失敗AT+CIPSTART=“TCP“,“192.168.124.1“,8080 ERROR CLOSED

1.檢查服務器端口8081是否開啟監聽2.檢查路由項是否被防火墻攔截方法 1&#xff1a;使用 netsh查看防火墻規則?netsh advfirewall firewall show rule nameall dirout | findstr "8081"如果無輸出&#xff0c;說明防火墻未針對該端口設置規則&#xff08;可能默認攔…

Linux 內存管理(2):了解內存回收機制

目錄一、透明大頁1.1 原理1.2 透明大頁的三大優勢1.3 透明大頁控制接口詳解1.4 使用場景與最佳實踐1.5 問題排查與監控1.6 與傳統大頁的對比二、Linux伙伴系統水位機制詳解2.1 三種核心水位詳解2.2 水位在伙伴系統中的實現2.3 水位觸發機制的實際行為2.4 水位關鍵操作接口2.5 水…