last post, I've written about lots of things but not about C pointers. I thought the topic deserves a post in itself because it's one of the most important features of the C language.
Let's start with some music please.
A pointer is a variable that contains the memory address of another variable. A pointer allows us to indirectly access and manipulate the value of the variable that is pointed to. Like other variables, C pointers have a type.
#include <stdio.h>
#include <stdlib.h>
int main () {
int foo = 42;
int bar = 83;
int *ptr = &foo; // Pointer variable, type int, receives the address of foo variable*
int **ptrptr = &ptr; // pointer to a pointer
printf("Value of foo: %d\n", foo);
printf("Value of ptr is: %p, and ptr is pointing to: %d\n", (void *)ptr, *ptr);
printf("Value of ptrptr is: %p & ptrptr is pointing to: %p\n", (void *)ptrptr, (void *)ptr);
printf("\n");
*ptr = 12;
printf("Value of foo after modification via ptr: %d\n", foo);
**ptrptr = 13;
printf("Value of foo after modification via ptr to ptr: %d\n", foo);
printf("\n");
ptr = &bar;
printf("Value of ptr is now: %p, and ptr is now pointing to: %d\n", (void *)ptr, *ptr);
printf("Value of ptrptr, still: %p & ptrptr is now pointing to: %p\n", (void *)ptrptr, (void *)ptr);
exit(EXIT_SUCCESS);
}
And the output of the previous program is:
Value of foo: 42
Value of ptr is: 0x7ffc6155fd60, and ptr is pointing to: 42
Value of ptrptr is: 0x7ffc6155fd68 & ptrptr is pointing to: 0x7ffc6155fd60
Value of foo after modification via ptr: 12
Value of foo after modification via ptr to ptr: 13
Value of ptr is now: 0x7ffc6155fd64, and ptr is now pointing to: 83
Value of ptrptr, still: 0x7ffc6155fd68 & ptrptr is now pointing to: 0x7ffc6155fd64
C offers operators for pointer manipulation:
All modern programming languages have pointers in some form or other. Pointers are used everywhere there is a program, even when we don't see them. Let's explore some of those use cases.
C Pointers can be used to access and manipulate elements in arrays and strings. C Pointers provide a way to efficiently iterate over arrays and perform operations on each item of the sequence.
int arr[5] = {1, 2, 3, 4, 5};
int *aptr = arr;
// print items of the sequence using array indices
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// print elements of the array using pointer
for (int i = 0; i < 5; i++) {
printf("%d ", *aptr);
aptr++;
}
// print items of the sequence using pointer arithmetic
// and the fact that arr is actually a pointer to the first item of the arr
for (int i = 0; i < 5; i++) {
printf("%d ", *arr+i);
}
// an array of characters
char str[] = "Hello world from West Africa";
char *sptr = str; // a pointer to the first element of the array
int i = 0;
while(i < 28){
// while i is less than 28, print the character at i-th index
printf("%c ", str[i]);
// increment i to go to the next index
i++;
}
printf("\n");
// while the value that is pointed to is different from the "\0" character
while (*sptr != '\0') {
// print the char
printf("%c ", *sptr);
// increment pointer (to go to the next character)
sptr = sptr + 1;
}
printf("\n");
int m = 0;
while(m < 28){
// str is actually a pointer to the first item of the object
// so str+m is the address of the m-ieth item of the object
printf("%c ", *(str+m));
m++;
}
// array of pointers to characters
char *names[] = {"Alice", "Bob", "Charlie", "David", "Emma"};
// Pointer to the first element of the first element of the array of strings
char **ptr_names = names;
for (int j = 0; j < 5; j++) {
// Print elements of the array of strings using indices
printf("%s\n", names[j]);
}
for (int x = 0; x < 5; x++) {
// Print the element currently pointed to
printf("%s\n", *ptr_names);
ptr_names = ptr_names + 1;
}
C Pointers can be used to pass arguments by reference to functions, allowing the function to modify the value of the original variable. This is particularly useful in the following situations:
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// let's suppose this is a laaaaaarge struct
typedef struct {
int id;
char name[50];
float salary;
} Employee;
void update_employee(Employee* emp) {
emp->salary *= 1.1; // 10% raise
}
int main() {
// We create the large structure once
Employee e1 = {1, "John Doe", 5000};
printf("Employee: %s, Salary: %.2f\n", e1.name, e1.salary);
// We pass the large structure by reference
// to avoid the overhead of copying the entire structure.
update_employee(&e1);
printf("Employee after update: %s, Salary: %.2f\n", e1.name, e1.salary);
exit(EXIT_SUCCESS);
}
C Pointers are also used to allocate memory dynamically at run-time using functions like malloc(), calloc(), and realloc(). Examples:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// a huuuuuuuge structure
typedef struct {
int id;
char name[50];
float salary;
} Employee;
// thanks to pointer & to malloc,
// we can create the structure inside the create_employee function
// and use the structure even after the function goes out of scope
Employee* create_employee(){
// Allocate memory space on heap, size should be the same as width of Employee struct
// Allocate a pointer on stack,
Employee* e2 = malloc(sizeof(Employee));
// always verify the allocation worked fine
if (e2 == nullptr) {
printf("Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
// the ptr e2 contains the value of the memory address of the struct that is on the heap
return e2;
}
void init_employee(Employee* emp, int id, const char* name, float salary) {
emp->id = id;
strcpy(emp->name, name);
emp->salary = salary;
}
void update_employee(Employee* emp) {
emp->salary *= 1.1; // 10% raise
}
int main() {
Employee* e2 = create_employee();
init_employee(e2, 2, "Nsukami Patrick", 6000);
printf("Employee: %s, Salary: %.2f\n", e2->name, e2->salary);
update_employee(e2);
printf("Employee after update: %s, Salary: %.2f\n", e2->name, e2->salary);
// never forget to free the allocated memory
free(e2);
exit(EXIT_SUCCESS);
}
Linked lists, Stacks, Queues, Trees, Graphs and many other data structures are implemented using C Pointers. Example:
A function pointer, is a pointer which point to the memory address of a function instead of pointing to the address of a data object. They are used to:
Here are some examples on how to use function pointers in C.
C Pointers are often used in low-level programming to interact with hardware devices and memory-mapped registers. They provide a way to access specific memory addresses and manipulate hardware directly. This part goes way beyond what my brain is able to understand. Hopefully, I'll be able to show you an example in another article.
To learn more about C pointers, we recommend the following pointers: