3

I have these definitions:

int data = uartBaseAddress[UART_DATA_REGISTER / 4]; // data coming from UART RX port
char message[20]; // array of 20 chars

Now when I try to do this:

message[0] = (char) data;
printf("%x", message[0]);

It prints (for example): "ffffff9c". Of course I want only the last 8 bits ("9c") and I don't understand how to properly do the conversion from int to char.

EDIT: I mean: i have to populate the array like this:

data = 0xFFFFFF9c;
message[0] = data & 0xFF; -- it has to contain only 9c
data = 0xFFFFFFde;
message[1] = data & 0xFF; -- it has to contain only de
etc...
HBv6
  • 3,487
  • 4
  • 30
  • 43

2 Answers2

6

The conversion is correct. It's the printf that's the problem.

Apparently plain char is signed on your system (it can be either signed or unsigned).

I'm going to guess that the value printed was ffffff9c (8 digits), not ffffffff9c (10 digits); please verify that.

The value of data was probably -100. Converting that value from int to char would yield -100, since that value is within the range of type char (probably -128 .. +127).

But the %x format specifier requires an argument of type unsigned int, not int. The value of message[0] is promoted to int when it's passed to printf, but printf, because of the format string, assumes that the argument is of type unsigned int.

Strictly speaking, the behavior is undefined, but most likely printf will simply take the int value passed to it and treat it as if it were an unsigned int. (int)-100 and (unsigned int)0xffffff9c have the same representation.

There is no printf format specifier to print a signed value in hexadecimal. If you change the format from %x to %d, you'll at least see the correct value.

But you should step back an decide just what you're trying to accomplish. If you want to extract the low-order 8 bits of data, you can do so by masking it, as unwind's answer suggests. Or you can convert it to unsigned char rather than plain char to guarantee that you'll get an unsigned value.

An unsigned char value is still promoted to int when passed to printf, so to be fully correct you should explicitly convert it to unsigned int:

int data = ...;
unsigned char message[20]; // change to unsigned char

message[0] = data;  // no cast needed, the value is implicitly converted
printf("%x", (unsigned int)message[0]);

Strictly speaking, the (unsigned int) isn't necessary. message[0] is an unsigned char, so it will be converted to a non-negative int value, and there's a special-case rule that says int and unsigned int arguments with the same value are interchangeable as function arguments. Still, my own preference is to use the cast because it's clearer, and because adding the cast is easier (particularly for anyone reading the code) than following the line of reasoning that says it's not necessary.

Community
  • 1
  • 1
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Sorry for the mistake, the value printed is of 8 digits. Thanks for the answer, very complete and clear! – HBv6 Jun 04 '14 at 15:07
  • 2
    As a side note, the cast to `unsigned int` isn't necessary. Either `int` covers the range of `unsigned char` in which case an `int` can be interpreted as an `unsigned int` (representing the same value) or (I'm not sure if this would be standard conforming) `UCHAR_MAX` is greater than `INT_MAX` in which case the argument would be promoted to `unsigned int` anyway. – mafso Jun 04 '14 at 15:53
  • @mafso: True, but I still prefer to use the cast; it makes the code clearer, and it's a lot easier than following the line of reasoning that provides it's not necessary. See the last paragraph of my updated answer. – Keith Thompson Jun 05 '14 at 16:22
5

There's no need for message, just mask off the bits you don't want:

printf("%x\n", data & 0xff);

To convert a 32-bit integer to a 4-element array of 8-bit numbers, do:

const uint32_t data = ...
uint8_t message[20];

for(int i = 0; i < 4; ++i)
{
  message[i] = data & 0xff;
  data >>= 8;
}

The above uses little-endian byte order. If data is 0x12345678, then message will begin 0x78, 0x56, 0x34, 0x12.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Your suggestion works fine, but what about if I have to populate an array of char? I tried to do: message[0] = data & 0xFF; but it didn't work... – HBv6 Jun 04 '14 at 14:58
  • 1
    @PierpaoloBagnasco If I got a dime everytime when someone says "didn't work" without more context, I'd be rich. – glglgl Jun 04 '14 at 14:59
  • @glglgl You are right, I mean: after that assignment, the char array populates like I wanted to? If I do: (in a for loop) message[i] = data & 0xFF; - are you sure that every element of the vector is the last 8 bits of data? – HBv6 Jun 04 '14 at 15:02
  • @PierpaoloBagnasco If everything else is correct, this should work exactly the way you describe. – glglgl Jun 04 '14 at 17:01