A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
You are reading input of unknown length. You allocated 16 bytes. The input is 200 characters. You need to grow the buffer without losing what you have already read. This is the string builder problem, and realloc is the tool. The pattern: track the current capacity, track the current length, and when length approaches capacity, double the capacity with realloc. Doubling gives you amortized O(1) appends, the same strategy used by C++ std::string and Python lists.
#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct {char *data;size_t length;size_t capacity;} StringBuilder;int sb_init(StringBuilder *sb, size_t initial_cap) {sb->data = malloc(initial_cap);if (sb->data == NULL) return -1;sb->data[0] = '\0';sb->length = 0;sb->capacity = initial_cap;return 0;}int sb_append(StringBuilder *sb, const char *str) {size_t add_len = strlen(str);size_t new_len = sb->length + add_len;if (new_len + 1 > sb->capacity) {size_t new_cap = sb->capacity * 2;while (new_cap < new_len + 1) new_cap *= 2;char *tmp = realloc(sb->data, new_cap);if (tmp == NULL) return -1;sb->data = tmp;sb->capacity = new_cap;}memcpy(sb->data + sb->length, str, add_len + 1);sb->length = new_len;return 0;}void sb_free(StringBuilder *sb) {free(sb->data);sb->data = NULL;sb->length = 0;sb->capacity = 0;}int main(void) {StringBuilder sb;if (sb_init(&sb, 8) != 0) {fprintf(stderr, "init failed\n");return 1;}sb_append(&sb, "Hello");sb_append(&sb, ", ");sb_append(&sb, "world");sb_append(&sb, "! This is a longer string that exceeds the initial capacity.");printf("Result (len=%zu, cap=%zu):\n%s\n", sb.length, sb.capacity, sb.data);sb_free(&sb);return 0;}
Started at 8 bytes. Grew to 16, 32, 64, 128 as needed. Each growth doubles the capacity, so the number of realloc calls is logarithmic in the final length. The memcpy includes the null terminator (+1). The sb_free function nullifies the pointer to prevent use-after-free. This is a production-quality pattern. You will use it in loggers, protocol builders, and any code that assembles strings incrementally.
The string builder is the canonical realloc application. The same pattern extends to dynamic arrays of structs, byte buffers for network I/O, and any growable collection. Next: resizing a dynamic array of arbitrary elements, generalizing the same technique.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.