c#堆與棧

1、在講堆棧以前,咱們先看看值類型和引用類型:c#

 

 

1,咱們看看值類型與引用類型的存儲方式:安全

引用類型:引用類型存儲在堆中。類型實例化的時候,會在堆中開闢一部分空間存儲類的實例。類對象的引用仍是存儲在棧中。數據結構

值類型:值類型老是分配在它聲明的地方,作爲局部變量時,存儲在棧上;類對象的字段時,則跟隨此類存儲在堆中。函數

什麼是堆什麼是棧咱們後面解釋。ui

 

                   圖1-1spa

 

2,咱們再看看引用類型與值類型的區別:.net

①引用類型和值類型都繼承自Systerm.Object類。不一樣之處,幾乎全部的引用類型都是直接從Systerm.Object繼承,而值類型則是繼承Systerm.Object的子類Systerm.ValueType類。3d

②咱們在給引用類型的變量賦值的時候,其實只是賦值了對象的引用;而給值類型變量賦值的時候是建立了一個副本(副本不明白?說通俗點,就是克隆了一個變量)。指針

文字不夠形象?咱們上代碼看看對象

                                       圖1-2

 

 3,咱們再看看引用類型和值類型的內存分配狀況(咱們對着代碼與圖看)

                                      圖1-3

 

 

                                   圖1-4

 

 看了圖1-3和圖1-4以後,大家可能問我了:你是怎麼知道變量在棧中的地址的。嘿嘿,等下教你用c#玩指針

從上面兩張圖咱們能夠看出:

①棧的結構是後進先出,也就是說:變量j的生命週期在變量s以前結束,變量s的生命週期在變量i以前結束,

②棧地址從高往底分配

③類型的引用也存儲在棧中

 

 

2、對於堆和棧的詳細介紹,咱們往下看。

 

1,有人總是搞不明白堆和棧的叫法。我來解釋下:

堆:在c裏面叫堆,在c#裏面其實叫託管堆。爲何叫託管堆,咱們往下看。

棧:就是堆棧,由於和堆一塊兒叫着彆扭,就簡稱棧了。

 

2,託管堆:

託管堆不一樣於堆,它是由CLR(公共語言運行庫(Common Language Runtime))管理,當堆中滿了以後,會自動清理堆中的垃圾。因此,作爲.net開發,咱們不須要關心內存釋放的問題。

 

3,有人總是搞不清楚內存堆棧與數據結構堆棧,咱們來看看什麼是內存堆棧,什麼是數據結構堆棧

①數據結構堆棧:是一種後進先出的數據結構,它是一個概念,圖4-1中能夠看出,棧是一種後進先出的數據結構。

②內存堆棧:存在內存中的兩個存儲區(堆區,棧區)。

      棧區:存放函數的參數、局部變量、返回數據等值,由編譯器自動釋放

      堆區:存放着引用類型的對象,由CLR釋放

 

 

 3、最後咱們用c#玩一玩指針

1,首先右鍵項目-->屬性-->生成-->勾選容許不安全代碼

 

2,在寫不安全代碼的時候須要加上unsafe

    //標記類
    unsafe public class Student
    {
        //標記字段
        unsafe int* pAge;
        //標記方法
        unsafe void getType(int* a)
        {
            //標記代碼段
            unsafe
            {
                int* pAbc;      //聲明指針語法
            }
        }
    }

  

3,指針的語法

            unsafe
            {
                int* pWidth, pHeight;
                double* pResult;
                byte*[] pByte;
                //&:表示「取地址」,並把一個值數據類型轉換爲指針,例如,int轉換爲*int。這個運算稱爲【尋址運算符】
                //*:表示「獲取地址內容」,把一個指針轉換爲一個值數據類型(例如:*float轉換爲float)。這個運算符被
                    //稱爲「間接尋址運算符」(有時稱「取消引用運算符」)
                int a = 10;//聲明一個值類型,給它賦值10
                int* pA, pB;//聲明2個指針
                pA = &a;//取出值類型a的地址,賦值給指針pA
                pB = pA;//把指針pA的地址賦值給pB
                *pB = 20;//獲取pB指向的地址內容,並賦值20
                Console.WriteLine(a);//輸出20
            }

  

4,將指針強制轉化爲整數類型(只能轉化爲uing、long、ulong類型)

int a = 10;
int* pA, pB;
pA = &a;
uint address = (uint)pA;//將指針地址強制轉換爲整數類型
pB = (int*)address;//將整數類型強制轉換爲指針
*pB = 20;//指針指向a
Console.WriteLine(a);//輸出20

  

5,指針類型的強制裝換

int b = 10;
int* pIa;
double* pDa;
pIa = &b;
pDa = (double*)pIa;//將int*強制轉換成double*

  

6,void指針(不指向任何數據類型的指針)

int c = 10;
int* pAA;
pAA = &c;
void* pVa = (void*)pAA;//轉換viod指針

  

7,指針算數的運算(不容許對void指針進行運算)

int d = 10;
int* pId;//若是地址爲100000
pId = &d;
pId++;//結果:100004(int類型的大小爲4個字節)

  

8,結構指針:指針成員訪問運算符

①指針不能只想任何引用類型
②結構裏面不能包含引用類型

MyStruct* pStruct;
MyStruct ms = new MyStruct();
pStruct = &ms;
//--取地址內容賦值爲10
(*pStruct).X = 10;
pStruct->X = 10;
//--取地址賦值給pIs
int* pIs1 = &(ms.X);
int* pIs2 = &(pStruct->X); 
//結構
struct MyStruct
{
        public int X = 1;
        public int Y = 2;
}

  

9,類成員指針

Person p = new Person();
fixed (int* pIp1 = &(p.X), pIp2 = &(p.Y))
{
}//pIp1和pIp2的生命週期
//類
class Person
{
     public int X;
     public int Y;
}

  

 

10,有趣的實驗

unsafe
{
//有趣的問題:爲何b的值改變了
//回答:由於i的地址指向了b
int a = 10;
int b = 20;
int* i;
i = &a;//將a的地址賦值給指針i
i -= 1;//將i的地址-1(至關於向下移動4個字節(int類型的大小爲4個字節))
*i = 30;//取出i指針指向的內容賦值爲30
Console.WriteLine(b);//輸出30
}
相關文章
相關標籤/搜索