Search

7.3 — Passing arguments by reference

While pass by value is suitable in many cases, it has a couple of limitations. First, when passing a large struct or class to a function, pass by value will make a copy of the argument into the function parameter. In many cases, this is a needless performance hit, as the original argument would have sufficed. Second, when passing arguments by value, the only way to return a value back to the caller is via the function’s return value. While this is often suitable, there are cases where it would be more clear and efficient to have the function modify the argument passed in. Pass by reference solves both of these issues.

Pass by reference

To pass a variable by reference, we simply declare the function parameters as references rather than as normal variables:

When the function is called, y will become a reference to the argument. Since a reference to a variable is treated exactly the same as the variable itself, any changes made to the reference are passed through to the argument!

The following example shows this in action:

This program is the same as the one we used for the pass by value example, except foo’s parameter is now a reference instead of a normal variable. When we call foo(x), y becomes a reference to x. This snippet produces the output:

value = 5
value = 6

As you can see, the function changed the value of the argument from 5 to 6!

Here’s another example:

This produces the output:

x = 5
x = 6

Note that the value of argument x was changed by the function.

Returning multiple values via out parameters

Sometimes we need a function to return multiple values. However, functions can only have one return value. One way to return multiple values is using reference parameters:

This function takes one parameter (by value) as input, and “returns” two parameters (by reference) as output. Parameters that are only used for returning values back to the caller are called out parameters. We’ve named these out parameters with the suffix “out” to denote that they’re out parameters. This helps remind the caller that the initial value passed to these parameters doesn’t matter, and that we should expect them to be rewritten.

Let’s explore how this works in more detail. First, the main function creates local variables sin and cos. Those are passed into function getSinCos() by reference (rather than by value). This means function getSinCos() has access to the actual sin and cos variables, not just copies. getSinCos() accordingly assigns new values to sin and cos (through references sinOut and cosOut respectively), which overwrites the old values in sin and cos. Main then prints these updated values.

If sin and cos had been passed by value instead of reference, getSinCos() would have changed copies of sin and cos, leading to any changes being discarded at the end of the function. But because sin and cos were passed by reference, any changes made to sin or cos (through the references) are persisted beyond the function. We can therefore use this mechanism to return values back to the caller.

This method, while functional, has a few minor downsides. First, the syntax is a bit unnatural, with both the input and output parameters being put together in the function call. Second, the caller must pass in variables to hold the updated values, which means it must have (or create) parameters to hold these output values even if it doesn’t intend to use them. Finally, it’s not obvious from the caller’s end that sin and cos are out parameters and will be changed. This is probably the most dangerous part of this method (as it can lead to mistakes being made). Some programmers and companies feel this is a big enough problem to advise not passing by reference this way, and using pass by address instead when mixing in and out parameters (which has a clearer syntax indicating whether a parameter is modifiable or not).

Personally, we recommend avoiding mixing input and output parameters for this reason, but if you do so, good documentation on the caller’s side can help.

Limitations of pass by reference

Non-const references can only reference non-const l-values (e.g. non-const variables), so a reference parameter cannot accept an argument that is a const l-value or an r-value (e.g. literals and the results of expressions).

Pass by const reference

As mentioned in the introduction, one of the major disadvantages of pass by value is that all arguments passed by value are copied into the function parameters. When the arguments are large structs or classes, this can take a lot of time. References provide a way to avoid this penalty. When an argument is passed by reference, a reference is created to the actual argument (which takes minimal time) and no copying of values takes place. This allows us to pass large structs and classes with a minimum performance penalty.

However, this also opens us up to potential trouble. References allow the function to change the value of the argument, which is undesirable when we want an argument be read-only. If we know that a function should not change the value of an argument, but don’t want to pass by value, the best solution is to pass by const reference.

You already know that a const reference is a reference that does not allow the variable being referenced to be changed through the reference. Consequently, if we use a const reference as a parameter, we guarantee to the caller that the function will not change the argument!

The following function will produce a compiler error:

Using const is useful for several reasons:

  • It enlists the compilers help in ensuring values that shouldn’t be changed aren’t changed (the compiler will throw an error if you try, like in the above example).
  • It tells the programmer that the function won’t change the value of the argument. This can help with debugging.
  • You can’t pass a const argument to a non-const reference parameter. Using const parameters ensures you can pass both non-const and const arguments to the function.
  • Const references can accept any type of argument, including l-values, const l-values, and r-values.

Rule: When passing an argument by reference, always use a const references unless you need to change the value of the argument

Pros and cons of pass by reference

Advantages of passing by reference:

  • References allow a function to change the value of the argument, which is sometimes useful. Otherwise, const references can be used to guarantee the function won’t change the argument.
  • Because a copy of the argument is not made, pass by reference is fast, even when used with large structs or classes.
  • References can be used to return multiple values from a function (via out parameters).
  • References must be initialized, so there’s no worry about null values.

Disadvantages of passing by reference:

  • Because a non-const reference cannot be made to an const l-value or an r-value (e.g. a literal or an expression), reference arguments must be normal variables.
  • It can be hard to tell whether a parameter passed by non-const reference is meant to be input, output, or both. Judicious use of const and a naming suffix for out variables can help.
  • It’s impossible to tell from the function call whether the argument may change. An argument passed by value and passed by reference looks the same. We can only tell whether an argument is passed by value or reference by looking at the function declaration. This can lead to situations where the programmer does not realize a function will change the value of the argument.

When to use pass by reference:

  • When passing structs or classes (use const if read-only).
  • When you need the function to modify an argument.

When not to use pass by reference:

  • When passing fundamental types (use pass by value).
  • When passing built-in arrays (use pass by address).
7.4 -- Passing arguments by address
Index
7.2 -- Passing arguments by value

58 comments to 7.3 — Passing arguments by reference

  • nikos-13

    "Some programmers and companies feel this is a big enough program to advise not passing by reference this way".
    What do you mean with "this is a big enough program"?

    • Alex

      Because passing arguments by reference doesn’t use any special syntax, the programmer may not realize from looking at the function call that the function will change the value of the arguments. If they then use the argument for something else, they may expect it to have one value when it will have another value. This can lead to bugs and defects in the code.

  • Sivasankar

    Hi Alex,

    Thanks again for your valuable tutorial.

    I have few doubts in this lesson.
    First, regarding the code snippet in "Returning multiple values via out parameters" section, you declared pi as below

    Is static keyword recommended here?

    Second, in "Pros and const of pass by reference" section, it is mentioned that caller should check the function definition which is mentioned as 1 of the disadvantage. I hope the caller can have the option to check the function declaration which may be handy in modern compilers.

    And just to mention, there is minor typo in the heading "Pros and const of pass by reference".

    • Alex

      It probably doesn’t matter either way. I think I may have added that so that pi doesn’t get reinitialized every time the function executes. But most likely the compiler would optimize the pi variable away anyway and just use the double literal, so it probably has no impact.

      I’ve gone ahead and removed it.

      Second, the caller should have the ability to check the function declaration (as all IDEs should have this capability), but it’s still easily overlooked.

      Typo fixed. Thanks!

  • I’ve usually been a individual who likes to concentrate on proper
    technique so I do like difficult function when it
    comes to classes.

  • Spazzer

    Kindly explain this thoroughly:

    Thank you

    • Alex

      Putting the trig aside, the main function creates local variables sin and cos. Those are passed into function getSinCos() by reference (rather than by value). This means function getSinCos() has access to the _actual_ sin and cos variables, not just copies. getSinCos() accordingly assigns new values to sin and cos, which main then prints.

  • Prajwal

    While passing arguments by reference,as you said we simply declare function parameters as reference rather than just variables.
    But during the function call you’ve called just the variable but not the reference variable.
    My question is whether during the function call is calling the reference variable legit ?
    As you’ve mentioned "Since a reference to a variable is treated exactly the same as the variable itself" ,I’m a bit unclear bout this.
    Ex: void randomfunction(float &x)
    {
    .
    .
    .
    }

    int main()
    {
    randomfunction(&x);      //Well,My doubt is that,is this line legit ?
    .
    .
    .
    return 0;
    }

    • Alex

      > My question is whether during the function call is calling the reference variable legit ?

      No. The & symbol only means “reference” in the context of a variable declaration. In all other cases, & means “address-of”. So in your example, the function parameter is a reference (because you’re declaring a type), but the argument of the randomfunction() function call would be the “address-of x”.

      You see something similar in cases like this:

      So, the takeaway is, when passing arguments, you will most often pass the variable as-is. The only exceptions are when you need to dereference a pointer (to pass a value instead of an address) or get the address of a value (to pass an address instead of a value).

  • mohit

    Amazing tutorial bro, thanks!

  • Vlad

    Hello Alex

    I see you #included <math.h>, but there is <cmath>, too. I suppose it’s fairly obvious, but since I’ve also seen (so far) <cstdlib> and <ctime>, and that using headers with the .h extension is rather outdated, I have to ask: is <cmath> for C and <math.h> for C++? Or is <math.h> an outdated header that has other functions?

  • Eric

    In the getSinCos example :

    should be :

  • Shiva

    Typo:

    > To pass a variable by reference, we simply declare the function parameters are (as) references rather than as normal variables

  • Supun

    Great article. I was looking exactly for this kind of article. Many thanks!

  • Rob G.

    So it would be good form to create static variables in every function where possible in addition to passing values as reference? It sounds like these two habits might speed things up a bit.

    • Alex

      I usually only do it if I’m initializing a constant that is used in the function. In all likelihood, the compiler will optimize the constant away regardless of whether it’s static or not, at least for fundamental types.

  • Rob G.

    Hey Alex, a little review on my part. 7 seems very straightforward but here:

    I understand wanting to have pi declared as read-only, so what does static do here?

  • Ran

    Hi Alex:

    You mentioned that it is not good to pass array by using reference. In Chapter 6 comprehensive quiz 6e, the code indicates that it uses reference to pass array. Could you please explain it ?

    Thanks,

    • Alex

      I’ve altered the wording. Passing arrays by reference (as a concept) in general is fine.

      Because std::array is a normal non-fundamental type variable, we pass it using a reference (to prevent making a copy), even though it contains an array.

  • UDAY KANTH REDDY

    Your tutorial is great!! 😀

  • Pete

    Hey Alex!
    Great explanations!
    Especially enjoying the sin & cos example.
    Maybe you want to add a cin user input there, store it into a double degrees variable, then pass it via value to getSinCos and edit it into the final cout print to make it a little more practical.

    Anyways thanks a lot for these great tutorials!

  • Sebastien

    Dear Alex,

    I think there’s a typo in the second code snippet:

    Should be

    Cheers!

  • Luiz Vitor

    Are the printed variables dSin/dCos or sin/cos? I think the name of the variables are wrong, because cos(0.0), sin(0.0) are evaluating math functions.

  • Josh

    At the very end of the lesson you put:
    "When not to use pass by value:"

    I think it should be:
    "When not to use pass by reference:"

  • soumya

    It tells the coder whether they need to worry about the function changing the value of the argument
    It helps the coder debug incorrect values by telling the coder whether the function might be at fault or not.

    Hey Alex ! Could you please clarify above two statements . I am unable to understand them. 🙁

    • soumya

      And one thing more..

      What does the following statement mean ?

      const int &a = 5 ;

      What does it do ? And in what way is it useful ? Why do we create  reference to a literal ?

      -----
      "Yes, that’s illegal, because you can’t bind a literal to a non-const reference.

      You can, however, bind a literal to a const reference. If you made &rVal const, the above program would compile."

      What is meant by the above lines ?

      Kindly reply to both of my posts above.
      I shall be highly obliged ! 🙂

      • Alex

        It means we’re creating a const reference to a literal value. What actually happens is the compiler puts the literal 5 in a temporary variable, and sets the reference to that. In reality, you’d never do something like this, but it is useful in a different form:

        Let’s say you wanted to print the value 5 using this function. Because you can set a const reference to a literal, you can call this as printValue(5), and it will work. Otherwise you’d have to create a temporary variable yourself, initialize it with the value 5, and pass it to function printValue().

        You can only assign literals to const references. It doesn’t work with non-const references.

    • Alex

      If a function reference parameter is non-const, you don’t know whether the function will change the argument passed in. If it’s const, you know the function will not change the argument.

      Consequently, if you’re getting the wrong value for something, you know that a function that takes only const reference parameters isn’t going to be at fault, because it can’t change the argument values. So you should look elsewhere.

  • Shivam Tripathi

    Alex…one confusion…in the chapter 6.11 (References) us said that:

    is illegal…and i understood that…
    but when we call a function and pass a literal constt. say 6 (for eg.), hw can it be assigned to a reference variable in a function parameter…consider this:

    Isn’t that illegal coz u can’t assign a literal to a non-const reference…all u hv to do is to declare a const reference then in the function parameter….plz help me..

    • Alex

      Yes, that’s illegal, because you can’t bind a literal to a non-const reference.

      You can, however, bind a literal to a const reference. If you made &rVal const, the above program would compile.

  • Todd

    Typos.

    "One way to allow functions to modify the value of (an) argument is by using pass by reference."

    In the following lines of code, "GetSinCos" should be "getSinCos" by naming conventions

  • Mike

    Hey man,

    great post dude. I never understood these things ’till I read this. Thanks man 😀

  • Gareth37

    Alex i’m having trouble grasping this code, i put it in visual studio and then thought how do i call the GetSinCos function from main ? i cant pass it the arguments because i dont know what &dSin and &dCos are and it wont let me call it with one argument of dX and it also fails to compile if i enter some spurious values for &dSin and &dCos hoping they would get overwritten by the dSin = sin(dx) calculation.
    i realise i am probably doing something fundamentally wrong (i feel a bit like a monkey trying to build a rocketship but i’m having a go anyway) i have this -

    its all wrong but why ?

    • I think what you are missing here is that dSin and dCos are meant to be “out” parameters -- that is, their values are being calculated by the GetSinCos() function and returned to the caller.

      Note that both dSin and dCos are non-const REFERENCE parameters. This means you MUST pass them a variable that has an address. So you should use the following code:

      I will update the example to make this clearer.

    • sujitbista

      The value assigned in dsin and dcos is returned to reference &dsin and &dcos which in turn passes the value to the calling fuction in the main program. So the code goes like this

      void main()
      {
      GetSincos(30.0,dSin,dCos);
      cout<<"The value of dSin is"<<dSin<<endl;
      cout<<"The value of dcos is"<<dCos;
      }

  • afds

    excellent tutorials

  • javid

    Superub website to learn c++…great going alex….

  • surya

    hai alex.fantastic explanation. n

  • parveen thakran

    This example is very good i learn many things by this example thnax …this site helps a lot

  • PRABHAKAR

    dear ALEX, i have already sent u my problem but i am herewith giving u the code i cant understand.
    std::istream& operator>>( std::istream& input, Complex& operand2 )
    {

    }

    the return type as well as the two parameters are references. how can u explain for providing ” return type as a reference” i am stuck up thanks prabhakar

    • Returning a value by reference works just like passing a value by reference (except the data is moving from the function to the caller instead of the other way around).

      When a value is returned by reference, a reference to the value is returned rather than the value itself. This is most typically done with classes and structs, because returning a reference to a class or struct is fast, whereas making a copy of the class or struct is slow.

  • PRABHAKAR

    dear ALEX, i am a novice to COMPUTERS.(born in 1938)but want to enjoy learning c++. i have tried to understand ” We can pass by const reference to avoid unintentional changes.” but nowhere i find an explanations re.” constant return references”. if i have missed this from your tutorial, please refer it to me. OR enlighten me re. “returning constant references with necessary explanations’ please bear with my ignorance. thanks. i am enjoying your tutorials prabhakar

    • Prabhakar, I suggest you review the lesson on references to get a better understanding of what references are.

      A const reference will not let you change the value it references. Consequently, when we pass an variable by const reference, we are not allowed to change the value of the variable within the function.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter