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 分配在堆上。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386266.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百度、华为、京东、B站最新面试题汇集,实战篇

前言 回顾一下自己这段时间的经历&#xff0c;因公司突然通知裁员&#xff0c;我匆匆忙忙地出去面了几家&#xff0c;但最终都没有拿到offer&#xff0c;我感觉今年的寒冬有点冷。公司开始第二波裁员&#xff0c;我决定主动拿赔偿走人。后续的面试过程我做了一些准备&#xff…

php 常用正则运算

$regx "/^[0-9]*$/"; var_dump(preg_match($regx, $phone));常用的正则运算&#xff1a; •验证数字&#xff1a;^[0-9]*$ •验证n位的数字&#xff1a;^\d{n}$ •验证至少n位数字&#xff1a;^\d{n,}$ •验证m-n位的数字&#xff1a;^\d{m,n}$ •验证零和非零开头的…

百度、阿里、滴滴、新浪的面试心经总结,一线互联网公司面经总结

开头 技术的发展产生了程序员这个职位&#xff0c;从这些年各大互联网公司曝光的一些员工收入水平来看&#xff0c;程序员的工资还是相对比较高的&#xff0c;可是我们在互联网上还听到了另外一种声音&#xff0c;很多程序员想转行&#xff0c;特别是大龄程序员&#xff0c;这…

go build编译不同环境

#usage: go build [-o output] [-i] [build flags] [packages] go build的使用比较简洁&#xff0c;所有的参数都可以忽略&#xff0c;直到只有go build&#xff0c;这个时候意味着使用当前目录进行编译&#xff0c;下面的几条命令是等价的&#xff1a; go buildgo build .go b…

百度、阿里、滴滴、新浪的面试心经总结,源码+原理+手写框架

前言 作为一个程序员&#xff0c;如果你在新知识、新技术面前仍一无所知&#xff0c;依然吃着十多年前的老本&#xff0c;那你在知识技术上肯定落伍&#xff0c;如果又未能进入管理层面&#xff0c;那你肯定就会被长江的后浪拍在沙滩上了。 而不少与时俱进、善于学习的程序员…

LINQ to SQL

Moved to http://blog.tangcs.com/2008/10/06/linq-to-sql/转载于:https://www.cnblogs.com/WarrenTang/archive/2008/10/06/1304727.html

目前最全的《Android面试题及解析》!面试真题解析

背景 惯例&#xff0c;先简单陈述一下自己的&#xff0c;91年生人&#xff0c;164年三本毕业后在深圳工作&#xff0c;末流小公司&#xff0c;工资13k&#xff0c;无房&#xff0c;无车&#xff0c;无户口。 那时候感觉生活也还行&#xff0c;父母有退休金&#xff0c;我基本…

有效的形成传感器(执行器)的控制方式

其实为了增加闭环的话需要再增加一个传感器&#xff0c;比如编码器。 转载于:https://www.cnblogs.com/yjphhw/p/11285145.html

直接上干货!技术水平真的很重要!复习指南

开头 25岁那年&#xff0c;我从京东离职&#xff0c;入职百度。 在百度认识了当时的架构师久哥&#xff08;T9级别&#xff09;&#xff0c;因为他的一番话&#xff0c;彻底改变了我的职业生涯&#xff0c;短短三年的时间&#xff0c;我从一枚普通程序员成长为别人眼中的技术…

实战HMM-Viterbi角色标注地名识别

http://www.hankcs.com/nlp/ner/place-names-to-identify-actual-hmm-viterbi-role-labeling.html 命名实体识别&#xff08;Named Entity Recognition&#xff09;也是自然语言处理中的一个难关&#xff0c;特别是中文这样没有大小写等固定形态的语言。上次介绍过《实战HMM-Vi…

看完99%的人都学会了!9次Android面试经验总结,我先收藏为敬

我们都是被圈养的人&#xff1f; 我的朋友程序员K&#xff0c;说他在电力行业的一家软件公司做了八年Android开发&#xff0c;用到的各种技术&#xff0c;数据库&#xff0c;网络请求&#xff0c;事件传递&#xff0c;响应框架都很熟悉&#xff0c;甚至JNI/NDK/Framework&…

从外包公司到今日头条offer,含BATJM大厂

前言 最近有不少人问我这样一个问题&#xff1a;「我刚接触编程&#xff0c;准备学习下Android开发&#xff0c;但是担心现在市场饱和了&#xff0c;Android开发的前景怎么样&#xff1f;」 想着可能有很多人都有这样的担心&#xff0c;于是就赶紧写篇文章&#xff0c;来跟你…

PTA -- A1046 Shortest Distance

题意及思路 题意&#xff1a;有N个节点&#xff08;1至N&#xff09;&#xff0c;求给定的st号到en号的距离最小值&#xff0c;这些点构成一个环&#xff0c;即1->2 ... ->N ->1。 思路&#xff1a;第一步&#xff0c;预处理操作&#xff0c;以dis[ i ] 表示&#xff…

从外包公司到今日头条offer,聪明人已经收藏了!

开头 让我们一起来看看&#xff0c;字节跳动的第三面&#xff0c;面试官都问了什么&#xff1f;&#xff08;第一二面的题目及答案已整理&#xff0c;需要的可以在文末领取&#xff09; 从七月中旬开始&#xff0c;我前前后后差不多一共投递了八十份简历&#xff0c;到目前为…

程序从高版本降到2.0,数据集报错 TypedTableBase

错误 命名空间“System.Data”中不存在类型或命名空间名称“TypedTableBase”(是缺少程序集引用吗?) 解决&#xff1a; 该错误出现在自动生成的XXX.Designer.cs里。 .NET 3.5 : public partial class T_OPERATOR_WLDataTable : global::System.Data.TypedTableBase<T_OPER…

从外包月薪5K到阿里月薪15K,原理+实战+视频+源码

前言 转眼间&#xff0c;2020 年已过去一大半了&#xff0c;2020 年很难&#xff0c;各企业裁员的消息蛮多的&#xff0c;降职&#xff0c;不发年终奖等等。2020 年确实是艰难的一年。然而生活总是要继续&#xff0c;时间不给你丧的机会&#xff01;如果我们能坚持下来&#x…

C#编写的发送手机短信的类库 C#开发短信的方法和简介 短信编程实例

发送手机短信源代码(针对国内短信编码) / ///文 件&#xff1a;PDUdecoding.cs ///概 要&#xff1a;针对国内短信编码&#xff08;USC2&#xff09; ///组成结构&#xff1a;包含四个函数&#xff1a; /// smsDecodedCenterNumber(string srvCenterNumber) …

从新手到Flutter架构师,一篇就够!吐血整理

在开始回答前&#xff0c;先简单概括性地说说Linux现有的所有进程间IPC方式&#xff1a; 1. **管道&#xff1a;**在创建时分配一个page大小的内存&#xff0c;缓存区大小比较有限&#xff1b; 2. 消息队列&#xff1a;信息复制两次&#xff0c;额外的CPU消耗&#xff1b;不合…

小程序自定义组件中observer函数的应用

<!-- 单个数据监听 --> <view>白菜</view> <view>价格&#xff1a;{{price}}</view> <!-- bindinput输入时触发方法 --> <view>数量&#xff1a; <input type"number" bindinputchangee value"{{num1}}">…

从新手到Flutter架构师,一篇就够!学习路线+知识点梳理

前言 IT行业薪水高&#xff0c;这是众所周知的&#xff0c;所以很多人大学都选择IT相关专业&#xff0c;即使非该专业的人&#xff0c;毕业了也想去一个培训机构镀镀金&#xff0c;进入这一行业。 但是有关这个行业35岁就退休的说法&#xff0c;也一直盛传。 加上这几年不断…