一、C#、CLR、.NET Framework(.NET框架)
C#是开发语言,用于编写代码;
CLR(Common Language Runtime,公共语言运行库),在运行期间进行内存管理、代码安全验证、代码执行、垃圾收集等(CLR有一项服务GC(Garbage Collector,垃圾收集),可以自动管理内存,减轻程序员负担);
.NET Framework是开发框架,可看作是一个独立发布的软件包,其中包含了CLR、类库以及相关的工具等开发环境。
C#代码需要依托.NET框架编译和运行。
二、编译
C#编译分两步走:
第一步是编译成中间语言CIL,生成计算机可识别的输出文件——程序集(包含程序元数据及对其他程序集引用的元数据),程序集可以是可执行文件(.exe)或者动态链接文件(.dll);
第二步是编译成本机代码并执行,即在本机上搭建.NET环境,将程序集中的可执行代码在需要的时候由实时编译器编译,然后被缓存以备后来的程序执行。一旦CIL被编译成本机代码,CLR就在其运行的时候管理它,执行内存及检查之类的工作。
三、托管和非托管
.NET环境中,系统的资源分为托管资源和非托管资源。
托管代码:为.NET框架编写的代码称为托管代码,需要CLR。
非托管代码:不在CLR控制之下运行的代码,比如Win32C/C++ DLL,成为非托管代码。
四、堆和栈
堆(heap):在C#中,又叫作托管堆,用于托管(存储)实例对象(new对象),可动态分布存储空间,能够存储大量数据。灵活性高,但,效率不高。对于堆的回收,C/C++是手动回收,.NET是CLR管理,满了之后,GC自动回收,无需担心内存释放问题。
(内存)栈(stack):先进后出,后进先出。入栈时,自动分配确定内存(譬如,int,4个字节),出栈时,自动清理所占的内存。灵活性不高,但,效率高。(区别数据栈)
堆存放引用类型对象,由CLR释放;栈存放函数的参数、局部变量、返回数据等值对象,由编译器自动释放。
五、值类型和引用类型
引用类型创建时,runtime会为其分配两个空间,一块空间分配在堆上,存储引用类型本身的数据,另一个块空间分配在栈上,存储对堆上数据的引用(实际上存储的堆上的内存地址,也就是指针),如类对象的引用还是存储在栈中。
值类型创建时,runtime会为其分配一个空间,这个空间分配在变量创建的地方。作为方法内局部变量时,则随方法入栈;作为引用类型的成员变量时,则跟随引用类型,存储在堆上。如,作为类对象的字段时,则跟随类存储在堆里。
栈是一个有逻辑顺序的结构,堆则是杂乱地堆在一起的,比较耗能。引用类型占空间较大,且分配空间不确定,故不直接存在栈中而是存放在堆中,通过栈中的指针引用。
- 栈内存存储的是局部变量而堆内存存储的是实体;
- 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
- 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。
①引用类型和值类型都继承自Systerm.Object类。不同之处,几乎所有的引用类型都是直接从Systerm.Object继承,而值类型则是继承Systerm.Object的子类Systerm.ValueType类。
②我们在给引用类型的变量赋值的时候,其实只是赋值了对象的引用;而给值类型变量赋值的时候是创建了一个副本(就是克隆了一个变量)。
五、指针
1、首先配置项目,允许不安全代码
2、指针定义
C#中可定义为指针的类型:sbyte、byte、ushort、int、uint、long、ulong、char、float、double、decimal、bool、struct,其中struct只能包含非托管类型(值类型数据)
定义指针 | 说明 |
int* p | 整型指针 |
int** p | 指向整型指针的指针 |
char* c | 指向字符的指针 |
int*[] arr | 整型一维数组指针 |
3、写代码时,使用关键字unsafe
fixed:用户暂时固定托管代码中引用类型的位置(固定时间越短,固定地址越小越好,以防产生内存碎片)
stackallc:给指针分配内存
unsafe void ClassPointer()
{
Person p = new Person();//堆中的存储空间是随意的,地址不固定
//fixed:临时固定地址, pIY = &p.Y
//stackalloc:从内存分配地址
fixed (int* pIX = &p.X)
{
int* ptr = stackalloc int[1];
char* cptr = stackalloc char[26];
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]));
}
}
}
class Person
{
//定义成变量才能正确取址
public int X = 1;
//定义成属性,则报错“无法获取给定表达式的地址”
public int Y { get; set; } = 2;
}
参考:
https://blog.youkuaiyun.com/lidandan2016/article/details/77868043
https://www.cnblogs.com/zd1994/p/4424329.html