2015-12-21 100 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