[Solution] C malloc() Returns NULL — ENOMEM Fix

When malloc(), calloc(), or realloc() cannot allocate the requested memory, it returns NULL and sets errno to ENOMEM. Ignoring this return value causes null pointer dereferences and segmentation faults. Properly handling allocation failures is essential for robust C programs.

Why malloc() Returns NULL

The most common reasons are requesting more memory than the system has available, hitting resource limits (ulimit), or memory fragmentation on embedded systems.

Wrong: Ignoring the Return Value

// WRONG — no check after malloc
#include <stdlib.h>
#include <stdio.h>

int main(void) {
    int *arr = malloc(1000000 * sizeof(int));
    arr[0] = 42; // segfault if malloc returned NULL
    printf("%d\n", arr[0]);
    free(arr);
    return 0;
}

Correct: Always Check the Return Value

// CORRECT
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(void) {
    int *arr = malloc(1000000 * sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "malloc failed: %s\n", strerror(errno));
        return 1;
    }

    arr[0] = 42;
    printf("%d\n", arr[0]);
    free(arr);
    return 0;
}

Using calloc Instead of malloc

calloc initializes all allocated memory to zero, which prevents use of uninitialized values and helps detect allocation:

// CORRECT — using calloc
#include <stdlib.h>
#include <stdio.h>

int main(void) {
    int count = 1000;
    int *arr = calloc(count, sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "calloc failed — out of memory\n");
        return 1;
    }

    // arr[0] through arr[999] are guaranteed to be 0
    for (int i = 0; i < count; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    free(arr);
    return 0;
}

Using realloc Safely

realloc can return NULL without freeing the original pointer, so always assign to a temporary variable:

// WRONG — original pointer lost if realloc fails
int *tmp = realloc(buf, new_size);
buf = tmp; // if tmp is NULL, buf is leaked
// CORRECT — preserve original pointer
int *tmp = realloc(buf, new_size);
if (tmp == NULL) {
    fprintf(stderr, "realloc failed\n");
    free(buf); // free the original buffer
    return 1;
}
buf = tmp; // safe to reassign

Checking System Memory

Before allocating large blocks, you can check available memory on Linux:

free -m
ulimit -v

On Linux, overcommit_memory controls whether the kernel allows allocations beyond physical memory:

cat /proc/sys/vm/overcommit_memory
  • 0: Heuristic overcommit (default).
  • 1: Always overcommit.
  • 2: Never overcommit — allocations are strictly limited.

Avoiding Large Allocations

When possible, stream data instead of loading everything into memory:

// CORRECT — read in chunks
#include <stdio.h>
#include <stdlib.h>

#define CHUNK_SIZE 4096

int main(void) {
    FILE *fp = fopen("largefile.bin", "rb");
    if (!fp) return 1;

    char buf[CHUNK_SIZE];
    size_t bytes_read;
    while ((bytes_read = fread(buf, 1, CHUNK_SIZE, fp)) > 0) {
        process_chunk(buf, bytes_read);
    }

    fclose(fp);
    return 0;
}

Summary

StrategyWhen to Use
Check malloc return for NULLAlways — every allocation
Use callocWhen you need zero-initialized memory
Use temp variable for reallocAlways — preserves original pointer on failure
Stream large files in chunksWhen processing files larger than available RAM
Check free -m / ulimitDuring debugging allocation failures
Handle errno with strerrorAlways — provides a human-readable error message