char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
const char* _GetLookupStatus ()
{
// By default mono string marshaler creates .Net string for returned UTF-8 C string
// and calls free for returned value, thus returned strings should be allocated on heap
return MakeStringCopy([[delegateObject getStatus] UTF8String]);
}
函数的C#声明如下:
[DllImport ("__Internal")]
private static extern string _GetLookupStatus ();
这里有一些让我感到困惑的事情:
这是将字符串从iOS本机代码返回到C#的正确方法吗?
如何释放返回的字符串?
有更好的方法吗?
对此事有任何见识都值得赞赏。
谢谢。
#1 楼
1.没有。2.你必须自己做。
3.是的
如果在C或C ++端的函数内部分配内存,则必须释放它。我没有看到任何在侧面分配内存的代码,但我认为您已经离开了那部分。另外,请勿将在堆栈上声明的变量返回给C#。您最终将遇到未定义的行为,包括崩溃。
这是一个C ++解决方案。
对于C解决方案:
char* getByteArray()
{
//Create your array(Allocate memory)
char * arrayTest = malloc( 2 * sizeof(char) );
//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;
//Return it
return arrayTest;
}
int freeMem(char* arrayPtr){
free(arrayPtr);
return 0;
}
唯一的区别是C版本使用
malloc
和free
函数来分配和取消分配内存。C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);
//Test
void Start()
{
//Call and return the pointer
IntPtr returnedPtr = getIntArray();
//Create new Variable to Store the result
byte[] returnedResult = new byte[2];
//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);
//Free native memory
freeMem(returnedPtr);
//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}
请注意,这只是一个测试仅使用2个字符的char。您可以通过向C#函数添加
out int outValue
参数,然后向C函数添加int* outValue
参数来使字符串的大小动态化。然后,您可以在C侧将此字符的大小写入该参数,并从C#侧访问该大小。 然后可以将此大小传递给
Marshal.Copy
函数的最后一个参数,并删除当前的硬编码2值限制。我将把这个留给您做,但是如果感到困惑,请参阅此示例。 更好的解决方案是将
StringBuilder
传递到本机端,然后写入。不利的一面是您必须及时声明StringBuilder
的大小。 C ++:
void __cdecl _GetLookupStatus (char* data, size_t size)
{
strcpy_s(data, size, "Test");
}
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int _GetLookupStatus(StringBuilder data, int size);
//Test
void Start()
{
StringBuilder buffer = new StringBuilder(500);
_GetLookupStatus (buffer, buffer.Capacity);
string result = buffer.ToString();
}
如果您正在寻找最快的方法,则应在C#侧使用char数组,将其固定在C#侧,然后将其作为
IntPtr
发送给C。在C端,您可以使用strcpy_s
修改char数组。这样,就不会在C端分配任何内存。您只是在重用C#中char数组的内存。您可以在此处的答案结尾处看到float[]
示例。
评论
非常感谢您的详细说明!我已将MakeStringCopy()添加到代码段中。
–SePröbläm
17年9月9日在20:24
关于不返回在堆栈上传递的变量的另一个问题:它是否也适用于原始数据类型?我做了一些测试,字符串立即失败,但是int似乎可以工作。
–SePröbläm
17年6月9日在22:59
如果是int,float,bool,那很好。如果要使用&返回其引用,或者使用*返回其指针,则其效果不佳。如果它是一个数组,例如int []或int *,那么它也不好,因为您要返回它的内存地址。如果它是用C中的malloc或C ++中的new关键字创建的变量,那很好,因为即使您不在函数中,它们也将在那里。我认为您应该了解有关在函数中返回引用或指针的知识。您将完全理解这些。
–程序员
17年6月9日在23:13
好,我知道了!再次感谢您的澄清。如果您刚开始使用C#封送处理,它就是一个复杂的野兽。非常感谢您的帮助!谢谢。
–SePröbläm
17年6月10日在0:26
实际上,第一个问题的答案是“是”,这是从本机函数返回字符串的有效方法。根据Unity的文档,“从本机方法返回的字符串值应该经过UTF-8编码并分配在堆上。单编组调用可免费用于此类字符串。”
– C0DEF52
12月1日19:03