Oops: A (Not Really) 3D Array

I screwed up some code in a most delightful manner, all without knowing what I did. In fact, it was a student who ran my less-than-brilliant code and discovered the obvious flaw. But I was clueless.

The code in question demonstrates a three dimensional array simulating three tic-tac-toe game boards. The 3D array is initialized, each value filled with the period character. Then each game board is output one after the other in a fancy way.

2022_01_29-Lesson-a.c

#include <stdio.h>

int main()
{
    char tictactoe[3][3][3];
    int x,y,z;

    /* initialize */
    for(x=0;x<3;x++)
        for(y=0;y<3;y++)
            for(z=0;z<3;z++)
                tictactoe[x][y][z] = '.';

    /* display */
    puts("Tic Tac Toe");
    for(x=0;x<3;x++)
    {
        printf("%*c",x+1,' ');
        for(y=0;y<3;y++)
        {
            for(z=0;z<3;z++)
                printf(" %c ",tictactoe[x][y][z]);
            printf("     ");    /* 5 spaces */
        }
        putchar('\n');
    }

    return(0);
}

Array tictactoe[][][] is declared at Line 5. A triple nested for loop initializes each element in the 3D array to the period character at Line 12: tictactoe[x][y][z] = '.';

Starting at Line 15, the array is output, again using a triple nested for loop and various devious printf() statements. Here is the output:

Tic Tac Toe
  .  .  .     .  .  .       .  .  .
   .  .  .     .  .  .       .  .  .
    .  .  .     .  .  .       .  .  .

So far, so good. But I wanted to drive home the point that the array is stored internally as one-dimensional, and that the multidimensional notation is for the programmer’s benefit.

In my not-so-brilliant efforts at demonstration, I modified the code. First to change the array definition to one dimension:

char tictactoe[27];

Then second, I changed the array’s reference in the initialization. Here is where I screwed up:

tictactoe[x+y+z] = '.';

And finally, I repeated the mistake in the output statement:

printf(" %c ",tictactoe[x+y+z]);

The full source code for this flawed program is available on GitHub, if you really want to barf.

The problem is that the element offsets are calculated cumulatively. Wrong. The proper math is to multiply them by their offset values: 9, 3, and 1. Unfortunately, this goof isn’t apparent in the output, which looks the same as before:

Tic Tac Toe
  .  .  .     .  .  .       .  .  .
   .  .  .     .  .  .       .  .  .
    .  .  .     .  .  .       .  .  .

Internally, however, the tictactoe[] array is only partially initialized: x+y+x at its maximum value is only six: 2+2+2. The rest of the array contains garbage, which isn’t output despite the fancy formatting.

To prove that the code is flawed, I added an initial loop to set all values in the array to periods. Then I used my flawed loop, tictactoe[x+y+x], to set hyphens in the array. Finally, I wrote the correct calculation in the output statement, so that I could see the entire improperly initialized array:

2022_01_29-Lesson-c.c

#include <stdio.h>

int main()
{
    char tictactoe[27];
    int x,y,z;

    /* initialize */
    for(x=0; x<27; x++ )
        tictactoe[x] = '.';

    for(x=0;x<3;x++)
        for(y=0;y<3;y++)
            for(z=0;z<3;z++)
                tictactoe[x+y+z] = '-';

    /* display */
    puts("Tic Tac Toe");
    for(x=0;x<3;x++)
    {
        printf("%*c",x+1,' ');
        for(y=0;y<3;y++)
        {
            for(z=0;z<3;z++)
                printf(" %c ",tictactoe[x*9+y*3+z]);
            printf("     ");    /* 5 spaces */
        }
        putchar('\n');
    }

    return(0);
}

At Line 25, you see the proper math required to access each element in the array:

tictactoe[x*9+y*3+z]

As the output loop, variable x must be multiplied by nine to get the offset correct. Then variable y is multiplied by three to account for the 3×3 grid in a tic-tac-toe game. Variable z is unchanged. Here is the revealing output:

Tic Tac Toe
  -  -  -     -  -  -       -  .  .
   .  .  .     .  .  .       .  .  .
    .  .  .     .  .  .       .  .  .

In the flawed code, only the first seven elements are initialized with [x+y+z]. The range is from zero through six. The rest of the array isn’t touched in the original, flawed code. This issue went unnoticed because my mistake was repeated in both the initialization and the output loops.

I call this mistake a lazy one. I’ve initialized 3D arrays before, but somehow my brain farted and I used [x+y+z] instead of [x*9+y*3+z]. Silly me. Lesson learned.

Leave a Reply