C# 从C+更新浮点数组+;本机插件 我在试图从C++到C语言中传递一个数组时,看到了一个非常奇怪的问题。我正在使用Marshal.Copy(特别是:) 问题:浮点数组从C++到C是在生成数组中产生了一些楠< /代码>。< /强> (注意:我在Unity游戏引擎的环境中工作)

C# 从C+更新浮点数组+;本机插件 我在试图从C++到C语言中传递一个数组时,看到了一个非常奇怪的问题。我正在使用Marshal.Copy(特别是:) 问题:浮点数组从C++到C是在生成数组中产生了一些楠< /代码>。< /强> (注意:我在Unity游戏引擎的环境中工作),c#,c++,unity3d,interop,C#,C++,Unity3d,Interop,代码 C++代码: extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) { std::vector<float> results; std::vector<SOME_TYPE> key_points = <SOME_POINTS> for (auto iter

代码

C++代码:

extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
        std::vector<float> results;
        std::vector<SOME_TYPE> key_points = <SOME_POINTS>

        for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
            results.push_back(static_cast<float>(iter->pt.x));
            results.push_back(static_cast<float>(iter->pt.y));
        }

        *points = results.data();
        *count = results.size();

        //<Print results to csv here>

        return true;
}
extern“C”bool UNITY\u INTERFACE\u EXPORT UNITY\u INTERFACE\u API getSomeFloats(浮点**点,整数*计数){
std::矢量结果;
标准::矢量关键点=
对于(自动iter=关键点。开始();iter<关键点。结束();iter++){
结果:推回(静态铸型(iter->pt.x));
结果:推回(静态铸型(iter->pt.y));
}
*点=结果。数据();
*count=results.size();
//
返回true;
}
示例C#代码:

[DllImport(“NativePlugin”)]
私有静态外部bool getSomeFloats(ref IntPtr ptrResultItems,ref intresultitemslength);
私有静态浮点[]getFloatArrayFromNative(){
IntPtr ptrResultItems=IntPtr.Zero;
int resultItemsLength=0;
bool success=getSomeFloats(ref ptrResultItems,ref resultItemsLength);
float[]resultItems=null;
如果(成功){
//将结果加载到托管数组中。
resultItems=新浮点[resultItemsLength];
封送处理副本(ptrResultItems)
,结果
, 0
,结果长度);
// 
返回结果;
}否则{
Log(“获取一些浮点值时出错”);
返回新的浮点[]{-1,-2};
}
}

输出示例: 以以下为例: C++输出(PrimtExOut.CSV):

123456789

C#输出(打印输出_cs.csv):

123,南,789


我在这件事上完全被难住了。我只是不明白为什么只有一些(大约7/100)浮动返回
NaN
。是否有人有任何可能有帮助的建议/见解


谢谢

getSomeFloats
中返回的指针归
results
所有。在
getSomeFloats
返回之前,
results
的向量析构函数将释放该内存。当C#代码试图使用指针时,您正在访问未分配的内存,这会导致未定义的行为。在您的情况下,大多数数据尚未更改,但其中一些已经更改。可能任何或所有数据都已更改(如果内存已被重用),甚至程序崩溃(如果释放的内存已返回到操作系统)。

getSomeFloats
中返回的指针归
results
所有。在
getSomeFloats
返回之前,
results
的向量析构函数将释放该内存。当C#代码试图使用指针时,您正在访问未分配的内存,这会导致未定义的行为。在您的情况下,大多数数据尚未更改,但其中一些已经更改。可能任何或所有数据都已更改(如果内存已被重用),甚至程序崩溃(如果释放的内存已返回到操作系统)。

在代码中发现一些问题:

1<代码>标准::矢量结果在堆栈上声明。当函数返回时,它将消失。将其声明为指针

std::vector<float> *results = new std::vector<float>(10);
你的C#:

您必须从C++侧删除<代码> CopyPox*姿势< /C> >或将<代码> ItpTrTror 添加到C的边。

3。使用
UNITY\u接口\导出UNITY\u接口\ API

你不需要那个。当您想要使用Unity的内置函数(如)时,可以使用此选项。在这种情况下,您没有使用它

这应该做到:

#define DLLExport __declspec(dllexport)

extern "C"
{
    DLLExport void fillArrayNative(float* data, int count, int* outValue) 
    {
    }
}
4.C#有一个垃圾收集器,可以在内存中移动变量。如果你想从C++侧修改C,你必须把C数组插入。您只需要固定C#阵列。另一种方法是在C++端分配数组,将它返回到C,拷贝到C边的一个临时变量,然后在C++侧删除它。p> 5。将结果复制回数组,而不是指定它



推荐方法

const int arraySize = 44500;
float[] arrayToFill = new float[arraySize];

void Start()
{
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
        //Do something with arrayToFill[i]
    }
}
有很多方法可以做到这一点,其中有些方法非常慢。如果你想使用<代码> MSEHAL.IONS/COD>,你必须在C++端分配数组,否则你会遇到一些未定义的行为。 最快、最有效的方法是将数组作为全局变量分配到C#端。将数组及其长度传递到本机端。还传递一个第三参数,C++可以用来告诉C已经被更新或写入的索引的数量。 这比创建新数组、将其复制到C#变量并在每次调用函数时销毁它要好得多

这是您应该使用的:

C++:

#define DLLExport __declspec(dllexport)

extern "C"
{
    DLLExport void fillArrayNative(float* data, int count, int* outValue) 
    {
        std::vector<float> results;
        for (int i = 0; i < count; i++)
        {
            //Fill the array
            data[i] = results[i];
        }
        *outValue = results.size();
    }
}
[DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
private static extern void fillArrayNative(IntPtr data, int count, out int outValue);

public unsafe void getFillArrayNative(float[] outArray, int count, out int outValue)
{
    //Pin Memory
    fixed (float* p = outArray)
    {
        fillArrayNative((IntPtr)p, count, out outValue);
    }
}
用法

const int arraySize = 44500;
float[] arrayToFill = new float[arraySize];

void Start()
{
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
        //Do something with arrayToFill[i]
    }
}
const int arraySize=44500;
float[]数组填充=新的float[arraySize];
void Start()
{
int length=arrayToFill.length;
int filledAmount=0;
getFillArrayNative(阵列填充、长度、未填充的装载量);
//然后,您可以使用返回的filledAmount遍历它
对于(int i=0;i

这只是一个例子,它比我以前使用的所有其他方法都要快。避免以当前使用
封送处理的方式执行。复制
。如果您仍想按自己的方式执行或使用
封送.复制
则是执行此操作的适当方式,它需要在每次调用中分配、复制数据和取消分配内存

在代码中发现了一些问题:

1<代码>标准::矢量结果[DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)] private static extern void fillArrayNative(IntPtr data, int count, out int outValue); public unsafe void getFillArrayNative(float[] outArray, int count, out int outValue) { //Pin Memory fixed (float* p = outArray) { fillArrayNative((IntPtr)p, count, out outValue); } }
const int arraySize = 44500;
float[] arrayToFill = new float[arraySize];

void Start()
{
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
        //Do something with arrayToFill[i]
    }
}