【C語言深度解剖】(15):動態內存管理和柔性數組

🤡博客主頁:醉竺

🥰本文專欄:《C語言深度解剖》

😻歡迎關注:感謝大家的點贊評論+關注,祝您學有所成!


??💜💛想要學習更多C語言深度解剖點擊專欄鏈接查看💛💜???


目錄

1. 為什么存在動態內存分配

2. 動態內存函數?

2.1 malloc和free?

2.2 calloc?

2.3 realloc?

3. 常見的動態內存錯誤?

3.1 對NULL指針的解引用操作

3.2 對動態開辟空間的越界訪問?

3.3 對非動態開辟內存使用free釋放?

3.4 使用free釋放一塊動態開辟內存的一部分?

3.5 對同一塊動態內存多次釋放?

3.6 動態開辟內存忘記釋放(內存泄漏)?

4. 幾個經典的筆試題?

5. C/C++程序的內存開辟?

6. 柔性數組

6.1 柔性數組的特性:?

6.2 柔性數組的使用?

6.3 柔性數組的優勢?


1. 為什么存在動態內存分配

我們已經掌握的內存開辟方式有:

int val = 20;//在棧空間上開辟四個字節
char arr[10] = { 0 };//在棧空間上開辟10個字節的連續空間

但是上述的開辟空間的方式有兩個特點:

  1. 空間開辟大小是固定的。
  2. 數組在申明的時候,必須指定數組的長度,它所需要的內存在編譯時分配。

但是對于空間的需求,不僅僅是上述的情況。有時候我們需要的空間大小在程序運行的時候才能知道, 那數組的編譯時開辟空間的方式就不能滿足了。 這時候就只能動態內存開辟了。?

2. 動態內存函數?

2.1 malloc和free?

void* malloc (size_t size);

這個函數向內存申請一塊連續可用的空間,并返回指向這塊空間的指針。?

  • 如果開辟成功,則返回一個指向開辟好空間的指針。
  • 如果開辟失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查。
  • 返回值的類型是 void* ,所以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自己 來決定。
  • 如果參數 size 為0,malloc的行為是標準是未定義的,取決于編譯器。?

C語言提供了另外一個函數free,專門是用來做動態內存的釋放和回收的,函數原型如下:?

void free (void* ptr);

free函數用來釋放動態開辟的內存。

  • 如果參數 ptr 指向的空間不是動態開辟的,那free函數的行為是未定義的。
  • 如果參數 ptr 是NULL指針,則函數什么事都不做。

malloc和free都聲明在 stdlib.h 頭文件中。

舉個例子:?

  • 在釋放內存之后將指針置為 NULL 是一個良好的編程習慣,但并不是強制性的。然而,這個做法可以提供額外的保護,防止懸空指針(dangling pointer)的問題。
  • 懸空指針是指向已經釋放的內存的指針。如果指針在釋放后沒有被置為 NULL,那么它仍然包含一個地址,這個地址可能不再有效,因為釋放的內存可能已經被重新分配給其他用途。如果懸空指針被錯誤地解引用,可能會導致未定義行為,包括程序崩潰、數據損壞或安全漏洞。
  • 將指針置為 NULL 可以防止懸空指針被解引用,因為解引用 NULL 指針通常會導致程序立即崩潰,這樣開發者可以立即發現問題并修復它,而不是面對更難以追蹤的未定義行為。

2.2 calloc?

C語言還提供了一個函數叫 calloc , calloc 函數也用來動態內存分配。原型如下:?

void *calloc(size_t num, size_t size);
  • num:要分配的內存塊的數量。
  • size:每個內存塊的大小,以字節為單位。
  • 函數的功能是為 num 個大小為 size 的元素開辟一塊空間,并且把空間的每個字節初始化為0。
  • 與函數 malloc 的區別只在于 calloc 會在返回地址之前把申請的空間的每個字節全初始化為0。?
#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));if (NULL != p){//使用空間}free(p);p = NULL;return 0;
}

所以如何我們對申請的內存空間的內容要求初始化,那么可以很方便的使用calloc函數來完成任務。

2.3 realloc?

  • realloc函數的出現讓動態內存管理更加靈活。
  • 有時會我們發現過去申請的空間太小了,有時候我們又會覺得申請的空間過大了,那為了合理的時 候內存,我們一定會對內存的大小做靈活的調整。那 realloc 函數就可以做到對動態開辟內存大小 的調整。

函數原型如下:

void* realloc (void* ptr, size_t size);
  • ptr 參數是一個指向之前分配的內存塊的指針。
  • size 參數指定了新的內存塊大小,以字節為單位。
  • 返回值:realloc 函數嘗試改變 ptr 指向的內存塊的大小為 size 字節。如果重新分配成功,它返回一個指向新分配內存塊的指針,這個指針可能與 ptr 相同(如果原地擴大成功),也可能不同(如果需要移動數據到新的位置)。如果失敗,它返回 NULL,并且原始內存塊保持不變?,不會釋放。

realloc在調整內存空間(下面演示的是擴大空間)時存在兩種情況:

情況1:原有空間之后有足夠大的空間

情況2:原有空間之后沒有足夠大的空間?

情況1:當是情況1的時候,擴展內存是在原有內存之后直接追加空間,realloc 函數重新申請空間之后,返回的地址還是原始空間的起始地址,?原來空間的數據部沒發生變化。如果新大小大于舊大小,超出舊大小部分的數據是未初始化的。

情況2:當是情況2的時候,原有空間之后沒有足夠的空間擴容。此時擴展的方法:在堆空間上另外找一個滿足大小的連續空間,并把舊空間的數據拷貝到新空間,然后釋放舊的空間,返回新空間的起始地址。

#include <stdio.h>
#include <stdlib.h>
int main() {int* ptr;int old_size = 5;int new_size = 10;// 分配一個整數數組的空間ptr = (int*)malloc(old_size * sizeof(int));if (ptr == NULL) {fprintf(stderr, "Initial memory allocation failed\n");return 1;}// 使用分配的內存for (int i = 0; i < old_size; i++) {ptr[i] = i;}// 重新分配內存以增加數組大小int* new_ptr = (int*)realloc(ptr, new_size * sizeof(int));if (new_ptr == NULL) {fprintf(stderr, "Memory reallocation failed\n");free(ptr); // 釋放原有內存return 1;}// 更新指針并使用新分配的內存ptr = new_ptr;for (int i = old_size; i < new_size; i++) {ptr[i] = i;}// 打印數組元素for (int i = 0; i < new_size; i++) {printf("%d ", ptr[i]);}printf("\n");// 釋放內存free(ptr);return 0;
}

3. 常見的動態內存錯誤?

3.1 對NULL指針的解引用操作

3.2 對動態開辟空間的越界訪問?

3.3 對非動態開辟內存使用free釋放?

3.4 使用free釋放一塊動態開辟內存的一部分?

3.5 對同一塊動態內存多次釋放?

3.6 動態開辟內存忘記釋放(內存泄漏)?

忘記釋放不再使用的動態開辟的空間會造成內存泄漏。

切記: 動態開辟的空間一定要釋放,并且正確釋放 。?


4. 幾個經典的筆試題?

4.1 題目1:?

請問運行Test 函數會有什么樣的結果???

4.2 題目2:?

請問運行Test 函數會有什么樣的結果??

4.3 題目3:?

請問運行Test 函數會有什么樣的結果??

4.4 題目4:?

請問運行Test 函數會有什么樣的結果??


5. C/C++程序的內存開辟?

C/C++程序內存分配的幾個區域:?

1. 棧區(stack):在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結 束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是 分配的內存容量有限。 棧區主要存放運行函數而分配的局部變量、函數參數、返回數據、返 回地址等。

2. 堆區(heap):一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。分 配方式類似于鏈表。

3. 數據段(靜態區):(static)存放全局變量、靜態數據。程序結束后由系統釋放。

4. 代碼段:存放函數體(類成員函數和全局函數)的二進制代碼。?

有了這幅圖,我們就可以更好的理解在 static關鍵字修飾局部變量的例子了。

實際上普通的局部變量是在棧區分配空間的,棧區的特點是在上面創建的變量出了作用域就銷毀。 但是被static修飾的變量存放在數據段(靜態區),數據段的特點是在上面創建的變量,直到程序 結束才銷毀 所以生命周期變長。?


6. 柔性數組

C語言中的柔性數組(Flexible Array Member, FAM)是C99標準引入的一個特性,它允許結構體的最后一個成員是一個大小未指定的數組,這為處理可變長度數據結構提供了一種高效而靈活的方式。?

例如:

有些編譯器會報錯無法編譯可以改成:

6.1 柔性數組的特性:?

  • 位置要求:柔性數組必須是結構體的最后一個成員。這是因為它不占用結構體本身的空間,而是緊隨結構體之后分配內存。
  • 內存分配:雖然聲明時數組長度為0(如int a[0]或直接int a[]),但實際使用時,會通過動態內存分配為它分配足夠的空間以容納實際需要的元素數量。
  • 內存對齊:柔性數組不會影響結構體的自然對齊,因此在計算結構體大小時,柔性數組不計入。這有助于高效地利用內存。
  • 便捷的內存管理:由于柔性數組與結構體主體連續存儲,只需要一次內存分配即可為結構體和其后的柔性數組分配空間,這簡化了內存管理,尤其是在需要同時釋放結構體和數組時。
  • 優化訪問速度:連續的內存布局有利于CPU緩存,提高數據訪問效率。

例如:

6.2 柔性數組的使用?

這樣柔性數組成員a,相當于獲得了100個整型元素的連續空間。?

6.3 柔性數組的優勢?

上述的 type_a 結構也可以設計為:?

上述 代碼1代碼2 可以完成同樣的功能,但是更推薦?方法1?,方法1的好處:?

擴展閱讀:

C語言結構體里的數組和指針

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

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

相關文章

I.MX6ULL的官方 SDK 移植實驗

系列文章目錄 I.MX6ULL的官方 SDK 移植實驗 I.MX6ULL的官方 SDK 移植實驗 系列文章目錄一、前言二、I.MX6ULL 官方 SDK 包簡介三、硬件原理圖四、試驗程序編寫4.1 SDK 文件移植4.2 創建 cc.h 文件4.3 編寫實驗代碼 五、編譯下載驗證5.1編寫 Makefile 和鏈接腳本5.2編譯下載 一、…

列表元素添加的藝術:從單一到批量

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、引言 二、向列表中添加單一元素 1. append方法 2. insert方法 三、向列表中添加批量…

MySQL 存儲過程(實驗報告)

一、實驗名稱&#xff1a; 存儲過程 二、實驗日期&#xff1a; 2024 年5 月 25 日 三、實驗目的&#xff1a; 掌握MySQL存儲過程的創建及調用&#xff1b; 四、實驗用的儀器和材料&#xff1a; 硬件&#xff1a;PC電腦一臺&#xff1b; 配置&#xff1a;內存&#xff0…

Android 配置本地解決下載 Gradle 慢的問題

步驟1 打開項目下 gradle/wrapper/gradle-wrapper.properties 文件。 步驟2 文件內容如下。 #Sat May 25 16:24:00 CST 2024 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists distributionUrlhttps\://services.gradle.org/distributions/gradle-8.7-bin…

SW焊接路徑不能是閉合的

如果是整個圓,就不能作出焊件

【Docker學習】深入研究命令docker exec

使用docker的過程中&#xff0c;我們會有多重情況需要訪問容器。比如希望直接進入MySql容器執行命令&#xff0c;或是希望查看容器環境&#xff0c;進行某些操作或訪問。這時就會用到這個命令&#xff1a;docker exec。 命令&#xff1a; docker container exec 描述&#x…

ssm275寵物醫院管理系統+vue-手把手調試搭建

ssm275寵物醫院管理系統vue-手把手調試搭建 ssm275寵物醫院管理系統vue-手把手調試搭建

Jmeter預習第1天

Jmeter參數化&#xff08;重點&#xff09; 本質&#xff1a;使用參數的方式來替代腳本中的固定為測試數據 實現方式&#xff1a; 定義變量&#xff08;最基礎&#xff09; 文件定義的方式&#xff08;所有測試數據都是固定的情況下[死數據]&#xff0c;eg:注冊登錄&#xff0…

Linux -- 進程間通信的五種方式

IPC&#xff08;InterProcess Communication&#xff09;的方式通常有管道&#xff08;包括無名管道和命名管道&#xff09;、消息隊列、信號量、共享存儲、Socket、Streams等。其中Socket和Stream支持不同主機上的兩個進程IPC。 管道&#xff08;Pipes&#xff09;&#xff1a…

STM32中斷編程入門

文章目錄 一、 理論部分1.中斷系統2.中斷執行流程3.NVIC的基本結構4.EXTI介紹5.AFIO復用IO口 二、實驗目的&#xff1a;學習stm32中斷原理和開發編程方法。使用標準完成以下任務&#xff1a;&#xff08;一&#xff09;實驗一 開關控制LED的亮滅1.代碼部分2.運行結果 &#xff…

Qt | QFormLayout 類(表單布局)

01、上節回顧 Qt | QGridLayout 類(網格布局)02、簡介 1、QFormLayout 布局以兩列的形式列出其子項目, 2、QFormLayout 被分成兩列,左列是標簽(label)部分,通常由標簽 QLabel 組成,右列是由 字段(field)部分,通常是 QLineEdit 行編輯器,QSpinBox 旋轉框等部件,注意:…

【簡單易用,新人友好】一個輕量級生物信息學流程框架,從此解決99%的生物信息學流程搭建問題...

生物信息學數據分析流程的搭建是一項繁重而復雜的工作。隨著行業的發展&#xff0c;各種生信流程框架層出不窮&#xff0c;比如有: NextflowSnakemakeCWLWDL 各種標準&#xff0c;各種規則&#xff0c;令人眼花繚亂。選擇太多&#xff0c;往往令人無所適從。特別是新進入行業的…

小程序的深層了解

一:wxss的全局樣式和局部樣式 寫在文件上,第一個路徑會執行全局和局部自帶的wxss給wxml,會執行wxml,會執行json和js. 無論那個文件都會執行文件夾內的和外部的app.wxss,但是如果有一樣的屬性,則看屬性的權重,權重一樣,則設置局部樣式. 二:全局配置 wx:key"寫的是data內…

17.7K星開源產品分析平臺:Posthog

Posthog&#xff1a;開源洞察&#xff0c;產品優化的得力助手 - 精選真開源&#xff0c;釋放新價值。 概覽 PostHog是一個全面開源的平臺&#xff0c;旨在幫助團隊構建更好的產品。它提供了從產品分析到會話回放、功能標志和A/B測試等一系列工具&#xff0c;支持自托管&#x…

如何通過Nginx配置將請求轉發到conf.d目錄下的各個配置文件

目錄 如何通過Nginx配置將請求轉發到conf.d目錄下的各個配置文件1. 修改主配置文件 nginx.conf2. 在 conf.d 目錄中創建站點配置3. 設置站點根目錄和權限4. 檢查配置并重新加載Nginx總結 如何通過Nginx配置將請求轉發到conf.d目錄下的各個配置文件 在使用Nginx進行網站管理時&…

讀人工智能時代與人類未來筆記14_管控人工智能

1. 管控人工智能 1.1. 歷史上的戰場進一步推進到與數字網絡相連的所有地方 1.2. 數字程序現在控制著一個由眾多實體系統構成的龐大且仍在不斷增長的領域&#xff0c;而且越來越多的此類系統已實現網絡化 1.2.1. 在某些情況下甚至連門鎖和冰箱都實現了網絡化 1.2.2. 這催生出…

拋出異常時仍然需要記錄錯誤日志

場景&#xff1a;當service的方法執行拋出異常時&#xff0c;事務會發生回滾&#xff0c;導致無法記錄錯誤日志 解決&#xff1a;切面 其他&#xff1a;1.日志需要記錄日志標題&#xff0c;保存入參 2.失敗時會拋出異常&#xff1b;日志需要判斷執行是否成功&#xff0c;記錄…

2024年漢字小達人活動4個多月開賽:18道歷年選擇題和答案、解析

根據近年的安排&#xff0c;2024年第11屆漢字小達人比賽還有4個多月就啟動&#xff0c;那么孩子們如何利用這段時間有條不紊地備考呢&#xff1f;我的建議是兩手準備&#xff1a;①把小學1-5年級的語文課本上的知識點熟悉&#xff0c;重點是字、詞、成語、古詩。②把歷年真題刷…

聯想端游聯運SDK接入指南

1. 接入流程 本文檔主要介紹了 聯想PC游戲SDK接入流程、聯想游戲提供的功能、接入注意事項等。 1.1. 接入方式 1. 聯想游戲SDK2.1版本支持“賬號防沉迷支付”接入方式&#xff1b; a. 聯想提供賬號注冊、登錄等能力 b. 聯想提供防沉迷服務 c. 聯想提供游戲內支付 1.2. 對…

【學習筆記】計算機組成原理(八)

CPU 的結構和功能 文章目錄 CPU 的結構和功能8.1 CPU的結構8.1.1 CPU的功能8.1.2 CPU結構框圖8.1.3 CPU的寄存器8.1.4 控制單元CU和中斷系統 8.2 指令周期8.2.1 指令周期的基本概念8.2.2 指令周期的數據流 8.3 指令流水8.3.1 指令流水原理8.3.2 影響流水線性能的因素8.3.3 流水…