bob体育平台C#数据类型及差别(复习专项使用卡塔尔(قطر‎

2019-12-14 03:09栏目:bob体育平台
TAG:

一.数据类型

.NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱

先简单介绍一下它们:

    1. 值类型

一.“堆”,“栈”专区

严格来说应该是下面的这些:值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型。

类型 描述 范围 默认值
bool 布尔值 True 或 False False
byte 8 位无符号整数 0 到 255 0
char 16 位 Unicode 字符 U +0000 到 U +ffff ''
decimal 128 位精确的十进制值,28-29 有效位数 (-7.9 x 1028 到 7.9 x 1028) / 100 到 28 0.0M
double 64 位双精度浮点型 (+/-)5.0 x 10-324 到 (+/-)1.7 x 10308 0.0D
float 32 位单精度浮点型 -3.4 x 1038 到 + 3.4 x 1038 0.0F
int 32 位有符号整数类型 -2,147,483,648 到 2,147,483,647 0
long 64 位有符号整数类型 -923,372,036,854,775,808 到 9,223,372,036,854,775,807 0L
sbyte 8 位有符号整数类型 -128 到 127 0
short 16 位有符号整数类型 -32,768 到 32,767 0
uint 32 位无符号整数类型 0 到 4,294,967,295 0
ulong 64 位无符号整数类型 0 到 18,446,744,073,709,551,615 0
ushort 16 位无符号整数类型 0 到 65,535 0

这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道。。。“堆”,“栈”是什么?哦,这个知道。。。

引用类型包括:数组,用户定义的类、接口、委托,object,字符串

    1. 引用类型
      1. 数组
      2. 类(自定义类)
      3. 字符串
      4. 接口
      5. Object
      6. 委托
    2. 指针类型

      1. 官方给出的说明

        1.   在指针类型中的 * 之前指定的类型被称为“referrent 类型”。 以下任一类型均可为 referrent 类型:

          • 任何整型类型:sbyte、byte、short、ushort、int、uint、long、ulong。
          • 任何浮点类型:浮点、双精度。
          • 字符。
          • 布尔型。
          • 小数。
          • 任何枚举类型。
          • 任何指针类型。 这允许如 void** 的表达式。
          • 任何仅包含非托管类型字段的用户定义的结构类型。

          指针类型不从对象继承,并且指针类型与 object 之间不存在转换。 此外,装箱和取消装箱不支持指针。但是,你可在不同的指针类型之间以及指针类型和整型之间进行转换。

          在同一个声明中声明多个指针时,星号 (*) 仅与基础类型一起写入;而不是用作每个指针名称的前缀。 例如:

          C# 复制

          int* p1, p2, p3;   // Ok
          int *p1, *p2, *p3;   // Invalid in C#
          

          指针不能指向引用或包含引用的结构,因为无法对对象引用进行垃圾回收,即使有指针指向它也是如此。垃圾回收器并不跟踪是否有任何类型的指针指向对象。

        2. 指针简单应用

          1.   

                //指针
                        unsafe
                        {
                            char* cptr = stackalloc char[26];//分配内存
                            //stringAppend();
                            for (int i = 0; i < 26; i++)
                            {
                                cptr[i] = (char)(i + 65);
                            }
                            for (int i = 0; i < 26; i++)
                            {
                                Console.WriteLine(string.Format("{0}:{1}",(int)&cptr[i],cptr[i]));
                            }
                        }
            

            定义一个char类型指针并分配26字节内存,for循环给指针赋值,输出int类型指针地址及它转化位string的值:

          2. 结果为:

          3. bob体育平台 1

             

          4. 在内存中查看 :调试-》窗口-》内存-》内存1 ,我们把int类型指针值复制到地址栏,vs自动转化位十六进制, 右键-》带符号显示 ,得到结果为char对应的数字

             

之前我也写过一篇堆栈的文章,不过写的不深刻,剖析的也不全面,所以今天也参考了一些大牛的资料。

为什么要设计值类型:

二. 基本类型主要差异

 

  1. 值类型不需要从托管堆分配
  2. 引用类型产生的实例对象在托管堆上都会有一些额外的成员,这些成员必须初始化
  3. 值类型不使用GC垃圾回收控制,作用域结束后,会自行释放。
    1. 基本类型:值类型,引用类型
      1. 相同点:  
        1. 引用类型可以实现接口,值类型当中的结构体也可以实现接口;
        2. 引用类型和值类型都继承自System.Object类。

一、预备知识—程序的内存分配  
  一个由C/C++编译的程序占用的内存分为以下几个部分  
  1、栈区(stack)—   由编译器自动分配释放   ,存放函数的参数值,局部变量的值等。其  
  操作方式类似于数据结构中的栈。栈是一个内存数组,是一个LIFO(last-in  first-out,后进先出)的数据结构。  
  2、堆区(heap)   —   一般由程序员分配释放,   若程序员不释放,程序结束时可能由OS回  
  收   。注意它与数据结构中的堆是两回事。堆是一块内存区域,在堆里可以分配大块的内存用于存储某类型的数据。

     综上,值类型更有效率,所以在.NET中将一些简单的,常用的,内存占用小的对象设置为值类型,大大提高了整个CLR的效率。

          2.不同点

与栈不同,堆里的内存可以任意顺序存入和移除。

 

             1.值类型直接储存在内存栈中,引用类型在栈中储存它在堆中内存单位的地址。

 

装箱与拆箱:

             2.值类型存取速度快,引用类型存取速度慢。

虽然程序可以在堆里保存数据,但并不能显示地删除它们。CLR的自动GC(Garbage Collector,垃圾收集器)再判断出程序的

     为什么会发生装箱:

             3.值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用。

  
  3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的  
  全局变量和静态变量在一块区域,   未初始化的全局变量和未初始化的静态变量在相邻的另  
  一块区域。   -   程序结束后由系统释放。  
  4、文字常量区   —常量字符串就是放在这里的。   程序结束后由系统释放  
  5、程序代码区—存放函数体的二进制代码。 

         因为值类型与引用类型在CLR中的内存管理方式不同,所以当值类型在程序中需要向引用类型转化的时候就会发生装箱。也就是当值类型的实例对象需要在一个程序作用域的场景下,转化成为不从System.ValueType继承的另外一个类型的对象时,装箱就会发生。

             4.值类型继承自System.ValueType,引用类型继承自System.Object。

 

      装箱有什么危害:

             5. 栈的内存分配是自动释放;而堆在.NET中会有GC来释放。

二、例子程序    
  这是一个前辈写的,非常详细    
  //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"  
  优化成一个地方。    
  }    

          因为设计值类型的本意是为了提升效率。但是当装箱发生时,值类型会转化成为引用类型,这个时候,效率没有得到任何提高。反而因为装箱,拆箱有可能重复多次的发生,反而让效率更受影响。

二. 类型之占用内存

bob体育平台 2

 

    1.值类型占用内存

三、堆和栈的理论知识    
  3.1申请方式    
  stack:    
  由系统自动分配。   例如,声明在函数中一个局部变量   int   b;   系统自动在栈中为b开辟空  
  间    
  heap:    
  需要程序员自己申请,并指明大小,在c中malloc函数    
  如p1   =   (char   *)malloc(10);    
  在C++中用new运算符    
  如p2   =   new   char[10];    
  但是注意p1、p2本身是在栈中的。    
   
   
  3.2    
  申请后系统的响应    
  栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢  
  出。    
  堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,  
  会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表  
  中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的  
  首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。  
  另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部  
  分重新放入空闲链表中。    
   
  3.3申请大小的限制    
  栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意  
  思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有  
  的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将  
  提示overflow。因此,能从栈获得的空间较小。    
  堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储  
  的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小  
  受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。    
   
   
   
  3.4申请效率的比较:    
  栈由系统自动分配,速度较快。但程序员是无法控制的。    
  堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.    
  另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是  
  直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。  
     
   
  3.5堆和栈中的存储内容    
  栈:   在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可  
  执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈  
  的,然后是函数中的局部变量。注意静态变量是不入栈的。    
  当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地  
  址,也就是主函数中的下一条指令,程序由该点继续运行。    
  堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。    
   
  3.6存取效率的比较    
   
  char   s1[]   =   "aaaaaaaaaaaaaaa";    
  char   *s2   =   "bbbbbbbbbbbbbbbbb";    
  aaaaaaaaaaa是在运行时刻赋值的;    
  而bbbbbbbbbbb是在编译时就确定的;    
  但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。    
  比如:    
  #include    
  void   main()    
  {    
  char   a   =   1;    
  char   c[]   =   "1234567890";    
  char   *p   ="1234567890";    
  a   =   c[1];    
  a   =   p[1];    
  return;    
  }    
  对应的汇编代码    
  10:   a   =   c[1];    
  00401067   8A   4D   F1   mov   cl,byte   ptr   [ebp-0Fh]    
  0040106A   88   4D   FC   mov   byte   ptr   [ebp-4],cl    
  11:   a   =   p[1];    
  0040106D   8B   55   EC   mov   edx,dword   ptr   [ebp-14h]    
  00401070   8A   42   01   mov   al,byte   ptr   [edx+1]    
  00401073   88   45   FC   mov   byte   ptr   [ebp-4],al    
  第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到  
  edx中,再根据edx读取字符,显然慢了。

 

            Console.WriteLine("bool size:{0}", sizeof(bool));
            Console.WriteLine("byte size:{0}", sizeof(byte));
            Console.WriteLine("char size:{0}", sizeof(char));
            Console.WriteLine("decimal size:{0}", sizeof(decimal));
            Console.WriteLine("double size:{0}", sizeof(double));
            Console.WriteLine("float size:{0}", sizeof(float));
            Console.WriteLine("int size:{0}", sizeof(int));
            Console.WriteLine("long size:{0}", sizeof(long));
            Console.WriteLine("sbyte size:{0}", sizeof(sbyte));
            Console.WriteLine("short size:{0}", sizeof(short));
            Console.WriteLine("uint size:{0}", sizeof(uint));
            Console.WriteLine("ulong size:{0}", sizeof(ulong));
            Console.WriteLine("ushort size:{0}", sizeof(ushort));
            Console.WriteLine("--------------------------------------");   

 

值类型和引用类型的区别:

      bob体育平台 3

四.为了理解栈和堆,让我们通过以下的代码来了解背后到底发生了什么。

1.    值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的
地址。

        · 1字节=8位 与值类型描述一致;

 1 public void Method1()
 2 {
 3     
 4 // Line 1
 5     int i=4;
 6  
 7     
 8 // Line 2
 9     int y=2;
10  
11     
12 //Line 3
13     class1 cls1 = new class1();
14 }

2.     值类型存取速度快,引用类型存取速度慢。

    2. 引用类型占用内存不固定, 根据它所创建的对象对应的数据大小不同而不同。

代码只有三行,现在我们可以一行一行地来了解到底内部是怎么来执行的。

3.     值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用

 

  • Line 1:当这一行被执行后,编译器会在栈上分配一小块内存。栈会在负责跟踪你的应用程序中是否有运行内存需要

4.     值类型继承自System.ValueType,引用类型继承自System.Object

5.     栈的内存分配是自动释放;而堆在.NET中会有GC来释放       

  • Line 2:现在将会执行第二步。正如栈的名字一样,它会将此处的一小块内存分配叠加在刚刚第一步的内存分配的顶部。你可以认为栈就是一个一个叠加起来的房间或盒子。在栈中,数据的分配和解除都会通过LIFO (Last In First Out)即先进后出的逻辑规则进行。换句话说,也就是最先进入栈中的数据项有可能最后才会出栈。
  • Line 3:在第三行中,我们创建了一个对象。当这一行被执行后,.NET会在栈中创建一个指针,而实际的对象将会存储到一个叫做“堆”的内存区域中。“堆”不会监测运行内存,它只是能够被随时访问到的一堆对象而已。不同于栈,堆用于动态内存的分配。
  • 这里需要注意的另一个重要的点是对象的引用指针是分配在栈上的。 例如:声明语句 Class1 cls1; 其实并没有为Class1的实例分配内存,它只是在栈上为变量cls1创建了一个引用指针(并且将其默认职位null)。只有当其遇到new关键字时,它才会在堆上为对象分配内存。
  • 离开这个Method1方法时(the fun):现在执行控制语句开始离开方法体,这时所有在栈上为变量所分配的内存空间都会被清除。换句话说,在上面的示例中所有与int类型相关的变量将会按照“LIFO”后进先出的方式从栈中一个一个地出栈。
  • 需要注意的是:这时它并不会释放堆中的内存块,堆中的内存块将会由垃圾回收器稍候进行清理。

6.      值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。

bob体育平台 4

7. 值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际

现在我们许多的开发者朋友一定很好奇为什么会有两种不同类型的存储?我们为什么不能将所有的内存块分配只到一种类型的存储上?

数据则保存在堆中。注意,堆和堆栈是两个不同的概念,在内存中的存储位置也不相同,堆一般用于存储

如果你观察足够仔细,基元数据类型并不复杂,他们仅仅保存像 ‘int i = 0’这样的值。对象数据类型就复杂了,他们引用其他对象或其他基元数据类型。换句话说,他们保存其他多个值的引用并且这些值必须一一地存储在内存中。对象类型需要的是动态内存而基元类型需要静态内存。如果需求是动态内存的话,那么它将会在堆上为其分配内存,相反,则会在栈上为其分配。栈的存取速度比堆快。

可变长度的数据,如字符串类型;而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量

版权声明:本文由bob体育app发布于bob体育平台,转载请注明出处:bob体育平台C#数据类型及差别(复习专项使用卡塔尔(قطر‎