C#中的ref和out是否与C ++中的指针相同?

时间:2009-07-22 18:46:21

标签: c# c++ pointers unsafe ref

我刚才用C#做了一个Swap例程:

static void Swap(ref int x, ref int y)
{
    int temp = x;
    x = y;
    y = temp;
}

这与C ++代码的作用相同:

void swap(int *d1, int *d2)
{
    int temp=*d1;
    *d1=*d2;
    *d2=temp;
}

refout关键字就像C#指针一样,不使用unsafe代码吗?

7 个答案:

答案 0 :(得分:10)

他们更有限。你可以在指针上说++,但不能在refout上说。


编辑评论中有些混乱,所以要绝对清楚:这里的重点是与指针的功能进行比较。您无法在ptr++ / ref上执行与out相同的操作,即将其设置为内存中的相邻位置。这是真的(但在此无关紧要),你可以执行相当于(*ptr)++,但这可以将它与的功能进行比较,而不是指针。


可以肯定的是,它们在内部只是指针,因为堆栈不会被移动并且C#经过精心组织,因此refout始终引用堆栈的活动区域。


编辑要再次绝对清楚(如果下面的例子中还没有明确说明),这里的要点不是ref / out可以指向堆栈。它是指向堆栈时,语言规则保证不会成为悬空指针。这种保证是必要的(并且相关/有趣),因为堆栈只是根据方法调用退出丢弃信息,没有检查以确保任何引用仍然存在。

相反,当ref / out引用GC堆中的对象时,这些对象只要有必要就能保持活动就不足为奇了:GC堆的设计正是为了将物品保留在其引荐者所需的任何时间长度内,并提供钉扎(参见下面的示例)以支持不能通过GC压缩移动物体的情况。


如果您在不安全的代码中使用互操作,您会发现ref与指针密切相关。例如,如果COM接口声明如下:

HRESULT Write(BYTE *pBuffer, UINT size);

互操作程序集会将其转换为:

void Write(ref byte pBuffer, uint size);

你可以这样做来调用它(我相信COM互操作的东西负责固定数组):

byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);

换句话说,ref到第一个字节可以访问所有内容;它显然是指向第一个字节的指针。

答案 1 :(得分:6)

C#中的引用参数可用于替换一个指针的使用,是的。但不是全部。

指针的另一个常见用途是作为迭代数组的手段。 Out / ref参数不能这样做,所以不,它们不是“与指针相同”。

答案 2 :(得分:3)

refout仅用于函数参数,以表示参数将通过引用而不是值传递。从这个意义上讲,是的,它们有点像C ++中的指针(实际上更像是引用)。请在this article中了解详情。

答案 3 :(得分:2)

实际上,我将它们与C ++引用而不是指针进行比较。在C ++和C中,指针是一个更通用的概念,引用将做你想要的。

当然,所有这些都是无形的指针。

答案 4 :(得分:1)

简短回答是(类似功能,但机制不完全相同)。 另请注意,如果您使用FxCop分析代码,则使用outref将导致“Microsoft.Design”错误“CA1045:DoNotPassTypesByReference。”

答案 5 :(得分:1)

使用 out 的好处是,您可以保证为该项分配一个值 - 如果没有,您将收到编译错误。

答案 6 :(得分:1)

虽然比较在旁观者的眼中......我说不。 'ref'更改调用约定,但不更改参数的类型。在C ++示例中,d1和d2的类型为int *。在C#中,它们仍然是Int32,它们恰好通过引用而不是值传递。

顺便说一下,你的C ++代码并没有真正交换传统意义上的输入。如此概括:

template<typename T>
void swap(T *d1, T *d2)
{
    T temp = *d1;
    *d1 = *d2;
    *d2 = temp;
}
除非所有类型的T都有拷贝构造函数,否则

...将无法工作,即使这样,交换指针的效率也会低得多。