C#参考和指针有什么区别?
对不起,这样一个新手问题,但有一些我不太明白C#引用和指针之间的区别。 他们都指向一个记忆中的地方吗? 唯一的区别是我可以弄清楚的是,指针不是那么聪明,不能指向堆上的任何东西,免于垃圾回收,只能引用结构体或基types。
我所要问的一个原因是,有一种看法认为,人们需要了解指针(从CI猜测),并成为一名优秀的程序员。 很多学习高级语言的人都会错过这个机会,所以有这个弱点。
我只是没有得到什么如此复杂的指针? 这基本上只是一个参考,在内存中的一个地方是不是? 它可以返回它的位置,并直接与该位置的对象进行交互?
我错过了一个重要的观点吗?
C#引用可以并且将被垃圾回收器重新定位,但是普通的指针是静态的。 这就是为什么我们在获取指向数组元素的指针时使用fixed
关键字,以防止它被移动。
编辑:概念上,是的。 他们或多或less是一样的。
指针和引用之间有一个轻微的但非常重要的区别。 指针指向内存中的某个位置,而引用指向内存中的对象。 指针不是“types安全的”,因为您无法保证指向的内存的正确性。
以下面的代码为例
int* p1 = GetAPointer();
这是types安全的,因为GetAPointer必须返回与int *兼容的types。 但是仍然不能保证* p1实际上会指向一个int。 它可能是一个字符,双倍或只是一个指向随机存储器的指针。
然而,参考指向特定的对象。 对象可以在内存中移动,但引用不能失效(除非使用不安全的代码)。 在这方面的参考比指针更安全。
string str = GetAString();
在这种情况下,str有两个状态之一1)它指向没有对象,因此是null或2)它指向一个有效的string。 而已。 CLR保证是这样的。 它不能也不会为一个指针。
一个引用是一个“抽象”指针:你不能用引用做算术运算,也不能用它的值来玩任何低级别的技巧。
首先,我认为你需要在你的sematics中定义一个“指针”。 你的意思是你可以使用固定的不安全代码创build指针吗? 你的意思是你从一个本地电话或Marshal.AllocHGlobal获得的IntPtr ? 你的意思是一个GCHandle ? 全部基本上是一样的东西 – 一个内存地址的存储位置的表示 – 无论是类,数字,结构等。 而为了logging,他们当然可以堆在一起。
一个指针(以上所有版本)是一个固定的项目。 GC不知道该地址是什么,因此无法pipe理对象的内存或生命。 这意味着你失去了垃圾收集系统的所有好处。 您必须手动pipe理对象内存,并有可能发生泄漏。
另一方面,引用GC几乎是GC知道的“pipe理指针”。 它仍然是一个对象的地址,但是现在GC知道目标的细节,所以它可以移动它,执行压缩,最终确定,处置以及托pipe环境所有其他好东西。
主要的区别在于如何以及为什么要使用它们。 对于托pipe语言中的绝大多数情况,您将使用对象引用。 指针在互操作中变得非常方便,而且对于真正快速的工作也很less需要。
编辑:其实这里有一个很好的例子 ,你可以在托pipe代码中使用“指针” – 在这种情况下,它是一个GCHandle,但是完全相同的事情可以用AllocHGlobal完成,或者固定在一个字节数组或结构上。 我倾向于更喜欢GCHandle,因为它感觉更“.NET”给我。
指针指向内存地址空间中的一个位置。 参考文献指向一个数据结构。 所有的数据结构都被垃圾收集器(为了压缩内存空间)而一直移动(当然不是那么频繁,但是偶尔也是这样)。 另外,正如你所说,没有引用的数据结构将在一段时间后被垃圾回收。
而且,指针只能在不安全的上下文中使用。
我认为开发人员理解指针的概念非常重要,也就是理解间接性。 这并不意味着他们必须使用指针。 理解引用的概念与指针的概念不同,也是很重要的,尽pipe只是微妙的,但是引用的实现几乎总是一个指针。
也就是说,一个持有引用的variables只是一个指针大小的内存块,它持有一个指向该对象的指针。 但是,这个variables不能像指针variables一样使用。 在C#(以及C和C ++,…)中,指针可以像数组一样索引,但引用不能。 在C#中,垃圾收集器跟踪引用,指针不能。 在C ++中,指针可以被重新分配,而引用不能。 在语法和语义上,指针和引用是完全不同的,但从机制上讲,它们是相同的。
指针可以指向应用程序地址空间中的任何字节。 .NET环境严格限制和控制和pipe理引用。
引用和指针之间的一个主要区别在于,指针是一个比特的集合,其内容只在被主动用作指针时才起作用,而引用不仅包含一组比特,还包含一些元数据,通知其存在的基础框架。 如果一个指针存在于内存中的某个对象,并且该对象被删除了,但是指针没有被擦除,指针的继续存在不会造成任何伤害,除非或者直到尝试访问指向的内存。 如果没有尝试使用指针,没有人会关心它的存在。 相比之下,像.NET或JVM这样的基于参考的框架要求系统始终能够识别存在的每个对象引用,并且存在的每个对象引用必须总是为null
或者标识其正确types的对象。
请注意,每个对象引用实际上封装了两种信息:(1)它标识的对象的字段内容,以及(2)对同一对象的其他引用的集合。 尽pipe系统没有任何机制可以快速识别对象存在的所有引用,但是存在于对象中的其他引用集通常可能是引用封装的最重要的东西(当Object
types的东西被用作locking令牌的东西)。 尽pipe系统为GetHashCode
每个对象保留了几位数据,但除了存在于这些对象之外的对象之外,对象并没有真正的身份。 如果X
保存对象的唯一现存引用,则将X
replace为具有相同字段内容的新对象的引用,除了更改由GetHashCode()
返回的位之外,将没有可识别的效果,即使该效果也不能保证。
关于使他们有点复杂的指针的事情不是他们是什么,而是你可以用他们做什么。 而当你有一个指针指向一个指针。 那是真正开始玩的时候。
引用超过指针的一个最大的好处是更简单和可读性。 和往常一样,当你简化一些东西时,你可以更容易地使用它,但是要以灵活性和控制的代价,使用低级的东西(正如其他人所提到的那样)。
指针经常被批评为“丑陋”。
class* myClass = new class();
现在,每当你使用它,你必须首先解除引用
myClass->Method() or (*myClass).Method()
尽pipe失去了一些可读性和增加的复杂性,人们仍然需要经常使用指针作为参数,所以你可以修改实际的对象(而不是通过值传递),并获得无需复制巨大对象的性能。
对我来说,这就是为什么引用是“天生的”,以提供与指针相同的好处,但没有所有的指针语法。 现在你可以传递实际的对象(不只是它的值),而且你有一个更可读的,正常的与对象交互的方式。
MyMethod(&type parameter) { parameter.DoThis() parameter.DoThat() }
C ++引用不同于C#/ Java引用,因为一旦赋值给它,就不能重新赋值了(并且在声明它时必须赋值)。 这与使用const指针(不能重新指向另一个对象的指针)相同。
Java和C#是非常高层次的现代语言,这些语言清理了C / C ++多年积累下来的大量垃圾,指针绝对是需要“清理”的东西之一。
至于你对于知道指针的评论会让你成为一个更强大的程序员,在大多数情况下是这样的。 如果你知道“怎么做”,而不是仅仅使用它,我会说这通常会给你一个优势。 多less边缘总是会变化的。 毕竟,在不知道如何实现的情况下使用某些东西是OOP和Interfaces的许多美丽之一。
在这个特定的例子中,什么知道指针可以帮助你参考? 理解一个C#引用不是对象本身,而是指向对象是一个非常重要的概念。
#1:你没有经过价值对于初学者来说,当你使用一个指针时,你知道指针只包含一个地址,就是这样。 variables本身几乎是空的,这就是为什么作为parameter passing这么好的原因。 除了性能增益之外,您正在处理实际的对象,所以您所做的任何更改都不是暂时的
#2:多态性/接口当你有一个引用是一个接口types,并指向一个对象时,即使该对象可能有更多的能力,也只能调用该接口的方法。 对象也可以以不同的方式实现相同的方法。
如果你理解了这些概念,那么我不认为你没有使用指针就错过了很多。 C ++经常被用作学习编程的语言,因为有时候弄脏你的手很好。 另外,与较低级别的方面一起工作,使您能够欣赏现代语言的舒适感。 我从C ++开始,现在是C#程序员,我觉得使用原始指针可以帮助我更好地理解底层的内容。
我不认为每个人都有必要从指针开始,但重要的是他们明白为什么使用引用而不是值types,而理解它的最好方法就是看它的祖先指针。