int a=5, *ptr;
ptr = &a;
Here are the properties of a and ptr
Name | Type | Location | Contents |
a | int | 22222 | 5 |
ptr | int * | 33333 | 22222 |
So the pointer variable now contains the address of the a variable.
We can use this to get and set the contents of a. There is an operator
that is used to retrieve the contents of the thing pointed at. For example,
if we print ptr like this
cout << ptr << endl;
We get the value 22222 printed. But we are not really interested in
this value. We are interested in the entry in the table above that has
a location value of 22222. We use the star * operator to do this,
cout << *ptr << endl;
would print 5. One way to view this is that the * operator searches
this table and looks for the row where the location field matches the contents
of the pointer variable. Another way is to see it as just getting the contents
indirectly. * ptr works in two steps. It first gets the contents
of pointer, which is 22222. Then it uses that number as a memory address
and retrieves whatever is stored there. In this case, that is 5. This is
because the location value is the actual machine address where the variables
contents are stored.
Now let's look at the relationship between arrays and pointers. An array
van be seen as a collection of individual variables. For example,
int foo[3];
This is three variables collected together. They are named foo[0].
foo[1] and foo[2]. These can be used anywhere an integer can be used. Another
analogy is to a carton of eggs. The individual eggs can be used in any
recipe that calls for eggs. But the carton cannot. Consider a function
like this.
int bar(int a) { cout << a << endl; } // barso a function call like b = bar(foo[1]); is legal but b = bar(foo); is not. Now this variable int *ptr; Let's point this at the first element in the array ptr = &foo[0];
now we have two ways to refer to the same memory location. Both *ptr and foo[0] refer to the same thing. We can use them interchangeably.
*ptr = 8; foo[0] = 8; x = *ptr; x = foo[0];
In C++, this two ways of getting to a memory address can be used mostly
the same. So we can say
ptr[0] = 8; *foo = 8;
Notice that we have used the name of the array as a pointer. This is
generally true and is more than a notation. The name of an array is a pointer
to the array. So instead of saying
ptr = &foo[0];
we could have said
ptr = foo;
The contents of ptr is the same as foo. And the contents of foo is
the address of the first element of foo. Now consider what would happen
if we added one to the contents of ptr. Since all elements of an array
are physically right next to each other, adding one to an address gives
us the next address. So, if ptr points to the first element of foo, ptr
+ 1, points to the second. And ptr + 2 points to the third. Now if we put
a * in front like this,
*ptr + 1
this would get the value stored at that location. But not quite. Due
to precedence rules, * is done before +, so this actually gets the value
stored at ptr and adds one to that. Not quite what we want. So we use parenthesis
to sort it out,
*(ptr+1)
This adds one to ptr, and retrieves the value stored there. This is
exactly that same as ptr[0]. In fact, that is what happened when the compiler
sees ptr[1].
This operation is called pointer arithmetic and is not quite
the same as regular arithmetic. We can actually see that here. An integer
is actually at least 2 bytes and more often 4 bytes long. Adding 1 to a
pointer (memory address) would normally get us the address of the next
byte. We want the address of the next integer. The compiler knows this
and translates the arithmetic to it actually adds 1 times the size of the
thing being pointed to. So ptr + 1 is really ptr + 4. You never have to
worry about this translation. It happens automatically.
We can now pass arrays into functions. If we write the function to take a pointer to an array, we can pass the array into the function.
void nyarf(int * p) { cout << p[0] << endl; cout << *p << endl; } // nyarf
We can call this function like this,
nyarf(foo);
This works since the type of foo, by itself, is pointer to int. This
is because the name of an array is a pointer to the base type. We could
also call this as
nyarf(ptr);
since ptr points at foo.