← C EnglishChapter 09 of 13

Dynamic Memory Allocation

## Learning Objectives - Understand stack vs heap - Master malloc, calloc, realloc, free - Learn common memory mistakes - Work with dynamic arrays ## Memory Layout ```text +------------------+ High Address | Stack | Local variables, function calls | | | | v | | | | ^ | | | | | Heap | Dynamic allocation (malloc) | | +------------------+ Low Address | Uninitialized | | Initialized | | Code Segment | +------------------+ ``` ## Stack vs Heap ### Stack (Automatic) ```c void function(void) { int arr[1000]; // Allocated on stack // Freed automatically when function returns } ``` ### Heap (Dynamic) ```c void function(void) { int *arr = malloc(1000 * sizeof(int)); // Allocated on heap // Must free manually! free(arr); } ``` ## malloc ### Basic Usage ```c #include int *ptr = (int*)malloc(sizeof(int)); if (ptr == NULL) { // Handle allocation failure return 1; } *ptr = 42; printf("%d\n", *ptr); free(ptr); ptr = NULL; ``` ### Allocate Array ```c int *arr = (int*)malloc(5 * sizeof(int)); if (arr == NULL) { return 1; } for (int i = 0; i < 5; i++) { arr[i] = i * 10; } free(arr); ``` ### malloc Does Not Initialize ```c int *arr = (int*)malloc(5 * sizeof(int)); // Values are UNINITIALIZED (garbage) int *arr2 = (int*)calloc(5, sizeof(int)); // Zero-initialized ``` ## calloc ### calloc Syntax ```c int *arr = (int*)calloc(5, sizeof(int)); // All 5 elements are 0 for (int i = 0; i < 5; i++) { printf("%d ", arr[i]); // 0 0 0 0 0 } free(arr); ``` ### Syntax ```c void *calloc(size_t num, size_t size); // num: number of elements // size: size of each element ``` ## realloc ### Resize Memory ```c int *arr = (int*)malloc(5 * sizeof(int)); // Fill array for (int i = 0; i < 5; i++) { arr[i] = i; } // Resize to 10 elements int *newArr = (int*)realloc(arr, 10 * sizeof(int)); if (newArr == NULL) { free(arr); // Original still valid return 1; } arr = newArr; // Update pointer // New elements are uninitialized for (int i = 5; i < 10; i++) { arr[i] = i * 10; } free(arr); ``` ### Shrink Memory ```c int *arr = (int*)malloc(10 * sizeof(int)); // ... use first 5 elements ... int *newArr = (int*)realloc(arr, 5 * sizeof(int)); // Can safely shrink even if newArr == arr ``` ### Common Pattern: Grow Array ```c int *arr = NULL; int size = 0; int capacity = 0; int value; while (scanf("%d", &value) == 1) { if (size >= capacity) { capacity = capacity == 0 ? 1 : capacity * 2; int *newArr = (int*)realloc(arr, capacity * sizeof(int)); if (newArr == NULL) { free(arr); return 1; } arr = newArr; } arr[size++] = value; } ``` ## free ### free Basic Usage ```c int *ptr = (int*)malloc(sizeof(int)); *ptr = 42; free(ptr); ptr = NULL; // Always set to NULL after free ``` ### free(NULL) is Safe ```c int *ptr = NULL; free(ptr); // No-op, perfectly safe ``` ## Common Mistakes ### Memory Leak ```c void leak(void) { int *ptr = malloc(sizeof(int)); *ptr = 42; // Missing free(ptr)! } ``` **Detected with valgrind:** ```bash valgrind --leak-check=full ./program ``` ### Double Free ```c int *ptr = malloc(sizeof(int)); free(ptr); free(ptr); // Undefined behavior! ``` ### Use After Free (Dangling Pointer) ```c int *ptr = malloc(sizeof(int)); *ptr = 42; free(ptr); printf("%d\n", *ptr); // Undefined behavior! ``` ### Not Checking NULL ```c int *ptr = (int*)malloc(sizeof(int)); *ptr = 42; // Crash if malloc returns NULL! ``` ### Partial Free ```c int **grid = (int**)malloc(10 * sizeof(int*)); for (int i = 0; i < 10; i++) { grid[i] = (int*)malloc(10 * sizeof(int)); } // Wrong: only frees outer array free(grid); // Correct: free inner arrays first for (int i = 0; i < 10; i++) { free(grid[i]); } free(grid); ``` ## Dynamic 2D Arrays ### Array of Pointers ```c int **createMatrix(int rows, int cols) { int **matrix = (int**)malloc(rows * sizeof(int*)); if (matrix == NULL) return NULL; for (int i = 0; i < rows; i++) { matrix[i] = (int*)malloc(cols * sizeof(int)); if (matrix[i] == NULL) { // Cleanup on failure for (int j = 0; j < i; j++) { free(matrix[j]); } free(matrix); return NULL; } } return matrix; } void freeMatrix(int **matrix, int rows) { for (int i = 0; i < rows; i++) { free(matrix[i]); } free(matrix); } ``` ### Contiguous 2D Array (Better) ```c int **createMatrix(int rows, int cols) { int **matrix = (int**)malloc(rows * sizeof(int*)); int *data = (int*)malloc(rows * cols * sizeof(int)); if (matrix == NULL || data == NULL) { free(matrix); free(data); return NULL; } for (int i = 0; i < rows; i++) { matrix[i] = data + i * cols; } return matrix; } ``` ## Allocating Strings ```c char *strdup(const char *s) { size_t len = strlen(s) + 1; char *copy = (char*)malloc(len); if (copy) { memcpy(copy, s, len); } return copy; } // Usage char *name = strdup("Hello"); free(name); ``` ## Flexible Array Members ```c struct Person { size_t nameLength; char name[]; // Flexible array member }; struct Person *p = malloc(sizeof(struct Person) + 21); p->nameLength = 20; strcpy(p->name, "Alice"); free(p); ``` ## Memory Management Best Practices ### Always Initialize ```c int *ptr = (int*)calloc(n, sizeof(int)); // Zero-initialized ``` ### Always Check ```c int *ptr = (int*)malloc(sizeof(int)); if (ptr == NULL) { // Handle error return; } ``` ### Always Free ```c free(ptr); ptr = NULL; ``` ### Tool: valgrind ```bash valgrind --leak-check=full --show-leak-kinds=all ./program ``` ## Summary - Stack: automatic, fast, limited size - Heap: manual, flexible, larger - `malloc(size)`: allocates raw memory - `calloc(n, size)`: allocates zero-initialized memory - `realloc(ptr, newSize)`: resizes memory - `free(ptr)`: releases memory - Always check for NULL - Always set pointer to NULL after free - Use valgrind to detect leaks

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →