C# 引用和指针之间有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/430112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the difference between a C# Reference and a Pointer?
提问by Richard
I do not quite understand the difference between a C# reference and a pointer. They both point to a place in memory don't they? The only difference I can figure out is that pointers are not as clever, cannot point to anything on the heap, are exempt from garbage collection, and can only reference structs or base types.
我不太明白 C# 引用和指针之间的区别。他们都指向记忆中的一个地方,不是吗?我能弄清楚的唯一区别是指针不那么聪明,不能指向堆上的任何东西,免于垃圾收集,并且只能引用结构或基类型。
One of the reasons I ask is that there is a perception that people need to understand pointers well (from C, I guess) to be a good programmer. A lot of people who learn higher level languages miss this out and therefore have this weakness.
我问的原因之一是人们认为人们需要很好地理解指针(我猜是从 C 语言)才能成为一名优秀的程序员。许多学习高级语言的人都忽略了这一点,因此有这个弱点。
I just don't get what is so complex about a pointer? It is basically just a reference to a place in memory is it not? It can return its location and interact with the object in that location directly?
我只是不明白指针有什么如此复杂?它基本上只是对内存中某个位置的引用,不是吗?它可以返回其位置并直接与该位置的对象进行交互吗?
Have I missed a massive point?
我错过了一个重要的点吗?
采纳答案by Mehrdad Afshari
C# references can, and will be relocated by garbage collector but normal pointers are static. This is why we use fixed
keyword when acquiring a pointer to an array element, to prevent it from getting moved.
C# 引用可以并且将由垃圾收集器重新定位,但普通指针是静态的。这就是为什么我们fixed
在获取指向数组元素的指针时使用关键字,以防止它被移动。
EDIT: Conceptually, yes. They are more or less the same.
编辑:从概念上讲,是的。它们或多或少是相同的。
回答by Tamas Czinege
Pointers point to a location in the memory address space. References point to a data structure. Data structures all moved all the time (well, not that often, but every now and then) by the garbage collector (for compacting memory space). Also, as you said, data structures without references will get garbage collected after a while.
指针指向内存地址空间中的一个位置。引用指向一个数据结构。垃圾收集器(用于压缩内存空间)一直在移动数据结构(好吧,不是那么频繁,而是时不时地)。另外,正如您所说,没有引用的数据结构将在一段时间后被垃圾收集。
Also, pointers are only usable in unsafe context.
此外,指针只能在不安全的上下文中使用。
回答by Robert C. Barth
The thing about pointers that makes them somewhat complex is not what they are, but what you can do with them. And when you have a pointer to a pointer to a pointer. That's when it really starts to get fun.
使指针变得有些复杂的原因不是它们是什么,而是你可以用它们做什么。当你有一个指向一个指针的指针时。那是它真正开始变得有趣的时候。
回答by ctacke
First I think you need to define a "Pointer" in your sematics. Do you mean the pointer you can create in unsafe code with fixed? Do you mean an IntPtrthat you get from maybe a native call or Marshal.AllocHGlobal? Do you mean a GCHandle? The all are essentially the same thing - a representation of a memory address where something is stored - be it a class, a number, a struct, whatever. And for the record, they certainly can be on the heap.
首先,我认为您需要在语义中定义一个“指针”。你的意思是你可以在不安全的代码中用fixed创建的指针吗?您的意思是从本地调用或Marshal.AllocHGlobal获得的IntPtr吗?你的意思是GCHandle吗?所有这些本质上都是一样的东西 - 存储某些东西的内存地址的表示 - 无论是类,数字,结构,等等。根据记录,它们当然可以在堆上。
A pointer (all of the above versions) is a fixed item. The GC has no idea what is at that address, and therefore has no ability to manage the memory or life of the object. That means you lose all of the benefits of a garbage collected system. You must manually manage the object memory and you have the potential for leaks.
指针(以上所有版本)是一个固定项。GC 不知道该地址是什么,因此无法管理对象的内存或生命周期。这意味着您将失去垃圾收集系统的所有好处。您必须手动管理对象内存,并且有可能发生泄漏。
A reference on the other hand is pretty much a "managed pointer" that the GC knows about. It's still an address of an object, but now the GC knows details of the target, so it can move it around, do compactions, finalize, dispose and all of the other nice stuff a managed environment does.
另一方面,引用几乎是 GC 知道的“托管指针”。它仍然是一个对象的地址,但现在 GC 知道目标的详细信息,因此它可以移动它、进行压缩、完成、处置以及托管环境所做的所有其他好事情。
The major difference, really, is in how and why you would use them. For a vast majority of cases in a managed language, you're going to use an object reference. Pointers become handy for doing interop and the rare need for really fast work.
实际上,主要区别在于您将如何以及为何使用它们。对于托管语言中的绝大多数情况,您将使用对象引用。指针对于进行互操作和真正快速工作的罕见需求变得非常方便。
Edit:In fact here's a good exampleof when you might use a "pointer" in managed code - in this case it's a GCHandle, but the exact same thing could have been done with AllocHGlobal or by using fixed on a byte array or struct. I tend to prefer the GCHandle becasue it feels more ".NET" to me.
编辑:事实上,这是一个很好的例子,说明何时可以在托管代码中使用“指针”——在这种情况下,它是一个 GCHandle,但完全相同的事情可以用 AllocHGlobal 或通过在字节数组或结构上使用 fixed 来完成。我倾向于更喜欢 GCHandle,因为它对我来说更像是“.NET”。
回答by P Daddy
I think it's important for developers to understand the conceptof a pointer—that is, to understand indirection. That doesn't mean they necessarily have to use pointers. It's also important to understand that the concept of a referencediffers from the concept of pointer, although only subtly, but that the implementation of a reference almost always isa pointer.
我认为对于开发人员来说理解指针的概念很重要——即理解间接性。这并不意味着他们必须使用指针。同样重要的是要明白,一个参考的概念,从不同指针的概念,虽然只有微妙,但参考的实施几乎都是是一个指针。
That is to say, a variable holding a reference is just a pointer-sized block of memory holding a pointer to the object. However, this variable cannot be used in the same way that a pointer variable can be used. In C# (and C, and C++, ...), a pointer can be indexed like an array, but a reference cannot. In C#, a reference is tracked by the garbage collector, a pointer cannot be. In C++, a pointer can be reassigned, a reference cannot. Syntactically and semantically, pointers and references are quite different, but mechanically, they're the same.
也就是说,持有引用的变量只是一个指针大小的内存块,持有指向对象的指针。但是,不能像使用指针变量那样使用该变量。在 C#(以及 C、C++、...)中,指针可以像数组一样被索引,但引用不能。在 C# 中,垃圾收集器跟踪引用,而不能跟踪指针。在 C++ 中,可以重新分配指针,而不能重新分配引用。在语法和语义上,指针和引用完全不同,但在机械上,它们是相同的。
回答by Chris Conway
A reference is an "abstract" pointer: you can't do arithmetic with a reference and you can't play any low-level tricks with its value.
引用是一个“抽象”指针:你不能用引用做算术,也不能用它的值玩任何低级技巧。
回答by jdigital
A pointer can point to any byte in the address space of the application. A reference is tightly constrained and controlled and managed by the .NET environment.
指针可以指向应用程序地址空间中的任何字节。引用受到 .NET 环境的严格约束、控制和管理。
回答by JaredPar
There is a slight, yet extremely important, distinction between a pointer and a reference. A pointer points to a place in memory while a reference points to an object in memory. Pointers are not "type safe" in the sense that you cannot guarantee the correctness of the memory they point at.
指针和引用之间存在细微但极其重要的区别。指针指向内存中的某个位置,而引用指向内存中的对象。指针不是“类型安全的”,因为您无法保证它们指向的内存的正确性。
Take for example the following code
以下面的代码为例
int* p1 = GetAPointer();
This is type safe in the sense that GetAPointer must return a type compatible with int*. Yet there is still no guarantee that *p1 will actually point to an int. It could be a char, double or just a pointer into random memory.
这是类型安全的,因为 GetAPointer 必须返回与 int* 兼容的类型。然而仍然不能保证 *p1 实际上会指向一个 int。它可以是 char、double 或只是一个指向随机内存的指针。
A reference however points to a specific object. Objects can be moved around in memory but the reference cannot be invalidated (unless you use unsafe code). References are much safer in this respect than pointers.
然而,一个引用指向一个特定的对象。对象可以在内存中移动,但引用不能无效(除非您使用不安全的代码)。在这方面,引用比指针安全得多。
string str = GetAString();
In this case str has one of two state 1) it points to no object and hence is null or 2) it points to a valid string. That's it. The CLR guarantees this to be the case. It cannot and will not for a pointer.
在这种情况下, str 具有两种状态之一:1)它不指向任何对象,因此为 null 或 2)它指向一个有效的字符串。就是这样。CLR 保证确实如此。它不能也不会用于指针。
回答by Despertar
One of the biggest benefits of references over pointers is greater simplicity and readability. As always when you simplify something you make it easier to use but at the cost of flexibility and control you get with the low-level stuff (as other people have mentioned).
引用相对于指针的最大好处之一是更加简单和可读。与往常一样,当您简化某些东西时,您会使其更易于使用,但以灵活性和控制为代价,您可以获得低级的东西(正如其他人所提到的)。
Pointers are often criticized for being 'ugly'.
指针经常被批评为“丑陋”。
class* myClass = new class();
Now everytime you use it you have to dereference it first either by
现在,每次使用它时,您都必须先通过以下方式取消引用它
myClass->Method() or (*myClass).Method()
Despite losing some readability and adding complexity, people still needed to use pointers often as parameters so you could modify the actual object (instead of passing by value) and for the performance gain of not having to copy huge objects.
尽管失去了一些可读性并增加了复杂性,人们仍然需要经常使用指针作为参数,以便您可以修改实际对象(而不是按值传递),并且不必复制大型对象来提高性能。
To me this is why references were 'born' in the first place to provide the same benefit as pointers but without all that pointer syntax. Now you can pass the actual object (not just its value) AND you have a more readable, normal way of interacting with the object.
对我来说,这就是为什么引用首先“诞生”以提供与指针相同的好处,但没有所有指针语法。现在,您可以传递实际对象(而不仅仅是它的值),并且您可以通过一种更易读、更正常的方式与对象交互。
MyMethod(&type parameter)
{
parameter.DoThis()
parameter.DoThat()
}
C++ references differed from C# / Java references in that once you assign a value to it that was it, you couldn't re-assign it (and it has to be assigned when it was declared). This was the same as using a const pointer (a pointer that could not be re-pointed to another object).
C++ 引用与 C#/Java 引用的不同之处在于,一旦为它分配了一个值,就不能重新分配它(并且必须在声明时分配)。这与使用 const 指针(无法重新指向另一个对象的指针)相同。
Java and C# are very high level, modern languages which cleaned up a lot of the messes that had accumulated in C / C++ over the years and pointers was definitely one of those things that needed to be 'cleaned up'.
Java 和 C# 是非常高级的现代语言,它们清理了多年来在 C/C++ 中积累的大量混乱,指针绝对是需要“清理”的东西之一。
As far as your comment about knowing pointers makes you a stronger programmer, this is true in most cases. If you know 'how' something works as opposed to just using it without knowing I would say this can often give you an edge. How much of an edge will always vary. After all, using something without knowing how it is implemented is one of the many beauties of OOP and Interfaces.
就您对了解指针的评论使您成为更强大的程序员而言,这在大多数情况下都是正确的。如果你知道“如何”工作,而不是在不知道的情况下使用它,我会说这通常会给你带来优势。边缘的大小总是会有所不同。毕竟,在不知道它是如何实现的情况下使用某些东西是 OOP 和接口的众多优点之一。
In this specific example, what would knowing about pointers help you with references? Understanding that a C# reference is NOT the object itself but points to the object is a very important concept.
在这个特定的例子中,了解指针会帮助你处理引用吗?理解 C# 引用不是对象本身而是指向对象是一个非常重要的概念。
#1: You are NOT passing by valueWell for starters when you use a pointer you know that the pointer holds just an address, that's it. The variable itself is almost empty and that's why it's so nice to pass as arguments. In addition to the performance gain, you are working with the actual object so any changes you make are not temporary
#1:你不是按值传递好吧,当你使用指针时,你知道指针只保存一个地址,就是这样。变量本身几乎是空的,这就是为什么作为参数传递非常好。除了性能提升之外,您正在使用实际对象,因此您所做的任何更改都不是暂时的
#2: Polymorphism / InterfacesWhen you have a reference that is an interface type and it points to an object, you can only call methods of that interface even though the object may have many more abilities. The objects may also implement the same methods differently.
#2:多态/接口当你有一个接口类型的引用并且它指向一个对象时,你只能调用该接口的方法,即使该对象可能有更多的能力。对象也可能以不同的方式实现相同的方法。
If you understand these concepts well then I don't think you are missing too much from not having used pointers. C++ is often used as a language for learning programming because it is good to get your hands dirty sometimes. Also, working with lower-level aspects makes you appreciate the comforts of a modern language. I started with C++ and am now a C# programmer and I do feel like working with raw pointers have helped me have a better understanding on what goes on under the hood.
如果你很好地理解了这些概念,那么我认为你不会因为没有使用指针而错过太多。C++ 经常被用作学习编程的语言,因为有时弄脏你的手是件好事。此外,使用较低级别的方面会让您欣赏现代语言的舒适性。我从 C++ 开始,现在是一名 C# 程序员,我确实觉得使用原始指针帮助我更好地了解幕后发生的事情。
I don't think it is necessary for everyone to start with pointers, but what is important is that they understand why references are used instead of value-types and the best way to understand that is to look at its ancestor, the pointer.
我认为没有必要让每个人都从指针开始,但重要的是他们理解为什么使用引用而不是值类型,理解这一点的最好方法是查看它的祖先,指针。
回答by supercat
A major difference between a reference and a pointer is that a pointer is a collection of bits whose content only matters when it is actively being used as a pointer, while a reference encapsulates not only a set of bits, but also some metadata which keeps the underlying framework informed of its existence. If a pointer exists to some object in memory, and that object is deleted but the pointer is not erased, the pointer's continued existence won't cause any harm unless or until an attempt is made to access the memory to which it points. If no attempt is made to use the pointer, nothing will care about its existence. By contrast, reference-based frameworks like .NET or the JVM require that it always be possible for the system to identify every object reference in existence, and every object reference in existence must always either be null
or else identify an object of its proper type.
引用和指针之间的主要区别在于,指针是位的集合,其内容仅在被主动用作指针时才有意义,而引用不仅封装了一组位,还封装了一些元数据,这些元数据保持基础框架告知其存在。如果指向内存中某个对象的指针存在,并且该对象被删除但指针没有被擦除,则该指针的继续存在不会造成任何危害,除非或直到尝试访问它所指向的内存。如果不尝试使用该指针,则不会关心它的存在。相比之下,.NET 或 JVM 等基于引用的框架要求系统始终可以识别存在的每个对象引用,并且存在的每个对象引用必须始终是null
或者标识其正确类型的对象。
Note that each object reference actually encapsulates two kinds of information: (1) the field contents of the object it identifies, and (2) the set of other references to the same object. Although there isn't any mechanism by which the system can quickly identify all the references that exist to an object, the set of other references that exist to an object may often be the most important thing encapsulated by a reference (this is especially true when things of type Object
are used as things like lock tokens). Although the system keeps a few bits of data for each object for use in GetHashCode
, objects have no real identity beyond the set of references that exist to them. If X
holds the only extant reference to an object, replacing X
with a reference to a new object with the same field contents will have no identifiable effect except to change the bits returned by GetHashCode()
, and even that effect isn't guaranteed.
请注意,每个对象引用实际上封装了两种信息:(1)它标识的对象的字段内容,以及(2)对同一对象的其他引用的集合。虽然没有任何机制可以让系统快速识别出一个对象存在的所有引用,但是存在于一个对象的其他引用的集合往往是一个引用封装的最重要的东西(尤其是当类型Object
的东西用作锁令牌之类的东西)。尽管系统为每个对象保留了几位数据以供在 中使用GetHashCode
,但对象除了存在于它们的引用集之外没有真正的身份。如果X
持有对对象的唯一现存引用,则替换X
引用具有相同字段内容的新对象将没有可识别的效果,除非更改由 返回的位GetHashCode()
,甚至不能保证这种效果。