C 語言進【進階篇】之動態內存管理:從底層機制到實戰優化

目錄

  • 🚀前言
  • 🌟動態內存分配的必要性
  • 🤔動態內存分配函數深度剖析
    • 💯malloc函數:內存申請的主力軍
    • 💯free函數:釋放內存的“清道夫”
    • 💯calloc函數:初始化內存的利器
    • 💯realloc函數:靈活調整內存大小的“魔術師”
  • 🐍常見的動態內存錯誤及避免方法
  • ??柔性數組:獨特而強大的內存管理工具
  • ??C/C++中程序內存區域劃分:深入理解內存布局
  • 🧑?🎓總結:掌握動態內存管理,提升編程能力

🚀前言

大家好!我是 EnigmaCoder。本文收錄于我的專欄 C,感謝您的支持!

  • 在C語言編程的廣袤天地中,內存管理堪稱核心支柱之一,它對程序的性能、穩定性起著決定性作用。熟練掌握動態內存管理技巧,是從編程新手邁向高手的必經之路。今天,就讓我們一同深入探尋C語言動態內存管理的奧秘。

🌟動態內存分配的必要性

在C語言里,常規的內存開辟方式有其局限性。像定義普通變量int val = 20;,它會在棧空間占用4個字節;定義數組char arr[10] = {0};,則在棧空間開辟10個字節的連續區域。這種方式的弊端在于空間大小固定,數組一經聲明長度就無法更改。但實際編程時,很多場景下所需的內存空間大小要在程序運行階段才能確定。比如開發一個學生成績管理系統,在錄入成績前,根本不知道會有多少學生,這時候常規的內存開辟方式就難以滿足需求,動態內存分配則能有效解決這類問題,讓程序根據實際情況靈活申請和釋放內存。

🤔動態內存分配函數深度剖析

💯malloc函數:內存申請的主力軍

malloc函數用于向系統申請一塊連續的可用內存空間,其函數原型為void* malloc (size_t size); 。若申請成功,它會返回一個指向該內存空間的指針;若失敗,就返回NULL指針。由于返回值是void*類型,在使用時需進行強制類型轉換,明確所指向的數據類型。特別要注意,當參數size為0時,malloc的行為在標準中未定義,不同編譯器的處理方式可能不同。

#include <stdio.h>
#include <stdlib.h>int main() {int num;printf("請輸入要開辟的整數個數: ");scanf("%d", &num);int* ptr = (int*)malloc(num * sizeof(int));if (ptr == NULL) {printf("內存分配失敗,原因可能是系統內存不足或其他錯誤。\n");return 1;}for (int i = 0; i < num; i++) {ptr[i] = i;}for (int i = 0; i < num; i++) {printf("%d ", ptr[i]);}free(ptr);ptr = NULL;return 0;
}

在這段代碼中,先根據用戶輸入的整數個數num,使用malloc申請相應大小的內存空間。申請后,立即檢查返回的指針是否為NULL,若為NULL,則輸出錯誤信息并終止程序。之后對申請的內存進行賦值和遍歷輸出操作,最后使用free釋放內存,并將指針置為NULL,防止出現野指針。

💯free函數:釋放內存的“清道夫”

free函數專門用于釋放動態開辟的內存,其函數原型為void free (void* ptr); 。若ptr指向的不是動態開辟的內存,調用free函數會導致未定義行為;若ptrNULL指針,函數則不執行任何操作。在實際編程中,正確使用free函數釋放不再使用的內存,是避免內存泄漏的關鍵。

💯calloc函數:初始化內存的利器

calloc函數同樣用于動態內存分配,原型是void* calloc (size_t num, size_t size); 。它的獨特之處在于,會為num個大小為size的元素開辟一塊內存空間,并將每個字節初始化為0。這在需要初始化內存的場景中,如創建用于存儲數據的數組且初始值都為0時,使用calloc會非常方便。

#include <stdio.h>
#include <stdlib.h>int main() {int *p = (int*)calloc(5, sizeof(int));if (p == NULL) {printf("內存分配失敗,可能是內存不足。\n");return 1;}for (int i = 0; i < 5; i++) {printf("%d ", p[i]);}free(p);p = NULL;return 0;
}

此代碼通過calloc為5個int類型的元素申請內存空間,并自動將每個元素初始化為0,然后進行遍歷輸出,最后釋放內存。

💯realloc函數:靈活調整內存大小的“魔術師”

realloc函數為動態內存管理帶來了極大的靈活性,可用于調整已動態開辟內存的大小。其函數原型為void* realloc (void* ptr, size_t size);ptr是要調整的內存地址,size是調整后的新大小,返回值為調整后的內存起始位置。在調整內存大小時,存在兩種情況:如果原有空間之后有足夠大的空間,直接在原有內存后追加空間,原有數據保持不變;若原有空間之后空間不足,則會在堆空間中另找一塊合適大小的連續空間,此時函數返回新的內存地址,原有數據會被復制到新空間。

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = (int*)malloc(5 * sizeof(int));if (ptr == NULL) {printf("內存分配失敗,請檢查系統內存狀態。\n");return 1;}for (int i = 0; i < 5; i++) {ptr[i] = i;}int *new_ptr = (int*)realloc(ptr, 10 * sizeof(int));if (new_ptr == NULL) {printf("內存調整失敗,可能無法找到足夠大的連續內存空間。\n");free(ptr);return 1;}ptr = new_ptr;for (int i = 5; i < 10; i++) {ptr[i] = i;}for (int i = 0; i < 10; i++) {printf("%d ", ptr[i]);}free(ptr);return 0;
}

這段代碼先使用malloc申請了5個int類型的內存空間,然后嘗試使用realloc將其擴展為10個int類型的空間。在擴展過程中,仔細檢查realloc的返回值,確保內存調整成功。若調整失敗,釋放原有的內存空間并輸出錯誤信息。

🐍常見的動態內存錯誤及避免方法

  1. 對NULL指針的解引用操作:當malloc因內存不足等原因返回NULL時,如果直接對返回的指針進行解引用操作,如*p = 20;,程序會發生嚴重錯誤,甚至崩潰。為避免這種情況,在使用malloc返回的指針前,一定要檢查其是否為NULL
  2. 對動態開辟空間的越界訪問:在訪問動態分配的數組或內存塊時,如果超出了分配的范圍,就會發生越界訪問。這會導致程序出現未定義行為,可能當時不會報錯,但后續會引發各種難以排查的問題。編寫代碼時,務必嚴格控制訪問邊界。
  3. 對非動態開辟內存使用free釋放free函數只能用于釋放動態開辟的內存。若對非動態開辟的內存(如普通局部變量的地址)使用free,會導致程序出現不可預測的錯誤。在調用free前,要確保所釋放的內存是通過動態分配獲得的。
  4. 使用free釋放一塊動態開辟內存的一部分free函數必須釋放動態內存的起始地址,若釋放的是動態內存中間的某個位置,會破壞內存管理機制,導致程序出錯。
  5. 對同一塊動態內存多次釋放:重復釋放同一塊動態內存會使內存管理系統混亂,通常會導致程序崩潰。為避免這種情況,釋放內存后,應及時將指針置為NULL,防止再次誤釋放。
  6. 動態開辟內存忘記釋放(內存泄漏):當動態開辟的內存不再使用,但未調用free釋放時,就會發生內存泄漏。隨著程序的運行,內存泄漏會不斷積累,導致系統內存逐漸減少,最終影響系統性能甚至使程序崩潰。養成良好的編程習慣,及時釋放不再使用的動態內存至關重要。

??柔性數組:獨特而強大的內存管理工具

C99標準引入了柔性數組這一特殊概念,它允許結構體中的最后一個元素是未知大小的數組。柔性數組具有兩個重要特點:一是結構體中的柔性數組成員前面必須至少有一個其他成員;二是sizeof返回的結構體大小不包括柔性數組的內存。

#include <stdio.h>
#include <stdlib.h>typedef struct st_type {int i;int a[]; 
} type_a;int main() {type_a *p = (type_a*)malloc(sizeof(type_a) + 5 * sizeof(int));p->i = 100;for (int i = 0; i < 5; i++) {p->a[i] = i;}for (int i = 0; i < 5; i++) {printf("%d ", p->a[i]);}free(p);return 0;
}

在上述代碼中,先定義了包含柔性數組的結構體type_a,然后使用malloc為結構體及其柔性數組分配內存空間。分配時,要確保分配的內存大小大于結構體本身的大小,以滿足柔性數組的預期大小。之后對柔性數組進行賦值和遍歷輸出操作,最后釋放整個結構體的內存。

柔性數組的優勢明顯。一方面,方便內存釋放。當在函數中使用柔性數組并返回結構體指針時,用戶只需調用一次free,就能釋放結構體及其柔性數組所占用的全部內存,無需額外處理。另一方面,它有利于提高訪問速度,因為柔性數組的內存是連續分配的,連續的內存訪問效率更高,同時也能減少內存碎片的產生。

??C/C++中程序內存區域劃分:深入理解內存布局

C/C++程序的內存主要分為以下幾個區域:

  1. 棧區(stack):在函數執行過程中,函數內的局部變量、函數參數、返回數據和返回地址等都存放在棧區。棧內存的分配和釋放由系統自動管理,函數執行結束時,棧上的存儲單元會自動釋放。棧區的內存分配效率高,但容量有限。
  2. 堆區(heap):一般由程序員手動分配和釋放。若程序員未釋放,程序結束時可能由操作系統回收。堆區的分配方式類似于鏈表,適合用于動態內存分配,如malloccallocrealloc等函數分配的內存都來自堆區。
  3. 數據段(靜態區):用于存放全局變量和靜態數據。程序運行期間,這些數據一直存在,程序結束后由系統釋放。
  4. 代碼段:存放函數體(包括類成員函數和全局函數)的二進制代碼,是只讀的,用于存儲程序的可執行指令。

高地址
┌───────────────┐
│ 內核空間 │
├───────────────┤
│ 棧區(向下增長)│
├───────────────┤
│ 內存映射段 │
├───────────────┤
│ 堆區(向上增長)│
├───────────────┤
│ 數據段(靜態區)│
├───────────────┤
│ 代碼段 │
└───────────────┘
低地址

理解這些內存區域的劃分,有助于我們更好地規劃和管理程序內存,避免因內存使用不當導致的錯誤。

🧑?🎓總結:掌握動態內存管理,提升編程能力

  • C語言的動態內存管理是一個功能強大且復雜的領域。通過mallocfreecallocrealloc等函數,我們能夠靈活地申請、釋放和調整內存空間,滿足各種復雜的編程需求。然而,在使用過程中,必須時刻警惕常見的動態內存錯誤,如對NULL指針的解引用、越界訪問、內存泄漏等,養成良好的編程習慣,確保程序的穩定性和可靠性。
  • 柔性數組作為C語言的一個獨特特性,為我們提供了一種高效的內存管理方式,在合適的場景下使用可以顯著提升程序性能。同時,深入理解C/C++程序的內存區域劃分,能讓我們從宏觀層面把握內存的使用,優化程序的內存布局。

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

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

相關文章

2023華東師范大學計算機復試上機真題

2023華東師范大學計算機復試上機真題 2022華東師范大學計算機復試上機真題 2021華東師范大學計算機復試上機真題 2023華東師范大學計算機復試機試真題 2022華東師范大學計算機復試機試真題 2021華東師范大學計算機復試機試真題 在線評測&#xff1a;傳送門&#xff1a;pgcode.…

Mac下安裝Zed以及Zed對MCP(模型上下文協議)的支持

Zed是當前新流行的一種編輯器&#xff0c;支持MCP&#xff08;模型上下文協議&#xff09; Mac下安裝Zed比較簡單&#xff0c;直接有安裝包&#xff0c;在這里&#xff1a; brew install --cask zedMac Monterey下是可以安裝上的&#xff0c;親測有效。 配置 使用CtrlShiftP…

Spring Boot 約定大于配置:實現自定義配置

文章目錄 Spring Boot 約定大于配置&#xff1a;實現自定義配置引言1. Spring Boot 的約定大于配置2. 自定義配置的需求3. 實現自定義配置的步驟4. 示例&#xff1a;自定義 Spring MVC 配置4.1 創建自定義配置類4.2 創建自定義攔截器4.3 測試自定義配置 5. 其他自定義配置場景5…

交易系統優化方案

證券交易系統優化方案 一、選股策略體系 (一)擇時策略矩陣 尾盤集中篩選法(14:30后)聚焦量價異動個股,捕捉次日溢價機會早盤轉債套利法(9:25-10:00)通過可轉債與正股聯動性捕捉日內機會龍頭戰法(全時段)行業板塊強度排序,鎖定前三大市值龍頭容量資金戰法(中盤股適用…

在線Doc/Docx轉換為PDF格式 超快速轉換的一款辦公軟件 文檔快速轉換 在線轉換免費轉換辦公軟件

小白工具https://www.xiaobaitool.net/files/word-pdf/提供了一項非常實用的在線服務——將Doc或Docx格式的文檔快速轉換為PDF格式。這項服務不僅操作簡單&#xff0c;而且轉換效率高&#xff0c;非常適合需要頻繁處理文檔轉換的用戶。 服務特點&#xff1a; 批量轉換&#x…

java學習總結(四)MyBatis多表

一、多表結構 學生表、班級表、課程表、班級課程表 二、一對一 一個學生只屬于一個班級。 查詢&#xff1a;id name age gender banjiName SELECT s.id,s.name,s.age,s.gender,b.id AS banjiId,b.name AS banjiName FROM student AS s INNER JOIN banji AS b ON s.banji_id…

大語言模型學習及復習筆記(1)語言模型的發展歷程

1.大模型進入人們視野 ChatGPT 于2022年11月底上線 模型名稱 發布時間 核心突破 GPT-3 2020年6月 首款千億參數模型&#xff0c;少樣本學習 GPT-3.5-Turbo 2022年11月 對話能力優化&#xff0c;用戶級應用落地 GPT-4 2023年3月 多模態、強邏輯推理 GPT-4o / GPT-4…

【NLP】 3. Distributional Similarity in NLP(分布式相似性)

Distributional Similarity in NLP&#xff08;分布式相似性&#xff09; 分布式相似性&#xff08;Distributional Similarity&#xff09; 是自然語言處理&#xff08;NLP&#xff09;中的核心概念&#xff0c;基于“相似的單詞出現在相似的上下文中”這一假設。它用于衡量單…

【C#學習】協程等待

來源GPT&#xff0c;僅記錄學習 yield return WaitForEndOfFrame() 適用于 渲染結束后再執行代碼&#xff0c;但 WebGL 可能不適合這個操作&#xff0c;會拖慢幀率。(渲染得太慢&#xff09; yield return null; 讓代碼在下一幀的 Update() 里繼續運行&#xff0c;更加流暢。 …

【項目】負載均衡式在線OJ

負載均衡式在線OJ 目錄 負載均衡式在線OJ 1.項目介紹&#xff1a; 2.comm 2.1 log.hpp 日志等級 開放式日志 時間戳工具 2.2 util.hpp TimeUtil類 PathUtil類 FileUtil類 StringUtil類 3.Compile_server 3.1compile_run.hpp RemoveTempFile CodeToDesc Start 3.…

實現“XXX一張圖“進行環境設施設備可視化管理

實現“電網一張圖”、“鐵路一張圖”、“水庫一張圖”、“森林一張圖”等概念,本質上是將某一領域的空間數據、設施設備、運行狀態等信息整合到一個統一的數字化平臺上,實現全域可視化、智能化管理和協同運營。這種“一張圖”模式依賴于地理信息系統(GIS)、物聯網(IoT)、…

《基於Python的網絡爬蟲抓包技術研究與應用》

## 摘要 本文探討了基于Python的網絡爬蟲抓包技術及其應用。隨著互聯網數據的快速增長&#xff0c;網絡爬蟲技術在數據采集和分析中扮演著越來越重要的角色。本研究首先介紹了網絡爬蟲的基本概念和Python在爬蟲開發中的優勢&#xff0c;然后深入分析了抓包技術的原理和常用工具…

【藍橋杯速成】| 1.暴力解題

1高頻考點與暴力解題_嗶哩嗶哩_bilibili 感謝up主分享&#xff0c;以下內容是學習筆記&#xff0c;以c為主&#xff0c;部分python 題目一&#xff1a;維納的年齡 題目內容 美國數學家維納(N.Wiener)智力早熟&#xff0c; 11歲就上了大學。他曾在1935~1936年應邀來中國清華大…

[C++Qt] 槽函數收不到信號問題(信號的注冊)

&#x1f4e2;博客主頁&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01;&#x1f4e2;本文由 丶布布原創&#xff0c;首發于 CSDN&#xff0c;轉載注明出處&#x1f649;&#x1f4e2;現…

從零開始用AI開發游戲(一)

1. 核心玩法設計 核心目標&#xff1a;玩家需在隨機生成的3D迷宮中尋找出口&#xff0c;躲避陷阱、收集道具、解開謎題。核心機制&#xff1a; 隨機生成迷宮&#xff1a;每次游戲生成不同結構的迷宮&#xff08;遞歸分割算法或深度優先搜索&#xff09;。第一人稱視角&#xf…

基于ssm的寵物醫院信息管理系統(全套)

一、系統架構 前端&#xff1a;html | layui | vue | element-ui 后端&#xff1a;spring | springmvc | mybatis 環境&#xff1a;jdk1.8 | mysql | maven | tomcat | idea | nodejs 二、代碼及數據庫 三、功能介紹 01. web端-首頁1 02. web端-首頁…

【CXX】6.7 SharedPtr<T> — std::shared_ptr<T>

std::shared_ptr 的 Rust 綁定稱為 SharedPtr。 限制&#xff1a; SharedPtr 不支持 T 為不透明的 Rust 類型。對于在語言邊界上傳遞不透明 Rust 類型的所有權&#xff0c;應改用 Box&#xff08;C 中的 rust::Box&#xff09;。 示例 // src/main.rsuse std::ops::Deref; …

利用python生成excel中模板范圍對應的shape文件

利用python生成excel中模板范圍對應的shape文件 # -*- coding: utf-8 -*- import os.pathimport pandas as pd from shapely.geometry import Polygon from shapely.wkt import dumps import argparse# 創建解析器 parser argparse.ArgumentParser(description"這是一個…

cursor使用

引入私有文檔 設置-> Features->下滑找到Docs url后邊多加一個 / 可以拉取url下所有的頁面(子頁面&#xff0c;子目錄)&#xff0c;不加只拉取url當前頁面 使用 選擇 Docs 回車 選擇 文檔 直接解析鏈接 鏈接 回車 搜索引擎 web 對比git版本差異 git 選擇其中一個 g…

達夢數據庫中插入導出圖片的方法與應用

達夢數據庫中插入導出圖片的方法與應用 在數據庫的實際應用場景中&#xff0c;圖片存儲是一項常見且重要的需求。以電商平臺為例&#xff0c;商品展示圖片是吸引消費者的關鍵元素&#xff1b;而在社交軟件里&#xff0c;用戶頭像更是個人形象的直觀體現。針對達夢數據庫&#…