Topic : An introduction to C
Author : Tom Torfs
Page : << Previous 11  Next >>
Go to page :


value of element number 4 (the 5th element) of the array, which is 7. Nothing new here.

   printf("the address of the array = %p\n", (void *)a);

We've seen before that by using something of the form arrayname[indexnumber] we can access an element of an array. But what happens if we simply use the arrayname itself (in this case a) ? That will return the address of the first element of the array (element number 0). That's also why assigning arrays simply using the assignment operator or passing the entire contents of an array as a parameter is not possible.
OK, so we have the address of the array's first element, which is the same value as the address of the array (the type is different; the former is a pointer to int, the latter a pointer to an array of int). Now how do we print it ? That's something special. The printf format specifier %p prints the value of a pointer (thus an address). What it prints exactly will be different on every system, but the concept will be the same.
But %p is used to print a generic pointer as seen in the note above: a void * instead of the int * that the address of the array will be. Therefore we have to typecast (see 4.2. Operators and typecasts) the pointer value (address) to a void *. [*]
[*] On many systems simply passing an int * to the printf() format specifier %p will work fine; but the standard requires this conversion.

   printf("the address of element nr. 0 of the array = %p\n", (void *)&a[0]);
   printf("the address of element nr. 4 of the array = %p\n", (void *)&a[4]);


These two lines are similar to the previous one: they print out an address using the %p format specifier, after typecasting that address value to a generic void *.
However, instead of using the arrayname a to get the address of the array's first element, an element index is used (0 resp. 4) between the [ ], and another operator is prepended: the address operator (&). Putting & before something takes the address of that something (when that something has an address of course; something like &5 will not work since the constant 5 doesn't have an address in memory; it is not an lvalue). So &variable takes the address of the variable, and &arrayname[indexnumber] takes the address of an element of an array.
In our case, the first line will always print the same address value as the previous one, because &a[0] (the address of element number 0, i.e. the first element of the array) will always be the same as just a (the address of the array's first element). But &a[4] (the address of element number 4, i.e. the fifth element of the array) will be a bit higher. [*]
[*] The example output printed above on my system can be explained as follows: an int is 4 bytes on my system (this may be different on yours), and my %p prints out addresses as hexadecimal values (that may also be different on yours), so 00030770 (&a[4]) is 16 (10h) bytes past 00030760 (&a[0] or just a).

   p = a;

What's this ? We assign an array of int (a) to a pointer to int (p). Like we've said in our strings example, assigning an array using the = operator does not copy the actual elements of the array (that's why we needed the strcpy() function for strings). We've seen above that the arrayname a on its own returns the address of the first element of the array. So it is that address (of type pointer to int) that we assign to p, which is a pointer to int. That's why it works. The result now is that p contains the address of array a's first element; we therefore say that p now points to a.

   printf("the value of the pointer = %p\n", (void *)p);

We print the address value stored in the pointer, which is of course the address of the array.

   printf("the value the pointer points at = %d\n", *p);

Ah, another operator. The * is called the dereference operator [*]. To dereference a pointer means to take the value of the variable to which the pointer is pointing. So *p means the value of the int that p is pointing at (i.o.w. whose address value p contains).
[*] As seen above, this * is also used for defining pointers. You could also read the definition int *p; as "p dereferenced is int".
Since p points at the first element of the array, the value of element number 0 of the array will be printed: 1.

   p += 4;

This is equivalent to p = p + 4; (see 4.2. Operators and typecasts). But how can you calculate with a pointer ? Well, the address value contained in the pointer variable can be modified using the operators + and -. However, there's one important difference to calculating with normal integral values: adding 4 to the pointer as in this example does not necessarily add 4 to the address value; instead it moves the pointer 4 elements of the type it points at further (so if your ints are 4 bytes like mine, this will add 4*4=16 to your address value). [*]
[*] You can't do this with the generic pointers to void, because the compiler doesn't know how many bytes the elements you're pointing at are.
So in this case p will now no longer point to element number 0 of the array, but to element number 4.

   printf("the value of the pointer now = %p\n", (void *)p);
   printf("the value the pointer now points at = %d\n", *p);


Which is verified by the output of these printf() calls.
Note: you can make pointers constants, too, just like regular variables; but watch out:

int *p;              /* p itself and what p points to may be modified */
const int *p;        /* p itself may be modified; what p points to may not */
int * const p;       /* p itself may not be modified; what p points to may */
const int * const p; /* p itself nor what it points to may be modified */


Something else to watch out for is this:

char *a,b;

This defines a char pointer (a) and a char (b), not 2 char pointers! On the other hand:

typedef char *char_pointer;
char_pointer a,b;


Does define 2 char pointers.
There is also a special pointer value, which may be used for all pointer types, that is used to indicate an invalid pointer value. This is the NULL pointer. NULL is defined in stdio.h, stdlib.h, stddef.h and in other headers. You should use NULL only for pointer values (to indicate an invalid pointer), not for other types of variables (e.g. not for an integral 0 value). You can use a plain 0 instead of NULL as well for this purpose, but most people prefer using NULL as a stylistic hint. [*]
[*] You shouldn't worry about what pointer value NULL is exactly. At the source code level 0 will evaluate to a NULL pointer value, but this does definitely not mean that the actual NULL pointer value must be a value with all bits 0.
Pointers are also useful in simulating "call by reference", which means that a function can change the value of its parameters and these changes will be passed on to the caller as well. With normal parameters (other than arrays) this is not possible in C, because they are always passed as "call by value", which means that the function can only access a copy of the actual values, and this will have no effect on the calling code.
By using a parameter of type "pointer to type" instead of just "type" this is possible. The calling code must then insert the address operator & before the variable passed as the parameter. The function can then access and modify the value using the dereference operator (*). This is also referred to as "pass by pointer" and you should not confuse it with the call by reference that is supported in C++, Pascal etc. because they don't require the calling code to explicitly insert an address operator.
An example may shed some light on this:

void swapintegers(int a, int b)
{
   int temp;

   temp = a;
   a = b;
   b = temp;
}


You would expect this function to swap the two integer variables a and be that it is passed as parameters. It does that, but only within the function itself. If the caller calls this function, e.g. like this:

swapintegers(myvariable, yourvariable);

Then after the call, myvariable and yourvariable will not be swapped. The solution is to do it

Page : << Previous 11  Next >>