0

I'd like to print both MSB and LSB of a u16 variable, here is my code:

__u16 reg = 0x10A0; /* Device register to access */
__s32 res;
char buf[10];

buf[0] = reg;

printf("0x%X\n", buf[0]);

This code just print the LSB part in hexa format, but can you tell me how do I print the MSB part?

Martin Denion
  • 352
  • 1
  • 3
  • 22
  • 2
    Are you familiar with the `>>` operator and bitmasking? Have you tried casting to `unsigned char*` and treating it as bytes? – tadman Feb 24 '21 at 08:42
  • If you want to print the value of `reg` without splitting into bytes, see https://stackoverflow.com/a/12120500/10622916 – Bodo Feb 24 '21 at 08:48
  • Also, do you wish to print the address or the contents? Printing the address seems mildly useful. – Lundin Feb 24 '21 at 10:00

1 Answers1

1

This code just print the LSB part

Well of course, buf[0] = reg; only copies 8 bits.

And what result you get isn't well-defined, because char cannot reliably store raw binary. It might be signed on your systems and then you can get all kinds of subtle bugs because of it.

Step 1 is to fix your types. Get rid of char as well as the home-made, garage standard integer types, in favour for standard C types:

#include <stdint.h>

uint16_t reg = 0x10A0u; 
uint8_t  buf [10];

Then decide the byte order of your array. Assuming that you want index 0 to be the most significant byte, then the quick & dirty version looks like this:

buf[0] = reg >> 8;
buf[1] = reg;

This will work in this specific case. However, good compilers will moan about implicit promotions and similar. Strictly speaking, you should make a habit of always casting to uint32_t before doing any kind of shifting (assuming >=32 bit system). We can make the code more rugged and silence warnings like this:

buf[0] = ((uint32_t)reg >> 8) & 0xFFu;
buf[1] = ((uint32_t)reg >> 0) & 0xFFu;

This method safely scales to larger types as well.

Also, be vary of various quack solutions on SO and elsewhere telling you to use union or character pointers for this. Such solutions make the code needlessly endianess dependent for nothing gained. The shift version is 100% portable even across different endianess CPUs.

Then finally, printing with printf. You can get the correct format specifier for any uintn_t automatically from the header inttypes.h. It provides PRIx8 for lower case hex and PRIX8 for upper case hex.

Complete program:

#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>

int main (void)
{
  uint16_t reg = 0x10A0u; 
  uint8_t  buf[10];

  buf[0] = ((uint32_t)reg >> 8) & 0xFFu;
  buf[1] = ((uint32_t)reg >> 0) & 0xFFu;

  printf("0x%"PRIX8 "%"PRIX8 "\n", buf[0], buf[1]);
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Good answer, except I differ in part: "Strictly speaking, you should make a habit of always casting to uint32_t before doing any kind of shifting (assuming >=32 bit system)" --> To not not get moaning and not rely on that assumption for the 16-bit `reg`, code could use `buf[0] = ((unsigned)reg >> 8) & 0xFFu;` – chux - Reinstate Monica Feb 24 '21 at 10:50
  • @chux-ReinstateMonica Yeah... I'm too used at always writing MISRA-compliant code I guess. `unsigned` isn't allowed by MISRA-C since you should always use the stdint.h types. But I agree that in your example it actually makes the code more portable. – Lundin Feb 24 '21 at 11:07
  • "MISRA-C" --> how to write code and remain as C89-like as possible compatible. I feel for you – chux - Reinstate Monica Feb 24 '21 at 11:16
  • @chux-ReinstateMonica Eh, well not really since they love `stdint.h` which is C99. Even the old MISRA-C:2004 which only covered C90 pushed for using `stdint.h` or equivalent typedefs. – Lundin Feb 24 '21 at 11:22
  • Agree about love for `stdint.h`, but it is not for desire to embrace C99 - more of a means to fix the C language to a non-evolving standard. Either at some point MISRA needs to evolve else it is dead weight to the progress of C. – chux - Reinstate Monica Feb 24 '21 at 11:32
  • We certainly agree on some areas: _bounds-checking interface_, easier to bloat than remove warts. I hope for Unicode improvements. Thanks for your insights. – chux - Reinstate Monica Feb 24 '21 at 11:45
  • All those `& 0xFFu` are redundant when the lvalue type is `uint8_t`. – 0andriy Mar 09 '21 at 17:16