Search

7.4 — Passing arguments by address

// The first half of this lesson is now in https://test.learncpp.com/cpp-tutorial/7-11-pass-by-pointer-and-return-by-pointer/

Addresses are actually passed by value

When you pass a pointer to a function, the pointer’s value (the address it points to) is copied from the argument to the function’s parameter. In other words, it’s passed by value! If you change the function parameter’s value, you are only changing a copy. Consequently, the original pointer argument will not be changed.

Here’s a sample program that illustrates this.

tempPtr receives a copy of the address that ptr is holding. Even though we change tempPtr to point at something else (nullptr), this does not change the value that ptr points to. Consequently, this program prints:

55

Note that even though the address itself is passed by value, you can still dereference that address to change the argument’s value. This is a common point of confusion, so let’s clarify:

  • When passing an argument by address, the function parameter variable receives a copy of the address from the argument. At this point, the function parameter and the argument both point to the same value.
  • If the function parameter is then dereferenced to change the value being pointed to, that will impact the value the argument is pointing to, since both the function parameter and argument are pointing to the same value!
  • If the function parameter is assigned a different address, that will not impact the argument, since the function parameter is a copy, and changing the copy won’t impact the original. After changing the function parameter’s address, the function parameter and argument will point to different values, so dereferencing the parameter and changing the value will no longer affect the value pointed to by the argument.

The following program illustrates the point:

This prints:

56

Passing addresses by reference

The next logical question is, “What if we want to change the address an argument points to from within the function?”. Turns out, this is surprisingly easy. You can simply pass the address by reference. The syntax for doing a reference to a pointer is a little strange (and easy to get backwards). However, if you do get it backwards, the compiler will give you an error.

The following program illustrates using a reference to a pointer:

When we run the program again with this version of the function, we get:

5 ptr is null

Which shows that calling setToNull() did indeed change the value of ptr from &five to nullptr!

There is only pass by value

Now that you understand the basic differences between passing by reference, address, and value, let’s get reductionist for a moment. :)

In the lesson on references, we briefly mentioned that references are typically implemented by the compiler as pointers. This means that behind the scenes, pass by reference is essentially just a pass by address (with access to the reference doing an implicit dereference).

And just above, we showed that pass by address is actually just passing an address by value!

Therefore, we can conclude that C++ really passes everything by value! The properties of pass by address (and reference) come solely from the fact that we can dereference the passed address to change the argument, which we can not do with a normal value parameter!

Pass by address makes modifiable parameters explicit

Consider the following example:

It’s not obvious from the call to foo2() that the function can modify variable i, is it?

For this reason, some guides recommend passing all modifiable arguments by address, so that it’s more obvious from an existing function call that an argument could be modified.

However, this comes with its own set of downsides: the caller might think they can pass in nullptr when they aren’t supposed to, and you now have to rigorously check for null pointers.

We lean towards the recommendation of passing non-optional modifiable parameters by reference. Even better, avoid modifiable parameters altogether.

Pros and cons of pass by address

Advantages of passing by address:

  • Pass by address allows a function to change the value of the argument, which is sometimes useful. Otherwise, const can be used to guarantee the function won’t change the argument. (However, if you want to do this with a non-pointer, you should use pass by reference instead).
  • Because a copy of the argument is not made, it is fast, even when used with large structs or classes.
  • We can return multiple values from a function via out parameters.

Disadvantages of passing by address:

  • Because literals (excepting C-style string literals) and expressions do not have addresses, pointer arguments must be normal variables.
  • All values must be checked to see whether they are null. Trying to dereference a null value will result in a crash. It is easy to forget to do this.
  • Because dereferencing a pointer is slower than accessing a value directly, accessing arguments passed by address is slower than accessing arguments passed by value.

When to use pass by address:

  • When passing built-in arrays (if you’re okay with the fact that they’ll decay into a pointer).
  • When passing a pointer and nullptr is a valid argument logically.

When not to use pass by address:

  • When passing a pointer and nullptr is not a valid argument logically (use pass by reference).
  • When passing structs or classes (use pass by reference).
  • When passing fundamental types (use pass by value).

As you can see, pass by address and pass by reference have almost identical advantages and disadvantages. Because pass by reference is generally safer than pass by address, pass by reference should be preferred in most cases.

Rule

Prefer pass by reference to pass by address whenever applicable.

%Missing lookup for lesson id 111%

68 comments to 7.4 — Passing arguments by address

  • Ben

    Post for nothing. I found the answer before the 5 minutes.

    Thanks anyway.

  • weirdolino

    I'm having trouble understanding the conclusions. Under advantages you say
    Because a copy of the argument is not made, it is fast, even when used with large structs or classes.
    and under disadvantages you say
    Because dereferencing a pointer is slower than accessing a value directly, accessing arguments passed by address is slower than accessing arguments passed by value.

    Is passing by reference/address slower or faster than by value? Does it depend on the size of the data passed to the function?

    • When we talk about speed and efficiency with regards to variables, we're talking about two separate things:

      1) The speed of creating the variable and initializing it's values. This cost is paid once when the variable is created, and can be significant for large classes that are copied by value. This is why large classes are better passed by pointer or reference than by value, especially if a function is called many times.

      2) The speed of accessing the value stored in the variable. This cost is paid each time the variable is accessed. Although accessing pointers and references is slower than accessing normal variables because of the dereference that takes place, in practice this is rarely a large concern unless you have some kind of loop that accesses the variable hundreds or thousands of times.

  • Pieter

    I can't get your last code (with the reference to a pointer) to work.
    The linker gives the following error :
    1>main.obj : error LNK2019: unresolved external symbol "void __cdecl SetToSix(int *)" (?SetToSix@@YAXPAH@Z) referenced in function _main
    1>D:\Users\Pieter\Documents\Visual Studio 2008\Projects\Test\Debug\Test.exe : fatal error LNK1120: 1 unresolved externals

    However, I was thinking...
    If you change pTempPtr = &nSix; to *pTempPtr = nSix; (so dereference it and then assign the value of nSix) it works as it should, except that it doesn't change the address of pTempPtr, but rather the contents.

  • el-mudo

    Hi Alex, it's me again
    There is a small error in the last example in this page, you forgot to change the comments in the code, it's writen like that:

    // This only changes pTempPtr, not pPtr!

    but, in fact, for that example, pPtr has its memory address changed together with pTempPtr since it's a reference to it.

  • Tom

    1. In your first example function, PrintArray, don't you have to dereference the pnArray variable, like this??:

    
    void PrintArray(int *pnArray, int nLength)
    {
        for (int iii=0; iii < nLength; iii++)
            cout << *pnArray[iii] << endl;
    }
    

    2. Minor typo: "The next logical question is, “What if we want to be able to be able to change the address"

  • Jason

    Awesome, I get it.

    I'd just like to say, that all made me realize that communicating C++'s logic is an artform.

  • I beefed up this lesson significantly because some of the stuff mentioned in the comments here is pretty important.

  • Jason

    Ok, the code below does act as your code does. So, what's up with my:
    char *array = "strings" code above?

    void PrintArray(char *pnArray)   
    {   
        pnArray[0] = 'c';
              
    }  
     
    int main()   
    {   
      char anArray[2] = "a"; 
      cout << anArray[0] << endl;
      PrintArray(anArray);
      cout << anArray[0] << endl;
    } 
    Output:
    a
    c 
    

    [ I think I answered this in my response above. Let me know if it isn't clear. -Alex ]

  • Jason

    I think I get it. Passing by value is slow because it does a copy. Passing by reference or address is basically the same thing, just different syntax.

    [ Correct. I've read that if you're passing simple built-in variables by value, it can be as fast or faster than passing by reference or address. Generally when we talk about passing by reference or address being faster, we're talking about for compound types (eg. structs and classes). -Alex ]

    ----------------------------------------------------
    Comparing these two programs:

    void foo(int *pValue)   
    {   
        *pValue = 6;   
    }   
      
    int main()   
    {   
        int nValue = 5;   
      
        cout << "nValue = " << nValue << endl;   
        foo(&nValue);   
        cout << "nValue = " << nValue << endl;   
        return 0;   
    } 
    
    Ouput:
    nValue = 5
    nValue = 6
    
    void func(char *address)
    {
        address = "b";
    }
    
    int main()
    {
        char *array = "a";
        cout << "array = " << array << endl;
        func(array);
        cout << "array = " << array << endl;
    }
    
    Output:
    array = a
    array = a
    

    Why doesn't the second example act as the first example. As the output would be:

    array = a
    array = b
    

    What I've noticed is when passing an array by address it changes the address that the pointer points to when inside the function. This causes the original value to never be changed. I guess that's just how it works.

    [ Good question and insight. As it turns out, pointer parameters are actually passed by value. Consequently, if you try to change what a pointer points to inside a function, it's the same as changing a variable locally -- as soon as you leave the function, it will revert back to what it was. However, if you dereference the pointer and change the value of what it points to, that won't be reverted. If you actually want to be able to change the address that a pointer points to inside a function, the best way to do this is to pass the pointer itself by reference:

    void func(char *&address)

    Incidentally, this line of code is dangerous:

    address = "b"

    This is setting the address of the "address" variable to the address of "b". What is the address of "b"? "b" isn't a variable, so this doesn't really make any sense. It probably works in this case due to the way the compiler is dealing with string literals, but I certainly wouldn't want to trust it. -Alex ]

    Any insight on this issue? Btw, I know that char *array is not an array but a pointer, but once again it really is an array when used with strings right? Just different syntax right?

    [ In C , arrays and pointers are pretty much identical. When you declare an array, all you're doing is setting a pointer to the beginning of your allocated memory. If I'm reading your above statement correctly, your understanding is correct. This means you can do something like this:

    char buf[] = "Hello, World"; // create a static array
    char *ptr = buf; // set a pointer to that array
    cout << ptr[4]; // use the pointer like an array
    

    and it will print 'o'. -Alex ]

    Sorry for the double post, but the input box doesn't work right almost everytime I use it. Most of the time it eats my pre tags too. I then have to edit my post to get it working. Just a heads up on that.

    [ I haven't had any problems using the pre tags from the input box. I have had visual problems after editing my posts where the pre tags don't render correctly, forcing me to refresh the page. I'd really like to move to a threaded comments model so I don't have to respond to comments like this, but there doesn't seem to be any good plugins for wordpress to do so. I might have to spend the time to write my own. -Alex ]

  • Jason

    Disadvantages of passing by address:

    Because dereferencing a pointer is slower than accessing a value directly, accessing arguments passed by address is slower than accessing arguments passed by value.

    Could that sentence possibly supposed to read as follows:

    Because dereferencing a pointer is slower than accessing a value directly, accessing arguments passed by address is slower than accessing arguments passed by reference.

    I would think (am assuming) that arguments passed by value would take the longest because they have to be copied.

    [ It turns out that references are usually implemented by the compiler (under the hood) using pointers. Consequently, references aren't any faster than addresses. They just have a nicer syntax, and are safer to use. -Alex ]

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">