tags:

views:

251

answers:

5

pixel_data is a vector of char.

When I do printf(" 0x%1x ", pixel_data[0] ) I'm expecting to see 0xf5.

But I get 0xfffffff5 as though I was printing out a 4 byte integer instead of 1 byte.

Why is this? I have given printf a char to print out - it's only 1 byte, so why is printf printing 4?

NB. the printf implementation is wrapped up inside a third party API but just wondering if this is a feature of standard printf?

+2  A: 

Then length modifier is the minimum length.

leppie
@leppie, so 1 is the minimum length? I thought length modifier was in bytes.
BeeBand
It is the length in characters.
leppie
@leppie, Sorry, I just don't understand what you mean. Why does what happened imply that the `length` modifier is the minimum length?
BeeBand
@BeeBand: It's what the docs say. You said you want it at least 1 character long.
leppie
@leppie, I see thanks - that is very useful to know - I've been wrong all these years! But I have decided to go with Charles' answer since he explained why I was seeing the leading f's.
BeeBand
A: 

Width-specifier in printf is actually min-width. You can do printf(" 0x%2x ", pixel_data[0] & 0xff) to print lowes byte (notice 2, to actually print two characters if pixel_data[0] is eg 0xffffff02).

phadej
+8  A: 

You're probably getting a benign form of undefined behaviour because the %x modifier expects an unsigned int parameter and a char will usually be promoted to an int when passed to a varargs function.

You should explicitly cast the char to an unsigned int to get predictable results:

printf(" 0x%1x ", (unsigned)pixel_data[0] );

Note that a field width of one is not very useful. It merely specifies the minimum number of digits to display and at least one digit will be needed in any case.

If char on your platform is signed then this conversion will convert negative char values to large unsigned int values (e.g. fffffff5). If you want to treat byte values as unsigned values and just zero extend when converting to unsigned int you should use unsigned char for pixel_data, or cast via unsigned char or use a masking operation after promotion.

e.g.

printf(" 0x%x ", (unsigned)(unsigned char)pixel_data[0] );

or

printf(" 0x%x ", (unsigned)pixel_data[0] & 0xffU );
Charles Bailey
@charles. Thank you for the explanation as to why the leading `f`'s! The third party API that I'm using has an `unsigned char` type - so I changed my definition for `pixel_data` to use this instead and get the results I need.
BeeBand
@BeeBand: Be careful, just changing to `unsigned char` may not be enough. On most platforms `unsigned char` will promote to `int`, not `unsigned int`.
Charles Bailey
@Charles - when I don't change to `unsigned char` and just do the cast, I still get those leading f's. I guess I need to both change to `unsigned int` and use a bitmask as well to ensure it works on all platforms.
BeeBand
@BeeBand: If you use a `char` that is signed it can hold a negative value, when you cast that to an `unsigned int` it will be converted to via modulo 2^N arithmetic to a large number (i.e. lots of leading `f`). I don't know exactly what you want, but if you want to interpret byte values always as unsigned values then you should (as you suggest) use an `unsigned char`; my previous comment was saying that even if you do this you should still cast the `unsigned char` explicitly to an `unsigned int` as otherwise it will (usually) be promoted to an `int` which is not the correct type for `%x`.
Charles Bailey
@BeeBand: I've updated my answer, does this more fully address your question?
Charles Bailey
@Charles - yes this answers my question perfectly, thanks!
BeeBand
@Charles: promoting unsigned char to int won't matter like promoting char -> int -> unsigned is doing. As uchars can't overflow an int (I think; standards pedants can correct me here), then the int will have exactly the same value as the uchar - exactly the behaviour we need, and what we don't get from schar -> int -> unsigned.
ijw
@ijw: The point is that you get _undefined behaviour_ if the parameter passed for a `%x` is an `int`, not an `unsigned int` in the varags parameter list. On many platforms it may not matter, but it is safer not to rely on this. There are plaforms where an `unsigned char` could overflow an `int`, but that's OK because on these platforms `unsigned char` would be promoted to `unsigned int` anyway.
Charles Bailey
Ever get that feeling that we should have written a C implementation with completely bonkers binary number representations, just for testing purposes?
ijw
Your conversion techniques are ugly and overly verbose. Either use the correct type to begin with (`unsigned char`), or simply bit-wise and with `0xffU` (which will inherently give an `unsigned int` result).
R..
@R..: `unsigned char` is _not_ the correct type for `%x`, `unsigned int` is (`unsigned char` is likely to be promoted to `int`, not `unsigned`). What would you do? It might not be practical to make `pixel_data` and array of `unsigned int`, it might imply using a lot more storage. Because you have to be exact with the types of parameters for `printf` I believe it pays to be explicit. Bitwise AND with 0xffU generates the correct type, but now you've hard coded and assumption about the number of value bits in a `char` (or `unsigned char`) so I don't think that it's a universally superior choice.
Charles Bailey
@Charles, @R.. I concur with Charles in that it pays to be explicit and make no assumptions about what data type you're dealing with every step of the way - especially when debugging at the bit level. I think someone made the point earlier about being careful with `printf` and bitmasks for debugging - in that it may not actually be a true representation of what's in memory. And actually, anyway it turns out that `pixel_data` *has* to be `unsigned char` array for various reasons - its RGB data that I pulled from a bitmap file. I get very wierd results if I use any other types.
BeeBand
@Charles, since the array is named `pixel_data`, we can safely assume it's being used for storing values in the range 0-255. Thus `unsigned char` is the right type and `0xffU` is the correct value to and it with. If the implementation happened to have a larger `char` type you still wouldn't want to use it.
R..
@R..: I'm sorry, I don't think I understand either of your points. Whatever the natural type for `pixel_data` is, the correct type for `printf`'s `%x` is `unsigned int`. If that's not the same as the type of `pixel_data` (0-255 and `unsigned char` aren't a given) then at least one conversion needs to be made. Yes, bitwise AND with an `unsigned` will force that conversion but it's not the only option. Second, if the implementation has a larger `char` type, you would have no option, `char` is as small as the smallest type on any implementation by definition.
Charles Bailey
By "use it", I meant make use of the additional range.
R..
+1  A: 

Better use the standard-format-flags

printf(" %#1x ", pixel_data[0] );

then your compiler puts the hex-prefix for you.

A: 

printf("%#04hhx ", foo);

domen
@domen - sadly I'm using a third party API implementation of `printf` which doesn't seem to allow the `hh` formatter. But good to know about - thanks.
BeeBand