• 欢迎访问我爱CSharp学习网,这里有最新最全的C#书籍,C#视频。
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏我爱C#学习网吧
  • 推荐使用最新版Chrome浏览器和火狐浏览器访问本网站

C#:3种方法写出类似C/C++的指针操作代码

C#杂烩 52csharp 1244次浏览 1个评论 扫描二维码

尝试用3种.NET方法写出下面的C/C++指针操作代码。


注意所有.NET方法均不直接引用外部函数资源(DllImport),都是使用.NET中原生态提供的方法。


注意:


所有代码输出仅代表在32位和Little-Endian的CPU环境的运行结果。

 

来看这一段典型的C/C++指针操作代码:


/****************************
* C/C++ 代码
****************************/

int i = 0xAA00FF;
//i的内存存储形式:FF 00 AA 00

unsigned char *p = (unsigned char*)&i;
for(int i = 0; i < sizeof(i); i++)
{
cout<<hex<<uppercase<<setw(2)<<setfill('0')<<(int)(*p++)<<" ";
}
//输出:FF 00 AA 00
   
//换行
puts("");
     
//注意p在循环后的位置,p-3 指向变量i在内存中第二个字节
*(p-3) = 0xCC;

//现在内存存储形式:FF CC AA 00
//i变成xAACCFF = 11193599

cout << dec << i << endl;
//输出:11193599

 

逻辑上注释已经解释地很清楚了,上面代码会输出:


FF 00 AA 00
11193599

 

下面看.NET(C#)的执行,要求逻辑上和输出就要和上面代码一样!


注意:


由于.NET对象内存分布是不同于非托管的C/C++的,因此并不是所有类型都可以这样做。


1. 直接操作托管堆:Buffer类


这个很惊奇,直接在托管堆中就可以干这样的事情。就靠这个Buffer类(System命名空间内),简而言之Buffer类是以连续字节流的形式操作原始数据类型。用他的GetByte和SetByte方法也可以轻松实现上面C/C++代码。


/****************************
 * C# 代码(托管堆:Buffer类)
****************************/

int[] arr = new int[] { 0xAA00FF };
//内存存储形式:FF 00 AA 00

for (int i = 0; i < sizeof(int); i++)
{
Console.Write("{0:X2} ", Buffer.GetByte(arr, i));
}

//换行
Console.WriteLine();

Buffer.SetByte(arr, 1, 0xCC);
//修改成:FF CC AA 00
//arr[0] = 0xAACCFF = 11193599
Console.WriteLine(arr[0]);


逻辑和输出都一样!

 

2. 非托管堆:Marshal类


接下来使用平台或者COM调用常用到的Marshal类(在System.Runtime.InteropServices命名空间内)。它主要用来在于非托管调用中环境对象的封送处理。

 

因此要处理我们程序的数据,需要先在非托管环境中建立这块数据,首先使用Marshal类的AllocHGlobal方法,这个方法调用了kernel32.dll的LocalAlloc函数,从而在进程的非托管堆中进行内存分配。然后就可以使用Marshal类的Readxxx和Writexxx方法来进行数据操作了。最后别忘了需要手动释放非托管堆中的资源!


代码:


/* **************************
* C#代码(非托管堆:Marshal类)
* **************************/

using System.Runtime.InteropServices;

//分配空间
var p = Marshal.AllocHGlobal(4);
//赋值
Marshal.WriteInt32(p, 0xAA00FF);
//内存存储形式:FF 00 AA 00

for (int i = 0; i < Marshal.SizeOf(typeof(int)); i++)
{
Console.Write("{0:X2} ", Marshal.ReadByte(p, i));
}

//换行
Console.WriteLine();

//修改成:FF CC AA 00
//p = 0xAACCFF = 11193599
Marshal.WriteByte(p, 1, 0xCC);
Console.WriteLine(Marshal.ReadInt32(p));

//清理非托管堆中的资源
Marshal.FreeHGlobal(p);


同样,逻辑相同,输出相同。


3. 托管函数栈:unsafe和stackalloc关键字


事实上这是大多数人想到用C#写类似指针代码的方式,这里unsafe关键字是必须的。stackalloc仅为了演示声明一个在本地函数栈的数组。


这个方法缺点是:必须使用unsafe代码。


优点是:指针逻辑代码和C/C++太像了。其次是stackalloc声明数组的空间是在栈中的,是非常有效率的,减少GC的压力。

 

代码:


//标记unsafe关键字。
//编译需要Visual Studio允许不安全代码或者csc的unsafe参数

/* **************************
* C#代码(函数栈:unsafe代码)
* **************************/

//分配空间
//这里直接声明变量也可以
//仅为做示例,我们把变量包在数组里!
//注意这里的数组不在堆里,而在本地函数栈中!
int* arr = stackalloc int[1];
*arr = 0xAA00FF;

//指针
byte* p = (byte*)arr;

//内存存储形式:FF 00 AA 00
for (int i = 0; i < Marshal.SizeOf(typeof(int)); i++)
{
Console.Write("{0:X2} ", (int)(*p++));
}
//换行
Console.WriteLine();

//修改成:FF CC AA 00
//p = 0xAACCFF = 11193599
*(p - 3) = 0xCC;

Console.WriteLine(*arr);


同样逻辑和输出是和C/C++代码一样的!


我爱CSharp学习网 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明C#:3种方法写出类似C/C++的指针操作代码
喜欢 (4)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. a149072017-11-18 16:29 回复 Windows 10 | Chrome 62.0.3202.94