将 C# int 数组传递给 C++
Passing C# int array to C++
所以我将我在 C# 中创建的数组传递给函数:
options = new int[3];
options[0] = 3;
options[1] = 5;
options[2] = 4;
formatvalue(options);// the function header is: formatvalue(object options)
从这里开始它通过一些接口并最终在一个 c++ 项目中结束。
在 C++ 中,函数头类似于:
formatvalue(System::Object^ value)
在c++函数中,我只想读取数据。传入数组的全部意义在于,我不必为该函数提供大量不同的参数。这花了一些时间才弄清楚,因为传递除了变量以外的任何东西都会给我一些非常奇怪的值。起初我试图将一个整数结构传递给它,但是在编译时将 System::Object 转换成任何东西都很困难。所以最后我得到了这段有效的 C++ 代码:
int* test;
memcpy(&test, &value, sizeof(value));
int x;
for (int i = 0; i < 16; i++)
{
x = *(test + i);
}
奇怪的是,当我取消引用测试时,它给了我一些奇怪的垃圾,直到我到达我的数组开始的 *(test+4) :D。这是内存的样子:
f8 ae 0b c2 fe 7f 00 00 03 00 00 00 00(数组从这里开始)00 00 00 03 00 00 00 05 00 00 00 04
我认为测试指向的地址应该是数组中的第一个值,但在我的数组实际开始之前我有 13 个字节的垃圾。有人告诉我这 13 个字节可能是 dope vector?这是有道理的,因为我的数组的长度是 3,并且在前 13 个字节中有一个随机的 3。
所以问题:
- 为什么test的地址不是数组的开头?
- 前 13 个字节是 dope vector 吗?
- 我对 pointers/memcpy 的用法正确吗?
编辑 1:更改了第一个问题。
我建议阅读有关 C++/CLI 的书籍或文章,因为您将希望对托管和非托管代码以及内存的行为方式有一个相当好的理解。
它不是一个 dope vector,开头的数据也不是垃圾(没有它你的程序就不能运行!)。
数组的开头实际上是 16 个字节而不是 13 个字节(请参阅内存对齐)。请记住,内存在您的机器上存储为小端存储,因此 3 将存储为 03 00 00 00
而不是 00 00 00 03
.
根据您提供的内存,您似乎正在将 int* test
初始化为 C# 数组的地址。 .NET 对象通过方法 table 指针跟踪信息类型信息的额外开销。数组还有一个字段,用于存储数组中元素的数量。在这种情况下:
f8 ae 0b c2 fe 7f 00 00 <- Method table pointer (7ffec20baef8)
03 00 00 00 <- The number of elements in the array
00 00 00 00 <- Padding
03 00 00 00 05 00 00 00 04 00 00 00 <- Array data
你想做的是类似这样的事情:
array<int>^ arr = /* ... */;
pin_ptr<int> ptr = &arr[0];
int* test = ptr;
获取数组中第一个元素的地址。您还必须使用 pin_ptr
,因为可能会发生 GC 并将数组移动到内存中的不同区域。
或者,您可以考虑使用 P/Invoke 而不是 C++/CLI。使用 P/Invoke 时,CLR 自动处理固定并确保指向数组和其他类型的指针正确传递到本机方法。
编辑:我忘了问题 3。
memcpy
获取源指针、目标指针和要复制的字节总数(注意这与数组中的元素数不同)。如果 value
是 formatvalue
的参数,那么 sizeof(value)
将与 sizeof(void*)
相同。因此,如果源指针和目标指针正确,您只会复制 8 个字节——数组的前两个元素。
但是,您的源指针和目标指针不正确。您已经定义了一个名为 test
的 int*
,但正在将 test
的地址传递给 memcpy
。您可能希望预先分配内存,将该内存的地址分配给 test
,然后将 test
按值传递给 memcpy
,而不是按引用传递。
像这样的东西会起作用:
int* test = allocate_enough_space_for_the_array();
array<int>^ arr = dynamic_cast<array<int>^>(value);
pin_ptr<int> ptr = &arr[0];
int* pval = ptr;
memcpy(test, pval, arr->Length * sizeof(int));
所以我将我在 C# 中创建的数组传递给函数:
options = new int[3];
options[0] = 3;
options[1] = 5;
options[2] = 4;
formatvalue(options);// the function header is: formatvalue(object options)
从这里开始它通过一些接口并最终在一个 c++ 项目中结束。
在 C++ 中,函数头类似于:
formatvalue(System::Object^ value)
在c++函数中,我只想读取数据。传入数组的全部意义在于,我不必为该函数提供大量不同的参数。这花了一些时间才弄清楚,因为传递除了变量以外的任何东西都会给我一些非常奇怪的值。起初我试图将一个整数结构传递给它,但是在编译时将 System::Object 转换成任何东西都很困难。所以最后我得到了这段有效的 C++ 代码:
int* test;
memcpy(&test, &value, sizeof(value));
int x;
for (int i = 0; i < 16; i++)
{
x = *(test + i);
}
奇怪的是,当我取消引用测试时,它给了我一些奇怪的垃圾,直到我到达我的数组开始的 *(test+4) :D。这是内存的样子:
f8 ae 0b c2 fe 7f 00 00 03 00 00 00 00(数组从这里开始)00 00 00 03 00 00 00 05 00 00 00 04
我认为测试指向的地址应该是数组中的第一个值,但在我的数组实际开始之前我有 13 个字节的垃圾。有人告诉我这 13 个字节可能是 dope vector?这是有道理的,因为我的数组的长度是 3,并且在前 13 个字节中有一个随机的 3。
所以问题:
- 为什么test的地址不是数组的开头?
- 前 13 个字节是 dope vector 吗?
- 我对 pointers/memcpy 的用法正确吗?
编辑 1:更改了第一个问题。
我建议阅读有关 C++/CLI 的书籍或文章,因为您将希望对托管和非托管代码以及内存的行为方式有一个相当好的理解。
它不是一个 dope vector,开头的数据也不是垃圾(没有它你的程序就不能运行!)。
数组的开头实际上是 16 个字节而不是 13 个字节(请参阅内存对齐)。请记住,内存在您的机器上存储为小端存储,因此 3 将存储为 03 00 00 00
而不是 00 00 00 03
.
根据您提供的内存,您似乎正在将 int* test
初始化为 C# 数组的地址。 .NET 对象通过方法 table 指针跟踪信息类型信息的额外开销。数组还有一个字段,用于存储数组中元素的数量。在这种情况下:
f8 ae 0b c2 fe 7f 00 00 <- Method table pointer (7ffec20baef8)
03 00 00 00 <- The number of elements in the array
00 00 00 00 <- Padding
03 00 00 00 05 00 00 00 04 00 00 00 <- Array data
你想做的是类似这样的事情:
array<int>^ arr = /* ... */;
pin_ptr<int> ptr = &arr[0];
int* test = ptr;
获取数组中第一个元素的地址。您还必须使用 pin_ptr
,因为可能会发生 GC 并将数组移动到内存中的不同区域。
或者,您可以考虑使用 P/Invoke 而不是 C++/CLI。使用 P/Invoke 时,CLR 自动处理固定并确保指向数组和其他类型的指针正确传递到本机方法。
编辑:我忘了问题 3。
memcpy
获取源指针、目标指针和要复制的字节总数(注意这与数组中的元素数不同)。如果 value
是 formatvalue
的参数,那么 sizeof(value)
将与 sizeof(void*)
相同。因此,如果源指针和目标指针正确,您只会复制 8 个字节——数组的前两个元素。
但是,您的源指针和目标指针不正确。您已经定义了一个名为 test
的 int*
,但正在将 test
的地址传递给 memcpy
。您可能希望预先分配内存,将该内存的地址分配给 test
,然后将 test
按值传递给 memcpy
,而不是按引用传递。
像这样的东西会起作用:
int* test = allocate_enough_space_for_the_array();
array<int>^ arr = dynamic_cast<array<int>^>(value);
pin_ptr<int> ptr = &arr[0];
int* pval = ptr;
memcpy(test, pval, arr->Length * sizeof(int));