free ()
#include <stdio.h>
#include <stdlib.h>
/*
* free() takes one argument: pointer to memory address
*
* NOTE: freeing all dynamically allocated memory is required
* to prevent memory leaks
*
* ADDITIONAL NOTES:
* - freeing invalid/uninitialized pointer can result in undefined behavior
* - double freeing memory can result in undefined behavior
*/
int main(void) {
int *num1 = malloc(sizeof(int));
int *num2 = malloc(sizeof(int));
// NOTE:
// Dereferencing a pointer before initializing the value
// that it points to can result in undefined behavior
// Example:
// int count = *num1;
// This line of code may cause a crash if placed here because
// *num1 is uninitialized (garbage value) until a value is assigned
*num1 = 25;
*num2 = 37;
// Currently, num1 points to an address containing 25
// and num2 points to an address containing 37
// DANGER:
// num2 = num1;
// Now num2 points to the same address as num1,
// but memory originally allocated by num2 still exists
// Reference to the address containing the value 37 is lost
// This is a memory leak
// INSTEAD you could write:
*num2 = *num1;
// This dereferences num1 (returns the value at that address)
// And stores that value at the address of num2 --> SAFE
// Still two separate memory addresses storing the same value
// ANOTHER DANGER!
// Let's free memory stored at num1
free(num1);
// num1 is now a dangling pointer -> CANNOT BE DEREFERENCED
// Calling *num2 = *num1; Now results in undefined behavior
free(num2);
/////////////////////////////////////////////////////////////////////
// Attempting to dereference a NULL pointer is another danger
// This will typically result in a segmentation fault
// Example:
// Initializing to NULL is a very important habit that enables you to
// safely check if a pointer can be dereferenced later in your code
int *val = NULL;
// BUT
// Calling int x = *val; attempts to dereference a NULL pointer
// INSTEAD verify pointer is valid prior to dereferencing:
int x;
if (val) // if(val != NULL)
x = *val;
else
x = 5;
////////////////////////////////////////////////////////////////////
// Freeing a pointer to pointer is also something to be careful with
// Prior to freeing the pointer to pointers
// - often used as an array of pointers or 2D array -
// you cannot free the pointer to pointer without first freeing all
// pointers being pointed to
// Sounds confusing, but isn't
// Let's make a dynaminacally allocated 2D array of integer pointers
// structured as: arr[size][size]
int size = 10;
int ** arr = malloc(size * sizeof(int*));
for (int i = 0; i < size; i++) {
arr[i] = calloc(size, sizeof(int));
}
// Now this 2D array exists and each element of every column is 0
// Calling free(arr); would result in a memory leak
// This is because the outer pointers are freed, however,
// each of the inner arrays still remain in memory and are inaccessible
// INSTEAD, Free inner arrays first:
for (int i = 0; i < size; i++) {
free(arr[i]);
}
free(arr);
// GOOD RULE OF THUMB!
// For EVERY malloc()/ calloc() call, there must be a corresponding free() call
printf("I hope this has helped to teach you how free() works :)\n");
return 0;
}
Academic Notice: The code shown here is provided solely as a learning reference. Copying and pasting is intentionally disabled to encourage independent practice. Students should implement solutions on their own to demonstrate understanding. This material is not intended for direct submission in assignments.
Additionally: This code was written by a former CS1 student and may not reflect your professor’s intended solution or instructional approach. For coursework, students are expected to follow examples, conventions, and requirements presented in class and in professor-assigned materials.