4

I want to print 64 bits hex data in following format:

0x1111_2222_3333_4444

I can think of following:

#define FIRST_MASK  0xFFFF000000000000
#define SECOND_MASK 0x0000FFFF00000000
#define THIRD_MASK  0x00000000FFFF0000
#define FOURTH_MASK 0x000000000000FFFF

val = 0x1111222233334444 
printf("0x%04x_%04x_%04x_%04x\n", \
       (unsigned int)((val & FIRST_MASK)  >> 48),
       (unsigned int)((val & SECOND_MASK) >> 32),
       (unsigned int)((val & THIRD_MASK)  >> 16),
       (unsigned int)(val & FOURTH_MASK)
);

Is there a simple (or maybe more clean) way to achieve this?

Patrick
  • 2,464
  • 3
  • 30
  • 47
  • 1
    ... a union with uint16_t[4]? – technosaurus Dec 05 '17 at 07:09
  • 1
    @technosaurus ...is a bad idea, because then the code turns endianess-dependent for nothing gained. – Lundin Dec 05 '17 at 07:12
  • @technosaurus I personally consider this as sad but I somewhere found (in SO) that it is UB when reading components from a `union` which have not written before. (And, yes, this was very common in old code. This was discussed as well.) If I only could remember how I found this... – Scheff's Cat Dec 05 '17 at 07:27
  • 4
    @Scheff You probably found it in some _C++_ discussion, as it is UB in C++ but not in C. At any rate, it doesn't matter, unions are bad in this case for a different reason: because of endianess. – Lundin Dec 05 '17 at 07:47
  • 3
    Not sure if I should laugh or cry each time this topic pops up. Bit shifts are superior for splitting up a larger type, it is the best solution and that's not subjective or open for debate. Unions, bit-field structs or pointer arithmetic are inferior because of endianess and poorly-specified behavior. Yet there are _always_ people suggesting those solutions. Sigh. – Lundin Dec 05 '17 at 07:52
  • @Lundin It's actually off-topic as pointed out by you (I agree) but it starts to pain me. I googled a little bit regarding "C union casting" and found some related Q/As for C as well as C++. However - none of them mentioned the resp. specification paragraph. Hence I wouldn't consider them as authoritative. I also saw a lot about aliasing but I think that isn't the same (as it might feel at first glance). And yes, that original topic (where I saw the answer _with_ reference to specification) - I cannot remember whether it was related to C or C++. Too bad... – Scheff's Cat Dec 05 '17 at 08:13
  • @Scheff This is stated in behavior of the `.`/`->` operator. Specifically, C11 6.5.2.3/3 allows "type punning" through unions. – Lundin Dec 05 '17 at 08:30
  • @Lundin "type punning" was the key word I just found out by myself. (I feel I'm coming closer.) Found this: [SO: Is type-punning through a union unspecified in C99, and has it become specified in C11?](https://stackoverflow.com/q/11639947/7478597). – Scheff's Cat Dec 05 '17 at 08:32
  • You could improve `printf` yourself (in GCC, at least). Here you have some clue: https://codereview.stackexchange.com/a/220581/200418 – alx - recommends codidact Jun 29 '19 at 23:18

1 Answers1

11

Bit shifts are always the best method, because they are both fast and platform-independent. However, your code is needlessly complex: you can mask after shifting instead of before.

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

int main (void)
{
  uint64_t val = 0x1111222233334444;
  printf("0x%04x_%04x_%04x_%04x\n", 
         (unsigned int)(val>>48 & 0xFFFF),
         (unsigned int)(val>>32 & 0xFFFF),
         (unsigned int)(val>>16 & 0xFFFF),
         (unsigned int)(val>> 0 & 0xFFFF) );
}
Lundin
  • 195,001
  • 40
  • 254
  • 396