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

C語言中的內(nèi)存管理詳情

 更新時間:2022年05月12日 09:12:18   作者:??編程學(xué)習(xí)網(wǎng)????  
這篇文章主要介紹了C語言中的內(nèi)存管理詳情,手工申請內(nèi)存使用malloc展開全文內(nèi)容,具有一定的參考價值,需要的小伙伴可以參考一下

內(nèi)容提要:

大家寫C程序時,手工申請過內(nèi)存嗎?每次需要存儲空間時都向操作系統(tǒng)申請嗎?使用完申請到的內(nèi)存后有把它還給操作系統(tǒng)嗎?遇到過“段錯誤”嗎?本文的主題和這一串問題有很大的關(guān)系。

1.malloc

手工申請內(nèi)存使用malloc。先看一段例程。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *say_hi();

int main(int argc, char *argv[])
{
   char *str = say_hi();
   printf("str = %s\n", str);
   free(str);
   return 0;
}

char *say_hi()
{
   char *ptr = (char *)malloc(100);
   char *str = "how are you?";
   strcpy(ptr, str);
  
   return ptr;
}

執(zhí)行結(jié)果如下:

[root@localhost compiler-basic]# gcc -o t t.c -g -m32
[root@localhost compiler-basic]# ./t
str = how are you?

把修改成:

char *say_hi()
{
   char *ptr = (char *)malloc(100);
   char *str = "how are you?";
   strcpy(ptr, str);
  
   return str;
}

執(zhí)行結(jié)果如下:

[root@localhost compiler-basic]# gcc -o t t.c -g -m32
[root@localhost compiler-basic]# ./t
str = how are you?
Segmentation fault (core dumped)

在第二版say_hi中,返回值是str,我們看到,打印出的數(shù)據(jù)是how are you?。然而,這只是偶爾正確。C語言的函數(shù)并不能總是正確返回非數(shù)字類型的局部變量的值。在程序變得復(fù)雜后,保不準(zhǔn)某個時候這樣的函數(shù)就不能返回預(yù)期數(shù)據(jù)。

Segmentation fault (core dumped),這又是怎么回事呢?str不是malloc申請到的內(nèi)存空間,用free釋放它導(dǎo)致錯誤。

2.內(nèi)存泄露

用malloc申請了內(nèi)存空間卻不用free釋放,會造成內(nèi)存泄露。

在前面的第二版say_hi中,ptr指向的內(nèi)存空間就被泄露了。在程序員看來,執(zhí)行完say_hi后,ptr指向的內(nèi)存就沒有價值了;由于沒有正確地釋放它,操作系統(tǒng)認(rèn)為它仍然在使用中,當(dāng)其他進(jìn)程申請內(nèi)存時,不會把這片內(nèi)存回收重新分配。

像這樣的內(nèi)存泄露越來越多,會一直多到耗盡所有的內(nèi)存。但實(shí)際上,那些被泄露的內(nèi)存是完全應(yīng)該被回收再使用的。

內(nèi)存泄露后,會成為操作系統(tǒng)和程序員都無法掌控的內(nèi)存。

我們在手工申請內(nèi)存、使用完畢之后,一定要釋放內(nèi)存。malloc和free猶如一對連體嬰兒,總是一起使用。

3.內(nèi)存池

前面的例子,在say_hi使用malloc,在main使用free。連體嬰兒卻只能出現(xiàn)在兩個函數(shù)中,這很危險(xiǎn)。一不留神,就會忘記釋放內(nèi)存。

每次申請內(nèi)存都使用malloc,需要陷入內(nèi)核,性能開銷很大。

有朋友會說,我只是個小菜鳥,暫時還不需要考慮性能開銷,只要我寫的程序能跑就行。

哈哈,你并不是第一個這么想的人,我也是這樣想的,所以我不厭其煩地、勤快地多次使用malloc。終于,在昨天,我遇到了非常煩人的段錯誤。

段錯誤發(fā)生在malloc中,導(dǎo)致錯誤的函數(shù)調(diào)用鏈不同,在測試數(shù)據(jù)中隨便加幾個字符后錯誤又消失。斷點(diǎn)調(diào)試不管用,在前幾十次執(zhí)行malloc沒有段錯誤,在后面幾十次中的某一次執(zhí)行malloc時才出現(xiàn)段錯誤。段錯誤出現(xiàn)在內(nèi)核中。內(nèi)核是不會有問題的,即使問題指向內(nèi)核,那一定是向內(nèi)核提供了錯誤的輸入數(shù)據(jù)。

在束手無策快要絕望之前,我把原始的malloc換成了向內(nèi)存池申請內(nèi)存。先前發(fā)生的奇怪錯誤,再也沒有出現(xiàn)了。

4.理論

內(nèi)存池,是使用malloc申請的一段內(nèi)存;進(jìn)程需要內(nèi)存空間時,從這段內(nèi)存中拿一塊去用;當(dāng)這段內(nèi)存被用完后,再使用malloc申請一段新內(nèi)存;像這樣重復(fù)這個過程。

很容易發(fā)現(xiàn),內(nèi)存池減少了使用malloc的次數(shù);在進(jìn)程結(jié)束前,程序員能方便地一次性釋放這些內(nèi)存。

5.代碼數(shù)據(jù)結(jié)構(gòu)

struct mblock{
   char *begin;
   char *avail;
   char *end;
};

typedef struct heap{
  struct mblock *last;
   struct mblock head;
} *Heap;

#define HEAP(hp) struct heap hp = { &hp.head }
Heap CurrentHeap;
struct heap ProgrameHeap;
int HeapAlloc(Heap hp, int size);
void *do_malloc(int size);

6.代碼

char *HeapAlloc(Heap hp, int size){
   struct mblock *blk = NULL;
   blk = hp->last;
  
   while(size > blk->end - blk->avail){
       int m = 4096 + sizeof(struct mblock) + size;
       blk->next = malloc(m);
       blk = blk->next;
       if(blk == NULL){
           printf("內(nèi)存耗盡\n");
           exit(-1);
        }
       blk->begin = blk->avail = (char *)(blk + 1);
       blk->end = (char *)blk + m;
       hp->last = blk;
    }
  
   blk->avail += size;
  
   return blk->avail - size;
}

void *do_malloc(int size){
  CurrentHeap = HEAP(ProgrameHeap);
   void *p = HeapAlloc(CurrentHeap, size);
   memset(p, 0, size);
  
   return 0;
}

要申請內(nèi)存的時候,原來是使用malloc,現(xiàn)在,我們有了上面的這套內(nèi)存管理機(jī)制后,就使用do_malloc來申請內(nèi)存。

**解說
HEAP**
這個宏把heap的第一個成員last的值設(shè)置成第二個成員head的內(nèi)存地址。

要熟悉這種{ &hp.head }初始化結(jié)構(gòu)體的語法。

7.blk->begin

blk->begin = blk->avail = (char *)(blk + 1);

先看blk + 1。它表示,在blk的基礎(chǔ)上,往后移動sizeof(struct mblock)個字節(jié)。指針的加減就是這么計(jì)算的。

blk指向一段m個字節(jié)的內(nèi)存空間,這段內(nèi)存空間的前sizeof(struct mblock)個字節(jié)存儲一個mblock結(jié)構(gòu)。怎么理解這個結(jié)構(gòu)?它是這段內(nèi)存空間的元數(shù)據(jù)。了解文件系統(tǒng)的實(shí)現(xiàn)機(jī)制的朋友會很容易理解這一點(diǎn)。

從元數(shù)據(jù)中,獲取begin、avail、end。

如果把blk->begin做如下修改。

blk->begin = blk->avail = (char *)(blk);

怎么樣?我們來推演一番。

  • 第一次執(zhí)行HeapAlloc,申請了一段內(nèi)存,這段內(nèi)存的前面是元數(shù)據(jù);返回給進(jìn)程的是這段內(nèi)存的開始地址,也就是元數(shù)據(jù)的開始地址。
  • 執(zhí)行memset(p, 0, size);,元數(shù)據(jù)被擦除。
  • 再次執(zhí)行HeapAlloc,元數(shù)據(jù)end、avail不再是前一次執(zhí)行HeapAlloc后設(shè)置的值,無法知曉內(nèi)存池是否還有內(nèi)存可以分配;已經(jīng)亂套了。

blk->end最后一個問題,理解下面的代碼。

blk->end = (char *)blk + m;

blk->end表示新申請的這片內(nèi)存的末尾地址。

末尾地址等于初始地址加上這片內(nèi)存的長度??纯瓷厦娴拇a是不是這個意思。

(char

)blk + m就是這個意思。而(char

)(blk + m)就不是這個意思。

blk是這片內(nèi)存的第一個字節(jié),(char *)blk + m-1是這片內(nèi)存的最后一個字節(jié)。

8.總結(jié)

每個內(nèi)存池的開頭都有一個mblock,存儲這個內(nèi)存池的元數(shù)據(jù)(begin、avail、end、next)。進(jìn)程需要內(nèi)存時,先向內(nèi)存池申請。當(dāng)前內(nèi)存池容量不夠時,再向系統(tǒng)申請一個內(nèi)存池。把這個內(nèi)存池連接到前一個內(nèi)存池的元數(shù)據(jù)的next上。

一個內(nèi)存池耗盡后,并非全部空間都被使用了。沒有被利用的空間,在當(dāng)前機(jī)制下,被浪費(fèi)了。以后再找機(jī)會優(yōu)化。

所有的內(nèi)存池構(gòu)成一個單鏈表。當(dāng)進(jìn)程完成它的功能后,在結(jié)束前,遍歷這個單鏈表,從元數(shù)據(jù)中獲取begin,然后調(diào)用free(begin)就能釋放所有的內(nèi)存。

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

相關(guān)文章

  • C++多線程編程超詳解

    C++多線程編程超詳解

    本文給大家介紹的是C++多線程編程,由于C++本身沒有多線程機(jī)制,在windows下我們使用調(diào)用SDK win32 api來實(shí)現(xiàn),示例都很簡單,講解的也很詳細(xì),推薦給大家
    2021-09-09
  • Qt超時鎖屏的實(shí)現(xiàn)示例

    Qt超時鎖屏的實(shí)現(xiàn)示例

    本文主要介紹了Qt超時鎖屏的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C++ 中將一維數(shù)組轉(zhuǎn)成多維的三種方式示例詳解

    C++ 中將一維數(shù)組轉(zhuǎn)成多維的三種方式示例詳解

    這篇文章主要介紹了C++ 中將一維數(shù)組轉(zhuǎn)成多維的三種方式,每種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-12-12
  • 淺談C++ Explicit Constructors(顯式構(gòu)造函數(shù))

    淺談C++ Explicit Constructors(顯式構(gòu)造函數(shù))

    下面小編就為大家?guī)硪黄獪\談C++ Explicit Constructors(顯式構(gòu)造函數(shù))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • C/C++仿華容道小游戲

    C/C++仿華容道小游戲

    這篇文章主要介紹了C/C++仿華容道小游戲的相關(guān)資料,模仿實(shí)現(xiàn)華容道游戲,感興趣的朋友可以參考一下
    2016-02-02
  • 正確理解C++的構(gòu)造函數(shù)和析構(gòu)函數(shù)

    正確理解C++的構(gòu)造函數(shù)和析構(gòu)函數(shù)

    在C++的學(xué)習(xí)中,可以把類當(dāng)作一個模具,類實(shí)例化出來的對象就是根據(jù)這個模具所產(chǎn)生的實(shí)體,對象看作是自己創(chuàng)建的一個新的數(shù)據(jù)類型。本文主要介紹了類對象通過拷貝函數(shù)進(jìn)行初始化,分析類對象的內(nèi)存模型,以及通過this指針實(shí)現(xiàn)更復(fù)雜的功能。最后介紹了析構(gòu)函數(shù)的基礎(chǔ)知識
    2021-06-06
  • C語言代碼實(shí)現(xiàn)掃雷小游戲

    C語言代碼實(shí)現(xiàn)掃雷小游戲

    這篇文章主要為大家詳細(xì)介紹了C語言代碼實(shí)現(xiàn)掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 淺談C++中replace()方法

    淺談C++中replace()方法

    C++編程語言中的string應(yīng)用方式多樣化,每一種應(yīng)用方式都能幫助我們提實(shí)現(xiàn)特定的功能需求。在這里我們將會為大家詳細(xì)介紹一下其中一個比較重要的用法,有關(guān)C++ replace()函數(shù)的應(yīng)用方式,需要的朋友可以參考下
    2015-11-11
  • C語言中的數(shù)組和指針匯編代碼分析實(shí)例

    C語言中的數(shù)組和指針匯編代碼分析實(shí)例

    這篇文章主要介紹了C語言中的數(shù)組和指針匯編代碼分析實(shí)例,本文用一則C語言例子來得到對應(yīng)的匯編代碼,并一一注解每句匯編代碼的含義,需要的朋友可以參考下
    2015-06-06
  • C++設(shè)計(jì)模式之建造者模式(Builder)

    C++設(shè)計(jì)模式之建造者模式(Builder)

    這篇文章主要介紹了C++設(shè)計(jì)模式之建造者模式Builder的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03

最新評論

子洲县| 临邑县| 衡水市| 临安市| 阳信县| 德庆县| 土默特右旗| 恩施市| 龙江县| 巴中市| 安平县| 德安县| 平阴县| 元氏县| 莲花县| 盐池县| 南溪县| 深圳市| 衡山县| 遵义市| 乌拉特后旗| 巢湖市| 蒙山县| 花莲县| 绍兴市| 绥滨县| 新晃| 金华市| 台前县| 张家界市| 呼图壁县| 禄劝| 和龙市| 潞西市| 八宿县| 顺义区| 同德县| 浪卡子县| 凭祥市| 株洲县| 林西县|