Search

6.11 — Reference variables

So far, we’ve discussed two basic variable types:

  • Normal variables, which hold values directly.
  • Pointers, which hold the address of another value (or null) and can be dereferenced to retrieve the value at the address they point to.

References are the third basic type of variable that C++ supports. A reference is a type of C++ variable that acts as an alias to another object or value.

C++ supports three kinds of references:

  1. References to non-const values (typically just called “references”, or “non-const references”), which we’ll discuss in this lesson.
  2. References to const values (often called “const references”), which we’ll discuss in the next lesson.
  3. C++11 added r-value references, which we cover in detail in the chapter on move semantics.

References to non-const values

A reference (to a non-const value) is declared by using an ampersand (&) between the reference type and the variable name:

In this context, the ampersand does not mean “address of”, it means “reference to”.

References to non-const values are often just called “references” for short.

References as aliases

References generally act identically to the values they’re referencing. In this sense, a reference acts as an alias for the object being referenced. For example:

In the above snippet, setting or getting the value of x, y, or z will all do the same thing (set or get the value of x).

Let’s take a look at references in use:

This code prints:

7
8

In the above example, ref and value are treated synonymously.

Using the address-of operator on a reference returns the address of the value being referenced:

Just as you would expect if ref is acting as an alias for the value.

l-values and r-values

In C++, variables are a type of l-value (pronounced ell-value). An l-value is a value that has an address (in memory). Since all variables have addresses, all variables are l-values. The name l-value came about because l-values are the only values that can be on the left side of an assignment statement. When we do an assignment, the left hand side of the assignment operator must be an l-value. Consequently, a statement like 5 = 6; will cause a compile error, because 5 is not an l-value. The value of 5 has no memory, and thus nothing can be assigned to it. 5 means 5, and its value can not be reassigned. When an l-value has a value assigned to it, the current value at that memory address is overwritten.

The opposite of l-values are r-values (pronounced arr-values). An r-value refers to any value that can be assigned to an l-value. r-values are always evaluated to produce a single value. Examples of r-values are literals (such as 5, which evaluates to 5), variables (such as x, which evaluates to whatever value was last assigned to it), or expressions (such as 2 + x, which evaluates to the value of x plus 2).

Here is an example of some assignment statements, showing how the r-values evaluate:

Let’s take a closer look at the last assignment statement above, since it causes the most confusion.

In this statement, the variable x is being used in two different contexts. On the left side of the assignment operator, “x” is being used as an l-value (variable with an address). On the right side of the assignment operator, x is being used as an r-value, and will be evaluated to produce a value (in this case, 7). When C++ evaluates the above statement, it evaluates as:

Which makes it obvious that C++ will assign the value 8 back into variable x.

The key takeaway is that on the left side of the assignment, you must have something that represents a memory address (such as a variable). Everything on the right side of the assignment will be evaluated to produce a value.

Note: const variables are considered non-modifiable l-values.

References must be initialized

References must be initialized when created:

Unlike pointers, which can hold a null value, there is no such thing as a null reference.

References to non-const values can only be initialized with non-const l-values. They can not be initialized with const l-values or r-values.

Note that in the middle case, you can’t initialize a non-const reference with a const object -- otherwise you’d be able to change the value of the const object through the reference, which would violate the const-ness of the object.

References can not be reassigned

Once initialized, a reference can not be changed to reference another variable. Consider the following snippet:

Note that the second statement may not do what you might expect! Instead of changing ref to reference variable value2, it assigns the value of value2 to value1.

References as function parameters

References are most often used as function parameters. In this context, the reference parameter acts as an alias for the argument, and no copy of the argument is made into the parameter. This can lead to better performance if the argument is large or expensive to copy.

In lesson 6.8 -- Pointers and arrays we talked about how passing a pointer argument to a function allows the function to dereference the pointer to modify the argument’s value directly.

References work similarly in this regard. Because the reference parameter acts as an alias for the argument, a function that uses a reference parameter is able to modify the argument passed in:

This program prints:

5
6

When argument n is passed to the function, the function parameter ref is set as a reference to argument n. This allows the function to change the value of n through ref! Note that n does not need to be a reference itself.

Best practice: Pass arguments by non-const reference when the argument needs to be modified by the function.

The primary downside of using non-const references as function parameters is that the argument must be a non-const l-value. This can be restrictive. We’ll talk more about this (and how to get around it) in the next lesson.

Using references to pass C-style arrays to functions

One of the most annoying issues with C-style arrays is that in most cases they decay to pointers when evaluated. However, if a C-style array is passed by reference, this decaying does not happen.

Here’s an example (h/t to reader nascardriver):

Note that in order for this to work, you explicitly need to define the array size in the parameter.

References as shortcuts

A secondary (much less used) use of references is to provide easier access to nested data. Consider the following struct:

Let’s say we needed to work with the value1 field of the Something struct of other. Normally, we’d access that member as other.something.value1. If there are many separate accesses to this member, the code can become messy. References allow you to more easily access the member:

The following two statements are thus identical:

This can help keep your code cleaner and more readable.

References vs pointers

References and pointers have an interesting relationship -- a reference acts like a pointer that is implicitly dereferenced when accessed (references are usually implemented internally by the compiler using pointers). Thus given the following:

*ptr and ref evaluate identically. As a result, the following two statements produce the same effect:

Because references must be initialized to valid objects (cannot be null) and can not be changed once set, references are generally much safer to use than pointers (since there’s no risk of dereferencing a null pointer). However, they are also a bit more limited in functionality accordingly.

If a given task can be solved with either a reference or a pointer, the reference should generally be preferred. Pointers should only be used in situations where references are not sufficient (such as dynamically allocating memory).

Summary

References allow us to define aliases to other objects or values. References to non-const values can only be initialized with non-const l-values. References can not be reassigned once initialized.

References are most often used as function parameters when we either want to modify the value of the argument, or when we want to avoid making an expensive copy of the argument.

6.11a -- References and const
Index
6.10 -- Pointers and const

148 comments to 6.11 — Reference variables

  • Ged

    You said that pointers decay and references don't. If we use a reference array we can get the array's length inside of the function. So we use pointers to array only if we don't know the specific length of the array? And references to array in every other situation? Are there any other exceptions or is this statement like a constant?

    • Alex

      I said no such thing. :) C-style arrays decay to pointers. Pointers do not decay.

      > So we use pointers to array only if we don't know the specific length of the array?

      IMO, you should only use pointers to an array if NULL is a valid argument. Otherwise, use references.

  • Wallace

    Hi Alex,

    I suggest integrating some of the thinking from section 7.3, "Passing arguments by reference," with this section. The present section recommends using references as function parameters without noting the significant concerns that 7.3 raises.

    Thanks for this great resource!

  • Marina

    Hi,

    int value1{ 5 };
        int value2{ 6 };

        int &ref{ value1 }; // okay, ref is now an alias for value1
        cout<<ref<<endl;
        ref = value2;
        cout<<value1<<endl;
        cout<<ref;

    in this code snippet, the values which are printed to screen are 5,6 and 6. I'm a bit confused since you said that expression ref = value2 does not the change ref, but when we print it it prints 6 instead of 5.
    Thanks

    • nascardriver

      doesn't change the reference, it changes the value of `value1`, because that's what the reference is pointing to.

      When you do

      `ref` is still a reference for `value1`, so both lines print the same.

  • Daniel

    Hey, you don't have to use dereferencing operator in following example you can just write

    to achieve the same result. Not sure though whether it's supposed to work like that or whether it might lead to undefined behaviour with some compilers.

    • nascardriver

      Hey

      Your suggestion doesn't do what you expect.

      `sizeof` will return the size of the pointer, not of the array.

      I updated the lesson to use `std::size` instead of `sizeof`. That way your version causes a compilation error because it doesn't do what you want.

  • hellmet

    So calling delete on a reference is equivalent to calling delete on the actual thing right? This means that any and all references are now garbage.

    Also, why can't I have a reference to a heap-allocated object; as in the tutorial, right before the summary, "Pointers should only be used in situations where references are not sufficient (such as dynamically allocating memory)"? I understood it as, "you can't have a reference to the heap-allocated value". The following code seems to work fine, in line with the expectations set forth within this chapter...

    • > calling delete on a reference is equivalent to calling delete on the actual thing right?
      Yes

      > why can't I have a reference to a heap-allocated object
      You can

      Your code still uses pointers. Once in line 5 and then again in the forgotten `delete v1` or `delete &r1`.
      Deleting the reference like that isn't safe, because the behavior of the `&` operator can be changed. `&r1` might not return the address of the referenced object.

      • hellmet

        Could you explain what this means then? "Pointers should only be used in situations where references are not sufficient (such as dynamically allocating memory)" I didn't get that part.

        > Yes, you can
        Okay! I wasn't sure if that was a supported operation

        > Deleting like that isn't safe ...
        So I should never use the delete operator on a reference unless I know what I'm doing right? Delete should only be called on what it was originally assigned to, as the & operator, like any other operator can have different meanings in different contexts. Got it, thanks!

        • > Could you explain what this means then?
          References introduce a problem when they're used to manage dynamic memory, and they can't replace the pointers used for `new` and `delete`, they just hide them.
          Dynamic memory is one place where pointer are better than references.

          > So I should never use the delete operator on a reference unless I know what I'm doing right?
          Right.

          > Delete should only be called on what it was originally assigned to
          `delete` can only be used on memory that was created with `new`. You can move the pointer around in between the `new` and `delete` call, but you have to make sure you don't lose it.

          > the & operator, like any other operator can have different meanings in different contexts.
          Yep.

  • Dear NASCAR driver,
    Please let me point out that program in section "References as aliases", prints 78. I suggest following:

    Regards.

  • Looptheloop

    Hello! thanks for this awesome lecture. Can you also tell me from where to learn the command line and also Linux os? Because I have been hearing that they are an imp part of the progammers cookbook.

  • Xueyang

    Hello, why does using array reference as function parameter prevents array decay to a pointer? In the code below, arr is an array reference. The size of arr is 16, which indicates that arr is not a pointer. However, printing arr itself still gives the memory address of the first element in arr, as if arr has decayed. So what exactly has happened to arr? Thanks.

  • James

    What should we prefer between copy argument and reference argument for build-in data types and non-expensive user-defined types?

  • AlirezaDX

    Hello and thank you for this awesome resource. I have a question regarding references.
    In this chapter one of the uses specified for references is "References as shortcuts". I personally really like using this to make my code more readable and debugging easier.
    In chapter 7.4, Alex mentions: "Because dereferencing a pointer is slower than accessing a value directly, ..." and "references are typically implemented by the compiler as pointers"
    This means using "References as shortcuts" can be a bit slower.
    I was wondering if there is a better alternative.
    For example for given structs:

    Use reference?

    Use macros?

    Using macros is not a really good alternative because of the debugging and scope issues.

  • Red Lightning

    "Note that the second statement may not do what you might expect! Instead of reassigning ref to reference variable value2, it instead assigns the value from value2 to value1 (which ref is a reference of)."

    It is not a mistake, but worth noting. There were two "instead"s in the same sentence, and they were arranged in a confusing way. This sentence might by hard to follow after.

  • Puya

    Hi, thank you for the great tutorial as always. May I suggest we use the

    convention instead of

    My reasoning:
    - Since T& is a compound type, I think it would make more sense that we show that fact by keeping all the parts of the type declaration together
    - In a function signature we could have just the type T&, which would make sense, but T & on it's own would look weird
    - IMO it reads better ("reference to type T")
    - Bjarne Stroustrup also prefers that style

    Cheers

    • Alex

      There's no right answer to this one, as there are just as many arguments for the other way. Some people prefer one form, some prefer the other. Pick whichever you prefer.

  • Max Morgan

    Hey Alex/Nascar,

    In an if statement like the above, is the ++i a reference to our i variable?

    I ask because I am on lesson 8.3 and the ++m_next is changing the value of our private variable. So I was thinking that since ++ is changing our original value, it must be a reference?

    • Hi Max!

      ++prefix usually returns a reference to the object it was called on. But you're not using the return value for anything, so you're probably talking about something else.
      You don't need references when you have access to the original variable. @i and @m_next are not copies, they are the original variable. You can modify them directly without references.

  • limcheling

    Can you elaborate on this: "references are not sufficient (such as dynamically allocating memory)"? What do you mean by "not sufficient" and why dynamic memory allocation would make references insufficient?

    • Alex

      If you want to dynamically allocate memory, operator new returns a pointer to said memory, so you need to stick the result in a pointer. A reference can't be used for this.

  • NXPY

    Why can't we just use the length of the array directly ,say, 5 instead of writing that ? Is it to make it automatically change when array length is changed ?

  • What's the benefit of using references to pass arrays to functions If we need to specify its size explicitly. As if we know the size then we can directly access and manipulate the array by running a for loop (size) number of times.

    And what's the meaning of C style arrays. I have only learned about C style strings.

    One more thing. Why the below program is not showing any output.

    Thanks a lot.

  • Shri

    there is a typo I believe.

        int const *ptr = &value;

    is a constant pointer.

    But in this tutorial, it says :     int *const ptr = &value;
    is a constant pointer

    • Hi Shri!

  • Simone

    Hi Alex,

    Reading this thread and also the related one "Passing arguments by reference" I have made up my mind that assigning by reference is in a way related to assigning using double pointers. I would like to explain better:

    If I have a piece of code of the type:

    then my understanding is that this would be equivalent to the following code:

    with the boundary that if later in the original code I have an assignment like:

    then I should replace with

    while if I have:

    then I should replace it with:

    If I want to print a or b I should write something like:

    Can you please tell me if all that makes sense?
    Thank you.

  • Dixit Singla

  • Quoxa

    Hi,
    at the time of this comment, I have only read up to Chapter 6.11a so do forgive me if the answer to the question I am asking can be found in later chapters.

    My question is: is there any benefit to using a reference over a const pointer, besides the fact that you cannot initialise a reference with null?

    • Hi Quoxa!

      You don't have to dereference variables to override them and you can access members with '.' instead of '->', so two characters of typing less, I guess. Other than that I don't think they are superior to pointers. Pointers have the advantage of being obvious when passing a variable to a function by address.

      References are mostly used when passing arguments by const reference, because they have the performance advantage of pointers and the simpler syntax of "normal" variables.

  • pete

    Hi Alex,
    is there a way to pass (through the function parameters) the length of the array along with the array itself? (or it's reference more presicely). I find it very limiting when trying to modulate functions that deals with arrays.
    once again, thanks you for these amazing tutorials, really not taken for granted.

    • nascardriver

      Hi pete!

  • Peter Baum

    I was thinking about how simple * and & are when used on the right side of an assignment, yet it was confusing to me sometimes when used on the left side.  More generally, I wondered what kinds of things made this lesson confusing to me and perhaps others.  Here are some ideas.  

    ****Symbols used in Algebra have very different meaning in C/C++ ****
    Most people learn about the equal sign and variables in algebra.  It can be confusing to find that the equal sign and variables have a different meaning in C/C++.  Variables are really more like structures, with address, mutability, volatility, type, etc. attached to it besides a value.

    ****    "*" and "&" have inconsistent meaning ****
    This goes way beyond their dual use in multiplication and bit manipulation.  Expressions like *p and *a in a declaration can have a completely different meaning when used to the right of an equal sign.  *p and &a are very simple when used outside of a declaration.  &a is particularly problematic because of the many (seemingly arbitrary) rules that apply.  Isn’t there a better way to create an alias?

    ****lvalue and rvalue****

    One way to confuse people is to have two or more incompatible definitions.  We have this situation with lvalue and rvalue.

    1. By their name, one might suppose that lvalue has something to do with values to the left of the = sign and rvalue something to do with values to the right of the = sign.  A mention of this history might help.

    2. In lesson 6.11 we find
        a. l-values are objects that have a defined memory address (such as variables), and persist
           beyond a single expression.
        b. r-values are temporary values that do not have a defined memory address, and only have
           expression scope.

    3. At https://docs.microsoft.com/en-us/cpp/cpp/lvalues-and-rvalues-visual-cpp we find this careful definition
        a. An lvalue is a glvalue that is not an xvalue.
        b. An rvalue is a prvalue or an xvalue.

    In other words
        a. An lvalue is an expression whose evaluation determines the identity of an object, bit-
           field, or function but does not denote denotes an object or bit-field whose resources can
           be reused (usually because it is near the end of its lifetime).
        b. An rvalue is an expression whose evaluation initializes an object or a bit-field, or
           computes the value of the operand of an operator, as specified by the context in which it
           appears or is an expression whose evaluation determines the identity of an object, bit-
           field, or function and denotes an object or bit-field whose resources can be reused.

    4. Although not a definition, we find as potential characteristics at https://docs.microsoft.com/en-us/cpp/cpp/lvalues-and-rvalues-visual-cpp that
        a. An lvalue has an address that your program can access.
        b. An rvalue  has no address that is accessible by your program or has an address that no
           longer accessible by your program but can be used to initialize an rvalue reference, which
           provides access to the expression.

    Note how these characteristics are different from those in 2 above.

    The webpage https://www.quora.com/What-is-lvalue-and-rvalue-in-C is helpful because it talks about what the compiler and cpu are actually doing.

    Here we have an explanation that suggests why things got so complicated: https://stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues

    Here is a pretty good description that takes us from C through C++11 and move semantics: https://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-and-c/

  • Silviu

    Hello,

    Using references it's good for reducing the memory use ? I think that using references can save a little memory where you need it.

    • nascardriver

      Hi Silviu!

      > Using references it's good for reducing the memory use ?
      Yes

      > I think that using references can save a little memory where you need it
      More than a little in some cases.
      Imaging you had a variable that's 4 megabytes (Maybe a database, something big) and you want to pass it to a function. Passing it by value would require another 4 megabytes to hold the copy. Passing it by reference only requires 4 or 8 bytes, depending on the architecture (x86, x64).
      That's just 0.0000954% or 0.0001907% of the original memory consumption.

  • J

    How's it going Alex? Just learned how to use a ref to a array. My question is if you use the ref to an array as function parameters will you still be able keep the size info inside the function or will it still revert to a pointer.

    • nascardriver

      Hi J!
      Usually when passing an array as a function argument it will deduced to a pointer, losing  the ability to get it's size. If you want to pass it by reference you will need to explicitly state the array's size in the function parameter, making the sizeof call redundant, because you already need to know the array's size when writing the function.
      You could use a template to accept custom size arrays, that's an ugly solution though.

      Lesson 6.15 (std::array) and 6.16 (std::vector) will sure come in handy for you. Those are the c++ ways of dealing with arrays.

      • J

        Yea that's what I use but was just wondering if a ref to a array would work also. But I agree std::array and std::vector functions are the way to go thanks for the response

        • Alex

          Yes, you can use a reference to an array and it will keep the type information (from which the size can be derived).

          • david

            Alex can you give an example of that please?
            and how to pass an array to a function by reference?

            • nascardriver

              Hi david!

  • Wunna

    I have a question, but I don't know how to put it.
    To put it simply,how can we do this?

    It prints &x and &ref the same address but &ptr is not.
    Does it means ptr itself was assigned to a memory location?
    By my code, int x strictly means we request a location for the variable x which will be assigned later.
    Which also goes the same for &ref,but since ref itself is the referenced variable itself, it cannot be assigned to a new rvalue, so int &ref = 5 will give compiler error.
    In this case initialization does something like "we want a location or variable to be assigned(or not) to that rvalue(for variable) or lvalue(for an address)"?
    In other words,when we do "int &ref = x;" we are actually assigning &ref to an integer. since & is an operator which is used to get the address of a declared variable, ref have to be a variable itself due to the nature of & operator?

    • Alex

      x, ptr, and ref all occupy discrete locations in memory (though in actuality, the reference may be optimized away). References are usually implemented using pointers, but when you say &pointer, you get the address of the pointer itself, whereas &reference gives you the address of the thing the reference is referencing, not the reference itself.

      When you do int &ref = x, you're telling the compiler that identifier ref is an alias for x. &ref will always give you the address of x, not ref. It's up to the compiler whether to allocate memory for ref or not.

      • Wunna

        But when I did int &ref = *ptr; it wasn't compile error, it means *ptr was treated as l-value?
        Here I thought *(expression) and &(expression) will give you the r-value.

        • Alex

          Yes, *ptr is treated as an l-value. *ptr is a region of storage that can be manipulated, so it qualifies as an l-value.
          &expression (used in the address-of context) is an r-value, because it is returning a value (an address).

  • umang

    can a reference variable be initialized with another reference variable?

    • Alex

      Yes. The reference variable on the right is treated as if it were the thing it's referencing.

      • John

        I think this example should be put in the main text. It's very important but rarely mentioned anywhere. In practice initialising a ref from another ref is often used in code like this

        • Alex

          I believe this topic is covered in chapter 7, as there is deeper discussion about how function parameters and return values intersect with pointers, references, and values. I'd like to bring these topics a bit closer together though, so I work through the next round of rewrites I'll keep this in mind.

          • John

            It's definitely covered later but I think it would be good to put your example of initializing a reference with another reference right on this page, ie.

            This is a fundamental property of a reference which I think should be mentioned before discussing the use of references in function call/return. Most textbooks and websites don't mention this property resulting in a lot of confusion. Some people think the above code is illegal or believe it's creating a reference to a reference. Here's an example question on Stack Overflow
            https://stackoverflow.com/questions/18473496/pass-a-reference-to-a-reference (BTW I think the code in that link would be a useful example in Chapter 7)

  • nikos-13

    If using the address-of operator on a reference returns the address of the value being referenced, then there is no way we can get the actual address of a reference?

      • Peter Baum

        I do not understand this response.  I believe you can get the actual address of a reference by using &.  Here is an example:

        which then outputs something like

        value = 4   alias=4    &alias=001AFB84    &value=001AFB84

        • nascardriver

          Compare the addresses, they're the same. The operator& applied to a reference returns the address of the referenced variable.

          • Peter Baum

            Yes... which is exactly what my comment intended.  My comment was in response to the statement by nikos-13, which was "...that then there is no way we can get the actual address of a reference...".  Alex agreed and I disagreed.

            I am going to think some more about the * and & operators because enough people have gotten confused about them for me to think that there is some fundamental problem that is causing difficulties.

            • Alex

              Taking the address of a reference gives you the address of the object being referenced.

              So when you take the address of the reference, you aren't actually getting the address of the reference itself. FWIW, references are typically implemented as pointers -- they aren't just an alias (although they appear to be when using them). There's no way to get the address of that pointer.

              See this thread for more context.

              • Peter Baum

                Hi Alex,

                I didn't find anything in the lesson that led me to think of the alias as being anything other than an alias, in other words, a convenience for the programmer but treated as just another name for the target by the compiler.  So perhaps it would be helpful if the lesson (or a later lesson) explains why "they aren't just an alias."

                The thread you supplied was great... thank you.  I also found it interesting that different compilers treat this differently.  

                In general, one thing that would help me in these lessons would be more talk about what the compiler is actually doing.  Also helpful would be more information about the evolution of the language.  In particular, some of the extensions in later versions SEEM to make unnecessary clutter, make it harder to learn and understand, and will limit the ability to make necessary extensions in the future.  I'm sure at least some of these additions are justified, but more information about them would be helpful.

                You might also want to consider moving this alias capability to later in the lesson sequence.

                • Alex

                  Thanks for the feedback. It's often not useful to talk about what the compiler is doing, because different compilers may be doing different things. I also try not to get too into the weeds unless it's to help inform a side-conversation such as this one. If you want to know more information, you can almost always find it on a reference site, or via a google search anyway.

                  Why do you think the aliases section should be moved? I think it's useful to talk about what references do in their most primitive form before getting into the more advanced cases...

                • Peter Baum

                  Hmmm... no reply button on your last post Alex, so I'll do it this way.  I suggested the move of the aliases section because at the point where it was introduced it didn't seem to have a very useful purpose.  

                  I have a hard time keeping arbitrary rules or multiple ways of doing the same thing in my head.  If I have a task and an appropriate tool, then it is much easier for me to remember.  It may just be me, but perhaps others feel the same way.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]