调用不修改int参数的值

时间:2014-01-03 19:37:56

标签: c# pinvoke intptr

该函数在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有关吗?

2 个答案:

答案 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)的副本:

C Programming Language Cover

答案 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完成所有工作。