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 the 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
|
6.9 — Dynamic memory allocation with new and delete
Index
6.7 — Introduction to pointers
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.
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.
This is going over my head:
Could you explain it, please?
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.