1

I am using a function in Arduino C associated with writing data to a binary file on an SD-card. The prototype function is:

write(const uint8_t *buf, size_t len);

write accepts a pointer to a variable of type const uint8_t, so if I am dealing with buffers containing elements of type uint16_t I have to type cast them to use the write function. I tested this out with an Arduino:

uint8_t buffer8[10];
uint16_t buffer16[10];

void setup() {
  Serial.begin(9600);
  for(int i = 0; i<10; i++){
    buffer8[i] = i;
    buffer16[i] = i;
  }
  test(buffer8,sizeof(buffer8));
  Serial.println();
  test((const uint8_t *)buffer16, sizeof(buffer16)); //Without &
  Serial.println();
  test((const uint8_t *)&buffer16, sizeof(buffer16)); //With &
  Serial.println();
}

void loop() {}

void test(const uint8_t *buf, size_t len){
  for(int i = 0; i<len; i++){
    Serial.print(buf[i]);
  }
}

I got this output which is correct:

0123456789
00102030405060708090
00102030405060708090

When I call my test-function on buffer16 I do it two times. The first time I don't include &: test((const uint8_t *)buffer16, sizeof(buffer16));. The second time I do include &: test((const uint8_t *)&buffer16, sizeof(buffer16));.

But both function calls yield the same output. So my question is: Do I have to include the & when I type cast like this? What does this (const uint8_t *)&buffer16 actually mean in words?

dbush
  • 205,898
  • 23
  • 218
  • 273
Carl
  • 149
  • 1
  • 7
  • 1
    Given that the in-scope declaration for `buffer16` declares it as an array, not a pointer, the same result is obtained whether you use `&` or not. Without `&`, you have the expression `buffer16` *automatically* converted to a pointer (of type `uint16_t *`) to its first element, then that converted to type `const uint8_t *` via the cast. With the `&`, the intermediate pointer is a pointer to the whole array, of type `uint16_t (*)[10]`, but the conversion from there to type `const uint8_t *` yields the same result. – John Bollinger Apr 22 '23 at 14:51
  • Hey @JohnBollinger, if you are given the inclination feel free to write an answer in more detail to give me some more insight. – Carl Apr 22 '23 at 14:56
  • 1
    I have not written that as an answer because it is a many-times-over dupe, whose targets I am too lazy at the moment to find. – John Bollinger Apr 22 '23 at 14:57
  • @JohnBollinger Your first comment answers my question. Please post it as an answer such that I can accept it and make this Q&A post "complete". – Carl Apr 22 '23 at 14:58

1 Answers1

3

Here is a translation into plain English:

  • (const uint8_t *)buffer16 means convert buffer16 to a pointer to uint8_t bytes that should not be used to modify the object. buffer16 is an array, so when used in an expression, it decays into a pointer to its first element. You are passing the address of the first element of the array as a pointer to individual bytes.

  • (const uint8_t *)&buffer16 means convert &buffer16 to a pointer to uint8_t bytes that should not be used to modify the object. &buffer16 is the address of an array, a pointer with type uint16_t (*)[10] that points to the array, hence the same address as that of buffer16[0], so you are also passing the address of the first element of the array as a pointer to individual bytes, but with a useless and confusing intermediary conversion.

Note that it would be less confusing to define test as taking a const void *, like write() and only cast the argument inside the function:

void test(const void *buf, size_t size) {
    const uint8_t *p = buf;
    for (size_t i = 0; i < size; i++) {
        Serial.print(p[i]);
    }
}

uint8_t buffer8[10];
uint16_t buffer16[10];

void setup() {
    Serial.begin(9600);
    for (int i = 0; i < 10; i++) {
        buffer8[i] = i;
        buffer16[i] = i;
    }
    test(buffer8, sizeof(buffer8));
    Serial.println();
    test(buffer16, sizeof(buffer16));
    Serial.println();
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189