淺談C#數組結構
在 C# 代碼中,大量摻雜著包括普通類型和C#數組結構,如定義 PE 文件頭結構的 IMAGE_OPTIONAL_HEADER 結構定義如下:
- typedef struct _IMAGE_DATA_DIRECTORY {
- DWORD VirtualAddress;
- DWORD Size;
- } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
- #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES16
- typedef struct _IMAGE_OPTIONAL_HEADER {
- WORDMagic;
- //...
- DWORD NumberOfRvaAndSizes;
- IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
- } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
在C#中這樣在結構中使用數組是完全正確的,因為這些數組將作為整個結構的一部分,在對結構操作時直接訪問結構所在內存塊。但在 C# 這類語言中,則無法直接如此使用,因為數組是作為一種特殊的引用類型存在的,如定義:
- public struct IMAGE_DATA_DIRECTORY
- {
- public uint VirtualAddress;
- public uint Size;
- }
- public struct IMAGE_OPTIONAL_HEADER
- {
- public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
- public ushort Magic;
- //...
- public uint NumberOfRvaAndSizes;
- public IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
- }
這樣定義C#數組結構中的錯誤,會在編譯時獲得一個 CS0650 錯誤:
語法錯誤,錯誤的數組聲明符。若要聲明托管數組,秩說明符應位于變量標識符之前,如果改用 C# 中引用類型的類似定義語法,如:
- public struct IMAGE_OPTIONAL_HEADER
- {
- public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
- public ushort Magic;
- //...
- public uint NumberOfRvaAndSizes;
- public IMAGE_DATA_DIRECTORY[] DataDirectory =
new IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];- }
則得到一個 CS0573 錯誤:結構中不能有實例字段初始值設定項
因為結構內是不能夠有引用類型的初始化的,這與 class 的初始化工作不同。如此一來只能將數組的初始化放到構造函數中,而且結構還不能有無參數的缺省構造函數,真是麻煩。
- public struct IMAGE_OPTIONAL_HEADER
- {
- public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
- public ushort Magic;
- public uint NumberOfRvaAndSizes;
- public IMAGE_DATA_DIRECTORY[] DataDirectory;
- public IMAGE_OPTIONAL_HEADER(IntPtr ptr)
- {
- Magic = 0;
- NumberOfRvaAndSizes = 0;
- DataDirectory = new IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
- }
- }
這樣一來看起來似乎能使了,但如果使用 Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER)) 看看就會發現,其長度根本就跟 C/C++ 中定義的長度不同。問題還是在于結構中數組,雖然看起來此數組是定義在結構內,但實際上在此結構中只有一個指向 IMAGE_DATA_DIRECTORY[] 數組類型的指針而已,本應保存在 DataDirectory 未知的數組內容,是在托管堆中。以上介紹C#數組結構中的錯誤。
【編輯推薦】