A Problem with Strings in Two-Dimensional Arrays

Declaring an array of strings as a pointer has many advantages, as demonstrated in last week’s Lesson. Above all, I like that I don’t have to calculate the longest string’s length to set the size for the second dimension. But what happens when set this size incorrectly?

Here is how I prefer to set an array of strings:

char *a[] = { "alfa", "bravo", "charlie",
    "delta", "echo", "foxtrot", "golf",
    "hotel"
};

Internally, each string is allocated its own chunk of memory. The array is composed of a series of pointers which reference where each string dwells in memory. Figure 1 illustrates how this works by using debugging information from the program:

Memory locations and their contents, strings.

Figure 1. How an array of strings maps out by using pointers (addresses) that reference each string in memory.

Here is a hexdump of the data stored in memory, which shows another perspective on how the data is packed:

00000000  61 6c 66 61 00 62 72 61  76 6f 00 63 68 61 72 6c  |alfa.bravo.charl|
00000010  69 65 00 64 65 6c 74 61  00 65 63 68 6f 00 66 6f  |ie.delta.echo.fo|
00000020  78 74 72 6f 74 00 67 6f  6c 66 00 68 6f 74 65 6c  |xtrot.golf.hotel|
00000030  00 20 25 73 00 00 00 00  01 1b 03 3b 28 00 00 00  |. %s.......;(...|

Note how each string is stored one after the other, the null byte carefully separating each?

Now consider this array:

char a[8][8] = { "alfa", "bravo", "charlie",
    "delta", "echo", "foxtrot", "golf",
    "hotel"
};

Array a[][] is composed of a grid of 64 characters. It contains eight strings, each allocated 8 characters for each string. Figure 2 illustrates how this data squats in memory.

memory locations and strings

Figure 2. How a two-dimensional array stores the eight strings.

From Figure 2, you see wasted space. Fourteen bytes are unused in the grid, which doesn’t seem like much but it’s close to 20 percent of storage. For larger data stored in this manner, a lot of memory is wasted. Here is how the memory dump looks:

00000000  61 6c 70 68 61 00 00 00  62 72 61 76 6f 00 00 00  |alpha...bravo...|
00000010  63 68 61 72 6c 69 65 00  64 65 6c 74 61 00 00 00  |charlie.delta...|
00000020  65 63 68 6f 00 00 00 00  66 6f 78 74 72 6f 74 00  |echo....foxtrot.|
00000030  67 6f 6c 66 00 00 00 00  68 6f 74 65 6c 00 00 00  |golf....hotel...|

The following is code that outputs the two-dimensional array’s data:

2025_03_15-Lesson.c

#include <stdio.h>

int main()
{
    char a[8][8] = { "alpha", "bravo", "charlie",
        "delta", "echo", "foxtrot", "golf",
        "hotel"
    };
    int x;

    for( x=0; x<8; x++ )
        puts(a[x]);

    return 0;
}

The output is predictably boring:

alpha
bravo
charlie
delta
echo
foxtrot
golf
hotel

What happens when the second dimension is set to 7 instead of 8? Yes, the program builds without any warnings or errors. But the gird is too tight to hold the null characters after strings charlie and foxtrot. Here’s the updated output:

alpha
bravo
charliedelta
delta
echo
foxtrotgolf
golf
hotel

Eight strings are output, as each string is still referenced by its starting character in elements a[0][0] through a[7][0]. Because the null character after the longest strings is clobbered in memory, you see bad data output in the form of charliedelta and foxtrotgolf. A hexdump of memory shows how tightly and improperly the data is packed:

00000000  61 6c 70 68 61 00 00 62  72 61 76 6f 00 00 63 68  |alpha..bravo..ch|
00000010  61 72 6c 69 65 64 65 6c  74 61 00 00 65 63 68 6f  |arliedelta..echo|
00000020  00 00 00 66 6f 78 74 72  6f 74 67 6f 6c 66 00 00  |...foxtrotgolf..|
00000030  00 68 6f 74 65 6c 00 00  00 00 00 00 00 00 00 00  |.hotel..........|

All this information just reinforces why I prefer to use an array of pointers to store strings as opposed to a two-dimensional array. You’re free to use both, but I find the limitations and awkwardness of dealing with a two-dimensional array to outweigh any negative consequences of using pointers.

Leave a Reply