代码改变世界

C# 指针(unsafe与fixed的使用)

2013-10-25 13:25  xchit  阅读(4756)  评论(0编辑  收藏  举报

c#在默认情况下生成的都是安全代码,即进行了代码托管(.NET的CLR机制好处之一是,进行代码托管,适时的释放内存,程序员便不必考虑资源的回收问题),而此时,指针不能出现在安全代码的编译条件下。

一、unsafe

如果因需要想在c#中使用指针,那么unsafe便是一个通道(当然在使用前,需在项目属性的生成选项中,选择“允许不安全代码”)。

example 1:

a) 创建项目,项目属性->生成->选择“允许不安全代码”

b) 编写代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleDll
{
    //此处为c++动态库的调用,因 DllC++.dll是示例测试文件,为不影响代码演示,此处屏蔽
    //public class Code
    //{
    //    [DllImport("DllC++.dll")]
    //    public unsafe static extern int Add(int a, int* b);
    //}
    class Program
    {
        public unsafe static int Add(int a, int* b)//此处使用 指针,需要加入非安全代码关键字unsafe
        {
            return a + *b;
        }
        static void Main(string[] args)
        {
        unsafe//此代码块为非安全代码,可以使用指针
        {
            int resDll,resInner, a;
            int* b;//如果在安全代码条件下,编译不会通过对指针的定义
            a = 1;
            b = &a;
           // resDll = Code.Add(a, b);
            resInner = Add(a,b);
        }
        }
    }
}

  上面代码简单的示范了何种情况下可以使用指针。

二、fixed

fixed其实是在非安全代码下用到才会有意义的。因为他负责对指针所指向的“动态分配内存的对象或对象中的成员”简称“对象”进行锁定(之所以要锁 定,是因为即使在非安全代码模式下,这些对象一样的使用CLR的代码托管,这样的话,很可能造成对象的地址变动,就是因为CLR对资源重新分配的不确定 性,假使指针在未完成对对象的操作时,对象地址变动,那么指针指向的地址就会出现混乱,很可能造成内存泄漏甚至系统崩溃。如果使用fixed进行锁定,那 么只有在执行完fixed模块后,指针所指向的对象才能被移动)。

a) 垃圾回收机制,维护引用信息不维护指针信息

b) 引用类型的实例化对象在生存期内由垃圾回收机制处理,可能移动内存

c) 当一个类的实例化对象中含有值类型时,定义指向这些值类型的指针编译报error,因为这些内嵌在引用类型中的值类型实例会随着引用实例化对象的内存移动而移动,所以指针值在不知情的情况下会发生变化,fixed关键字做的工作便是让这样的类实例化对象(不是类类型本身,而是一个实例化对象)

example 2:

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleDll
{   //例子1

    unsafe void SquarePtrParam(int* p)
    {

      *p *= *p;

    }

 

    public unsafe void run()

    {

       int[] arr = new int[] { 1, 2, 3, 4, 5 };

      fixed (int* p = &arr[0]) //此处将锁住arr[0],使得在fixed操作块内,arr[0]不会被CLR移动

      {

        for (int i = 0, n = arr.Length; i < n; i++)

        { 

          SquarePtrParam(p + i);

          Console.WriteLine(p[i]);

        }

      }

    }    

    //例子2
        public static int Add(int[] a,int b)
        {
            unsafe
            {
                fixed (int* pa = a)//此处将锁住a,使得在fixed操作块内,a不会被CLR移动
                {
                    return *pa + b;
                }
            }
            
        }
        static void Main(string[] args)
        {
            int[] a = new int[1];
            a[0] = 1;
            int b = 2;
            int res = Add(a,b);
        }
    }
}

  上述代码需注意的是:pa的地址也是被固定了的,所以不能对他进行赋值操作。