References are not addresses

References are not addresses

Rate This
  • Comments 76

[NOTE: Based on some insightful comments I have updated this article to describe more clearly the relationships between references, pointers and addresses. Thanks to those who commented.]

I review a fair number of C# books; in all of them of course the author attempts to explain the difference between reference types and value types. Unfortunately, most of them do so by saying something like "a variable of reference type stores the address of the object". I always object to this. The last time this happened the author asked me for a more detailed explanation of why I always object, which I shall share with you now:

We have the abstract concept of "a reference". If I were to write about "Beethoven's Ninth Symphony", those two-dozen characters are not a 90-minute long symphonic masterwork with a large choral section. They're a reference to that thing, not the thing itself. And this reference itself contains references -- the word "Beethoven" is not a long-dead famously deaf Romantic Period composer, but it is a reference to one.

Similarly in programming languages we have the concept of "a reference" distinct from "the referent".

The inventor of the C programming language, oddly enough, chose to not have the concept of references at all. Rather, Ritchie chose to have "pointers" be first-class entities in the language. A pointer in C is like a reference in that it refers to some data by tracking its location, but there are more smarts in a pointer; you can perform arithmetic on a pointer as if it were a number, you can take the difference between two pointers that are both in the interior of the same array and get a sensible result, and so on.

Pointers are strictly "more powerful" than references; anything you can do with references you can do with pointers, but not vice versa. I imagine that's why there are no references in C -- it's a deliberately austere and powerful language.

The down side of pointers-instead-of-references is that pointers are hard for many novices to understand, and make it very very very easy to shoot yourself in the foot.

Pointers are typically implemented as addresses. An address is a number which is an offset into the "array of bytes" that is the entire virtual address space of the process (or, sometimes, an offset into some well-known portion of that address space -- I'm thinking of "near" vs. "far" pointers in win16 programming. But for the purposes of this article let's assume that an address is a byte offset into the whole address space.) Since addresses are just numbers you can easily perform pointer arithmetic with them.

Now consider C#, a language which has both references and pointers. There are some things you can only do with pointers, and we want to have a language that allows you to do those things (under carefully controlled conditions that call out that you are doing something that possibly breaks type safety, hence "unsafe".)  But we also do not want to force anyone to have to understand pointers in order to do programming with references.

We also want to avoid some of the optimization nightmares that languages with pointers have. Languages with heavy use of pointers have a hard time doing garbage collection, optimizations, and so on, because it is infeasible to guarantee that no one has an interior pointer to an object, and therefore the object must remain alive and immobile.

For all these reasons we do not describe references as addresses in the specification. The spec just says that a variable of reference type "stores a reference" to an object, and leaves it completely vague as to how that might be implemented. Similarly, a pointer variable stores "the address" of an object, which again, is left pretty vague. Nowhere do we say that references are the same as addresses.

So, in C# a reference is some vague thing that lets you reference an object. You cannot do anything with a reference except dereference it, and compare it with another reference for equality. And in C# a pointer is identified as an address.

By contrast with a reference, you can do much more with a pointer that contains an address. Addresses can be manipulated mathematically; you can subtract one from another, you can add integers to them, and so on. Their legal operations indicate that they are "fancy numbers" that index into the "array" that is the virtual address space of the process.

Now, behind the scenes, the CLR actually does implement managed object references as addresses to objects owned by the garbage collector, but that is an implementation detail. There's no reason why it has to do that other than efficiency and flexibility. C# references could be implemented by opaque handles that are meaningful only to the garbage collector, which, frankly, is how I prefer to think of them. That the "handle" happens to actually be an address at runtime is an implementation detail which I should neither know about nor rely upon. (Which is the whole point of encapsulation; the client doesn't have to know.)

I therefore have three reasons why authors should not explain that "references are addresses".

1) It's close to a lie. References cannot be treated as addresses by the user, and in fact, they do not necessarily contain an address in the implementation. (Though our implementation happens to do so.)

2) It's an explanation that explains nothing to novice programmers. Novice programmers probably do not know that an "address" is an offset into the array of bytes that is all process memory. To understand what an "address" is with any kind of depth, the novice programmer already has to understand pointer types and addresses -- basically, they have to understand the memory model of many implementations of C. This is one of those "it's clear only if it's already known" situations that are so common in books for beginners.

3) If these novices eventually learn about pointer types in C#, their confused understanding of references will probably make it harder, not easier, to understand how pointers work in C#. The novice could sensibly reason "If a reference is an address and a pointer is an address, then I should be able to cast any reference to a pointer in unsafe code, right?"  But you cannot.

If you think of a reference is actually being an opaque GC handle then it becomes clear that to find the address associated with the handle you have to somehow "fix" the object. You have to tell the GC "until further notice, the object with this handle must not be moved in memory, because someone might have an interior pointer to it". (There are various ways to do that which are beyond the scope of this screed.)

Basically what I'm getting at here is that an understanding of the meaning of "addresses" in any language requires a moderately deep understanding of the memory model of that language. If an author does not provide an explanation of the memory model of either C or C#, then explaining references in terms of addresses becomes an exercise in question begging. It raises more questions than it answers.

This is one of those situations where the author has the hard call of deciding whether an inaccurate oversimplification serves the larger pedagogic goal better than an accurate digression or a vague hand-wave.

In the counterfactual world where I am writing a beginner C# book, I would personally opt for the vague hand-wave.  If I said anything at all I would say something like "a reference is actually implemented as a small chunk of data which contains information used by the CLR to determine precisely which object is being referred to by the reference". That's both vague and accurate without implying more than is wise.

  • @Filini,

    a address is not a way to get a reference to the house, copying the address or asking for a address is a way to get a reference to the house. The address is the reference.

    A address is the information that allows you to get to the house, through a well known process (following the address) in programming terms, dereferencing the reference).

    A address is a "piece of information" that when processed in a well known way, allows you to access the object it refers to.

    For example

    var person = GetSomePerson(..) // Aquire a address of the person we are interested in

    var age = person.GetAge();

    // there are two operations here

    1) "person." , follow the address of person to reach the person through a well know way (if the address is a phone number, call him, it it's and email email him, if it's a house address go there)

    "person." ->  "the real person" (dereference the reference)

    2). GetAge() call, is performed on "the the real person" not on it's reference.

    Let's say you are a company CEO called C#, you say to one of your employee, "here's the address of John get me his age". (your employee know it has to follow the address, implicit dereferencing)

    If you were an company CEO called C, and you would say to an employee "here's the address of John get me his age". The employee would answer the address doesn't have an age.  Then you would have to say, follow the address and get me the age of the person you found at the address. (pointers need explicit dereferencing).

    In C# dereferencing of references is implicit and hidden behind the member access operator ".", when used after a  variable name of a variable that stores a reference.

  • For developers coming into C# fresh, or from scripting or another managed language, "address" doesn't have the meaning or the implications that it does for a C programmer. Address when understood in the sense of postal, email or web is a reasonable analogy for a reference in a managed runtime. To reach the widest audience, and keep things simple for novices, I think the original statement is fine, but should be accompanied by a warning:

    "a variable of reference type stores the address of the object (C programmers take note: not *literally* the address in memory; references are not pointers)."

  • @Pop, maybe I wasn't clear enough.

    My point was that a "reference" is a way to find/handle the object (correct me if I'm wrong), and the address is not the only way to find the house (Jon asked for a way to explain it without "address").

    There are other possible implementations.

    I can write "the house at coordinates 150,95" on the paper instead of "the house at 27, Evergreen Terrace, Greendale".

    Different implementations of reference:

    - Address: based on an index (the streets registry of the Unites States, or whatever)

    - Coordinates: double index on the whole world map

    - Polar Coordinates: algorithm based on a common start point

  • Ok, I understand the post, but if a reference doesn't hold an address, what does it hold? I understand the abstract definition but when i pass a reference what the hell am i  passing?

    You're passing enough information to allow the callee to find the referenced thing. What that information is, you don't need to care about.

    Look at it this way -- ultimately, the reference is going to somehow locate an address. But why stop there in your quest to dig into an abstraction? That address is an address in a virtual memory space. Somewhere in the operating system there is a map between the address in the virtual memory space and the location of the information in physical RAM and/or the swap file on disk.

    Do you care about that level of abstraction? Probably not -- the operating system manages that for you so seamlessly that most of the time you can just ignore it.

    My point is that "a reference" is just a magic token that lets the callee find the information. It happens to be an address. What's an address? It's a magic token that lets the operating system find the information. For most applications you don't need to understand how the magic works, so don't stress about it. -- Eric

  • If I had to explain this to a beginner, I would probably make the distinction that an address is a means of *locating* something, whereas a reference is merely a means of *identifying* it.  Addresses tell you *where*, references only tell you *what*.

    It's the difference between saying "get me the Johnson file" and "get me the 6th file in the 3rd drawer from the bottom".  Both are technically references but the second assumes much more and is more prone to error.

    Oh, and to answer the question about "if a reference doesn't hold an address, what does it hold?"  I can think of at least three possibilities:

    - A CPU register

    - A unique key (i.e. hash code) in some dictionary

    - A function that retrieves the value (this is technically still an address, but not the address you would expect)

    It's best not to worry about it - that's why the compiler does it for you.

  • I think Jon Skeet's first post is a good start: A house has an address, a piece of paper with "go to 123 Main St." on it is a pointer to the house.

    A reference to the house would be a piece of paper with "go to Silverhalide's house" on it.

    Ultimately, they are both talking about the same house. One is direct, allows things like "go to the house 3 doors down from 123 Main St.", and is fragile: if I move, "123 Main St." becomes useless for knowning anything about me. The other is indirect, and "Silverhalide's house" is still good when I move from 123 Main St. to 321 Water St.

    If pointers are "indexes into an array of memory", then I think of references as indexes into an array of (indexes into an array of memory). This allows us to change the actual memory location of the referenced object around without needing to change the reference, it allow us simplify GC since all use of the object goes through the handle (unlike pointers which can randomly reference any memory)

    References also tend to know more about the referenced object then C-style pointers do. C-style pointers can point to any piece of memory and treat it as any type of object regardless of what lies underneath (ignoring undefined behaviour, etc.). Pointers tend not to contain any information other than the address. References may actually know the reference count or other "meta" information about the object.

  • References are *directions* to a thing.

    That an acceptable implementation of directions (the most obvious and simplest) is the absolute position relative to a well defined origin does not mean that you should assume that that's how it works.

    In a GC environment the directions idea is even better because things move around in unpredictable ways but so long as you have a reference the GC promises it will keep the directions up to date all the time.

    If for some reason, say to talk to an older program that doesn't know how to read anything but the absolute location sort of directions, you can ask the GC to give you a copy of those absolute directions which is a pointer. However older programs also don't expect directions to change, and the GC probably couldn't change the pointer where it is going if it tried. So to be helpful (who likes getting lost in seg fault land) it can promise not to move the thing for a while, since you normally wish to do both at the same time fixed exists.

  • Regarding your examples in the response to my comment: the first one ("you can have a pointer to a pointer to a pointer to a pointer to an int if you want") is not an example of something you can do with pointers and can't do with reference - it's an example of a syntax (int****) that doesn't exist with references - but what can you do with that syntax? As for the second example - I never thought of that! Are there any more limitations in reference types? (There are a lot with value types - you can't keep two references to the same int as fields, for example)

    Regarding your response to Jonathan: There are more abstraction layers in between, right? I'd like to know the low-level details; what is actually kept in the reference. If I thought about it in a C# world (if the CLR was implemented in C#) it'd probably be a reference type with a pointer inside it, and the GC would change the pointer when relocating data (thus changing the pointer for everyone). But what is actually kept there? A pointer to a pointer? An index to a really big "All reference types data" array? An imp that tells the runtime where to look?

  • Respectfully, Eric, I have to point out that imo, anyway, you're not necessarily going to be able to put yourself into the shoes of a novice programmer, or more precisely, not necessarily going to be able to accurately predict what novices will and won't understand.

    As a guy who is way, way closer to that particular demographic :-) I can tell what might be useful for me. (Of course, I represent a set of 1 w/r/t people's learning styles.) I am a mild advocate of the approach of using analogy even if it isn't perfect. My justification is two-fold: true novices need something to grasp onto that gives them a concrete model for what the hell is going on; people (well, I) accrue knowledge by extending, comparing, and/or contrasting what they (I) already know. I need a point at which the new knowledge maps to something, anything that gives some sort of comprehensible model. The model has to be sufficient only to the point of getting the novice (me) over the initial conceptual hurdle.

    The second part of the justifiaction is that once the novice has an overall model of what's going on, it's ok to explain that the initial model was not entirely accurate. ("Remember how I told you how references were like ... ? I lied.") Assuming that the original analogy was not just completely misleading, people can adjust their view, because they have a more sophisticated understanding of what's going on. You just can't explain MS-Word styles and templates to someone who's still grappling with how to center a paragraph.

    Just to be clear, I have no problem with an analogy. "A reference is like the address of an apartment" is fine. But "a reference is an address" is defining jargon in terms of other jargon. -- Eric

    I also wonder how much of this a novice programmer has to understand at all. A problem that I've seen with books that are aimed at novices is that they're rarely aimed at novices. This is due to my very initial point, which is that someone who has sufficient understanding of (e.g.) C# to write a book about it is almost certainly not going to remember what it was like when all of this stuff was just a tangled mass of blurry concepts in their head. I, too, have reviewed books, and I virtually always have to point out to authors that they are assuming that the reader already understands things that the author has not in fact explained, that the author is making (sometimes huge) conceptual leaps between their initial explanations and their subsequent discussion, or that -- my last point -- they're writing about stuff that's just not necessary for the reader at that particular point.

    Anyway, thanks for listening. :-)

  • Aren't we missing the really important point?  We should define references by the semantic differences between reference types and value types.

    C# has variables

    Variables have a type

    Types are classified as either references types or value types

    Variables support assignment

    Value type variables copy state on assignment

    Reference type variables share state on assignment

    This means mutating operations on value type variables affect only that variable while mutating operations on reference type variables affect all variables that share that reference.

    For example consider the different semantics of the following operations;

    Person x = new Person(28);

    Person y = x;

    x.HappyBirthday();

    int a = 28;

    b = a;

    a++;

    Then extend the assignment rules to passing parameters and how ref/out parameters can further change the semantics of variables.

  • > Value type variables copy state on assignment

    > Reference type variables share state on assignment

    > This means mutating operations on value type variables affect only that variable while mutating operations on reference type variables affect all variables that share that reference.

    I think this is a wrong way to go about it. When a variable is of a reference type, the value of that variable is the reference - so reference type variables also copy state on assignment, it's just that the state happens to be the reference! With your approach, you'll have to answer all sorts of inconvenient questions, such as "what state is copied from where to where when assigning a null value to a variable".

    In contrast, ref arguments can be described this way: there's no "ref value" - a ref is an alias for a location, and that's it. The reason is that, in C#, there are no operations on refs, so wherever you deal with them, you're _always_ dealing with the aliased location, never with the "address". This is in contrast to reference types, for which you do deal with actual "references as values" - when comparing them, for example.

    This is really very similar to the difference between pointers and references in C++, which is probably why it's so tempting for people with C++ background to describe them that way.

  • I agree with Eamon's comment above. There's no need to put an equal sign between the terms "address" and "memory address". In fact, most C/C++ books explicitly say that a pointer variable stores a **memory** address.

  • c++ already has the "reference" type, it's just alias of some type, so I think this word in C# should have the same meaning, it's just an alias,used  for convenience. In the c# language specification,the author never says a reference equals a pointer;

    pointers can do everything you needed to manipulate the memory,but pointers are also sources of bugs,so C# designed many features to free us from that

  • While I agree that a simple and shallow statement like "a variable of reference type stores the address of the object" is a really bad explanation, I strongly disagree with the seemingly implied notion that an author shouldn't use the word "address" at all when explaining the concepts of reference type and value type.

    As a learner, I would greatly appreciate an author's good explanation of what "reference type" means, and the only way to give a good explanation is to explain a little about the memory model and use the word "address"! I disagree that in order for the reader to understand, he/she would need a *deep* understanding of the memory model. Yes, he/she would need some explanation of the memory model, but it doesn't have to be that deep and complicated. It just has to be good.

    Personally, hand-waving would really really put me off and make me think very poorly of the author. It isn't that extremely hard to explain, and if you (as a hypothetical author) felt pedagogically that it really would be too hard on the poor little reader, then fine; hand-wave at first. Then, after that, include a side panel or a "more in-depth look" box for those readers that are serious about learning. Hand-waving, to me, indicates a lazy author who just can't be bothered to try.

    I'd like to present to you a good example of an explanation that uses the word "address." The following is an excerpt from Microsoft® Visual C#® 2008 Step by Step, by John Sharp, from page 145 of Chapter 8, Understanding Values and References.

    (Mr. Sharp has just finished giving an example of what happens in memory when you declare a variable as a value type.)

    Begin Quote:

    "Class types, such as Circle (described in Chapter 7), are handled differently. When you declare a Circle variable, the compiler does not generate code that allocates a block of memory big enough to hold Circle; all it does is allot a small piece of memory that can potentially hold the address of (or a reference to) another block of memory containing Circle. (An address specifies the location of an item in memory.) The memory for the actual Circle object is allocated only when the new keyword is used to create the object. A class is an example of a reference type. Reference types hold references to blocks of memory."

    End Quote.

    After this quote, Mr. Sharp gives an example of code demonstrating the differences in copying a value type variable versus copying a reference type variable, and he includes a visual diagram to help explain.

    Now, that is a good, accurate explanation.

    Here are several counter-points to your argument that I'd like to present, using Mr. Sharp's explanation as the example:

    1. It's not a lie. Mr. Sharp uses the term "address" to explain, but at no point does he impart upon the reader that references can be treated as addresses by the user. The reader doesn't come out suddenly thinking "wow, now I can manipulate references numerically! Let's try adding and subtracting from them!"

    2. It is a very good explanation that imparts a great deal of understanding to novice programmers without being overly complicated or overwhelming. Notice how Mr. Sharp gives a clear, simple understanding of the memory model without getting into complicated details. At no point does he use the words "stack" or "heap," which meant he didn't need to get into the nitty gritty details of what a stack or heap are or how they work.

    3. Novice C# programmers have to get a good understanding of references anyway if they want to leave the ranks of the novice. By the time they get to learning about pointers, they wouldn't be as novice as they once were. Having a good understanding of references would make it easier, not harder, to understand how pointers work. The learner could sensibly think, "Wow. That's why we have references in C#; they are so much safer than pointers."

    In summary, if you take the time and care to thoughtfully explain, the reader would learn more and appreciate it more than having a vague hand-waving.

    Alright, that concludes my two cents.

  • Alex G., I believe that the Mr. Sharp example illustrates Eric's point exactly.  Look at the modified quote below that does not contain "address".

    "Class types, such as Circle (described in Chapter 7), are handled differently. When you declare a Circle variable, the compiler does not generate code that allocates a block of memory big enough to hold Circle; all it does is allot a small piece of memory that can potentially hold a reference to another block of memory containing Circle.  The memory for the actual Circle object is allocated only when the new keyword is used to create the object. A class is an example of a reference type. Reference types hold references to blocks of memory."

    Isn't this more clear than the original?  The original adds confusion when it brings in the term "address" and tries to define it.  I personally am confused whether Mr. Sharp is trying to say "address" and "reference" are the same or whether he is expicitly trying to say they are different.  Keep it SIMPLE!

Page 2 of 6 (76 items) 12345»