全局變數、局部變數、靜態全局變數、靜態局部變數在記憶體里的區別

来源:http://www.cnblogs.com/BJXYD/archive/2016/10/14/5961801.html
-Advertisement-
Play Games

C語言 變數存儲 數據結構 嵌入式 ARM 靜態變數 局部變數 全局變數 單片機 記憶體分配 ...


一、程式的記憶體分配

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

1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變數的值等。其操作方式類似於數據結構中的棧。

2、堆區(heap) — 一般由程式員分配釋放, 若程式員不釋放,程式結束時可能由OS回收 。註意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表。

3、全局區(靜態區)(static)— 全局變數和靜態變數的存儲是放在一塊的,初始化的全局變數和靜態變數在一塊區域, 未初始化的全局變數和未初始化的靜態變數在相鄰的另一塊區域。程式結束後有系統釋放

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

5、程式代碼區 — 存放函數體的二進位代碼。

二、例子程式

//main.cpp

int a = 0; 全局初始化區

char *p1; 全局未初始化區

main()

{

int b;// 棧

char s[] = "abc"; //棧

char *p2; //棧

char *p3 = "123456"; 123456\0";//在常量區,p3在棧上。

static int c =0; //全局(靜態)初始化區

p1 = (char *)malloc(10);

p2 = (char *)malloc(20);

//分配得來得10和20位元組的區域就在堆區。

strcpy(p1, "123456"); //123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。

}

三、從作用域看:

全局變數具有全局作用域。全局變數只需在一個源文件中定義,就可以作用於所有的源文件。當然,其他不包含全局變數定義的源文件需要用extern 關鍵字再次聲明這個全局變數。

局部變數也只有局部作用域,它是自動對象(auto),它在程式運行期間不是一直存在,而是只在函數執行期間存在,函數的一次調用執行結束後,變數被撤銷,其所占用的記憶體也被收回。

靜態局部變數具有局部作用域,它只被初始化一次,自從第一次被初始化直到程式運行結束都一直存在,它和全局變數的區別在於全局變數對所有的函數都是可見的,而靜態局部變數只對定義自己的函數體始終可見。

靜態全局變數也具有全局作用域,它與全局變數的區別在於如果程式包含多個文件的話,它作用於定義它的文件里,不能作用到其它文件里,即被static關鍵字修飾過的變數具有文件作用域。這樣即使兩個不同的源文件都定義了相同名字的靜態全局變數,它們也是不同的變數。

從分配記憶體空間看:
全局變數,靜態局部變數,靜態全局變數都在靜態存儲區分配空間,而局部變數在棧里分配空間。

從以上分析可以看出, 把局部變數改變為靜態變數後是改變了它的存儲方式即改變了它的生存期。把全局變數改變為靜態變數後是改變了它的作用域,限制了它的使用範圍。因此static 這個說明符在不同的地方所起的作用是不同的。

四、總的來說就是:

1、生存周期不同
2、作用範圍不同
3、分配方式不同

----------------------------------------------

再來看下堆和棧的不同:

1、分配方式不同;
2、空間大小不同;
3、分配效率不同;
4、能否產生碎片不同;
5、生長方向不同;

1、分配方式不同

棧:
由系統自動分配。 例如,聲明在函數中一個局部變數 int b; 系統自動在棧中為b開闢空間
堆:
需要程式員自己申請,並指明大小,在c中malloc函數
如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = (char *)new(10);
但是註意p1、p2本身是在棧中的。

2、 空間大小不同

一般來講在32位系統下,堆記憶體可以達到4G的空間,從這個角度來看堆記憶體幾乎是沒有什麼限制的。但是對於棧來講,一般都是有一定的空間大小的,例如,在VC6下麵,預設的棧空間大小是1M。

3、分配效率

棧 是機器系統提供的數據結構,電腦會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執 行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的,例如為了分配一塊記憶體,庫函數會按照一定的演算法(具體的演算法可以參考 數據結構/操作系統)在堆記憶體中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於記憶體碎片太多),就有可能調用系統功能去增加程式數據段的 記憶體空間,這樣就有機會分到足夠大小的記憶體,然後進行返回。顯然,堆的效率比棧要低得多。

4、碎片問題

棧:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢出。
堆:首先應該知道操作系統有一個記錄空閑記憶體地址的鏈表,當系統收到程式的申請時,
會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程式,另外,對於大多數系統,會在這塊 記憶體空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本記憶體空間。另外,由於找到的堆結點的大小不一定正好等於申請的 大小,系統會自動的將多餘的那部分重新放入空閑鏈表中。

對於堆來講,頻繁的new/delete勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低。對於棧來講,則不會存在這個問題,因為棧是先 進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個記憶體塊從棧中間彈出,在他彈出之前,在他上面的後進的棧內容已經被彈出。

5、生長方向

對於堆來講,生長方向是向上的,也就是向著記憶體地址增加的方向;對於棧來講,它的生長方向是向下的,是向著記憶體地址減小的方向增長。


堆和棧相比,由於大量new/delete的使用,容易造成大量的記憶體碎片;由於沒有專門的系統支持,效率很低;由於可能引發用戶態和核心態的切換,記憶體 的申請,代價變得更加昂貴。所以棧在程式中是應用最廣泛的,就算是函數的調用也利用棧去完成,函數調用過程中的參數,返回地址,EBP和局部變數都採用棧 的方式存放。所以,我們推薦大家儘量用棧,而不是用堆。雖然棧有如此眾多的好處,但是由於和堆相比不是那麼靈活,有時候分配大量的記憶體空間,還是用堆好一 些。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在做學校的課程設計,java編程需要用到對話框彈出,第一反應是js中的alert和confirm,java的話瞬間懵,查閱學習總結如下,用以以後的學習 1.顯示一個錯誤對話框,該對話框顯示的 message 為 'alert':JOptionPane.showMessageDialog(null ...
  • *************************hibernate.cfg.xml <?xml version='1.0' encoding='utf-8'?><!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Confi ...
  • 之前是一直做asp.net開發的,Visual Studio 2012 用起來感覺是相當的舒服,調試功能也是相當好、相當智能,大大的提高了開發效率減少了程 序BUG的出現,而且直接安裝好就能用了,無需多餘的操作,非常方便。 由於這次要做PHP開發,為了搭建開發環境也是搜索了不少資料,選擇IDE也是參 ...
  • 雖然目前Java算不上前端開發的主力,但是作為Java入門基礎的一部分,學習Java的GUI編程還是有必要的,而且可以做出一些小且有趣的圖形程式來提高學習熱情。本篇學習總結均為一個Beginner的筆記與心得,如有描述不到或錯誤之處,敬請指正。 一個合格的Java Developer,不僅要掌握技術... ...
  • 一、Hibernate 二級緩存 1.Hibernate 二級緩存是 SessionFactory 級別的緩存。 2.二級緩存分為兩類: (1)Hibernate內置二級緩存 (2)外置緩存,可配置的,可插撥的,外置緩存中的數據是資料庫數據的複製。 3.二級緩存的併發訪問策略 (1)兩個併發的事務同 ...
  • sizeof關鍵字和strlen()標準函數都可以用來測試字元串的長度,但是兩者有很大的不同 sizeof只能在 本函數內 , 使用 和 不指定長度的字元數組 中才能測出字元串的真實長度,當然,包括 strlen()任何情況下都能正確的得出字元串的字面值大小 在 本函數內 ,sizeof測試 的結果 ...
  • ...
  • ``` include int main(int argc, const char argv[]) { int i=0,j=0; for(i=1;i ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...