1. 程式人生 > >C語言裡棧和堆的區別整理

C語言裡棧和堆的區別整理

這裡說的是C語言程式記憶體分配中的堆和棧。下面先談談C語言的記憶體管理:

可執行程式在儲存時(沒有調到記憶體)分為程式碼區(text)、資料區(data)和未初始化資料區(bss)3個部分。

(1)程式碼區(text segment)。存放CPU執行的機器指令(machine instructions)。通常,程式碼區是可共享的(即另外的執行程式可以呼叫它),因為對於頻繁被執行的程式,只需要在記憶體中有一份程式碼即可。程式碼區通常是隻讀的,使其只讀的原因是防止程式意外地修改它的指令。另外,程式碼區還規劃了局部變數的相關資訊。

(2)全域性初始化資料區/靜態資料區(initialized data segment/data segment)。該區包含了在程式中明確被初始化的全域性變數、靜態變數(包括全域性靜態變數和區域性靜態變數)和常量資料(如字串常量)。

(3)未初始化資料區(BSS區,uninitialized data segment),存入的是全域性未初始化變數。BSS區的資料在程式開始執行之前被核心初始化為0或者空指標(NULL)。


上圖表示可執行程式碼儲存時結構和執行時結構的對照圖。一個正在執行著的C編譯程式佔用的記憶體分為程式碼區、初始化資料區、未初始化資料區、堆區和棧區5個部分。

(1)程式碼區。程式碼區指令根據程式設計流程依次執行,對於順序指令,則只會執行一次(每個程序),如果反覆,則需要使用跳轉指令,如果進行遞迴,則需要藉助棧來實現。

程式碼區的指令中包括操作碼和要操作的物件(或物件地址引用)。如果是立即數(即具體的數值),將直接包含在程式碼中;如果是區域性資料,將在棧區

分配空間,然後引用該資料地址;如果是BSS區和資料區,在程式碼中同樣將引用該資料地址。

(2)全域性初始化資料區/靜態資料區。只初始化一次。

(全域性變數的值可以改變

#include <iostream>
using namespace std;
 
int a = 8;
int main(){
	a = 4;
	cout<<a;
}

輸出     4

(3)未初始化資料區(BSS)。在執行時改變其值。

(4)棧區。由編譯器自動分配釋放,存放函式的引數值、區域性變數的值等。其操作方式類似於資料結構中的棧。每當一個函式被呼叫,該函式返回地址和一些關於呼叫的資訊,比如某些暫存器的內容,被儲存到棧區。然後這個被呼叫的函式再為它的自動變數和臨時變數在棧區上分配空間,這就是C實現函式遞迴呼叫的方法。每執行一次遞迴函式呼叫,一個新的棧框架就會被使用,這樣這個新例項棧裡的變數就不會和該函式的另一個例項棧裡面的變數混淆。

(5)堆區(heap)。用於動態記憶體分配。堆在記憶體中位於BSS區和棧區之間。一般由程式設計師分配和釋放,若程式設計師不釋放,程式結束時有可能由OS回收。

之所以分成這麼多個區域,主要基於以下考慮:

一個程序在執行過程中,程式碼是根據流程依次執行的,只需要訪問一次,當然跳轉和遞迴有可能使程式碼執行多次,而資料一般都需要訪問多次,因此單獨開闢空間以方便訪問和節約空間。

臨時資料及需要再次使用的程式碼在執行時放入棧區中,生命週期短。

全域性資料和靜態資料有可能在整個程式執行過程中都需要訪問,因此單獨儲存管理。

堆區由使用者自由分配,以便管理。

下面是網上一個典型的例子來幫助理解C程式記憶體分配:

int a = 0;     //a在全域性已初始化資料區
char *p1;     //p1在BSS區(未初始化全域性變數)

void main()
{
     int b;     //b在棧區
     char s[] = "abc";    //s為陣列變數,儲存在棧區,“abc”為字串常量,儲存在已初始化資料區
     char *p1, p2;    //p1、p2在棧區   這裡我覺得p2不是指標型別,下面直接malloc有問題,用dev C顯示不是指標,VC編譯器下也不是指標型別
     char *p3 = "123456";    //123456\0在已初始化資料區,p3在棧區
     static int c = 0;    //c為全域性(靜態)資料,存在於已初始化資料區
     //另外,靜態資料會自動初始化
     p1 = (char *)malloc(10);   //分配得來的10個位元組的區域在堆區
     p2 = (char *)malloc(20);   //分配得來的20個位元組的區域在堆區
     free(p1);
     free(p2);
}

記憶體分配方式:

在C語言中,物件可以使用靜態或動態的方式分配記憶體空間。

靜態分配:編譯器在處理程式原始碼時分配。

動態分配:程式在執行時呼叫malloc庫函式申請分配。

靜態記憶體分配是在程式執行之前進行的,因而效率比較高,而動態記憶體分配則可以靈活處理資料。

靜態與動態記憶體分配的主要區別如下:

靜態物件是有名字的變數,可以直接對其進行操作;動態物件是沒有名字的變數,需要通過指標間接地對它進行操作。

注:這裡我的理解有     如果我們在程式中有寫malloc,它是靜態物件還是動態物件(這是針對“動態物件是沒有名字的變數”)

         解答:我的理解有問題,malloc出來的就是在堆中開闢記憶體空間,是沒有名字的。

         如:p1 = (char *)malloc(sizeof(int));            //此行程式碼分配了一個int型別大小的區域在堆區(物件),然後返回物件在記憶體中的地址,接著這個地址被用來初始化指標物件p1,對於動態分配的記憶體唯一的訪問方式是通過指標間接地訪問。

int *p;       //p中的地址所指向的內容
p;              //p這個變數的內容,這裡p存的是地址,則為地址
&p;           //取p的地址

靜態物件的分配與釋放由編譯器自動處理;動態物件的分配與釋放必須由程式設計師顯式地管理,它通過malloc()和free()兩個函式(C++中為new和delete運算子)來完成。

下面就來正式講講棧與堆的區別:

1、申請方式不同

2、管理方式不同。堆容易產生記憶體洩露。(這個就看程式設計師啦)

3、空間大小不同。

棧是向低地址擴充套件的資料結構,是一塊連續的記憶體區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩餘空間時,將提示溢位。因此,使用者能從棧獲得的空間較小。

堆是向高地址擴充套件的資料結構(它的生長方向與記憶體的生長方向相同),是不連續的記憶體區域。因為系統是用連結串列來儲存空閒記憶體地址的,且連結串列的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。

4、系統響應:

棧:只要棧的空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢位。

堆:作業系統有一個記錄空閒記憶體地址的連結串列,當系統收到程式的申請時,會遍歷該連結串列,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒連結串列中刪除,並將該結點的空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中的首地址處記錄本次分配的大小,這樣,程式碼中的free語句才能正確的釋放本記憶體空間。另外,找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒連結串列中。

對於堆來講,頻繁的malloc/free勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低。對於棧就不會存在這個問題。

5、增長方向不同

6、申請效率不同

堆的效率要低於棧。

相關推薦

C語言區別整理

這裡說的是C語言程式記憶體分配中的堆和棧。下面先談談C語言的記憶體管理: 可執行程式在儲存時(沒有調到記憶體)分為程式碼區(text)、資料區(data)和未初始化資料區(bss)3個部分。 (1)程式碼區(text segment)。存放CPU執行的機器指令(machi

C語言實現佇列(佇列的基本操作)

棧: 棧:棧(stack)又名堆疊,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。 特點:先進後出 stack.h #pragma once #include <stdio.h> #include <

關於C語言getcharscanf的思考

今天在做《C primer plus》的課後習題的時候,有這樣一道題: 編寫一個程式讀入一行輸入,然後反向列印該行。 您可以把輸入儲存在一個char陣列中;假定該行不超過255個字元。 回憶一下,您可以使用具有%c說明符的scanf()從輸入中一次讀入一個字元, 而且當您按

C#中的、值型別與引用型別、值引數、引用引數、輸出引數、引數陣列

程式執行時,資料必須儲存在記憶體中,一個數據需要多大的記憶體、儲存的位置、如何儲存依賴於該資料的資料型別。執行中的程式使用兩個記憶體區域來儲存資料:棧和堆。 棧:                 棧是一

C++ 類的例項化

#include "stdafx.h" #include <iostream> using namespace std; class Coordnate { public: Coord

c/c++專業知識講解」超詳細講解區別

預備知識—程式的記憶體分配 一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分   1、棧區(stack):由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其 操作方式類似於資料結構中的棧。 2、堆區(heap):一般由程式設計師分配釋放, 若程式

.net/c#中區別及程式碼在中的執行流程詳解

在.NET framework環境下,當我們的程式碼執行時,記憶體中有兩個地方用來儲存這些程式碼。假如你不曾瞭解,那就讓我來給你介紹棧(Stack)和堆(Heap)。棧和堆都用來幫助我們執行程式碼的,它們駐留在機器記憶體中,且包含所有程式碼執行所需要的資訊。 棧負責儲存我們的程式碼執行(或呼叫)路徑,而

Java 自增(++) C語言中自增的區別

%d 區別 但是 [] .cn cnblogs 微軟雅黑 自增 華麗 在Java、c語言等高級語言中自增和自減的作用基本一致,都是變量自身加一或減一。下面我只對自增進行說明,自減是類似的。 自增運算符(++),有兩種書寫形式,一個是在變量前: ++ num; 另一種

愛創課堂每日一題第二十六天-2017/9/28 區別

前端 前端學習 前端入門棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收。堆(數據結構):堆可以被看成是一棵樹,如:堆排序;棧(數據結構):一種先進後出的數據結構。愛

區的區別

初始 伸縮性 繼續 點菜 walk 時間 討論 而在 匯編代碼 轉載自http://blog.csdn.net/slj_win/article/details/8608436 堆和棧的區別一、預備知識—程序的內存分配一個由c/C++編譯的程序占用的內存分為以下幾個部分1、棧

Java中區別

分配內存 基本 變量 類型 名稱 垃圾回收器 函數 一個數 棧內存 在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。當在一段代碼塊定義一個變量時,Java就在棧中為這個變量分配內存空間,當超過變量的作用域後,Java會自動釋放掉為該變量

C語言區、

空間 bsp 動態 info cat malloc 分享圖片 code clu 一 局部變量存放在棧區中,函數調用結束後釋放內存空間。 #include "stdio.h"; #include "stdlib.h"; int *getNum(){ int i

Java中的記憶體分配以及區別

Java中的記憶體分配以及棧和堆的區別 (1)棧: 存放的是區域性變數 區域性變數:在方法定義中或者方法宣告上的變數都是區域性變數。 (2)堆: 存放的是所有new出來的東西 特點: a: 每一個new出來的東西都會為其分配一個地制值。 b: 每

C語言實現串列埠通訊知識點整理(四)】關於執行緒程序

轉載:https://www.cnblogs.com/fuchongjundream/p/3829508.html 因為在外部檔案中呼叫結構體沒有用extern修飾,導致獲取不到正確的值,一直糾結線上程上。現在大概總結執行緒和程序的特點: 概念 1、程序(process) 狹義定義:

面試題24——C++的引用C語言的指標有什麼區別

C++的引用和C語言的指標有以下區別: (1)引用必須初始化,但是不分配儲存空間。指標不宣告時初始化,在初始化的時候需要分配儲存空間。引用較比指標更加安全; (2)引用指向一塊特定的記憶體,不能被更改。不存在指向空值的引用,但是存在指向空值的指標。指標可指向任意一塊記憶體,可以改變所指的物件

C語言的小坑-之方法內的常量變數

char* fun1() {     char *a;     a = "c語言的那些小坑";     return a; } char* fun2() {     char a[128];     sprintf(a, "c語言的那些小坑");     return a;

C語言】definetypedef的區別

#define是 巨集定義命令,#define DINT int相當於將程式碼中的int可以寫為DINT,DINT等價於int。typedef int TINT; 是型別定義,TINT型別的變數就是int型別的變數。 1.typedef int TINT; 和#define

C語言結構體C++類的區別

1.C的結構體和C++結構體的區別 1.1 C的結構體內不允許有函式存在,C++允許有內部成員函式,且允許該函式是虛擬函式。所以C的結構體是沒有建構函式、解構函式、和this指標的。 1.2 C的結構體對內部成員變數的訪問許可權只能是public,而C++允許public,protec

C語言程式語言科技 c語言中的= = =有什麼區別?(精華篇)

一等賦,二等於。 C語言中,很多初學者經常會弄混 = 與 == 。我們從小學習數學時就知道“ = ”是 “等於”的意思,可是在C語言中,“ = ”就不是等於的意思了。 我寫了如下的C語言程式碼: 在上述C語言程式碼中,整型變數 a 的初始值是0。在兩個 if 語句判

c語言編譯過程標頭檔案<>與""的區別

編譯過程:   預處理--編譯--彙編--連結 預處理:用於將所有#include標頭檔案及#define等巨集定義替換成真正的內容,預處理後的得到的仍然是文字檔案,但體積會大很多。 編譯:將預處理之後的程式轉換成特定彙編程式碼的過程