A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
You cannot return an array from a C function. Arrays are not first-class values. They decay to pointers. A local array is destroyed when the function returns. So what can you do? Four patterns: return a pointer to a static array, return a pointer to heap-allocated memory, have the caller provide the array, or wrap the array in a struct. Each has ownership implications. Choosing wrong gives you a dangling pointer, a memory leak, or a buffer overflow.
#include <stdio.h>void fill_squares(int *out, int n) {for (int i = 0; i < n; i++) {out[i] = i * i;}}int main(void) {int result[5];fill_squares(result, 5);printf("Squares: ");for (int i = 0; i < 5; i++) printf("%d ", result[i]);printf("\n");return 0;}
#include <stdio.h>#include <stdlib.h>int *make_range(int start, int count) {int *arr = malloc(count * sizeof(int));if (arr == NULL) return NULL;for (int i = 0; i < count; i++) {arr[i] = start + i;}return arr;}int main(void) {int *data = make_range(10, 5);if (data) {printf("Range: ");for (int i = 0; i < 5; i++) printf("%d ", data[i]);printf("\n");free(data);}return 0;}
#include <stdio.h>#define MAX_SIZE 16typedef struct {int data[MAX_SIZE];int length;} IntArray;IntArray make_fibs(int count) {IntArray arr = {.length = count};if (count > MAX_SIZE) count = MAX_SIZE;arr.data[0] = 0;if (count > 1) arr.data[1] = 1;for (int i = 2; i < count; i++) {arr.data[i] = arr.data[i-1] + arr.data[i-2];}arr.length = count;return arr;}int main(void) {IntArray fibs = make_fibs(10);printf("Fibonacci: ");for (int i = 0; i < fibs.length; i++) printf("%d ", fibs.data[i]);printf("\n");return 0;}
The struct wrapper returns the array and its length together. The struct is returned by value, so the data is copied to the caller's stack frame. No allocation. No free. No dangling pointer. The downside is the fixed maximum size. For variable-length data, combine the struct wrapper with a heap-allocated data pointer inside the struct.
Returning arrays is a design decision about ownership and lifetime. Caller-provided buffers are safest. Heap allocation is most flexible. Struct wrappers are most convenient for fixed-size data. Choose based on who owns the memory and how long it must live. Next: returning a dynamically allocated 2D array, which adds a layer of complexity.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.