從另外一個角度理解C\C++動態內存分配

之前在學C++ 的時候,一直不懂:動態內存分配的本質,或者更加深刻到底層的意義.雖說,動態內存分配就是,隨機在內存中分配一個地址,可是這句話包含的內容實在太多了,不去理解底層的話,不管如何也僅僅停留在表面階段,永遠提升不了.今天下午,忽然看到很久之前的QT5,基本半年沒研究過了,仍是之前沒事幹瞎作播放器的時候安裝的,因而來了興趣,想玩玩看.程序員

可是,我遇到了一個問題,就是始終不懂QT框架這麼設計的核心,或者從操做系統級別來說,它爲何提供這樣的方式?編程

請看代碼,最簡單的默認項目;微信

#include <QApplication>#include <MainWindow>



 int main(int argc, char *argv[])

 {

    QApplication a(argc, argv);

    MainWindow w;

    w.show();

    return a.exec();

 }

 

這是一個最簡單的程序:就是顯示一個窗口,恐怕不少人已經習覺得常了吧.架構

可是,我此時有問題要問:app

 

#include <QApplication>#include <MainWindow>



 int main(int argc, char *argv[])

 {

    QApplication a(argc, argv);

    MainWindow *w=new MainWindow;

    w->show();

    return a.exec();

 }

這個程序運行起來,和上面是徹底同樣的,可是請問:有什麼問題?也許不少人一口就說,沒問題啊,不就是和上面同樣嗎?框架

細心的人發現有不一樣異步

而後,咱們在看一下這個代碼:函數

#include <QApplication>#include <QPushButton>#include <mainwindow.h>#include <QDebug>int main(int argc, char *argv[])

{

    QApplication app(argc, argv);

    QPushButton bt1;

    QPushButton *bt2=new QPushButton;



    MainWindow w1;

    MainWindow *w2=new MainWindow;

    qDebug() << "bt1:%d " << &bt1;

    qDebug() << "bt2:%d " << bt2;



    qDebug() << "w1:%d " << &w1;

    qDebug() << "w2:%d " << w2;



    return app.exec();

}

 

咱們來測試一下內存分配結果:性能

 

注意:系統分配法和動態分配法地址長度不同!測試

一個用了直接定義法,一個用了指針寫法,問題就是:每一個人都能說:後面的是動態內存分配,前面的是操做系統分配.還有呢?動態內存分配究竟怎麼個分配法?

或許不少人學C++的時候,也是面對這兩種寫法 很懵逼,貌似同樣吧,可是本質又不同.一直在表面逗留,而深刻不到本質去.

我這裏,從更加底層開始分析.咱們首先從芯片結構來講起.有人以爲C++怎麼會和芯片結構有關係?別忘了,C++繼承了C的全部.

好了,開始說芯片:咱們從最簡單的51單片機提及.

下面是百科:

8051是MCS-51系列單片機的典型產品,咱們以這一表明性的機型進行系統的講解。

  8051單片機包含中央處理器、程序存儲器(ROM)、數據存儲器(RAM)、定時/計數器、並行接口、串行接口和中斷系統等幾大單元及數據總線、地址總線和控制總線等三大總線,如今咱們分別加以說明:

  · 中央處理器:

  中央處理器(CPU)是整個單片機的核心部件,是8位數據寬度的處理器,能處理8位二進制數據或代碼,CPU負責控制、指揮和調度整個單元系統協調的工做,完成運算和控制輸入輸出功能等操做。

  · 數據存儲器(RAM):

  8051內部有128個8位用戶數據存儲單元和128個專用寄存器單元,它們是統一編址的,專用寄存器只能用於存放控制指令數據,用戶只能訪問,而不能用於存放用戶數據,因此,用戶能使用的的RAM只有128個,可存放讀寫的數據,運算的中間結果或用戶定義的字型表。

  · 程序存儲器(ROM):

  8051共有4096個8位掩膜ROM,用於存放用戶程序,原始數據或表格。

  · 定時/計數器(ROM):

  8051有兩個16位的可編程定時/計數器,以實現定時或計數產生中斷用於控制程序轉向。

  · 並行輸入輸出(I/O)口:

  8051共有4組8位I/O口(P0、 P一、P2或P3),用於對外部數據的傳輸。

  · 全雙工串行口:

  8051內置一個全雙工串行通訊口,用於與其它設備間的串行數據傳送,該串行口既能夠用做異步通訊收發器,也能夠當同步移位器使用。

  · 中斷系統:

  8051具有較完善的中斷功能,有兩個外中斷、兩個定時/計數器中斷和一個串行中斷,可知足不一樣的控制要求,並具備2級的優先級別選擇。

  · 時鐘電路:

  8051內置最高頻率達12MHz的時鐘電路,用於產生整個單片機運行的脈衝時序,但8051單片機需外置振盪電容。

  · 時鐘電路:

  8051內置最高頻率達12MHz的時鐘電路,用於產生整個單片機運行的脈衝時序,但8051單片機需外置振盪電容。

  單片機的結構有兩種類型,一種是程序存儲器和數據存儲器分開的形式,即哈佛(Harvard)結構,另外一種是採用通用計算機普遍使用的程序存儲器與數據存儲器合二爲一的結構,即普林斯頓(Princeton)結構。INTEL的MCS-51系列單片機採用的是哈佛結構的形式,然後續產品16位的MCS-96系列單片機則採用普林斯頓結構。

注意:重點在這裏 

數據存儲器?程序存儲器?這是什麼?和C++動態呢誒村分配又有什麼關係?

那咱們就來抓一下頭緒.

下面是百科,關於程序區和數據區:

C語言可執行代碼結構

 名稱

內容

代碼段

 可執行代碼、字符串常量

數據段

 已初始化全局變量、已初始化全局靜態變量、局部靜態變量、常量數據

BSS段

 未初始化全局變量,未初始化全局靜態變量

 局部變量、函數參數

 動態內存分配


        通常狀況下,一個可執行二進制程序(更確切的說,在Linux操做系統下爲一個進程單元,在UC/OSII中被稱爲任務)在存儲(沒有調入到內存運行)時擁有3個部分,分別是代碼段(text)、數據段(data)和BSS段。這3個部分一塊兒組成了該可執行程序的文件。

        (1)代碼段(text segment):存放CPU執行的機器指令。一般代碼段是可共享的,這使得須要頻繁被執行的程序只須要在內存中擁有一份拷貝便可。代碼段也一般是隻讀的,這樣能夠防止其餘程序意外地修改其指令。另外,代碼段還規劃了局部數據所申請的內存空間信息。

        代碼段(code segment/text segment)一般是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經肯定,而且內存區域一般屬於只讀, 某些架構也容許代碼段爲可寫,即容許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。

        (2)數據段(data segment):或稱全局初始化數據段/靜態數據段(initialized data segment/data segment)。該段包含了在程序中明確被初始化的全局變量、靜態變量(包括全局靜態變量和局部靜態變量)和常量數據。

        (3)未初始化數據段:亦稱BSS(Block Started by Symbol)。該段存入的是全局未初始化變量、靜態未初始化變量。

        而當程序被加載到內存單元時,則須要另外兩個域:堆域和棧域。圖1-1所示爲可執行代碼存儲態和運行態的結構對照圖。一個正在運行的C程序佔用的內存區域分爲代碼段、初始化數據段、未初始化數據段(BSS)、堆、棧5個部分。

 

 

圖1-1  C語言可執行代碼結構

      (4)棧段(stack):存放函數的參數值、局部變量的值,以及在進行任務切換時存放當前任務的上下文內容。

      (5)堆段(heap):用於動態內存分配,即便用malloc/free系列函數來管理的內存空間。

     在將應用程序加載到內存空間執行時,操做系統負責代碼段、數據段和BSS段的加載,並將在內存中爲這些段分配空間。棧段亦由操做系統分配和管理,而不須要程序員顯示地管理;堆段由程序員本身管理,即顯示地申請和釋放空間。

    另外,可執行程序在運行時具備相應的程序屬性。在有操做系統支持時,這些屬性頁由操做系統管理和維護。

重點:堆棧!

先來看51單片機:

大概就長這樣,最簡單的微型計算機,它裏面包含了256個地址,每一個地址對應一個彙編指令,而後經過這些指令來進行運做.關於單片機的問題,能夠本身去看相關內容.

如今的疑問就是:什麼事堆棧?

對於單片機來說,能夠簡單的理解爲:片內,片外存儲.片內指的是,單片機的自身內存,很是小,片外值得是ROM,一班有8KB.能夠看出是很是小的.

爲何要扯着麼多:爲了你後面理解C++指針.

你們都知道,C語言的基礎數據類型int ,char,double......等等,這些數據類型,是內置的,也就是說,已經有他們的地址存在了,若是放在但聯機裏,那就是,預先在片內給他們一個地址.可是,咱們的自定義類型呢?好比結構體,自定義後,並非系統能夠知道的數據類型,因此要進行動態內存分理,一班會在程序區給它分配一個地址,看下面的代碼:

int a=6;//定義一個a,值6,此時它是在系統棧裏面的地址.

int *b=&a; //b拿到a的地址,此時b就能夠操做a所在的內存了

由於int是預約義的數據類型,因此,在操做系統棧區分配內存就很快,可是換作自定義的類型的話,就比較慢了:

看這個QT代碼:


 

#include <QApplication>#include <MainWindow>



 int main(int argc, char *argv[])

 {

    QApplication a(argc, argv);

    MainWindow *w=new MainWindow;

    w->show();

    return a.exec();

 }

此時MainWindow 是一個自定義類型,若是這樣寫,會在程序區申請空間,可是若是關閉窗口的時候,沒有釋放內存,會形成內存泄漏由於[w->show()]完了之後就沒有釋放操做了,爲了不,應該用操做系統棧的分配方法,這樣把窗口的操做控制權交給系統,能夠避免內存泄漏.

看了上面的例子,咱們來總結一下:

什麼是操做系統棧:就是一個內存區,這個區是操做系統使用的,負責系統使用變量地址的建立.

是麼是程序運行堆:application運行的時候使用的內存區域,好比QQ,微信等等,都是在程序運行區運行的.

咱們列舉了單片機的特性,單片機內存很小,爲了防止溢出,因此有時候要向片外申請地址,此時動態內存分配就派上用場了.

換言之,曾今幾十年前C語言剛出來的時候,機器的性能也就今天的51單片機的性能,爲了優化機器運行,因此要進行動態內存分配.

深刻理解指針系列,仍是要從最原來的含義開始提及,若是從硬件的內部進行研究的話,我以爲理解指針和動態內存分配也就不難了.

相關文章
相關標籤/搜索