Skip to content

Intermediate · Dynamic memory · topic 30 of 60 · 25 min

Dynamic strings and 2D arrays

Explanation

To store text whose length is known only at run time, allocate strlen + 1 bytes (or use strdup, standard since C23 and long available in POSIX) and free it later. A 2D table of run-time size can be an array of row pointers (each row allocated separately) or one contiguous block indexed as row * cols + col. Free rows before the row array.

Copying a string to the heap

c
char *copy_string(const char *src) {
    size_t len = strlen(src);
    char *dst = malloc(len + 1);          /* + 1 for '\0' */
    if (!dst) return NULL;
    memcpy(dst, src, len + 1);
    return dst;                           /* the caller must free it */
}

char *name = copy_string("Meenakshi");    /* or: char *name = strdup("Meenakshi"); */
free(name);
Careful: malloc(strlen(s)) without + 1 is one of the most common C bugs: the terminator is written one byte past the block (a heap overflow that may not crash until much later). Always allocate strlen + 1 for a string copy.

Building a string of unknown length

c
char *make_greeting(const char *name, const char *city) {
    int needed = snprintf(NULL, 0, "Namaste %s from %s!", name, city);   /* measure only */
    if (needed < 0) return NULL;
    char *s = malloc((size_t) needed + 1);
    if (!s) return NULL;
    snprintf(s, (size_t) needed + 1, "Namaste %s from %s!", name, city);
    return s;
}
snprintf(NULL, 0, …) returns the length the text would need.

2D arrays: an array of row pointers

c
int rows = 3, cols = 4;
int **grid = malloc(rows * sizeof *grid);          /* an array of row pointers */
for (int r = 0; r < rows; r++) {
    grid[r] = calloc(cols, sizeof *grid[r]);       /* each row separately */
}
grid[1][2] = 7;

/* free in reverse order: rows first, then the row array */
for (int r = 0; r < rows; r++) free(grid[r]);
free(grid);

grid ──► [ row0 ptr ] ──► | 0 | 0 | 0 | 0 |
         [ row1 ptr ] ──► | 0 | 0 | 7 | 0 |
         [ row2 ptr ] ──► | 0 | 0 | 0 | 0 |
Easy to use with grid[r][c]; rows can even have different lengths. (Error checks omitted for brevity; check every allocation in real code.)

2D arrays: one contiguous block

c
int *cells = calloc((size_t) rows * cols, sizeof *cells);
cells[1 * cols + 2] = 7;                  /* row 1, column 2 */
free(cells);                              /* one free */
One allocation, better cache behaviour and simpler freeing; common for images and matrices.
Array of row pointersOne contiguous block
Indexinggrid[r][c]cells[r * cols + c]
Allocationsrows + 11
FreeingEach row, then the arrayOne free
Jagged rowsPossibleNo
SpeedRows may be scattered in memoryCache-friendly

Arrays of strings

c
char **names = malloc(count * sizeof *names);
for (int i = 0; i < count; i++) names[i] = strdup(input[i]);   /* each name its own block */
/* … */
for (int i = 0; i < count; i++) free(names[i]);
free(names);
Think of it like this: An array of row pointers is like a register listing which shelf each family's books are on: easy to find "family 2, book 3", but the shelves can be anywhere in the library. A contiguous block is one long bookshelf where you count along: row × width + column.
Tip: When a structure owns several allocations (a grid, a list of names), write one function that frees all of it, and call that function on every exit path.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Dynamic strings and 2D arrays · 1/6

Dynamic strings and 2D arrays

In this lesson you will allocate strings and tables at run time.

Prefer a video by a teacher? Find free videos on “c dynamic 2d array malloc array of strings strdup” on YouTube ↗

In real life: A seating chart sized at run time

An exam-hall allocation tool reads the number of rows and benches per row for each hall from a file, then allocates a contiguous grid of roll numbers. One allocation per hall keeps freeing simple and printing fast.

c
int *seats = calloc((size_t) rows * benches, sizeof *seats);
if (!seats) return 1;
int roll = 1001;
for (int r = 0; r < rows; r++)
    for (int b = 0; b < benches; b++)
        seats[r * benches + b] = roll++;
/* print, then */
free(seats);

Practice (0/3 done)

Task 1 · Question

Which allocation correctly holds a copy of the string s?

Task 2 · Question

A grid was made with grid = malloc(rows * sizeof *grid) and a malloc per row. What is the correct way to free it?

Task 3 · Practice

Write char *join_names(const char **names, int n) that returns one heap string with all names separated by ", " (measure the total length first, allocate once, then build it). Also write a matrix allocator int *matrix_new(int rows, int cols) using one calloc, and a function to print it. Free everything and check with a leak checker.

Quick quiz

Question 1

1.What does strdup do?

Question 2

2.In a contiguous rows × cols block, where is element (r, c)?

Question 3

3.How many frees does an array of 5 row pointers (each row malloc'd) need?

Question 4

4.What does snprintf(NULL, 0, "%d", 12345) return?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does strdup do?

Answer: Allocates a copy of a string on the heap

The caller must free the result.

In a contiguous rows × cols block, where is element (r, c)?

Answer: cells[r * cols + c]

Row-major indexing.

How many frees does an array of 5 row pointers (each row malloc'd) need?

Answer: 6

5 rows + the pointer array.

What does snprintf(NULL, 0, "%d", 12345) return?

Answer: 5

The number of characters the text would need (without the terminator).

What is the index of the last item in an array of length 8?Related

Answer: 7

Indexes go from 0 to Length - 1.

From Arrays: one name, many values

In a function parameter for a 2D int array with 4 columns, which is valid?Related

Answer: int g[][4]

All dimensions except the first are required.

From 2D arrays and classic array algorithms

What is the index of the LAST element of an array of size 10?Related

Answer: 9

Indexes run from 0 to size - 1.

From Arrays, std::array and a first look at std::vector

In int[][] t = new int[3][5]; what is t.length and t[0].length?Related

Answer: 3 and 5

First dimension rows (3), second columns (5).

From Two-dimensional arrays and array algorithms

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next: Structs →

Next steps

  1. This is the last topic of “Dynamic memory”. The module exam comes next.
  2. Module 6 exam: Dynamic memory · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Structs, unions, enums and files. 60 topics left in the course before the final exam.

← Previous: Memory leaks and other heap bugs