Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 11

Pointer Difference in Arrays

Recommended resources

Hello! Welcome to the fourth lesson in our module on Pointer Arithmetic and Arrays.

In our previous lesson, we established the fundamental equivalence between array subscripting (arr[i]) and pointer arithmetic (*(arr + i)). You learned how to use both styles to access and iterate through array elements, and we discussed how modern compilers often produce identical machine code for both, making readability the key factor in choosing which to use. We focused on pointer addition to move forward in an array.

Today, we'll explore the inverse operation: pointer subtraction. Our goal is to understand how to calculate the difference between two pointers that point to elements of the same array. This is a powerful technique for determining the number of elements in a range, calculating the length of a data segment, or finding an element's position relative to another—all common tasks when working with data buffers in systems programming.

What Does Pointer Subtraction Calculate?

When you add an integer to a pointer, the result is a new pointer, moved forward by that many elements. Logically, when you subtract one pointer from another, the result should represent the "distance" between them. But is this distance in bytes or in elements?

The C language defines this operation to give a result that is intuitive and highly useful: the number of elements separating the two pointers.

Let's watch a short video that illustrates this core concept.

Pointer Arithmetic (Subtraction)

This clip from Neso Academy clearly explains the result of subtracting two pointers that point into the same array. It introduces the idea that the result is simply the difference between their logical indices.

Please watch from 02:52 to 03:37. Focus on how the subtraction q - p directly yields the distance in terms of array elements, not memory addresses.

This concept is visualized very effectively in the following diagram.

Pointer Arithmetic (Subtraction) in C
This diagram shows two pointers, `p` and `q`, pointing to indices 2 and 6 of an integer array `a`. The subtraction `q - p` yields 4, which is the number of elements between them (6 - 2). The underlying memory addresses are also shown to illustrate the mechanism we'll discuss next.

The Mechanism: From Bytes to Elements

Your experience with debuggers and memory analysis tells you that pointer values are just memory addresses (like 0x1008 and 0x1024 in the diagram). A raw subtraction of these addresses would yield the difference in bytes (1024 - 1008 = 16 bytes). So how does the C compiler produce the element count (4)?

The compiler performs an implicit scaling operation. It takes the difference in memory addresses and automatically divides it by the size of the data type the pointers point to.

Formula: pointer2 - pointer1 is equivalent to (address_of(pointer2) - address_of(pointer1)) / sizeof(element_type)

The result of this operation is a signed integer type called ptrdiff_t, defined in <stddef.h>.

The next video segment explains this mechanism in detail.

Pointer Arithmetic (Subtraction)

Continuing with the Neso Academy video, this part breaks down the actual calculation, showing why a simple address subtraction is insufficient and how dividing by the element size gives the correct result.

Now watch from 03:37 to 06:14. Pay close attention to how the raw byte difference (16) is scaled by sizeof(int) (4 bytes) to arrive at the element count (4).


The Rules of Engagement: Ensuring Defined Behavior

Pointer subtraction is not a free-for-all. To ensure your code is correct, portable, and safe, you must follow two critical rules. Violating them results in undefined behavior, a concept you'll be very familiar with from functional safety standards like ISO 26262.

Rule 1: Pointers Must Belong to the Same Array Object
The C standard is very specific: you can only subtract two pointers if they both point to elements of the same array, or one past the last element of that array. Subtracting a pointer to an element in array_A from a pointer to an element in array_B is undefined. The compiler makes no guarantees about the relative memory layout of different variables.

Rule 2: Pointers Must Be of the Same Type
You cannot subtract an int* from a char*. The compiler enforces this because the scaling operation (/ sizeof(type)) would be ambiguous.

The following resource from the Software Engineering Institute at Carnegie Mellon University (SEI CERT) is a standard reference for safe C coding and states these rules clearly. Given your work in automotive software, adhering to such guidelines is paramount.

ARR36-C. Do not subtract or compare two pointers that do not refer to the same array

The SEI CERT C Coding Standard provides authoritative guidance on pointer operations. This section explains the rules for subtraction and comparison to avoid undefined behavior.

Read the first two paragraphs under the main heading. Focus on the C Standard's requirements for pointer subtraction and the explicit mention of 'undefined behavior' if those rules are broken.


Practical Applications

Now, let's see how this is used in real code.

1. Calculating Remaining Space in a Buffer

A common task is to determine how much space is left in a buffer that is being filled. Pointer subtraction is perfect for this.

Imagine you have a buffer and a pointer that tracks the next available slot.

#include <stdio.h>
#define BUFFER_SIZE 10

int main(void) {
    int buffer[BUFFER_SIZE];
    int *next_available = buffer + 4; // Let's say we've filled 4 elements

    // Calculate the total capacity
    ptrdiff_t capacity = (buffer + BUFFER_SIZE) - buffer;

    // Calculate the number of elements used
    ptrdiff_t used = next_available - buffer;

    // Calculate the number of elements remaining
    ptrdiff_t remaining = (buffer + BUFFER_SIZE) - next_available;

    printf("Capacity: %td elements\n", capacity);    // Prints 10
    printf("Used: %td elements\n", used);          // Prints 4
    printf("Remaining: %td elements\n", remaining);  // Prints 6

    return 0;
}

In this example, buffer + BUFFER_SIZE gives a pointer to one memory location past the end of the array. This is a valid address to use in pointer arithmetic, though it must not be dereferenced.

2. Implementing a String Length Function

A classic example that beautifully demonstrates pointer subtraction is re-implementing the standard library function strlen. The logic is simple: find the start of the string, find the end of the string (the terminating \0 character), and subtract the pointers.

The following resource provides an elegant implementation.

11 Pointers II: Arithmetic

Beej's Guide to C Programming offers a wealth of practical examples. This section demonstrates how to write a custom strlen() function using pointer subtraction.

Read the subsection that begins 'But we can also subtract two pointers...'. Study the my_strlen function to see how a while loop finds the end of the string, and a single subtraction then calculates its length.

Here is the code from the guide for your reference:

int my_strlen(char *s)
{
    // Start scanning from the beginning of the string
    char *p = s;

    // Scan until we find the NUL character
    while (*p != '\0')
        p++;

    // Return the difference in pointers
    return p - s;
}

This is a concise and efficient implementation that showcases the power of pointer arithmetic.

Test your understanding!

You are given an array of sensor readings and a pointer that indicates a specific reading of interest (e.g., a peak value).

#include <stdio.h>
#include <stddef.h>

#define NUM_READINGS 20

int main(void) {
    int sensor_data[NUM_READINGS] = { /* ... some data ... */ };

    // A pointer to the 15th element (index 14)
    int *peak_reading_ptr = &sensor_data[14]; 

    // Your code here
    ptrdiff_t elements_before;
    ptrdiff_t elements_after;
    
    // Calculate the number of elements before and after the peak_reading_ptr
    // and store them in the variables above.
    
    printf("Elements before peak: %td\n", elements_before);
    printf("Elements after peak: %td\n", elements_after);

    return 0;
}

Complete the code snippet to correctly calculate elements_before and elements_after using pointer subtraction. The "after" count should not include the peak element itself.

Show answer
// Calculate the number of elements before the peak
elements_before = peak_reading_ptr - sensor_data;

// Calculate the number of elements after the peak
// (pointer to one-past-the-end) - (pointer to one-past-the-peak)
elements_after = (sensor_data + NUM_READINGS) - (peak_reading_ptr + 1);

Explanation:

  • elements_before: Subtracting the pointer to the start of the array (sensor_data) from the pointer to the peak element gives the number of elements from the start up to (but not including) the peak. Since peak_reading_ptr points to index 14, this is 14 - 0 = 14.
  • elements_after: To find the elements after the peak, we subtract a pointer to the element following the peak (peak_reading_ptr + 1) from a pointer to one position past the end of the entire array (sensor_data + NUM_READINGS). This gives 20 - 15 = 5.

Conclusion

Today we've added a crucial tool to your pointer arsenal: subtraction. You are now equipped to calculate the distance between elements within a contiguous block of memory, a skill essential for buffer management, data parsing, and algorithm implementation.

Key Takeaways:

  • Subtracting two pointers (ptr2 - ptr1) yields the number of elements between them, not the byte difference.
  • The compiler achieves this by calculating (addr2 - addr1) / sizeof(element_type). The result is of type ptrdiff_t.
  • This operation is only valid for pointers of the same type that point into the same array object (or one past its end). Any other use is undefined behavior.
  • Pointer subtraction is highly practical for calculating sizes, offsets, and remaining capacity in buffers.

In our next lesson, we will focus on different patterns for traversing arrays using pointers, building directly on the arithmetic operations we've now covered.