I have a requirement to have an unsigned 3 byte type in C. I am looking for a way to pack them into a single unsigned int.
Is this safe or does this need to be stored inside an array/structure for the 24 bit size?
unsigned int pack_3Byte(unsigned char b1, unsigned char b2, unsigned char b3)
{
return (b1 << 16) | (b2 << 8) | (b3);
}
Your code is correct but like Olaf says you should use the types uint8_t and uint32_t to ensure that your types are really the width you expect them to be.
This may not be a problem right now, but you should also be aware that the bytes in an integer are stored in different order on different processors. This is called endianness.
Related
unsigned int b;
unsigned char a[] =
{0x00,0x00,0x00,0x12,0x00,0x00,0x81,0x03,0x00,0x00,0x00,0x00,0x01,0x91,0x01,0x01,0xb1,0x04,0x47,0x86,0x8f,0xf8,0x00};
I'm a newbie in C programming,
I need to take the 4 bytes subarray start at a[18] which is 0x47868ff8,
and cast it into corresponding decimal integer:1200001016.
I try to use memcpy(&b,a+18, 4), but it does not seem to work,
Could anyone give me some hints to work out this function?
And if I want to read a char pointer message then cast per 4 bytes in order into integer array,
what is the best way to do that? Thanks.
Copying like that has implementation-defined behavior, and you'll get different results depending on the endianness of the CPU.
To do it portably you can use bitwise operations.
b = (unsigned int)a[18] << 24 | (unsigned int)a[19] << 16 | (unsigned int)a[20] << 8 | a[21];
I need to store a large number, but due to limitations in an old game engine, I am restricted to working with signed short (I can, however, use as many of these as I want).
I need to split an unsigned long (0 to 4,294,967,295) into multiple signed short (-32,768 to 32,767). Then I need to recombine the multiple signed short into a new unsigned long later.
For example, take the number 4,000,000,000. This should be split into multiple signed short and then recombined into unsigned long.
Is this possible in C? Thanks.
In addition to dbush's answer you can also use a union, e.g.:
union
{
unsigned long longvalue;
signed short shortvalues[2];
}
value;
The array of two shorts overlays the single long value.
I assume your problem is finding a place to store these large values. There are options we haven't yet explored which don't involve splitting the values up and recombining them:
Write them to a file, and read them back later. This might seem silly at first, but considering the bigger picture, if the values end up in a file later on then this might seem like the most attractive option.
Declare your unsigned long to have static storage duration e.g. outside of any blocks of code A.K.A globally (I hate that term) or using the static keyword inside a block of code.
None of the other answers so far are strictly portable, not that it seems like it should matter to you. You seem to be describing a twos complement 16-bit signed short representation and a 32-bit unsigned long representation (you should put assertions in place to ensure this is the case), which has implications that restrict the options for the implementation (that is, the C compiler, the OS, the CPU, etc)... so the portability issues associated with them are unlikely to occur. In case you're curious, however, I'll discuss those issues anyway.
The portability issues associated are that one type or the other might have padding bits causing the sizes to mismatch, and that there might be trap representations for short.
Changing the type but not the representation is by far much cleaner and easier to get right, though not portable; this includes the union hack, you could also avoid the union by casting an unsigned long * to a short *. These solutions are the cleanest solutions, which makes Ken Clement's answer my favourite so far, despite the non-portability.
Binary shifts (the >> and << operators), and (the & operator), or (|) operators introduce additional portability issues when you use them on signed types; they're also bulky and clumsy leading to more code to debug and a higher chance that mistakes are made.
You need to consider that while ULONG_MAX is guaranteed to be at least 4,294,967,295, SHORT_MIN is not guaranteed by the C standard to be -32,768; it might be -32,767 (which is quite uncommon indeed, though still possible)... There might be a negative zero or trap representation in place of that -32,768 value.
This means you can't portably rely upon a pair of signed shorts being able to represent all of the values of an unsigned long; even when the sizes match up you need another bit to account for the two missing values.
With this in mind, you could use a third signed char... The implementation-defined and undefined behaviours of the shift approaches could be avoided that way.
signed short x = (value ) & 0xFFF,
y = (value >> 12) & 0xFFF,
z = (value >> 24) & 0xFFF;
value = (unsigned long) x
+ ((unsigned long) y << 12)
+ ((unsigned long) z << 24);
You can do it like this (I used fixed size types to properly illustrate how it works):
#include<stdio.h>
#include<stdint.h>
int main()
{
uint32_t val1;
int16_t val2a, val2b;
uint32_t val3;
val1 = 0x11223344;
printf("val1=%08x\n", val1);
// to short
val2a = val1 >> 16;
val2b = val1 & 0xFFFF;
printf("val2a=%04x\n", val2a);
printf("val2b=%04x\n", val2b);
// to long
val3 = (uint32_t)val2a << 16;
val3 |= (uint32_t)val2b;
printf("val3=%08x\n", val3);
return 0;
}
Output:
val1=11223344
val2a=1122
val2b=3344
val3=11223344
There are any number of ways to do it. One thing to consider is that unsigned long may not have the same size on different hardware/operating systems. You can use exact length types found in stdint.h to avoid ambiguity (e.g. uint8_t, uint16_t, etc.). One implementation incorporating exact types (and cheezy hex values) would be:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>
int main (void) {
uint64_t a = 0xfacedeadbeefcafe, b = 0;
uint16_t s[4] = {0};
uint32_t i = 0, n = 0;
printf ("\n a : 0x%16"PRIx64"\n\n", a);
/* separate uint64_t into 4 uint16_t */
for (i = 0; i < sizeof a; i += 2, n++)
printf (" s[%"PRIu32"] : 0x%04"PRIx16"\n", n,
(s[n] = (a >> (i * CHAR_BIT))));
/* combine 4 uint16_t into uint64_t */
for (n = i = 0; i < sizeof b; i += 2, n++)
b |= (uint64_t)s[n] << i * CHAR_BIT;
printf ("\n b : 0x%16"PRIx64"\n\n", b);
return 0;
}
Output
$ ./bin/uint64_16
a : 0xfacedeadbeefcafe
s[0] : 0xcafe
s[1] : 0xbeef
s[2] : 0xdead
s[3] : 0xface
b : 0xfacedeadbeefcafe
This is one possible solution (which assumes ulong is 32-bits, and sshort is 16-bits):
unsigned long L1, L2;
signed short S1, S2;
L1 = 0x12345678; /* Initial ulong to store away into two sshort */
S1 = L1 & 0xFFFF; /* Store component 1 */
S2 = L1 >> 16; /* Store component 2*/
L2 = S1 | (S2<<16); /* Retrive ulong from two sshort */
/* Print results */
printf("Initial value: 0x%08lx\n",L1);
printf("Stored component 1: 0x%04hx\n",S1);
printf("Stored component 2: 0x%04hx\n",S2);
printf("Retrieved value: 0x%08lx\n",L2);
In the bit-shifting example shown
here:
unsigned long int longInt = 1234567890;
unsigned char byteArray[4];
// convert from an unsigned long int to a 4-byte array
byteArray[0] = (int)((longInt >> 24) & 0xFF) ;
byteArray[1] = (int)((longInt >> 16) & 0xFF) ;
byteArray[2] = (int)((longInt >> 8) & 0XFF);
byteArray[3] = (int)((longInt & 0XFF));
Three questions:
Why is it (int) instead of (unsigned char)? I tried it with unsigned char and it seems to compile just fine.
Is 0XFF necessary? Isn't the new bit shifted-in 0 because Wikipedia says C uses logical shifting and logical shifting shifts in 0? (EDIT: at least it doesn't seem necessary on one with >> 24?)
Can't I just do a memcpy() to copy longInt to a unsigned char buffer? Is it not so because of issue with Endianness? Is there any other reason?
1.
((longInt >> 24) & 0xFF) expression is of type unsigned long int. With the cast to int the expression is first converted to int then to unsigned char. If you don't cast to int the expression is not first converted to int. There are no difference in the two situations and the cast is superfluous.
2.
The 0xff is not necessary. The conversion to unsigned char actually performs the same.
3.
You can use memcpy but it is not portable because it depends on the endianness of the system. It will give different results if the system is big endian or little endian while the bitwise shift solution will give the same results.
I need a function that takes four unsigned char variables as parameters and combines them into an unsigned int. The first char variable being the first byte of the int, the second char being the second byte and so on. Here is what I have so far, it is not working properly and I can't figure out why after messing around with it and googling for several hours.
uint32_t combineChar(unsigned char one, unsigned char two, unsigned char three, unsigned char four){
uint32_t com;
com = (uint32_t)one;
com = com << 8 | (uint32_t)two;
com = com << 8 | (uint32_t)three;
com = com << 8 | (uint32_t)four;
return com;
}
Your code is endianess-depend. The first byte (of uint32_t) is in some systems the leftest, and in some systems the rightest, so you may store the bytes in the opposite way than what to you want.
(Actually, if you want just the uint32_t, it's fine. Problems begin when you union it with char[4], or similar stuff)
Check the precedence of the "<<" and "|" operators.
uint32_t combineChar(unsigned char one, unsigned char two
, unsigned char three, unsigned char four){
return one | (two << 8) | (three << 16) | (four <<24);
}
These are my two variables with which I want to do an xor operation (in C).
unsigned long int z=0xB51A06CD;
unsigned char array[] = {0xF0,0xCC,0xAA,0xF0};
desired output= 0X45D6AC3D
I know I cannot do a simple z ^ array, because it's a character array and not a single character. Will I have to do XOR of one byte at a time or is there a function for it in C?
I am trying all kinds of crazy things to get it done, but failing miserably all the time. If anyone can help me out with a small code snippet or at least a rough idea, I would be extremely grateful.
Cast the array, which is treat as a pointer to the first element in an expression like this one, to a long pointer instead of char pointer , and dereference it.
unsigned long result = z ^ *(unsigned long *)array;
Just make an unsigned long int out of your array (warning, it depends on the machine endianness!):
unsigned long int z=0xB51A06CD;
unsigned char array[] = {0xF0,0xCC,0xAA,0xF0};
unsigned long int w = 0;
w |= array[0] << 0;
w |= array[1] << 8;
w |= array[2] << 16;
w |= array[3] << 24;
unsigned long output = z ^ w;
unsigned long int z=0xB51A06CD;
unsigned char array[] = {0xF0,0xCC,0xAA,0xF0};
unsigned long tmp;
memcpy(&tmp, array, sizeof tmp);
... z ^ tmp ...
Note that this still makes a number of non-portable assumptions: that unsigned long is 4 bytes, and that the system's endianness is what you expect it to be.
As others mentioned, you have to worry about endianness and size of long. Here's how to make it safe:
1) instead of unsigned long, use uint32_t (from inttypes.h), to be sure you get a 4 byte type.
2) use htonl() as a platform-independent way to ensure you interpret the array as a big-endian value
z ^ htonl(*(uint32_t *)array);