最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言動態(tài)內(nèi)存管理示例詳解

 更新時間:2025年12月19日 08:58:55   作者:Yue丶越  
在C語言編程中,動態(tài)內(nèi)存管理是一項(xiàng)核心技能,它允許程序在運(yùn)行時靈活地分配和釋放內(nèi)存,這篇文章主要介紹了C語言動態(tài)內(nèi)存管理的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

在C語言編程中,靜態(tài)內(nèi)存分配(如數(shù)組、局部變量)受限于編譯時固定大小的特性,無法滿足程序運(yùn)行中動態(tài)調(diào)整內(nèi)存的需求。動態(tài)內(nèi)存管理則可通過malloccalloc等函數(shù),可以讓我們自主申請和釋放內(nèi)存,以成為了靈活處理內(nèi)存需求的核心技術(shù)。本文我們將從基礎(chǔ)原理出發(fā),結(jié)合實(shí)戰(zhàn)案例,以帶你掌握動態(tài)內(nèi)存管理的關(guān)鍵知識點(diǎn)。

一、為什么需要動態(tài)內(nèi)存分配?

靜態(tài)內(nèi)存分配(如int arr[10] = {0})存在兩個核心局限:

  • 空間大小編譯時固定,無法根據(jù)運(yùn)行時數(shù)據(jù)(如用戶輸入)調(diào)整。
  • 數(shù)組聲明時必須指定長度,一旦我們確定則無法修改。

而動態(tài)內(nèi)存分配允許程序在運(yùn)行時根據(jù)需求申請內(nèi)存,用完后手動釋放,極大提升了內(nèi)存使用的靈活性。例如處理用戶輸入的數(shù)組長度、動態(tài)存儲不確定數(shù)量的數(shù)據(jù)時,動態(tài)內(nèi)存是我們唯一的選擇。

二、動態(tài)內(nèi)存核心函數(shù)

C語言提供了4個核心動態(tài)內(nèi)存函數(shù),它們均聲明在stdlib.h頭文件中,各自適用場景不同,我們在運(yùn)用時需精準(zhǔn)區(qū)分。

2.1 malloc

  • 函數(shù)原型
  • void* malloc(size_t size)

  • 功能:向堆區(qū)申請一塊連續(xù)的、大小為size字節(jié)的內(nèi)存,返回指向該內(nèi)存的指針。
  • 關(guān)鍵點(diǎn)
    • 申請成功返回非NULL指針,失敗返回NULL,必須檢查返回值。
    • 返回類型為void*,需強(qiáng)制轉(zhuǎn)換為目標(biāo)類型(如int*)。
    • 申請的內(nèi)存未初始化,內(nèi)容為隨機(jī)值。

示例代碼

#include <stdio.h>
#include <stdlib.h>

int main() {
    int num = 0;
    scanf("%d", &num);
    // 申請num個int大小的內(nèi)存
    int* ptr = (int*)malloc(num * sizeof(int));
    if (NULL != ptr) { // 檢查申請是否成功
        for (int i = 0; i < num; i++) {
            *(ptr + i) = 0; // 初始化內(nèi)存
        }
    }
    free(ptr); // 釋放內(nèi)存
    ptr = NULL; // 避免野指針
    return 0;
}

2.2 free

  • 函數(shù)原型
  • void free(void* ptr)

  • 功能:釋放ptr指向的動態(tài)內(nèi)存(堆區(qū)),將內(nèi)存歸還給系統(tǒng)。
  • 致命誤區(qū)
    • 僅能釋放動態(tài)內(nèi)存(malloc/calloc/realloc申請的內(nèi)存),釋放棧區(qū)內(nèi)存(如局部變量地址)會導(dǎo)致未定義行為。
    • ptr為NULL,free無任何操作,因此釋放后建議將指針置為NULL,避免“野指針”。

2.3 calloc

  • 函數(shù)原型
  • void* calloc(size_t num, size_t size)

  • 功能:申請num個大小為size字節(jié)的連續(xù)內(nèi)存,并將每個字節(jié)初始化為0。
  • 與malloc的核心差異calloc自動初始化內(nèi)存,無需手動賦值。若需申請“干凈”的內(nèi)存(如統(tǒng)計(jì)數(shù)組、初始值為0的緩存),calloc更高效。

示例代碼

int* p = (int*)calloc(10, sizeof(int)); 
if (NULL != p) {
    for (int i = 0; i < 10; i++) {
        printf("%d ", *(p + i)); // 輸出:0 0 0 0 0 0 0 0 0 0
    }
}
free(p);
p = NULL;

2.4 realloc

  • 函數(shù)原型

  • void* realloc(void* ptr, size_t size)

  • 功能:調(diào)整ptr指向的動態(tài)內(nèi)存大小為size字節(jié),返回調(diào)整后的內(nèi)存起始地址。

  • 擴(kuò)容的兩種場景(核心點(diǎn)):

    • 場景1:原有內(nèi)存后有足夠空間:直接在原有內(nèi)存后追加空間,數(shù)據(jù)不移動,返回原地址。
    • 場景2:原有內(nèi)存后空間不足:在堆區(qū)新找一塊連續(xù)內(nèi)存,拷貝原數(shù)據(jù)到新地址,釋放原內(nèi)存,返回新地址。
  • 使用禁忌:禁止直接將返回值賦值給原指針(如ptr = realloc(ptr, 1000))。若擴(kuò)容失敗返回NULL,會導(dǎo)致原指針地址丟失,造成內(nèi)存泄漏。正確做法是先用臨時指針接收返回值,檢查成功后再賦值。

正確示例代碼

int* ptr = (int*)malloc(100);
if (NULL != ptr) {
    // 業(yè)務(wù)處理
}
// 擴(kuò)容:先存臨時指針
int* tmp = (int*)realloc(ptr, 1000); 
if (NULL != tmp) {
    ptr = tmp; // 擴(kuò)容成功,更新原指針
    // 后續(xù)業(yè)務(wù)處理
}
free(ptr);
ptr = NULL;

三、6個高頻動態(tài)內(nèi)存錯誤

動態(tài)內(nèi)存錯誤是C語言調(diào)試的重災(zāi)區(qū),以下6類錯誤需重點(diǎn)規(guī)避,幾乎覆蓋了我們所有筆試/面試考點(diǎn)。

錯誤類型錯誤代碼示例后果
對NULL指針解引用int* p = (int*)malloc(INT_MAX/4); *p = 20;malloc失敗返回NULL,解引用會導(dǎo)致程序崩潰
越界訪問int* p = (int*)malloc(10*sizeof(int)); for(i=0; i<=10; i++) *(p+i)=i;訪問超出申請的內(nèi)存區(qū)域,破壞堆區(qū)數(shù)據(jù),導(dǎo)致程序異常
釋放非動態(tài)內(nèi)存int a=10; int* p=&a; free(p);釋放棧區(qū)內(nèi)存,觸發(fā)未定義行為(程序崩潰或亂碼)
釋放部分動態(tài)內(nèi)存int* p = (int*)malloc(100); p++; free(p);p不再指向內(nèi)存起始地址,free無法識別,導(dǎo)致內(nèi)存泄漏
重復(fù)釋放int* p = (int*)malloc(100); free(p); free(p);同一內(nèi)存被多次釋放,破壞堆區(qū)結(jié)構(gòu),程序崩潰
內(nèi)存泄漏void test(){int* p=(int*)malloc(100);}申請的內(nèi)存未釋放,程序運(yùn)行中內(nèi)存持續(xù)占用,最終耗盡

四、動態(tài)內(nèi)存經(jīng)典筆試題

以下4道筆試題是企業(yè)面試高頻題,需結(jié)合內(nèi)存原理分析錯誤根源。

題目1:指針傳值導(dǎo)致內(nèi)存泄漏

void GetMemory(char *p) { p = (char *)malloc(100); }
void Test(void) {
    char *str = NULL;
    GetMemory(str); // 傳值調(diào)用,p是str的副本
    strcpy(str, "hello world"); // str仍為NULL,解引用崩潰
    printf(str);
}

錯誤原因GetMemory采用值傳遞,pstr的副本,malloc申請的內(nèi)存地址僅存于p,未傳遞給str。str始終為NULL,strcpy時解引用崩潰,且malloc的內(nèi)存未釋放,造成泄漏。

修正方案:改用指針的指針(char** p)傳址調(diào)用,將內(nèi)存地址賦值給*p。

題目2:棧區(qū)內(nèi)存釋放后訪問

char *GetMemory(void) {
    char p[] = "hello world"; // 棧區(qū)局部數(shù)組
    return p; // 返回棧區(qū)地址,函數(shù)結(jié)束后p被釋放
}
void Test(void) {
    char *str = NULL;
    str = GetMemory(); // str指向已釋放的棧區(qū)內(nèi)存
    printf(str); // 訪問“野內(nèi)存”,輸出亂碼
}

錯誤原因p是棧區(qū)局部數(shù)組,函數(shù)GetMemory結(jié)束后,棧區(qū)內(nèi)存被系統(tǒng)回收。str指向的地址已無效,訪問時屬于“野內(nèi)存”操作,結(jié)果不確定。

修正方案:將p改為動態(tài)內(nèi)存(char* p = (char*)malloc(12);),或用靜態(tài)數(shù)組(static char p[])。

題目3:正確傳址但未釋放內(nèi)存

void GetMemory(char **p, int num) { *p = (char *)malloc(num); }
void Test(void) {
    char *str = NULL;
    GetMemory(&str, 100); // 傳址調(diào)用,str獲得內(nèi)存地址
    strcpy(str, "hello");
    printf(str); // 輸出“hello”
}

潛在問題malloc申請的內(nèi)存未用free釋放,程序結(jié)束前會造成內(nèi)存泄漏。

修正方案:在printf后添加free(str); str = NULL;

題目4:釋放后仍訪問野指針

void Test(void) {
    char *str = (char *)malloc(100);
    strcpy(str, "hello");
    free(str); // 釋放內(nèi)存,但str未置NULL
    if (str != NULL) { // 條件為真,錯誤訪問
        strcpy(str, "world"); // 寫入已釋放的內(nèi)存,破壞堆區(qū)
        printf(str);
    }
}

錯誤原因free(str)后,內(nèi)存被歸還給系統(tǒng),但str仍指向原地址(野指針)。if條件誤判為“非空”,strcpy向已釋放的內(nèi)存寫入數(shù)據(jù),破壞堆區(qū)結(jié)構(gòu),可能導(dǎo)致程序崩潰。

修正方案free(str)后立即將str置為NULL(str = NULL;)。

五、柔性數(shù)組

C99標(biāo)準(zhǔn)允許結(jié)構(gòu)體的最后一個成員為“未知大小的數(shù)組”,稱為柔性數(shù)組,適用于需要“結(jié)構(gòu)體+動態(tài)數(shù)組”連續(xù)內(nèi)存的場景。

5.1 柔性數(shù)組的定義

typedef struct st_type {
    int i; // 前面至少有一個其他成員
    int a[]; // 柔性數(shù)組成員(部分編譯器支持int a[0];)
} type_a;
  • 關(guān)鍵特性sizeof(type_a)僅計(jì)算非柔性成員的大?。ㄊ纠?code>sizeof(type_a) = 4,不包含a的內(nèi)存)。
  • 內(nèi)存申請:需通過malloc分配“結(jié)構(gòu)體大小+柔性數(shù)組大小”的連續(xù)內(nèi)存,確保數(shù)組與結(jié)構(gòu)體在同一塊內(nèi)存中。

5.2 柔性數(shù)組的使用示例

#include <stdio.h>
#include <stdlib.h>

typedef struct st_type {
    int i;
    int a[]; // 柔性數(shù)組
} type_a;

int main() {
    // 申請“結(jié)構(gòu)體大小 + 100個int”的連續(xù)內(nèi)存
    type_a *p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
    if (NULL != p) {
        p->i = 100; // 初始化非柔性成員
        for (int i = 0; i < 100; i++) {
            p->a[i] = i; // 操作柔性數(shù)組
        }
    }
    free(p); // 一次釋放所有內(nèi)存,包括柔性數(shù)組
    p = NULL;
    return 0;
}

5.3 柔性數(shù)組的優(yōu)勢

若用“結(jié)構(gòu)體+指針”(如下代碼)實(shí)現(xiàn)類似功能,柔性數(shù)組有兩大核心優(yōu)勢:

// 對比方案:結(jié)構(gòu)體+指針
typedef struct st_type {
    int i;
    int *p_a; // 指針指向動態(tài)內(nèi)存
} type_a;
  • 優(yōu)勢1:方便內(nèi)存釋放:柔性數(shù)組只需free(p)一次釋放所有內(nèi)存;而“結(jié)構(gòu)體+指針”需先釋放p->p_a,再釋放p,若用戶遺漏釋放p->p_a,會造成內(nèi)存泄漏。
  • 優(yōu)勢2:提升訪問效率:柔性數(shù)組與結(jié)構(gòu)體在同一塊連續(xù)內(nèi)存中,CPU緩存命中率更高;而“結(jié)構(gòu)體+指針”的內(nèi)存是離散的(結(jié)構(gòu)體在一塊內(nèi)存,指針指向另一塊內(nèi)存),訪問時需兩次尋址,效率更低。

六、C/C++程序內(nèi)存區(qū)域劃分:從內(nèi)核到棧區(qū)

理解內(nèi)存區(qū)域是掌握動態(tài)內(nèi)存的基礎(chǔ),C/C++程序的內(nèi)存空間從高到低分為5個區(qū)域:

內(nèi)存區(qū)域存儲內(nèi)容生命周期管理方式
內(nèi)核空間操作系統(tǒng)內(nèi)核代碼/數(shù)據(jù)系統(tǒng)運(yùn)行期間操作系統(tǒng)管理
棧區(qū)(向下增長)局部變量、函數(shù)參數(shù)、返回值函數(shù)執(zhí)行期間編譯器自動分配/釋放
內(nèi)存映射段動態(tài)庫、文件映射隨進(jìn)程/庫加載/卸載系統(tǒng)管理
堆區(qū)(向上增長)動態(tài)內(nèi)存(malloc/calloc等)程序員分配/釋放(或程序結(jié)束后OS回收)程序員手動管理
數(shù)據(jù)段(靜態(tài)區(qū))全局變量、靜態(tài)變量(static程序運(yùn)行期間程序結(jié)束后系統(tǒng)釋放
代碼段函數(shù)二進(jìn)制代碼、只讀常量(如字符串字面量)程序運(yùn)行期間只讀,系統(tǒng)管理

關(guān)鍵區(qū)分:堆區(qū)與棧區(qū)的差異是高頻考點(diǎn)——棧區(qū)內(nèi)存自動管理,大小有限(通常幾MB)。堆區(qū)內(nèi)存手動管理,大小可至GB級,是動態(tài)內(nèi)存的核心區(qū)域。

掌握動態(tài)內(nèi)存管理,不僅能解決實(shí)際開發(fā)中的靈活內(nèi)存需求,更能深入理解C語言的內(nèi)存模型,為后續(xù)底層開發(fā)(如操作系統(tǒng)、嵌入式)打下堅(jiān)實(shí)基礎(chǔ)。

至此,我們已梳理完“動態(tài)內(nèi)存管理”的全部內(nèi)容了。最后我們在文末來進(jìn)行一個投個票,告訴我你對哪部分內(nèi)容最感興趣、收獲最大,也歡迎在評論區(qū)聊聊你的學(xué)習(xí)感受。

總結(jié)

到此這篇關(guān)于C語言動態(tài)內(nèi)存管理的文章就介紹到這了,更多相關(guān)C語言動態(tài)內(nèi)存管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于C語言sprintf函數(shù)的深入理解

    基于C語言sprintf函數(shù)的深入理解

    本篇文章是對C語言中的sprintf函數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++異常捕捉與處理的深入講解

    C++異常捕捉與處理的深入講解

    這篇文章主要給你大家介紹了關(guān)于C++異常捕捉與處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • C++ Log日志類輕量級支持格式化輸出變量實(shí)現(xiàn)代碼

    C++ Log日志類輕量級支持格式化輸出變量實(shí)現(xiàn)代碼

    這篇文章主要介紹了C++ Log日志類輕量級支持格式化輸出變量實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2019-04-04
  • 基于C語言實(shí)現(xiàn)http下載器

    基于C語言實(shí)現(xiàn)http下載器

    做OTA升級功能時,我們能直接拿到的往往只是升級包的鏈接,需要我們自己去下載,這時候就需要用到http下載器。本文將利用C語言實(shí)現(xiàn)簡單的http下載器,感興趣的可以了解一下
    2022-12-12
  • C語言全面梳理文件操作方法

    C語言全面梳理文件操作方法

    這篇文章主要為大家詳細(xì)介紹了C語言的文件操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-05-05
  • C++?QT實(shí)現(xiàn)獲取本機(jī)網(wǎng)卡信息

    C++?QT實(shí)現(xiàn)獲取本機(jī)網(wǎng)卡信息

    這篇文章主要為大家詳細(xì)介紹了如何利用qt自帶的QNetworkInterface接口以及c++Iphlpapi庫,獲取當(dāng)前windows電腦下的網(wǎng)卡信息,需要的可以參考下
    2024-01-01
  • C語言中的回調(diào)函數(shù)實(shí)例

    C語言中的回調(diào)函數(shù)實(shí)例

    如果函數(shù)A的指針作為函數(shù)B的參數(shù),在函數(shù)B中利用該指針調(diào)用函數(shù)A,則此時的A就是回調(diào)函數(shù)。
    2014-04-04
  • c語言實(shí)現(xiàn)的幾種常用排序算法

    c語言實(shí)現(xiàn)的幾種常用排序算法

    C,語言常用的排序方法有很多種。比如說冒泡排序,直接交換排序,直接選擇排序,直接插入排序,二分插入排序,快速排序,歸并排序等等,下面這篇文章主要給大家介紹了關(guān)于c語言實(shí)現(xiàn)幾種常用的排序算法,需要的朋友可以參考下
    2021-06-06
  • C++字符數(shù)組的輸入輸出和字符串結(jié)束標(biāo)志使用講解

    C++字符數(shù)組的輸入輸出和字符串結(jié)束標(biāo)志使用講解

    這篇文章主要介紹了C++字符數(shù)組的輸入輸出和符串結(jié)束標(biāo)志使用講解,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • Windows進(jìn)程崩潰問題的定位方法

    Windows進(jìn)程崩潰問題的定位方法

    這篇文章主要介紹了Windows進(jìn)程崩潰問題的定位方法,本文使用nstd工具進(jìn)行進(jìn)程崩潰時內(nèi)存和堆棧轉(zhuǎn)儲的方法查明問題所在,需要的朋友可以參考下
    2015-04-04

最新評論

甘泉县| 银川市| 邯郸县| 鄯善县| 金华市| 杭州市| 长宁区| 沁水县| 鹤峰县| 民勤县| 定南县| 红桥区| 兴和县| 崇仁县| 邓州市| 金沙县| 霸州市| 灵武市| 新河县| 宁武县| 晋城| 肥东县| 新田县| 蓝山县| 常德市| 南岸区| 宝鸡市| 瓮安县| 昭平县| 商水县| 惠州市| 曲阜市| 双牌县| 普陀区| 乌拉特后旗| 通许县| 无锡市| 兰溪市| 平阴县| 永宁县| 漳州市|