程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#堆棧原理(我有兩個例子測試你到底會不會),

C#堆棧原理(我有兩個例子測試你到底會不會),

編輯:C#入門知識

C#堆棧原理(我有兩個例子測試你到底會不會),


背景

  • 上次寫了一篇文章關於try finnally的一些疑問(被我用windows live覆蓋了,草),後來經過大神們解釋,我明白了在我理解了try、finnally運行原理後,還欠缺的就是關於值類型引用類型在內存中的存儲問題。

  • 我仔細拜讀了一些大神們的文章,主要的參考點是一個系列文章《譯文---C#堆VS棧》,裡面詳細解釋了堆棧的要點,基礎差的禽獸們就別看我這個總結了。

    什麼是堆棧

  • 堆棧在內存中可以理解為兩個不同的容器。

  • 棧:棧就是一個先入後出的存儲空間,必須按照順序依次執行。比如一筒羽毛球,只有一個出入口,如果想取出來球,只能先把最後塞入的那個球拿出來才能繼續拿其他的球。

  • 堆:堆也是一個按序排列的存儲空間,不同的是,堆裡面的數據可以隨便取。比如圖書館裡的書都是按照一定順序排列的,但是取書的時候,可以直接拿出來自己需要的書籍而不用管其他的書籍。

  •  

    數據在堆棧中的存儲位置

  • 在堆棧上保存的數據分為四種:值類型、引用類型、指針、指令。

  • 值類型重點關注struct,因為其容易被認為成引用類型,而且其存儲的數據有時候很大,傳值時最好用ref;

  • 引用類型關注string,因為其雖然是引用類型,但是表現上是值類型,即通過形參傳入的string數據的操作不會影響實參。

  • 指針通常不會由我們顯示的使用,它們由公共語言運行時(CLR)來管理。指針(或引用)是不同於引用類型的,是因為當我們說某個事物是一個引用類型時就意味著我們是通過指針來訪問它的。指針是一塊內存空間,而它指向另一個內存空間。就像棧和堆一樣,指針也同樣要占用內存空間,但它的值是一個內存地址或者為空。(我復制的,我也不太懂)

  • 指令,在我參考的文檔裡好像並沒有提到,我理解的是,當JIT編譯器把方法的IL語句變為機器語言後,會將其存入到一個動態分配的內存空間,然後將地址返回給CLR的一個內部數據結構表(此表記錄一個類裡面的所有方法的地址),因為指令相當於值類型,具體存儲位置可以參考值類型。

  •  

  • 類型存在位置的定律

       引用類型總是在堆上創建,然後將地址返回,地址的保存位置類似與值類型。

       值類型的存儲位置和聲明它的類型有關。當Main()執行的時候,會分配一個棧區,如果在Main()裡面聲明了值類型,那麼它就會保存在這個棧上;如果Main()先聲明了一個struct,而struct裡面聲明了值類型,那麼這個值類型也一樣保存在棧上,因為聲明這個值類型的類型即struct也在棧上;而如果聲明值類型的類型是一個引用類型,比如一個類,那麼這個值類型就會保存在堆上。

    賦值時的區別

    值類型賦值就是一種深拷貝,即將值的內容傳遞過去,且不會受相互操作的影響。

    引用類型賦值是一種淺拷貝,只是將引用類型的地址傳遞了過去,比如A = B,那麼A、B的操作都會相互影響。當然,string是一種特例,賦值或者傳參後不會相互影響。如果你想引用類型也能執行深拷貝,就要自己寫Clone()方法了。

    空間分配及回收

  • 堆和棧這裡的區別具體不解釋,可以查看相關文檔,這裡引用一位前輩的比喻來看出: 
       使用棧就象我們去飯館裡吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等准備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 

       使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。比喻很形象,說的很通俗易懂,不知道你是否有點收獲。

    舉例

  • 以下是我想的兩個例子,如果你都能答對且明白原理,則說明你至少理解了堆棧。

    1、引用類型賦值,修改某一個引用類型,會有什麼結果

           static void Main(string[] args)
            {
                var my = new MyInt();
                var you = my;
                you.index = 1;
                you = null;
                Console.WriteLine(my.index.ToString());
                Console.ReadLine();
            }
            public class MyInt
            {
                public int index;
            }
  • 結果是:1;而且沒有返回異常。(重點是為什麼沒發生異常)?

    2、引用類型傳遞和指針傳遞(ref代表傳遞的是指針)的區別

          static void Main(string[] args)
            {
                Program main = new Program();
                var my = new MyInt();
                main.CommonMethod(my);
                Console.WriteLine(my.index.ToString());
                main.RefMethod(ref my);
                Console.WriteLine(my.index.ToString());
           
                Console.ReadLine();
            }
    
            public void CommonMethod(MyInt myInt)
            {
                myInt.index = 1;
                myInt = null;
            }
    
            public void RefMethod(ref MyInt myInt)
            {
                myInt.index = 2;
                myInt = null;
            }
            public class MyInt
            {
                public int index;
            }


  • 結果是1,然後異常;(為什麼引用類型傳遞不異常,而指針傳遞異常)?


    疑問

  • 數據結構的棧和內存中的棧有什麼區別嗎?網上說數據結構的棧是一個理論,一個是實現方式,那麼數據結構中的隊列,在內存中有什麼實現呢?還是說網上的說法是錯誤的?

  • 希望大神們幫我解釋一下。

  •  

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved