How Big Is A Pointer In C

8 min read

How big is a pointer in C is a question that appears frequently when developers move code between different hardware platforms or when they try to predict memory usage. The size of a pointer is not a fixed constant in the C language; it depends on the underlying architecture, the data model chosen by the compiler, and sometimes the operating system’s ABI. Understanding these factors helps you write portable code, avoid subtle bugs, and make informed decisions when dealing with low‑level memory manipulation The details matter here..

What Is a Pointer in C?

A pointer is a variable that stores a memory address. Plus, rather than holding a value directly, it “points” to the location where another object resides. Because a pointer’s job is to refer to a location in the process’s address space, its size must be large enough to hold any valid address that the CPU can generate.

Pointer semantics are defined by the C standard, but the actual number of bytes used to represent that address is left to the implementation. This deliberate flexibility allows C to run on everything from 8‑bit microcontrollers to 64‑bit supercomputers.

Factors That Determine Pointer Size

Factor How It Influences Size Typical Values
CPU address bus width The number of bits the processor can place on its address lines determines the maximum addressable memory. 16 bits → 2 bytes, 32 bits → 4 bytes, 64 bits → 8 bytes
Data model (LP32, ILP32, LLP64, LP64) Defines the sizes of int, long, and pointer types relative to each other. On the flip side, ILP32 (int, long, pointer = 32 bits) on Windows 32‑bit; LP64 (long & pointer = 64 bits, int = 32 bits) on Linux/macOS 64‑bit
Compiler ABI Some compilers offer switches to generate code for a different memory model (e. Still, g. , -m32 on gcc to produce 32‑bit binaries on a 64‑bit host). Can force 4‑byte pointers even on a 64‑bit CPU
Operating system The OS may expose different ABIs for user‑space vs. kernel‑space, or support multiple execution environments (e.g., x86‑64 with ILP32 via x32 ABI). But x32 ABI uses 32‑bit pointers while still using 64‑bit registers
Memory segmentation (rare in modern C) In segmented architectures (e. g., old 8086 real mode), a pointer may consist of a segment and an offset, making its effective size larger than the offset alone.

Because of these variables, the safest way to know the pointer size for your target is to query it at compile time with the sizeof operator.

Typical Pointer Sizes on Common Platforms

Platform / Architecture Data Model Pointer Size (sizeof(void*))
8‑bit AVR (e., ATmega328P) AVR (near/far) 2 bytes (near) or 3 bytes (far)
16‑bit MSP430 TI’s memory model 2 bytes
32‑bit ARM Cortex‑M0/M3/M4 ILP32 4 bytes
32‑bit x86 (Windows/Linux) ILP32 4 bytes
64‑bit x86‑64 (Linux/macOS) LP64 8 bytes
64‑bit Windows (Win64) LLP64 8 bytes
64‑bit ARMv8 (AArch64) LP64 8 bytes
x32 ABI (Linux on x86‑64) ILP32 4 bytes (despite 64‑bit registers)
DSPs with Harvard architecture Often separate code/data pointers May differ (e.Here's the thing — g. g.

Note: The size of a pointer to a function can sometimes differ from the size of a pointer to an object, especially on architectures with separate code and data address spaces (Harvard architecture). In most von‑Neumann machines (the vast majority of desktops, servers, and smartphones) they are the same And it works..

Pointer to Function vs. Pointer to Data

While the C standard treats all object pointers as interchangeable via void*, function pointers have their own type. On many platforms they share the same representation, but there are exceptions:

  • Harvard architecture (e.g., some microcontrollers) stores program memory in a separate address space, so a function pointer may need more bits than a data pointer.
  • Pointers to member functions in C++ (not relevant to pure C) can be larger due to the need to encode virtual base offsets.

For plain C, you can safely assume sizeof(void (*)(void)) == sizeof(void*) unless you are targeting a known exotic architecture Worth keeping that in mind. That's the whole idea..

The Role of void* and Generic Pointers

A void* is a generic pointer that can hold the address of any object type. Its size is the same as any other object pointer because it must be capable of representing any valid address. Converting between void* and other pointer types does not change the representation; only the type information attached by the compiler changes.

#include 

int main(void) {
    int   i = 42;
    double d = 3.14;
    void *p;

    p = &i;          // p now holds address of an int
    printf("p points to int: %d\n", *(int *)p);

    p = &d;          // reuse same pointer for a double
    printf("p points to double: %f\n", *(double *)p);

    printf("Size of void* = %zu bytes\n", sizeof(void*));
    return 0;
}

Running this on a typical 64‑bit Linux machine prints:

Size of void* = 8 bytes

How to Determine Pointer Size Portably

The most reliable method is to use the sizeof operator at compile time:

#include 
#include 

int main(void) {
    printf("Pointer size: %zu byte(s)\n", sizeof(void*));
    return 0;
}

If you need to know the size of a pointer to

a specific type—say, a pointer to struct node—simply replace void* with struct node* in the sizeof expression. The result is a compile‑time constant of type size_t, so it can be used in array dimensions, static assertions, or preprocessor conditionals (via a helper macro) without any runtime overhead Not complicated — just consistent..

For cases where you need to store a pointer in an integer variable—such as when implementing a custom allocator, a hash table keyed by address, or interfacing with hardware registers—the standard headers <stdint.h> and <inttypes.h> provide the exact‑width integer types uintptr_t and intptr_t Practical, not theoretical..

#include 
#include 

void *ptr = (void *)0xDEADBEEFCAFEBABEL;
uintptr_t addr = (uintptr_t)ptr;          // pointer → integer
void *restored = (void *)addr;            // integer → pointer
printf("Original: %p, Restored: %p\n", ptr, restored);

Using uintptr_t (or intptr_t for signed arithmetic) avoids the undefined behavior that results from casting pointers to long, unsigned long, or long long on platforms where those types are narrower than a pointer.

Pointer Arithmetic and Alignment

Pointer size alone does not dictate how pointer arithmetic behaves. Even so, the C standard defines addition and subtraction in terms of the pointed‑to type: ptr + n advances the address by n * sizeof(*ptr) bytes. This means the same numeric pointer value can refer to different objects depending on the type used to dereference it.

This is where a lot of people lose the thread.

Alignment requirements further constrain usable addresses. On most architectures, a pointer to a type with alignment A must be a multiple of A. Consider this: violating this—e. So g. , by casting a char* at an odd address to int*—can trigger hardware faults (SIGBUS on POSIX, STATUS_DATATYPE_MISALIGNMENT on Windows) or, on more forgiving CPUs, severe performance penalties.

Honestly, this part trips people up more than it should Worth keeping that in mind..

#include 
#include 

struct example { char c; double d; };  // likely 8‑byte alignment because of double

int main(void) {
    printf("Alignment of struct example: %zu\n", alignof(struct example));
    printf("Alignment of max_align_t:   %zu\n", alignof(max_align_t));
    return 0;
}

max_align_t is a type whose alignment is at least as strict as any standard type, making it useful for implementing generic memory pools No workaround needed..

Common Portability Pitfalls

  1. Assuming sizeof(void*) == sizeof(int) – True only on ILP32 (32‑bit) data models. On LP64 and LLP64, pointers are 8 bytes while int remains 4 bytes.
  2. Printing pointers with %x or %lx – Always use %p with a void* cast (printf("%p", (void*)ptr);). The %p format expects a void* and prints an implementation‑defined representation (usually hexadecimal with a 0x prefix).
  3. Storing pointers in long for serialization – Use uintptr_t and the PRIuPTR/PRIxPTR macros from <inttypes.h> for portable binary or text serialization.
  4. Ignoring the x32 ABI – Linux’s x32 ABI runs 64‑bit code with 32‑bit pointers (ILP32). Code that hard‑codes pointer width to 8 bytes on x86‑64 will break there.

Summary

Pointer size is a direct consequence of the platform’s data model (ILP32, LP64, LLP64, etc.) and, in rare cases, the underlying memory architecture (Harvard vs. von Neumann) Easy to understand, harder to ignore..

  • Query sizes with sizeof(void*) or sizeof(T*) rather than assuming fixed widths.
  • Use uintptr_t/intptr_t for integer‑pointer conversions.
  • Respect alignment via alignof/_Alignof and max_align_t.
  • Format pointers exclusively with %p.

By adhering to these practices, your C programs will behave correctly across 32‑bit and 64‑bit systems, embedded microcontrollers, and emerging architectures alike—without a single #ifdef checking for pointer width Surprisingly effective..

Don't Stop

What's New

More Along These Lines

Good Company for This Post

Thank you for reading about How Big Is A Pointer In C. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home