What are Pointers and 3D Arrays- Learn C Programming Online Free

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Free C Programming Course · Pointers · Lecture 27

Pointers and 3D Arrays

A three-dimensional array is just an array of 2D arrays. See what each level of the pointer ladder points at, with real byte offsets from a program you can run.

13 min
Reading time
Intermediate
Level
1 demo
Compile and run

Working with pointers and 3D arrays in C looks scary until you notice one pattern: every extra dimension just wraps the previous one in another array. In this lecture of our free C programming course you will name every level of a 3D array, see the type of each pointer expression, and verify the byte offsets by running a short program. This is the same thinking you use in embedded C programming when a buffer is organised by board, channel and sample.

What You Will Learn

  • Why a 3D array is an array of 2D arrays, which are arrays of 1D arrays
  • The size and type of every level
  • What arr, *arr and **arr each point at
  • How adding 1 behaves at every level
  • Why a 3D array is not the same as an int ***
  • When firmware actually needs three dimensions

Prerequisites

To follow pointers and 3D arrays in C you need the two previous lectures: pointer addresses in 2D arrays and subscripting a pointer to an array. Both are part of this free C tutorial series and take about ten minutes each.

Pointers and 3D Arrays in C: Building the Mental Model

Let us build a small example. A data logger has 2 ADC boards, each board has 2 channels, and each channel stores 3 samples.

int adc[2][2][3] = {
    { {1, 2, 3},   {4, 5, 6}    },
    { {7, 8, 9},   {10, 11, 12} }
};

Read the declaration from the left. adc is an array of 2 elements. Each element is a board, which is an array of 2 channels. Each channel is an array of 3 ints. So the array is a stack of 2D arrays, and each 2D array is a stack of 1D arrays.

adc[2][2][3] as boards, channels and samples
adc[0] board 0 at +0ch 0123ch 1456
adc[1] board 1 at +24ch 0789ch 1101112

Size and type at every level

ExpressionLevelSize (4-byte int)Type when used in an expression
adcWhole array48 bytesint (*)[2][3], pointer to a board
adc[i], *(adc+i)One board24 bytesint (*)[3], pointer to a channel
adc[i][j]One channel12 bytesint *, pointer to a sample
adc[i][j][k]One sample4 bytesint, the value

Each time you apply * or a subscript you peel off the leftmost dimension, and the type loses one pair of brackets. This is why the step size changes at every level: stepping a board pointer moves 24 bytes, stepping a channel pointer moves 12, and stepping a sample pointer moves 4.

The Pointer Ladder for Pointers and 3D Arrays in C

Here is the full ladder, from the array name down to a single value. Memorise this table once and the long expressions stop looking mysterious.

ExpressionWhat it gives you
adcPointer to board 0
adc + iPointer to board i
*(adc + i)Board i itself, which decays to a pointer to its channel 0
*(adc + i) + jPointer to channel j of board i
*(*(adc + i) + j)Channel j of board i, which decays to a pointer to its sample 0
*(*(adc + i) + j) + kPointer to sample k of channel j of board i
*(*(*(adc + i) + j) + k)The value of that sample
Peeling one dimension per dereference
adcint (*)[2][3]step = 24 bytes
*adcint (*)[3]step = 12 bytes
**adcint *step = 4 bytes
***adcintvalue 1

Demo: Measuring Every Level

The next program is the smallest demo of pointers and 3D arrays in C that still shows every level. It declares one pointer for each level and prints how far a single step moves it. Save it as ep_plane_walk.c.

#include <stdio.h>

#define BOARDS   2
#define CHANNELS 2
#define SAMPLES  3

int main(void)
{
    /* adc[board][channel][sample] */
    int adc[BOARDS][CHANNELS][SAMPLES] = {
        { {1, 2, 3},   {4, 5, 6}    },
        { {7, 8, 9},   {10, 11, 12} }
    };
    const char *base = (const char *)adc;

    printf("whole array : %zu bytes\n", sizeof adc);
    printf("one board   : %zu bytes\n", sizeof adc[0]);
    printf("one channel : %zu bytes\n", sizeof adc[0][0]);
    printf("one sample  : %zu bytes\n", sizeof adc[0][0][0]);

    int (*board)[CHANNELS][SAMPLES] = adc;   /* walks boards   */
    int (*chan)[SAMPLES]            = *adc;  /* walks channels */
    int *cell                       = **adc; /* walks samples  */

    printf("board+1 at +%td\n", (const char *)(board + 1) - base);
    printf("chan+1  at +%td\n", (const char *)(chan + 1) - base);
    printf("cell+1  at +%td\n", (const char *)(cell + 1) - base);

    printf("*(adc+1)       at +%td\n", (const char *)*(adc + 1) - base);
    printf("*(adc+1)+1     at +%td\n", (const char *)(*(adc + 1) + 1) - base);
    printf("**(adc+1)      at +%td\n", (const char *)**(adc + 1) - base);

    return 0;
}
gcc -std=c11 -Wall -Wextra -pedantic -o ep_plane_walk ep_plane_walk.c
./ep_plane_walk

Expected output (4-byte int):

whole array : 48 bytes
one board   : 24 bytes
one channel : 12 bytes
one sample  : 4 bytes
board+1 at +24
chan+1  at +12
cell+1  at +4
*(adc+1)       at +24
*(adc+1)+1     at +36
**(adc+1)      at +24

What each line tells you

  • The sizes: 48 = 2 boards x 24, 24 = 2 channels x 12, 12 = 3 samples x 4.
  • board+1 is +24: a board pointer jumps a whole board.
  • chan+1 is +12 and cell+1 is +4: the same address, but the smaller the target type, the smaller the step.
  • *(adc+1) is +24: board 1 starts right after the 24 bytes of board 0.
  • *(adc+1)+1 is +36: inside board 1, one channel step (12 bytes) is added to 24.
  • **(adc+1) is +24: sample 0 of channel 0 of board 1 is the first byte of board 1.

Pattern

The address stays the same as you dereference, only the type changes. The compiler uses the type to decide how big each step is.

A 3D Array Is Not an int ***

This is the biggest trap in the topic. Beginners see three subscripts and three stars and assume that int *** can stand in for a 3D array. It cannot. An int *** points at a pointer that points at a pointer that points at an int, and each of those objects can live anywhere in memory. A real 3D array is one block of contiguous ints, and the compiler computes addresses by arithmetic alone.

Propertyint adc[2][2][3]int ***pp
MemoryOne contiguous blockScattered blocks linked by pointers
Address of [i][j][k]Computed with multiplication and addFound by following pointers
Extra storage for pointersNoneMany pointer variables
Passable to a function taking int ***NoYes
Warning: casting a 3D array to int *** to silence a compiler message compiles fine and then crashes or corrupts memory at run time, because the integers are treated as addresses.

When Firmware Uses Pointers and 3D Arrays in C

  • Image or sensor frames with rows, columns and colour channels.
  • Multi-board or multi-sensor data logging: board, channel, sample.
  • History buffers for an IMU: axis, window, filter stage.
  • Calibration tables indexed by temperature range, gain setting and channel.

Keep an eye on memory. A large 3D array declared as an ordinary local variable lives on the stack, and small microcontrollers have very little stack. Declare big tables static or global, and mark read-only tables const so the linker can place them in flash.

Common Mistakes With Pointers and 3D Arrays in C

MistakeResultFix
Treating int *** as a 3D arrayWrong addresses, crashesUse int (*)[C][S] for the board pointer
Forgetting that each level has its own step sizeOff-by-one-block readsCheck the type at every level
Mixing the order of dimensionsSamples read as channelsName the dimensions with constants and keep the order in comments
Large local 3D array in an embedded functionStack overflowMake it static or global

Best Practices for Pointers and 3D Arrays in C

  • Declare each dimension with a named constant, never a bare number.
  • Add a comment with the meaning of the dimensions in order, for example board, channel, sample.
  • Prefer the subscript form in production code; use pointer form for understanding and debugging.
  • Performance: iterate with the last index in the innermost loop so that memory is read in order.
  • Safety: validate board and channel numbers that come from outside the program before indexing.

Summary and Key Takeaways

  • A 3D array is an array of 2D arrays, stored contiguously.
  • adc, *adc and **adc point at a board, a channel and a sample, with steps of 24, 12 and 4 bytes in our example.
  • Each dereference removes one dimension from the type.
  • *(*(*(adc+i)+j)+k) is the long way of writing adc[i][j][k].
  • A 3D array is not an int ***, and the two are not interchangeable.

That is the full picture of pointers and 3D arrays in C. In the next lecture we turn this ladder into a loop that visits every element and prove the address formula with code. Keep going through this free embedded C course to finish the chapter.

Frequently Asked Questions

How do pointers work with 3D arrays in C?

The array name decays to a pointer to its first 2D block. Each dereference removes one dimension, so the step size shrinks from a board to a channel to a single element.

Is a 3D array the same as an int ***?

No. A 3D array is one contiguous block of values. An int *** is a chain of pointers. Their memory layout and types are different.

What is the type of the array name for int a[2][3][4]?

In an expression it decays to int (*)[3][4], a pointer to a 3 by 4 block.

How many bytes does a[2][2][3] of int occupy?

With 4-byte ints it is 2 x 2 x 3 x 4 = 48 bytes. The product of all dimensions times sizeof(int).

Why does *(adc+1) print the same offset as **(adc+1)?

Dereferencing changes the type but not the address. Both point to the first byte of board 1.

Should I use 3D arrays in embedded C programming?

Yes, when the data really has three natural dimensions and fixed sizes. Place large ones in static or flash memory instead of on the stack.

Free Embedded C Course

Every lecture in this free C programming course is tested code with real output. Continue to the next lecture or open the course index to jump to any topic.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *