C#引用和指针之间有什么区别?


85

我不太了解C#引用和指针之间的区别。它们都指向内存中的位置,不是吗?我能弄清的唯一区别是,指针不那么聪明,不能指向堆上的任何东西,可以免于垃圾回收,并且只能引用结构或基本类型。

我问的原因之一是,人们认为要成为一名优秀的程序员,人们需要很好地理解指针(我猜是C)。许多学习高级语言的人都错过了这一点,因此有这个缺点。

我只是不了解指针的复杂性?它基本上只是对内存中位置的引用,不是吗?它可以返回其位置并直接与该位置的对象进行交互吗?

我错过了重点吗?


1
简短的回答是,您错过了相当重要的内容,这就是“……人们需要理解指针的感知”的原因。提示:C#不是唯一的语言。
jdigital

Answers:


50

C#引用可以并且将由垃圾回收器重定位,但是普通指针是静态的。这就是为什么我们fixed在获取指向数组元素的指针时使用关键字,以防止其被移动的原因。

编辑:从概念上讲,是的。他们或多或少都是一样的。


难道没有其他命令阻止C#引用让其引用的对象被GC移动吗?
理查德

哦,对不起,我认为这是另外一回事了,因为帖子提到了一个指针。
理查德

是的,GCHandle.Alloc或Marshal.AllocHGlobal(已修复)
ctacke,2009年

它固定在C#中,pin_ptr在C ++ / CLI
迈赫达德Afshari

Marshal.AllocHGlobal根本不会在托管堆中分配内存,并且自然也不会进行垃圾回收。
Mehrdad Afshari,2009年

130

指针和引用之间有微小但非常重要的区别。指针指向内存中的位置,而引用指向内存中的对象。从不能保证指针所指向的内存正确的意义上说,指针不是“类型安全的”。

以下面的代码为例

int* p1 = GetAPointer();

从GetAPointer必须返回与int *兼容的类型的意义上说,这是类型安全的。但是,仍然不能保证* p1实际上指向一个int。它可以是char,double或只是指向随机内存的指针。

但是,引用指向特定的对象。可以在内存中移动对象,但不能使引用无效(除非您使用不安全的代码)。在这方面,引用比指针要安全得多。

string str = GetAString();

在这种情况下,str具有以下两种状态之一:1)指向无对象,因此为null或2)指向有效字符串。而已。CLR保证确实如此。它不能也不会为指针。


14
很好的解释。
iTayb

13

引用是“抽象”指针:您不能使用引用进行算术运算,也不能使用其值进行任何低级技巧。


8

引用和指针之间的主要区别在于,指针是位的集合,其内容仅在将其主动用作指针时才重要,而引用不仅封装了一组位,而且还封装了一些元数据底层框架告知其存在。如果指针指向内存中的某个对象,并且删除了该对象但未删除该指针,则指针的继续存在不会造成任何伤害,除非或直到尝试访问它所指向的内存为止。如果不尝试使用指针,则不会关心它的存在。相比之下,基于引用的框架(如.NET或JVM)要求系统始终有可能标识存在的每个对象引用,并且存在的每个对象引用必须始终是null 否则,请识别其正确类型的对象。

请注意,每个对象引用实际上都封装了两种信息:(1)它标识的对象的字段内容,以及(2)对同一对象的其他引用集。尽管系统没有任何机制可以快速识别存在于某个对象的所有引用,但是存在于该对象的一组其他引用通常可能是引用封装的最重要的东西(尤其是当类型Object的事物用作锁定令牌之类的事物)。尽管系统为每个对象保留了几位数据供使用GetHashCode,但对象除了存在的引用集外没有其他真实身份。如果X拥有对对象的唯一现存引用,则替换X引用具有相同字段内容的新对象将不会产生可识别的效果,除非更改所返回的位GetHashCode(),甚至不能保证该效果。


5

指针指向内存地址空间中的某个位置。引用指向数据结构。数据结构一直(通过压缩收集器)一直(不经常,但时不时地)移动(压缩内存空间)。而且,正如您所说,没有引用的数据结构将在一段时间后被垃圾收集。

另外,指针仅在不安全的上下文中可用。


5

我认为对于开发人员而言,理解指针的概念(即理解间接性)很重要。这并不意味着它们必须使用指针。同样重要的是要明白,一个参考的概念,从不同指针的概念,虽然只有微妙,但参考的实施几乎都是一个指针。

也就是说,保存引用的变量只是保存指向对象的指针的指针大小的内存块。但是,不能以与可以使用指针变量相同的方式来使用此变量。在C#(以及C和C ++,...)中,指针可以像数组一样被索引,但是引用不能。在C#中,垃圾收集器会跟踪引用,而指针不能。在C ++中,可以重新分配指针,而引用则不能。在语法和语义上,指针和引用是完全不同的,但是从机械上来说,它们是相同的。


数组听起来很有趣,基本上是可以在无法使用引用执行此操作的情况下告诉指针像数组那样偏移内存位置的地方吗?想不到什么时候会有用,但仍然很有趣。
理查德

如果p是一个int *(指向int的指针),则(p +1)是由p + 4个字节(一个int的大小)标识的地址。p [1]与*(p +1)相同(也就是说,它“解引用” p后面4个字节的地址)。相反,使用数组引用(在C#中),[]运算符执行函数调用。
P爸爸2009年

5

首先,我认为您需要在语义中定义一个“指针”。您的意思是您可以使用固定的不安全代码创建指针吗?您的意思是从本地调用还是Marshal.AllocHGlobal获得的IntPtr?您的意思是GCHandle吗?所有这些本质上都是相同的-表示存储内容的内存地址-无论是类,数字,结构还是其他。记录下来,它们肯定可以堆砌。

指针(所有上述版本)是固定项。GC不知道该地址是什么,因此无法管理对象的内存或寿命。这意味着您将失去垃圾收集系统的所有好处。您必须手动管理对象内存,并且有泄漏的可能性。

另一方面,引用几乎是GC知道的“托管指针”。它仍然是对象的地址,但是现在GC知道了目标的详细信息,因此它可以移动目标,执行压缩,完成,处置以及托管环境所做的所有其他出色工作。

实际上,主要区别在于您将如何使用它们以及为什么使用它们。对于托管语言中的大多数情况,您将使用对象引用。指针可以方便地进行互操作,并且很少需要真正快速的工作。

编辑:实际上,这是一个很好的示例,说明您何时可以在托管代码中使用“指针”-在这种情况下,它是GCHandle,但是完全相同的事情可以用AllocHGlobal来完成,或者通过在字节数组或结构上使用固定来完成。我倾向于使用GCHandle,因为它对我来说更像“ .NET”。


一个小小的问题,也许您不应该在这里说“托管指针”,即使带有引号也是如此,因为这与IL中的对象引用完全不同。尽管C ++ / CLI中有托管指针的语法,但是通常无法从C#访问它们。在IL中,它们是通过ldloca和ldarga指令获得的。
Glenn Slayden 2011年

5

指针可以指向应用程序地址空间中的任何字节。.NET环境对引用进行了严格的约束,控制和管理。


1

指针使它们有些复杂的问题不是它们是什么,而是您可以使用它们做什么。并且当您有一个指向指针的指针时。那时候它才真正开始变得有趣。


1

与指针相比,引用的最大好处之一就是更好的简单性和可读性。与往常一样,当您简化某些内容时,您会更易于使用,但会以灵活性和控制为代价,而使用低级内容(就像其他人所提到的那样)。

指针经常因“丑陋”而受到批评。

class* myClass = new class();

现在,每次使用它时,都必须先取消引用

myClass->Method() or (*myClass).Method()

尽管失去了一些可读性并增加了复杂性,但是人们仍然仍然需要经常使用指针作为参数,以便您可以修改实际对象(而不是按值传递),并获得不必复制大对象的性能。

对我来说,这就是为什么引用首先是“天生的”,以提供与指针相同的好处,却没有所有的指针语法。现在,您可以传递实际的对象(而不仅仅是其值),并且可以使用一种更具可读性的,与该对象进行交互的常规方式。

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

C ++引用与C#/ Java引用的不同之处在于,一旦为它分配了一个值,就无法重新分配它(并且必须在声明它时对其进行分配)。这与使用const指针(无法重新指向另一个对象的指针)相同。

Java和C#是非常高级的现代语言,它们清除了多年来在C / C ++中积累的许多混乱情况,而指针无疑是需要“清除”的那些事情之一。

就您对了解指针的评论使您成为一名更强大的程序员而言,在大多数情况下都是如此。如果您知道某事是如何工作的,而不是不知不觉地使用它,我会说这通常可以为您带来优势。边缘的多少将始终变化。毕竟,在不知道如何实现的情况下使用某些东西是OOP和接口的众多优点之一。

在此特定示例中,对指针的了解将对您的引用有什么帮助?理解C#引用不是对象本身而是指向对象是一个非常重要的概念。

#1:您不会按值传递 对于初学者来说,当您使用指针时,您知道指针仅包含一个地址,仅此而已。变量本身几乎为空,这就是为什么它很适合作为参数传递的原因。除了提高性能外,您还需要处理实际的对象,因此所做的任何更改都不是临时的

#2:多态性/接口 当您有一个引用是一种接口类型并且它指向一个对象时,即使该对象可能具有更多的功能,您也只能调用该接口的方法。这些对象还可以不同地实现相同的方法。

如果您很好地理解了这些概念,那么我认为您不会因没有使用指针而丢失太多。C ++通常被用作学习编程的一种语言,因为有时可以使您的手变得干净。此外,使用较低层次的方面会使您欣赏现代语言的舒适性。我从C ++开始,现在是C#程序员,我的确感觉使用原始指针可以帮助我更好地了解幕后情况。

我认为没有必要让每个人都从指针开始,但是重要的是,他们应该理解为什么使用引用而不是值类型,而理解引用的最佳方法是查看其祖先指针。


1
就个人而言,我认为C#会是一个更好的语言如果用得最多的地方.使用->,但foo.bar(123)用静态方法的调用的代名词fooClass.bar(ref foo, 123)。那会允许这样的事情myString.Append("George"); [将要修改的变量 myString],以及两者之间的意义变得更明显的差异myStruct.field = 3;myClassObject->field = 3;
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.