• C# unsafe模式内存操作深入探索


    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class CTile
        {
            public CTileData _dat;
            public int x;
        }
    
        //结构体可能分配在堆上,也可能分配在栈上
    
        //1,结构体中无引用类型,则:
        //a:若该结构体类型的变量X是类的内部成员,由于类是引用类型,则X分配在堆上
        //b:若非a的情况,则结构体分配在栈上
        unsafe struct CTileData//为了避开C#数组,因为它是一个引用类型。
        {
            public int var1;
            public float var2;
            public fixed sbyte name[6]; //使用C++风格的定长数组,避免C#风格的引用数组
            public float var3;
        }
        
        //2,结构体中有引用类型,则该结构体类型的变量分配在堆上
        struct CTileData2
        {
            public int var1;
            public float var2;
            public string name;//有引用类型,结构体无论如何都分配在堆上了
            public float var3;
        }
    
        unsafe class Program
        {
            [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            static extern void MemCopy(void* dest, void* src, int count);
    
            static void Main(string[] args)
            {
                unsafe
                {
    
                    var tile = new CTile();
                    var dat = new CTileData();
    
                    //栈上的结构体,可以直接取地址,堆上的则不行,因为堆上对象的地址是不定的(原因是内存管理)
                    //同理,堆上的任何对象都不可直接取地址,必须使用fixed才行
                    CTileData* ptd = &dat; 
    
                    var ms = new MemoryStream();
                    var binWr = new BinaryWriter(ms, Encoding.ASCII);
                    binWr.Write(10);
                    binWr.Write(3.2f);
                    binWr.Write("hello");//先写入1字节长度(也就是说字符串长度最大256???),然后写入hello
                    binWr.Write(109.9f);
    
                    var bts = ms.GetBuffer();
    
                    fixed (void* pbts = bts)//堆对象,必须使用fixed语法才能取地址
                    {
                        var sz = sizeof(CTileData);
                        MemCopy(ptd, pbts, sz);
    
                        fixed (void* pt = &tile._dat)//堆上的结构体(堆对象),必须使用fixed语法才能取地址
                        {
                            MemCopy(pt, pbts, sz);
                        }
    
                    }
    
                    var v1 = ptd->var1;                             //10
                    var v2 = ptd->var2;                             //3.2
                    var strlen = *(ptd->name);                      //取一字节,字符串长度 5
                    var straddr = ptd->name + 1;                    //跳过一字节,到达字符串起始地址
                    string name = new string(straddr, 0, strlen);   //hello
                    var v3 = ptd->var3;                             //这里数据不对,原因????
                    
                    //结论:C#真不适合做内存操作,若使用marshal,虽然方便了一些,但要经过一次内存申请和一次内存释放,一次转换到C#结构的过程,很蹩脚
                }
            }
        }
    }
  • 相关阅读:
    js获取服务器值以及服务器获取客户端值
    兼容IE Firefox的table自动换行
    sql行转列,列转行
    JS 压缩解压工具
    ASP.NET组织结构图的画法——数据来源读取数据库
    ANGULAR7的应用和跨域问题解决
    Ajax的使用之ScriptManager
    【.NET序列化和反序列化】基本篇
    Web Service的安全访问【SoapHeader身份认证】
    【C#3.0本质论 第一章】C#和.NET Framework概览
  • 原文地址:https://www.cnblogs.com/timeObjserver/p/8728282.html
Copyright © 2020-2023  润新知