Understanding C Pointer Types and the Const Qualifier

The Significance of Pointer Data Types

In C programming, the type of a pointer does more than just specify what kind of data it points to; it defines the pointer's behavior during memory access and arithmetic operations.

1. Memory Access and Dereferencing

The pointer type determines the "step size" or the number of bytes the CPU accesses when the pointer is dereferenced. Consider the following comparison:

// Scenario A: Using an integer pointer
#include <stdio.h>

int main() {
    unsigned int data = 0x12345678;
    unsigned int *p_int = &data;
    *p_int = 0; // Affects all 4 bytes of the integer
    return 0;
}

// Scenario B: Using a character pointer
#include <stdio.h>

int main() {
    unsigned int data = 0x12345678;
    unsigned char *p_char = (unsigned char *)&data;
    *p_char = 0; // Affects only the first byte (usually 0x78 on little-endian)
    return 0;
}

The primary takeaway is that the pointer type dictates the scope of the operation. Common byte sizes for types include:

  • char*: 1 byte
  • short*: 2 bytes
  • int* / float* / long*: Typically 4 bytes
  • double* / long long*: 8 bytes

2. Pointer Arithmetic

When you add an integer to a pointer, the actual memory address increases by sizeof(type) * integer. This ensures that the pointer moves to the next valid element of that specific type.

#include <stdio.h>

int main() {
    int value = 100;
    int *ptr_i = &value;
    char *ptr_c = (char *)&value;

    printf("Base address: %p\n", ptr_i);
    printf("int pointer + 1: %p\n", ptr_i + 1);  // Increases by 4 bytes
    printf("char pointer + 1: %p\n", ptr_c + 1); // Increases by 1 byte
    
    return 0;
}

This behavior is fundamental for iterating through arrays. For example, incrementing a char* moves through a byte-aray one index at a time:

#include <stdio.h>

int main() {
    char letters[] = {'W', 'X', 'Y', 'Z'};
    char *cursor = letters;

    for (int i = 0; i < 4; i++) {
        printf("Value at %p: %c\n", cursor, *cursor);
        cursor++; // Moves exactly 1 byte
    }
    return 0;
}

3. The Generic void* Pointer

The void* type is a "generic" pointer. It can store the address of any data type without causing type-mismatch warnings. However, because it has no associated data type, you cannot dereference it or perform arithmetic directly without casting it first.

#include <stdio.h>

int main() {
    int num = 50;
    void *generic_ptr = &num;

    // Error: *generic_ptr = 10; (Compiler doesn't know the size)
    // Correct way:
    *(int *)generic_ptr = 25; 
    
    return 0;
}

Using the const Qualifier with Pointers

1. Protecting Variables

The const keyword locks a variable's value, making it read-only from the perspective of its identifier. However, one could technically circumvent this protection by using a pointer to the variable's address, though this is generally discouraged as it breaks the contract of the const keyword.

#include <stdio.h>

int main() {
    const int limit = 100;
    // limit = 200; // This would cause a compilation error
    
    int *hack = (int *)&limit;
    *hack = 200; // Bypassing the const restriction via pointer
    printf("limit: %d\n", limit);
    return 0;
}

2. Pointer-Specific const Variations

The placement of const in a pointer declaration determines what exactly is being protected.

Case A: Constant Data (const int *p)

The pointer can be redirected to differant addresses, but the data it points to cannot be modified through this pointer.

void example_const_data() {
    int x = 10, y = 20;
    const int *ptr = &x;
    
    // *ptr = 15; // Error: Data is read-only
    ptr = &y;     // Success: Pointer itself can change
}

Case B: Constant Pointer (int * const p)

The pointer is locked to a specifci memory address, but the data at that address can be modified.

void example_const_pointer() {
    int x = 10, y = 20;
    int * const ptr = &x;
    
    *ptr = 15;    // Success: Data can be modified
    // ptr = &y;  // Error: Pointer address is fixed
}

Case C: Constant Data and Constant Pointer (const int * const p)

Both the address stored in the pointer and the data at that address are immutable.

void example_totally_const() {
    int x = 10;
    const int * const ptr = &x;
    
    // *ptr = 15; // Error
    // ptr = &y;  // Error
}

Tags: C pointers Memory Management Const Qualifier programming fundamentals

Posted on Fri, 19 Jun 2026 16:19:41 +0000 by paradigmapc