More pointers and arrays

In the terms we have been using, a pointer is used to hold the location of another variable. The contents property of the pointer contains the location property of the other variable. So in the code below,
	int a=5, *ptr;
	ptr = &a;

The ampersand extracts the location from the a variable and puts it in as the contents of the ptr variable. Lets look at some concrete examples.

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;
} // bar

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