Skip to Content
Course content

111: Essential Types with stddef.h: size_t, ptrdiff_t, NULL

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I want to show you a snippet of code that looks perfectly fine at first glance. It's a common pattern: we have a buffer of data, and we want to iterate through it backward to perform some operation. Take a look at this:

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

void print_backward(const char *str, size_t len) {
    for (size_t i = len - 1; i >= 0; i--) {
        printf("%c", str[i]);
    }
    printf("\n");
}

int main() {
    const char *msg = "Hello C";
    print_backward(msg, 7);
    return 0;
}

If you run this, you aren't going to see "C olleH". Instead, you're going to get a crash or an infinite loop that prints garbage until the program dies. Why? Because size_t is an unsigned type. When i is 0 and the loop executes i--, it doesn't become -1. It wraps around to the largest possible value a size_t can hold. Since that huge number is definitely greater than or equal to 0, the loop never ends.

The Danger of Unsigned Wrap-around

This is the "gotcha" of size_t. We use it for sizes and indices because a memory address or a buffer length can't be negative, and we want to be able to address as much memory as the CPU architecture allows. If you used a signed int, you'd limit yourself to 2GB of addressable space on many 64-bit systems, which is a waste of a modern processor.

The fix isn't to go back to using int. The fix is to change how you think about the loop or use the correct type for the distance. If you must go backward, you can use a while loop or a slightly more clever for loop:

for (size_t i = len; i > 0; i--) {
    printf("%c", str[i - 1]);
}

By checking i > 0 and accessing i - 1, we avoid the underflow entirely. This is the "professional" way to handle unsigned indices.

Measuring Memory Gaps with ptrdiff_t

Sometimes, you aren't just counting indices; you're calculating the distance between two pointers. This is where ptrdiff_t comes in. While size_t is for sizes, ptrdiff_t is for differences.

If you subtract one pointer from another, the result could be negative (if the first pointer comes before the second). ptrdiff_t is a signed integer type specifically designed to hold this result. I've seen developers try to cast pointer subtraction to size_t, and that's a recipe for disaster the moment your pointers are out of order.

char buffer[100];
char *start = &buffer[0];
char *end = &buffer[20];

ptrdiff_t diff = end - start; // Result: 20
ptrdiff_t reverse_diff = start - end; // Result: -20

Use size_t when you're talking about "How big is this?" or "Which index am I at?" Use ptrdiff_t when you're asking "How far apart are these two memory locations?"

The Standard Way to say "Nothing"

Finally, let's talk about NULL. You've likely used it, but it's important to understand that it's defined in stddef.h (among other places). In C, NULL is a macro that expands to a null pointer constant.

I often see beginners use 0 or '\0' interchangeably with NULL. While the compiler often lets you get away with it, using NULL explicitly tells anyone reading your code: "I am dealing with a pointer here, not an integer or a character." It's about intent. When you check if (ptr == NULL), you are making a statement about the state of a memory address, not a numeric value.

  • size_t: Unsigned. Use for array indexing, sizeof results, and loop counters that only go up.
  • ptrdiff_t: Signed. Use for the result of pointer subtraction.
  • NULL: A constant representing a pointer that points to nothing.



📋 Practical Task

Measuring Buffer Offsets with ptrdiff_t

Your task is to write a function that calculates the distance between a "cursor" pointer and the start of a buffer. However, the cursor might be moved behind the start of the buffer (simulating an underflow or a relative offset).

Create a program that does the following:

  • Defines a char array of 50 elements.
  • Creates a pointer cursor that points to the 10th element.
  • Calculates the distance from the start of the array to the cursor using ptrdiff_t and prints it.
  • Moves the cursor to point to the 5th element.
  • Calculates the distance from the 10th element to the new cursor position using ptrdiff_t and prints it (this should be a negative number).
  • Includes a check to see if the cursor is NULL before performing the calculations to ensure the program is robust.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.