none
非托管C++中的结构体数组指针在CLI中如何调用 RRS feed

  • 问题

  • 非托管C++中有个结构体数组:

    structtest* stest= NULL; 
    stest= (structtest*)calloc(3, sizeof(structtest)); 
    如果stest是个结构体数组,此时在CLI中该如何才能调用这个结构体数组,获取或修改该数组结构体中的成员?

    努力~

    2019年4月22日 9:05

答案

  • 1. fnStaticLibStructTest这个函数是不是你自己写的,这个函数是完全是错误的,应该不是类库里现有的东西,否则类库就是错误的,应该联系作者。

    2. c++ cli还是c++,指针方面没有托管不托管的区别。

    例如:

    #include "stdafx.h"
    #include "malloc.h"
    
    #pragma unmanaged
    
    struct untestStru
    {
    	int ua;
    	double ub;
    };
    
    void fnStaticLibStructTest(untestStru** teststr, int *num)
    {
    	if (teststr == nullptr)
    	{
    		return;
    	}
    	int n = 5;
    	auto str = new untestStru[n];
    	for (int i = 0; i < n; i++)
    	{
    		str[i].ua = i * 5;
    		str[i].ub = (i + 20.0);
    	}
    	*teststr = str;
    	*num = n;
    }
    
    #pragma managed
    
    //以上部分模拟标准c++,以下部分为c++ cli内容。
    
    using namespace System;
    
    ref struct CLITestStru
    {
    	int a;
    	double b;
    };
    
    int main(array<System::String ^> ^args)
    {
    	int nnn;
    	untestStru* ptStru;
    	fnStaticLibStructTest(&ptStru, &nnn);
    	auto cliTestStruct = gcnew array<CLITestStru^>(nnn);
    	for (int i = 0; i != nnn; ++i)
    	{
    		cliTestStruct[i] = gcnew CLITestStru();
    		cliTestStruct[i]->a = ptStru[i].ua;
    		cliTestStruct[i]->b = ptStru[i].ub;
    		Console::WriteLine(cliTestStruct[i]->a);
    		Console::WriteLine(cliTestStruct[i]->b);
    	}
    	delete[] ptStru;
        return 0;
    }


    • 已编辑 [-] 2019年4月23日 10:14
    • 已标记为答案 zjyh16 2019年4月24日 9:13
    2019年4月23日 10:11
  • bug 1 :fnStaticLibStructTest完全没有使用参数teststr的值

    bug 2:CLI中代码假定abc的大小是5个untestStru(实际上是1个,fnStaticLibStructTest函数里给参数赋值只影响函数内那个参数副本,函数执行完那个参数的值就被丢弃了)

    你的函数参数是传值的。如果你要从函数参数输出一个指针,你的函数的类型应该是指针的引用或者指针。

    你也可以在函数外部分配传结构体数组的内存,前提是你已经知道数组大小。



    Visual C++ MVP


    2019年4月23日 13:00
    版主
  • 1. 如果是提前分配好的,那参数应该是const int num,而不是int* num。已经确定好个数了,不需要也不应该再回写。

    2. 【*teststr = str;】不能这样写是因为teststr是数组,不是一个单一的值,数组本身就是用指针类型描述的,指向数组的首地址,因此数组的指针就等于指针的指针,所以也就不存在【teststr = &str;】这样的写法。详细内容还是需要阅读教程,教程中会有关于数组的详细介绍,论坛上不可能全篇摘录。

    3. 至于更加妥当的写法吗,这个没办法谈啊。因为不知道你写这段代码的目的是什么?学习练习?解决某种问题?不知道目的也就没法说如何才“更妥当”。总体上说一般纯c++代码(包括c++ cli)肯定不会这么写,这种写法更多是为了兼容c语言。另外如果你用的是c++ cli,按说根本就不需要这么折腾啊,AllocHGlobal和FreeHGlobal一般都是在c#、vb里面才需要用,你这里是为了测试吗,或是在练习?总的来说就是你目前的代码是正确的,但如果是真实项目肯定不应该这么写,如果是做练习那就没问题。

    补充一点:上面代码里面少了一句。
    auto str = new untestStru[n];
    这里分配了内存,在fnStaticLibTest函数结束前必须用delete[] str;来释放,不然就泄漏了。

    • 已编辑 [-] 2019年4月24日 8:23
    • 已标记为答案 zjyh16 2019年4月24日 9:12
    2019年4月24日 8:11
  • 传指针的指针**就是为了回写,如果在cli里分配内存则可以传*,但是就不能在c++里面用new再次分配了。不传**,传指针的引用(*&)也是可以的,但是这个不兼容c语言,只能给c++用,因此大部分类库都用**,这样可以与c语言兼容。一般只有在c++当前项目或静态库里面才用*&。

    上面我的示例代码和我与“Sheng Jiang蒋晟”谈论的就是【要传指针且不知道大小内部模拟函数来确定大小】这个问题啊。

    • 已标记为答案 zjyh16 2019年4月24日 9:12
    2019年4月23日 15:04

全部回复

  • 你好,

    感谢您在MSDN论坛发帖提问。

    >>在CLI中该如何才能调用这个结构体数组,获取或修改该数组结构体中的成员? 

    Best Wishes,

    Jeanine Zhang
    2019年4月22日 9:55
    版主
  • 你好,

    感谢您在MSDN论坛发帖提问。

    >>在CLI中该如何才能调用这个结构体数组,获取或修改该数组结构体中的成员? 

    Best Wishes,

    Jeanine Zhang

    尝试按照链接中的去做,结果失败了。感觉和我想要的有点不一样,我是想获得非托管输出的结构体数组,示例更像是传入结构体数组。

    我的代码,不明白问题出在哪?请指教:

    非托管C++:

    void fnStaticLibStructTest(untestStru *teststr, int *num)
    {
    	if (teststr == nullptr)
    	{
    		return;
    	}
    	int n = 5;
    	teststr = (untestStru*)calloc(n, sizeof(teststr));
    	for (int i = 0; i < n; i++)
    	{
    		teststr[i].ua = i * 5;
    		teststr[i].ub = (i + 20.0);
    	}
    	*num = n;
    }

    CLI中代码:

    		untestStru abc;
    		int nnn;
    		fnStaticLibStructTest(&abc, &nnn);
    		cliTestStruct = gcnew array<CLITestStru^>(nnn);
    		untestStru* ptStru = static_cast<untestStru*>(IntPtr(&abc).ToPointer());
    		for (int i = 0; i < nnn; i++)
    		{
    			cliTestStruct[i]->a = ptStru[i].ua;
    			cliTestStruct[i]->b = ptStru[i].ub;
    			Console::WriteLine(cliTestStruct[i]->a);
    			Console::WriteLine(cliTestStruct[i]->b);
    		}
    查询其他资料做了尝试还是没能搞定,C++初学者寻求帮助。谢谢!


    努力~

    2019年4月23日 7:42
  • 下面的语句表示什么意思:

    *num = n;

    看不太懂,可能问题就出在这里。

    2019年4月23日 8:41
  • 下面的语句表示什么意思:

    *num = n;

    看不太懂,可能问题就出在这里。

    给num赋值。感觉这句没有问题啊。

    努力~

    2019年4月23日 8:55
  • fnStaticLibStructTest函数外定义 int nnn;

    值nnn却从函数fnStaticLibStructTest内传出,有没有问题。

    2019年4月23日 9:18
  • 1. fnStaticLibStructTest这个函数是不是你自己写的,这个函数是完全是错误的,应该不是类库里现有的东西,否则类库就是错误的,应该联系作者。

    2. c++ cli还是c++,指针方面没有托管不托管的区别。

    例如:

    #include "stdafx.h"
    #include "malloc.h"
    
    #pragma unmanaged
    
    struct untestStru
    {
    	int ua;
    	double ub;
    };
    
    void fnStaticLibStructTest(untestStru** teststr, int *num)
    {
    	if (teststr == nullptr)
    	{
    		return;
    	}
    	int n = 5;
    	auto str = new untestStru[n];
    	for (int i = 0; i < n; i++)
    	{
    		str[i].ua = i * 5;
    		str[i].ub = (i + 20.0);
    	}
    	*teststr = str;
    	*num = n;
    }
    
    #pragma managed
    
    //以上部分模拟标准c++,以下部分为c++ cli内容。
    
    using namespace System;
    
    ref struct CLITestStru
    {
    	int a;
    	double b;
    };
    
    int main(array<System::String ^> ^args)
    {
    	int nnn;
    	untestStru* ptStru;
    	fnStaticLibStructTest(&ptStru, &nnn);
    	auto cliTestStruct = gcnew array<CLITestStru^>(nnn);
    	for (int i = 0; i != nnn; ++i)
    	{
    		cliTestStruct[i] = gcnew CLITestStru();
    		cliTestStruct[i]->a = ptStru[i].ua;
    		cliTestStruct[i]->b = ptStru[i].ub;
    		Console::WriteLine(cliTestStruct[i]->a);
    		Console::WriteLine(cliTestStruct[i]->b);
    	}
    	delete[] ptStru;
        return 0;
    }


    • 已编辑 [-] 2019年4月23日 10:14
    • 已标记为答案 zjyh16 2019年4月24日 9:13
    2019年4月23日 10:11
  • bug 1 :fnStaticLibStructTest完全没有使用参数teststr的值

    bug 2:CLI中代码假定abc的大小是5个untestStru(实际上是1个,fnStaticLibStructTest函数里给参数赋值只影响函数内那个参数副本,函数执行完那个参数的值就被丢弃了)

    你的函数参数是传值的。如果你要从函数参数输出一个指针,你的函数的类型应该是指针的引用或者指针。

    你也可以在函数外部分配传结构体数组的内存,前提是你已经知道数组大小。



    Visual C++ MVP


    2019年4月23日 13:00
    版主
  • bug 1 :fnStaticLibStructTest完全没有使用参数teststr的值

    bug 2:CLI中代码假定abc的大小是5个untestStru(实际上是1个,fnStaticLibStructTest函数里给参数赋值只影响函数内那个参数副本,函数执行完那个参数的值就被丢弃了)

    你的函数参数是传值的。如果你要从函数参数输出一个指针,你的函数的类型应该是指针的引用或者指针。

    你也可以在函数外部分配传结构体数组的内存,前提是你已经知道数组大小。



    Visual C++ MVP


    应该还有一个bug 3,用calloc分配了非托管内存后没有free,因此内存泄漏。
    2019年4月23日 13:23
  • bug 1 :fnStaticLibStructTest完全没有使用参数teststr的值

    bug 2:CLI中代码假定abc的大小是5个untestStru(实际上是1个,fnStaticLibStructTest函数里给参数赋值只影响函数内那个参数副本,函数执行完那个参数的值就被丢弃了)

    你的函数参数是传值的。如果你要从函数参数输出一个指针,你的函数的类型应该是指针的引用或者指针。

    你也可以在函数外部分配传结构体数组的内存,前提是你已经知道数组大小。



    Visual C++ MVP


    应该还有一个bug 3,用calloc分配了非托管内存后没有free,因此内存泄漏。
    感谢大师解答!fnStaticLibStructTest是自己写的模拟,我还是个C++初学者因此有可能写的不对,感谢指出来!还有个疑问,void fnStaticLibStructTest(untestStru** teststr, int *num)第一参数你的模拟函数写**,写*是否可以实现呢?按照“Sheng Jiang蒋晟”版主说到,我的模拟是传值参数?如果要传指针且不知道大小内部模拟函数来确定大小是否同样可以获取到这个非托管的结构体数组呢?新手还一知半解,求赐教!再次谢过!

    努力~

    • 已标记为答案 zjyh16 2019年4月24日 9:12
    • 取消答案标记 zjyh16 2019年4月24日 9:12
    2019年4月23日 14:52
  • 传指针的指针**就是为了回写,如果在cli里分配内存则可以传*,但是就不能在c++里面用new再次分配了。不传**,传指针的引用(*&)也是可以的,但是这个不兼容c语言,只能给c++用,因此大部分类库都用**,这样可以与c语言兼容。一般只有在c++当前项目或静态库里面才用*&。

    上面我的示例代码和我与“Sheng Jiang蒋晟”谈论的就是【要传指针且不知道大小内部模拟函数来确定大小】这个问题啊。

    • 已标记为答案 zjyh16 2019年4月24日 9:12
    2019年4月23日 15:04
  • 传指针的指针**就是为了回写,如果在cli里分配内存则可以传*,但是就不能在c++里面用new再次分配了。不传**,传指针的引用(*&)也是可以的,但是这个不兼容c语言,只能给c++用,因此大部分类库都用**,这样可以与c语言兼容。一般只有在c++当前项目或静态库里面才用*&。

    上面我的示例代码和我与“Sheng Jiang蒋晟”谈论的就是【要传指针且不知道大小内部模拟函数来确定大小】这个问题啊。

    嗯,这里基本理解了,谢谢!自学中还有个困惑请教一下,假设已知要传的指针大小的情况,那我下面这段代码是否正确?有更加妥当的写法吗?还有个问题就是如注释中写的那样,为何不能直接去str的地址给teststr?

    typedef struct _untestStru
    {
    	int     ua = 0;
    	float ub = 0;
    
    }untestStru;
    
    void fnStaticLibTest(untestStru *teststr, int *num)
    {
    	if (teststr == nullptr)
    	{
    		return;
    	}
    	int n = 5;
    	auto str = new untestStru[n];
    
    	for (int i = 0; i < n; i++)
    	{
    		str[i].ua = i * 5;
    		str[i].ub = (i + 20.0);
    	}
    
    	for (int i = 0; i < n; i++)
    	{
    		teststr[i] = str[i];
    	}
    
    	//*teststr = str;//为何不能这样写?
    	//teststr = &str;//为何不能这样写?
    
    	*num = n;//这里却是可以,两者有什么区别?
    }
    
    //以上部分模拟标准c++,以下部分为c++ cli内容。
    
    ref struct CLITestStru
    {
    	int a;
    	double b;
    };
    
    int main(array<System::String^>^ args)
    {
    	int nnn = 5;
    	IntPtr pt = Marshal::AllocHGlobal(nnn * Marshal::SizeOf(untestStru::typeid));
    	untestStru* ptStru = static_cast<untestStru*>(pt.ToPointer());
    	fnStaticLibTest(ptStru, &nnn);
    	auto cliTestStruct = gcnew array<CLITestStru^>(nnn);
    	for (int i = 0; i != nnn; ++i)
    	{
    		cliTestStruct[i] = gcnew CLITestStru();
    		cliTestStruct[i]->a = ptStru[i].ua;
    		cliTestStruct[i]->b = ptStru[i].ub;
    		Console::WriteLine(cliTestStruct[i]->a);
    		Console::WriteLine(cliTestStruct[i]->b);
    	}
    	Marshal::FreeHGlobal(pt);
    	//delete[] ptStru;
    	return 0;
    }


    努力~



    • 已编辑 zjyh16 2019年4月24日 7:07
    2019年4月24日 6:48
  • 1. 如果是提前分配好的,那参数应该是const int num,而不是int* num。已经确定好个数了,不需要也不应该再回写。

    2. 【*teststr = str;】不能这样写是因为teststr是数组,不是一个单一的值,数组本身就是用指针类型描述的,指向数组的首地址,因此数组的指针就等于指针的指针,所以也就不存在【teststr = &str;】这样的写法。详细内容还是需要阅读教程,教程中会有关于数组的详细介绍,论坛上不可能全篇摘录。

    3. 至于更加妥当的写法吗,这个没办法谈啊。因为不知道你写这段代码的目的是什么?学习练习?解决某种问题?不知道目的也就没法说如何才“更妥当”。总体上说一般纯c++代码(包括c++ cli)肯定不会这么写,这种写法更多是为了兼容c语言。另外如果你用的是c++ cli,按说根本就不需要这么折腾啊,AllocHGlobal和FreeHGlobal一般都是在c#、vb里面才需要用,你这里是为了测试吗,或是在练习?总的来说就是你目前的代码是正确的,但如果是真实项目肯定不应该这么写,如果是做练习那就没问题。

    补充一点:上面代码里面少了一句。
    auto str = new untestStru[n];
    这里分配了内存,在fnStaticLibTest函数结束前必须用delete[] str;来释放,不然就泄漏了。

    • 已编辑 [-] 2019年4月24日 8:23
    • 已标记为答案 zjyh16 2019年4月24日 9:12
    2019年4月24日 8:11
  • 谢谢!一次练习学了很多很多东西。教程也同步在看,有些理论知识理解还是欠缺了点。感谢!

    努力~

    2019年4月24日 9:11