0

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?

siz
  • 117
  • 3
  • 9
  • 1
    In C, a function parameter declared as if it had array type is interpreted to have pointer type instead. This dovetails with the fact that if you present an argument that has array type to a function call then it is automatically converted to a pointer. Thus, *as a function parameter*, `uint8_t rgb[][3]` is equivalent to `uint8_t (*rgb)[3]`. Then `sizeof(rgb)` is the size of a pointer, apparently 4 bytes for you. `rgb[0]` is an array of 3 `uint8_t`, whose size is 3. the result of the integer division 4 / 3 is 1. – John Bollinger Aug 10 '23 at 13:57
  • 1
    Your `gradientFunction` will need an additional parameter by which the first dimension of `rgb` can be conveyed to it. – John Bollinger Aug 10 '23 at 13:58
  • The post was literally closed as a dupe the second before I tried to post an answer :/ I reopened, though if you still think the dupe is more canonical than my tailored answer then by all means go ahead and close as dupe. – Lundin Aug 10 '23 at 14:10

1 Answers1

2

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396