Search

7.4 — Passing arguments by address

There is one more way to pass variables to functions, and that is by address. Passing an argument by address involves passing the address of the argument variable rather than the argument variable itself. Because the argument is an address, the function parameter must be a pointer. The function can then dereference the pointer to access or change the value being pointed to.

Here is an example of a function that takes a parameter passed by address:

The above snippet prints:

value = 5
value = 6

As you can see, the function foo() changed the value of the argument (variable value) through pointer parameter ptr.

Pass by address is typically used with pointers, which most often are used to point to built-in arrays. For example, the following function will print all the values in an array:

Here is an example program that calls this function:

This program prints the following:

6 5 4 3 2 1

Remember that fixed arrays decay into pointers when passed to a function, so we have to pass the length as a separate parameter.

It is always a good idea to ensure parameters passed by address are not null pointers before dereferencing them. Dereferencing a null pointer will typically cause the program to crash. Here is our printArray() function with a null pointer check:

Passing by const address

Because printArray() doesn’t modify any of its arguments, it’s good form to make the array parameter const:

This allows us to tell at a glance that printArray() won’t modify the array argument passed in, and will ensure we don’t do so by accident.

Addresses are actually passed by value

When you pass a pointer to a function by address, 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) comes 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!

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 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 and dereference the pointer argument).
  • 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.

7.4a -- Returning values by value, reference, and address
Index
7.3 -- Passing arguments by reference

124 comments to 7.4 — Passing arguments by address

  • Hardik

    Alex?
    Why does this work ?

    In this, after execution of foo(), the variable ‘x’ and the local pointer ‘ptr’ is destroyed, so should dereferencing the pointer ‘tmp1’ not show undefined behaviour ? (The Same Question Goes For References !)

    • Hardik

      I think this is because as return by address is actually return by value only, so the pointer is not being passed rather it’s value is being copied into tmp1. Tell me if I’m right Alex !
      But, What about references, they can be used as l-values, so they can’t be returned by value, otherwise this would be illegal (foo() = /* something */), YES?
      Thanks in Advance, Alex 🙂

      • Alex

        Yes, the value of ptr (which is the address of variable x) is copied from foo back to main (and then assigned to tmp1). Then foo’s x is destroyed. So when you dereference *tmp1, you’re dereferencing an address that’s no longer valid -- which will cause undefined behavior. It might still print 6 -- after all, one of the possible outcomes of undefined behavior is that you get the expected answer anyway.

        I’m not sure what you’re asking about references. If you wrote the same code using references instead of pointers you’d get the same result, because you’d be referencing a variable that no longer exists (at an invalid address).

        • Hardik

          Man ! But This thing Works Everytime !
          But, yes, Returning &x causes undefined behaviour !
          And, yes for references, if i create a reference to a local variable say, int &ref = x, and, then if i return ref, it works, but if i return x, it causes again undefined behaviour.

          • Alex

            It works because the value you’re pointing at or referencing hasn’t been overwritten by anything yet. If you were to call another function between getting the return value and printing it, it probably wouldn’t work any more.

            • Hardik

              I also thought this and even, tried this but nothing changed !
              The crashing is shown only if I return &x, and, on the other hand, if i return a pointer to x(ie. int *ptr = &x; return ptr;), it works perfectly!

  • AMG

    Alex,
    Came across of "C++ tr1’s shared_ptr<> is a smart pointer that helps in these kinds of situations - it manages this ownership concept by keeping a reference count that tracks the number of references to the data. If the reference count is 1, then there is 1 clear owner. If the reference count is larger than 1, then ownership is shared. If the reference count is 0, then there are no more references to the data, and shared_ptr<> will delete it when the shared_ptr<> destructor is called." Wondering if you will talk about it. Thanks.

  • AMG

    Alex,
    Slightly modified your example from "Addresses are actually passed by value". Seems pass dynamically allocated array to function and delete array inside the function is not a good idea. nullptr does not work, so delete[]. Is it correct. Please share your thoughts.

    • Alex

      You should use delete if tempPtr is a pointer to a single object, and delete[] if tempPtr is a pointer to a dynamically allocated array.

      • AMG

        Yes, I got it, but nor sure if delete[] works in above example (int *tempPtr is a pointer to dynamically allocated array). As you said, tmpPntr = nullptr won’t change tmpPtr outside setToNull(). Because of that, I think delete[] doesn’t work. Thanks.

      • AMG

        Alex, never mind. I wrote code and understood. Found answers for my questions. Frankly speaking, I should have written a code before ask you for help. Sorry about that.

  • nikos-13

    When nullptr is a valid argument?

    • Alex

      A nullptr is a valid argument for any pointer parameter.

      • nikos-13

        Oh, so what do you mean with "When passing pointer values and nullptr is not a valid argument"? Can you give me an example?

        • Alex

          nullptr is always a valid argument for a pointer parameter from a syntax perspective, but it may not be from a logical perspective. For example, consider the following function:

          Although you could pass a nullptr for the array parameter, logically it doesn’t make any sense to, and it will cause a null dereference, which will crash your program. This is what I mean by “not a valid argument”.

          Now, when passing a fixed array, it still makes sense to pass it by address, so this is really pertaining to pointers to non-array values.

  • nikos-13

    "In the lesson on passing arguments by reference, we briefly mentioned that references are typically implemented by the compiler as pointers."

    We mentioned that at lesson 6.11 -- Referende variables ( http://www.learncpp.com/cpp-tutorial/611-references/ ), not on the previous lesson.

  • justin

    At the beginning you say "Pass by address is typically used with pointers, which most often are used to point to built-in arrays or dynamically allocated variables." but dynamically allocated variables are assigned to pointers them selves, were passing an address by reference would be the right way to do that I would think. Or maybe by that you were saying that pass by address is often used as a method to assign a pointer a dynamically allocated variable by passing the memory request (new int) as an argument to a pointer declared as a parameter in a function deceleration like this

    • justin

      sorry meant to say "void foo(*ptr)" not "void foo(*point)".

    • justin

      My bad I reread it and understand it now. you can delete my question if you wish. Its not reverent.

    • Alex

      It depends on whether nullptr is a valid option or not. If nullptr is a valid argument to your function, then pass by address is better than pass by reference. If nullptr is not a valid argument to your function, then I agree pass by reference is safer.

  • Mr C++

    Don’t You Think It should be like this because when i do this it prints nothing(returns void) which was intended…And, When i do it through your prog. it prints 0 0 0 0 0 0(which i think would not be intended by the programmer)….as we passed a null array so it should return nothing rather than printing 0 0 0 0 0…..??

    Tell Me If I’m Right !!

    • Alex

      Your array isn’t a null array, it’s a 6-element array with each element initialized to 0.

      You clearly didn’t test this code. Try passing in a nullptr (or 0) for the array parameter and see what happens.

      • Mr C++

        Oh ! I thought U were talking about what if all elements of array are set to NULL then what happens so in that case my program works right and returns nothing instead of printing 0’s but Yes, I Know Dereferencing a nullptr causes undefined behaviour….

  • Drew

    Hi Alex!

    I believe I understand the point you’re making here, but I think it’s a bit confusing:

    "Therefore, we can conclude that C++ really passes everything by value! The properties of pass by address (and reference) comes 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!"

    But, you’ve mentioned numerous times up to this point that passing large classes/structs by value can be very expensive because it copies those variables into the function parameters every time you call the function. So in fact there is a difference between passing by value and passing by reference, right?

    • Alex

      Yes, there is a difference!

      In the case of pass by value, the entire argument is copied into the parameter, which can be slow if the argument is large.
      In the case of pass by address, the address that the argument pointer is holding is copied. This is fast because an address is only 32 or 64 bits.
      In the case of pass by reference, the reference is passed (which is usually an address as well). This is also fast.

      So regardless of whether you pass by value, address, or reference, C++ is copying the argument to the parameter. However, the size of what is copied can vary significantly depending on the technique used.

  • J

    Question if you make a ref to a pointer and pass in a class do you still use -> to access the class members Im wondering because i f I create a dynamic object with new I have to use a pointer but dont really want to dereference the pointer everytime

  • boddooo

    Hi sir, This is a great work thank you
    my question
    you said when not to pass by address in passing structs or classes but I see in many great libraries like Assimp and many others they pass structs by address.

    • Alex

      I presume these libraries are either C libraries, or were ported from C. C doesn’t have references, which are superior for most kinds of parameter passing. So if you want a library to be compatible with both C and C++, you’re stuck passing by address.

  • simone

    Hi, Alex. I was trying to buils a simple function that takes a numeric array as input and returns the address of a struct representing a pile with the array values "piled up". However, something does not work.

    This returns the output: 3 (which is fine) and then other two strange numbers.

    I thought that the problem could be that the data pile->next is destroyed, as well as all the other nexts. I also tried to allocate in the main

    before calling the function. In this way, it works. But I was wondering whether there was a way to make it work without doing anything outside the simple_function.

    Can you help me?

    Thanks
    [/code]

  • Xoce

    Hi Mr Alex,
    can we pass an array to a function by value since an array is implicitly converted to a pointer to its first element. which means that an array must be passed by address or reference to a function like for example: func(int &array) or func(int* array) or func(int array[]), am i right? thank you.

    • Alex

      No, you can’t pass a C-style array by value (in a way that will cause the entire array to be copied). You can pass it by address. There is a way to pass them by reference but not as you suggest (func(int &array) won’t work).

  • Ninja

    So I tried to use pointer arithmetic to print the array but it is displaying 6 6 6 6 6 6 instead of 1 2 3 4 5 6.
    I’m following the same logic you used in lesson 6.8a (pointer arithmetic and array indexing) for the C-style string array but its not working here.
    Here is my code below

    • Alex

      Your printArray() loop is printing the value *array, which always points to the first element of the array and never gets incremented.

      You probably meant to print *i, not *array.

      • Ninja

        Oh silly me lol. Makes sense thx. Also I noticed the compiler won’t let me make the *array constant (const int *array) when I use pointer arithmetic. I was wondering why I can’t make it constant since I’m not changing *array?

        • Alex

          (comment removed to avoid confusion)

          • Ninja

            I’m confused, wasn’t it the other way around the whole time? That if you put const before the type, then it is a pointer to a constant value and if const is between type and variable, then its a constant pointer to a non-constant value. I feel like this code does the opposite of everything I learned (about constants)

            • Alex

              Yes, you’re right. Sorry, I totally screwed that up.

              I think the actual issue here is that when you have const int *array (which is a pointer to an array of const ints), you can’t assign int *i to point to it (because i is non-const). You’d have to make i point to a const int as well for this to work (const int *i = array).

Leave a Comment

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