Search code examples
carduinoesp32

Getting number of rows in 2D array: my function always returns 1 - Arduino


I am writing a function for use in a LED project on a esp32. I am using 2D arrays to store RGB hex codes and calling my gradientFunction to send to a BLE controller, but the number colors of may vary:

...
     if (counter == 0) {
        uint8_t rgb[4][3] = { {0xff, 0x00, 0x00}, {0xff, 0x11, 0x11}, {0xea, 0x1f, 0x10}, {0xff, 0x00, 0x00} };            
        gradientFunction(rgb);
        Serial.println("Red gradient");
    }
}
    void gradientFunction(uint8_t rgb[][3]) {
      int length = sizeof(rgb) / sizeof(rgb[0]);
      Serial.println(length);
      for (int i = 0; i < length; i++) {                                                
        uint8_t bytes[9] = {0x7b, 0x01 + i, 0x0e, 0xfd, rgb[i][0], rgb[i][1], rgb[i][2], length, 0xbf}; 
        pRemoteCharacteristic->writeValue(bytes, 9);
        delay(100);    
      }
    }

However, calculating the number of rows (length) returns 1. Printing sizeof(rgb) returns 4 and sizeof(rgb[0]) returns 3. This results in only 1 color code being transmitted to the BLE controller.

I can't seem to figure where I am going wrong with calculating the number of rows, or am I calling the function incorrectly?


Solution

  • C programming FAQ: an array when passed to a function "decays" into a pointer to the first element. This is the very reason why you are able to type uint8_t rgb[][3] with an empty dimension to begin with: the first item of an array of dimensions uint8_t [n][3] is always uint8_t [3] regardless of n.

    • Therefore sizeof(rgb) gives the size of a pointer to the first element. The first element being uint8_t [3] and a pointer to one being uint8_t (*)[3]. Size 4 on a 32 bit ESP32.

    • Whereas sizeof(rgb[0]) actually gives the size of the first element, similar to sizeof(uint8_t[3]), meaning 3.

    • 4 / 3 with integer arithmetic gives a truncated 1.

    To solve this, rewrite the function like this instead:

    void gradientFunction(uint8_t x, uint8_t y, uint8_t rgb[x][y]) {
      uint16_t size = x * y;
    

    This of course assuming that x and y are never larger than 255. Since this is a microcontroller, I'm intentionally using as small as possible integer types. On a PC you wouldn't care and use size_t instead.