了解.NET的人都知道其定义了两种数据类型:值类型和引用类型。实际上还有第三种:指针类型。要使用指针类型,系统为我们提供了特定的操作符和关键字,可以绕开CLR的内存管理机制,自己处理。
指针相关的操作符和关键字
*
|
该操作符用于创建一个指针变量(也就是一个表示直接内存位置的变量)。和在C/C++中一样,同样的操作符用于指针间接寻址
|
&
|
获取内存中变量的地址
|
->
|
访问一个由指针表示的类型的字段(C#的不安全版本)
|
[ ]
|
在不安全的上下文中[ ]索引指针变量指向的位置
|
++, --
|
不安全上下文中,可递增递减
|
+,-
|
加减运算符
|
==,!=,<,>,<=,=>
|
比较和相等
|
stackallo
|
直接在栈上分配C#数组
|
fiexd
|
临时固定一个变量以使他的地址可以被找到
|
.NET使用指针类型的情况很少,一般使用指针有两种情形
1要绕过CLR管理直接操作指针以优化应用程序的特定部分
2要调用基于C的.dll或调用需要指针作为参数的COM服务器。即使在这种情况下,为了支持System.IntPr类型和System.Runtime.InteropServices.Marshal类型,也可以不使用指针。
编写非安全代码时,需要将项目属性中生的的允许不安全代码打勾
代码里需添加unsafe关键字
static void Main(string[] args) { unsafe { //在此处理指针类型 } //在此处不可以处理指针类型 }
除了声明代码块为不安全代码外,还可构建“不安全的”结构,类,类型和成员参数。
unsafe struct Node { //整个结构都是不安全的 public int Value; public Node* Left; public Node* Right; } unsafe struct Node2 { //结构安全,但Node2成员不安全 public int Value; public unsafe Node2* Left; public unsafe Node2* Right; }
静态方法或实例方法都可以被标记为不安全。如果不想强制调用者将调用封装进不安全上下文,可以利用unsafe关键字修改Main().
unsafe static void Main(string[] args) { int myInt2 = 5; zhizhen(&myInt2); Console.WriteLine(myInt2); Console.ReadKey(); } unsafe static void zhizhen(int* myIntPointer) { *myIntPointer *= *myIntPointer; }
建立了不安全上下文后,可以使用*操作符构建数据类型的指针,使用&操作符获取被指向的内存的地址。
声明指针变量
stackallo关键字
在不安全上下文中,可能需要声明一个直接从调用栈分配内存(不受制于.NET垃圾收集器)的本地变量。C#提供了与C运行函数库_alloca等效的stackalloc关键字来满足这个需求.
unsafe static void UnsafeStackalloc() { char* p = stackalloc char[256]; for (int k = 0; k < 256; k++) p[k] = (char)k; }
使用fixed关键字固定类型
为了将不安全上下内存中的引用类型变量固定,C#提供了fixed关键字。fixed语句设置向托管类型的的指针并在代码执行过程中固定该变量。由于垃圾收集器将在不可预知的情况下重置变量,没有fixed的话。托管变量的指针将没有多大用处。
unsafe static void UseAndPinPoint() { PointRef pt = new PointRef(); pt.x = 5; pt.y = 6; //在适当的位置固定变量 fixed (int* p = &pt.x) { //在此使用int变量 } Console .WriteLine ("{0}",pt); } class PointRef { public int x; public int y; public override string ToString() { return string.Format("({0},{1})", x, y); } }
sizeof关键字
sizeof用来获取值类型的字节大小。
unsafe static void UnseofSizeod() { Console.WriteLine ("{0}",sizeof (short )); }