locked
类数组和一般类型的数组在栈中和堆中是怎么存储的? RRS feed

  • 問題

  •  对于值类型存储 在栈中,引用类型存储在堆中我是理解的,但当一个类中的成员字段本身也是引用类型时,数据是怎样存储我我搞不清楚了。还有,对于一个结构体中,成员字段是怎样存储也搞不清楚。例如:

    情况一:
    class A{}
    class B
    {
    private A a;  //问题1)对于这个字段到底是存储在堆中还是栈中?
    public void Test(){}   //问题2) 对于这个成员方法是怎么存储的呢?
    }

    情况二:

    struct JGT
    {
      A a;//问题3::A是前面定义的类,照理说结构体是值类型,应该存储在栈中,但是它这个成员字段是引用类型,应该存储在哪呢???
    }

    情况一中.的A也是存储在堆上的.
    栈上指向堆上B的指针->B(这个在堆上),然后B中有个指针指向堆中A所在的地址

    情况二中.JGT被分配在栈上.同样内部有个指向堆中A所在内存的指针.

    问题四::对于一般定义的函数或者类或者结构体中的成员方法在内存中是怎么存储的??
    问题五::对于类中的静态变量或者静态方法,存储的方式是怎样的??

      public class Deck
      {
        private Card[] cards;  //声明类数组

        public Deck()
        {
          cards = new Card[52];  //创建52个对象
          for (int suitVal = 0; suitVal < 4; suitVal++)
          {
            for (int rankVal = 1; rankVal < 14; rankVal++)
            {
              cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal,(Rank)rankVal); //实例化52个对象,并且通过构造函数给suit和rank赋值,问题来了。。类不是引用类型的吗,这样赋值suit和rank不是都是一样的吗?
            }
          }
        }

    }

    问题六:类数组是怎么存储的啊?比如:cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal,(Rank)rankVal);其中Card是一个自定义的类!

    2011年1月14日 3:26

解答

  • 您好小冯:

    欢迎来到MSDN论坛。

    Class A 和 Class B 都是类型,他们被分配在堆(HEAP)中。在Main()方法调用时,会首先创建它们的对象a和b, 该对象的实例位于堆中,同时在栈中会有2个引用,分别指向分配在堆中的具体的对象a和b.堆中的对象会包含一个引用(指针),指向该对象所对应的类型信息(称之为MethodTable,也在堆中)。

    问题1:Class B中的这个字段 a 存储在堆中,但是这个a字段也仅仅是一个引用而已(指针,只占有4个字节),a会指向类A的某一个实例.
    问题2:Test()方法也存储在堆中,准确的说,该方法存放在MethodTable中.
    问题3:结构体JGT是一种类型,并且存储于堆中,而JGT存在一个对象a,该对象a指向一个Class A的实例.
    问题4:对于一般定义的函数或者类或者结构体中的成员方法都存储于堆中。
    问题5:静态变量和静态方法是被存储在堆中的。
    问题6:数组是引用类型,位于堆中,首先Card本身是一个引用类型,也存储在堆中。其次,通过创建一个card对象,在堆中被分配一块空间,用于存储52个元素的数据,但是此时仅仅分配了空间,并没有数据。经过2重循环,该地址空间依次被填充数据,但是堆地址空间还是原来那块堆的地址空间,只是地址空间中的值被填充了,不再是空的了。

    感谢您的参与。
    如果您还有任何问题,请随时告知我们。


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the C# Forum! If you have any feedback, please tell us.
    • 已提議為解答 Neddy Ren 2011年1月19日 2:17
    • 已標示為解答 Neddy Ren 2011年1月24日 9:49
    2011年1月18日 6:45

所有回覆

  •  对于值类型存储 在栈中,引用类型存储在堆中我是理解的,但当一个类中的成员字段本身也是引用类型时,数据是怎样存储我我搞不清楚了。还有,对于一个结构体中,成员字段是怎样存储也搞不清楚。例如:

    情况一:
    class A{}
    class B
    {
    private A a;  //问题1)对于这个字段到底是存储在堆中还是栈中?
    public void Test(){}   //问题2) 对于这个成员方法是怎么存储的呢?
    }

    情况二:

    struct JGT
    {
      A a;//问题3::A是前面定义的类,照理说结构体是值类型,应该存储在栈中,但是它这个成员字段是引用类型,应该存储在哪呢???
    }

    情况一中.的A也是存储在堆上的.
    栈上指向堆上B的指针->B(这个在堆上),然后B中有个指针指向堆中A所在的地址

    情况二中.JGT被分配在栈上.同样内部有个指向堆中A所在内存的指针.

    问题四::对于一般定义的函数或者类或者结构体中的成员方法在内存中是怎么存储的??
    问题五::对于类中的静态变量或者静态方法,存储的方式是怎样的??

      public class Deck
      {
        private Card[] cards;  //声明类数组

        public Deck()
        {
          cards = new Card[52];  //创建52个对象
          for (int suitVal = 0; suitVal < 4; suitVal++)
          {
            for (int rankVal = 1; rankVal < 14; rankVal++)
            {
              cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal,(Rank)rankVal); //实例化52个对象,并且通过构造函数给suit和rank赋值,问题来了。。类不是引用类型的吗,这样赋值suit和rank不是都是一样的吗?
            }
          }
        }

    }

    问题六:类数组是怎么存储的啊?比如:cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal,(Rank)rankVal);其中Card是一个自定义的类!

    • 已合併 Neddy Ren 2011年1月19日 2:16 the same question
    2011年1月16日 7:20
  • 情况一:
    这2个情况实际上还没有让代码存储起来。
    只有你声明了类型A,B才会有这个存储动作,因为A,B都是类,所以都回被存储在托管堆上。

    情况二:
    如果声明了结构体,那么结构体是在堆栈上,结构体的一个成员是存储在托管堆上(一个指针指过去)。

    方法是映射在虚拟函数表中

    静态类因为没有实例,且它的字段,方法都是静态的,所以,在程序加载的时候,它已经在堆上分配一个空间来存储其数据结构。所以,静态类的字段,无论是值类型还是引用类型,无论是静态和动态的,无论是只读的还是可读可写的,存储在堆上的。而它的方法,则映射存储在分配的虚拟函数表里,在调用方法的时候,CLR将该方法的原型复制到内存中运行。静态类的加载效率上比动态类(对象)的效率高。

    suitVal和rankVal值在这里被装箱成suit和rank对象,不是一样的。

    数组属于引用类型,在堆栈上存储一个地址引用,而真正的值存储在托管堆中的。

     

    这些实际上涉及到.NET框架的很多细节,建议找本类似的书看看(推荐:微软的.NET框架程序设计,清华版)。

     


    family as water
    2011年1月16日 11:08
  • 关于值类型、引用类型的问题已经在这里讨论过多次,我也做出过比较详细的解答。首先,值类型存储在栈上,引用类型存储在堆上,这个说法就是有问题的。

    引用类型的实例 (属性和域) 存储在托管堆上,且当前的执行栈会有一个指向该引用类型实例的指针。
    值类型的实例 (属性和域) 存储在分配它的地方,即,如果该值类型实例指向一个本地变量,则该值类型实例会被存储在当前执行栈上;如果该值类型实例被一个引用类型所包含,如一个类 A 上的实例属性 B (值类型),那么,该值类型的实例被存储在托管堆上 (在类 A 实例的托管地址空间内)。

    这里很难对这个东西做深度的解释,您可以搜索本论坛的相关话题获取更多信息,或者直接去看 CLR  via C#, Third Edition,第五章,Type Fundamentals。


    Mark Zhou
    2011年1月17日 10:39
  • I am sorry, I don't understand what is "堆中", "栈中", "静态变量", "赋值". Can u translate this into English?
    大家一齊探討、學習和研究,謝謝!
    Microsoft MVP, Microsoft Community Star(TW & HK), MCT,
    MCSD, MCAD, MCSE+I, MCDBA, MCDST, MCSA, MCTS, MCITP, MCPD
    2011年1月18日 3:47
  • 您好小冯:

    欢迎来到MSDN论坛。

    Class A 和 Class B 都是类型,他们被分配在堆(HEAP)中。在Main()方法调用时,会首先创建它们的对象a和b, 该对象的实例位于堆中,同时在栈中会有2个引用,分别指向分配在堆中的具体的对象a和b.堆中的对象会包含一个引用(指针),指向该对象所对应的类型信息(称之为MethodTable,也在堆中)。

    问题1:Class B中的这个字段 a 存储在堆中,但是这个a字段也仅仅是一个引用而已(指针,只占有4个字节),a会指向类A的某一个实例.
    问题2:Test()方法也存储在堆中,准确的说,该方法存放在MethodTable中.
    问题3:结构体JGT是一种类型,并且存储于堆中,而JGT存在一个对象a,该对象a指向一个Class A的实例.
    问题4:对于一般定义的函数或者类或者结构体中的成员方法都存储于堆中。
    问题5:静态变量和静态方法是被存储在堆中的。
    问题6:数组是引用类型,位于堆中,首先Card本身是一个引用类型,也存储在堆中。其次,通过创建一个card对象,在堆中被分配一块空间,用于存储52个元素的数据,但是此时仅仅分配了空间,并没有数据。经过2重循环,该地址空间依次被填充数据,但是堆地址空间还是原来那块堆的地址空间,只是地址空间中的值被填充了,不再是空的了。

    感谢您的参与。
    如果您还有任何问题,请随时告知我们。


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the C# Forum! If you have any feedback, please tell us.
    • 已提議為解答 Neddy Ren 2011年1月19日 2:17
    • 已標示為解答 Neddy Ren 2011年1月24日 9:49
    2011年1月18日 6:45
  • Hi Neddy Ren, Could you translate your reply into English? Then I could learn some Chinese terms to match the English terms, So that I could understand the question next time.


    大家一齊探討、學習和研究,謝謝!
    Microsoft MVP, Microsoft Community Star(TW & HK), MCT,
    MCSD, MCAD, MCSE+I, MCDBA, MCDST, MCSA, MCTS, MCITP, MCPD
    2011年1月18日 7:15
  • Hi Neddy Ren, Could you translate your reply into English? Then I could learn some Chinese terms to match the English terms, So that I could understand the question next time.


    大家一齊探討、學習和研究,謝謝!
    Microsoft MVP, Microsoft Community Star(TW & HK), MCT,
    MCSD, MCAD, MCSE+I, MCDBA, MCDST, MCSA, MCTS, MCITP, MCPD

    Sorry KenLin, My english is not good.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the C# Forum! If you have any feedback, please tell us.
    2011年1月19日 8:18
  • 谢谢大家的回答,对于类数组的存储我还是有一点点的疑问,对此我做了一个图,来表达我的理解程度!
    首先有一个Deck类  ,该类里面存储了52个指向Card类实例的引用(地址),这段话有点不太明白,“当执行cards = new Card[52];的时候此时仅仅分配了空间,经过2重循环,该地址空间依次被填充数据,但是堆地址空间还是原来那块堆的地址空间,只是地址空间中的值被填充了,不再是空的了”能不能用上面的图解释解释啊?真是谢谢大家了。
    2011年1月19日 13:14
  • 你好,小冯:

    您的图片我们无法看到,请把图片上传到你的SkyDrive上,再提取你的图片地址给我们。

    谢谢您的谅解!


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the C# Forum! If you have any feedback, please tell us.
    2011年1月20日 7:34
  • http://user.qzone.qq.com/249887175/photo/85c7a30c-c908-407a-ba02-a9b335ef7072/M1q2pPTVH0U1iWnlCKTkq4czF2Yn0ykAAA!!/

    这是照片的地址

     

    谢谢大家的回答,对于类数组的存储我还是有一点点的疑问,对此我做了一个图,来表达我的理解程度!
    首先有一个Deck类  ,该类里面存储了52个指向Card类实例的引用(地址),这段话有点不太明白,“当执行cards = new Card[52];的时候此时仅仅分配了空间,经过2重循环,该地址空间依次被填充数据,但是堆地址空间还是原来那块堆的地址空间,只是地址空间中的值被填充了,不再是空的了”能不能用上面的图解释解释啊?真是谢谢大家了。
    2011年1月20日 10:27
  • I am sorry, I don't understand what is "堆中", "栈中", "静态变量", "赋值". Can u translate this into English?
    大家一齊探討、學習和研究,謝謝!
    Microsoft MVP, Microsoft Community Star(TW & HK), MCT,
    MCSD, MCAD, MCSE+I, MCDBA, MCDST, MCSA, MCTS, MCITP, MCPD

    I try to translate them into English.

    "堆" means "heap" or "managed heap" in managed language like c#

    "栈" or "堆栈" means "stack"

    "静态变量" means "static variant"

    "赋值" means "int a = 0 称之为 把0赋值 (=)给变量a"

    2011年1月20日 15:24
  • 您好,我想特别讨论一下第三点。

    结构体(struct)是值类型,如果它被声明为局部变量那么是在栈上分配;如果被声明为成员变量那么将在堆上分配。

    在您举的例子中,结构体内又引用了一个引用类型,那么这个结构体则包含了一个指向引用实例的指针。如果结构体分配在栈上这个指针地址则在栈上,如果分配到堆上,那么这个指针地址在堆上。

    另,个人觉得这只能作为一个例子学习用,作为结构体类型,本身就包含了“轻量”的含义,不建议在设计中在结构体中引用引用类型。

    2011年1月20日 15:36
  • http://user.qzone.qq.com/249887175/photo/85c7a30c-c908-407a-ba02-a9b335ef7072/M1q2pPTVH0U1iWnlCKTkq4czF2Yn0ykAAA!!/

    这是照片的地址

     

    谢谢大家的回答,对于类数组的存储我还是有一点点的疑问,对此我做了一个图,来表达我的理解程度!
    首先有一个Deck类  ,该类里面存储了52个指向Card类实例的引用(地址),这段话有点不太明白,“当执行cards = new Card[52];的时候此时仅仅分配了空间,经过2重循环,该地址空间依次被填充数据,但是堆地址空间还是原来那块堆的地址空间,只是地址空间中的值被填充了,不再是空的了”能不能用上面的图解释解释啊?真是谢谢大家了。


    您好,小冯:

    您的图片我看到了,我觉得你的理解还是不够正确。

    card 是你New出来的一个对象,card对象本身又是一个数组,当你New的时候,是给card对象分配了一块内存,该内存存储了一个类似的自定义的数组,而这个数组的本身有52个元素。而您的图片中显示有52个card对象,这是不正确的。

    如果您对这方面需要更多更深入的理解,我建议你看下微软MVP推荐的一本书叫:《CLR via C# 》第三版,其中的PART1:CHAPTER 5就重点讨论了CLR是如何分配堆栈信息的。

     


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the C# Forum! If you have any feedback, please tell us.
    2011年1月21日 8:58
  • 非常感谢你 Neddy Ren,你能不能也用一个既简单又明了的图片表示一下啊??这方面确实转不过来,要是能用一个图片表达一下的话就太好了,真是非常感谢!!
    2011年1月22日 10:19
  • 对于值类型存储 在栈中,引用类型存储在堆中我是理解的,但当一个类中的成员字段本身也是引用类型时,数据是怎样存储我我搞不清楚了。还有,对于一个结构体中,成员字段是怎样存储也搞不清楚。例如:

    情况一:
    class A{}
    class B
    {
    private A a;  //问题1)对于这个字段到底是存储在堆中还是栈中?
    public void Test(){}   //问题2) 对于这个成员方法是怎么存储的呢?
    }

    情况二:

    struct JGT
    {
      A a;//问题3::A是前面定义的类,照理说结构体是值类型,应该存储在栈中,但是它这个成员字段是引用类型,应该存储在哪呢???
    }

    情况一中.的A也是存储在堆上的.
    栈上指向堆上B的指针->B(这个在堆上),然后B中有个指针指向堆中A所在的地址

    情况二中.JGT被分配在栈上.同样内部有个指向堆中A所在内存的指针.

    问题四::对于一般定义的函数或者类或者结构体中的成员方法在内存中是怎么存储的??
    问题五::对于类中的静态变量或者静态方法,存储的方式是怎样的??

      public class Deck
      {
        private Card[] cards;  //声明类数组

        public Deck()
        {
          cards = new Card[52];  //创建52个对象
          for (int suitVal = 0; suitVal < 4; suitVal++)
          {
            for (int rankVal = 1; rankVal < 14; rankVal++)
            {
              cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal,(Rank)rankVal); //实例化52个对象,并且通过构造函数给suit和rank赋值,问题来了。。类不是引用类型的吗,这样赋值suit和rank不是都是一样的吗?
            }
          }
        }

    }

    问题六:类数组是怎么存储的啊?比如:cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal,(Rank)rankVal);其中Card是一个自定义的类!

    • 已合併 Neddy Ren 2011年1月24日 1:46 the same question
    2011年1月22日 10:27
  • 引用类型分配在堆上

    值类型分下面几种情况:

    1.作为类成员时随类一起分配在堆上

    2.装箱时保存在堆上

    3.其它时候保存在堆栈上

    数组是引用类型 所以是分配在堆上

    内存分配在哪里和是否是静态没有直接关系

    方法内变量的内存分配你可以认为和其它变量分配一样处理 这块是由CLR来加载控制的

    2011年1月22日 12:12
  • 非常感谢你 Neddy Ren,你能不能也用一个既简单又明了的图片表示一下啊??这方面确实转不过来,要是能用一个图片表达一下的话就太好了,真是非常感谢!!


    您好,小冯:

    请看这个图片:


    数组在堆中的存储
    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the C# Forum! If you have any feedback, please tell us.
    2011年1月24日 2:02