Quiz 2
Registry Synced

Dynamic Memory — malloc, calloc, realloc, free

813 words
4 min read

Reading compass

Now · 🎯 Learning Objectives

Dynamic Memory — malloc, calloc, realloc, free

🎯 Learning Objectives

  • Allocate and free memory dynamically
  • Differentiate malloc, calloc, and realloc
  • Implement dynamic arrays and linked lists
  • Detect and prevent memory leaks

1. Heap Memory Functions

1.1 malloc

c
#include <stdlib.h>
// Allocate 10 integers (uninitialized memory)
int *arr = (int*)malloc(10 * sizeof(int));
if (arr == NULL) {
    fprintf(stderr, "Memory allocation failed\n");
    exit(1);
}
// Memory contains garbage values
arr[0] = 42;  // Initialize after allocation

1.2 calloc

c
// Allocate and zero-initialize 10 integers
int *arr = (int*)calloc(10, sizeof(int));
// All elements are guaranteed 0
// calloc(10, 4) vs malloc(40): calloc zeros the memory

1.3 realloc

c
int *arr = (int*)malloc(5 * sizeof(int));
// ... use 5 elements ...
// Resize to 10 elements (may move to new location)
int *temp = (int*)realloc(arr, 10 * sizeof(int));
if (temp != NULL) {
    arr = temp;  // Update pointer
} else {
    // realloc failed! arr still valid but unchanged
    fprintf(stderr, "realloc failed\n");
}
// realloc preserves existing content, new space is uninitialized

1.4 free

c
free(arr);   // Release memory back to heap
arr = NULL;  // Prevent dangling pointer

2. Common Patterns

2.1 Dynamic Array (Growth Strategy)

c
typedef struct {
    int *data;
    int size;
    int capacity;
} DynArray;
void da_init(DynArray *da) {
    da->size = 0;
    da->capacity = 4;
    da->data = (int*)malloc(da->capacity * sizeof(int));
}
void da_append(DynArray *da, int value) {
    if (da->size >= da->capacity) {
        // Double capacity when full
        da->capacity *= 2;
        int *new_data = (int*)realloc(da->data,
                                        da->capacity * sizeof(int));
        if (new_data) {
            da->data = new_data;
        } else {
            fprintf(stderr, "Failed to grow array\n");
            exit(1);
        }
    }
    da->data[da->size++] = value;
}
void da_free(DynArray *da) {
    free(da->data);
    da->data = NULL;
    da->size = da->capacity = 0;
}

2.2 Singly Linked List

c
typedef struct Node {
    int data;
    struct Node *next;
} Node;
Node* create_node(int value) {
    Node *node = (Node*)malloc(sizeof(Node));
    if (node) {
        node->data = value;
        node->next = NULL;
    }
    return node;
}
void free_list(Node *head) {
    while (head) {
        Node *temp = head;
        head = head->next;
        free(temp);
    }
}

3. Common Pitfalls

Pitfall 1: Forgetting to check malloc return

c
int *arr = (int*)malloc(1000000000 * sizeof(int));  // May fail!
arr[0] = 42;  // SEGFAULT if malloc returned NULL
Fix: Always check if (ptr == NULL) { /* handle error */ }.

Pitfall 2: Memory leaks

c
void leak() {
    int *p = (int*)malloc(100 * sizeof(int));
    // Function returns without freeing p → memory leak
}
Fix: Use a systematic deallocation strategy. In long-running programs, every malloc should match with a free.

Pitfall 3: Double free

c
int *p = malloc(sizeof(int));
free(p);
free(p);  // Undefined behavior! Double free
Fix: Set p = NULL after freeing. free(NULL) is safe.

Pitfall 4: Buffer overflow on heap

c
int *arr = (int*)malloc(5 * sizeof(int));
arr[100] = 42;  // Heap buffer overflow! Corrupts heap metadata

4. 📝 Practice Questions

Q1: What is the difference between malloc and calloc?
Answer: malloc allocates uninitialized memory (garbage values). calloc allocates and zero-initializes. calloc also takes separate count and size arguments, which can help detect arithmetic overflow: calloc(n, size) returns NULL if n*size overflows. Q2: What does realloc do if it cannot extend the existing block?
Answer: realloc allocates a new block elsewhere, copies the old contents, and frees the old block. If allocation fails, it returns NULL and the original block remains untouched. Always use a temporary pointer for realloc. Q3: Write a function that concatenates two strings using dynamic memory.
c
char* concat(const char *s1, const char *s2) {
    size_t len1 = strlen(s1);
    size_t len2 = strlen(s2);
    char *result = (char*)malloc(len1 + len2 + 1);
    if (result) {
        strcpy(result, s1);
        strcat(result, s2);
    }
    return result;
}
Q4: What happens if you forget to free memory in a long-running program?
Answer: The program's memory usage grows monotonically (memory leak). Eventually, the OS runs out of memory, causing the program to crash (OOM) or be killed by the OS. This is especially critical in servers that run for weeks or months. Q5: Why is setting freed pointers to NULL a good practice?
Answer: It prevents use-after-free bugs (accessing freed memory) and double-free bugs. Accessing a NULL pointer causes an immediate crash (segfault), which is easier to debug than corrupting heap metadata silently.

5. 🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.