[C][動態內存分配][柔性數組]詳細講解

目錄

  • 1.動態內存函數的介紹
    • 1.malloc
    • 2.free
    • 2.calloc
    • 4.realloc
  • 2.常見的動態內存錯誤
  • 3.C/C++程序的內存開辟
  • 4.柔性數組
    • 1.是什么?
    • 2.柔性數組的特點
    • 3.柔性數組的使用
    • 4.柔性數組的優勢


1.動態內存函數的介紹

1.malloc

  • 函數原型void* malloc(size_t size)
  • 功能
    • malloc()向內存申請一塊連續可用的空間,并返回指向這塊空間的指針
  • 返回值
    • 如果開辟成功,則返回一個指向開辟好空間的指針
    • 如果開辟失敗,則返回一個NULL指針
      • 因此malloc的返回值一定要做檢查
  • 返回值的類型是void*,所以**malloc()并不知道開辟空間的類型**,具體在使用的時候使用者自己來決定(強制類型轉換)
  • 如果參數size == 0malloc()的行為是標準是未定義的,取決于編譯器

2.free

  • 函數原型void free(void* ptr)
  • 功能free()用來釋放動態開辟的內存
  • 如果參數ptr指向的空間不是動態開辟的,那free()的行為是未定義的
  • 如果參數ptrNULL,則函數什么事都不做
  • 一次完整的動態內存開辟的例子
int* p = (int*)malloc(10 * sizeof(int));
if(p == NULL)
{perror("main"); // main: xxxxreturn 0;
}// 使用
for(int i = 0; i < 10; i++)
{*(p + i) = i;
}free(p); // 回收空間
p = NULL; // 手動把p置為NULL

2.calloc

  • 函數原型void* calloc(size_t num, size_t size)
  • 功能:為num個大小為size的元素開辟一塊空間,并且把空間的每個字節初始化為0
  • 與函數malloc()的區別只在于calloc()會在返回地址之前把申請的空間的每個字節初始化為全0
  • 如果對申請的內存空間的內容要求初始化,那么可以很方便的使用calloc函數來完成任務

4.realloc

  • 函數原型void* realloc(void* ptr, size_t size)

  • 功能

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

    • ptr:要調整的內存地址
      • ptr == NULL,則realloc()作用同malloc()
    • size:調整之后新大小
  • 返回值:調整之后的內存起始位置

  • 注意:這個函數調整原內存空間大小的基礎上,還會將原來內存中的數據移動到新的空間

  • realloc()在調整內存空間時,存在兩種情況

    1. 原有空間之后有足夠大的空間
      • 要擴展內存就直接原有內存之后直接追加空間,原來空間的數據不發生變化
    2. 原有空間之后沒有足夠大的空間
      • 擴展方法
        • 在堆空間上另找一個合適大小的連續空間來使用
        • 這樣函數返回的是一個新的內存地址
    • 由于上述的兩種情況,realloc()的使用就要注意一些
      請添加圖片描述
  • 例如:為了確保內存空間成功開辟,拿一個臨時指針去接收新開辟的空間的地址,再賦值,這樣保證了萬一沒有成功開辟新的地址,而丟失了原來的地址

int* p =int*)calloc(10, sizeof(int));
if(p == NULL)
{perror("main");return 1;
}for(int i = 0; i < 10; i++) // 使用
{*(p + i) = i;
}// 這里需要p指向更大空間,realloc調整
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if(ptr != NULL)
{p = ptr;
}free(p);
p = NULL;

2.常見的動態內存錯誤

  1. 對NULL指針的解引用操作
  2. 對動態開辟空間的越界訪問
  3. 使用free釋放非動態開辟的空間
  4. 使用free釋放動態內存中的一部分
  5. 對同一塊動態開辟的空間,多次釋放
  6. 動態開辟的空間忘記釋放- 內存泄漏 -> 比較嚴重
  • 經典例子一
    • str傳給GetMemory()的時候是值傳遞,所以GetMemory()的形參pstr的一份臨時拷貝
    • GetMemory()內部動態申請空間的地址,存放在p中,不會影響外邊的str,所以當GetMemory()返回之后,str依然是NULL,所以strcpy()會失敗
    • GetMemory()返回之后,形參p銷毀,使得動態開辟的100個字節存在內存泄漏無法釋放
void GetMemory(char* p)
{p = (char *)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");
}int main()
{Test();return 0;
}// 改進一
char* GetMemory(char* p)
{p = (char *)malloc(100);return p;
}void Test(void)
{char* str = NULL;str = GetMemory(str);strcpy(str, "SnowK");free(str);str = NULL:
}// 改進二
void GetMemory(char** p)
{*p = (char *)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(&str);strcpy(str, "SnowK");free(str);str = NULL:
}
  • 經典例子二
    • GetMemory()內部創建的數組是在棧區上創建的
    • 出了函數,p[]的空間就還給了操作系統
    • 返回的地址是沒有實際的意義,如果通過返回的地址去訪問內存,就是非法訪問內存
char* GetMemory()
{char p[] = "SnowK";return p;
}void Test()
{char* str = NULL;str = GetMemory();
}

3.C/C++程序的內存開辟

  • C/C++程序內存分配的幾個區域:
    1. 棧區(stack)
      • 在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放
      • 棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限
      • 棧區主要存放運行函數而分配的局部變量、函數參數、返回數據、返回地址等
    2. 堆區(heap)
      • 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收
      • 分配方式類似于鏈表
    3. 數據段(靜態區)(static)
      • 存放全局變量、靜態數據程序
      • 結束后由系統釋放
    4. 代碼段:存放函數體(類成員函數和全局函數)的二進制代碼
      • 實際上普通的局部變量是在棧區分配空間的,棧區的特點是在上面創建的變量出了作用域就銷毀
      • 但是被static修飾的變量存放在數據段(靜態區),數據段的特點是在上面創建的變量,直到程序結束才銷毀,所以生命周期變長
        請添加圖片描述

4.柔性數組

1.是什么?

struct s
{int n;int arr[]; // 大小是未知
}

2.柔性數組的特點

  • 結構中的柔性數組成員前面必須至少一個其他成員
  • sizeof返回的這種結構大小不包括柔性數組的內存
  • 包含柔性數組成員的結構用malloc()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小
typedef struct st_type
{int i;int arr[0]; // 柔性數組成員
}type_a;printf("%d\n", sizeof(type_a)); // 輸出的是4

3.柔性數組的使用

// 期望arr的大小是10個整形
struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
ps->n = 10;for(int i = 0; i < 10; i++)
{ps->arr[i] = i;
}// 增加
struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
if(ptr != NULL)
{ps = ptr;
}// 使用
// ...
// 釋放
free(ps);
ps = NULL;

4.柔性數組的優勢

  • 方便內存釋放
    • 如果代碼是在一個給別人用的函數中,你在里面做了二次內存分配,并把整個結構體返回給用戶
    • 用戶調用free()可以釋放結構體,但是用戶并不知道這個結構體內的成員也需要free(),所以你不能指望用戶來發現這個事
    • 所以,如果把結構體的內存以及其成員要的內存一次性分配好了,并返回給用戶一個結構體指針,用戶做一次free()就可以把所有的內存也給釋放掉
  • 這樣有利于訪問速度
    • 連續的內存有益于提高訪問速度,也有益于減少內存碎片
      • 其實,個人覺得也沒高多少,反正也避免不了要用做偏移量的加法來尋址

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

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

相關文章

iOS馬甲包, AB面,H5跳轉包,開發上架

什么是馬甲包 馬甲包一般是主APP的分身或者克隆&#xff0c;也或者說是穿著馬甲的一個APP&#xff0c;脫掉馬甲&#xff0c;APP將呈現另一種樣式&#xff0c;也就是常說的AB面APP。 1. 馬甲包、AB面、白包、h5跳轉包 2.蘋果開發者 3.TG&#xff1a;APPYKJ 4.喂心&#xff1…

【AI算法崗面試八股面經【超全整理】——概率論】

AI算法崗面試八股面經【超全整理】 概率論信息論機器學習CVNLP 目錄 1、古典概型、幾何概型2、條件概率、全概率公式、貝葉斯公式3、先驗概率、后驗概率4、離散型隨機變量的常見分布5、連續型隨機變量的常見分別6、數學期望、方差7、協方差、相關系數8、獨立、互斥、不相關9.大…

【PB案例學習筆記】-11動畫顯示窗口

寫在前面 這是PB案例學習筆記系列文章的第11篇&#xff0c;該系列文章適合具有一定PB基礎的讀者。 通過一個個由淺入深的編程實戰案例學習&#xff0c;提高編程技巧&#xff0c;以保證小伙伴們能應付公司的各種開發需求。 文章中設計到的源碼&#xff0c;小凡都上傳到了gite…

ESP32 - Micropython ESP-IDF 雙線教程 WIFI (2)

ESP32 - Micropython ESP-IDF 雙線教程 WIFI ESP32 - IDF WIFI轉換為ESP32-IDF的示例代碼main/main.c 代碼解釋 ESP32 - IDF WIFI 轉換為ESP32-IDF的示例代碼 以下是使用ESP-IDF&#xff08;Espressif IoT Development Framework&#xff09;編寫的連接到Wi-Fi網絡的示例代碼…

頸源性頭痛癥狀及表

頸源性頭痛一般表現為&#xff0c;就是說從枕后一直顳側&#xff0c;到太陽穴附近&#xff0c;這個是枕小的一個疼痛&#xff0c;還有一部分人從枕后&#xff0c;沿著一個弧線&#xff08;如下圖&#xff09;的軌跡到了前額&#xff0c;到我們前額&#xff0c;這樣一個疼痛&…

Bitbucket的原理及應用詳解(一)

本系列文章簡介&#xff1a; 在數字化和全球化的今天&#xff0c;軟件開發和項目管理已經成為企業成功的關鍵因素之一。隨著團隊規模的擴大和項目的復雜化&#xff0c;如何高效地協同開發、管理代碼和確保代碼質量成為了開發者和管理者面臨的重要挑戰。Bitbucket作為一款功能強…

深入解析線程上下文切換:掌握線程上下文切換的核心原理

1. 進程與線程的基本概念 1.1 進程與線程的區別 在操作系統中&#xff0c;進程和線程是兩個基本的概念&#xff0c;它們共同構成了程序的執行環境。了解它們的區別是理解線程上下文切換的基礎。 進程&#xff1a;進程是程序的一次執行實例。它是操作系統資源分配的基本單位。…

pytest的斷言與Selenium 模擬操作的一個例子

在Python中&#xff0c;pytest是一個流行的單元測試框架&#xff0c;而Selenium是一個用于自動化瀏覽器操作的工具。結合這兩者&#xff0c;我們可以編寫自動化測試腳本來驗證網頁的行為是否符合預期。下面是一個簡單的例子&#xff0c;展示了如何使用pytest的斷言功能以及Sele…

解決在Mac下使用npm報錯:Error: EACCES: permission denied

原因說明&#xff1a;沒有足夠的權限在 /usr/local/lib/node_modules 目錄下創建文件夾 這個錯誤表明你在安裝或更新 Vue.js&#xff08;vue&#xff09;包時&#xff0c;沒有足夠的權限在 /usr/local/lib/node_modules 目錄下創建文件夾。這通常是因為默認情況下&#xff0c;普…

【頭歌-Python】文件自學引導

禁止轉載&#xff0c;原文&#xff1a;https://blog.csdn.net/qq_45801887/article/details/139258793 參考教程&#xff1a;B站視頻講解——https://space.bilibili.com/3546616042621301 如果代碼存在問題&#xff0c;麻煩大家指正 ~ ~有幫助麻煩點個贊 ~ ~ 文件自學引導 第…

算數運算符

算術運算符是用于數值類型變量計算的運算符。 它的返回結果是數值。 賦值符號 關鍵知識點&#xff1a;先看右側&#xff0c;再看左側&#xff0c;把右側的值賦值給左側的變量。 附上代碼&#xff1a; string myName "唐唐"; int myAge 18; float myHeight 177.5…

202312青少年軟件編程(Python)等級考試試卷(四級)

第 1 題 【單選題】 下列有關分治算法思想的描述不正確的是?(?) A :將問題分解成的子問題具有相同的模式 B :將問題分解出的各個子問題相互之間有公共子問題 C :當問題足夠小時,可以直接求解 D :可以將子問題的求解結果合并成原問題的解 正確答案:B 試題解析: 第 2…

ADIL簡單測試實例

參考資料&#xff1a;https://blog.csdn.net/geyichongchujianghu/article/details/130045373這個連接是Java的代碼&#xff0c;我根據它的鏈接寫了一個kotlin版本的。 AIDL&#xff08;Android Interface Definition Language&#xff09;是Android平臺上用于進程間通信&…

AI辦公自動化:kimi批量新建文件夾

工作任務&#xff1a;批量新建多個文件夾&#xff0c;每個文件夾中的年份不一樣 在kimi中輸入提示詞&#xff1a; 你是一個Python編程專家&#xff0c;要完成一個編寫關于錄制電腦上的鍵盤和鼠標操作的Python腳本的任務&#xff0c;具體步驟如下&#xff1a; 打開文件夾&…

FFmpeg編解碼的那些事(1)

看了網上很多ffmpeg的編解碼的文章和代碼&#xff0c;發現有很多文章和代碼都過時了&#xff0c;主要還是ffmpeg有很多接口都已經發生變化了。 這里簡單說一下&#xff0c;什么是編碼和解碼。 1.視頻編碼 對于視頻來說&#xff0c;可以理解為多張&#xff08;rgb或者yuv&…

Python散點圖矩陣代碼模版

本文分享Python seaborn實現散點圖矩陣代碼模版&#xff0c;節選自&#x1f449;嫌Matplotlib繁瑣&#xff1f;試試Seaborn&#xff01; 散點圖矩陣&#xff08;scatterplot matrix&#xff09;展示原始數據中所有變量兩兩之間關系&#xff0c;可以規避單一統計指標的偏差&…

二分查找算法詳講(三種版本寫法)原創

介紹: 二分查找算法&#xff08;Binary Search&#xff09;是一種在有序數組中查找目標元素的算法。 它的基本思想是通過將目標元素與數組的中間元素進行比較&#xff0c;從而將搜索范圍縮小一半。 如果目標元素等于中間元素&#xff0c;則搜索結束&#xff1b;如果目標元素小…

Neural Filters:照片恢復

Ps菜單&#xff1a;濾鏡/Neural Filters/恢復/照片恢復 Neural Filters/RESTORATION/Photo Restoration 照片恢復 Photo Restoration借助 AI 強大功能快速恢復舊照片&#xff0c;提高對比度、增強細節、消除劃痕。將此濾鏡與著色相結合以進一步增強效果。 “照片恢復”濾鏡利用…

Scikit-Learn隨機森林

Scikit-Learn隨機森林 1、隨機森林1.1、集成學習1.2、Bagging方法1.3、隨機森林算法1.4、隨機森林的優缺點2、Scikit-Learn隨機森林回歸2.1、Scikit-Learn隨機森林回歸API2.2、隨機森林回歸實踐(加州房價預測)1、隨機森林 隨機森林是一種由決策樹構成的集成算法,它在大多情況…

mac安裝的VMware虛擬機進行橋接模式配置

1、先進行網絡適配器選擇&#xff0c;選擇橋接模式 2、點擊網絡適配器 設置... 3、選擇WiFi&#xff08;我使用的是WiFi&#xff0c;所以選擇這個&#xff09;&#xff0c;注意看右邊的信息&#xff1a;IP和子網掩碼&#xff0c;后續配置虛擬機的ifcfg-ens文件會用到 4、編輯if…