Gabriel Staples
Gabriel Staples

Reputation: 52449

How to make a 1D PROGMEM array (stored in Flash) of 2D PROGMEM arrays on AVR (ex: ATMega) or Arduino microcontrollers

I have several 2D arrays in PROGMEM.

I want to store them into another array, hence having a PROGMEM array of 2d PROGMEM arrays.

Then I want to read the data out.

Here's what I have

void myFunc()
{
  const static byte DATA1[3][2] PROGMEM = 
  {
    -1, 6,
    -3, 6,
    -5, 5
  };
  const static byte DATA2[3][2] PROGMEM = 
  {
    1,  0,
    1,  0,
    1,  0
  };
  const static byte DATA3[6][2] PROGMEM = 
  {
    0,  1,
    1,  3,
    2,  4,
    3,  4,
    4,  3,
    5,  1
  };
  //PROGMEM array of 2d arrays in PROGMEM
  const static byte* const MY_DATA[] PROGMEM = {DATA1, DATA2, DATA3}; 
  
  //read out the data now:
  byte myData = pgm_read_byte(&((MY_DATA[arrayNum])[x][y]));

  //use the data here, etc, etc...
}

My error is:

error: cannot convert 'const byte (*)[2] {aka const 
unsigned char (*)[2]}' to 'const byte* const {aka const
unsigned char* const}' in initialization

How am I supposed to do this? I've been reading around and trying to figure this out for well over an hr. I don't know what I'm doing wrong.

Helpful Reference pages:

  1. http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
  2. http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga963f816fc88a5d8479c285ed4c630229
  3. https://www.arduino.cc/en/Reference/PROGMEM

Related:

Update: 6 years later (June 2022 now) and this is still super confusing stuff!

Here are a couple related answers I wrote though which help add further clarity. Using a typedef would significantly improve clarity in the accepted answer here as well.

  1. [my answer] Passing 1D arrays as function parameters in C (and C++)
  2. [my answer] How to use multidimensional (ex: 2D) arrays, and pointers to them, as function parameters in C and C++

Upvotes: 1

Views: 1399

Answers (2)

Gabriel Staples
Gabriel Staples

Reputation: 52449

@MikeCAT's answer is correct.

But, here's how to typedef it to make it much easier to read, understand, and use!:

How to make a 1D PROGMEM array of 2D PROGMEM arrays

Use a typedef to create a 1D array of 2D arrays which naturally decay (adjust) to type "pointer to a 1D array of 2 bytes".

Typedefs can help with clarity a lot like this when dealing with complicated pointer definitions, such as pointers to multi-dimensional arrays, or pointers to functions. typedef byte (*array2d_t)[2]; defines array2d_t as a type byte (*)[2], which means "pointer to an array of 2 bytes". This is the pointer type which the 2d array byte array2d[][2] ("n by 2 (2D) array of bytes") naturally adjusts to. For more on this topic, see my references below.

Here is a much-easier-to-read version using a typedef:

// 1. define the 1D array of 2D arrays
// First, make a typedef of a _2D array_ as a _ptr to a 1D array_
// (same thing) to make it **much** easier to use! I've created a `const`
// typedef and a non-const typedef here:
typedef byte (*array2d_t)[2];
// Note: the left-most `const` below makes the `byte`s being pointed-to
// `const`, whereas the right-most `const` makes the actual ptr itself
// `const`, NOT what it points to.
typedef const byte (* const array2d_const_t)[2];

static const array2d_const_t ALL_DATA[] PROGMEM =
{
    DATA1,
    DATA2,
    DATA3,
};

// 2. read out a byte
byte my_data = pgm_read_byte(&((ALL_DATA[array_index])[x][y]));

Notice how much clearer that ALL_DATA[] definition above is over this hard-to-read ALL_DATA2[] definition below!:

// 1. define the 1D array of 2D arrays
static const byte (* const ALL_DATA2[])[2] PROGMEM = 
{
    DATA1,
    DATA2,
    DATA3,
};

// 2. read out a byte
byte my_data = pgm_read_byte(&((ALL_DATA2[array_index])[x][y]));

For the typedefs above, here is a case where you must use the non-const version of the typedef so that you can re-assign the variable. Notice that I must use the non-const array2d_t type here instead of the const array2d_const_t type:

array2d_t array2d;
size_t i_data;
// DATA1
i_data = 0;
array2d = pgm_read_ptr(&ALL_DATA[i_data]);
print_2d_progmem_array(array2d, NUM_ROWS(DATA1));
// DATA2
i_data = 1;
array2d = pgm_read_ptr(&ALL_DATA[i_data]);
print_2d_progmem_array(array2d, NUM_ROWS(DATA2));
// DATA3
i_data = 2;
array2d = pgm_read_ptr(&ALL_DATA[i_data]);
print_2d_progmem_array(array2d, NUM_ROWS(DATA3));

Here is a full, runnable Arduino/AVR example:

progmem_array_of_2d_arrays.ino from my eRCaGuy_hello_world repo:

// Get the number of elements in any C array
// - from my repo here:
//   https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/utilities.h#L42
#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))

/// Definitions: `rows` = "rows"; `cols` = "columns"

/// Get number of rows in a 2D array
#define NUM_ROWS(array_2d) ARRAY_LEN(array_2d)

/// Get number of columns in a 2D array
#define NUM_COLS(array_2d) ARRAY_LEN(array_2d[0])


/// \brief      Print a 2D array which has a VARIABLE number of rows but
///             FIXED number of columns.
///     This code is from Example 2 here:
///     https://stackoverflow.com/a/67814330/4561887
///     For PROGMEM help, see:
///     1. ***** [main AVR tutorial]
///        https://www.nongnu.org/avr-libc/user-manual/pgmspace.html
///     1. [Arduino reference]
///        https://www.arduino.cc/reference/en/language/variables/utilities/progmem/
///     1. [AVR reference pg]
///        https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
/// \param[in]  array_2d    a 2D array; is of type `int [][2]` (n x 2 (2D) array
///                         of ints), which naturally decays to type
///                         `int (*)[2]` (ptr to (1D) array of 2 ints)
/// \param[in]  num_rows    The number of rows in the array
/// \return     None
void print_2d_progmem_array(const byte array_2d[][2], size_t num_rows)
{
    Serial.println(F("print_2d_progmem_array:"));

    for (size_t row = 0; row < num_rows; row++)
    {
        for (size_t col = 0; col < NUM_COLS(array_2d); col++)
        {
            // This works!:
            // byte val = pgm_read_byte(&array_2d[row][col]);

            // But, let's print out the address too, for learning
            uint16_t address = (uint16_t)(&array_2d[row][col]);
            byte val = pgm_read_byte(address);

            Serial.print(F("array_2d["));
            Serial.print(row);
            Serial.print("]");
            Serial.print("[");
            Serial.print(col);
            Serial.print(F("]=address:"));
            Serial.print(address);
            Serial.print(F("=val:"));
            Serial.print(val);
            Serial.print("  ");
        }
        Serial.println();
    }
    Serial.println();
}


void setup()
{
    Serial.begin(115200);
    Serial.print(F("\n\nBEGIN:\n\n"));

    static const byte DATA1[3][2] PROGMEM =
    {
        (byte)-1, 6, // Note: -1 underflows to 255
        (byte)-3, 7, // Note: -3 underflows to 253
        (byte)-5, 8, // Note: -5 underflows to 251
    };

    static const byte DATA2[3][2] PROGMEM =
    {
        10,  13,
        11,  14,
        12,  15,
    };

    // static const byte DATA3[6][2] PROGMEM =
    // {
    //     0,  100,
    //     1,  101,
    //     2,  102,
    //     3,  103,
    //     4,  104,
    //     5,  (byte)-8, // Note: -8 underflows to 248
    // };

    // alternative, better and clearer way to define the array just above:
    static const byte DATA3[][2] PROGMEM =
    {
        {0,         100},
        {1,         101},
        {2,         102},
        {3,         103},
        {4,         104},
        {5,  (byte)-8}, // Note: -8 underflows to 248
    };

    // First, prove we can read some values from the above 2D PROGMEM arrays.
    print_2d_progmem_array(DATA1, NUM_ROWS(DATA1));
    print_2d_progmem_array(DATA2, NUM_ROWS(DATA2));
    print_2d_progmem_array(DATA3, NUM_ROWS(DATA3));


    // -------------------------------------------------------------------------
    // Now let's store the above 2D PROGMEM arrays all into a 1D PROGEMEM array
    // -------------------------------------------------------------------------


    // Technique 1 [best]: use a typedef for the `array2d_t` type to make the
    // definition of the `ALL_DATA` array easier to understand and read
    {
        Serial.println(F("\n===== Technique 1: ====="));

        typedef byte (*array2d_t)[2];
        // Note: the left-most `const` here makes the `byte`s being pointed-to
        // `const`, whereas the right-most `const` makes the actual ptr itself
        // `const`, NOT what it points to.
        typedef const byte (* const array2d_const_t)[2];

        static const array2d_const_t ALL_DATA[] PROGMEM =
        {
            DATA1,
            DATA2,
            DATA3,
        };

        // now print out all 2D PROGMEM arrays within the `ALL_DATA` 1D PROGMEM
        // array

        // This does NOT work quite right!
        // Broken: it will only print the 1st row.
        //
        // `NUM_ROWS(array2d)` always returns 1, since the `typedef`ed
        // `array2d_t` type is actually a ptr (it represents a 2D array by
        // being a ptr to a 1D array), so this only prints the first row of
        // each data array since the ptr length is always 1. We will have to do
        // this explicitly instead.
        Serial.println(F("\n--- 1.A: ---\n"));
        for (size_t i = 0; i < ARRAY_LEN(ALL_DATA); i++)
        {
            // Attempt to print the entire data array
            array2d_t array2d = pgm_read_ptr(&ALL_DATA[i]);

            // broken! `NUM_ROWS(array2d)` always returns 1 below!
            // prove that `NUM_ROWS(array2d)` always returns 1
            size_t num_rows = NUM_ROWS(array2d);
            Serial.print(F("NUM_ROWS(array2d)="));
            Serial.println(num_rows);

            print_2d_progmem_array(array2d, NUM_ROWS(array2d));
        }


        // This works! Be **explicit** in manually specifying the number of rows
        // for each 2D array! See the comment just above!
        Serial.println(F("\n--- 1.B: ---\n"));
        array2d_t array2d;
        size_t i_data;
        // DATA1
        i_data = 0;
        array2d = pgm_read_ptr(&ALL_DATA[i_data]);
        print_2d_progmem_array(array2d, NUM_ROWS(DATA1));
        // DATA2
        i_data = 1;
        array2d = pgm_read_ptr(&ALL_DATA[i_data]);
        print_2d_progmem_array(array2d, NUM_ROWS(DATA2));
        // DATA3
        i_data = 2;
        array2d = pgm_read_ptr(&ALL_DATA[i_data]);
        print_2d_progmem_array(array2d, NUM_ROWS(DATA3));

        // print the addresses just above, for understanding, too
        uint16_t address;
        // DATA1
        address = (uint16_t)(&ALL_DATA[0]);
        Serial.print(F("address (`&ALL_DATA[0]`)="));
        Serial.println(address);
        // DATA2
        address = (uint16_t)(&ALL_DATA[1]);
        Serial.print(F("address (`&ALL_DATA[1]`)="));
        Serial.println(address);
        // DATA3
        address = (uint16_t)(&ALL_DATA[2]);
        Serial.print(F("address (`&ALL_DATA[2]`)="));
        Serial.println(address);

        // print the values in the 1D outer-most array too, for understanding,
        // as these **values** should be the **addresses** of the `DATA1`,
        // `DATA2`, and `DATA3` sub-arrays!
        //
        // NB: the **value** of the outer array = the **address** of the inner
        // array, so it is still acceptable to call this value an "address"!
        // DATA1
        address = (uint16_t)(ALL_DATA[0]);
        Serial.print(F("value of outer array / address of inner array "
                       "(`ALL_DATA[0]`)="));
        Serial.println(address);
        // DATA2
        address = (uint16_t)(ALL_DATA[1]);
        Serial.print(F("value of outer array / address of inner array "
                       "(`ALL_DATA[1]`)="));
        Serial.println(address);
        // DATA3
        address = (uint16_t)(ALL_DATA[2]);
        Serial.print(F("value of outer array / address of inner array "
                       "(`ALL_DATA[2]`)="));
        Serial.println(address);


        Serial.println(F("\n--- general indexing: ---\n"));

        // Works! [GREAT]
        byte my_data = pgm_read_byte(&((ALL_DATA[2])[5][1])); // 248  <============ WORKS GREAT! Great general indexing example ===========
        Serial.print(F("my_data="));
        Serial.print(my_data);
        // print the address too, for understanding
        address = (uint16_t)(&((ALL_DATA[2])[5][1]));
        Serial.print(F("; address="));
        Serial.print(address);
        Serial.println(F(" <=== this is the sum of the value or address just "
            "above, + 11, which is the offset to this member of that array!\n"
            "That's how this works! That RAM address is then mapped by the "
            "`pgm_read_*()` functions into a PROGMEM (Flash) address, which\n"
            "gets read by those functions!"));

        // Also works, but is perhaps unnecessarily verbose when just trying to
        // obtain one number. So, I'd prefer to use the style shown just above
        // instead.
        array2d = pgm_read_ptr(&ALL_DATA[2]);    // DATA3
        my_data = pgm_read_byte(&array2d[5][1]); // 248
        Serial.print(F("my_data="));
        Serial.print(my_data);
        // print the addresses too, for understanding
        address = (uint16_t)(&ALL_DATA[2]);
        Serial.print(F("; address (`&ALL_DATA[2]`)="));
        Serial.print(address);
        address = (uint16_t)(&array2d[5][1]);
        Serial.print(F("; address (`&array2d[5][1]`)="));
        Serial.println(address);
    }


    // Technique 2: withOUT a typedef (very hard to read)
    // See https://cdecl.org/ for help.
    {
        Serial.println(F("\n===== Technique 2: ====="));

        // defined withOUT the typedef; notice the extra `const` here after
        // the asterisk (`*`). That's ok. It makes the ptr itself `const`,
        // whereas the `const` to the far left makes the stuff being pointed-to
        // `const`.
        static const byte (* const ALL_DATA2[])[2] PROGMEM =
        {
            DATA1,
            DATA2,
            DATA3,
        };

        // now print out all 2D PROGMEM arrays within the `ALL_DATA2` 1D PROGMEM
        // array

        // skip attempt A since we already tested it above, and just go straight
        // to attempt B, which we know works

        Serial.println(F("\n--- 2.B: ---\n"));

        // defined withOUT the typedef;
        // Notice I _removed_ the `const` after the asterisk so that this ptr
        // itself is NOT `const`, so that it can be re-assigned 3 separate
        // times--once per DATA array.
        byte (* array2d)[2];
        size_t i_data;
        // DATA1
        i_data = 0;
        array2d = pgm_read_ptr(&ALL_DATA2[i_data]);
        print_2d_progmem_array(array2d, NUM_ROWS(DATA1));
        // DATA2
        i_data = 1;
        array2d = pgm_read_ptr(&ALL_DATA2[i_data]);
        print_2d_progmem_array(array2d, NUM_ROWS(DATA2));
        // DATA3
        i_data = 2;
        array2d = pgm_read_ptr(&ALL_DATA2[i_data]);
        print_2d_progmem_array(array2d, NUM_ROWS(DATA3));
    }
}

void loop()
{
    // nothing to do
}

Sample output (tested on an Arduino Nano with ATmega328 mcu):

BEGIN:

print_2d_progmem_array:
array_2d[0][0]=address:952=val:255  array_2d[0][1]=address:953=val:6
array_2d[1][0]=address:954=val:253  array_2d[1][1]=address:955=val:7
array_2d[2][0]=address:956=val:251  array_2d[2][1]=address:957=val:8

print_2d_progmem_array:
array_2d[0][0]=address:946=val:10  array_2d[0][1]=address:947=val:13
array_2d[1][0]=address:948=val:11  array_2d[1][1]=address:949=val:14
array_2d[2][0]=address:950=val:12  array_2d[2][1]=address:951=val:15

print_2d_progmem_array:
array_2d[0][0]=address:934=val:0  array_2d[0][1]=address:935=val:100
array_2d[1][0]=address:936=val:1  array_2d[1][1]=address:937=val:101
array_2d[2][0]=address:938=val:2  array_2d[2][1]=address:939=val:102
array_2d[3][0]=address:940=val:3  array_2d[3][1]=address:941=val:103
array_2d[4][0]=address:942=val:4  array_2d[4][1]=address:943=val:104
array_2d[5][0]=address:944=val:5  array_2d[5][1]=address:945=val:248


===== Technique 1: =====

--- 1.A: ---

NUM_ROWS(array2d)=1
print_2d_progmem_array:
array_2d[0][0]=address:952=val:255  array_2d[0][1]=address:953=val:6

NUM_ROWS(array2d)=1
print_2d_progmem_array:
array_2d[0][0]=address:946=val:10  array_2d[0][1]=address:947=val:13

NUM_ROWS(array2d)=1
print_2d_progmem_array:
array_2d[0][0]=address:934=val:0  array_2d[0][1]=address:935=val:100


--- 1.B: ---

print_2d_progmem_array:
array_2d[0][0]=address:952=val:255  array_2d[0][1]=address:953=val:6
array_2d[1][0]=address:954=val:253  array_2d[1][1]=address:955=val:7
array_2d[2][0]=address:956=val:251  array_2d[2][1]=address:957=val:8

print_2d_progmem_array:
array_2d[0][0]=address:946=val:10  array_2d[0][1]=address:947=val:13
array_2d[1][0]=address:948=val:11  array_2d[1][1]=address:949=val:14
array_2d[2][0]=address:950=val:12  array_2d[2][1]=address:951=val:15

print_2d_progmem_array:
array_2d[0][0]=address:934=val:0  array_2d[0][1]=address:935=val:100
array_2d[1][0]=address:936=val:1  array_2d[1][1]=address:937=val:101
array_2d[2][0]=address:938=val:2  array_2d[2][1]=address:939=val:102
array_2d[3][0]=address:940=val:3  array_2d[3][1]=address:941=val:103
array_2d[4][0]=address:942=val:4  array_2d[4][1]=address:943=val:104
array_2d[5][0]=address:944=val:5  array_2d[5][1]=address:945=val:248

address (`&ALL_DATA[0]`)=887
address (`&ALL_DATA[1]`)=889
address (`&ALL_DATA[2]`)=891
value of outer array / address of inner array (`ALL_DATA[0]`)=952
value of outer array / address of inner array (`ALL_DATA[1]`)=946
value of outer array / address of inner array (`ALL_DATA[2]`)=934

--- general indexing: ---

my_data=248; address=945 <=== this is the sum of the value or address just above, + 11, which is the offset to this member of that array!
That's how this works! That RAM address is then mapped by the `pgm_read_*()` functions into a PROGMEM (Flash) address, which
gets read by those functions!
my_data=248; address (`&ALL_DATA[2]`)=891; address (`&array2d[5][1]`)=945

===== Technique 2: =====

--- 2.B: ---

print_2d_progmem_array:
array_2d[0][0]=address:952=val:255  array_2d[0][1]=address:953=val:6
array_2d[1][0]=address:954=val:253  array_2d[1][1]=address:955=val:7
array_2d[2][0]=address:956=val:251  array_2d[2][1]=address:957=val:8

print_2d_progmem_array:
array_2d[0][0]=address:946=val:10  array_2d[0][1]=address:947=val:13
array_2d[1][0]=address:948=val:11  array_2d[1][1]=address:949=val:14
array_2d[2][0]=address:950=val:12  array_2d[2][1]=address:951=val:15

print_2d_progmem_array:
array_2d[0][0]=address:934=val:0  array_2d[0][1]=address:935=val:100
array_2d[1][0]=address:936=val:1  array_2d[1][1]=address:937=val:101
array_2d[2][0]=address:938=val:2  array_2d[2][1]=address:939=val:102
array_2d[3][0]=address:940=val:3  array_2d[3][1]=address:941=val:103
array_2d[4][0]=address:942=val:4  array_2d[4][1]=address:943=val:104
array_2d[5][0]=address:944=val:5  array_2d[5][1]=address:945=val:248

Notes About How the PROGMEM pgm_read_*() functions work and what they are doing

The way that the pgm_read_*() functions work, such as pgm_read_byte(), pgm_read_word(), etc., is that they map regular RAM addresses into PROGMEM (Flash) addresses, then read the contents of those Flash addresses! In this way, the regular C and C++ language and compiler constructs work like normal, producing RAM addresses and offsets from pointers and array starts and things, withOUT any customization or modifications to the compiler from AVR-libc. BUT, those RAM addresses do NOT actually contain any of the useful PROGMEM data we are after! The RAM addresses obtained for arrays stored in PROGMEM contain garbage or unknown data. Rather, AVR-libc must then map those RAM addresses to the real Flash addresses and read the Flash data. If we read the contents of those RAM address, they'd contain garbage or random data, NOT the data we are after. So, the pgm_read_*() functions do the mapping from RAM to Flash memory and then give us the Flash data we need.

Example and explantion of this:

// Works! [GREAT]
byte my_data = pgm_read_byte(&((ALL_DATA[2])[5][1])); // 248

When you do something more complicated, like &((ALL_DATA[2])[5][1]), it all still works! The value at ALL_DATA[2] is an address to a 2D sub-array, such as DATA1, DATA2, or DATA3. Indexing into that sub-array then takes place via [5][1]. The value at that location in RAM is pure garbage! We don't know what might actually be there! BUT, we don't care! We take the address at that indexed location via & in order to obtain a RAM address. We then pass that RAM address to pgm_read_byte() to do the mapping from the RAM address to a Flash memory address, and it then reads the contents at that mapped Flash address. We never find out what that mapped Flash address is, but that's ok. So long as we can obtain the RAM address we always have a way to read the Flash value again via those pgm_read_*() functions.

Key takeaway of PROGMEM

The implication of all of this is that when dealing with any data type in PROGMEM (Flash), including 1D arrays, multi-dimensional arrays, and even including nested data types, such as arrays of arrays, arrays of structs, structs with arrays or structs, arrays of structs, C++ classes, etc., you can imagine that the relative memory layout of the bytes of the object in Flash is identical to the relative memory layout in RAM. This may not actually be the case, but it doesn't matter because that's how the memory address input to the pgm_read_*() functions is expected to be!--it's expected to be addresses of those objects as though they were stored in RAM by the compiler.

References

  1. My comments under MikeCAT's answer:

    Looking at this even 6 years later and after having written the 1D and 2D array answers now posted in the bottom of my answer, this is still super-confusing stuff. Adding a typedef would make it so much clearer and easier to read and write. I think it would look like this, if you want to verify it and add it to your answer: typedef byte (*array2d)[2];. Then we could do this to declare the array of 2D arrays: static const array2d MY_DATA[] PROGMEM = {DATA1, DATA2, DATA3};. Right?

    This should work I think because the array2d type is a 2D array type since a 2D array like this: byte array2d[][2], naturally decays (adjusts) to a byte (*)[2] pointer, which is a pointer to a 1D array of length 2. In thinking about this, I referenced my notes in the section "A quick reminder on pointers" in my answer here, as well as by looking at my comments in the bottom of my Example 2 there.

  2. https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
  3. *****+ [EXCELLENT AVR tutorial!] https://www.nongnu.org/avr-libc/user-manual/pgmspace.html
  4. https://www.arduino.cc/reference/en/language/variables/utilities/progmem/
  5. [my answer] 1D arrays: Passing 1D arrays as function parameters in C (and C++)
  6. [my answer] 2d arrays and automatic array "decay" or "adjusting" to ptrs: How to use multidimensional (ex: 2D) arrays, and pointers to them, as function parameters in C and C++
    1. See especially my "Example 2" here. I referenced it heavily to write my answer above, and I borrowed the outline of my print_2d_progmem_array() function above from it.

Upvotes: 0

MikeCAT
MikeCAT

Reputation: 75062

The type of elements of MY_DATA is const byte* const, but DATA1, etc. are converted to const byte (*)[2] as the error message suggests, so type mismatch happens. Note that arrays in expression are automatically converted to pointers pointing their first elements except for some exceptions such as operator of unary & or sizeof.

Use correct type: arrays of pointers to const byte (*)[2] like this:

const static byte(* const MY_DATA[])[2] PROGMEM = {DATA1, DATA2, DATA3};

This is "static variable MY_DATA as array of const pointer to array 2 of const byte".

According to cdecl, const char(* const MY_DATA[99])[2]; can be decoded as "MY_DATA as array 99 of const pointer to array 2 of const char". With some modifications, you can get the implemention of the correct type.

You can decode type declaration without const in reversed order of normal expression evaluation. (Sorry, currently I'm not good at decoding qualifiers) Let me decode byte(*MY_DATA[])[2] in this method.

Normal expression evaluation:

  1. MY_DATA
  2. MY_DATA[] : some element of MY_DATA
  3. *MY_DATA[] : dereference the element
  4. (*MY_DATA[])[2] : some element of what is dereferenced

Decoding type declaration (something not yet decoded is represented by @):

  1. byte @ : something having type byte
  2. byte @[2] : 2-element array of byte
  3. byte (*@)[2] : pointer to 2-element array of byte
  4. byte (*@[])[2] : array of pointers to 2-element array of byte
  5. byte (*MY_DATA[])[2] : MY_DATA, which is an array of pointers to 2-element array of byte

Upvotes: 1

Related Questions