C Language

To share this page click on the buttons below;

Pointers & Array

When we talked about arrays I briefly mentioned that arrays are pointer. This chapter will deal with that topic along with another interesting concept: the pointers arithmetic.

Arrays are just a collection of variables of a certain type: an array of 10 integers is a set of 10 integer variables packed together. Those variables are put one after other in memory. The variable name for the whole array is the address of the first element of the array. There you are: being an address is a pointer.

Any variable (not necessary an an array) is the symbolic name of the address where the variable is placed in memory (and in fact you can always get the address of a variable using the operator &), but in case of an array variable this connection is much stronger to the point that you must avoid the use of the operator & to get the address of the array variable (arrays are pointers so no need for for the & operator).

Why this? Well because a pointer to the first element of an array is just what you need to access all the elements of an array. Arrays are always stored in memory in long contiguous block of memory (no holes) one element after another in an ordered way from the element 0 till the last element of the array. Once you have the pointer to the first element you can access the n-th element of the array by "moving" the pointer to the address of the n-th element: that address can be easily calculated because it will be the starting address plus n times the dimension of the type the array is holding (due to the way the array is stored in memory).

Luckily you do not have to make the calculation yourself, you can use the pointer arithmetic. When you sum 1 to a pointer you get a new location of memory, depending on the size of the type the pointer is pointing to. That means that if you have a pointer to an integer (which is 4 bytes big) when you sum 1 to the pointer you get the address of integer plus 4 bytes because that is the next location of an integer variable. The very same happens for a pointer to a double variable (which is 8 bytes big): when you sum 1 to a double pointer you actually increment the address held by the pointer of 8 bytes.

This behavior is consistent along all the C: if you have a pointer to a structure which is 56 bytes wide, by incrementing of 1 that pointer you move the address of 56 bytes.

Of course you can increment or decrement a pointer not just by one but by a whatever integer you want and the behavior is always consistent.

Since the array is the pointer to the first element of an array, by adding for example 4 to this pointer you get the element 4 of the array, which (pay attention) is the fifth element of the array, because indexes start from 0. You can now access the value held at this location using the * operator in this way: *(ar + 4).

The aim of the following image is to show in a graphical way the previous concept:

Visual of pointer arithmetic

Array as function argument

In case of a function used to operate on an array the argument used to pass the array can simply be a pointer. The example here below will show this concept:

#include <stdio.h>

#define ARRAY_DIM 10

void increment_all_element(int *a, int dim);
void increment_all_element_v1(int a[], int dim);

void increment_all_element(int *a, int dim) {
   int i = 0;

   for(i = 0; i < dim; i++) {
      (*(a + i))++;
   }
}

void increment_all_element_v1(int a[], int dim) {
   int i = 0;

   for(i = 0; i < dim; i++) {
      (*(a + i))++;
   }
}

int main(void) {
   int ar[ARRAY_DIM] = {1,2,3,4,5,6,7,8,9,10};
   int i = 0;

   printf("\nPrinting the elements of the array:\n");
   for(i = 0; i < ARRAY_DIM; i++) {
      printf("   - ar[%i] = %i\n", i, ar[i]);
   }

   /* changing the value of the FIFTH element via pointer */
   *(ar + 4) = 32;

   printf("\nPrinting again the elements of the array:\n");
   for(i = 0; i < ARRAY_DIM; i++) {
      printf("   - ar[%i] = %i\n", i, ar[i]);
   }

   printf("\nCalling increment_all_element on the array\n");

   increment_all_element(ar, ARRAY_DIM);

   printf("\nAnd... printing again the elements of the array:\n");
   for(i = 0; i < ARRAY_DIM; i++) {
      printf("   - ar[%i] = %i\n", i, ar[i]);
   }

   increment_all_element_v1(ar, ARRAY_DIM);

   printf("\nAnd... printing again the elements of the array:\n");
   for(i = 0; i < ARRAY_DIM; i++) {
      printf("   - ar[%i] = %i\n", i, ar[i]);
   }

   return 0;
}

Here below there are the points you should pay attention to:

  • the function increment_all_element shows the most common way to declare a function able to operate on an array: an argument is a pointer to the array type and an argument is an integer for the dimension of the array
  • when the function increment_all_element is called there is no need for & operator, because the array itself is a pointer
  • increment_all_element_v1 shows another possible declaration of a function able to operate on an array, it uses the usual array square brackets. I have to say that (in my experience) this kind of notation is not so common (at least among C programmers, other programmers that come from different languages might find it more familiar)
  • line *(ar + 4) = 32; shows another possibility to access an element of an array using the pointer notation instead of indexing the array (that line is equivalent to ar[4] = 32;
  • the way each element of the array is incremented into the 2 functions (increment_all_element and increment_all_element_v1) needs probably a short clarification: they increment the i+1th element of the array by one, in fact (a + i) "moves" the pointer to the correct address, the * operator access the variable located at this location which is then incremented using the operator ++.

This brief example also introduces another concept which is not strictly related to pointers: the use of a preprocessor macro to define a constant. This is done at line 4: in this line the ARRAY_DIM symbolic name is "defined" as 10. This means that during "compilation" (I used quotation mark because this is not really correct) every time ARRAY_DIM is found, it is replaced by 10. This is just a literal substitution.

This technique is just one of the possibility offered by the C preprocessor (we will see that later on) and it is extremely useful because you can use one label to indicate a constant value. The first advantage is that the label can make the code more readable, but the important thing is that if you need to make a change (for example you need a bigger array) you have to simply modify 1 row and that change will be reflected wherever is necessary (wherever the symbolic name is used).

The use of preprocessor macro is powerful and can be applied to solve a much wider range of problems, although the correct use of the preprocessor macro can be difficult and with mysterious side effects (we will see).

To share this page click on the buttons below;