重生之我在異世界學編程之C語言:深入動態內存管理收尾 + 柔性數組篇

大家好,這里是小編的博客頻道
小編的博客:就愛學編程

很高興在CSDN這個大家庭與大家相識,希望能在這里與大家共同進步,共同收獲更好的自己!!!

本文目錄

  • 引言
  • 正文
    • 常見的動態內存管理錯誤
      • (1)對指針的越界訪問
      • (2)free函數對非堆區的指針的釋放是非法的
      • (3)使用free函數釋放動態內存開辟空間的一部分
      • (4)對于同一塊動態內存釋放了一次以上
      • (5)內存泄漏(未使用free釋放不再使用的動態內存)
    • 柔性數組
      • 一 柔性數組的定義與特性
      • 二 柔性數組的用法
      • 三 柔性數組的大小計算
      • 三 柔性數組的優勢與應用場景
      • 四 注意事項與常見問題
      • 五 總結與展望
  • 快樂的時光總是短暫,咱們下篇博文再見啦!!!不要忘了,給小編點點贊和收藏支持一下,在此非常感謝!!!

引言

本文是小編承接上一篇——《重生之我在異世界學編程之C語言:深入動態內存管理篇》所作的收尾和補充。邏輯上存在一定的先后順序,建議先搞懂之前的知識點再進行本章內容的學習。那現在寶子們就跟著小編的步伐一起進入本章知識的學習。Go!Go!Go!

在這里插入圖片描述


那接下來就讓我們開始遨游在知識的海洋!

正文


常見的動態內存管理錯誤

溫馨提示:

  • 本節內容所展示的代碼都為錯誤代碼,寶子們可以根據提示進行自查。

(1)對指針的越界訪問

常見于:對于malloc函數的參數理解錯誤

例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>int main(){int* p = (int*)malloc(100);if (p == NULL) {perror("malloc");}for (int i = 0; i < 100; i++) {printf("%d ", p[i]);}free(p);p = NULL;return 0;
}

(2)free函數對非堆區的指針的釋放是非法的

常見于:對于free函數理解錯誤

例:

#include<stdio.h>
int main() {int arr[10] = { 0 };int* p = arr;free(p);p = NULL;
}

(3)使用free函數釋放動態內存開辟空間的一部分

常見于:對于free函數理解錯誤

例:

#include<stdio.h>
#include<stdlib.h>
int main() {int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 0;}	p++;free(p);p = NULL;return 0;
}

(4)對于同一塊動態內存釋放了一次以上

常見于:對于free函數理解錯誤

例:

#include<stdio.h>
int main(){int* p = (int*)malloc(10 * sizeof(int));int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}free(p);//.....free(p);p = NULL;return 0;
}

(5)內存泄漏(未使用free釋放不再使用的動態內存)

常見于:對于free函數不會運用

例:

void test() {int* p = (int*)malloc(10 * sizeof(int));int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}return p;
}
#include<stdio.h>
int main() {int* p = (int*)malloc(10 * sizeof(int));int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}//未使用free釋放p指向的內存會導致內存的泄露p = NULL;return 0;//吃內存的程序while (1) {void* p = malloc(1);//未使用free釋放p指向的內存會導致內存的泄露}}

在C語言編程中,結構體(struct)是一種非常重要的數據類型,它允許我們將多個不同類型的數據組合在一起,形成一個復合類型。然而,在某些情況下,我們可能希望結構體的某個成員能夠具有可變的大小,以適應不同的數據需求。為了滿足這一需求,C99標準引入了柔性數組的概念

柔性數組

一 柔性數組的定義與特性

1. 定義

  • 柔性數組是結構體中的一個特殊成員,它的類型是未指定大小的數組。換句話說,這個數組在定義時沒有給出具體的長度,而是留待后續使用時動態確定這種設計使得結構體可以靈活地適應不同大小的數據塊

2. 特性

  • 靈活性:柔性數組允許結構體根據實際需要動態調整大小,從而提高了代碼的靈活性和可重用性
  • 內存連續性:由于柔性數組緊跟在結構體的其他成員之后,因此可以保證數據的內存連續性,這對于某些需要連續訪問數據的算法來說是非常有利的
  • 限制:柔性數組必須是結構體的最后一個成員,且只能有一個柔性數組成員。這是因為編譯器需要在編譯時知道結構體的大小,以便進行內存分配和訪問。如果柔性數組不是最后一個成員或者存在多個柔性數組成員,那么編譯器將無法確定結構體的大小

二 柔性數組的用法

1. 基本用法

下面是一個簡單的例子,展示了如何使用柔性數組來創建一個可變大小的結構體:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct {int id;char name[20];// 柔性數組,用于存儲可變長度的數據char data[];
} MyStruct;MyStruct* create_mystruct(const char* name, const char* data) {size_t datalen = strlen(data) + 1; // 包括終止符'\0'size_t structsize = sizeof(MyStruct) - sizeof(char) + datalen; // 計算結構體總大小// 使用malloc分配內存MyStruct* p = (MyStruct*)malloc(structsize);if (!p) {perror("Failed to allocate memory");exit(EXIT_FAILURE);}p->id = 1; // 設置IDstrncpy(p->name, name, sizeof(p->name)); // 復制名稱到結構體中strncpy(p->data, data, datalen); // 復制數據到柔性數組中return p;
}void free_mystruct(MyStruct* p) {free(p);
}int main() {const char* name = "Alice";const char* data = "This is a test string.";MyStruct* s = create_mystruct(name, data);printf("ID: %d
", s->id);printf("Name: %s
", s->name);printf("Data: %s
", s->data);free_mystruct(s);return 0;
}
  • 在這個例子中,MyStruct結構體包含了一個柔性數組data,用于存儲可變長度的字符串數據。create_mystruct函數根據輸入的名稱和數據計算結構體所需的總大小,并使用malloc函數分配相應的內存空間。然后,它將輸入的數據復制到結構體中并返回指向該結構體的指針。最后,在main函數中,我們創建了一個MyStruct實例并打印出其內容。使用完畢后,通過調用free_mystruct函數釋放分配的內存。

2.與指針的比較

雖然柔性數組看起來類似于在結構體中使用字符指針來指向動態分配的數據塊,但它們之間有一些重要的區別:

  • 內存連續性:如前所述,柔性數組保證了數據的內存連續性,而指針則不一定。當使用指針時,數據塊可能位于內存的任何位置,這可能導致緩存未命中率的增加和性能下降
  • 內存管理使用柔性數組時,整個結構體(包括柔性數組部分)通常是通過單個malloc或類似函數一次性分配的。這意味著我們可以更容易地管理內存,因為只需要一個free調用就可以釋放整個結構體。相比之下,如果使用指針來指向動態分配的數據塊,則需要分別管理這些塊的內存分配和釋放

例:

柔性數組實現柔性

typedef struct stu {int a;char arr[0];    //char arr[];   這兩種寫法都是C99語法規定內的,編譯器會支持至少一種寫法
}stu;
int main() {stu* p = (stu*)malloc(4 * sizeof(int) + 10 * sizeof(char));if (p == NULL) {perror("malloc::");return 1;}//使用int i = 0;//賦值for (i = 0; i < 10; i++) {(p->arr)[i] = i;}//打印for (i = 0; i < 10; i++) {printf("%d ", p->arr[i]);}//釋放內存free(p);p = NULL;
}

非柔性數組實現柔性

typedef struct stu {int a;//char arr[0];    char* arr;}stu;int main() {//第一次申請動態內存stu* p = (stu*)malloc(sizeof(int));if (p == NULL) {perror("malloc::");return 1;}p->a = 100;//第二次申請動態內存char* pa = (char*)realloc(NULL, 10);if (pa == NULL) {perror("realloc->arr");return 1;}p->arr = pa;pa = NULL;//使用int i = 0;//賦值for (i = 0; i < 10; i++) {p->arr[i] = 'a';}//打印for (i = 0; i < 10; i++) {printf("%c ", p->arr[i]);}//第一次釋放內存free(p->arr);p->arr = NULL;//第二次釋放內存free(p);p = NULL;}

所以我們通過對比可以得出使用柔性數組的好處有:

  • 1.方便釋放內存。
  • 2.減少系統碎片,加快訪問速度。

三 柔性數組的大小計算

基本規則

  • 計算含有柔性數組(成員)的結構體的大小不包括柔性數組成員的大小,所以含有柔性數組(成員)的結構體至少有除了柔性數組外的1個成員

例:

typedef struct stu {int a;char arr[0];    //char arr[];   這兩種寫法都是C99語法規定內的,編譯器會支持至少一種寫法
}stu;
int main() {printf("%zd\n", sizeof(stu));   
} 

運行結果:

在這里插入圖片描述

由此我們就能驗證以上規則的正確性。


三 柔性數組的優勢與應用場景

1. 優勢

  • 簡化內存管理:通過使用柔性數組,我們可以將數據和結構 體一起分配和管理,從而簡化了內存管理的復雜性。
  • 提高性能:由于柔性數組保證了數據的內存連續性,因此可以提高某些算法的性能。例如,在處理需要連續訪問數據的操作時(如字符串處理、圖像處理等),使用柔性數組可以減少緩存未命中率并提高訪問速度
  • 增強代碼可讀性:通過將數據和結構體結合在一起,我們可以使代碼更加清晰易懂。這有助于維護和理解復雜的程序結構。

2. 應用場景

柔性數組在許多領域都有廣泛的應用,包括但不限于以下幾個方面:

  • 網絡通信在網絡通信中,數據包的大小通常是可變的。通過使用柔性數組,我們可以輕松地創建可變大小的數據包結構體來處理不同類型的網絡消息
  • 文件處理在處理文件時,文件的內容大小和格式可能是未知的或可變的。柔性數組可以用于創建可變大小的文件頭或記錄結構體來讀取和處理文件中的數據
  • 圖像處理在圖像處理中,圖像的大小和分辨率可能是不同的。通過使用柔性數組,我們可以創建可變大小的圖像數據結構來存儲和處理不同尺寸的圖像
  • 數據庫操作在數據庫操作中,記錄的長度和內容可能是變化的。柔性數組可以用于創建可變大小的記錄結構體來存儲和操作數據庫中的數據

四 注意事項與常見問題

1. 注意事項

  • 確保柔性數組是最后一個成員:如前所述,柔性數組必須是結構體的最后一個成員。否則,編譯器將無法確定結構體的大小并進行正確的內存分配。
  • 避免多次分配內存:在使用柔性數組時,應盡量避免對結構體進行多次內存分配。最好是一次性分配足夠的內存來容納所有成員(包括柔性數組部分)。這樣可以減少內存碎片和提高性能。
  • 正確處理內存釋放:在使用完柔性數組后,應確保正確釋放分配的內存以避免內存泄漏。這通常是通過調用free函數來實現的。但是需要注意的是,如果結構體中還包含了其他動態分配的資源(如指針指向的字符串或其他數據結構),則需要分別釋放這些資源

2. 常見問題

  • 編譯器支持問題:盡管柔性數組是C99標準的一部分,但并不是所有的編譯器都完全支持這一特性。因此,在使用之前請檢查您的編譯器是否支持柔性數組以及相關的語法和功能。
  • 跨平臺兼容性:由于不同平臺和編譯器之間的實現差異可能會導致柔性數組的行為有所不同。因此,在進行跨平臺開發時請謹慎使用柔性數組并確保在不同平臺上進行測試和驗證。
  • 安全性問題:在使用柔性數組時需要注意安全性問題。例如,在將數據復制到柔性數組中時要確保不會超出分配的內存范圍導致緩沖區溢出等問題。此外還需要注意防止內存泄漏和其他常見的安全問題。

五 總結與展望

  • 柔性數組是C語言中一個非常有用的特性,它允許開發者創建可變大小的結構體以適應不同的數據需求。通過使用柔性數組,我們可以簡化內存管理、提高性能和增強代碼可讀性。然而,在使用柔性數組時也需要注意一些問題和限制,如確保它是結構體的最后一個成員、避免多次分配內存以及正確處理內存釋放等。隨著技術的不斷發展,未來可能會有更多的優化和改進來提高柔性數組的性能和可靠性。同時我們也期待看到更多創新的應用場景出現以充分利用這一強大的特性。

快樂的時光總是短暫,咱們下篇博文再見啦!!!不要忘了,給小編點點贊和收藏支持一下,在此非常感謝!!!

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

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

相關文章

無人直播源碼

DY無人直播系統架構設計介紹 在DY等短視頻平臺的直播中&#xff0c;無人直播系統能夠提供自動化、智能化的互動體驗&#xff0c;既減輕了主播的工作量&#xff0c;又提升了觀眾的參與感。以下是一個典型的無人直播系統架構設計&#xff0c;包含全局配置、點對點互動、產品話術、…

被裁20240927 --- 嵌入式硬件開發 前篇

前篇主要介紹一些相關的概念&#xff0c;用于常識掃盲&#xff0c;后篇開始上干貨&#xff01; 他捧著一只碗吃過百家的飯 1. 處理器芯片1.1 處理器芯片制造商一、 英特爾&#xff08;Intel&#xff09;二、 三星&#xff08;SAMSUNG&#xff09;三、 高通&#xff08;Qualcomm…

準備考試:解決大學入學考試問題

引言 在編程競賽和算法挑戰中&#xff0c;我們經常會遇到各種類型的組合問題。這些問題不僅考驗我們的邏輯思維能力&#xff0c;還要求我們熟練掌握數據結構和算法。在這篇文章中&#xff0c;我們將探討一個有趣的問題——“準備考試”&#xff0c;這個問題來自于一個虛構的情…

【Linux】進程間通信 -> 匿名管道命名管道

進程間通信的目的 數據傳輸&#xff1a;一個進程許需要將它的數據發送給另外一個進程。資源共享&#xff1a;多個進程之間共享同樣的資源。通知事件&#xff1a;一個進程需要向另一個或一組進程發送消息&#xff0c;通知它們發生了某種事件&#xff08;如進程終止時要通知父進程…

Pytorch注意力機制應用到具體網絡方法(閉眼都會版)

文章目錄 以YoloV4-tiny為例要加入的注意力機制代碼模型中插入注意力機制 以YoloV4-tiny為例 解釋一下各個部分&#xff1a; 最左邊這部分為主干提取網絡&#xff0c;功能為特征提取中間這邊部分為FPN&#xff0c;功能是加強特征提取最后一部分為yolo head&#xff0c;功能為獲…

修改el-select下拉框高度;更新:支持動態修改

文章目錄 效果動態修改&#xff1a;效果代碼固定高度版本動態修改高度版本&#xff08;2024-12-25 更新&#xff1a; 支持動態修改下拉框高度&#xff09; 效果 動態修改&#xff1a;效果 代碼 固定高度版本 注意點&#xff1a; popper-class 盡量獨一無二&#xff0c;防止影…

開關電源特點、分類、工作方式

什么叫開關電源隨著電力電子技術的發展和創新&#xff0c;使得開關電源技術也在不斷地創新。目前&#xff0c;開關電源以小型、輕量和高效率的特點被廣泛應用幾乎所有的電子設備&#xff0c;是當今電子信息產業飛速發展不可缺少的一種電源方式。 開關電源是利用現代電力電子技…

Linux應用軟件編程-文件操作(目錄io)

1.打開目錄&#xff1a; DIR *opendir(const char *name); 功能&#xff1a;打開一個目錄獲得一個目錄流指針 參數: name:目錄名 返回值&#xff1a;成功返回目錄流指針&#xff1b;失敗返回NULL 2.讀目錄&#xff1a; struct dirent *readdir(DIR *dirp); 功能&…

有哪些開發者模式?

1、單例開發模式&#xff08;Singleton Pattern&#xff09; 單例模式是一種創建型設計模式&#xff0c;目的是確保在程序運行期間&#xff0c;某個類只有一個實例&#xff0c;并提供一個全局訪問點來訪問該實例。 核心特點 唯一實例&#xff1a;一個類只能創建一個對象實例。…

如何完全剔除對Eureka的依賴,報錯Cannot execute request on any known server

【現象】 程序運行報錯如下&#xff1a; com.netflix.discovery.shared.transport.TransportException報錯Cannot execute request on any known server 【解決方案】 &#xff08;1&#xff09;在Maven工程中的pom去掉Eureka相關的引用&#xff08;注釋以下部分&#xff0…

vscode寫python,遇到問題:ModuleNotFoundError: No module named ‘pillow‘(已解決 避坑)

1 問題&#xff1a; ModuleNotFoundError: No module named pillow 2 原因&#xff1a; 原因1&#xff1a;安裝Pillow的pip命令所處的python版本與vscode調用的python解釋器版本不同。 如&#xff1a; 原因2&#xff1a;雖然用的是pillow&#xff0c;但是寫代碼的時候只能用…

Ashy的考研游記

文章目錄 摘要12.1112.2012.21 DAY1&#xff08;政治/英語&#xff09;政治英語 12.22 DAY2&#xff08;數學/專業課&#xff09;數學專業課 結束估分 摘要 在24年的12月里&#xff0c;Ashy完成了他的考研沖刺&#xff0c;順利的結束了他本年度的考研之旅。 在十二月里&#…

AIGC實踐|AI/AR助力文旅沉浸式互動體驗探索

前言&#xff1a; 本篇文章的創作靈感來源于近期熱門話題——讓文物“動起來”&#xff0c;各大博物館成為新進潮流打卡地。結合之前創作的AI文旅宣傳片良好的流量和反饋&#xff0c;外加最近比較感興趣的AR互動探索&#xff0c;想嘗試看看自己能不能把這些零碎的內容整合起來…

tcp 的三次握手與四次揮手

問1: 請你說一下tcp的三次握手一次握手兩次握手三次握手問: 為什么不四(更多)次握手? 問 2: 請說一下 tcp 的 4 次揮手一次揮手兩次揮手問題:能不能等到數據傳輸完成再返回 ack? 三次揮手四次揮手問: 為什么要等兩個最大報文存在時間? bg: tcp 是可靠的連接,如何保證 建立連…

Kubernetes(k8s)離線部署DolphinScheduler3.2.2

1.環境準備 1.1 集群規劃 本次安裝環境為&#xff1a;3臺k8s現有的postgreSql數據庫zookeeper服務 1.2 下載及介紹 DolphinScheduler-3.2.2官網&#xff1a;https://dolphinscheduler.apache.org/zh-cn/docs/3.2.2 官網安裝文檔&#xff1a;https://dolphinscheduler.apach…

C++的侵入式鏈表

非侵入式鏈表 非侵入式鏈表是一種鏈表數據結構&#xff0c;其中每個元素&#xff08;節點&#xff09;并不需要自己包含指向前后節點的指針。鏈表的結構和節點的存儲是分開的&#xff0c;鏈表容器會單獨管理這些指針。 常見的非侵入式鏈表節點可以由以下所示&#xff0c;即&a…

Flutter組合動畫學習

如何使用動畫控制器和動畫來創建一個簡單的動畫效果。具體來說&#xff0c;它通過一個 AnimationController 來控制兩個動畫&#xff0c;一個用于旋轉&#xff0c;一個用于繪制。 前置知識點學習 SingleTickerProviderStateMixin SingleTickerProviderStateMixin 是 Flutter …

在vscode的ESP-IDF中使用自定義組件

以hello-world為例&#xff0c;演示步驟和注意事項 1、新建ESP-IDF項目 選擇模板 從hello-world模板創建 2、打開項目 3、編譯結果沒錯 正在執行任務: /home/azhu/.espressif/python_env/idf5.1_py3.10_env/bin/python /home/azhu/esp/v5.1/esp-idf/tools/idf_size.py /home…

2025差旅平臺怎么選?一體化、全流程降本案例解析

差旅支出在企業中一直是一項重要但容易被忽視的成本開支&#xff0c;尤其是在項目驅動型企業中&#xff0c;因頻繁的差旅需求&#xff0c;支出規模往往持續增長。以差旅平臺分貝通簽約伙伴——某智能制造業的業務模式為例&#xff0c;該模式要求員工定期前往不同的工廠、供應商…

【linux】NFS實驗

NFS NFS服務 nfs,最早是Sun這家公司所發展出來的,它最大的功能就是可以透過網絡,讓不同的機器,不同的操作系統,進行實現文檔的共享。所以你可以簡單的將他看做是文件服務器。 實驗準備 ①先準備一個服務器端的操作系統和客戶端的操作系統(Red Hat)。 ②選擇NAT模式,…