Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Memory Addressing and Pointer Fundamentals in C

Tech 2

Memory addresses serve as unique identifiers for data storage locations in RAM. In C programming, a pointer represents a variable specifically designed to contain these memory addresses, enabling indirect data manipulation.

Address Retrieval and Storage

When declaring a varible, the compiler allocates contiguous memory bytes to hold its value. The address-of operator & retrieves the starting address of this allocated region.

#include <stdio.h>

int main(void) {
    int counter = 100;
    printf("Address: %p\n", (void*)&counter);
    return 0;
}

The & operator yields the lowest address among the bytes occupied by the operand. For multi-byte types like integers, the system utilizes this base address combined with type information to access the complete data range.

Pointer Declaration and Indirection

Pointer variables require explicit type declarations that match the data they reference:

double salary = 5000.50;
double *salary_ptr = &salary;

The asterisk in the declaration double *salary_ptr establishes the variable as a pointer, while the preceding type specifier double indicates the type of data residing at the target adress. This type association determines memory access boundaries during dereferencing operations.

The indirection operator * accesses or modifies values at the stored address:

*salary_ptr = 5500.75;  // Modifies the original variable

Memory Access Patterns and Type Significance

Pointer types govern byte-level access permissions. Consider the following contrast:

unsigned int flags = 0x12345678;
unsigned int *int_view = &flags;
*int_view = 0;  // Clears all 4 bytes (on typical 32-bit systems)

Versus narrow access:

unsigned int flags = 0x12345678;
unsigned char *byte_view = (unsigned char*)&flags;
*byte_view = 0;  // Only modifies the least significant byte: 0x12345600

This demonstrates how type casting alters the memory footprint of write operations. Character pointers provide single-byte granularity, while integer pointers operate on word-sized chunks.

Generic Pointers

The void* type represents a generic pointer capable of storing any address without type enforcement. However, arithmetic operations and dereferencing require explicit casting to a concrete type:

void *generic = &flags;
// *generic = 5;  // Invalid: cannot dereference void*
*(unsigned int*)generic = 5;  // Valid with cast

Pointer Arithmetic

Arithmetic operations on pointers scale according to the underlying type size. Incrementing an integer pointer advances the address by sizeof(int) bytes:

int dataset[] = {10, 20, 30, 40, 50};
int *cursor = dataset;
size_t elements = sizeof(dataset) / sizeof(dataset[0]);

for (size_t i = 0; i < elements; i++) {
    printf("%d ", *cursor);
    cursor++;  // Advances to next integer element
}

Subtracting two pointers yields the element count between their addresses, useful for computing offsets:

ptrdiff_t span = &dataset[4] - &dataset[0];  // Results in 4

Relational operators compare memory locations:

int *boundary = dataset + elements;
while (cursor < boundary) {
    printf("%d ", *cursor++);
}

Undefined Behavior and Memory Safety

Dangling pointers create undefined behavior through several mechanisms:

Uninitialized Storage:

int *uninitialized;
*uninitialized = 10;  // Dangerous: random memory target

Bounds Violation:

int buffer[5];
int *iter = buffer;
for (int i = 0; i <= 5; i++) {  // Erroneous: iter exceeds allocation
    *iter++ = i;
}

Stack Lifetime Violation:

int* create_dangling(void) {
    int temporary = 99;
    return &temporary;  // Returns address of expiring stack frame
}

Immutable References with const

The const qualifier restricts modification capabilities. When applied to the pointed-to data:

const int *read_only;      // Data immutable, pointer mutable
int const *alternate_syntax;  // Equivalent to above

When applied to the pointer itself:

int *const fixed_address;  // Pointer immutable, data mutable

For complete immutability:

const int *const immutable_both;

This distinction enables fine-grained control over memory access permissions, preventing accidental modifications while maintaining pointer flexibility.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.