该函数在C#项目中定义为:
[DllImport("Project2.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void modifyVar(ref int x);
函数调用是:
modifyVar(ref a)
其中a = 4
本机C ++函数定义如下:
extern "C" _declspec(dllexport) void modifyVar(int* a)
{
int x = 1;
a = &x;
}
为什么在C#项目中,a在函数调用后没有指向值为1的新变量?它仍然是4。
但是,如果我修改C ++函数,它可以正常工作:
extern "C" _declspec(dllexport) void modifyVar(int* a)
{
int x = 1;
*a = x;
}
但是,如何让它指向新的内存位置而不是修改值?
它与使用IntPtr而不是ref int有关吗?
答案 0 :(得分:2)
您的第一个片段:
void modifyVar( int* a )
{
int x = 1;
a = &x;
}
采用int*
(指向int的指针)。当被调用时,它在堆栈上接收一个包含地址的单词,可能是调用者范围内int
的位置。然后,将该字
调用者不知道您的函数刚刚做了什么,因为您没有修改调用者范围内的任何内容。您尚未修改int
指向的内容(int*
)。
在你的第二个片段中
void modifyVar( int* a )
{
int x = 1;
*a = x;
}
同样的事情发生在电话上。函数体做了不同的事情:语句*a = x;
执行此操作:
int*
的{{1}} a
变量int
的值存储在该位置。现在您已修改了来电者的存储空间,并且调用者已知此更改。
在第一个实例中,您所做的只是修改了一个参数,一个指针:您没有修改它所在的内存,因此调用者没有意识到它。
如果你已经将一个本地变量的地址返回给调用者并且它使用了它,那么很可能会发生坏事,因为调用者会使用可能使用或可能不使用的堆栈内存。
您需要获取 C编程语言(又名K + R)的副本:
答案 1 :(得分:1)
但是,如何让它指向新的内存位置而不是修改值?
所以,正如我理解这个问题,你问的是如何在本机模块中返回变量的地址。这样做:
static int i = 42;
extern "C" _declspec(dllexport) int* getPtr()
{
return &i;
}
请注意,返回其地址的变量是不一个局部变量。它不能是局部变量的地址,因为局部变量的范围在函数返回时结束。
从C#调用它就像这样:
[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr getPtr();
....
IntPtr ptr = getPtr();
int i = Marshal.ReadInt32(ptr);
如果您更愿意通过参数返回指针,请执行以下操作:
static int i = 42;
extern "C" _declspec(dllexport) void getPtr(int** ptr)
{
*ptr = &i;
}
来自C#:
[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
static extern void getPtr(out IntPtr ptr);
....
IntPtr ptr;
getPtr(out ptr);
int i = Marshal.ReadInt32(ptr);
然而,说完所有这些之后,我真的怀疑你确实想要返回一个变量的地址。这个全局变量看起来不会非常有用。另一种方法是堆分配内存。但是,您需要导出解除分配器,或分配共享堆。再次,在那里没有多少乐趣。对于编组而言,颈部也会疼痛。
更有可能的是在调用代码,托管代码中分配变量,并要求本机代码修改该变量的值。这使得终身管理变得微不足道,并允许你让pinvoke marshaller完成所有工作。