Skip to main content
GameDev.net gamedev.net
🔒 Locked

References vs Pointers

Started by hale550 Dec 27, 2006 at 1:05 AM 14 replies 2k views
Original Post
hale550
hale550
Hey, everyone, this is my first post here. I just finished both the references and pointers chapters in this great book - "Beginning C(plus plus) Game Programming", and the concepts are pretty clear to me, but for some reason the author prefers using references whenever possible as apposed to pointers. It's clear there's a reason for this, however, the only one Mr. Dawson (author) provides is that the syntax for references is clearer. This truely is a mystery of ultimate intrigue! Who is bold enough to venture a stab at this?! Awesome community BTW - looking forward to frequently revisiting.
-HaleMy blog
Ra
Ra
References, unlike pointers, must be initialized when they are created. This means you can't have a null reference and any object passed as a reference is guaranteed to exist. A reference is more of an alias of an object, while a pointer just specifies a memory address which isn't necessarily anything valid.

So use references whenever possible and only use pointers when they're absolutely necessary. It's both easier and safer.
Ra
hale550
hale550
Yeah, I understand that, but say you have the option to choose between using a constant pointer or a reference, to a single object, the pointer adheres to the address of the object where the reference, the actual object. If that much is negligible, then what does it matter which I use?
-HaleMy blog
Ra
Ra
The pointer can point to an invalid object, the reference cannot. You have no way of telling if a pointer is actually a valid object in code.
Ra
hale550
hale550
constant pointers don't have to be initialized?
-HaleMy blog
Ra
Ra
Standalone there is no reason to use a constant pointer over a reference, as they'll have the same effect. When passing an object to a function however, a constant pointer can be initialized to anything, while the reference will point to a valid object.

Another thing I forgot to mention is that pointers can be manipulated (++ -- + - []) and point to a number of objects over their lifetime, while references can only point to one object.
Ra
daviangel
daviangel
Use references when you can, and pointers when you have to.
And remember not to confuse references and pointers since a pointer is a complicated thing whereas a reference is just another name for an object making it easier for you to keep track of things.
So what Ra is basically saying and what your book should've is that a reference can never be changed while a pointer can.
It's these kinda things that make C++ such a difficult language!
[size="2"]Don't talk about writing games, don't write design docs, don't spend your time on web boards. Sit in your house write 20 games when you complete them you will either want to do it the rest of your life or not * Andre Lamothe
hale550
hale550
I See, that makes much more sense now. Thanks a bunch!
-HaleMy blog
Skizz
Skizz
Quote:
Original post by Ra
The pointer can point to an invalid object, the reference cannot. You have no way of telling if a pointer is actually a valid object in code.


Well, that's not entirely true:
  int &invalid = *(int *)0;

Although the above is obviously wrong (but compilable), it could manifest thus:
void func (int &param){}void foo (void){  int *ptr = 0;  func (*ptr);}


Skizz
ToohrVyk
ToohrVyk
Quote:
Original post by Skizz
Well, that's not entirely true:
  int &invalid = *(int *)0;


The error lies in the *(int*)0, which dereferences a null pointer, and nothing can be said about what happens once this expression is evaluated (the standard explicitly says it is undefined behaviour). The int &invalid = part is entirely meaningless, since it won't be reliably reached anyway. In particular, if you say that invalid is an invalid reference, then I can say that invalid is cheese, and we'd both be right about it.

In short, make sure you dereference a valid pointer when creating a reference. The point is, unlike pointers, which may hold any address (including an invalid one), references always "point" to a valid object. Any attempts to make a reference "point" to an invalid object results in undefined behaviour instead.
Skizz
Skizz
Quote:
Original post by ToohrVyk
Quote:
Original post by Skizz
Well, that's not entirely true:
  int &invalid = *(int *)0;


The error lies in the *(int*)0, which dereferences a null pointer, and nothing can be said about what happens once this expression is evaluated (the standard explicitly says it is undefined behaviour).


It is undefined because it can be perfectly valid to write to address 0 (yes, really) and sometimes it's not - it depends on the system and context the program is running on. The standard had to consider architectures other than the Wintel PC (embedded systems, non Intel, non Windows, etc).

The first line of code was to show that it is possible to have a reference object refering to a non-valid object, I could have used 0x12345678 instead of 0. The second block of code was to demonstrate how this could happen in a real world situation.

My point was to disprove the statement "The pointer can point to an invalid object, the reference cannot." (my emphasis) and that you shouldn't blindly assume references are valid.

Skizz
ToohrVyk
ToohrVyk
Quote:
Original post by Skizz
The first line of code was to show that it is possible to have a reference object refering to a non-valid object, I could have used 0x12345678 instead of 0. The second block of code was to demonstrate how this could happen in a real world situation.


This is incorrect. Your program does not have any reference objects, because the code that is responsible for creating a reference is not executed before undefined behaviour happens. If you arbitrarily state that you have a reference in your program, then I can arbitrarily state that the reference points to a valid object — we're both making assumptions over what undefined behaviour will happen to do, so we'd both be wrong anyway.

Again, if you're dereferencing a pointer which contains an invalid address, you create undefined behaviour. The same happens when you invalidate an object that is currently being referenced. In fact, anything that would contradict the "references are valid" assertion is undefined behavior, and everything is possible from then on.

Quote:
My point was to disprove the statement "The pointer can point to an invalid object, the reference cannot." (my emphasis) and that you shouldn't blindly assume references are valid.


If you have a reference, then it is valid. If you have undefined behaviour, you have a problem, but not an "invalid reference" (whatever that would mean). I fail to see how someone could be programming while actively assuming that their program contains undefined behaviour — what could they expect from such a program?

Quote:
It is undefined because it can be perfectly valid to write to address 0 (yes, really) and sometimes it's not - it depends on the system and context the program is running on. The standard had to consider architectures other than the Wintel PC (embedded systems, non Intel, non Windows, etc).


Please, please, please do not confuse address 0 and null pointers. A null pointer is defined by the standard as a pointer which never points to anything, ever (and, as a consequence, should not be dereferenced) — if address 0 can be safely written to, then address 0 won't be used to represent a null pointer, period.

Second, you seem to be confusing undefined behavior with implementation-defined behavior. Implementation-defined behavior (such as when writing to certain predetermined addresses in memory) is always defined, but not by the standard (it's required to be defined by the implementation, obviously). Undefined behavior, on the other hand, is not required to be defined, deterministic or even reproductible. Something (or nothing) happens and that's about all the guarantees you have. Sure, many implementations will explain that this-or-that may happen when a certain type of undefined behavior happens (to aid in debugging), but this behavior should never be relied on in code.
Skizz
Skizz
Quote:
Original post by ToohrVyk
Please, please, please do not confuse address 0 and null pointers.

I didn't. None of my posts have mentioned null pointers.
Quote:
Original post by ToohrVyk
The error lies in the *(int*)0, which dereferences a null pointer

but it appears you did.

Nit-picking aside. In general, references should be valid objects and you are correct in what you say. In practice, you can easily write code that compiles without warnings or errors but breaks the reference rules (i.e. it should reference a valid object) and should be something to remember when debugging (i.e. a reference is implemented like a pointer and could point anywhere). Dereferencing pointers to get a reference is a commonly used construct:
stream &stream::operator << (/*args*/){  return *this;}

and 'this' can be a null pointer:
stream *s = new stream (/*args*/); //could return a null pointer*s << something << something_else;



Skizz
Spoonbender
Spoonbender
Quote:
Original post by hale550
Yeah, I understand that, but say you have the option to choose between using a constant pointer or a reference, to a single object, the pointer adheres to the address of the object where the reference, the actual object. If that much is negligible, then what does it matter which I use?


Yeah, if both will do the job, then obviously use the one with the cleaner syntax... I don't see how that is a mystery. [wink]

References always point to the same object, a pointer can be set to point to a different object. If you don't need that capability, then use references where you're sure it won't happen accidentally.
ToohrVyk
ToohrVyk
Quote:
Original post by Skizz
Quote:
Original post by ToohrVyk
Please, please, please do not confuse address 0 and null pointers.

I didn't. None of my posts have mentioned null pointers.
Quote:
Original post by ToohrVyk
The error lies in the *(int*)0, which dereferences a null pointer

but it appears you did.


There, you did it again. The standard specifies that a compile-time integer constant equal to 0 can be converted to a pointer, and doing so yields a null pointer (as opposed to a pointer with the address 0, which must be obtained through other means). So (int*)0 yields a null pointer, and you dereferenced it.

Yes, the creators of the C language deserve a beating for this part.

Quote:
Nit-picking aside. In general, references should be valid objects and you are correct in what you say. In practice, you can easily write code that compiles without warnings or errors but breaks the reference rules (i.e. it should reference a valid object) and should be something to remember when debugging (i.e. a reference is implemented like a pointer and could point anywhere).


We agree on this. It's good to keep this in mind when debugging, but writing code that relies on this is definitely not correct.

Quote:
Dereferencing pointers to get a reference is a commonly used construct.


Although it is then the responsibility of the pointer dereferencer to ensure that the pointer is valid. To use your code:

stream &stream::operator << (/*args*/){  // This is always valid, since <tt>this</tt> is guaranteed to be   // non-null in a well-formed program  return *this;}// This is safe by default - new never returns a null pointer, it// throws an exception if it cannot return a valid pointer to an// initialized object. Some compiler options may disable this, // although it would be foolish to do so for code that relies on the// legitimate "new throws" behavor.stream *s = new stream (/*args*/); // Assuming that the s pointer was modified in the mean time, we// have to assume that it is either null or valid. Therefore:assert(s);// We know that s is not a null pointer*s << something << something_else;


Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.