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[]; //使用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();
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; //
var v2 = ptd->var2; //3.2
var strlen = *(ptd->name); //取一字节,字符串长度 5
var straddr = ptd->name + ; //跳过一字节,到达字符串起始地址
string name = new string(straddr, , strlen); //hello
var v3 = ptd->var3; //这里数据不对,原因???? //结论:C#真不适合做内存操作,若使用marshal,虽然方便了一些,但要经过一次内存申请和一次内存释放,一次转换到C#结构的过程,很蹩脚
}
}
}
}
C# unsafe模式内存操作深入探索的更多相关文章
- Linux操作系统基础(四)保护模式内存管理(2)【转】
转自:http://blog.csdn.net/rosetta/article/details/8570681 Linux操作系统基础(四)保护模式内存管理(2) 转载请注明出处:http://blo ...
- Java API —— IO流(数据操作流 & 内存操作流 & 打印流 & 标准输入输出流 & 随机访问流 & 合并流 & 序列化流 & Properties & NIO)
1.操作基本数据类型的流 1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出 ...
- 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 ...
- LabVIEW之生产者/消费者模式--队列操作 彭会锋
LabVIEW之生产者/消费者模式--队列操作 彭会锋 本文章主要是对学习LabVIEW之生产者/消费者模式的学习笔记,其中涉及到同步控制技术-队列.事件.状态机.生产者-消费者模式,这几种技术在在本 ...
- java 21-11 数据输入、输出流和内存操作流
IO数据流: 可以读写基本数据类型的数据 数据输入流:DataInputStream DataInputStream(InputStream in) 数据输出流:DataOutputStream ...
- 【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误
原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构 ...
- c++ void,内存操作函数
void的含义 void的字面意思是“无类型”, void * 则为“无类型指针”, void * 可以指向任何类型的数据 void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变 ...
- java基础知识回顾之javaIO类--内存操作流ByteArrayInputStream和ByteArrayOutputSteam(操作字节数组)
直接看代码: package cn.itcast.io.p6.bytestream; import java.io.ByteArrayInputStream; import java.io.ByteA ...
- 【C++基础】内存操作 getMemory改错
内存操作的考察点:①指针 ②变量生存期及作用范围 ③动态内存申请和释放 笔试题************************************************************* ...
随机推荐
- HihoCoder 1075 开锁魔法III(概率DP+组合)
描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它 ...
- tab页面自动跳转原因【在控制ul和li的时候没有细分】
效果图 存储buy的tab跳转js代码 $(function() { $('.tabPanel ul li').click(function(){ $(this).addClass('hit').si ...
- mysql 随机选取一条记录
要从tablename表中随机提取一条记录,大家一般的写法就是:SELECT * FROM tablename ORDER BY RAND() LIMIT 1. 1 2 3 4 5 6 7 8 9 1 ...
- CentOS 7.0 yum安装Apache、PHP和MySQL
centos7默认将mariadb视作mysql. p.s.因为mysql被oracle收购后,原作者担心mysql闭源,所以又写了一个mariadb,这个数据库可以理解为mysql的分支. 卸载ma ...
- altium常用快捷键记录
选中一个网络的点和线ctrl+h: 翻转器件的层 鼠标拖动+L: 镜像器件 鼠标拖动+x: 查看单一层shift+s: 隐藏/查看某些器件ctrl+d:
- BZOJ1556 墓地秘密
题意 费尽周折,终于将众将士的残骸运送到了KD军事基地地底层的大型墓地入口.KD的伙伴和战友们都参加了这次重大的送葬仪式.右边是一扇敞开的大门,进去便是墓地了,左边是一堵凹进去的墙,没有什么特别的地方 ...
- hashids 了解
用于隐藏真实的id 原理是从数字经过一个加盐(salted)算法产生一个哈希(hash)字符串.这样算法就是通过混淆使结果具有不可预测性,而唯一性依然由数字本身来达成,从而得到(类似 youtube ...
- getpwuid()
getpwuid函数是通过用户的uid查找用户的passwd数据.如果出错时,它们都返回一个空指针并设置errno的值,用户可以根据perror函数查看出错的信息. 外文名 getpwuid() 头文 ...
- filter添加水印
1filter写法 先定义自己的responseWrapper chain.doFilter(request,responseWrapper); responseWrapper来输出 package ...
- 如何用TortoiseSVN管理本地文档
1.安装(略) 2.搭建本地SVN版本管理数据库(服务器) (1)在本地磁盘上新建一个目录,例如E:\SVN,用来存储各种需要进行版本管理的文档:接着在该目录下再创建一个新的空目录,例如创建一个E:\ ...