1、在vs中使用指针,还要对项目做一些设置,需要允许不安全代码
在项目上右键属性,在点击 "生成" ,勾选允许不安代码,有些vs中那个选项可能是英文的,需要注意一下,如下图,
2、C#指针的基础语法
在代码中用指针有两种方式,一种是unsafe { },表明括中的代码是不安全的,另一种是在方法或属性上加入unsafe关键字。两种方法分别如下
1)private static IntPtr GetMemoryAddress(MethodBase mb)
{
unsafe
{
RuntimeHelpers.PrepareMethod(mb.MethodHandle);
if ((Environment.Version.Major >= 4) || ((Environment.Version.Major == 2) && (Environment.Version.MinorRevision >= 3053)))
{
return new IntPtr(((int*)mb.MethodHandle.Value.ToPointer() + 2));
}
UInt64* location = (UInt64*)(mb.MethodHandle.Value.ToPointer());
int index = (int)(((*location) >> 32) & 0xFF);
if (IntPtr.Size == 8)
{
ulong* classStart = (ulong*)mb.DeclaringType.TypeHandle.Value.ToPointer();
ulong* address = classStart + index + 10;
return new IntPtr(address);
}
else
{
uint* classStart = (uint*)mb.DeclaringType.TypeHandle.Value.ToPointer();
uint* address = classStart + index + 10;
return new IntPtr(address);
}
}
}
2)private static unsafe void MemoryPatching(MethodBase miEvaluation, MethodBase miLicensed)
{
IntPtr IntPtrEval = GetMemoryAddress(miEvaluation);
IntPtr IntPtrLicensed = GetMemoryAddress(miLicensed);
if (IntPtr.Size == 8)
*((long*)IntPtrEval.ToPointer()) = *((long*)IntPtrLicensed.ToPointer());
else
*((int*)IntPtrEval.ToPointer()) = *((int*)IntPtrLicensed.ToPointer());
}
在看一下具体怎么在C#中使用指针,怎么分配内存,和怎么释放内存。
1)要用指针操作托管堆上的值类型,需要用到 fixed关键字public unsafe class Coder
{
public int Age;
public void SetAge(int age)
{
fixed (int* p = &Age)
{
*p = age;
}
}
}
2)用指针操作栈上的值类型int* p = &maxValue;
*p = 20;
Console.WriteLine(p->ToString());
3)指针操作非托管堆上的内存,非托管的内存需要手动释放,GC就不管理了,需要手动管理。System.Runtime.InteropServices.Marshal.AllocHGlobal()//用来从非托管堆上分配内存
System.Runtime.InteropServices.Marshal.FreeHGlobal(handle)//用来释放从非托管对上分配的内存
IntPtr handle = System.Runtime.InteropServices.Marshal.AllocHGlobal(4);
Int32* p = (Int32*)handle;
*p = 20;
Console.WriteLine(p->ToString());
System.Runtime.InteropServices.Marshal.FreeHGlobal(handle);
还可以 用IDispose接口管理内存,代码如下:public unsafe class UnmanagedMemory : IDisposable
{
public int Count { get; private set; }
private byte* Handle;
private bool _disposed = false;
public UnmanagedMemory(int bytes)
{
Handle = (byte*) System.Runtime.InteropServices.Marshal.AllocHGlobal(bytes);
Count = bytes;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(true);
}
protected virtual void Dispose( bool isDisposing )
{
if (_disposed) return;
if (isDisposing)
{
if (Handle != null)
{
System.Runtime.InteropServices.Marshal.FreeHGlobal((IntPtr)Handle);
}
}
_disposed = true;
}
~UnmanagedMemory()
{
Dispose( false );
}
}
使用方法:using (UnmanagedMemory memory = new UnmanagedMemory(10))
{
int* p = (int*)memory.Handle;
*p = 20;
Console.WriteLine(p->ToString());
}
4)使用stackalloc关键字在栈中分配内存,一般况下,是比堆中的内存速度快,在此情况下栈中分配的内存,方法退出内存自动释放,不用担心内存泄漏。/* 使用指针优化性能 */
//使用关键字stackalloc(指示.net运行库分配堆栈上一定内存)创建基于堆栈的高效数组
unsafe
{
/*
* stackalloc命令只分配堆栈内存而已,不会把内存初始化为任何默认值
* 1.其后紧跟要存储数据类型名(该数据类型必须是一值类型)
* 2.分配的字节数为变量个数*sizeof(数据类型)
*/
int* pInt = stackalloc int[10];
*pInt = 0;
*(pInt + 1) = 1;
pInt[2] = 5; //表达式被编译为*(pInt + 2)
pInt[50] = 100; //不报错,使用stackalloc声明一相同数组,对数组边界检查,这数组没封装任何对象
}
3、C# 指针操作的几个缺点
缺点1:只能用来操作值类型 .Net中,引用类型的内存管理全部是由GC管理的,无法取得其地址,因此,无法用指针来操作引用类型。
缺点2:泛型不支持指针类型 C# 中泛型不支持指针类型。可以考虑其它替代方案解决这个限制。
缺点3:没有函数指针 幸运的是,C# 中有delegate,delegate 支持支持指针类型,lambda 表达式也支持指针。