C Dynamic Strings

Memory allocation for flexible string handling

🔄 What are Dynamic Strings?

Dynamic strings in C use malloc() and related functions to allocate memory at runtime. This allows creating strings of variable sizes, resizing them as needed, and efficiently managing memory for text processing applications.


#include <stdlib.h>
char *str = malloc(50 * sizeof(char));
strcpy(str, "Dynamic string!");
free(str);  // Don't forget to free memory!
                                    

Dynamic Memory Concepts

🏗️

malloc()

Allocate memory dynamically

char *str = malloc(100);
📏

realloc()

Resize allocated memory

str = realloc(str, 200);
🗑️

free()

Release allocated memory

free(str);
🔍

NULL Check

Always check allocation success

if(str == NULL) exit(1);

🔹 Basic Dynamic String Allocation

Dynamic string allocation using malloc() and related functions provides flexible memory management for strings of varying lengths. Unlike fixed-size character arrays, dynamically allocated strings can be resized as needed during program execution. For example, char *str = malloc(50 * sizeof(char)); allocates 50 bytes for a string on the heap, which persists until explicitly freed. Functions like strcpy(), strlen(), and strcat() work seamlessly with dynamic strings just like static arrays. Always remember to free dynamically allocated strings with free(str); when finished to prevent memory leaks that gradually consume available memory and degrade application performance over time.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    // Allocate memory for string
    char *dynamicStr = malloc(50 * sizeof(char));
    
    // Check if allocation was successful
    if(dynamicStr == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    
    // Use the string
    strcpy(dynamicStr, "Hello, Dynamic World!");
    printf("Dynamic string: %s\n", dynamicStr);
    printf("Length: %d\n", strlen(dynamicStr));
    
    // Modify the string
    strcat(dynamicStr, " How are you?");
    printf("Modified: %s\n", dynamicStr);
    
    // Free the memory
    free(dynamicStr);
    dynamicStr = NULL;  // Good practice
    
    return 0;
}

Output:

Dynamic string: Hello, Dynamic World!

Length: 21

Modified: Hello, Dynamic World! How are you?

🔹 Dynamic String Input

Reading strings of unknown length from users requires dynamic memory allocation that grows as characters are received. A common approach allocates an initial buffer, reads characters one by one using getchar() or fgets(), and reallocates with realloc() when the buffer fills. For example, starting with a 16-byte buffer and doubling capacity when full efficiently handles inputs of any size. This technique prevents buffer overflow vulnerabilities common in fixed-size input buffers while accommodating both short and long user inputs gracefully. Always check allocation success and handle newline characters appropriately when processing line-based input from standard input or files.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* readDynamicString() {
    int capacity = 10;  // Initial capacity
    int length = 0;
    char *str = malloc(capacity * sizeof(char));
    char ch;
    
    if(str == NULL) return NULL;
    
    printf("Enter text (press Enter to finish): ");
    
    // Read character by character
    while((ch = getchar()) != '\n' && ch != EOF) {
        // Resize if needed
        if(length >= capacity - 1) {
            capacity *= 2;
            str = realloc(str, capacity * sizeof(char));
            if(str == NULL) return NULL;
        }
        
        str[length] = ch;
        length++;
    }
    
    str[length] = '\0';  // Null terminate
    
    // Shrink to exact size
    str = realloc(str, (length + 1) * sizeof(char));
    
    return str;
}

int main() {
    char *userInput = readDynamicString();
    
    if(userInput != NULL) {
        printf("You entered: %s\n", userInput);
        printf("Length: %d characters\n", strlen(userInput));
        free(userInput);
    }
    
    return 0;
}

Sample Run:

Enter text (press Enter to finish): This is a very long string that grows dynamically!

You entered: This is a very long string that grows dynamically!

Length: 53 characters

🔹 Resizing Dynamic Strings

The realloc() function enables growing and shrinking dynamically allocated strings as program requirements change during execution. This function takes an existing allocation and a new size, attempting to resize the memory block in place or allocating a new block and copying data if necessary. For example, str = realloc(str, new_size); adjusts the string's capacity efficiently. After expansion, you can append more data; after shrinking, you optimize memory usage. Always assign the return value back to your pointer since realloc() may move the data to a different memory location. Check for NULL returns to detect allocation failures and handle them appropriately.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    // Start with small string
    char *str = malloc(10 * sizeof(char));
    strcpy(str, "Hello");
    
    printf("Initial: %s (allocated: 10 bytes)\n", str);
    
    // Need more space for concatenation
    str = realloc(str, 30 * sizeof(char));
    if(str == NULL) {
        printf("Reallocation failed!\n");
        return 1;
    }
    
    strcat(str, " World! This is longer.");
    printf("Expanded: %s (allocated: 30 bytes)\n", str);
    
    // Shrink to exact size needed
    int needed = strlen(str) + 1;
    str = realloc(str, needed * sizeof(char));
    printf("Optimized: %s (allocated: %d bytes)\n", str, needed);
    
    free(str);
    return 0;
}

Output:

Initial: Hello (allocated: 10 bytes)

Expanded: Hello World! This is longer. (allocated: 30 bytes)

Optimized: Hello World! This is longer. (allocated: 29 bytes)

🔹 Dynamic String Array

Creating arrays of dynamic strings requires allocating memory for both the pointer array and each individual string. First, allocate an array of character pointers: char **strings = malloc(count * sizeof(char*));, then allocate each string individually: strings[i] = malloc(length);. This two-level allocation creates a flexible structure where each string can have different lengths, unlike two-dimensional arrays with fixed row sizes. When finished, free each string individually before freeing the pointer array to prevent memory leaks. This data structure is essential for handling collections of variable-length text like command-line arguments, file contents, or database query results.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int numStrings = 3;
    char **stringArray;
    
    // Allocate array of string pointers
    stringArray = malloc(numStrings * sizeof(char*));
    
    // Allocate each string
    stringArray[0] = malloc(20 * sizeof(char));
    stringArray[1] = malloc(30 * sizeof(char));
    stringArray[2] = malloc(25 * sizeof(char));
    
    // Initialize strings
    strcpy(stringArray[0], "First string");
    strcpy(stringArray[1], "Second longer string");
    strcpy(stringArray[2], "Third string here");
    
    // Display all strings
    printf("Dynamic string array:\n");
    for(int i = 0; i < numStrings; i++) {
        printf("%d: %s (length: %d)\n", i+1, stringArray[i], strlen(stringArray[i]));
    }
    
    // Free all memory
    for(int i = 0; i < numStrings; i++) {
        free(stringArray[i]);
    }
    free(stringArray);
    
    return 0;
}

Output:

Dynamic string array:

1: First string (length: 12)

2: Second longer string (length: 20)

3: Third string here (length: 17)

🔹 Practical Dynamic String Functions

Building a library of reusable dynamic string utility functions simplifies common string operations and reduces code duplication. Useful functions include dynamic string duplication str_dup() that allocates and copies a string, dynamic concatenation str_concat() that joins strings with automatic resizing, and dynamic input functions that safely read unlimited-length strings. For example, a read_line() function can encapsulate the complex logic of growing buffers during input, providing a simple interface to the rest of your program. Well-designed string utilities improve code reliability, maintainability, and security by centralizing memory management and error handling in thoroughly tested, reusable functions.

Create Dynamic Copy:

char* createCopy(const char* source) {
    if(source == NULL) return NULL;
    
    int len = strlen(source);
    char* copy = malloc((len + 1) * sizeof(char));
    
    if(copy != NULL) {
        strcpy(copy, source);
    }
    return copy;
}

Concatenate Multiple Strings:

char* concatenateStrings(char* strings[], int count) {
    int totalLen = 0;
    
    // Calculate total length needed
    for(int i = 0; i < count; i++) {
        totalLen += strlen(strings[i]);
    }
    
    char* result = malloc((totalLen + 1) * sizeof(char));
    if(result == NULL) return NULL;
    
    result[0] = '\0';  // Start with empty string
    for(int i = 0; i < count; i++) {
        strcat(result, strings[i]);
    }
    
    return result;
}

Safe String Append:

char* appendString(char* original, const char* toAppend) {
    if(original == NULL || toAppend == NULL) return original;
    
    int oldLen = strlen(original);
    int newLen = oldLen + strlen(toAppend);
    
    original = realloc(original, (newLen + 1) * sizeof(char));
    if(original != NULL) {
        strcat(original, toAppend);
    }
    
    return original;
}

🔹 Memory Management Best Practices

Dynamic memory management is crucial when working with strings in C. When allocating memory for strings, always use malloc() to reserve space on the heap and remember to free the allocated memory with free() to prevent memory leaks. Properly managing dynamic strings involves initializing them with appropriate sizes, checking if allocation was successful, and explicitly deallocating memory when it's no longer needed to maintain efficient program performance.

✅ Do:

  • Always check if malloc/realloc returns NULL
  • Free every allocated pointer exactly once
  • Set pointers to NULL after freeing
  • Use valgrind or similar tools to check for leaks

❌ Don't:

  • Use freed pointers (dangling pointers)
  • Free the same pointer twice
  • Forget to free allocated memory
  • Access memory outside allocated bounds

🧠 Test Your Knowledge

What function is used to release dynamically allocated memory?