C - unsigned int to unsigned char array conversion
Asked Answered
R

7

23

I have an unsigned int number (2 byte) and I want to convert it to unsigned char type. From my search, I find that most people recommend to do the following:

 unsigned int x;
 ...
 unsigned char ch = (unsigned char)x;

Is the right approach? I ask because unsigned char is 1 byte and we casted from 2 byte data to 1 byte.

To prevent any data loss, I want to create an array of unsigned char[] and save the individual bytes into the array. I am stuck at the following:

 unsigned char ch[2];
 unsigned int num = 272;

 for(i=0; i<2; i++){
      // how should the individual bytes from num be saved in ch[0] and ch[1] ??
 }

Also, how would we convert the unsigned char[2] back to unsigned int.

Thanks a lot.

Rooted answered 25/4, 2012 at 16:26 Comment(1)
possible duplicate of signed short to byte in c++Redraft
M
26

You can use memcpy in that case:

memcpy(ch, (char*)&num, 2); /* although sizeof(int) would be better */

Also, how would be convert the unsigned char[2] back to unsigned int.

The same way, just reverse the arguments of memcpy.

Mickens answered 25/4, 2012 at 16:29 Comment(0)
G
16

How about:

ch[0] = num & 0xFF;
ch[1] = (num >> 8) & 0xFF;

The converse operation is left as an exercise.

Grigsby answered 25/4, 2012 at 16:28 Comment(0)
T
8

How about using a union?

union {
    unsigned int num;
    unsigned char ch[2];
}  theValue;

theValue.num = 272;
printf("The two bytes: %d and %d\n", theValue.ch[0], theValue.ch[1]);
Trudey answered 25/4, 2012 at 16:31 Comment(0)
N
7

It really depends on your goal: why do you want to convert this to an unsigned char? Depending on the answer to that there are a few different ways to do this:

  • Truncate: This is what was recomended. If you are just trying to squeeze data into a function which requires an unsigned char, simply cast uchar ch = (uchar)x (but, of course, beware of what happens if your int is too big).

  • Specific endian: Use this when your destination requires a specific format. Usually networking code likes everything converted to big endian arrays of chars:

    int n = sizeof x;
    for(int y=0; n-->0; y++)
        ch[y] = (x>>(n*8))&0xff;
    

    will does that.

  • Machine endian. Use this when there is no endianness requirement, and the data will only occur on one machine. The order of the array will change across different architectures. People usually take care of this with unions:

    union {int x; char ch[sizeof (int)];} u;
    u.x = 0xf00
    //use u.ch 
    

    with memcpy:

    uchar ch[sizeof(int)];
    memcpy(&ch, &x, sizeof x);
    

    or with the ever-dangerous simple casting (which is undefined behavior, and crashes on numerous systems):

    char *ch = (unsigned char *)&x;
    
Nilotic answered 25/4, 2012 at 16:54 Comment(0)
L
4

Of course, array of chars large enough to contain a larger value has to be exactly as big as this value itself. So you can simply pretend that this larger value already is an array of chars:

unsigned int x = 12345678;//well, it should be just 1234.
unsigned char* pChars;

pChars = (unsigned char*) &x;

pChars[0];//one byte is here
pChars[1];//another byte here

(Once you understand what's going on, it can be done without any variables, all just casting)

Lightly answered 25/4, 2012 at 16:33 Comment(2)
If 12345678 fits in unsigned int and sizeof(unsigned int) == 2, then CHAR_BIT is bigger than usual ;-)Linsang
I blame the 32-bit society for spoiling me!Lightly
N
3

You just need to extract those bytes using bitwise & operator. OxFF is a hexadecimal mask to extract one byte. Please look at various bit operations here - http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/

An example program is as follows:

#include <stdio.h>

int main()
{
    unsigned int i = 0x1122;
    unsigned char c[2];

    c[0] = i & 0xFF;
    c[1] = (i>>8) & 0xFF;

    printf("c[0] = %x \n", c[0]);
    printf("c[1] = %x \n", c[1]);
    printf("i    = %x \n", i);

    return 0;
}

Output:

$ gcc 1.c 
$ ./a.out 
c[0] = 22 
c[1] = 11 
i    = 1122 
$
Niedersachsen answered 25/4, 2012 at 16:37 Comment(1)
You meant to shift num & 0xFF00 over by 8, right? (num & 0xFF00) >>8. Otherwise, you just have a 16-bit integer where the low-byte happens to be zero. You still don't have a byte. Alternately, you can just shift: num >> 8;Trudey
A
1

Endorsing @abelenky suggestion, using an union would be a more fail proof way of doing this.

union unsigned_number {
    unsigned int  value;        // An int is 4 bytes long
    unsigned char index[4];     // A char is 1 byte long
};

The characteristics of this type is that the compiler will allocate memory only for the biggest member of our data structure unsigned_number, which in this case is going to be 4 bytes - since both members (value and index) have the same size. Had you defined it as a struct instead, we would have 8 bytes allocated on memory, since the compiler does its allocation for all the members of a struct.

Additionally, and here is where your problem is solved, the members of an union data structure all share the same memory location, which means they all refer to same data - think of that like a hard link on GNU/Linux systems.

So we would have:

union unsigned_number my_number;

// Assigning decimal value 202050300 to my_number
// which is represented as 0xC0B0AFC in hex format
my_number.value = 0xC0B0AFC;   // Representation:  Binary - Decimal
                               // Byte 3: 00001100 - 12
                               // Byte 2: 00001011 - 11
                               // Byte 1: 00001010 - 10
                               // Byte 0: 11111100 - 252

// Printing out my_number one byte at time
for (int i = 0; i < (sizeof(my_number.value)); i++)
{
    printf("index[%d]: %u, 0x%x\n", \
        i, my_number.index[i], my_number.index[i]);
}

// Printing out my_number as an unsigned integer
printf("my_number.value: %u, 0x%x", my_number.value, my_number.value);

And the output is going to be:

index[0]: 252, 0xfc
index[1]: 10, 0xa
index[2]: 11, 0xb
index[3]: 12, 0xc
my_number.value: 202050300, 0xc0b0afc

And as for your final question, we wouldn't have to convert from unsigned char back to unsigned int since the values are already there. You just have to choose by which way you want to access it

Note 1: I am using an integer of 4 bytes in order to ease the understanding of the concept. For the problem you presented you must use:

union unsigned_number {
    unsigned short int value;        // A short int is 2 bytes long
    unsigned char      index[2];     // A char is 1 byte long
};

Note 2: I have assigned byte 0 to 252 in order to point out the unsigned characteristic of our index field. Was it declared as a signed char, we would have index[0]: -4, 0xfc as output.

Abarca answered 7/4, 2022 at 17:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.