Search

Admin

6.8 — Pointers and arrays

Pointers and arrays are intrinsically related in C++.

Similarities between pointers and fixed arrays

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). But what value does array itself have?

The variable array contains the address of the first element of the array, as if it were a pointer! You can see this in the following program:

On the author’s machine, this printed:

The array has address: 0042FD5C
Element 0 has address: 0042FD5C

Note that the address held by the array variable is the address of the first element of the array.

It’s a common fallacy in C++ to believe an array and a pointer to the array are identical. They’re not. Although both point to the first element of the array, they have different type information. In the above case, array is of type int[5], whereas a pointer to the array would be of type int *. We’ll see where this makes a difference shortly.

The confusion is primary caused by the fact that in many cases, when evaluated, a fixed array will “decay” (be implicitly converted) into a pointer to the first element of the array (essentially, losing its type information).

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

For example, we can dereference the array to get the value of the first element:

Note that we’re not actually dereferencing the array itself. The array (of type int[5]) gets implicitly converted into a pointer (of type int *), and we dereference the pointer to get the value at that 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, 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 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.

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 ptr is dereferenced, the actual array is dereferenced!

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 an 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

38 comments to 6.8 — Pointers and arrays

  • Alex,

    Your site is wonderful, thanks for providing it. I was editing some of the examples you provide to see what happens with the output and I noticed something I cannot explain. When I substitute the following into your last example (Mollie/Vowels) I get the output 3, which makes sense.

    But When I change the code to the below, the result becomes 4, which I do not understand. It is as if the program is counting the last whitespace character as a vowel. Can you explain this to me?

    BTW, I am using Cygwin with Dev-C++ and the GNU compilers.

    Thanks and best regards,
    Pete

    • When I ran your Lazy Brown Fox example with Visual C++ 2005, I got 3 vowels, not 4. So at the moment I’m not sure why you’re getting a different result.

      My advise to you would be to run your program using a debugger, and step through the code. Put a watch on nVowels and then watch it count up -- when it becomes 4, you’ll at least know what’s causing it.

  • Stuart

    This is going over my head:

    Could you explain it, please?

    • miroslav

      I’m sorry but would it be possible to explain? For example the ‘pnPtr <= szName + nArraySize)’

      • Hmmm, not sure how I missed the original comment here. Sure, here’s the explanation.

        In this example, szName is an array. As you have learned, arrays and pointers are intricately linked in C++. Let’s break this for loop into it’s 3 components and examine each one:

        This is creating a new pointer named pnPtr and setting it to point to the first character in the name array. After this, pnPtr and szName will be holding the same address.

        The tricky part of this is determining what szName + nArraySize means. Arrays are laid out contiguously in memory. If szName is the start of the array, and nArraySize is the length of the array, then szName + nArraySize must be the end of the array. So what this is really saying is, “while pnPtr hasn’t reached the end of the array, keep looping”.

        Increment the pnPtr pointer to the next character.

        So, putting it all together: “pnPtr is set to the start of the array. While it hasn’t reach the end of the array, do the loop, and then increment pnPtr to the next character”.

        That’s it.

  • Yogesh Shastri

    Great Site. A lot of things are said about sharing learning and wisdom, but a few are able to do it. You are one of them, and since you have done it with excellence, so kudos to you! I have tried to write tutorials for another programming language that I have learnt, but couldn’t do it, because of so many factors - so I can really appreciate your efforts. Thanks to you!

  • Yogesh Shastri

    Dear Alex,

    Thank you for providing this wonderful resource for learning C++. I was trying my luck at pointers, but when I wrote a code like this:

    #include
    #include
    void main()
    {
    clrscr();
    int arr[5]={1,2,3,4,5};
    char name[]=”Pratyush”;
    cout<< *arr++ <<endl;
    cout<< *name++ <<endl;
    getch();
    }

    I get error on line on which I use *arr++ and *name++ saying that an Lvalue is required. Could you please explain.

    Regards and bets wishes.

    • Sandesh

      Yogesh,

      L-Value means a quantity on left side of any equation or assignment.L-Values are always variables while values on right side(R-Values) are numbers or characters.The ++ operator requires varibles that is L-Values.

      The dereferencing operator “*” has higher precedency than the increment by 1 “++” operator.If the program would have exceuted,while excecuting the first cout, the operation *arr would be excecuted first then ++.*arr would result the address of first element of array ‘arr’ as array name is a pointer that addresses the first element.the address would be a hexadecimal number and therefore not a variable and ++ statement would not execute.Therefore,it shows L-value(variable) required.

      it is the same with second cout.* operator would now be the value of first element and therefore ++ would not execute.

      i hope you understood it.

      Cheers;Sandesh

      • rawr

        You say that *arr results in a hexadecimal number, yet when I type *arr it uses the value. Also, the code works with (*arr)++.

        Is this because addresses are simply treated differently to pointers by the compiler?

      • AUASP

        “the dereferencing operator “*” has higher precedency than the increment by 1 “++” operator”
        Actually the reverse is true.check any c++ associativity and precedence list.

  • Jacob Quadro

    Thanks for the great tutorial Alex.

    In the above code:
    const int nArraySize = 7;
    char szName[nArraySize] = “Mollie”;

    I’m confused why should nArraySize = 7, when Mollie is just six characters.
    I tried replacing it with 6 but it won’t compile.

    Thanks again.

    • Quinn

      You had forgotten to include the null terminating character, . Every C-string must end with the null terminating character, and as a direct result, all C-strings are sized to one larger than the string you’re putting into it. In essence, it would work out like this…

      Alex covered this in much more detail in C-Style Strings, so I’d suggest revisiting that section to see it in action.

      When you change nArraySize to equal 6, that meant that the szName array could only contain a string of five characters (because it MUST end with a null-terminating character, if it doesn’t, it causes overflows). So the compiler realizing this, issued a build error since this would inevitably, without fail, lead to an overflow. This is just one of the many little quirks of the C/C++ language, and computer programming in general, you’ll have to get used to. :)

  • Realth

    Why does cout << *(&szName) output the entire string ? Shouldn’t it only return the first character of szName ? &szName returns the address of the first element right ? So derefrencing it should give the first character.

    Also why does my compiler complain when I try this :

    I want pnPntr to point to the first Address of szName.

    Yet this works,

    Depending on what is on the left of szName, szName means completely different things!

    I get mollie..

    put a pointer declaration and suddenly szname is and address!

    It’s and address again! ??

    Is this just how it is ?

    Excellent tutorials by the way!

    • eric

      *(&szName) is doing inverse operations, so it ends up just szName. The value of szName is actually an address (because it’s a pointer to the first address in the array), but the compilers treat it differently because it’s a string. The compiler jumps to the address in szName and reads the values starting from that address until it hits a null.
      This is probably done for convenience, because when you’re looking at an array you usually want to see its contents and not its address. To access the first element, use *szName or szName[0]

      After playing with it for awhile, I found that this will work setting to the address of the szName. I have no idea why, however. Intuitively, this should set pnPntr to the address of the first element in the array which szName points to, but the value of (int*)szName matches &szName:

      However, changing the contents via “*pnPntr = “doesn’t work too well, as you’re limited to the first 4 characters’ ASCII values (my compiler had the added annoyance of outputting these in decimal rather than hex).

      Hopefully someone else can be a little more clear on all this, because I don’t know enough about it to say, yet.

    • Alex

      The short answer to all your questions is that szName decays to a pointer to the first element of the array.

      So when you say this:

      You’re setting pnPntr to the content of szName, which is the address of the first array element. That’s likely what you’d intended.

      When you say this:

      You’re setting pnPntr to the address of szName, which itself is pointing to the first element of the array.

      Assuming the former:

      This is the same as pnPntr[0], so it should be no surprise it only prints the first element.

      cout is smart enough to see that pnPntr is a char pointer, so it assumes you want to print the whole string.

  • Wow. I finally understand pointers! Yay!

    That is a pretty hand way of stepping through an array.

  • Andreas

    I really appreciate your guide! THANKS

  • Chris

    First, thanks so much for your work on this…your tutorial is a huge help, Alex!

    Walking through this portion on the use of pointers with arrays, I’m slightly confused by why using the reference operator (&) on a pointer created for a char array won’t return the value’s address, while one created for an int array will.

    For example the following code:

    Will output the following:

    pnPtr: Christopher *pnPtr: C
    pnPtr: hristopher *pnPtr: h
    pnPtr: ristopher *pnPtr: r
    pnPtr: istopher *pnPtr: i
    pnPtr: stopher *pnPtr: s
    pnPtr: topher *pnPtr: t
    pnPtr: opher *pnPtr: o
    pnPtr: pher *pnPtr: p
    pnPtr: her *pnPtr: h
    pnPtr: er *pnPtr: e
    pnPtr: r *pnPtr: r
    pnPtr: *pnPtr:
    Christopher has 3 vowels.

    …rather than a hex address for pnPtr.

    While setting up a random integer array and carrying out the same process (sans vowel counting)…

    …generates this output, with address output for the value of pnPtr and specific array entries for *pnPtr. (I expected the char array output to similar to this.)


    pnPtr: 0xbfc6e1d8 *pnPtr: 1
    pnPtr: 0xbfc6e1dc *pnPtr: 2
    pnPtr: 0xbfc6e1e0 *pnPtr: 3
    pnPtr: 0xbfc6e1e4 *pnPtr: 4
    pnPtr: 0xbfc6e1e8 *pnPtr: 5
    pnPtr: 0xbfc6e1ec *pnPtr: 10
    pnPtr: 0xbfc6e1f0 *pnPtr: 20

    Can you explain why C++ deals with them differently or what I’m doing improperly?

    • Alex

      std::cout assumes that it should treat char* as a string, so it prints objects of that type as a string. For other types of pointers, it just prints the content of the pointer (the address the pointer is holding as a value).

  • WGL

    I have a question regarding a simple program which indexes using pointer arithmetic in order to reverse the case of a string. The code is this:

    Unfortunately, the output gives a completely uppercase string, “THIS IS A STRING”, rather than the inverted case where a few letters should now be lowercase. I cannot see what the problem is! The code is not complicated, but admittedly I am not used to using pointers to index an array (I am a scientist writing a program which needs to be as efficient as possible - I have been led to believe that pointer arithmetic can produce faster code).

    Any help would be appreciated!

    • Gene

      You probably discovered the reason for uppercase only a long time ago, but here’s an explanation in case you left this unsolved. The teacher in me will explode unless the answer can be revealed 😉

      You need to add “else” in between the first and second “if” clause (or you could use switch…case conditional structure instead).

      The current logic of your original code says, “if current character is uppercase, make it lowercase”, then “if current character is lowercase” (and it always will be at this point), “make it uppercase”.

      I’ve been a programmer for 23 years and I STILL sometimes miss obvious mistakes like this, so don’t feel bad about missing something so obvious.

      There is a valuable underlying principle here that might help you spot things like this in the future:

      When logical conditions are not behaving as expected, start by checking on the simplest things first, not the complicated things.

      In other words, your problem had nothing to do with pointers.

      Another way to solve the problem is to swallow your pride and ask someone else to look your code over and tell you what’s wrong with it. Just be warned that it will be embarassing! I’ve spent hours at times trying to figure out why my code isn’t working, only to watch as someone who just glances at it sees the problem instantly!

      It’s painful to do that, but you’ll help the other person feel smart and important. And that’s actually more valuable in the long run than figuring the code out yourself most of the time.

      And I agree with everybody else about this site. One of the best C++ tutorials on the web. Thanks, Alex!

      Hope this helps…

  • sagar24

    Hi,

    I had a basic doubt regarding pointers. As per the pointer arithmetic, pnPtr+1 would refer to next object of the type that pnPtr points to. Thus, if pnPtr is an integer pointer than (pnPtr+1) would be (current_address_of_pnPtr + 4).
    Consider the memory block as follows :
    Memory location 0: pnPtr (Some_Int_Pointer. Assuming int takes 4 bytes)
    Memory location 4: pchPtr (Some_Char_Pointer. Assuming char takes 1 byte)
    Memory location 5: pnPtr1 (Some_Int_Pointer)

    Now when we try to access (pnPtr+2), it would try to access memory location 8 (the last byte for pnPtr1).

    Isnt that an undesirable situation. Wouldnt it return some gibberish value ?

    Thanks!

  • Typos:
    "This effectively allows us to treats(treat) an array as a pointer in most cases"

    char array variable is named "name". "szname" is undefined, so dereferencing it will cause a compile error. Remove "sz" from the pointer name in the last line.

    "Note that ptr+1 does not return the memory address after pnPtr(should be ptr)"

  • I found one more:
    "In the fixed array case, the program allocates memory for a fixed array of length 5, and initializes that memory with the string “Alex\n”"

    "What usually happens is that the compiler places the string “Alex\n” into read-only memory somewhere, and then sets the pointer to point to it"

    If a string terminates with a null terminator and not with a newline escape sequence(\n), the "Alex\n" part should be "Alex" in both sentences. Take a look at my previous comment in this section. There are more typos.

    A question…actually 2:
    1. If an array decays into a pointer when evaluated, why sizeof(array) prints length*element value size. Does this means, the bracketed term (array) is not evaluated in this statement.
    2. When I tried to get the address of a char variable by following program, it prints a strange character and not the address:

    Why…???

    • Alex

      Thanks for the typo notifications.

      1) This is just a special case.
      2) This is an interesting case you’ve found. You intended to print the memory address of variable value, but &value is being interpreted as a value of type char*. When you pass a value of type char* to std::cout, it prints that value as a string. Your variable value isn’t null terminated, so it prints a, and then runs off into uninitialized memory and prints some garbage before randomly hitting a 0, which acts as a null terminator.

  • Thanks Alex, seems like you didn’t noticed this in my comment:

    "This effectively allows us to treats(should be treat) an array as a pointer in most cases"

    Typo is in the first paragraph after the first program posted in this lesson. The last line.

    I am again with a question, If we can increment or decrement a pointer by 1 adrress by using increment decrement operators, why compiler complains when these operators are used directly on arrays to increment/decrement the address (something like (&array[0]) + 1) by 1 address? I know question will not explain what I am asking. Here is the program:

    If array breaks into a pointer when evaluated, what is happening in 15th line? Is it also a special case? If yes, can I say that array remains array (and doesn’t decay into a pointer) when seen with an unary operator by the compiler?
    I am not sure because dereference operator is also an unary operator.
    Whatever it is, I am unable to understand why line no. 15 gives a compiler error. Can’t it print the address of the integer, that comes after the address of the last element in the array.

    • Alex

      Thanks for the heads up about the typo.

      ++array is the equivalent of array = array + 1. A fixed array’s value (the address it points to) is considered const (it’s non-reassignable). You’re trying to change it, which is illegal.

  • One more conclusion, may be wrong:
    I was testing arrays with different operators. Some operators cause it to break into a pointer and some doesn’t. If I am right(not sure, I am just a beginner), I found the solution of the question why array breaks into a pointer  when passed to a function. May be, the function call operator {()} is one of the operator that causes the array to break into a pointer, with no side effect. Please read my previous comment. I asked a question there.

    • Alex

      There are other operators that cause arrays to decay into pointers, such as unary operator+. So it’s not just the function call operator that causes decay.

  • 1. "To us, the above is an array of 5 integers, but to the compiler, array is a variable of type int[5]"
    array is a pointer variable of type int[5], right? Add "pointer" before the term "variable" or let me know if I am taking it wrong.

    2. Alex, help me get out of this big confusion:

    In this program, if array is a variable of type int[5] and name is of char[], that points to the first element of that  array, what is decayed then. Code is printing what’s expected. array variable pointing to first element of array[5], and dereferencing it will definitely output the first element. Hope you understand what I am asking.

    3. "Taking the address of the array returns a pointer to the entire array (in the above example, int[5] *), not just the first element (int *)." I can’t understand, what are you trying to say here. Do you mean this: "&array returns the address of entire array"? If yes, then again this program tricks me:

    If &array is returning the address of the entire array, then why address of first element (&array[0]) is also same(printing the same address as &array)?

    4. How to print the address of entire array and not just the address of its first element?
    Sorry for bunch of questions, but this lesson is really confusing to me.

    • Alex

      Good questions.
      1) Array has type int[5], not int*. This is why we can use sizeof() on it and get the size of the array, not the size of a pointer.
      2) Arrays decay from their array types into a pointer type in most cases, including when they’re sent to cout. In practical terms, this rarely matters unless you need the size information.
      3) The address of the first element and address of the entire array are the same. The only difference is the type information that is returned. A pointer to the first element would have type int*, whereas a pointer to the whole array would have type (int[5] *). The only case where this is likely to matter is when you’re doing pointer arithmetic. Incrementing a pointer to the first element will advance to the second element. Incrementing a pointer to the entire array will move to the memory address just beyond the array.
      4) Again, they both point to the same address. It’s only the typing that’s different.

      Let me know if this is clear, or how I could make the above lesson clearer to begin with.

  • Thanks Alex..you’r a star

    Suggestion:
    You should exactly tell what cout prints when asked to print the address of a variable. Take this for example:

    cout is printing the "very first" address of first and second element of the array. An int takes 4 bytes, means 4 addresses. Those 4 memory addresses are reserved for that int variable. cout never prints all the 4 addresses reserved for that variable, only the first 1 byte sized address. In the above program, the first line of output is the (starting)address reserved for variable value. Let’s say, it is 0*0000. Memory from 0*0000 to 0*0003 is reserved for value. When cout is asked to print the address of value2, it gives 0*0004, again the starting address. This clears a confusion that why array’s address and its first elements address are the same when printed(my one :-)). An array has nothing in it but its elements. Thus, an arrays address starts from the (first) address of its first element and ends with the (last) address of its last element. cout only prints the starting address, that’s why it gives identical outputs when printing address of an array(&array) and its first element(&array[0]). Sizeof returns the total size of its parameter(I don’t think operators accept any parameter, but no words for those bracket closed objects right now).
    The part Arrays and Pointers in C++ is really confusing for beginners like me. I gave my 5 days to this chapter to clear everything and today at least not afraid of the title “Arrays and Pointers”. You can’t do anything better. Your site has the simplest explanation of Arrays and Pointers.

    • Alex

      Thanks for the feedback. I’ve rewritten parts of the article to try and make some of the points more clear. Talking about the sequential memory addresses of arrays is a good idea, but probably better for the next lesson. I’ll look at adding it there.

  • Quang

    Hi Alex, thank you for this tutorial. but I wonder there are any other books, documentaries that can help me practice coding, i mean a book full of exercises like your quiz. Most regard!!!

Leave a Comment

  

  

  

10 + seven =

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