`

C#学习之unsafe

阅读更多

为了保持类型安 全,默认情况下,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 分配在堆上。

    分享到:
    评论

    相关推荐

      c#学习笔记.txt

      c#学习笔记(1) 51099在线学习网发布 文章来源:网络收集 发布时间:2006-05-25 字体: [大 中 小] 51099在线学习网 http://www.51099.com 1, 结构(struct) 与 类(class) [attributes] [modifiers] struct ...

      C#中使用指针源程序,我想许多C程序员不愿意学习Java的原因之一就是Java不支持指针

      C#中使用指针源程序,我想许多C程序员不愿意学习Java的原因就是Java不支持指针,但是现在类似于Java的C#却已经支持了指针,你可以使用unsafe关键字来告诉编译器下面的函数或者代码是不安全的。一旦你使用了unsafe,...

      在一小时内学会 C#(txt版本)

      本文特别适合有 C++ 基础却没有太多精力学习 C# 的读者。 关于作者 Aisha Ikram 我现在在英国一家软件公司任技术带头人。我是计算机科学的硕士。我主要使用 .NET 1.1/2.0, C#, VB.NET, ASP.NET, VC++ 6, MFC, ...

      利用Visual C#处理数字图像

      微软的新的.NET平台为开发者带来了许多新的诸如GDI+、Globalization之类的编程机制,同时还发明了一门全新的类似Java的编程语言-C#。对于这些新知识,我们应尽快了解、掌握并试图运用到实践项目中去,而通过实例...

      C#微软培训资料

      第一部分 C#语言概述.4 第一章 第一章第一章 第一章 .NET 编 编 编程语言 程语言编程语言 程语言 C#.4 1.1 Microsoft.NET——一场新的革命.4 1.2 .NET 与 C#.6 1.3 C#语言的特点.8 1.4 小 结 .11...

      C# for CSDN 乱七八糟的看不懂

      C#关键字 关键字 abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach ...

      C# 语言规格说明(English Edition第五版)

      建议作为平时学习阅读之用 目录: Table of Contents 1. Introduction 1 1.1 Hello world 1 1.2 Program structure 2 1.3 Types and variables 4 1.4 Expressions 6 1.5 Statements 8 1.6 Classes and objects 12 ...

      C_基本数字图像处理.doc

      .NET平台为开发者带来了许多新的诸如GDI+、Globalization之类的编程机制,同时还发明了一门全新的类似Java的编程语言-C#。对于这些新知识,我们应尽快了解、掌握并试图运用到实践项目中去,而通过实例学习的方法...

      C#快速入门.doc

      本文特别适合有 C++基础却没有太多精力学习 C#的读者。 目录 野比经验交流系列(C#) 前言 .................................................................................4 说明 ......................

      Aspose.Pdf v12.0 破解

      Aspose.Pdf v12.0 通过unsafe方式破解,无水印,内含破解方式,亲测,绝对可用! 仅用于学习研究. 不得用于商业用处.

      net学习笔记及其他代码应用

      要请求垃圾收集,可以调用下面的方法之一: System.gc() Runtime.getRuntime().gc() 37.String s = new String(\"xyz\");创建了几个String Object? 答:两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。...

      运用VisualC#完成基本数字图像处理

      引言: 微软的新的.NET平台为开发者带来了许多新的诸如GDI+、Globalization之类的编程机制,同时还发明了一门全新的类似Java的编程语言-C#。对于这些新知识,我们应尽快了解、掌握并试图运用到实践项目中去,而...

      sesvc.exe 阿萨德

      C# Node.Js 一文让你彻底理解 Java HashMap 和 ConcurrentHashMap 2018-07-25 分类:JAVA开发、编程开发、首页精华0人评论 来源:crossoverjie.top 分享到:更多0 前言 Map 这样的 Key Value 在软件开发中是非常...

    Global site tag (gtag.js) - Google Analytics