This episode dissects pointers, the heart of the C language: memory addresses, dereferencing, pointers to arrays, strings, and functions, dynamic memory allocation with malloc, calloc, realloc, and free, and pointer safety and common bugs like use-after-free and null pointers.

Pointers are the feature that makes C special and, at the same time, most dangerous. A pointer stores the memory address of a value, not the value itself. With pointers, you can manipulate large data without copying, build dynamic data structures, and call functions indirectly.
Episode 7 covers pointer basics and dereferencing, pointers to arrays, strings, and functions, dynamic memory allocation with malloc, calloc, realloc, and free, and safety patterns that prevent classic bugs like null pointer dereference and use-after-free.
Mastering pointers is the dividing line between someone who writes C code and someone who truly understands C. This episode is the most important investment in the whole series.
Every variable has an address in memory. The & operator takes the address of a variable, and pointer types are declared with an asterisk after the base type:
cat > pointer.c <<'EOF'
#include <stdio.h>
int main(void) {
int nilai = 42;
int *alamat = &nilai;
printf("Nilai: %d\n", nilai);
printf("Alamat: %p\n", (void *)alamat);
printf("Dereference: %d\n", *alamat);
return 0;
}
EOF
gcc -Wall -Wextra pointer.c -o pointer && ./pointerint *alamat = &nilai stores the address of nilai in the pointer alamat. The * operator used in front of a pointer is called dereferencing — reading the value being pointed to. %p prints the address in hexadecimal form.
Pointers let a function modify the original arguments, overcoming the passing by value limitation from episode 5:
void tambah_satu(int *x) {
(*x)++;
}
int main(void) {
int angka = 10;
tambah_satu(&angka);
printf("%d\n", angka);
return 0;
}The function tambah_satu(&angka) receives the address of angka and changes its original value through dereferencing. Without a pointer, changes would only apply to the copy inside the function.
The name of an array acts as a pointer to its first element. This means nilai and &nilai[0] point to the same address. Pointer arithmetic enables element iteration:
int nilai[5] = {10, 20, 30, 40, 50};
for (int *p = nilai; p < nilai + 5; p++) {
printf("%d ", *p);
}The iteration for (int *p = nilai; ...; p++) advances the pointer one element per step, not one byte. The step size is determined by the pointer's type, so an int advances 4 bytes on common platforms.
Immutable strings are stored as pointers to char: const char *s = "teks". Pointers to functions, as introduced in episode 5, store a function's address so functions can be chosen at runtime. Both show that pointers are a general concept, not specific to data.
Heap memory is allocated at runtime through four functions from <stdlib.h>:
malloc: allocates bytes without initializing them.calloc: allocates and zero-fills.realloc: resizes an existing block.free: releases a block back to the system.cat > alokasi.c <<'EOF'
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *data = malloc(5 * sizeof(int));
if (data == NULL) {
return 1;
}
for (int i = 0; i < 5; i++) {
data[i] = i * i;
}
int *besar = realloc(data, 10 * sizeof(int));
if (besar != NULL) {
data = besar;
}
free(data);
return 0;
}
EOF
gcc -Wall -Wextra alokasi.c -o alokasi && ./alokasiThe pattern malloc(5 * sizeof(int)) allocates enough memory for 5 ints. Always check for a NULL result, because allocation can fail. realloc returns a new pointer that may differ, and free must be called exactly once per block.
A memory leak occurs when a block is allocated but never freed. Detect it with valgrind:
valgrind --leak-check=full ./alokasiThe output of valgrind --leak-check=full ./alokasi reports blocks that were never freed. The sentence "All heap blocks were freed" means there are no leaks — the target you should always aim for.
The two most common memory bugs are:
NULL.free twice on the same pointer.All of the bugs above trigger undefined behavior that can crash or corrupt data. Preventive discipline: always initialize pointers, check for NULL, set pointers to NULL after free, and use a single owner for each memory block.
free(data);
data = NULL;The sequence free(data); data = NULL; prevents use-after-free and double free because any use of a NULL pointer is immediately caught. This pattern is simple but saves production systems.
Warning
The behavior defined by the C standard is only a fraction of what seems reasonable. Dereferencing the wrong pointer, reading past an array, or using an already-freed block are all undefined behaviors that must be avoided from the start.
Key takeaways:
In the next episode 8 we will discuss input/output and file handling — standard I/O with printf, scanf, getchar, and putchar, file I/O with fopen, fread, fwrite, fprintf, fgets, and fclose, file pointers and file modes, and I/O error handling with errno and perror.