20

I am trying to printout an unsigned char value as a 2-Digit hex value, but always getting the result as 4-Digit hex values, not sure what's wrong with my code.

// unsigned char declaration
unsigned char status = 0x00;
// printing out the value 
printf("status = (0x%02X)\n\r", (status |= 0xC0));

I am expecting a 2 digit hex result as 0xC0, but I always get 0xC0FF.

As well, when I tried to print the same variable (status) as an unsigned char with the %bu format identifier I got the output as 255.

How do you get just the two hex characters as output?

Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145
Asad Waheed
  • 459
  • 2
  • 4
  • 10
  • There'd be more ways of explaining what was going on if the output was 0xFFC0 (though this would be verging on compiler bug territory). What happens if you move the `|=` assignment into a separate statement before the `printf()`? If the result changes, you probably do have a compiler bug to deal with. Which version of which compiler are you using on which version of which platform (o/s)? – Jonathan Leffler Sep 10 '12 at 02:58
  • IDE-Version: µVision V4.02 Copyright (c) Keil Elektronik GmbH / Keil Software, Inc. 1995 - 2009 Tool Version Numbers: Toolchain: PK51 Prof. Developers Kit Version: 9.01 Toolchain Path: C:\Software\Keil\C51\BIN\ C Compiler: C51.Exe V9.01 Assembler: A51.Exe V8.02 Linker/Locator: BL51.Exe V6.22 Librarian: LIB51.Exe V4.24 Hex Converter: OH51.Exe V2.6 CPU DLL: S8051.DLL V3.72 Dialog DLL: DP51.DLL V2.59 – Asad Waheed Sep 10 '12 at 03:00
  • When I run a small program containing the code you showed, the output is `status = (0xC0)`. Are you sure that code is *exactly* what's in your program? Write a small self-contained program (with `#include ` and a full definition of the `main()` function), and show it to us along with your output. Copy-and-paste both; don't re-type them. – Keith Thompson Sep 10 '12 at 03:02
  • 5
    As I understand it, Keil C generates code for the 8051 (a small CPU for embedded systems), and I don't think it fully conforms to the C standard. Your program *should* work correctly. I've added a "keil" tag; perhaps that will draw the attention of someone who knows more about the vagaries of the compiler you're using. – Keith Thompson Sep 10 '12 at 03:04
  • Incidentally, your `printf()` format string includes `\n\r`. That is not the conventional order for the CRLF line ending; you'd normally write `\r\n`. However, that should have very little to do with the output of the number. – Jonathan Leffler Sep 10 '12 at 03:07
  • I double check by loading my code again to my hardware and it seems to show a 4-digit hex outout again :( – Asad Waheed Sep 10 '12 at 03:09
  • It is beginning to feel like the compiler has a problem. Have you tried writing: `unsigned char status = 0x00; status |= 0xC0; printf("0x%02X\r\n", status);`? What about: `int st_pr; unsigned char status = 0x00; status |= 0xC0; st_pr = status & 0xFF; printf("0x%02X\r\n", st_pr);`? What if you change the format to `%d`? Did you include ``? – Jonathan Leffler Sep 10 '12 at 03:12
  • Yes, I did try (both of the suggested solution) just now, and again a 4-digit hex output is coming. – Asad Waheed Sep 10 '12 at 03:17

4 Answers4

35

As far as I know, the Keil C compiler doesn't fully conform to the C standard. If so, it's likely that it doesn't quite follow the standard promotion rules for things like passing char values to variadic functions; on an 8-bit CPU, there are performance advantages in not automatically expanding 8-bit values to 16 bits or more.

As a workaround, you can explicitly truncate the high-order bits before passing the argument to printf. Try this:

#include <stdio.h>

int main(void) {
    unsigned char status = 0x00;
    status |= 0xC0;

    printf("status = 0x%02X\n", (unsigned int)(status & 0xFF));
    return 0;
}

Doing a bitwise "and" with 0xFF clears all but the bottom 8 bits; casting to unsigned int shouldn't be necessary, but it guarantees that the argument is actually of the type expected by printf with a "%02X" format.

You should also consult your implementation's documentation regarding any non-standard behavior for type promotions and printf.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Thanks for this workaround solution. It is working correctly. – Asad Waheed Sep 10 '12 at 03:26
  • 2
    The `& 0xFF` shouldn't be necessary unless the compiler is far more broken than not implicitly converting char arguments to int. The output of 0xC0FF suggests that it is in fact not doing the conversion, and picking up the other 8 bits from trash on the stack or left over in a register. But +1 for the good catch. – Jim Balter Sep 10 '12 at 03:41
7

you are sending a char to a format string which expects an int. The printf function is grabbing another byte off the stack to fill it out. Try

 printf("%02X",(int)(status|0xC0));
AShelly
  • 34,686
  • 15
  • 91
  • 152
  • Could you tell me how to print it as hex (which expects it as unsigned char)? – Asad Waheed Sep 10 '12 at 02:50
  • 1
    Incorrect. Since `printf` is a variadic function, arguments of type `char` or `unsigned char` are promoted to `int` (or, on some exotic systems, `unsigned int`). And `"%02X"` expects `unsigned int`, not `int`. – Keith Thompson Sep 10 '12 at 02:50
  • 1
    `unsigned char` gets promoted to `int` because `printf()` is a variadic function (assuming `` is included). If the header isn't included, then (a) it should be and (b) you don't have a prototype in scope so the `unsigned char` will still be promoted to `int`. – Jonathan Leffler Sep 10 '12 at 02:53
  • 1
    And it turns out I was mostly correct, by accident. :) The error sure smelled like a caller/callee mismatch.. – AShelly Sep 11 '12 at 20:36
5

Looking at all the answers, I think probably we are missing another way of doing this.

const unsigned char chararr[]="abceXYZ";
for (int i=0; i< 7; ++i) {
    printf("%#04X %d %c\n", chararr[i], chararr[i], chararr[i]);
}
0X61 97 a
0X62 98 b
0X63 99 c
0X65 101 e
0X58 88 X
0X59 89 Y
0X5A 90 Z

If you use %#04x small x then the output will b 0x small x prefix. The # pound sign tells the function to print the 0x. 04 to instruct how many digits to output, if input is '0x0a' it will print this,without 04 it will print '0xa'.

In my computer, Dell workstation, the output is as expected by the question. Unless

unsigned char status = 0x00;
printf("status = (0x%02X)\n\r", (status |= 0xC0));
// output
//status = (0xC0)
// is exactly expected by the original question. 

Better illustrated by examples:

 37    printf("status = (%#02x)\n", (status |= 0xC0));
 38    printf("status = (%#04x)\n", (status |= 0xC0));
 39    printf("status = (%#04x)\n", 0x0f);
 40    printf("status = (%#02x)\n", 0x0f);
status = (0xc0)
status = (0xc0)
status = (0x0f)
status = (0xf)
Kemin Zhou
  • 6,264
  • 2
  • 48
  • 56
1

Cast it to unsigned char:

printf("status = (0x%02X)\n\r", (unsigned char)(status |= 0xC0));
Tutankhamen
  • 3,532
  • 1
  • 30
  • 38
  • It have to work [check here](http://liveworkspace.org/code/84e1cbab43e57af5cd355e9634ac346d)... BTW, which compiler do you use? – Tutankhamen Sep 10 '12 at 02:59
  • If that did make a difference, it would only be because it was avoiding some compiler bug. Notice that your link gives the same result with the OP's code. (You should have tried that first.) – Jim Balter Sep 10 '12 at 03:03
  • IDE-Version: µVision V4.02 Copyright (c) Keil Elektronik GmbH / Keil Software, Inc. 1995 - 2009 License Information: Tool Version Numbers: Toolchain: PK51 Prof. Developers Kit Version: 9.01 Toolchain Path: C:\Software\Keil\C51\BIN\ C Compiler: C51.Exe V9.01 Assembler: A51.Exe V8.02 Linker/Locator: BL51.Exe V6.22 Librarian: LIB51.Exe V4.24 Hex Converter: OH51.Exe V2.6 CPU DLL: S8051.DLL V3.72 Dialog DLL: DP51.DLL V2.59 – Asad Waheed Sep 10 '12 at 03:04