to get at the values in the variables in the main program and modify them. That is, the function should be:

 

function swap(int *a , int *b);

 {

  int temp;

  temp = *a;

  *a   = *b;

  *b   = temp;

 }

 

 

Notice that now the two parameters a and b are pointers and the assignments that effect the swap have to use the de-reference operator to make sure that it is the values of the variables pointed at that are swapped. You should have no difficulty with:

temp = *a;

this just stores the value pointed at by a into temp. However,

*a = *b;

is a little more unusual in that it stores that value pointed at by b in place of the value pointed at by a. There is one final complication. When you use swap you have to remember to pass the addresses of the variables that you want to swap. That is not:

swap(a,b)

but

swap(&a,&b)

The rule is that whenever you want to pass a variable so that the function can modify its contents you have to pass it as an address. Equally the function has to be ready to accept an address and work with it. You can't take any old function and suddenly decide to pass it the address of a variable instead of its value. If you pass an address to a function that isn't expecting it the result is usually disaster and the same is true if you fail to pass an address to a function that is expecting one.

For example, calling swap as swap(a,b) instead of swap(&a,&b) will result in two arbitrary areas of memory being swapped over, usually with the result that the entire system, not just your program, crashes.

The need to pass an address to a function also explains the difference between the two I/O functions that we have been using since the beginning of this course. printf doesn't change the values of its parameters so it is called as printf("%d",a) but scanf does, because it is an input function, and so it is called as scanf("%d",&a).


Pointers And Arrays

In C there is a very close connection between pointers and arrays. In fact they are more or less one and the same thing! When you declare an array as:

int a[10];

you are in fact declaring a pointer a to the first element in the array. That is, a is exactly the same as &a[0]. The only difference between a and a pointer variable is that the array name is a constant pointer - you cannot change the location it points at. When you write an expression such as a[i] this is converted into a pointer expression that gives the value of the appropriate element. To be more precise, a[i] is exactly equivalent to *(a+i) i.e. the value pointed at by a + i . In the same way *(a+ 1) is the same as a[1] and so on.

Being able to add one to a pointer to get the next element of an array is a nice idea, but it does raise the question of what it means to add 'one' to a pointer. For example, in most implementations an int takes two memory locations and a float takes four. So if you declare an int array and add one to a pointer to it, then in fact the pointer will move on by two memory locations. However, if you declare a float array and add one to a pointer to it then the pointer has to move on by four memory locations. In other words, adding one to a pointer moves it on by an amount of storage depending on the type it is a pointer to.

This is, of course, precisely why you have to declare the type that the pointer is to point at! Only by knowing that a is a pointer to int and b is a pointer to float can the compiler figure out that

a + 1

means move the pointer on by two memory locations i.e. add 2, and

b + 1

means move the pointer on by four memory locations i.e. add 4. In practice you don't have to worry about how much storage a pointer's base type takes up. All you do need to remember is that pointer arithmetic works in units of the data type that the pointer points at. Notice that you can even use ++ and -- with a pointer, but not with an array name because this is a constant pointer and cannot be changed. So to summarise:

  1. An array's name is a constant pointer to the first element in the array that is a==&a[0] and *a==a[0].
  2. Array indexing is equivalent to pointer arithmetic - that is a+i=&a[i] and *(a+i)==a[i].

It is up to you whether you want to think about an array as an array or an area of storage associated with a constant pointer. The view of it as an array is the more sophisticated and the further away from the underlying way that the machine works. The view as a pointer and pointer arithmetic is more primitive and closer to the hardware. In most cases the distinction is irrelevant and purely a matter of taste.

One final point connected with both arrays and functions is that when you pass an entire array to a function then by default you pass a pointer. This allows you to write functions that process entire arrays without having to pass every single value stored in the array - just a pointer to the first element. However, it also temps you to write some very strange code unless you keep a clear head. Try the following - write a function that will fill an array with random values randdat(a,n) where a is the array and n is its size. Your first attempt might be something like:

 

void randdat(int *pa , int n)

  {

   for (pa = 0 ; pa < n ; pa++ ) *pa = rand()%n + 1;

  }

 

Well I hope your first attempt wouldn't be like this because it is wrong on a number of counts! The problem is that the idea of a pointer and the idea of an index have been confused. The pointer pa is supposed to point to the first element of the array, but the for loop sets it to zero and then increments it though a series of memory locations nowhere near the array. A lesser error is to suppose that n-1 is the correct final value of the array pointer! As before, you will be lucky if this program doesn't crash the system, let alone itself! The correct way of doing the job is to use a for loop to step from 0 to n-1, but to use pointer arithmetic to access the correct array element:

 

int randdat(int *pa , int n)

{

  int i;

  for ( i=0 ; i< n ; ++i)

   {

     *pa = rand()%n + 1;

     ++pa;

   }

}

 

Notice the way that the for loop looks just like the standard way of stepping through an array. If you want to make it look even more like indexing an array using a for loop you could write:

for(i=0 ; i

or even:

for(i=0 ; i

In other words, as long as you define pa as a pointer you can use array indexing notation with it and it looks as if you have actually passed an array. You can even declare a pointer variable using the notation:

int pa[];

that is, as an array with no size information. In this way the illusion of passing an array to a function is complete.