4

I have variable uint16_t value, I want to copy it to uint8_t buffer[3]. Is it possible to do (Little endian):

*buffer=*(uint8_t *)&value;

Instead of:

buffer[0] = highByte(value);
buffer[1] = lowByte(value);

Since this replacement cause stm32f7 I2C to not working correctly. Is there any correct casting?

mohammadsdtmnd
  • 330
  • 1
  • 11

3 Answers3

6

STM32 is little endian so you get the lowest significant byte first:

uint8_t* ptr = (uint8_t*)&value;
uint8_t low  = ptr[0];
uint8_t high = ptr[1];

Doing casts and de-referencing like this is fine for character types only. The above code is assuming that uint8_t is a character type, which is very likely the case (on gcc and other mainstream compilers).

For more info see What is CPU endianness?

EDIT:

If you simply wish to copy a 16 bit number into an array of bytes, the correct solution is this:

memcpy(buffer, &value, sizeof(uint16_t)).

We cannot do *(uint16_t*) buffer=value; because it invokes undefined behavior. buffer could be misaligned and it's also a strict aliasing violation. And this is why I wrote with emphasis above "this is fine for character types only".

Lundin
  • 195,001
  • 40
  • 254
  • 396
2

Is there any correct casting?

No

*buffer = <expression>

will always and only write to buffer[0] and never to buffer[1].

One thing you could do - but I strongly urge you NOT to is stuff like this:

uint16_t *ptr = (uint16_t*)buffer; // Ugh, danger
*ptr = value;

This will be problematic if you run it on machines with different endianess. And it's quite possible that there are other reasons to not do this too. As soon as you ask yourself if you can solve something via casting, you should really take a step back. Here are two answers I have written about that:

https://stackoverflow.com/a/62563330/6699433

https://stackoverflow.com/a/63773195/6699433

klutt
  • 30,332
  • 17
  • 55
  • 95
  • Maybe I can pass the hole address `buffer=(uint8_t *)&value;` but still other problem is persist: As @Lundin told. low byte will save first. – mohammadsdtmnd Dec 14 '21 at 07:18
  • @mohammadsdtmnd What is the problem with your current approach? That it takes one extra line? – klutt Dec 14 '21 at 07:23
  • `value` is continually refreshed from ADC by DMA, I want to pass it to I2C which uses 8bit buffer, I don't want make CPU interaction in I2C and it's related DMA Transmission. then I start I2c transmission in one line and passing value to it's `uint8_t buffer`. – mohammadsdtmnd Dec 14 '21 at 07:29
  • @mohammadsdtmnd In that case, keep in mind that ADC might align bits in their own special way. In addition to endianess the bits could also be "left aligned" or "right aligned". There's usually a setting in the ADC peripheral where you can pick which format you prefer. – Lundin Dec 14 '21 at 07:31
  • @mohammadsdtmnd Are you worried that `value` will change in the middle of the operation? – klutt Dec 14 '21 at 07:33
  • I'm not yet in that stage of codding I just try to correctly pass value to buffer `function(..., uint8_t *buffer, ...)` – mohammadsdtmnd Dec 14 '21 at 07:37
  • @mohammadsdtmnd It seems like you have a [XY-problem](https://meta.stackexchange.com/q/66377/374458). I suggest you ask a new question where you clearly state your *actual* problem. – klutt Dec 14 '21 at 07:41
  • Nice link, ty. I feel I have found my answer from @Lundin. My problem is now that think about it's high side and low side problem. ty sincerely. – mohammadsdtmnd Dec 14 '21 at 07:44
0

Assuming to resolve the problem related to high side and low side as mentioned by @Lundin, and The fact that dereferencing uint8_t grant access to only it's first array element, I reached this solution by only a single cast:

*(uint16_t*) buffer=value;

Which is reduced version of:

uint16_t* p;
p= buffer;
*p=value;
mohammadsdtmnd
  • 330
  • 1
  • 11
  • 1
    No this is wrong, it's a strict aliasing violation and possibly gives misaligned access. If you wish to do a 16 bit copy you should be using `memcpy(buffer, &value, sizeof(uint16_t))`. – Lundin Dec 14 '21 at 11:31
  • @Lundin I know some alignment access but, what's wrong if code do what I want to do? though I've read misaligned access is slower and not allowed in every mem reg. Can you please explain how is mis. acc. happens by this code?I've read 8 16 32 bit alignment access is aligned and am I violated this? – mohammadsdtmnd Dec 15 '21 at 12:36
  • It depends on target system. Some systems don't have alignment, others produce slower code for misaligned access and others produce hardware exceptions/trap instructions causing a program crash. But misaligned access isn't the only problem - as mentioned in my answer this is also a so-called "strict aliasing violation", meaning that the code has undefined behavior and therefore the result can become unexpected and non-deterministic. – Lundin Dec 15 '21 at 12:39
  • @Lundin I think it's safe. Because of allocated memory by array definition of `buffer` the 8 bit to 16 bit cast will work but maybe casting to 32bit may cause problem. I mean you must be aware to not pass the storage limit of `buffer`. – mohammadsdtmnd Dec 19 '21 at 11:26
  • https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule – Lundin Dec 19 '21 at 18:41