Correctly Printing Memory Addresses in C: The %p Format Specifier and void* Pointer Conversion

Dec 03, 2025 · Programming · 11 views · 7.8

Keywords: C language | pointers | memory address | printf | format specifier

Abstract: This article provides an in-depth exploration of the correct method for printing memory addresses in C using the printf function. Through analysis of a common compilation warning case, it explains why using the %x format specifier for pointer addresses leads to undefined behavior, and details the proper usage of the %p format specifier as defined in the C standard. The article emphasizes the importance of casting pointers to void* type, particularly for type safety considerations in variadic functions, while discussing risks associated with format specifier mismatches. Clear technical guidance is provided through code examples and standard references.

Problem Context and Common Error

In C programming, developers often need to examine memory addresses of variables for debugging or understanding memory layout. A typical erroneous example is:

#include <stdio.h>

int main() {
    int A = -73;
    printf("variable A is at address: %08x\n", &A);
    return 0;
}

When compiled with GCC, this produces a warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’. This occurs because the %x format specifier expects an unsigned int argument, while &A is an int * pointer. Such format specifier mismatches lead to undefined behavior, potentially causing program crashes or incorrect output.

Correct Approach: Using the %p Format Specifier

The C language standard provides the dedicated format specifier %p for printing pointer addresses. The correct code should be:

printf("variable A is at address: %p\n", (void*)&A);

Two critical points are involved:

  1. Using the %p format specifier: %p is specifically designed for pointers, converting pointer values to printable character sequences in an implementation-defined manner.
  2. Casting to void* type: The C standard requires that arguments to %p be of type void*. In variadic functions like printf, no implicit conversion from T* to void* occurs, necessitating explicit casting.

Technical Principles and Standard Basis

According to Section 7.21.6 (Formatted input/output functions) of the C11 standard:

p The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.

This means:

In variadic functions like printf, argument type checking occurs at runtime, with compilers only able to issue warnings. Passing arguments of incorrect types results in undefined behavior, which must be strictly avoided in C programming.

Complete Example and Best Practices

The following complete example demonstrates how to correctly print memory addresses of various variable types:

#include <stdio.h>
#include <string.h>

int main() {
    char string[10] = "sample";
    int A = -73;
    unsigned int B = 31337;
    
    // Printing variable values
    printf("[A] Dec: %d, Hex: %x, Unsigned: %u\n", A, A, A);
    printf("[B] Dec: %d, Hex: %x, Unsigned: %u\n", B, B, B);
    
    // Correctly printing memory addresses
    printf("Address of A: %p\n", (void*)&A);
    printf("Address of B: %p\n", (void*)&B);
    printf("Address of string: %p\n", (void*)string);
    printf("Address of string[0]: %p\n", (void*)&string[0]);
    
    return 0;
}

Best practice recommendations:

  1. Always use the %p format specifier for printing pointer addresses
  2. Explicitly cast pointers to (void*) before passing to printf
  3. Pay attention to compiler warnings, particularly those regarding format specifiers
  4. Understand that different platforms may display addresses in varying formats (with or without 0x prefix)

Related Concept Extensions

Understanding memory address printing requires mastery of these related concepts:

By correctly using the %p format specifier with void* conversion, developers can safely and portably print memory addresses, which is crucial for debugging, memory analysis, and low-level programming.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.