Memory Organization and Addressing
Computer memory is divided into discrete units, with each unit typically comprising one byte. Each byte is assigned a unique identification number known as its address. In C programming, these addresses are referred to as pointers.
On 32-bit architectures, addresses are generated using 32 address lines, producing 32-bit binary signals. Each address thus requires 4 bytes of storage space, enabling access to 232 bytes (4GB) of memory. Similarly, 64-bit systems use 64-bit addresses requiring 8 bytes of storage.
Pointer Fundamentals
Pointer Variables and Address Extraction
A pointer variable stores memory addresses. We can extract the address of a variable using the address-of operator (&). This address represents the starting location of the variable's allocated memory space.
int number = 42;
int* addr_ptr = &number; // Stores the address of 'number'
Pointer Size Considerations
The size of any pointer variable is determined by the architecture:
- 4 bytes on 32-bit systems (32-bit addresses)
- 8 bytes on 64-bit systems (64-bit addresses)
This size remains constant regardless of the pointer's type (int*, char*, etc.) since all pointers store addresses.
Dereferencing Pointers
The indirection operator (*) accesses the value stored at a pointer's address:
int value = 100;
int* ptr = &value;
*ptr = 200; // Modifies 'value' to 200 through the pointer
Pointer Types and Their Significance
Type-Specific Behavior
While all pointers store addresses, their type determines two crucial behaviors:
- Dereferencing scope: The type indicates how many bytes to access when dereferencing
- Pointer arithmetic: The type determines the increment/decrement stride
int data = 0x11223344;
int* int_ptr = &data; // Dereferences 4 bytes
char* char_ptr = &data; // Dereferences 1 byte
// Pointer arithmetic example
int* ip = &data;
char* cp = &data;
// ip + 1 advances by sizeof(int) bytes (usually 4)
// cp + 1 advances by sizeof(char) byte (1)
Dangerous Pointers: Dangling References
Common Causes
- Uninitialized pointers: Contain garbage values pointing to arbitrary locations
- Arbitrary addresses: Explicitly assigning invalid memory locations
- Boundary violations: Accessing beyond allocated memory ranges
- Expired references: Pointing to memory after it's been deallocated
// Uninitialized pointer example
int* wild_ptr; // Contains random address
*wild_ptr = 10; // Dangerous operation
Prevention Strategies
- Always initialize pointers (to NULL if no valid address yet)
- Validate pointers before use (check for NULL)
- Respect array boundaries
- Avoid returning addresses of local variables
int* safe_ptr = NULL;
int variable = 5;
safe_ptr = &variable;
if (safe_ptr != NULL) {
*safe_ptr = 10; // Safe to use
}
Pointer Operations and Arithmetic
Pointer-Integer Arithmetic
Pointers can be incremented or decremented, with the step size determined by their type:
int array[] = {10, 20, 30, 40, 50};
int* ptr = array;
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i)); // Accesses array elements
}
Pointer Subtraction
Subtracting two pointers (of the same type pointing to the same array) yields the element count between them:
int data[10] = {0};
int* start = &data[0];
int* end = &data[5];
ptrdiff_t distance = end - start; // Result: 5
Pointer Comparisons
Pointers can be compared using relational operators. The C standard allows comparisons between pointers within the same array, including comparison with one position past the array's end (but not before its start).
Multi-Level Pointers
Double Pointers
A pointer to a pointer (double pointer) stores the address of another pointer variable:
int value = 100;
int* single_ptr = &value;
int** double_ptr = &single_ptr;
**double_ptr = 200; // Modifies 'value' through double indirection
Pointers and Arrays
Array-Pointer Relationship
Array names typically decay to pointers to their first element, except in specific contexts:
sizeof(array)returns the total array size&arrayyields the address of the entire array
int numbers[10];
int* ptr = numbers; // Points to first element
// These expressions are equivalent:
numbers[i] == *(numbers + i) == *(ptr + i) == ptr[i]
Important Distinction
While array names and pointers can often be used interchangeably, pointers are variables that can be modified, while array names are constant addresses:
int arr[10] = {0};
int* p = arr;
p++; // Valid: advances pointer
// arr++; // Invalid: array name is constant
Pointer Arrays
Pointer arrays are arrays containing pointer elements. They can simulate multidimensional arrays:
int row1[] = {1, 2, 3};
int row2[] = {4, 5, 6};
int row3[] = {7, 8, 9};
int* matrix[] = {row1, row2, row3};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}