Search

6.8 — Pointers and arrays

Pointers and arrays are intrinsically related in C++.

Array decay

In lesson 6.1 -- Arrays (part i), you learned how to define a fixed array:

To us, the above is an array of 5 integers, but to the compiler, array is a variable of type int[5]. We know what the values of array[0], array[1], array[2], array[3], and array[4] are (9, 7, 5, 3, and 1 respectively).

In all but two cases (which we’ll cover below), when a fixed array is used in an expression, the fixed array will decay (be implicitly converted) into a pointer that points to the first element of the array. You can see this in the following program:

On the author’s machine, this printed:

Element 0 has address: 0042FD5C
The array decays to a pointer holding address: 0042FD5C

It’s a common fallacy in C++ to believe an array and a pointer to the array are identical. They’re not. In the above case, array is of type “int[5]”, and its “value” is the array elements themselves. A pointer to the array would be of type “int *”, and its value would be the address of the first element of the array.

We’ll see where this makes a difference shortly.

All elements of the array can still be accessed through the pointer (we’ll see how this works in the next lesson), but information derived from the array’s type (such as how long the array is) can not be accessed from the pointer.

However, this also effectively allows us to treat fixed arrays and pointers identically in most cases.

For example, we can use indirection through the array to get the value of the first element:

Note that we’re not actually indirecting through the array itself. The array (of type int[5]) gets implicitly converted into a pointer (of type int *), and we use indirection through the pointer to get the value at the memory address the pointer is holding (the value of the first element of the array).

We can also assign a pointer to point at the array:

This works because the array decays into a pointer of type int *, and our pointer (also of type int *) has the same type.

Differences between pointers and fixed arrays

There are a few cases where the difference in typing between fixed arrays and pointers makes a difference. These help illustrate that a fixed array and a pointer are not the same.

The primary difference occurs when using the sizeof() operator. When used on a fixed array, sizeof returns the size of the entire array (array length * element size). When used on a pointer, sizeof returns the size of a memory address (in bytes). The following program illustrates this:

This program prints:

20
4

A fixed array knows how long the array it is pointing to is. A pointer to the array does not.

The second difference occurs when using the address-of operator (&). Taking the address of a pointer yields the memory address of the pointer variable. Taking the address of the array returns a pointer to the entire array. This pointer also points to the first element of the array, but the type information is different (in the above example, the type of &array is int(*)[5]). It’s unlikely you’ll ever need to use this.

Revisiting passing fixed arrays to functions

Back in lesson 6.2 -- Arrays (part ii), we mentioned that because copying large arrays can be very expensive, C++ does not copy an array when an array is passed into a function. When passing an array as an argument to a function, a fixed array decays into a pointer, and the pointer is passed to the function:

This prints:

32
4

Note that this happens even if the parameter is declared as a fixed array:

This prints:

32
4

In the above example, C++ implicitly converts parameters using the array syntax ([]) to the pointer syntax (*). That means the following two function declarations are identical:

Some programmers prefer using the [] syntax because it makes it clear that the function is expecting an array, not just a pointer to a value. However, in most cases, because the pointer doesn’t know how large the array is, you’ll need to pass in the array size as a separate parameter anyway (strings being an exception because they’re null terminated).

We lightly recommend using the pointer syntax, because it makes it clear that the parameter is being treated as a pointer, not a fixed array, and that certain operations, such as sizeof(), will operate as if the parameter is a pointer.

Recommendation: Favor the pointer syntax (*) over the array syntax ([]) for array function parameters.

An intro to pass by address

The fact that arrays decay into pointers when passed to a function explains the underlying reason why changing an array in a function changes the actual array argument passed in. Consider the following example:

Element 0 has value: 1
Element 0 has value: 5

When changeArray() is called, array decays into a pointer, and the value of that pointer (the memory address of the first element of the array) is copied into the ptr parameter of function changeArray(). Although the value in ptr is a copy of the address of the array, ptr still points at the actual array (not a copy!). Consequently, when indirection through ptr is performed, the element accessed is the actual first element of the array!

Astute readers will note this phenomena works with pointers to non-array values as well. We’ll cover this topic (called passing by address) in more detail in the next chapter.

Arrays in structs and classes don’t decay

Finally, it is worth noting that arrays that are part of structs or classes do not decay when the whole struct or class is passed to a function. This yields a useful way to prevent decay if desired, and will be valuable later when we write classes that utilize arrays.

In the next lesson, we’ll take a look at pointer arithmetic, and talk about how array indexing actually works.


6.8a -- Pointer arithmetic and array indexing
Index
6.7a -- Null pointers

241 comments to 6.8 — Pointers and arrays

  • Nick

    Something that I can't understand is that an array decays into a pointer once passed into a function, but then inside the scope of the function the array is again implicit dereference and can be accessed by, let's say arr[1] instead of *(arr+1)?, why does that happened?

    • nascardriver

      `[]` syntax doesn't care if the target is an array. It works the same for arrays and single-object pointers, just that it causes undefined behavior for single-object pointers if the index is > 0.

  • Yousuf

    Size of integer and integer to pointer might be different across machines. On instructor's machine size of an integer is 4 bytes and size of integer to pointer is 4 bytes too. However on my machine it is different:

  • CC

    Since you recommend that one use

    as the syntax for functions that take arrays an argument, how do you distinguish between functions that take arrays of integers as an argument vs functions that take integer pointers as an argument?

    Or is it that one never really passes pointers to functions?

    • nascardriver

      doesn't make sense, because `printSize` wouldn't know the array's length. There'd usually be a second parameter that holds the array's length. This hints that the functions wants to have an array, not a pointer to a single value.

      The recommended way of passing arrays is via `std::span`. `std::span` is not yet covered on learncpp. You can read up on it yourself if you like to, it should be fairly easy to understand, at least at the end of this chapter.

      • CC

        I understand it doesn’t make sense — does this mean that distinguishing between a function that takes an array as a parameter / argument vs one that takes a plain pointer is purely a semantic exercise?

        It has to be given your recommendation, since the syntax you suggest is identical.

        • nascardriver

          It's purely semantic through the other parameters, parameter names, and documentation. There's no way to tell a pointer to an array apart from a regular pointer. One of the reasons why C-style arrays aren't welcome in C++.

  • omen

    Why am i getting  a runtime error(SIGSEGV), I don't get what is wrong the array is properly declared

    I have also replaced 30 in Initialization of array with Test, also initialized the array to zero but then the answer is wrong, when tried in codeblocks it is running fine but not on the site(codechef).
    Thank You!!

    • nascardriver

      If `Test >= 30` you're overflowing the array. What are you inputting?

      • omen

        when I'm using variable then the site's complier is saying that is code is wrong so tried of using literals, test is the number of cases have to run(in which the last and first digit is to be added and returned and printed out).

        • nascardriver

          There's nothing wrong with the 30 in line 7. The problem is if you input a value larger than 30 for `Test`.
          What are you giving `std::cin` as inputs?

          • omen

            Got the answer, removed the array as it is taking all the inputs at the same time so it is all good(as i don't have to show answer all at a time).

            Also, the complier is rejecting the value as it is runtime value and in fixed array it can't be used, hence getting runtime error(SIGSEGV).

            Test is the number of time the code is run on different cases but the thing is that even if I gave 100 as the array size it is showing the whole code as incorrect maybe the case are even more, haaa let it be the problem is partially solved. Thanks for the help.

  • Mike

    Hi, is possible to store part of a text in an array just in case this exceeds its size?

    Like: char text [5];

    cout << "Type a word " << endl;
    cin.getline(text, 5);

    But the user types something like "Orange", can you save just the first five characters to display them later?

    • nascardriver

      You already showed how to do this, so I don't understand what your question is. Can you elaborate?

      • Mike

        Sorry about that, I meant that if it's only with one array it's fine but when I try to use it with more than one one array it just skips to the end of the program if the first one exceeds the size

        char text [5];
        char text2 [10];

        cout << "Type a word " << endl;
        cin.getline(text, 5);

        cout << "Type a second word " << endl;
        cin.getline(text2, 10);

        cout << "Your words are: \n";
        cout << text << "\n"
             << text2 << endl;

        If the user for some reason types a word that exceeds the size of the first array the program doesn't allow to input the second word and just jumps to the end and shows just the characters of the first word.

        I know that the easiest solution would be making the size of the array bigger, but the idea is that if the word or sentence is bigger than the array then it just stores the first characters and then ignores the exceeding ones

        • nascardriver

          When `std::cin.getline` reaches the limit (in your case 4 and 9 characters), it places `std::cin` in a failed state. To get out of that state, call `std::cin.clear()` after calling `std::cin.getline()`. Then you can proceed extracting more data.
          However, the second word will then contain the remaining parts of the first word. To remove the rest of the first word, use `std::cin.ignore()`.

Leave a Comment

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