C Memory Management
C - Memory Management
Section titled “C - Memory Management”C programs use memory divided into several segments: the text segment (code), data segment (global and static variables), stack (local variables, function calls), and heap (dynamic memory). This chapter focuses on dynamic memory management on the heap.
Dynamic memory allocation allows programs to request memory at runtime as needed, rather than allocating fixed amounts at compile time. This is crucial when the amount of memory required is unknown beforehand or varies significantly. The standard library <stdlib.h> provides key functions for this purpose.
| S.N. | Function | Description |
|---|---|---|
| 1 | void *malloc(size_t size); | Allocates a block of size bytes of uninitialized memory from the heap. Returns a void* pointer to the start of the block, or NULL if allocation fails. |
| 2 | void *calloc(size_t num, size_t size); | Allocates memory for an array of num elements, each of size bytes. The allocated memory is initialized to zero. Returns a void* pointer, or NULL on failure. |
| 3 | void *realloc(void *ptr, size_t new_size); | Changes the size of the memory block pointed to by ptr to new_size bytes. It might move the block to a new location. Returns a pointer to the potentially relocated block, or NULL if resizing fails (the original block remains allocated in this case). If ptr is NULL, it behaves like malloc(new_size). If new_size is 0, it might free the memory and return NULL (behavior can vary). |
| 4 | void free(void *ptr); | Deallocates the memory block previously allocated by malloc, calloc, or realloc and pointed to by ptr. Passing a NULL pointer to free is safe and does nothing. Crucially, allocated memory must be freed when no longer needed to prevent memory leaks. |
Note: size_t is an unsigned integer type defined in <stdlib.h> (and other headers) suitable for representing sizes of objects in memory.
Allocating Memory Dynamically
Section titled “Allocating Memory Dynamically”If you know the exact size needed at compile time, you can use arrays on the stack or static memory. For example, storing a short name:
char short_name[50]; // Max 49 characters + null terminatorHowever, consider storing a user-provided description of unknown length. We can’t define a fixed-size array that’s guaranteed to be large enough without potentially wasting huge amounts of memory. Dynamic allocation solves this. We use a pointer and allocate memory on the heap using malloc:
#include <stdio.h>#include <stdlib.h> // For malloc, free, fprintf, stderr#include <string.h> // For strcpy
int main(void) { // Use standard main signature char name[100]; // Stack allocated buffer for a known max size char *description = NULL; // Pointer for dynamically allocated memory, initialize to NULL
strcpy(name, "Zara Ali");
// Allocate memory dynamically for the description // Let's say we estimate needing 200 chars initially size_t description_size = 200; description = malloc(description_size * sizeof(char)); // sizeof(char) is always 1, but good practice
// *** ALWAYS check the return value of malloc! *** if (description == NULL) { fprintf(stderr, "Error: Unable to allocate required memory for description.\n"); return 1; // Indicate failure }
// Memory allocated successfully, now we can use it strcpy(description, "Zara Ali is a student."); // Ensure string fits allocated size!
printf("Name = %s\n", name); printf("Description: %s\n", description);
// *** IMPORTANT: Free the allocated memory when done *** free(description); description = NULL; // Good practice to NULL the pointer after freeing
return 0; // Indicate success}When compiled and executed, this might produce:
Name = Zara AliDescription: Zara Ali is a student.Using calloc is similar but initializes the memory to zero:
// Allocate and zero-initialize memory for 200 charactersdescription = calloc(description_size, sizeof(char));if (description == NULL) { /* ... error handling ... */ }
// 'description' now points to 200 bytes of zeroDynamic allocation gives you control over memory lifetime and size at runtime, unlike fixed-size arrays.
Resizing and Releasing Memory
Section titled “Resizing and Releasing Memory”While the operating system reclaims memory when your program exits, it’s crucial best practice to explicitly release memory using free() as soon as it’s no longer needed. Failing to do so leads to memory leaks, where your program consumes more and more memory over time, potentially crashing itself or the system.
Sometimes, the initial allocated size isn’t sufficient. You can resize a previously allocated block using realloc(). Let’s modify the previous example to store a longer description:
#include <stdio.h>#include <stdlib.h> // For malloc, realloc, free, fprintf, stderr#include <string.h> // For strcpy, strcat
int main(void) { char name[100]; char *description = NULL;
strcpy(name, "Zara Ali");
// Initial allocation (e.g., for a short description) size_t initial_size = 30; description = malloc(initial_size * sizeof(char)); if (description == NULL) { fprintf(stderr, "Error: Initial memory allocation failed.\n"); return 1; } strcpy(description, "Zara is a student."); // Fits initial size
printf("Initial Description: %s\n", description);
// Now, suppose we need to add more information const char *additional_info = " She studies Computer Science."; size_t current_len = strlen(description); size_t additional_len = strlen(additional_info); size_t required_size = current_len + additional_len + 1; // +1 for null terminator
// Resize the memory block if needed if (required_size > initial_size) { char *new_description = realloc(description, required_size);
// *** ALWAYS check the return value of realloc! *** if (new_description == NULL) { fprintf(stderr, "Error: Memory reallocation failed.\n"); // IMPORTANT: 'description' still points to the old block! // We must still free the original block before exiting. free(description); return 1; } // Reallocation successful, update the pointer description = new_description; }
// Now we have enough space, append the additional info strcat(description, additional_info); // strcat is safe now
printf("Name = %s\n", name); printf("Full Description: %s\n", description);
// *** Release the final allocated memory *** free(description); description = NULL;
return 0;}Executing this code would output:
Initial Description: Zara is a student.Name = Zara AliFull Description: Zara is a student. She studies Computer Science.If you tried the strcat() without realloc() in the example above, you would likely cause a buffer overflow, writing past the allocated memory boundary. This leads to undefined behavior, often crashing the program or corrupting data, and can be a serious security vulnerability.
Common Pitfalls:
Section titled “Common Pitfalls:”- Memory Leaks: Forgetting to
free()allocated memory. - Dangling Pointers: Using a pointer after the memory it pointed to has been
free()d. - Double Free: Calling
free()twice on the same pointer. - Invalid Free: Calling
free()on a pointer not returned bymalloc/calloc/realloc(e.g., a stack variable address). - Not Checking
malloc/calloc/reallocReturn: Assuming allocation succeeded without checking forNULL.
Tools like Valgrind (on Linux/macOS) can help detect memory errors.