c-memory-management

Master manual memory management in C with proper allocation, deallocation, pointer handling, and techniques to avoid memory leaks and corruption.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "c-memory-management" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-c-memory-management

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

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

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

// malloc - allocate memory #include <stdlib.h> #include <string.h>

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 } }

  1. Pointer Basics

// 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*)&#x26;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 &#x3C; 5; i++) {
    printf("%d ", *(ptr + i));  // Same as ptr[i]
}
printf("\n");

}

  1. Dynamic Strings

#include <string.h>

// 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;

}

  1. Structures and Memory

// 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;

}

  1. Memory Pools

// 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;

}

  1. Reference Counting

// 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;

}

  1. Memory Debugging

// 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

  1. Stack vs Heap

// 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;

}

  1. Double Free Prevention

// 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; } }

  1. Memory Alignment

#include <stdalign.h>

// 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(&#x26;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

  • Always check malloc return value - Handle allocation failures

  • Free all allocated memory - Prevent memory leaks

  • Set pointers to NULL after free - Avoid dangling pointers

  • Use sizeof with types - Ensure correct allocation size

  • Initialize allocated memory - Use calloc or memset

  • Match malloc/free calls - Every allocation needs deallocation

  • Use valgrind for testing - Detect memory errors

  • Avoid manual pointer arithmetic - Use array indexing when possible

  • Handle realloc failures - Keep original pointer valid

  • Document ownership - Clarify who frees memory

Common Pitfalls

  • Memory leaks - Forgetting to free allocated memory

  • Double free - Freeing same pointer twice

  • Use after free - Accessing freed memory

  • Buffer overflow - Writing beyond allocated bounds

  • Dangling pointers - Using pointers after free

  • Null pointer dereference - Not checking for NULL

  • sizeof mistakes - Using wrong size calculations

  • Stack overflow - Large stack allocations

  • Uninitialized memory - Reading uninitialized data

  • 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)

  • C Memory Management

  • Valgrind Documentation

  • Modern C by Jens Gustedt

  • C Standard Library Reference

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

android-jetpack-compose

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

storybook-story-writing

No summary provided by upstream source.

Repository SourceNeeds Review
General

atomic-design-fundamentals

No summary provided by upstream source.

Repository SourceNeeds Review