Mastering Systems Programming with C
Pointers and Memory
Direct Memory Access
Every variable you declare in a C program occupies a specific location in your computer's memory. Think of memory as a vast, ordered collection of mailboxes, each with a unique address. When you create an integer int age = 30;, the system reserves a mailbox for age and puts the value 30 inside.
These memory addresses are typically represented in hexadecimal notation. For example, an address might look like 0x7ffc9a4e8d3c. While it seems complex, it's just a base-16 numbering system that's more compact than binary for representing large address values.
A is a special variable that doesn't store a value like an integer or character. Instead, it stores a memory address. It literally "points" to the location of another variable. This gives us a way to indirectly access and manipulate data, which is a cornerstone of C programming.
Declaring and Using Pointers
To declare a pointer, you specify the data type it will point to, followed by an asterisk * and the pointer's name. To get the memory address of an existing variable, you use the address-of operator &.
int age = 30; // A regular integer variable
int *p_age = NULL; // A pointer that can point to an integer
// Initialised to NULL (pointing nowhere)
p_age = &age; // Assign the address of 'age' to p_age
Here, p_age now holds the memory address of age. To access the value at that address, we use the indirection or dereference operator *. This is the inverse of the & operator. It follows the pointer to its stored address and retrieves the value found there.
| Operator | Name | Meaning |
|---|---|---|
& | Address-of | "What is the address of...?" |
* | Dereference/Indirection | "What is the value at address...?" |
// Continuing from the previous example...
// Print the memory address stored in p_age
printf("Address of age: %p\n", p_age);
// Print the value at the address p_age points to
printf("Value of age: %d\n", *p_age); // Prints 30
// We can also modify the original variable via the pointer
*p_age = 31; // This changes the value of 'age' to 31
printf("New value of age: %d\n", age); // Prints 31
Pointers and Data Types
You might wonder why we need to declare the type of data a pointer points to, like int * or char *, if it only stores an address. The reason is scale factor. When you perform arithmetic on a pointer, the compiler needs to know the size of the data type it points to.
If you have an integer pointer p_int and you add 1 to it (p_int++), the address doesn't just increase by 1 byte. It increases by the size of an integer, which is typically 4 bytes. This automatically moves the pointer to the location of the next integer in memory. This behaviour is fundamental to how arrays work in C.
pointer + nactually meansaddress + n * sizeof(data_type).
This is why the name of an array can be used like a pointer. An array name is essentially a constant pointer to the first element of the array. The expressions arr[i] and *(arr + i) are equivalent.
int scores[3] = {88, 92, 79};
int *p_scores = scores; // No '&' needed for arrays
// These three lines all print 92
printf("%d\n", scores[1]);
printf("%d\n", *(scores + 1));
printf("%d\n", *(p_scores + 1));
Pointers in Functions
By default, C passes arguments to functions by value. This means the function gets a copy of the variable, and any changes inside the function don't affect the original. Pointers allow us to achieve pass-by-reference.
Instead of passing the variable itself, you pass a pointer to it. The function then receives the memory address and can dereference the pointer to modify the original variable directly. This is essential for functions that need to alter their inputs, like a function that swaps two numbers.
#include <stdio.h>
// This function takes pointers as arguments
void swap(int *a, int *b) {
int temp = *a; // Get the value at address 'a'
*a = *b; // Put the value from 'b' into address 'a'
*b = temp; // Put the saved value into address 'b'
}
int main() {
int x = 10;
int y = 20;
printf("Before: x = %d, y = %d\n", x, y);
// Pass the addresses of x and y to the function
swap(&x, &y);
printf("After: x = %d, y = %d\n", x, y);
return 0;
}
The output will show that the values of
xandyin themainfunction have been successfully swapped.
This powerful technique is used extensively in C for building efficient programs and complex data structures. It gives you fine-grained control over how your program interacts with the computer's memory.
Time to check your understanding.
What does a pointer variable in C primarily store?
Which operator is used to get the memory address of a variable?
Mastering pointers is a significant step. It moves you from simply using variables to directly managing the memory where they live.