Convert int (32 bits) to char (8 bits) - c

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...

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.

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.

Related

How to convert int to byte in c?

I would like to convert int to byte in C.
How could i get the value?
in Java
int num = 167;
byte b = num.toByte(); // -89
in C
int num = 167;
???
There is no such type as Byte in native C. Although if you don't want to import new libs, you can create one like this :
typedef unsigned char Byte
And then create any variable you'd like with it :
int bar = 15;
Byte foo = (Byte)bar
You can simply cast to a byte:
unsigned char b=(unsigned char)num;
Note that if num is more than 255 or less than 0 C won't crash and simply give the wrong result.
In computer science, the term byte is well-defined as an 8 bit chunk of raw data. Apparently Java uses a different definition than computer science...
-89 is not the value 167 "converted to a byte". 167 already fits in a byte, so no conversion is necessary.
-89 is the value 167 converted to signed 2's complement with 8 bits representation.
The most correct type to use for signed 2's complement 8 bit integers in C is int8_t from stdint.h.
Converting from int to int8_t is done implicitly in C upon assignment. There is no need for a cast.
int num = 167;
int8_t b = num;
byte is a java signed integer type with a range of -128 to 127.
The corresponding type in C is int8_t defined in <stdint.h> for architectures with 8-bit bytes. It is an alias for signed char.
You can write:
#include <stdint.h>
void f() {
int num = 167;
int8_t b = num; // or signed char b = num;
...
If your compiler emits a warning about the implicit conversion to a smaller type, you can add an explicit cast:
int8_t b = (int8_t)num; // or signed char b = (signed char)num;
Note however that it is much more common to think of 8-bit bytes as unsigned quantities in the range 0 to 255, for which one would use type uint8_t or unsigned char. The reason java byte is a signed type might be that there is no unsigned type in this language, but it is quite confusing for non-native readers.
byte can also be defined as a typedef:
typedef unsigned char byte; // 0-255;
or
typedef signed char byte; // -128-127;
Do not use type char because it is implementation defined whether this type is signed or unsigned by default. Reserve type char for the characters in C strings, although many functions actually consider these to be unsigned: strcmp(), functions from <ctype.h>...

Clarifications about unsigned type in C

Hi I'm currently learning C and there's something that I quite don't understand.
First of all I was told that if I did this:
unsigned int c2 = -1;
printf("c2 = %u\n", c2);
It would output 255, according to this table:
But I get a weird result: c2 = 4294967295
Now what's weirder is that this works:
unsigned char c2 = -1;
printf("c2 = %d\n", c2);
But I don't understand because since a char is, well, a char why does it even print anything? Since the specifier here is %d and not %u as it should be for unsigned types.
The following code:
unsigned int c2 = -1;
printf("c2 = %u\n", c2);
Will never print 255. The table you are looking at is referring to an unsigned integer of 8 bits. An int in C needs to be at least 16 bits in order to comply with the C standard (UINT_MAX defined as 2^16-1 in paragraph §5.2.4.2.1, page 22 here). Therefore the value you will see is going to be a much larger number than 255. The most common implementations use 32 bits for an int, and in that case you'll see 4294967295 (2^32 - 1).
You can check how many bits are used for any kind of variable on your system by doing sizeof(type_or_variable) * CHAR_BIT (CHAR_BIT is defined in limits.h and represents the number of bits per byte, which is again most of the times 8).
The correct code to obtain 255 as output is:
unsigned char c = -1;
printf("c = %hhu\n", c);
Where the hh prefix specifier means (from man 3 printf):
hh: A following integer conversion corresponds to a signed char or unsigned char argument, or a following n conversion corresponds to a pointer to a signed char argument.
Anything else is just implementation defined or even worse undefined behavior.
In this declaration
unsigned char c2 = -1;
the internal representation of -1 is truncated to one byte and interpreted as unsigned char. That is all bits of the object c2 are set.
In this call
printf("c2 = %d\n", c2);
the argument that has the type unsigned char is promoted to the type int preserving its value that is 255. This value is outputted as an integer.
Is this declaration
unsigned int c2 = -1;
there is no truncation. The integer value -1 that usually occupies 4 bytes (according to the size of the type int) is interpreted as an unsigned value with all bits set.
So in this call
printf("c2 = %u\n", c2);
there is outputted the maximum value of the type unsigned int. It is the maximum value because all bits in the internal representation are set. The conversion from signed integer type to a larger unsigned integer type preserve the sign propagating it to the width of the unsigned integer object.
In C integer can have multiple representations, so multiple storage sizes and value ranges
refer to the table below for more details.

Converting unsigned char array to signed short in C

I want to convert an array of unsigned char to a signed int!
I've already done some try, and the conversion works for the single element like this:
unsigned char byte[2];
signed char *valueS[2];
byte[0] = 0b11110111;
byte[1] = 0b00001001;
//Conversion
for(int i = 0; i < 2; i++)
{
valueS[i] = (signed char*)&byte[i];
}
//Result
printf("Val 0 -> %d \n", *valueS[0]); // print -9 Correctly
printf("Val 1 -> %d \n", *valueS[1]); // print 9 Correctly
//But when i try to print a 16 bit signed
printf("Int %d \n", *(signed short*)valueS); //It doesn't work! I expected -2295
How can i get the 16 bit signed int from that unsigned char array? Thank you in advance!
How can i get the 16 bit signed int from that unsigned char array?
Supposing you mean you want to obtain the int16_t whose representation is byte-for-byte identical to the contents of an arbitrary array of two unsigned char, the only conforming approach is to declare an int16_t object and copy the array elements to its representation. You could use the memcpy() function to do the copying, or you could do it manually.
For example,
#include <stdint.h>
// ...
unsigned char byte[2] = { 0xF7, 0x05 };
int16_t my_int;
unsigned char *ip = (unsigned char *) &my_int;
ip[0] = byte[0];
ip[1] = byte[1];
printf("Int %d \n", my_int);
You might see a recommendation to use a pointer aliasing trick to try to reinterpret the bytes of the array directly as the representation of an integer. That would take a form similar to your code example, but such an approach is non-conforming, and formally it yields undefined behavior. You may access the representation of an object of any type via a pointer to [unsigned] char, as the code in this answer does, but, generally, you may not otherwise access an object via a pointer to a type incompatible with that object's.
Note also that the printf above is a bit sloppy. In the event that int16_t is a different type from int, such as short int, the corresponding printf directive for it will have a length modifier in it -- likely %hd. But because of details of the way printf is declared, it is the result of promoting my_int to int that will be passed to printf. That rescues the mismatch, and in practice, the printed result will be the same as if you used the correct directive.

Converting non-Ascii characters to int in C, the extra bits are supplemented by 1 rather than 0

When coding in C, I have accidently found that as for non-Ascii characters, after they are converted from char (1 byte) to int (4 bytes), the extra bits (3 bytes) are supplemented by 1 rather than 0. (As for Ascii characters, the extra bits are supplemented by 0.) For example:
char c[] = "ā";
int i = c[0];
printf("%x\n", i);
And the result is ffffffc4, rather than c4 itself. (The UTF-8 code for ā is \xc4\x81.)
Another related issue is that when performing right shift operations >> on a non-Ascii character, the extra bits on the left end are also supplemented by 1 rather than 0, even though the char variable is explicitly converted to unsigned int (for as for signed int, the extra bits are supplemented by 1 in my OS). For example:
char c[] = "ā";
unsigned int u_c;
int i = c[0];
unsigned int u_i = c[0];
c[0] = (unsigned int)c[0] >> 1;
u_c = (unsigned int)c[0] >> 1;
i = i >> 1;
u_i = u_i >> 1;
printf("c=%x\n", (unsigned int)c[0]); // result: ffffffe2. The same with the signed int i.
printf("u_c=%x\n", u_c); // result: 7fffffe2.
printf("i=%x\n", i); // result: ffffffe2.
printf("u_i=%x\n", u_i); // result: 7fffffe2.
Now I am confused with these results... Are they concerned with the data structures of char, int and unsigned int, or related to my operating system (ubuntu 14.04), or related to the ANSI C requirements? I have tried to compile this program with both gcc(4.8.4) and clang(3.4), but there is no difference.
Thank you so much!
It is implementation-defined whether char is signed or unsigned. On x86 computers, char is customarily a signed integer type; and on ARM it is customarily an unsigned integer type.
A signed integer will be sign-extended when converted to a larger signed type;
a signed integer converted to unsigned integer will use the modulo arithmetic to wrap the signed value into the range of the unsigned type as if by repeatedly adding or subtracting the maximum value of the unsigned type + 1.
The solution is to use/cast to unsigned char if you want the value to be portably zero-extended, or for storing small integers in range 0..255.
Likewise, if you want to store signed integers in range -127..127/128, use signed char.
Use char if the signedness doesn't matter - the implementation will probably have chosen the type that is the most efficient for the platform.
Likewise, for the assignment
unsigned int u_c; u_c = (uint8_t)c[0];,
Since -0x3c or -60 is not in the range of uint16_t, then the actual value is the value (mod UINT16_MAX + 1) that falls in the range of uint16_t; iow, we add or subtract UINT16_MAX + 1 (notice that the integer promotions could trick here so you might need casts if in C code) until the value is in the range. UINT16_MAX is naturally always 0xFFFFF; add 1 to it to get 0x10000. 0x10000 - 0x3C is 0xFFC4 that you saw. And then the uint16_t value is zero-extended to the uint32_t value.
Had you run this on a platform where char is unsigned, the result would have been 0xC4!
BTW in i = i >> 1;, i is a signed integer with a negative value; C11 says that the value is implementation-defined, so the actual behaviour can change from compiler to compiler. The GCC manuals state that
Signed >> acts on negative numbers by sign extension.
However a strictly-conforming program should not rely on this.

How to print 1 byte with printf?

I know that when using %x with printf() we are printing 4 bytes (an int in hexadecimal) from the stack. But I would like to print only 1 byte. Is there a way to do this ?
Assumption:You want to print the value of a variable of 1 byte width, i.e., char.
In case you have a char variable say, char x = 0; and want to print the value, use %hhx format specifier with printf().
Something like
printf("%hhx", x);
Otherwise, due to default argument promotion, a statement like
printf("%x", x);
would also be correct, as printf() will not read the sizeof(unsigned int) from stack, the value of x will be read based on it's type and the it will be promoted to the required type, anyway.
You need to be careful how you do this to avoid any undefined behaviour.
The C standard allows you to cast the int to an unsigned char then print the byte you want using pointer arithmetic:
int main()
{
int foo = 2;
unsigned char* p = (unsigned char*)&foo;
printf("%x", p[0]); // outputs the first byte of `foo`
printf("%x", p[1]); // outputs the second byte of `foo`
}
Note that p[0] and p[1] are converted to the wider type (the int), prior to displaying the output.
You can use the following solution to print one byte with printf:
unsigned char c = 255;
printf("Unsigned char: %hhu\n", c);
If you want to print a single byte that is present in a larger value type, you can mask and/or shift out the required value (e.g. int x = 0x12345678; x & 0x00FF0000 >> 16). Or just retrieve the required byte by casting the needed byte location using a (unsigned) char pointer and using an offset.

Resources