13

When the c++ compiler generates very similar assembler code for a reference and pointer, why is using references preferred (and considered safer) compared to pointers?

I did see

EDIT-1:

I was looking at the assembler code generated by g++ for this small program:

int main(int argc, char* argv[])
{
  int a;
  int &ra = a;
  int *pa = &a;
}
3
  • @YouKnowWho: I'd take this with a grain of salt --> a compiler do few errors, but they generally have very interesting effets :) Commented Jan 17, 2011 at 17:09
  • 2
    You didn't provide a citation for "using references [is] preferred (and considered safer) compared to pointers". I'd wager that many people on this forum might disagree with that assertion. Commented Jan 17, 2011 at 17:11
  • 1
    @Tomalak: I don't know of any concrete reference. And that's what I am trying to get cleared through this question.
    – yasouser
    Commented Jan 17, 2011 at 18:15

9 Answers 9

23

It's considered safer because a lot of people have "heard" that it's safer and then told others, who now have also "heard" that it's safer.

Not a single person who understands references will tell you that they're any safer than pointers, they have the same flaws and potential to become invalid.

e.g.

#include <vector>

int main(void)
{
    std::vector<int> v;
    v.resize(1);

    int& r = v[0];
    r = 5; // ok, reference is valid

    v.resize(1000);
    r = 6; // BOOM!;

    return 0;
}

EDIT: Since there seems to be some confusion about whether a reference is an alias for an object or bound to a memory location, here's the paragraph from the standard (draft 3225, section [basic.life]) which clearly states that a reference binds to storage and can outlive the object which existed when the reference was created:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and
  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
  • the original object was a most derived object of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).
5
  • 3
    +1 simply for this: "It's considered safer because a lot of people have "heard" that it's safer and then told others, who now have also "heard" that it's safer."
    – yasouser
    Commented Jan 17, 2011 at 18:10
  • 9
    "Not a single person who understands references will tell you that they're any safer than pointers" ---> I understand them (as I'm sure others do too) and I still say they're safer. Note that "safer" does not mean "safe"... you can still shoot yourself in the foot, it's just harder.
    – user541686
    Commented Jan 17, 2011 at 20:12
  • "a reference that referred to the original object, or the name of the original object will automatically refer to the new object." This quote supports what I have been saying all along, that a reference to an object is no more a "address" than the object itself, i.e., what the quote calls "the name." References are addresses the same way that names are addresses. Both are symbols for the address of a data object in memory. But "the name" of a pointer represents the address of an address, not the address of any data object. Commented Jan 18, 2011 at 4:54
  • 1
    @ThomasMcLoed: Perhaps you missed that pointers fall into exactly the same category -- all three, a pointer, a reference, and the variable name, all address the storage location, not the object. Don't try to equate "the name" with "the object itself", when the standard clearly describes different lifetimes.
    – Ben Voigt
    Commented Jan 18, 2011 at 5:01
  • 1
    @masters3d: Too bad he makes about as many mistakes while explaining it there are in the "fact" he debunks. I got as far as calling if (ptr) "fault-tolerance", which is just absurd. But yes, other people have noticed that references are made safe by the compiler, they have to be made safe by correct code -- exactly like pointers.
    – Ben Voigt
    Commented Jun 5, 2016 at 0:57
8

It depends how you define "safer".

The compiler won't let you create an uninitialised reference, or one that points to NULLness, and it won't let you accidentally make your reference refer someplace else whilst you're using it. These stricter rules also mean that your compiler can give you some more warnings about common mistakes, whereas with pointers it's never really sure whether you meant to do what you did, or not.

On the other hand, the transparency of the syntax -- namely, what Alexandre C. mentioned about what it looks like at the call-site to pass an argument as a reference -- makes it quite easy not to realise that you're passing a reference. Consequently, you might not realise that you're supposed to maintain ownership and lifetime of the argument, or that your argument may get permanently modified.

4
  • 1
    Of course, you can declare a const pointer (I don't mean pointer-to-const), which will require you to initialize it and prevent making it refer somewhere else.
    – Ben Voigt
    Commented Jan 17, 2011 at 17:13
  • 3
    I'm just saying it's not an exclusive benefit of using a reference, you can get the same behavior from a pointer (by declaring it const).
    – Ben Voigt
    Commented Jan 17, 2011 at 17:18
  • Regarding uninitialised references, int *const p also has that feature. Regarding nullness, [[gnu::nonnull]] int *p also has that feature. BTW, the compiler doesn't complain if you assign a (possibly-NULL) pointer to a nonnull pointer nor if you assign it to a reference. So in the end, a reference is as unsafe (and uglier than, IMO) as [[gnu::nonnull]] int *const p. Commented Nov 16, 2021 at 15:58
  • Re: "whereas with pointers it's never really sure whether you meant to do what you did, or not": except if you use Clang's _Nonnull, in which case you can get even more warnings with pointers than with references. References are a disguised [[gnu::nonnull]] *const pointer. Commented Nov 16, 2021 at 18:32
7

Because references (which are simply an alias for other variable) can't be NULL by definition, providing an inherent layer of safety.

55
  • 9
    Forbidding NULLs doesn't make references safe. In fact, references are not any safer than pointers, and the fact that the indirection is hidden makes them more dangerous.
    – Ben Voigt
    Commented Jan 17, 2011 at 17:06
  • 2
    @Alexandre: They can be created as null (dereferencing a pointer), but the most important point is that they can refer to garbage too. Commented Jan 17, 2011 at 17:10
  • 5
    @Alexandre C.: They cannot be NULL unless you have previously invoked UB, at which point all bets are off, therefore it's not the fault of the reference. :) Commented Jan 17, 2011 at 17:11
  • 2
    @Matthie - no, they can't. Dereferencing a NULL pointer is UB. A program that does this is ill-formed already. Thus the lesson to learn there is NOT to dereference invalid or null pointers. Commented Jan 17, 2011 at 17:13
  • 4
    @Ben: No, there is a difference. This function (note the unit) is guaranteed to work: void f(int& i) { i = 0; } each and every time, no matter what. If I have a bug, I never need to consider this function the cause. Contrarily, this function can break: void f(int* i) { *i = 0; }, and passing a null pointer to the function is something I'd need to consider in the face of a bug. What references do is make my functions self-contained, and put the onus on the user of the function to handle any pointers he may have correctly.
    – GManNickG
    Commented Jan 17, 2011 at 20:07
2

Because references must always be initialized and since they must refer to an existing object, it is much harder (but by no means impossible) to end up with dangling references than it is to have uninitialized/dangling pointers. Also, it's easier to manipulate references because you don't have to worry about taking addresses and dereferencing them.

But just to show you that a reference by itself doesn't make your program 100% safe, consider this:

int *p = NULL;
int &r = *p;
r = 10; /* bad things will happen here */

Or this:

int &foo() {
  int i;
  return i;
}

...

int &r = foo();
r = 10; /* again, bad things will happen here */
2

A reference is always initialized from an existing object, thus it can never be NULL, whereas a pointer variable is allowed to be NULL.

EDIT: Thanks for all of the replies. Yes, a reference can indeed point to garbage, I forgot about dangling references.

10
  • 2
    A reference can be initialized from a pointer, so it really isn't any safer.
    – Ben Voigt
    Commented Jan 17, 2011 at 17:05
  • 1
    This isn't necessarily true. A reference can be initialized from a dereferenced pointer, in which case if the pointer is null an exception will be thrown.
    – Beanz
    Commented Jan 17, 2011 at 17:06
  • 1
    @Alexandre C.: No, it is not. It is undefined behaviour. Commented Jan 17, 2011 at 17:12
  • 1
    @nightcracker: Only by letting an object fall out of scope! Yes, it's easy to be left with a dangling reference. Commented Jan 17, 2011 at 17:13
  • 2
    Practically speaking (i.e. on all major compilers) binding a reference by dereferencing a NULL pointer won't cause an access violation, segmentation fault, null pointer exception, or any behavior different from copying a NULL pointer to another pointer. Bad things won't happen until access is made through the reference. A reference is basically an automatically dereferenced pointer, at least in real life. And this is perfectly compliant to the standard... UB means the program can fail immediately, work, or fail later in some obscure unexpected way.
    – Ben Voigt
    Commented Jan 17, 2011 at 17:22
2

It is a little safer, but not the same thing. Note that you have the same problems of "dangling references" as with "dangling pointers". For instance, returning a reference from a scoped object yields undefined behaviour, exactly the same as pointers:

int& f() { int x = 2; return x; }

The only benefit is that you cannot create a null reference. Even if you try hard:

int& null_ref = *((int*)0); // Dereferencing a null pointer is undefined in C++
                            // The variable null_ref has an undefined state.

As class members, pointers are preferred since they have better assignment semantics: you cannot reassign a reference when it has been initialized. The compiler won't be able to provide a default assignment operator if there are reference members in the class.

Therefore, C++ cannot get rid of pointers, and you can use them freely: by passing arguments as pointers and not as (non const) references, you make it clear at the call site that the object will be modified. This can add a little safety, since you see by naked eye what functionsindeed modify objects.

I play a little the devil's advocate, but references are easy to abuse.

5
  • I don't see how the story in the first paragraph makes them less "safe". Just more irritating. Commented Jan 17, 2011 at 17:07
  • @Tomalak: yes, it is a little off topic, but I must confess I read quickly the question. Commented Jan 17, 2011 at 17:09
  • "Dereferencing a null pointer is undefined in C++ so the program is ill formed." That's wrong, and doesn't make any sense. That line is perfectly well-formed (a measurement made of the source code at compile-time), but its result is undefined (a measurement made of the running environment at run-time). A run-time measurement has no bearing on the compile-time measurement, unless you're proposing taking "anything can happen in UB" a bit too far (time machine?).
    – GManNickG
    Commented Jan 17, 2011 at 20:10
  • @GMan: you're right, but the contents of the null_ref are undefined. Commented Jan 17, 2011 at 20:57
  • @Alexandre: You could say that, but undefined behavior is the end-all result. null_ref's value is unknown, sure, but the state of the entire program is unknown.
    – GManNickG
    Commented Jan 17, 2011 at 21:04
0

Well the answer you point out answer that. From the "safer" point of view I think that basically it is hard to write code like :

int* i;
// ...
cout << *i << endl; // segfault

As a reference is always initialized, and

MyObject* po = new MyObject(foo);
// ...
delete po;
// ...
po->doSomething(); // segfault

But as said in the question you mention, that's not only because they are safer that references are used ...

my2c

0

Suppose you didn't have the reference operator <type>& in the language. Then whenever you wanted to pass a reference to an object to a function you would have to do what's done in C, pass the argument as &<myobject> and receive it in the function as a pointer parameter <type>* p. Then you would have to discipline yourself not to do something like p++.

That's where the safety comes in. Passing by reference is very useful, and having the reference operator avoids the risk that you'll modify the pointer.

(Maybe a const pointer would accomplish the same thing, but you've got to admit the & is cleaner. And it can be applied to other variables besides parameters.)

-1

A pointer is an independent variable that can be reassigned to point to another date item, unintialized memory, or just no where at all (NULL). A pointer can be incremented, decremented, subtracted from another pointer of the same type, etc. A reference is tied to an existing variable and is simply an alias for the variable name.

17
  • A reference can point into memory just like a pointer, you can create a reference out of a pointer, so anything that would invalidate the pointer also invalidates the reference.
    – Ben Voigt
    Commented Jan 17, 2011 at 17:12
  • 2
    @ThomasMcLeod: That a reference is an alias is a nice dream, but also a sad and common misconception. Commented Jan 17, 2011 at 17:15
  • @Ben: A reference is not a pointer and does not point into memory anymore than any variable name points into memory. When I declare int i; does "i" "point into memory"? Well, i as a symbol represents a memory address, but it's still not a pointer. Same with a reference. Furthermore, you cannot initialize a reference to T with a pointer to T, unless you dereference the pointer, at which point you are not initializing with a T *, but simply a T. Commented Jan 17, 2011 at 17:45
  • @ThomasMcLeod: Of course a reference points into memory. How else would it be possible to have references as class members and then use them in an entirely different part of the program?
    – Ben Voigt
    Commented Jan 17, 2011 at 17:50
  • @Ben: There is confusion between the semantics of data and the meaning of symbols in C++. When declaring: int i; does "i" point into memory? In a general sense, symbols are memory addresses, including the "i" in "int i = 0;" and the "j" in "int & j = i;". The distinction is the meaning of the data at the memory address. For both i & j, the data behind the symbol is "0". However, in the case of "int * pi = & i;" the data behind symbol pi is a memory address, not the value of i, which is "0". Commented Jan 17, 2011 at 18:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.