Table of Contents

stddef.h

size_t, NULL, offsetof — you have seen these in practically every C codebase. They all come from stddef.h, which defines the handful of fundamental types and macros that the rest of the standard library depends on. Most headers pull it in transitively, so you often get it without including it directly.

The main definitions:

#include <stddef.h>
 
size_t    n = sizeof(int);   // unsigned type for sizes and counts; result of sizeof
ptrdiff_t d = p2 - p1;      // signed type for pointer differences
wchar_t   wc = L'A';        // wide character type (platform-dependent width)
max_align_t;                 // type with the strictest alignment of any standard type
 
NULL                         // null pointer constant
offsetof(struct s, member)   // byte offset of member within struct s

size_t is the right type for array indices, loop counters over arrays, and anything produced by sizeof. It is unsigned and wide enough to hold the size of any object (32 bits on 32-bit platforms, 64 on 64-bit). Using int for array indices can produce sign-comparison warnings and wrong results when arrays exceed 2 GB.

ptrdiff_t is the type you get when subtracting two pointers within the same array. It is signed, so p2 - p1 can be negative if p2 precedes p1.

offsetof tells you where a struct member sits within the struct. It is used in serialisation, memory-mapped hardware register layouts, and the container_of pattern common in Linux kernel code:

struct point { int x; int y; };
offsetof(struct point, y)    // typically 4 (after the 4-byte x)

Practice

// compile: gcc -o deftest deftest.c
// run: ./deftest
// description: print sizes and offsets for a packed-ish struct
 
#include <stddef.h>
#include <stdio.h>
 
struct packet {
    uint8_t  type;
    uint16_t length;
    uint32_t checksum;
};
 
int main(void) {
    printf("sizeof(struct packet) = %zu\n", sizeof(struct packet));
    printf("offsetof type        = %zu\n", offsetof(struct packet, type));
    printf("offsetof length      = %zu\n", offsetof(struct packet, length));
    printf("offsetof checksum    = %zu\n", offsetof(struct packet, checksum));
    return 0;
}

The offsets will likely be 0, 2, and 4 due to alignment padding after type. Add __attribute__((packed)) and rerun — the offsets become 0, 1, 3, and the struct shrinks, at the cost of unaligned access for length and checksum.