58

what happens when you dereference a pointer when passing by reference to a function?

Here is a simple example

int& returnSame( int &example ) { return example; }

int main()
{
  int inum = 3;
  int *pinum = & inum;

  std::cout << "inum: " <<  returnSame(*pinum) << std::endl;

  return 0;          

}

Is there a temporary object produced?

MWright
  • 1,681
  • 3
  • 19
  • 31
  • This isn't valid C++, and you're not passing by value. – Luchian Grigore Jul 05 '12 at 15:04
  • `int pinum = & inum` - This code won't compile if you turn your compiler warning level up. – Oliver Charlesworth Jul 05 '12 at 15:05
  • @OliCharlesworth even without the warning level, `*pinum` shouldn't compile. – Luchian Grigore Jul 05 '12 at 15:05
  • 6
    I have an urge to link to [The Definitive C++ Book Guide and List](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – R. Martinho Fernandes Jul 05 '12 at 15:05
  • 17
    I'm not sure downvoting the question into oblivion is necessary. We here have the unique opportunity to correct the broken understanding of references of the author. – Wug Jul 05 '12 at 15:07
  • @Wug: Wrong. That's what downvoting is for and there are plenty of comments and 1 answer already. –  Jul 05 '12 at 15:07
  • Massive typo it was pass by reference not value. – MWright Jul 05 '12 at 15:08
  • 14
    @0A0D Downvoting is for questions that have little ability to elicit useful answers. As pointers and references are an exceedingly common stumbling block for early programmers using languages that support them, and given a typical instance of such a misunderstanding, this could probably be answered in such a way as to be very useful to other lost beginners. – Wug Jul 05 '12 at 15:12
  • 12
    Too late to answer, but: dereferencing the pointer doesn't create a copy; it creates an *lvalue* that refers to the pointer's target. This can be bound to the *lvalue* reference argument, and so the function receives a reference to the object that the pointer points to, and returns a reference to the same. This behaviour is well-defined. (If it took the argument by value, then that would create a temporary copy, and returning a reference to that would be bad, giving undefined behaviour if it were accessed). – Mike Seymour Jul 05 '12 at 15:13
  • If someone were to reopen this question, I'd be willing to take a stab at answering it in a way that addresses the misunderstanding of reference and pointer types in addition to the actual question – Wug Jul 05 '12 at 15:15
  • @Wug: http://meta.stackexchange.com/questions/111421/what-is-downvoting-really-for –  Jul 05 '12 at 15:16
  • @0A0D "Is this post useful and clear?" It's clear, and a little effort can make it useful. – Wug Jul 05 '12 at 15:18
  • 1
    @Wug You might have missed the original state of the question (which is not available now, due to the 5 minute edit grace period) : code that didn't actually compile, and a "massive typo" that removd all of the the little sense that could be made from what was there. That's what many of those downvotes downvoted. It wasn't clear at all and it showed very little research effort, because it made assumptions (because of typos) that any basic understanding of C++ would show were wrong. – R. Martinho Fernandes Jul 05 '12 at 15:18
  • @Wug: Well, it's only useful to the point of showing what not to do, which does not fit with the intent of the question. –  Jul 05 '12 at 15:19
  • 9
    @0A0D: I must agree with Wug here, the question (once the *value* was corrected to *reference*) is a valid question. The downvotes (any of the ones after that fix) are only penalizing the lack of knowledge, which should **not** be a good practice in a Q&A site. Stackoverflow is not about *fancy* questions, just about *questions*, and helping people understanding. – David Rodríguez - dribeas Jul 05 '12 at 15:21
  • @DavidRodríguez-dribeas: "This question is ambiguous, vague, incomplete...". –  Jul 05 '12 at 15:22
  • 3
    @0A0D: What part is ambiguous? vague? incomplete? The assumption in the question is **wrong**, but it is not ambiguous vague or incomplete. What in the question (at least in the current form) meets that description? That is, the question can be rewritten as: *In the code above, why is the copy constructor not called? would it exhibit undefined behavior?* – David Rodríguez - dribeas Jul 05 '12 at 15:24
  • It cannot be reasonably answered in its current form and it is ambiguous. –  Jul 05 '12 at 15:25
  • 3
    @0A0D: Simple stab at an answer: *You are mistaken, dereferencing a pointer does not create a copy, but an lvalue*. Note: this has already been tackled by Mike Seymour in a previous comment to a greater length. I did not see him having issues trying to clear the misunderstanding. – David Rodríguez - dribeas Jul 05 '12 at 15:27
  • Let me know if I haven't done it justice. References warped my poor brain too back when I first met them. – Wug Jul 05 '12 at 16:11

3 Answers3

55

Dereferencing the pointer doesn't create a copy; it creates an lvalue that refers to the pointer's target. This can be bound to the lvalue reference argument, and so the function receives a reference to the object that the pointer points to, and returns a reference to the same. This behaviour is well-defined, and no temporary object is involved.

If it took the argument by value, then that would create a local copy, and returning a reference to that would be bad, giving undefined behaviour if it were accessed.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
35

The Answer To Your Question As Written

No, this behavior is defined. No constructors are called when pointer types are dereferenced or reference types used, unless explicitly specified by the programmer, as with the following snippet, in which the new operator calls the default constructor for the int type.

int* variable = new int;

As for what is really happening, as written, returnSame(*pinum) is the same variable as inum. If you feel like verifying this yourself, you could use the following snippet:

returnSame(*pinum) = 10;
std::cout << "inum: " << inum << std::endl;

Further Analysis

I'll start by correcting your provided code, which it doesn't look like you tried to compile before posting it. After edits, the one remaining error is on the first line:

int& returnSame( int &example ) { return example; } // semi instead of colon

Pointers and References

Pointers and references are treated in the same way by the compiler, they differ in their use, not so much their implementation. Pointer types and reference types store, as their value, the location of something else. Pointer dereferencing (using the * or -> operators) instructs the compiler to produce code to follow the pointer and perform the operation on the location it refers to rather than the value itself. No new data is allocated when you dereference a pointer (no constructors are called).

Using references works in much the same way, except the compiler automatically assumes that you want the value at the location rather than the location itself. As a matter of fact, it is impossible to refer to the location specified by a reference in the same way pointers allow you to: once assigned, a reference cannot be reseated (changed) (that is, without relying on undefined behavior), however you can still get its value by using the & operator on it. It's even possible to have a NULL reference, though handling of these is especially tricky and I don't recommend using them.

Snippet analysis

int *pinum = & inum;

Creates a pointer pointing to an existing variable, inum. The value of the pointer is the memory address that inum is stored in. Creating and using pointers will NOT call a constructor for a pointed-to object implicitly, EVER. This task is left to the programmer.

*pinum

Dereferencing a pointer effectively produces a regular variable. This variable may conceptually occupy the same space that another named variable uses, or it may not. in this case, *pinum and inum are the same variable. When I say "produces", it's important to note than no constructors are called. This is why you MUST initialize pointers before using them: Pointer dereferencing will NEVER allocate storage.

returnSame(*pinum)

This function takes a reference and returns the same reference. It's helpful to realize that this function could be written with pointers as well, and behave exactly the same way. References do not perform any initialization either, in that they do not call constructors. However, it is illegal to have an uninitialized reference, so running into uninitialized memory through them is not as common a mistake as with pointers. Your function could be rewritten to use pointers in the following way:

int* returnSamePointer( int *example ) { return example; }

In this case, you would not need to dereference the pointer before passing it, but you would need to dereference the function's return value before printing it:

std::cout << "inum: " <<  *(returnSamePointer(pinum)) << std::endl;

NULL References

Declaring a NULL reference is dangerous, since attempting to use it will automatically attempt to dereference it, which will cause a segmentation fault. You can, however, safely check if a reference is a null reference. Again, I highly recommend not using these ever.

int& nullRef = *((int *) NULL);      // creates a reference to nothing
bool isRefNull = (&nullRef == NULL); // true

Summary

  • Pointer and Reference types are two different ways to accomplish the same thing
  • Most of the gotchas that apply to one apply to the other
  • Neither pointers nor references will call constructors or destructors for referenced values implicitly under any circumstances
  • Declaring a reference to a dereferenced pointer is perfectly legal, as long as the pointer is initialized properly
Wug
  • 12,956
  • 4
  • 34
  • 54
  • 2
    Gigantic block of text yay. I'll probably make about 8 edits for typos – Wug Jul 05 '12 at 16:06
  • I think it would be more appropriate to say that dereferencing a pointer produces an lvalue, rather than saying that dereferencing a pointer produces a variable. Please do consider if it would make sense to edit that into your answer. – batbrat Mar 30 '17 at 02:17
2

A compiler doesn't "call" anything. It just generates code. Dereferencing a pointer would at the most basic level correspond to some sort of load instruction, but in the present code the compiler can easily optimize this away and just print the value directly, or perhaps shortcut directly to loading inum.

Concerning your "temporary object": Dereferencing a pointer always gives an lvalue.

Perhaps there's a more interesting question hidden in your question, though: How does the compiler implement passing function arguments as references?

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084