C# 内存操作常用函数
来源:互联网 发布:真皮床哪个品牌好 知乎 编辑:程序博客网 时间:2024/05/29 03:23
写这篇文章是有一个起因的...
最近在学socket编程 翻源代码的时候 无意发现一个没有用过的函数Marshal.UnsafeAddrOfPinnedArrayElement()
它的原形如下:
public static IntPtr UnsafeAddrOfPinnedArrayElement(Array arr, int index);
这个静态函数的作用是返回一个数组第index个元素的首地址
而且没有值类型和引用类型的限制 没有数组索引越界的检查
简单的说 它是一个托管的C#数组与非托管指针的一个合法的转换接口
这样 我们就很容易写出以下代码了:
unsafe{ byte[] array = new byte[1024]; byte* pArray = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(array, 0); for (int i = 0, j = array.Length; i < j; i++) { (pArray[i])++; }}
他的好处是 在改变数组值的时候没有进行索引越界检查 效率会略微提高
当然 它只在极端需要效率的场合值得一用 或者是密集计算的场合懒得用C++写底层然后dllImport的时候用
它比普通的数组遍历能提高5%~15%的效能
毕竟 指针玩不好终究是个挺危险的东西..
另外一个有趣的函数是Buffer.BlockCopy()
原形如下:
public static void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count);
它也是用来复制内存区块的 和Array.Copy()不同 它最后一个参数count是表示复制的字节长度 而不是复制元素的数量
当然 这个函数的源数组和目的数组 类型可以不同 但是必须是基本类型(原始的各种数值型)
大致用法是这样:
double[] src = new double[5] { 1, 2, 3, 4, 5 };byte[] dst = new byte[40];Buffer.BlockCopy(src, 0, dst, 0, 40);
于是它就会按照double的IEEE内存标准 原封不动的复制到byte[]里去了...
事实上用上面提供的数组转指针 以及Marshal.Copy()方法也可以做到 不过这函数方便了很多
BitConverter类中提供了一系列静态函数 如GetBytes()和ToInt32()等 可以在基本类型和byte[]间进行转换
它比较适合单一转换的场合 如果需要循环转换的话还是使用BlockCopy或者直接unsafe指针读取更为方便
Marshal.Copy()提供了一系列的重载 可以提供托管数组和IntPtr之间的内存复制功能...
于是经常我们会看到一段GDI图像处理代码如下:
Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName);BitmapData data = bitmap.LockBits(new Rectangle(new Point(), bitmap.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);byte[] buffer = new byte[data.Stride * data.Height];Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);for (int h = 0; h < data.Height; h++){ for (int w = 0; w < data.Width; w++) { //... }}Marshal.Copy(buffer, 0, data.Scan0, buffer.Length);bitmap.UnlockBits(data);
事实上 直接使用指针可以在不创建缓冲区的情况下对非托管数据进行操作
如果是对一定批量的非托管数据进行处理 可以使用缓冲区(如上面的情况可以图像逐行复制处理)
或者直接使用unsafe代码 更为高效
另外一件比较讨厌的事 Marshal.Copy()没有提供从指针到指针复制的函数重载 不过我们可以用API来实现它:
[DllImport("kernel32.dll")]public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);
IntPtr当然也可以使用void*来替代或者重载 以达到更好的灵活性
结合数组取地址的方式 这个函数可以提供不同类型的 非基础类型数组之间的复制
总之 因为C#的特殊支持 可以很轻松的直接处理内存 写出类似C++的指针运算代码
在保证足够安全性的前提下 它会带来意想不到的语法便利 以及高效的处理速率
- C# 内存操作常用函数
- 常用的内存操作函数
- 常用的内存操作函数
- 常用函数-------内存、指针操作函数
- 常用文件及内存操作函数
- c++中常用的内存操作函数
- c#读写共享内存操作函数封装
- 字符串操作:C#常用函数介绍
- C——常用字符串函数、内存操作函数总结
- Linux常用C函数-内存及字符串操作篇
- Linux 常用C函数(内存及字符串操作篇2)
- Linux 常用C函数(内存及字符串操作篇1)
- Linux 常用C函数(内存及字符串操作篇1)
- Linux 常用C函数(内存及字符串操作篇2)
- Linux常用C函数—内存及字符串操作篇
- Linux常用C函数—内存及字符串操作篇
- Linux 常用C函数(内存及字符串操作篇2)
- Linux常用C函数—内存及字符串操作篇
- 倒车的时候要加油门吗?
- 云计算基础知识-2. 虚拟化技术
- 面试训练二叉树两结点的最低共同父结点
- 把数据库的表从sqlserver转移到mysql
- 统计一个长度为2的子字符串在另一个字符串中出现的次数.例如:假定输入的字符串为“asd asasdfg asd as zx67 asd mklo”,子字符串为“as”,函数返回值为6。
- C# 内存操作常用函数
- 手波的车起步的时候为什么倒车比前进要容易的多
- 最经典最动听的1000首欧美金曲
- Linux字符驱动中动态分配设备号与动态生成设备节点
- linux内核多线程
- C++中的临时对象都是const类型
- C++中的new和delete
- VS2010帮助文档安装的问题
- 通过批处理来启动和关闭Oracle和SqlServer数据库服务