Navigation



6.8 — Pointers, arrays, and pointer arithmetic

Pointers and arrays
Pointers and arrays are intricately linked in the C language. In previous lessons, you learned how to declare an array of variables:

int anArray[5]; // declare array of 5 integers

anArray is actually a pointer that points to the first element of the array! Because the array variable is a pointer, you can dereference it, which returns array element 0:

int anArray[5] = { 9, 7, 5, 3, 1 };

// dereferencing an array returns the first element (element 0)
cout << *anArray; // prints 9!

char szName[] = "Jason"; // C-style string (also an array)
cout << *szName; // prints 'J'

Pointer arithmetic

The C language allows you to perform integer addition or subtraction operations on pointers. If pnPtr points to an integer, pnPtr + 1 is the address of the next integer in memory after pnPtr. pnPtr - 1 is the address of the previous integer before pnPtr.

Note that pnPtr+1 does not return the address after pnPtr, but the next object of the type that pnPtr points to. If pnPtr points to an integer (assuming 4 bytes), pnPtr+3 means 3 integers after pnPtr, which is 12 addresses after pnPtr. If pnPtr points to a char, which is always 1 byte, pnPtr+3 means 3 chars after pnPtr, which is 3 addresses after pnPtr.

When calculating the result of a pointer arithmetic expression, the compiler always multiplies the integer operand by the size of the object being pointed to. This is called scaling.

The following program:

int nValue = 7;
int *pnPtr = &nValue;

cout << pnPtr << endl;
cout << pnPtr+1 << endl;
cout << pnPtr+2 << endl;
cout << pnPtr+3 << endl;

Outputs:

0012FF7C
0012FF80
0012FF84
0012FF88

As you can see, each of these addresses differs by 4 (7C + 4 = 80 in hexadecimal). This is because an integer is 4 bytes on the author’s machine.

The same program using short instead of int:

short nValue = 7;
short *pnPtr = &nValue;

cout << pnPtr << endl;
cout << pnPtr+1 << endl;
cout << pnPtr+2 << endl;
cout << pnPtr+3 << endl;

Outputs:

0012FF7C
0012FF7E
0012FF80
0012FF82

Because a short is 2 bytes, each address differs by 2.

It is rare to see the + and – operator used in such a manner with pointers. However, it is more common to see the ++ or — operator being used to increment or decrement a pointer to point to the next or previous element in an array.

Pointer arithmetic and arrays

If anArray is a pointer that points to the first element (element 0) of the array, and adding 1 to a pointer already returns the next object, then anArray+1 must point to the second element (element 1) of the array! We can verify experimentally that this is true:

int anArray[5] = { 9, 7, 5, 3, 1 };
cout << *(anArray+1) << endl; // prints 7

The parentheses are necessary to ensure the operator precedence is correct — operator * has higher precedence than operator +.

Note that *(anArray+1) has the same effect as anArray[1]. It turns out that the array indexing operator ([]) actually does an implicit pointer addition and dereference! It just looks prettier.

We can use a pointer and pointer arithmetic to loop through an array. Although not commonly done this way (using indices is generally easier to read and less error prone), the following example goes to show it is possible:

const int nArraySize = 7;
char szName[nArraySize] = "Mollie";
int nVowels = 0;
for (char *pnPtr = szName; pnPtr < szName + nArraySize; pnPtr++)
{
    switch (*pnPtr)
    {
        case 'A':
        case 'a':
        case 'E':
        case 'e':
        case 'I':
        case 'i':
        case 'O':
        case 'o':
        case 'U':
        case 'u':
            nVowels++;
            break;
    }
}

cout << szName << " has " << nVowels << " vowels" << endl;

This program uses a pointer to step through each of the elements in an array. Each element is dereferenced by the switch expression, and if the element is a vowel, nVowels is incremented. The for loop then uses the ++ operator to advance the pointer to the next character in the array. The for loop terminates when all characters have been examined.

The above program produces the result:

Mollie has 3 vowels
6.9 — Dynamic memory allocation with new and delete
Index
6.7 — Introduction to pointers

44 comments to 6.8 — Pointers, arrays, and pointer arithmetic

  • Jacob

    Hi Alex!

    I’ve been learning C from your tutorial – and I think it’s really great, so thanks.
    However I just wanted to point (speaking of pointers ;-)) out, that the guy behind these two articles
    http://www.cplusplus.com/articles/siavoshkc1.html
    http://www.cplusplus.com/articles/Arrptr.html
    would have to disagree with you on whether arrays and pointers are the same. Personally, I don’t care if he is right, since the code presented here still works intentionally.

    Best regards
    Jacob

  • The author of those articles is right on some points and wrong on others. However, I find his logic and examples to be quite misleading. Rather than refute his arguments, instead, let me see if I can shed some light on the situation as I understand it.

    Arrays and pointers are not strictly identical. Although arrays are implemented using pointers, arrays also contain slightly more information. When you create an array, the compiler has to keep track of how many elements the array has. Otherwise, the compiler would not know how many elements to delete when it came time to delete[] the array.

    With a non-dynamically allocated array, it’s possible to see the actual size of the array:

    int anArray[5]; cout < < sizeof(anArray);

    This program prints out 20 (assuming 4-byte integers). This is the size of the array.

    On the other hand:

    int *ptr = anArray; cout < < sizeof(ptr);

    prints 4. This is the size of the pointer.

    When you access an array through a pointer, you're accessing it in a slightly limited manner, because you lose access to the size and type information of the original array. This can be a pain (because it means you have to store the size of the array in a separate variable) but that's about the extent of the practical consequences.

    Note that for dynamically allocated arrays:

    int *pnArray = new int[5]

    the new operator returns a pointer to the array, and that's all you ever have to work with. You never get access to the actual array object.

    Functionally, in terms of how you access an array, there is no real difference between accessing the elements of an actual array and accessing the elements of an array through a pointer.

    So technically, it is correct that arrays and pointers aren't identical. Pointers give you access to a limited subset of the array information. Practically speaking, it's generally not even worth worrying about.

  • 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.

    const int nArraySize = 14;
    char szName[nArraySize] = "Lazy BrownFox";
    

    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?

    const int nArraySize = 15;
    char szName[nArraySize] = "Lazy Brown Fox";
    

    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.

  • [...] 2007 Prev/Next Posts « A PHP script to unzip files with file overwriting | Home | 6.8 — Pointers, arrays, and pointer arithmetic » Tuesday, July 10th, 2007 at 6:52 [...]

  • Stuart

    This is going over my head:

    for (char *pnPtr = szName; pnPtr <= szName + nArraySize; pnPtr++)

    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:

        for (char *pnPtr = szName;

        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.

        pnPtr <= szName + nArraySize

        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”.

        pnPtr++

        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.

        • Cory

          For the last part, “Increment the pnPtr pointer to the next character.”, did you mean increment the point to the address of the next character?

          • Moogie

            For what it’s worth (maybe more for others who read this, not necessarily Cory, who probably stopped caring about a response to this message months ago :)) I think the answer is: yes. Although, it’ll really be looking at the character at that address, as Alex said, not the address itself. But yes, you’re thinking along the right lines there.

  • Stuart

    It’s okay – I’ve come back to it with a clear head and I get it now. (And I realize that nArraySize-2 works.) :)

    Thanks for tutorials, BTW.

  • I am thankfull for above Information.I am doing C++ programming…and am doing a Project In Multiple Arrays, using Functions.

    Hope To Get More Information in this web.

    Thank you very much

    Savenaca yauyau

  • Nickell

    how would you sort an char array using pointers like you did above?

  • Alan Hurley

    “It turns out the the array” should read, “It turns out that the array”.

    [ Indeed! Thank you. -Alex ]

  • runner

    You’re making a one-off error here, I think:

    for (char *pnPtr = szName; pnPtr <= szName + nArraySize; pnPtr++)

    The for loop should run for

    pnPtr <= szName + nArraySize-1;

    or

    pnPtr <= szName + nArraySize-2;

    since the junk data after szString[nArraySize - 1] might end up being a vowel (right?)

    [ You are correct. I changed the example so it loops the correct number of times. It still goes through the loop once when pnPtr is pointing to the terminating NULL, but this doesn't hurt anything. -Alex ]

  • Noha

    I just wonder why did write in the last example (in for loop):
    char *pnPtr = szName
    in place of:
    char *pnPtr = *szName
    since you need the pointer of the first element in the array not the element itself!

  • 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…

      const int nArraySize = 7;
      char szName[nArraySize] = "Mollie";
      
      // This produces an array of characters like so.
      // szName[0] = 'M' ,  szName[1] = 'o'
      // szName[2] = 'l' ,  szName[3] = 'l'
      // szName[4] = 'i' ,  szName[5] = 'e'
      // szName[6] = ''.

      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. :)

  • kanks

    Actually, there is one place difference could matter

    //file1.cpp
    int myarray[10];
    
    //file2.cpp
    extern int* myarray;
    
    void foo()
    {
        myarray[0] = 0;
    }
    

    a main function in file2.cpp calling “foo” will crash

  • Realth
    #include 
    
    int main()
    {
        using namespace std;
        char szName[7] = "Mollie";
    
        cout << *(&szName); // Returns  Mollie
    
        cout << &szName; // Returns Address of first element of szName
    
        cin.clear();
        cin.ignore(255, 'n');
        cin.get();
    }
    

    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 :

    char *pnPntr = &szName;
    

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

    Yet this works,

    char *pnPntr = szName;
    

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

    cout << szName ;
    

    I get mollie..

    char *pnPntr=szName;
    

    put a pointer declaration and suddenly szname is and address!

    cout << *pnPntr;
    

    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:

      int *pnPntr = (int*)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.

  • Wow. I finally understand pointers! Yay!

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

  • Andreas

    I really appreciate your guide! THANKS

  • This contains misinformation: “anArray is actually a pointer that points to the first element of the array”.

    An array is not a pointer, ever. An array is an array, a pointer is a pointer. This isn’t up for discussion, read the language standard. An array has an implicit array-to-pointer conversion, but that doesn’t make it a pointer anymore than it makes a function a pointer because a function-to-pointer conversion exists.

    The type of

    a

    in

    int a[10]

    is

    int[10]

    There are no pointers there.

    • 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:

      #include <iostream>
      
      int main()
      {
      	using namespace std;
      	//string myString = "Christopher";
      	char myStringArray[] = "Christopher";
      	int arraySize = sizeof(myStringArray)/sizeof(myStringArray[0]);
      	int nVowels = 0;
      
      	for (char *pnPtr = myStringArray; pnPtr < myStringArray + arraySize; pnPtr += 1)
      	{
      		switch (toupper(*pnPtr))
      		{
      			case 'A':
      			case 'E':
      			case 'I':
      			case 'O':
      			case 'U':
      				nVowels++;
      				break;
      		}
      		cout << "pnPtr: " << pnPtr << "\t*pnPtr: " << *pnPtr << endl;
      	}
      		//cout << myStringArray << " has " << nVowels << " vowels." << endl;
      }

      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)…

      #include <iostream>
      
      int main()
      {
      	using namespace std;
      	int myStringArray[] = {1, 2, 3, 4, 5, 10, 20};
      	int arraySize = sizeof(myStringArray)/sizeof(myStringArray[0]);
      
      	for (int *pnPtr = myStringArray; pnPtr < myStringArray + arraySize; pnPtr += 1)
      		cout << "pnPtr: " << pnPtr << "\t*pnPtr: " << *pnPtr << endl;
      
      }

      …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?

  • wow.! this site is great,
    it somehow helps me to learn more.

    can i ask?
    how i can i determine the numbers even and odd inputs using the pointer arithmetic?

  • 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:

    #include <iostream>
    #include <cctype>
    using namespace std;
    
    int main()
    {
    	char str[] = "This is a String";
    	char *p = str;
    
    	cout << "Original string: " << str << 'n';
    
    	while(*p)
    	{
    		if(isupper(*p)) (*p) = tolower(*p);
    		if(islower(*p)) (*p) = toupper(*p);
    		p++;
    	}
    
    	cout << "Inverted-case string: " << str << 'n';
    
    	return 0;
    }
    

    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…

  • Dr. HaXX

    I keep seeing stuff like:

    [ CODE ] somerandomstufftypedhere [ /CODE ]

    How is that possible?

  • Matt

    I have a question about the use of the test-condition in the loop of this example and was hoping you could clear it up for me Alex.

    const int nArraySize = 7;
    char szName[nArraySize] = “Mollie”;
    int nVowels = 0;
    for (char *pnPtr = szName; pnPtr < szName + nArraySize; pnPtr++)
    {
    switch (*pnPtr)
    {
    case 'A':
    case 'a':
    case 'E':
    case 'e':
    case 'I':
    case 'i':
    case 'O':
    case 'o':
    case 'U':
    case 'u':
    nVowels++;
    break;
    }
    }

    cout << szName << " has " << nVowels << " vowels" << endl;

    If I understand this correctly, in pnPtr < szName + nArraySize;, szName is the name of the array which points to the address of element 0 in the array. nArraySize evaluates to 7, the actual size of the array which is equivalent to the pointer addition of pPtr (for the sake of making it similar to your earlier pointer arithmetic example) + 7.

    szName points to element[0] or the "M", pPtr + 1 to "o", 2 to "l", 3 to the second "l", 4 to "i", 5 to "e", 6 to the null terminator, and because the less-than sign was used instead of <=, the final element must be 1 integer higher than the size of the array so the loop ends. In this case though since you're using addresses, would it be that pPtr + 7 be the address of the next char in memory as opposed to some integer index number?

  • Matt

    Oh, In my haste writing my comment I forgot to mention that the main question was if I correctly understand how the test condition of the loop was working. That last bit at the end was something I just thought to ask at the last second.

  • 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!

  • I CAME UP WITH DYNAMIC EXAMPLE FROM ALEX EXAMPLE…
    PEOPLE CAN ENTER A WORDS OF THEIR CHOICE.. BY ICT DE IFYCENT2…
    // ICT DE IFYCENT2 EXAMPLE
    #include “stdafx.h”
    #include

    using namespace std;

    int main()
    {
    const int nArraySize = 50;
    char szName[nArraySize];
    cout <> szName;
    int nVowels = 0;
    for (char *pnPtr = szName; pnPtr < szName + nArraySize; pnPtr++)
    {
    switch (*pnPtr)
    {
    case 'A':
    case 'a':
    case 'E':
    case 'e':
    case 'I':
    case 'i':
    case 'O':
    case 'o':
    case 'U':
    case 'u':
    nVowels++;
    break;
    }
    }

    cout << szName << " has " << nVowels << " vowels" << endl;
    system ("PAUSE");
    return 0;
    }
    THANKS MR.ALEX

  • ahrramin

    Why does:

    “char szName[] = “Stefan Molyneux”;

    cout << szName;"

    output: Stefan Molyneux

    whereas:

    "int x[] = { 1, 2, 3, 4, 5 };

    cout << x;"

    outputs the address of x?

    I tried to do the same thing with a string array:

    "string x[] = {"a", "b", "c"};

    cout << x;"

    and I got some sort of runtime error.

You must be logged in to post a comment.