how do I print an unsigned char as hex in c++ using ostream?

151,672

Solution 1

I would suggest using the following technique:

struct HexCharStruct
{
  unsigned char c;
  HexCharStruct(unsigned char _c) : c(_c) { }
};

inline std::ostream& operator<<(std::ostream& o, const HexCharStruct& hs)
{
  return (o << std::hex << (int)hs.c);
}

inline HexCharStruct hex(unsigned char _c)
{
  return HexCharStruct(_c);
}

int main()
{
  char a = 131;
  std::cout << hex(a) << std::endl;
}

It's short to write, has the same efficiency as the original solution and it lets you choose to use the "original" character output. And it's type-safe (not using "evil" macros :-))

Solution 2

Use:

cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;

And if you want padding with leading zeros then:

#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ; 

As we are using C-style casts, why not go the whole hog with terminal C++ badness and use a macro!

#define HEX( x )
   setw(2) << setfill('0') << hex << (int)( x )

you can then say

cout << "a is " << HEX( a );

Edit: Having said that, MartinStettner's solution is much nicer!

Solution 3

You can read more about this at http://cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/ and http://cpp.indi.frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/. I am only posting this because it has become clear that the author of the above articles does not intend to.

The simplest and most correct technique to do print a char as hex is

unsigned char a = 0;
unsigned char b = 0xff;
auto flags = cout.flags(); //I only include resetting the ioflags because so
                           //many answers on this page call functions where
                           //flags are changed and leave no way to  
                           //return them to the state they were in before 
                           //the function call
cout << "a is " << hex << +a <<"; b is " << +b << endl;
cout.flags(flags);

The readers digest version of how this works is that the unary + operator forces a no op type conversion to an int with the correct signedness. So, an unsigned char converts to unsigned int, a signed char converts to int, and a char converts to either unsigned int or int depending on whether char is signed or unsigned on your platform (it comes as a shock to many that char is special and not specified as either signed or unsigned).

The only negative of this technique is that it may not be obvious what is happening to a someone that is unfamiliar with it. However, I think that it is better to use the technique that is correct and teach others about it rather than doing something that is incorrect but more immediately clear.

Solution 4

Well, this works for me:

std::cout << std::hex << (0xFF & a) << std::endl;

If you just cast (int) as suggested it might add 1s to the left of a if its most significant bit is 1. So making this binary AND operation guarantees the output will have the left bits filled by 0s and also converts it to unsigned int forcing cout to print it as hex.

I hope this helps.

Solution 5

In C++20 you'll be able to use std::format to do this:

std::cout << std::format("a is {:x}; b is {:x}\n", a, b);

Output:

a is 0; b is ff

In the meantime you can use the {fmt} library, std::format is based on. {fmt} also provides the print function that makes this even easier and more efficient (godbolt):

fmt::print("a is {:x}; b is {:x}\n", a, b);

Disclaimer: I'm the author of {fmt} and C++20 std::format.

Share:
151,672

Related videos on Youtube

Nathan Fellman
Author by

Nathan Fellman

SOreadytohelp

Updated on July 05, 2022

Comments

  • Nathan Fellman
    Nathan Fellman almost 2 years

    I want to work with unsigned 8-bit variables in C++. Either unsigned char or uint8_t do the trick as far as the arithmetic is concerned (which is expected, since AFAIK uint8_t is just an alias for unsigned char, or so the debugger presents it.

    The problem is that if I print out the variables using ostream in C++ it treats it as char. If I have:

    unsigned char a = 0;
    unsigned char b = 0xff;
    cout << "a is " << hex << a <<"; b is " << hex << b << endl;
    

    then the output is:

    a is ^@; b is 377
    

    instead of

    a is 0; b is ff
    

    I tried using uint8_t, but as I mentioned before, that's typedef'ed to unsigned char, so it does the same. How can I print my variables correctly?

    Edit: I do this in many places throughout my code. Is there any way I can do this without casting to int each time I want to print?

    • tdihp
      tdihp over 11 years
      I think MartinStettner's answer is rather confusing, I don't think it is worth to implement an extra struct and an extra stream operator. anon's solution is straight forward and works good enough for me.
  • Konrad Rudolph
    Konrad Rudolph about 15 years
    Can I convince you to eschew the evil C-style casts once and for all? stackoverflow.com/questions/527999/…
  • Admin
    Admin about 15 years
    Not in this case - it's about the only place I think they are justified.
  • Admin
    Admin about 15 years
    But then he has to use casts if he actually wants them output as chars, which seems a little unnatural!
  • Dai Doan
    Dai Doan about 15 years
    I would do it the same, except to avoid the cast I would use cout << hex << int(a); It means the same thing as a cast, without the cast. :)
  • Seth Johnson
    Seth Johnson almost 15 years
    As long as we're eschewing macros: in order to be more fully C++, shouldn't you write (int)hs.c as static_cast<int>(hs.c)? :P
  • Ted
    Ted about 14 years
    There is a slight bug in your code. If you feed a negative character in the code the hex value becomes 4 bytes instead of 2. Switching to unsigned char fixes the problem.
  • Nathan Fellman
    Nathan Fellman about 12 years
    While this does solve the problem, one of my requirements was not to have to cast to int whenever I want to do this, since this appears too many times in the code.
  • musiphil
    musiphil almost 12 years
    Another bug(?) is that the above operator<< changes the mode of the given stream to hexadecimal: cout << hex(a) << 100 will give you a surprise. You should store the state of the stream before modifying it, and restore it later.
  • sehe
    sehe over 10 years
    I like it. Succinct. No redundant info required. I'd love a bool to indicate whether upper or lowercase is desired. Or honour the std::uppercase and std::nouppercase manipulators (which manipulate the std::ios_base::flags::uppercase iosflag)
  • daminetreg
    daminetreg almost 10 years
    This is a question about C++ and std::ostream not about C and printf. :)
  • James Pack
    James Pack about 9 years
    Why is this not the top answer? It is short and effective.
  • Brian McFarland
    Brian McFarland about 8 years
    This technique sign-extends types smaller than unsigned int / int, which means for negative signed chars (or int16_t for example), you'll get 8 nibbles of output, the first 6 of which are F.
  • To마SE
    To마SE over 7 years
    @BrianMcFarland I noticed that issue even for unsigned char (when greater than 127), so I solved it with a bitmask: (+c & 0xFF).
  • Craig Ringer
    Craig Ringer over 6 years
    I'm truly amazed at how bad C++ is at converting char to hex.
  • Adrian
    Adrian almost 5 years
    @To마SE, the unary + is unnecessary, the binary & already converts to an int.
  • Adrian
    Adrian almost 5 years
    This is the simplest and cleanest method of the bunch.
  • sep
    sep over 3 years
    I really like this answer too! I used this solution.
  • Shn
    Shn about 2 years
    This works, but any suggestion on how I can prefix characters <= 9 to have a 0 prepended?
  • VinGarcia
    VinGarcia about 2 years
    @Shn_Android_Dev you can do it like this: stackoverflow.com/questions/1714515/…