为了保持类型安 全,默认情况下,C# 不支持指针算法。 不过,通过使用 unsafe 关键字,可以定义可使用指针的不安全上下文。

unsafeC# 程 序中的使用场合:

1)实时应用,采用指针来提高性能;

2)引用非.net DLL提供的如C++编写的外部函数,需要指针来传递该函数;

3)调试,用以检测程序在运行过程中的内存使用状况。

使用unsafe 的利弊:

好处:性能和灵活性提高;可以调用其他dll的函数,提高了兼容性;可以得到内存地址;

坏处:非法修改了某些变量;内存泄漏。

unsafe 与unmanaged的区别:

managed code是在CLR监管下运行的程序。以下任务由CLR来执行:管理对象内存,类型安全检测和冗余处理。

unmanaged code也就是能由程序员直接进行内存操作的程序。

unsafe 是介于managed和unmanaged之间的桥梁,它使得managed code也能使用指针来控制和操作内存。

unsafe 的使用:


unsafe 可以用来修饰类、类的成员函数、类的全局变量,但不能用来修饰类成员函数内的局部变量。 编译带有unsafe 代码的程序也要在 “configuration properties>build” 中把允许unsafe 代码设为真。

但是在managed code中使用unsafe 时也要注意,正因为CLR可以操作内存对象,假如你写了一下代码:

public unsafe void add(int *p)
      {
          *p=*p+4;
      }

p的地址值可能会在运行过程中被CLR所修改,这通常可采用fixed来处理,使指针所指向的地址不能被改变。如下:

fixed(int *p=& value)
        {
            add(p);
        }

托管代码 (managed code):由公共语言运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。

非托管代码(Unmanaged Code):在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务;它与托管代码不同,后者从公共语言运行库中获得这些服务。 
Unsafe的代码介于这两者之间,它也是在CLR的环境中执行,但是我们可以直接操作内存。只要我们的代码包含下面三个指针操作符之一就需要使用Unsafe关键字:
* & ->

例如:

unsafe static void ChangeValue(int* pInt){*pInt = 23;}

上面的代码由于是在CLR下托管执行,为 了减少内存碎片C#的自动垃圾回收机制会允许已经分配的内存在运行时进行位置调整,所以如果我们多次调用的话就可能
导致指针指向其他的变量。比如*pInt为 指向一个变量的地址为1001,CLR在重新内存整理分配后该变量 就存储在地址为5001的地方。而原来1001的地方可能会
被分配其他变量,要解决这个问题我们就需要使用Fixed关键字。

fixed 语句禁止垃圾回收器重定位可移动的变量。fixed 语句只能出现在不安全的上下文中。Fixed 还可用于创建固定大小的缓冲区。如下面例子:

using System;class CaryData{public int data;}

class CProgram{

unsafe static void ChangeValue(int* pInt){*pInt = 23; //3为这个指针的地址赋值23}

public unsafe static void Main(){CaryData cd = new CaryData();Console.WriteLine("改变前: {0}", cd.data);        

fixed (int* p = &cd.data) // 1把整形的地址赋给了指针P{ChangeValue(p); //2专递指针}Console.WriteLine("改变后: {0}", cd.data); //4由于cd.data的和*p地址相同,所以cd.data 的输出是23}}

注意要勾选项目属性中生成标签的允许不安全代码。

T_Account ret;unsafe{fixed(void* ptr =  body){ret = *((T_Account*)ptr); // 转换指针为(T_Account*),再获得指针的值也就是T_Account类型值}}
T_Account x = new T_Account();x.ID = 12;x.Name = "thisistest";x.Native_Currency = "USD";byte[] message = new byte[T_Account.Size];unsafe{void* ptr = &x; //将地址赋给这个指针fixed(void* des = message) // 值赋给了这个地址{MemoryUtility.CopyData(des, ptr, T_Account.Size);}}

(*) unsafe 和 fixed

unsafe
{              
    int[] array = new int[10];
    for (int i = 0; i < array.Length; i++)
    {
        array[i] = i;
    }
    fixed (int* p = array)
    {
        for (int i = 0; i < array.Length; i++)
        {
            System.Console.WriteLine(p[i]);
        }                   
    }              
}

指针在c#中是不提倡使用的,有关指针的操作被认为是不安全的(unsafe)。因此运行这段代码之前,先要改一个地方,否则编译不过无法运行。
修 改方法:在右侧的solution Explorer中找到你的项目,在项目图标(绿色)上点右键,选最后一项properties,然后在Build标签页里把Allow unsafe code勾选上。之后这段代码就可以运行了,你会看到,上面这段代码可以像C语言那样用指针操纵数组。但前提是必须有fixed (int* p = array),它的意思是让p固定指向数组array,不允许改动。因 为C#的自动垃圾回收机制会允许已经分配的内存在运行时进行位置调整,如果那样,p可能一开始指的是array,但后来array的位置被调整到别的位置 后,p指向的就不是array了。所以要加一个fixed关键字,把它定在那里一动不动,之后的操作才有保障。

另有两点需要注意:

1)指针的使用必须放在unsafe的区域里;unsafe关键字也可作为类或方法的修饰符。

2)fixed (int* p = array)中,p的定义不能写在别处,而且fixed关键字也只能在unsafe区域里使用。

(*) 略简洁的unsafe写法

class Program
    {
        unsafe public static UInt16 Htons(UInt16 src)
        {
            UInt16 dest;
            // 不能照搬C的源代码,因为有些类型长度不一样,如char(2字节),long(8字节)
            // ((char*)&dest)[0] = ((char*)&src)[1];
            // ((char*)&dest)[1] = ((char*)&src)[0];
            ((byte*)&dest)[0] = ((byte*)&src)[1];
            ((byte*)&dest)[1] = ((byte*)&src)[0];
            return dest;
        }

public static UInt16 ConciseHtons(UInt16 src)
        {
            UInt16 dest;
            unsafe
            {
                ((byte*)&dest)[0] = ((byte*)&src)[1];
                ((byte*)&dest)[1] = ((byte*)&src)[0];
            }           
            return dest;
        }
       
        static void Main()
        {
            UInt16 val = 1;

// 如果方法是unsafe的,则必须在unsafe block里调用
            unsafe
            {               
                val = Htons(val);
            }
            Console.WriteLine(val);

// 更简洁的写法是把unsafe block写在函数内部
            val = ConciseHtons(val);
            Console.WriteLine(val);
        }               
    }

(*) stackalloc

stackalloc的用处仅仅是把数组分配在栈上(默认是分配在托管堆上的)。

class MyClass
    {
        public int val;
    }

class Program
    {               
        static void Main()
        {           
            unsafe
            {               
                MyClass *p = stackalloc MyClass[1]; // Error!! 如果类型要放在托管堆上则不行,如果MyClass是struct就OK了
                p->val = 1;

int *iArray = stackalloc int[100];  // OK,在栈上创建数组, int类型本身就是放在栈上的
            }           
        }               
    }

注意:指针指向的内存一定要固定。凡是C#里的引用类型(一切类型的 数组都是引用类型 )都是分配在托管堆上的,都不固定。有两种方法强制固定,一种是用stackalloc分配在栈上,另一种是用fixed 分配在堆上。

转载于:https://www.cnblogs.com/yefengmeander/archive/2011/01/05/2888040.html

C#学习之unsafe相关推荐

  1. Java双刃剑之Unsafe类详解

    前一段时间在研究juc源码的时候,发现在很多工具类中都调用了一个Unsafe类中的方法,出于好奇就想要研究一下这个类到底有什么作用,于是先查阅了一些资料,一查不要紧,很多资料中对Unsafe的态度都是 ...

  2. 【JAVA拾遗】Java8新特性合辑

    [JAVA拾遗]Java8新特性合辑 文章目录 [JAVA拾遗]Java8新特性合辑 0. 逼逼 [--/--]126 Lambda Expressions & Virtual Extensi ...

  3. Rust for Linux 源码导读 | Ref 引用计数容器 原创

    引子 2022 年,我们很可能会看到 Linux 内核中的实验性 Rust 编程语言支持成为主流.2021.12.6 早上发出了更新的补丁,介绍了在内核中处理 Rust 的初始支持和基础设施. 这次更 ...

  4. 通关GO语言17 SliceHeader:lice 如何高效处理数据?

    在"第 4 讲|集合类型:如何正确使用 array.slice 和 map?"中,你已经学习了 slice(切片),并且知道如何使用.这节课我会详细介绍 slice 的原理,带你学 ...

  5. Unsafe 类的学习

    思考 呃,为啥学习JUC要先学习这个类呢,而且这个类大家可以去看一下,是不允许我们直接使用的,可以去看一下他的构造方法是私有的,所以这个类是只是提供jdk的官方使用. 为啥呢? 看一下就知道了 Uns ...

  6. Go 学习笔记(74)— Go 标准库之 unsafe

    Go 语言自带的 unsafe 包的高级用法, 顾名思义,unsafe 是不安全的.Go 将其定义为这个包名,也是为了让我们尽可能地不使用它.不过虽然不安全,它也有优势,那就是可以绕过 Go 的内存安 ...

  7. 【我的C语言学习进阶之旅】解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.

    一.问题描述 今天在Visual Studio 2019中写一段C语言的代码,发生生成错误.弹框如下: 点击[否(N)],提示如下: 错误具体信息为: 错误 C4996 'fscanf': This ...

  8. java unsafe获取指针_【实战Java高并发程序设计 1】Java中的指针:Unsafe类

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

  9. atomic原子类实现机制_JUC学习笔记--Atomic原子类

    Atomic 原子操作类包 Atomic包 主要是在多线程环境下,无锁的进行原子操作.核心操作是基于UnSafe类实现的CAS方法 CAS CAS: compareAndSwap :传入两个值:期望值 ...

最新文章

  1. 关于“我的边栏,我做主”——Windows Vista Gadgets大赛报名
  2. 硬盘安装WIN7方法
  3. 【存储知识学习】第五章-5.1-5.3 RAID磁盘阵列-《大话存储》 阅读笔记
  4. 小程序 video 控制器外观调整_最好的Nintendo Switch控制器
  5. boost的chrono模块线程时钟的测试程序
  6. Java微服务(二)【idea中文插件安装】(手把手编写,超级详细)
  7. c语言函数参数类型格式化,格式化输出的几种方法 主要介绍format函数的用法
  8. riak php7,Laravel中服务提供者的register和boot分别是干什么
  9. 如何实现上一条、下一条的功能
  10. TCP聊天工具的实现
  11. 基于live555开发嵌入式linux系统的rtsp直播服务
  12. linux 中断程序设计,linux – CPU0被eth1中断淹没
  13. teechart mysql_TeeChart的X轴为时间,多个Y轴的显示
  14. MySQL生成测试数据相关脚本(持续更新)
  15. 戴尔服务器系统缓存怎么清理,戴尔笔记本怎样清理磁盘空间
  16. 计算机检测不到蓝牙,图解Win10 1809系统中检测不到蓝牙设备的方法
  17. 孙海波:重新发现“同案”:构建案件相似性的判断标准
  18. Qt在线帮助文档网址以及安装包下载地址
  19. 压缩比13为什么建议用92的油_马自达为啥能在13:1高压缩比下仍然使用92汽油
  20. Unity3D 动态加载本地/网络GLB模型

热门文章

  1. MySQL使用学习使用,mysql学习--基本使用_MySQL
  2. 小米10pro使用说明书_华为Mate40、华为P40和小米10拍照对比:哪一款最好?
  3. app vue 真机运行_使用 HBuilder 将 Vue 项目打包成手机 App
  4. matlab 等分矩阵,用matlab根据列拆分矩阵.
  5. Mysql函数访问oracle,Oracle与MySql函数
  6. 升级glibc的影响_Java 11 升级:“债务”“危机”
  7. vue watch 第一次不执行_Vue 实现前进刷新,后退不刷新的效果
  8. php绘制一个三角形,如何利用css或html5画出一个三角形?两种不同的制作三角形方法(代码实例)...
  9. java tm无响应_Java(TM) Platform SE binary 未响应 是怎么个情况?
  10. linux里hba状态_Windows和Linux系统查看HBA卡wwn号的方法 | 系统之家官网