【问题标题】:What C# type stands for C++ float*?什么 C# 类型代表 C++ float*?
【发布时间】:2013-03-13 20:55:04
【问题描述】:

我有一个从 C++ 库中调用的函数,该函数已导入到 C# 项目中

我认为它要求一个指向数组的指针。但我不确定如何使它工作。

这是它所要求的function(float*, float*);

但是如果我做类似的事情

float[] f = {};
float[] f1 = {};

function(f,f1);

它说有无效的参数。

【问题讨论】:

  • 我很困惑 - 你为什么使用c# 标签?你的意思是使用c++
  • 这是一个被导入C#的C++库
  • 指针类型在 C# 中支持。为什么大家都糊涂了?
  • 它被认为是“不安全的”,因为 C# 是一种托管语言。

标签: c#


【解决方案1】:

float * 是 C# 中的浮点指针类型。

如果函数需要浮点指针参数 (float *),则必须假定函数在指针上工作,可能涉及指针算术。因此,保留该签名很重要。

要在 C# 中将浮点数组作为浮点指针 (float*) 传递,您需要在不安全的上下文中固定/修复内存中的数组以获取指向它们的指针,您可以将其传递给函数以保留其功能:

unsafe
{
    fixed (float* ptr_f = f) //or equivalently "... = &f[0]" address of f[0]
    fixed (float* ptr_f2 = f2) //or equivalently "... = &f2[0]" address of f2[0]
    {
        function( ptr_f, ptr_f2 );
    }
}

您还需要将您的程序集标记为不安全(在项目属性 > 构建选项卡 > 允许不安全代码复选框中)。

【讨论】:

  • 要清楚...保留签名需要对代码进行最少的更改,但是如果您想避免不安全的上下文和低效的内存固定,只需将浮点指针 (float*) 参数转换为更安全的浮点数组 (float[]) 参数类型并检查函数的逻辑以确保不存在诸如 f++ 或 f+=4 之类的指针算术或诸如 (*(f + 4)) 之类的解引用。如果浮点指针通过 f[i] 之类的数组索引访问,那么在 C# 中访问数组的语法是相同的,只是风格上的不同,请参阅 stackoverflow.com/q/1790704/88409
【解决方案2】:

如果这是通过DLLImport() 导入的方法,您可以简单地将数组指针替换为类型化数组。

所以一个签名:

[DLLImport("some.dll")]
SomeMethod(int* a, float* b)

变成

[DLLImport("some.dll")]
SomeMethod(int[] a, float[] b)

请注意,这假定原始 c/c++ 方法需要一个数组。如果指针不打算引用数组,这将不起作用。

【讨论】:

  • 点赞。我也回答了,假设 OP 正在将源代码导入 C# 项目(不是很清楚),但如果它是一个 p-invoke 调用......这适用于简单类型。 CLR 将在函数调用期间自动固定对托管数组的引用(尽管要注意尝试缓存托管指针的函数):stackoverflow.com/a/2218540/88409
  • 需要更多注意复杂类型的编组,如字符串、结构和多维数组,例如编组是否需要复制到非托管内存和/或从非托管内存复制以及是否需要进行任何转换(例如从 unicode 到 ANSI 字符串)。这取决于类型是否为“blittable”以及您是否在 MarshalAs、In 或 Out 等参数上指定属性(或使用“out”或“ref”关键字暗示 In 或 Out 属性)。请参阅:msdn.microsoft.com/en-us/library/23acw07k.aspxmsdn.microsoft.com/en-us/magazine/cc164193.aspx
【解决方案3】:

您需要确切地知道调用的 C++ 函数期待什么。

在 C(和 C++)中,所有这些函数签名的所有意图和目的都完全相同:

void foo( float *x   , float *y   ) ;
void foo( float *x   , float  y[] ) ;
void foo( float  x[] , float *y   ) ;
void foo( float  x[] , float  y[] ) ;

它们都带有 2 个参数,每个参数都包含一个指向(包含地址)float 类型变量的指针。所有这些表达式在 C/C++ 中都是完全相同的:

float x[] ;
float *y  ;

float r1 = *(x+37) ; // all evaluate to the zero-relative
float r2 = x[37]   ; // 37th element of the array.
float r3 = *(y+37) ;
float r4 = y[37]   ;
  • 表达式*x 表示,“获取位于x 中包含的地址的float(4 个字节)。
  • 表达式*(x+n),其中n 是一个整数值,它表示“取x 中包含的地址,并将通过表达式sizeof(float) * n 获得的字节偏移量相加。获取找到的float 值在结果地址。
  • 数组表达式x[n] 完全等价于指针表达式*(x+n)

由于 C/C++ 中的数组没有任何关联的元数据来描述它们的大小,因此您需要确切地知道被调用函数的期望。

通常,传递一个指针(通过引用调用)以允许调用者取消引用该点并为您设置一个值 - 相当于 C# 的 refout 关键字:

float x ;
float y ;
Foo( ref x , ref y ) ; // the callee MAY, but is not REQUIRED to set a value before returning to the caller.
Bar( out x , out y ) ; // the callee MUST set a value before returning to the caller.

您的函数使用的惯用语总是用于定位数组,但通常也会传递一个大小:

void foo( float *array , int array_length ) ;

虽然这并不罕见,但如果函数期望数组是一个非零数值列表,则类似于 C 风格、以 NUL 结尾的字符串。给定函数签名,例如:

float compute_mean( float *Xs ) ;

这样调用它并不罕见:

float Xs[] = { 3.0 , 2.5 , 9.8 , 7,5 , 0 , } ;
float mean = compute_mean( Xs ) ;

定义如下:

float compute_mean( float *Xs )
{
  float n   = 0.0 ;
  float sum = 0.0 ;
  float mean ;
  while ( *p )
  {
    ++n ;
    sum += *p++ ;
  }
  mean = n > 0 ? sum / n : 0.0 ;
  return mean ;
}

所以你需要知道你正在调用的方法的语义。

希望这会有所帮助。

【讨论】:

    【解决方案4】:

    这在 C# 中根本不起作用,在 C# 中导入 C/C++ 库时,您应该使用 IntPtr 而不是 float*,请参见此处的一些示例:float* from C to C#

    【讨论】:

      【解决方案5】:

      它要求一个浮点指针,(内存中包含 IEEE 浮点值的点的机器地址。)C# 不允许托管使用指针,因此您需要构造一个 IntPtr 变量和将其传递给 C++ 函数。

      注意:仅仅因为它被称为IntPtr 并不意味着它只是一个指向整数的指针。 Int 是因为所有指针都是整数。它可以是指向任何东西的指针。

      【讨论】:

      • C# 不允许托管使用指针?您可以在托管(不安全)代码中使用它们,甚至可以将它们传递给非托管 DllImported 函数...
      猜你喜欢
      • 2018-12-02
      • 1970-01-01
      • 2012-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-24
      • 2020-12-29
      • 2012-11-27
      相关资源
      最近更新 更多