编写不安全代码
2008-06-06 06:34
155 查看
编写不安全代码
MSDN:"尽管实际上对 C 或 C++ 中的每种指针类型构造,C# 都设置了与之对应的引用类型,但仍然会有一些场合需要访问指针类型。例如,当需要与基础操作系统进行交互、访问内存映射设备,或实现一些以时间为关键的算法时,若没有访问指针的手段,就不可能或者至少很难完成。为了满足这样的需求,C# 提供了编写不安全代码的能力。在不安全代码中,可以声明和操作指针,可以在指针和整型之间执行转换,还可以获取变量的地址,等等。在某种意义上,编写不安全代码很像在 C# 程序中编写 C 代码。"
不安全代码必须用修饰符
unsafe明确地标记。
这里所谓的编写"不安全代码",就是要跳出.net CLR的限制,自己进行地址分配和垃圾回收.在C++里面,我们定义一个简单的指针,至少要做三件事,1.给他分配内存,2.保证类型转换正确3将内存空间释放,而在.net环境里,CLR将程序员从这些事情里解放出来,用户不再需要直接手工地进行内存操作。但是有时候,比如调用windows底层函数, 或是效率上的原因使我们需要自己去操作地址空间,本文主要是说明在c#里面指针的用法.
指针的使用、操作内存
1.& 和 *
c++里面,我们很熟悉这两个东西.在c#里面他们也一样可以用,只不过含有他们的代码如果不在unsafe标记下,编译器将会将它拒之门外.当然如果条件编译参数没有/
unsafe也是无法编译通过的(如果用vs.net集成编译环境,则在项目属性页-代码生成节将"允许不安全代码块"设置成true).
&可以取得变量的地址,但是并不是所有的变量,托管类型,将无法取得其地址.c#里面,普通的值类型都是可以取得地址的,比如struct,int,long等,而class是无法取得其地址的,另外string是比较特殊的类型,虽然是值类型,但它也是受管的.这里插一下另一个运算符,sizeof,它也是仅可用于unsafe模式下.
看下面这段代码,里面简单的用到了*,&,sizeof,还有c#里很少见但c++里大家很熟的->:
class Class1
class Point
public static unsafe void Main()
{
Point pt = new Point();
int[] arr = new int[10];
//如果不用fixed语句,无论是静态成员还是实例成员,都将无法取得其地址。
//int* ps = &CPoint.StaticField;
//PrintAddress(ps);
fixed (int* p = &Point.x)
Console.WriteLine("Address is 0x{0:X}",(int)p);
fixed (int* p = &pt.y)
Console.WriteLine("Address is 0x{0:X}",(int)p);
fixed (int* p1 = &arr[0],p2 = arr)
{
Console.WriteLine("Address is 0x{0:X}",(int)p1);
Console.WriteLine("Address is 0x{0:X}",(int)p2);
}
Console.ReadLine();
}
我这里运行的输出结果是:
Address is 0x3D5404 Address is 0x4BF1968 Address is 0x4BF1978 Address is 0x4BF1978
3.分配内存
在堆栈上分配内存c#提供stackalloc ,在堆栈上而不是在堆上分配一个内存块,语句为 type * ptr = stackalloc type [ expr ];它的大小足以包含 type 类型的 expr 元素;该块的地址存储在 ptr 指针中。此内存不受垃圾回收的制约,因此不必使用fixed将其固定。此内存块的生存期仅限于定义该内存块的方法的生存期。如果内存空间不足,将会抛出System.StackOverflowException异常.
以下是一段示例程序(form msdn),
public static unsafe void Main() { int* fib = stackalloc int[100]; int* p = fib; *p++ = *p++ = 1; //fib[0]=fib[1]=1 for (int i=2; i<100; ++i, ++p) *p = p[-1] + p[-2];//fib[i]=fib[i-1]+fib[i-2]; for (int i=0; i<10; ++i) Console.WriteLine (fib[i]); Console.ReadLine(); }
在堆上分配内存
既然有stackalloc,有没有heapalloc呢?答案是没有,c#没有提供这样的语法.想在堆上动态分配内存,只能靠自己想办法了.通过Kernel32.dll里的HeapAlloc()和HeapFree()可以达到这个目的.看下面的代码:
using System; using System.Runtime.InteropServices; public unsafe class Memory { const int HEAP_ZERO_MEMORY = 0x00000008;//内存起始地址 //获得进程堆的句柄 [DllImport("kernel32")] static extern int GetProcessHeap(); //内存分配 [DllImport("kernel32")] static extern void* HeapAlloc(int hHeap, int flags, int size); //内存释放 [DllImport("kernel32")] static extern bool HeapFree(int hHeap, int flags, void* block); static int ph = GetProcessHeap();//获得进程堆的句柄 private Memory() {} public static void* Alloc(int size) //内存分配 { void* result = HeapAlloc(ph, HEAP_ZERO_MEMORY, size); if (result == null) throw new OutOfMemoryException(); return result; } public static void Free(void* block) //内存释放 { if (!HeapFree(ph, 0, block)) throw new InvalidOperationException(); } } class Test { unsafe static void Main() { byte* buffer = (byte*)Memory.Alloc(256); for (int i = 0; i < 256; i++) buffer[i] = (byte)i; for (int i = 0; i < 256; i++) Console.WriteLine(buffer[i]); Memory.Free(buffer); Console.ReadLine(); } }
相关文章推荐
- php程序员编写的代码安全
- 如何编写异常安全的C++代码
- 如何编写异常安全的C++代码
- 如何编写异常安全的C++代码
- 如何编写异常安全的C++代码
- 编写安全的Symbian C++游戏代码
- 编写安全的Symbian C++游戏代码
- 编写安全的代码的一些技巧
- php 编写安全的代码时容易犯的错误小结
- 编写安全代码:再论整数类型转换 (
- 编写安全代码:小心volatile的原子性误解
- 如何编写安全的Java代码
- 编写安全的C/C++代码
- 编写安全的代码
- 编写安全代码:数组和指针的本质以及何时不能互换
- 编写安全的ADO.NET代码
- 编写高质量代码改善C#程序的157个建议——建议114:MD5不再安全
- 编写安全代码:避免奇怪的逻辑引发的bug
- 编写安全代码:有符号数和无符号数的移位区别---右移
- 如何编写出安全高效的代码