2015-12-21 97 views
4

我正在將C#中的結構傳遞給C++。在C++/C之間傳遞結構中的字符串/數組#

C#代碼:

[StructLayout(LayoutKind.Sequential, Pack = 8)] 
public struct Data 
{ 
[MarshalAs(UnmanagedType.U4)] 
public int number; 

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] 
public int[] array; 

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)] 
public string buffer; 
} 

C++代碼:

struct Data 
{ 
public: 
    int number; 
    int array[5]; 
    char buffer[512]; 
    //char *buffer; 
}; 

上述方法工作得很好。而是如果我用指針來處理數據C++我得到的錯誤爲:

未處理的異常:System.AccessViolationException:嘗試讀取或寫入受保護的內存

struct Data 
{ 
public: 
    int number; 
    int *array; 
    char *buffer; 
}; 

爲什麼不能我處理用指針在這裏? 通過指針處理這種情況是有利的嗎?

+0

如果不更改C#聲明,則無法更改C++聲明。之後你很快會發現int []不會飛。帶指針的結構是一個非常討厭的內存管理問題,但是誰又不能再次釋放內存是非常明確的。你必須自己承擔責任並使用IntPtr。並擔心C++代碼是否要深拷貝數組和字符串,如果沒有,那麼你有下一個問題保持這些指針有效。 –

回答

1

問題是您的數據在內存中的表現方式。

讓我們假設你有一個c#結構的實例,它封送到非託管代碼甚至是文件。

[StructLayout(LayoutKind.Sequential, Pack = 8)] 
public struct Data 
{ 
[MarshalAs(UnmanagedType.U4)] 
public int number = 5; 

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] 
public int[] array = {0, 1, 2, 3, 4}; 

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)] 

public string buffer = "Happy new Year"; 
} 

根據這一點,你的內存佈局將是這樣的(十六進制樣視圖):

05 00 00 00 00 00 00 00 
01 00 00 00 02 00 00 00 
03 00 00 00 04 00 00 00 
00 48 00 61 00 70 00 70 
00 79 00 20 00 6E 00 65 
00 77 00 20 00 59 00 65 
00 61 00 72 

在這裏,我們第一個四個字節「05 00 00 00」,這意味着數字「 5「在你的」數字「變量的內存中。 (請注意,以顛倒的次序這些字節因爲英特爾架構LittleEndian,見Endiannes瞭解詳細信息)

然後我們未來五年的整數爲 「00 00 00 00」= 0, 「01 00 00 00」= 1,「02對於名爲「array」的數組,「00 00 00」= 2,「03 00 00 00」= 3,「04 00 00 00」= 4。

和字符串「緩衝」表示這樣的:

"00 48" = H 
"00 61" = a 
"00 70" = p 
"00 70" = p 
"00 79" = y 
"00 20" = <space> 
"00 6E" = n 
"00 65" = e 
"00 77" = w 
"00 20" = <space> 
"00 59" = Y 
"00 65" = e 
"00 61" = a 
"00 72" = r 

有一些技巧的。NET始終使用Unicode來存儲它的字符串變量。每個Unicode字符都是雙字節表示。

現在,對於該C++結構

struct Data 
{ 
public: 
    int number; 
    int array[5]; 
    char buffer[512]; 
    //char *buffer; 
}; 

的sizeof(int)的是4。所以的記憶變量 「號碼」 的內容= 「05 00 00 00」,這是5號。在內存塊「00 00 00 00」= 0,「01 00 00 00」= 1,「02 00 00 00」上佈置陣列[0],陣列1,陣列[2],陣列[3],陣列[4] 「= 2,」03 00 00 00「= 3,」04 00 00 00「= 4. 其他所有內容都保留在緩衝區[512]變量中。但在C++中,sizeof(char)== 1。字符數據類型通常用於用單字節編碼表示舊的ASCII樣式文本。您應該使用wchar_t來代替它,它非常適合Unicode編碼。

現在讓我們來看看

struct Data 
{ 
public: 
    int number; 
    int *array; 
    char *buffer; 
}; 

這種結構將在相同的內存佈局上述的投影。 如果您在32位環境(win32) 下運行,則「數組」指針的內容將爲「00 00 00 00」(4字節用於指針) 並且「緩衝區」指針將爲「01 00 00 00」 。

如果您在64位環境(win64) 下運行,「數組」指針的內容將爲「00 00 00 00 01 00 00 00」(指針爲8個字節),緩衝區指針爲「02 00 00 00 03 00 00 00「。

這些是一些指向誰知道在哪裏的無效指針。這就是爲什麼當你試圖解引用它們時你會得到訪問衝突。

+0

「每個Unicode字符都有其雙字節表示法」:這是不可能的;有太多了。 UTF-16以一個或兩個16位代碼單元編碼Unicode碼點。 –

+0

那麼,我應該離開我的代碼,還是修改它們以使用指針?哪個更安全?而且,轉向指針的優勢呢? – Joga

+0

我會保持原樣。 –

1

第一個結構的工作原理是因爲它在結構中分配了數組。 第二個是有問題的,因爲它只在結構中分配一個int指針和char指針(這是sizeof(void*)取決於您的平臺),而不是int數組。 如果你堅持使用指針,你必須自己分配和釋放內存(即newdelete[])。

+0

那麼,我應該離開我的代碼,還是修改它們以使用指針?哪個更安全?而且,轉向指針的優勢呢? – Joga