在C++/C之间传递结构中的字符串/数组#
我正在将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;
};
为什么不能我处理用指针在这里? 通过指针处理这种情况是有利的吗?
问题是您的数据在内存中的表现方式。
让我们假设你有一个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“。
这些是一些指向谁知道在哪里的无效指针。这就是为什么当你试图解引用它们时你会得到访问冲突。
“每个Unicode字符都有其双字节表示法”:这是不可能的;有太多了。 UTF-16以一个或两个16位代码单元编码Unicode码点。 –
那么,我应该离开我的代码,还是修改它们以使用指针?哪个更安全?而且,转向指针的优势呢? – Joga
我会保持原样。 –
第一个结构的工作原理是因为它在结构中分配了数组。 第二个是有问题的,因为它只在结构中分配一个int
指针和char
指针(这是sizeof(void*)
取决于您的平台),而不是int
数组。 如果你坚持使用指针,你必须自己分配和释放内存(即new
和delete[]
)。
那么,我应该离开我的代码,还是修改它们以使用指针?哪个更安全?而且,转向指针的优势呢? – Joga
如果不更改C#声明,则无法更改C++声明。之后你很快会发现int []不会飞。带指针的结构是一个非常讨厌的内存管理问题,但是谁又不能再次释放内存是非常明确的。你必须自己承担责任并使用IntPtr。并担心C++代码是否要深拷贝数组和字符串,如果没有,那么你有下一个问题保持这些指针有效。 –