1. 程式人生 > >c++各種型別變數的記憶體分配

c++各種型別變數的記憶體分配

                                                             VC ++ 6.0  編譯器編譯期儲存器分配模型(記憶體佈局)

                                                                                                                   ----轉載自網路

一、記憶體區域的劃分

      一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分:

     1)、棧區(Stack):由編譯器(Compiler)自動分配釋放,存放函式的引數值,區域性變的值等。其操作方式類似於資料結構中的棧。

     2)、堆區(Heap ):一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收。注意它與資料結構中的堆是兩回事,分配

             方式倒是類似於連結串列。

     3)、全域性區(靜態區)(static):全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全

             局變量和未初始化的靜態變數在相鄰的另一塊區域。程式結束後由系統釋放。

     4)、文字常量區:常量字串就是放在這裡的。程式結束後由系統釋放。

     5)、程式程式碼區:存放函式體的二進位制程式碼。

二、測試案例(原始碼與反彙編對照)

     2.1   測試案例原始碼與反彙編對照

             為了能夠形象地說明記憶體佈局模型,先來看一段Win32 Console Application程式碼(表3.1),其中,加粗文字(行最左端為行標號)

     為C原始碼,未加粗文字(行最左端為地址)為反彙編後的指令程式碼。看上去比較零亂,不過一定要耐住性子,後面的文字將基於此。

    

3.2   記憶體佈局圖

        對於該案例,以下幾幅圖形象地說明了第2節提到的記憶體5大區域。需要注意的是,圖中各區域的起始地址不是絕對的,不同的編譯環境可能不完全相同,這裡給出的只是一個典型示例。需要注意的是,不同區域地址編址方向也不同。

   

   

   

   

   

   

3、應用

     通過對第3節案例的理解,我們將對一些現象予以解釋。

3.1、變數初始化

        1)區域性變數將被分配在棧中,如果不初始化,則為不可預料值。編譯時,編譯器將丟擲一個編號為C4700警告錯誤(local variable '變數名' used without having been initialized)。

        表4.1程式碼測試了局部變數未初始化的情況。

      

   該測試的一個典型的輸出結果為:-858993460,同時,編譯時編譯器丟擲了一條警告錯誤。

   2)全域性變數如果不初始化,則預設為0,編譯時編譯器不提示“變數未初始化”。

        表4.2程式碼測試了全域性變數未初始化的情況。

  該測試的輸出結果為:0.

   3)全域性變數初始化為0與不初始化效果一樣。請留意表3.1第9行程式碼,即

  intinit_array_g1[10]={0};                       //初始化的全域性陣列1

      等效於:

      int   init_array_g1[10];                       //初始化的全域性陣列1

      當然,出於謹慎,我們還是建議在使用全域性變數前對其初始化。

   3.2 變數初始化對程式碼空間的影響

         本小節任然討論變數初始化問題,但出於重視,我們將其獨立成小節。現在看兩個測試案例。

         案例1:建立Win32 Console Application工程,工程名:Test1,程式碼如表4.3。

   編譯成Debug版本,察看Debug目錄下的Test1.exe可執行檔案大小,典型大小約184KB(約0.18MB)。

   案例2:建立Win32 Console Application工程,工程名:Test2,程式碼如表4.4。

   編譯成Debug版本,察看Debug目錄下的Test2.exe可執行檔案大小,典型大小約46MB。

   兩個案例唯一區別不過在於是用0還是1初始化 init_array_g1[]陣列第0個元素。生成的可執行檔案大小卻天壤之別。

   上面已經說過,對於全域性變數初始化為0與不初始化效果一樣。因此,這裡的Test1案例並沒有對全域性變數初始化。

   那麼全域性變數初始化於不初始化對程式碼空間又有什麼影響呢?

   我們知道,運行於基於馮·諾依曼體系結構系統上的程式,資料和程式是一起儲存了。因此,編譯時,編譯器會將全域性變數的初始化資料捆綁到最終生成的程式檔案中,而對於未初始化的全域性變數只是為其分配(指示)了儲存位置,不會將大量的0捆綁到程式中。

   現在再來看以上兩個案例。Test1實質上沒有初始化全域性變數,編譯時編譯器只是為了init_array_g1[]指出了將要使用的記憶體位置,而不發生 資料繫結。Test2則不同,它將init_array_g1[0]初始化為1,其它元素全部初始化為0,因此,編譯器將把 init_array_g1[]陣列的10000000個元素的初始化資料全部捆綁到最終的可執行檔案中,導致編譯後的檔案十分龐大。

    3.3   關於堆和棧

    由於歷史原因,我們習慣把堆和棧合在一起稱呼(堆疊),然而,在這裡我們要嚴格區分堆和棧的概念。

    例程中宣告的區域性變數被分配在棧中,而棧的大小是相當有限的(一、兩個兆),龐大的陣列可能使棧不夠用,造成執行期棧溢位(Overflow)錯誤(注意:不是編譯器錯誤),而堆的大小主要取決於系統可用記憶體和虛存的多少。下面來看幾個例子:

    案例3程式碼如表4.5所示:

   編譯該程式碼,沒有編譯期錯誤。執行時卻發生了執行期錯誤(提示Statck Overflow),因為棧空間不夠用。

   案例4,把案例3程式碼改一下,陣列定位為全域性變數,如表4.6所示:

   

     編譯該程式碼,沒有編譯期錯誤,也不發生執行期錯誤。因為全域性變數不是分配在棧中的(注意:也不在堆中),能用多大空間取決於系統可用記憶體和虛存的多少。

     對於案例3的問題還有一種方法可以解決:動態申請記憶體空間。

     動態申請的記憶體空間是在執行期分配的,一旦申請成功,將分配在堆中,因此,大小也是取決於系統可用記憶體和虛存的多少。

     案例5:把案例3程式碼用另一種方法改一下,如表4.7所示。

    案例5的記憶體空間在堆中。還有一點不同於案例4:案例4的記憶體空間是在編譯器分配的,而案例5的記憶體空間是在執行期分配的,有可能分配不到空間。

    3.4) 地址遞減編制方式

    或許其它資料中已經描述了“地址遞減”編址方式分配記憶體的概念,所謂“地址遞減“是指編譯器編譯程式時,按變數宣告先後,從可分配記憶體中從高地址向低地址分配記憶體。什麼意思?還是先來看一個例子。

    案例6是一個有邏輯錯誤的程式(表4.7所示),不妨稱其為“變態”程式。那麼它是如何BT的呢?

   

      這個程式沒有編譯器錯誤,但卻是一個死迴圈程序。我們想知道的是:它為什麼是個死迴圈,而不是其它什麼錯誤?通過以上文字對記憶體佈局的介紹,我們已經可以很容易解釋之。

      仿照第3節內容可以畫出記憶體佈局示意圖(如圖4.1所示,圖中起始地址只是一個典型情況)。

      注意,程序中引用了array[10]————陣列下標越界(VC++6.0編譯器可以檢查出顯示的下標越界,但是不檢查隱式的下標越界)。迴圈內部會將 所謂的array[10]置1,而從圖4.1可知,array[10]實質上就是i,導致程式最終死迴圈也就理所當然了。

      一切變得明朗起來,我們不僅解釋了程式中的問題,同時還明白了“地址遞減”編址方式並不神祕,它原來就是我們前面提到的棧記憶體區的編址方式。

相關推薦

c++各種型別變數記憶體分配

                                                             VC ++ 6.0  編譯器編譯期儲存器分配模型(記憶體佈局)                                                              

5.2-day02-C++/內聯/動態記憶體分配/引用/顯示型別轉換

九、 3.內聯 1)編譯器用函式的二進位制程式碼替換函式呼叫語句,減少函式呼叫的時間開銷。這種優化策略成為內聯。 2)頻繁呼叫的簡單函式適合內聯,而稀少呼叫的複雜函式不適合內聯。 3)遞迴函式無法內聯。 4)通過inline關鍵字,可以建議

C# 淺析值型別與引用型別記憶體分配

1、  值型別和引用型別的區別 1.值型別的資料儲存在記憶體的棧中;引用型別的資料儲存在記憶體的堆中,而記憶體單元中只存放堆中物件的地址。 2.     值型別存取速度快,引用型別存取速度慢。 3.     值型別表示實際資料,引用型別表示指向儲存在記憶體堆中的資料的指標

記憶體探尋1之——值型別和引用型別記憶體分配機制

String物件和值型別的記憶體分配機制:           同樣由前延伸,上上篇《由String型別分析,所產生的對引數傳遞之惑的解答》中,最後提及,如果將引用型別的按值傳遞和按引用傳遞,用託管堆表

C++ 模板類動態記憶體分配

總時間限制:  1000ms   記憶體限制:  65536kB // 在此處補充你的程式碼 描述 程式填空,輸出指定結果 #include <iostream> #include <string> #includ

面試題9——簡述CC++程式編譯的記憶體分配情況

一個C,C++程式編譯時記憶體分為5大儲存區:堆區,棧區,全域性區,文字常量區,程式程式碼區。 C,C++中記憶體分配方式可以分為三種: (1)從靜態儲存區域分配: 記憶體在程式編譯時就已經分配好,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static變數等。 (2)在棧上分

C/C++基本型別佔用記憶體總結

C / C ++型別佔用記憶體總結 ç語言 Ç程式碼32位註釋 /************************************************************************* > Desc

C/C++程式中的記憶體分配

有人說,一個學習過C語言的人,在看到程式碼時,看到的不是程式碼,而是一塊又一塊的記憶體,那麼一個由C/C++編譯的程式佔用的記憶體分為哪幾個部分呢? 1.棧區(Stack):由編譯器自動分配釋放,存放的是為執行函式而分配的區域性變數、函式的引數、返回的資料、返回的地址等等,它的操

C語言】動態記憶體分配小結

為什麼存在動態記憶體分配? 我們已經掌握的記憶體開闢方式有: int val = 20;//在棧空間上開闢四個位元組 char arr[10];//在棧空間上開闢10個位元組的連續空間 但是上面開闢空間的方式有兩個特點: 1.空間開闢的大小是固定的 2.陣列在申明的時

淺析值型別與引用型別記憶體分配[轉載]

1、值型別和引用型別的區別 1.值型別的資料儲存在記憶體的棧中;引用型別的資料儲存在記憶體的堆中,而記憶體單元中只存放堆中物件的地址。 2.     值型別存取速度快,引用型別存取速度慢。 3.     值型別表示實際資料,引用型別表示指向儲存在記憶體堆中的資料的指標或引用

c#資料型別/變數/常量/計算機資料儲存(進位制之間的相互轉化)

一c#資料型別 注:值型別:值直接儲存在堆疊中(儲存空間小,呼叫速度快); 引用型別:值儲存在堆中(儲存空間大,呼叫速度慢) 1.值型別 a. 值型別變數可以直接分配給一個值.它們是從類System.ValueType中派生的. b.值型別直接儲存其值. c.值型別的例項通常是線上程棧上

mysql中各種型別變數的定義以及賦值使用

第一節 MySQL儲存過程中,定義變數有兩種方式: 1.使用set或select直接賦值,變數名以 @ 開頭. 例如:set @var=1; 可以在一個會話的任何地方宣告,作用域是整個會話,稱為使用者變數。 2.以 DECLARE 關鍵字宣

區域性變數記憶體分配時間、靜態變數的初始化時間、常量摺疊

記憶體分配時間 例1 區域性變數 void Test1() { ………… int buf[1024 * 1024]; ………… } 在windows下,由於棧限制為1M,上面程式碼可能會棧溢位,因此通過單步除錯我們可以知道,

c++ STL容器的記憶體分配

一.前言 在使用STL各類容器的時候,有時會出現迭代器失效,引用(指標)失效等情況的而發生,即使看似你的操作都是合法的情況下。 要了解問題的原因,我們就要了解C++中stl容器的記憶體分配策略。我們才知道在哪些操作下可能導致迭代器失效,引用(指標)失效。

c#引用型別變數在程式中的變化

這是我的第一篇部落格。 想到寫部落格,是因為記錄在紙上的東西,總怕哪天一丟,那些出過的錯誤又回來纏繞自己。 好了,那就步入正題吧! c#引用型別,具體請看別人寫的部落格吧 這裡有一個有趣的現象: class Program     {         static void

C語言】動態記憶體分配(malloc,realloc,calloc,free)的基本理解和區別

#include<Windows.h> #include<stdio.h> #include<malloc.h> int main() { int* p = NULL; printf("%x\n", p); p = (int*)malloc(sizeof(int)*

C語言之動態記憶體分配與釋放

一,堆記憶體 1,堆記憶體特點 堆記憶體可以存放任意型別的資料,但需要自己申請與釋放。 2,堆大小 堆大小,想像中的無窮大,但實際使用中,受限於實際記憶體的大小和記憶體是否有連續性。 二,堆記憶體的申請與釋放 1,malloc函式

C++的 new 代替 C 的 malloc 進行記憶體分配

例子: (int*)malloc(100*sizeof(int)) 是先取得int型別的位元組寬度,然後乘100計算後得到400,然後呼叫malloc,並將400傳遞給函式,分配400位元組的記憶體空間,但是返回的是虛指標,即純地址,該地址的型別是未知的,也即缺少資料寬度的

C/C++中結構體記憶體分配問題

有人問起當一個結構體裡面只有一個char型元素的時候,為什麼這個型別的變數也佔了四個位元組呢.下面我們來看一下: 對於一個結構體,裡面的元素排列順序不同,那麼分配的空間也會不同. 我們在VC6.0下看如下結構體: struct test{    char    item1; 

區域性變數記憶體分配詳解

#include <stdio.h> int main() { #if 1 int a; char b; int c; char d; a=1; b='2'; c=3; d=4; prin