.NET(C#):3種方法寫出類似C/C++的指標作業碼

來源:互聯網
上載者:User

嘗試用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++)<<" ";

     

//換行

puts("");

     

//輸出:FF 00 AA 00

//注意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類
  • 2. 非託管堆:Marshal類
  • 3. 託管函數棧:unsafe和stackalloc關鍵字

 

 

返回目錄

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參數

unsafe static void Main()

{

    /* **************************

     * 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++代碼一樣的!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.