Pointer Arithmetic in C

Pointer arithmetic represents one of the fundamental concepts of programming in C language. Through it, it is possible to exploit the power of pointers to the fullest.

In C language, only three arithmetic operations on a pointer are allowed:

  • Addition of an integer to a pointer;
  • Subtraction of an integer from a pointer;
  • Subtraction of a pointer from another pointer.

In this lesson, we will see how these operations work and we will also see how to exploit them to access the elements of an array.

Pointer Arithmetic

In previous lessons, we have seen two operations that can be performed on a pointer:

  • The assignment of an address to a pointer through the address operator &:

    int a = 10;
    int *p = &a;
    
  • Access to the pointed value through the indirection operator *:

    int a = 10;
    int *p = &a;
    int b = *p;
    

With these two basic operations, we managed to create aliases for variables and we managed to pass arguments to functions by reference.

In this lesson, we will see other types of operations that can be performed on a pointer and especially the close link that exists between pointers and arrays.

Previously, we mentioned the fact that a pointer can refer to the elements of an array. For example, suppose we have an array of 10 integers:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

We can define a pointer that refers to the element with index 5 of the array, for example, in this way:

int *p = &a[5];

What happens is shown in the figure:

Pointer pointing to the element with index 5 of the array
Picture 1: Pointer pointing to the element with index 5 of the array

From this moment on, using p we can modify the value of the element with index 5 of array a:

*p = 100;

In this way, array a will have the following content:

int a[10] = { 1, 2, 3, 4, 5, 100, 7, 8, 9, 10 };

The situation is shown in the figure:

Pointer pointing to the element with index 5 of the array
Picture 2: Pointer pointing to the element with index 5 of the array

Modifying the value of a single element of an array does not have great utility in itself. However, through pointer arithmetic it is possible to perform much more complex operations. Through it, it is possible to access the other elements of an array.

Before seeing how, we must study which arithmetic operations are allowed on a pointer:

  • Addition of an integer to a pointer;
  • Subtraction of an integer from a pointer;
  • Subtraction of a pointer from another pointer.

Only these three operations are allowed on a pointer in C language. Now we will see them in detail.

Addition of an Integer to a Pointer

Suppose we have an array of 10 integers and a pointer p to an integer:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *p;

We initialize the pointer p by making it point to the element with index 3 of array a:

p = &a[3];

So the initial situation will be the following:

Sum of a Pointer and an integer - Initial Situation
Picture 3: Sum of a Pointer and an integer - Initial Situation

If we add an integer n to the pointer p the result will be a pointer that refers to n elements after. In particular, if p points to the location a[3] and we add n to the pointer p, the result will be a pointer that points to the location a[3 + n]. Let's see with an example:

p = &a[3];
p = p + 2;

Now p points to the location a[3 + 2] that is, to the location a[5]. In this way, we can access the value of the element with index 5 of array a.

In plain words, the pointer has moved two locations forward. The final situation is shown in the figure:

Sum of a Pointer and an integer - Final Situation
Picture 4: Sum of a Pointer and an integer - Final Situation

However, this does not mean that 2 has been added to the address contained in the pointer. To better understand what happens, let's see an example:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int *p = &a[3];

/* Print the address contained in p to the screen */
printf("Address of p: %p\n", p);

/* Add 2 to the pointer p */
p = p + 2;

/* Print the address contained in p to the screen */
printf("Address of p: %p\n", p);

If we try to execute this code snippet on a 64-bit machine, what we can obtain is the following output:

Address of p: 0x7ffc0d1f678c
Address of p: 0x7ffc0d1f6794

In practice, before the addition operation, the pointer p pointed to the address 0x7ffc0d1f678c which is equal to 140720528648076. Subsequently, the pointer p points to the address 0x7ffc0d1f6794 which is equal to 140720528648084. In other words, the pointer p has moved 8 bytes and not two bytes as one might think. Why?

The reason lies in the fact that when an integer is added to a pointer, the result is a pointer that points to an address equal to the starting address plus the number of bytes occupied by the pointed type multiplied by the added integer.

Since p is a pointer to an integer and an integer in this case occupies 4 bytes, the result will be a pointer that points to an address equal to the starting address plus 4 multiplied by 2.

We can confirm everything with this example:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int *p = &a[3];

/* Get the address of p */
unsigned long int address_p = (long int) p;

/* Print the address contained in p to the screen */
printf("Address of p:                   %p\n", p);

/* Print the size of an integer */
printf("Size of an integer:          %d\n", sizeof(int));

/* Add 2 to the pointer p */
p = p + 2;

/* Print the address contained in p to the screen */
printf("Address of p:                   %p\n", p);

/*
* Print the result of the sum of the address of p
* with the size of an integer multiplied by 2
*/
printf("Address of p + 2 * sizeof(int): %lx\n",
        (address_p + 2 * sizeof(int)));

If we try to execute this code we obtain:

Address of p:                   0x7fffedb4e35c
Size of an integer:          4
Address of p:                   0x7fffedb4e364
Address of p + 2 * sizeof(int): 0x7fffedb4e364

This confirms what we obtained previously. The size of an integer is 4 bytes and the result of the sum of the address of p with the size of an integer multiplied by 2 is equal to the address of p after the addition operation.

Definition

Addition of an Integer to a Pointer

Adding an integer to a pointer means obtaining a pointer that points to an address equal to the starting address plus the number of bytes occupied by the pointed type multiplied by the added integer.

In formal terms, if p is a pointer to a type type:

type *p;

type is d bytes:

d = sizeof(type);

and n is an integer, then:

p = p + n;

is equivalent to:

\mbox{address}(p) = \mbox{address}(p) + n \cdot d

Subtraction of an Integer from a Pointer

Analogously to the case of addition, we can subtract an integer from a pointer. In this case, the result will be a pointer that points to an address equal to the starting address minus the number of bytes occupied by the pointed type multiplied by the subtracted integer.

Returning to the case of arrays, if we have an array a of 10 elements:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

and a pointer p that points to the element with index 3:

int *p = &a[3];

The initial situation is the following:

Difference between a Pointer and an integer - Initial Situation
Picture 5: Difference between a Pointer and an integer - Initial Situation

we can subtract 2 from the element pointed to by p:

p = p - 2;

In this way, p will point to the element with index 1. The final situation will be the following:

Difference between a Pointer and an integer - Final Situation
Picture 6: Difference between a Pointer and an integer - Final Situation

We can verify everything with this example:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int *p = &a[3];

/* Print the address contained in p to the screen */
printf("Address of p: %p\n", p);

/* Subtract 2 from the pointer p */
p = p - 2;

/* Print the address contained in p to the screen */
printf("Address of p: %p\n", p);

If we try to execute this code snippet on a 64-bit machine, what we can obtain is the following output:

Address of p: 0x7ffc0d1f678c
Address of p: 0x7ffc0d1f6774

As can be observed, after subtracting the integer 2 from the pointer p, it has moved 8 bytes backward.

Definition

Subtraction of an Integer from a Pointer

Subtracting an integer from a pointer means obtaining a pointer that points to an address equal to the starting address minus the number of bytes occupied by the pointed type multiplied by the subtracted integer.

In formal terms, if p is a pointer to a type type:

type *p;

type is d bytes:

d = sizeof(type);

and n is an integer, then:

p = p - n;

is equivalent to:

\mbox{address}(p) = \mbox{address}(p) - n \cdot d

Difference Between Two Pointers

The last arithmetic operation we can do with pointers is to calculate the difference between two pointers. This operation is only possible if the pointers point to the same data type. In this case, the result will be an integer that represents the number of elements that separate the two pointers.

Returning to the case of arrays, if we have an array a of 10 elements:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

and two pointers p and q that point to the elements with index 3 and 7 respectively:

int *p = &a[3];
int *q = &a[7];

we can calculate the difference between the two pointers:

int diff = q - p;

In this way, diff will be equal to 4. The two pointers are represented in the figure:

Difference between two pointers
Picture 7: Difference between two pointers

We can verify everything with this example:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int *p = &a[3];
int *q = &a[7];

/* Calculate the difference between the two pointers */
int diff = q - p;

/* Print the result to the screen */
printf("Difference between the two pointers: %d\n", diff);

If we try to execute this code snippet on a 64-bit machine, what we can obtain is the following output:

Difference between the two pointers: 4

As can be observed, the difference between the two pointers is equal to 4.

Definition

Difference Between Two Pointers

The difference between two pointers, as long as they point to the same data type, is an integer that represents the number of elements that separate the two pointers.

In other words, the result will be equal to:

\frac{\mbox{address}(q) - \mbox{address}(p)}{d}

where d is the number of bytes occupied by the pointed type.

Subtracting two pointers makes sense, as we have seen, if both point to the same data type. Furthermore, attention must be paid to the fact that the difference between two pointers can be positive or negative. In particular, if the first pointer is greater than the second, the difference will be positive, otherwise it will be negative.

Obviously, if two pointers point to different arrays, the result of the subtraction is not defined.

Comparison Between Pointers

A last type of operation that can be performed between pointers is comparison. In particular, it is possible to verify if two pointers point to the same address, or if the first pointer is greater or less than the second.

This is possible through the use of the relational operators >, <, >= and <= and through the use of the equality operators == and !=.

For example, if we have an array a of 10 elements:

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

and two pointers p and q that point to the elements with index 3 and 7 respectively:

int *p = &a[3];
int *q = &a[7];

we can verify if p is greater than q:

if (p > q) {
    printf("p is greater than q\n");
} else {
    printf("p is not greater than q\n");
}

or we can verify if p points to the same memory location as q:

if (p == q) {
    printf("p points to the same memory location as q\n");
} else {
    printf("p does not point to the same memory location as q\n");
}
Definition

Comparison Between Pointers

Between two pointers, it is possible to use the relational operators >, <, >= and <= and the equality operators == and != to verify if the two pointers point to the same address, or if the first pointer is greater or less than the second.

These operations are only valid if the two pointers point to the same data type.

In Summary

In this lesson, we have studied the arithmetic operations that can be performed with pointers. In particular, we have seen that it is possible to:

  • Add an integer to a pointer
  • Subtract an integer from a pointer
  • Calculate the difference between two pointers

Furthermore, we have seen that it is possible to perform comparison between pointers.

These operations will be fundamental for being able to work with arrays and strings.