--- name: c-memory-management description: Use when managing memory in C programs with malloc/free, pointers, and avoiding common memory safety pitfalls. allowed-tools: - Read - Write - Edit - Grep - Glob - Bash --- # C Memory Management Master manual memory management in C with proper allocation, deallocation, pointer handling, and techniques to avoid memory leaks and corruption. ## Overview C requires manual memory management through explicit allocation and deallocation. Understanding pointers, the heap, and proper memory handling is crucial for writing safe and efficient C programs. ## Installation and Setup ### Compiler and Tools ```bash # Install GCC compiler # macOS xcode-select --install # Linux (Ubuntu/Debian) sudo apt-get install build-essential # Check installation gcc --version # Memory debugging tools # Install Valgrind (Linux) sudo apt-get install valgrind # Install Address Sanitizer (built into GCC/Clang) gcc -fsanitize=address -g program.c -o program ``` ### Compilation Flags ```bash # Basic compilation gcc program.c -o program # With warnings and debugging gcc -Wall -Wextra -g program.c -o program # With Address Sanitizer gcc -fsanitize=address -g program.c -o program # With optimization gcc -O2 -Wall program.c -o program ``` ## Core Patterns ### 1. Dynamic Memory Allocation ```c // malloc - allocate memory #include #include int* allocate_array(size_t size) { int* arr = malloc(size * sizeof(int)); if (arr == NULL) { return NULL; // Allocation failed } return arr; } // calloc - allocate and zero-initialize int* allocate_zeroed_array(size_t size) { int* arr = calloc(size, sizeof(int)); if (arr == NULL) { return NULL; } return arr; } // realloc - resize allocation int* resize_array(int* arr, size_t old_size, size_t new_size) { int* new_arr = realloc(arr, new_size * sizeof(int)); if (new_arr == NULL && new_size > 0) { // Reallocation failed, original array still valid return NULL; } return new_arr; } // free - deallocate memory void cleanup_array(int** arr) { if (arr != NULL && *arr != NULL) { free(*arr); *arr = NULL; // Prevent dangling pointer } } ``` ### 2. Pointer Basics ```c // Pointer declaration and usage void pointer_basics() { int value = 42; int* ptr = &value; // ptr points to value printf("Value: %d\n", value); printf("Address: %p\n", (void*)&value); printf("Pointer: %p\n", (void*)ptr); printf("Dereferenced: %d\n", *ptr); *ptr = 100; // Modify through pointer printf("New value: %d\n", value); } // Null pointers void null_pointer_check(int* ptr) { if (ptr == NULL) { printf("Null pointer\n"); return; } printf("Valid pointer: %d\n", *ptr); } // Pointer arithmetic void pointer_arithmetic() { int arr[] = {10, 20, 30, 40, 50}; int* ptr = arr; for (int i = 0; i < 5; i++) { printf("%d ", *(ptr + i)); // Same as ptr[i] } printf("\n"); } ``` ### 3. Dynamic Strings ```c #include // Create string copy char* string_duplicate(const char* str) { if (str == NULL) { return NULL; } size_t len = strlen(str); char* copy = malloc(len + 1); // +1 for null terminator if (copy != NULL) { strcpy(copy, str); } return copy; } // String concatenation char* string_concat(const char* s1, const char* s2) { if (s1 == NULL || s2 == NULL) { return NULL; } size_t len1 = strlen(s1); size_t len2 = strlen(s2); char* result = malloc(len1 + len2 + 1); if (result == NULL) { return NULL; } strcpy(result, s1); strcat(result, s2); return result; } // Safe string functions char* safe_string_copy(const char* src, size_t max_len) { if (src == NULL) { return NULL; } size_t len = strnlen(src, max_len); char* dest = malloc(len + 1); if (dest != NULL) { memcpy(dest, src, len); dest[len] = '\0'; } return dest; } ``` ### 4. Structures and Memory ```c // Structure with dynamic members typedef struct { char* name; int* scores; size_t num_scores; } Student; Student* create_student(const char* name, size_t num_scores) { Student* student = malloc(sizeof(Student)); if (student == NULL) { return NULL; } student->name = string_duplicate(name); if (student->name == NULL) { free(student); return NULL; } student->scores = malloc(num_scores * sizeof(int)); if (student->scores == NULL) { free(student->name); free(student); return NULL; } student->num_scores = num_scores; memset(student->scores, 0, num_scores * sizeof(int)); return student; } void destroy_student(Student** student) { if (student == NULL || *student == NULL) { return; } free((*student)->name); free((*student)->scores); free(*student); *student = NULL; } ``` ### 5. Memory Pools ```c // Simple memory pool typedef struct { void* pool; size_t block_size; size_t num_blocks; size_t next_free; } MemoryPool; MemoryPool* create_pool(size_t block_size, size_t num_blocks) { MemoryPool* pool = malloc(sizeof(MemoryPool)); if (pool == NULL) { return NULL; } pool->pool = malloc(block_size * num_blocks); if (pool->pool == NULL) { free(pool); return NULL; } pool->block_size = block_size; pool->num_blocks = num_blocks; pool->next_free = 0; return pool; } void* pool_allocate(MemoryPool* pool) { if (pool == NULL || pool->next_free >= pool->num_blocks) { return NULL; } void* block = (char*)pool->pool + (pool->next_free * pool->block_size); pool->next_free++; return block; } void destroy_pool(MemoryPool** pool) { if (pool == NULL || *pool == NULL) { return; } free((*pool)->pool); free(*pool); *pool = NULL; } ``` ### 6. Reference Counting ```c // Reference counted string typedef struct { char* data; size_t ref_count; } RefString; RefString* refstring_create(const char* str) { RefString* rs = malloc(sizeof(RefString)); if (rs == NULL) { return NULL; } rs->data = string_duplicate(str); if (rs->data == NULL) { free(rs); return NULL; } rs->ref_count = 1; return rs; } RefString* refstring_retain(RefString* rs) { if (rs != NULL) { rs->ref_count++; } return rs; } void refstring_release(RefString** rs) { if (rs == NULL || *rs == NULL) { return; } (*rs)->ref_count--; if ((*rs)->ref_count == 0) { free((*rs)->data); free(*rs); } *rs = NULL; } ``` ### 7. Memory Debugging ```c // Debug wrapper for malloc #ifdef DEBUG_MEMORY typedef struct { void* ptr; size_t size; const char* file; int line; } AllocationInfo; static AllocationInfo allocations[1000]; static size_t num_allocations = 0; void* debug_malloc(size_t size, const char* file, int line) { void* ptr = malloc(size); if (ptr != NULL && num_allocations < 1000) { allocations[num_allocations].ptr = ptr; allocations[num_allocations].size = size; allocations[num_allocations].file = file; allocations[num_allocations].line = line; num_allocations++; } return ptr; } void debug_free(void* ptr) { for (size_t i = 0; i < num_allocations; i++) { if (allocations[i].ptr == ptr) { allocations[i] = allocations[num_allocations - 1]; num_allocations--; break; } } free(ptr); } void print_leaks() { printf("Memory leaks: %zu\n", num_allocations); for (size_t i = 0; i < num_allocations; i++) { printf(" %p (%zu bytes) at %s:%d\n", allocations[i].ptr, allocations[i].size, allocations[i].file, allocations[i].line); } } #define malloc(size) debug_malloc(size, __FILE__, __LINE__) #define free(ptr) debug_free(ptr) #endif ``` ### 8. Stack vs Heap ```c // Stack allocation void stack_example() { int local_var = 42; // Stack char buffer[100]; // Stack // Automatically freed when function returns } // Heap allocation void heap_example() { int* dynamic = malloc(sizeof(int)); // Heap if (dynamic != NULL) { *dynamic = 42; free(dynamic); // Must manually free } } // Mixed allocation typedef struct { int id; // Stack (part of struct) char* name; // Heap (pointer to heap) } Record; Record* create_record(int id, const char* name) { Record* rec = malloc(sizeof(Record)); // Heap if (rec == NULL) { return NULL; } rec->id = id; // Stack value rec->name = string_duplicate(name); // Heap if (rec->name == NULL) { free(rec); return NULL; } return rec; } ``` ### 9. Double Free Prevention ```c // Safe free macro #define SAFE_FREE(ptr) do { \ if (ptr != NULL) { \ free(ptr); \ ptr = NULL; \ } \ } while(0) // Usage void safe_cleanup() { int* arr = malloc(10 * sizeof(int)); // ... use arr ... SAFE_FREE(arr); // arr is now NULL, can safely call again SAFE_FREE(arr); // No-op, safe } // Reference clearing void clear_reference(void** ref) { if (ref != NULL && *ref != NULL) { free(*ref); *ref = NULL; } } ``` ### 10. Memory Alignment ```c #include // Aligned allocation void* aligned_malloc(size_t size, size_t alignment) { void* ptr = NULL; #ifdef _WIN32 ptr = _aligned_malloc(size, alignment); #else if (posix_memalign(&ptr, alignment, size) != 0) { return NULL; } #endif return ptr; } void aligned_free(void* ptr) { #ifdef _WIN32 _aligned_free(ptr); #else free(ptr); #endif } // Structure alignment typedef struct { alignas(16) double values[4]; // 16-byte aligned } AlignedData; ``` ## Best Practices 1. **Always check malloc return value** - Handle allocation failures 2. **Free all allocated memory** - Prevent memory leaks 3. **Set pointers to NULL after free** - Avoid dangling pointers 4. **Use sizeof with types** - Ensure correct allocation size 5. **Initialize allocated memory** - Use calloc or memset 6. **Match malloc/free calls** - Every allocation needs deallocation 7. **Use valgrind for testing** - Detect memory errors 8. **Avoid manual pointer arithmetic** - Use array indexing when possible 9. **Handle realloc failures** - Keep original pointer valid 10. **Document ownership** - Clarify who frees memory ## Common Pitfalls 1. **Memory leaks** - Forgetting to free allocated memory 2. **Double free** - Freeing same pointer twice 3. **Use after free** - Accessing freed memory 4. **Buffer overflow** - Writing beyond allocated bounds 5. **Dangling pointers** - Using pointers after free 6. **Null pointer dereference** - Not checking for NULL 7. **sizeof mistakes** - Using wrong size calculations 8. **Stack overflow** - Large stack allocations 9. **Uninitialized memory** - Reading uninitialized data 10. **Memory fragmentation** - Poor allocation patterns ## When to Use - Systems programming requiring manual control - Embedded systems with limited resources - Performance-critical applications - Operating system development - Device drivers and kernel modules - Real-time systems - Legacy codebase maintenance - Interfacing with hardware - Memory-constrained environments - Low-level library development ## Resources - [The C Programming Language (K&R)](https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628) - [C Memory Management](https://en.cppreference.com/w/c/memory) - [Valgrind Documentation](https://valgrind.org/docs/manual/manual.html) - [Modern C by Jens Gustedt](https://modernc.gforge.inria.fr/) - [C Standard Library Reference](https://en.cppreference.com/w/c)