Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to cast uint8_t array of 4 to uint32_t in c

Tags:

c

casting

I am trying to cast an array of uint8_t to an array of uint32_t, but it seems not to be working.
Can any one help me on this. I need to get uint8_t values to uint32_t.
I can do this with shifting but i think there is a easy way.

uint32_t *v4full;
v4full=( uint32_t *)v4;
while (*v4full) {
    if (*v4full & 1)
        printf("1");
    else
        printf("0");

    *v4full >>= 1;
}
printf("\n");
like image 202
user2714949 Avatar asked Nov 30 '13 03:11

user2714949


4 Answers

Given the need to get uint8_t values to uint32_t, and the specs on in4_pton()...

Try this with a possible correction on the byte order:

uint32_t i32 = v4[0] | (v4[1] << 8) | (v4[2] << 16) | (v4[3] << 24);
like image 84
bvj Avatar answered Nov 09 '22 22:11

bvj


There is a problem with your example - actually with what you are trying to do (since you don't want the shifts).

See, it is a little known fact, but you're not allowed to switch pointer types in this manner

specifically, code like this is illegal:

type1 *vec1=...;
type2 *vec2=(type2*)vec1;
// do stuff with *vec2

The only case where this is legal is if type2 is char (or unsigned char or const char etc.), but if type2 is any other type (uint32_t in your example) it's against the standard and may introduce bugs to your code if you compile with -O2 or -O3 optimization.

This is called the "strict-aliasing rule" and it allows compilers to assume that pointers of different types never point to related points in memory - so that if you change the memory of one pointer, the compiler doesn't have to reload all other pointers.

It's hard for compilers to find instances of breaking this rule, unless you make it painfully clear to it. For example, if you change your code to do this:

uint32_t v4full=*((uint32_t*)v4);

and compile using -O3 -Wall (I'm using gcc) you'll get the warning:

warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

So you can't avoid using the shifts.

Note: it will work on lower optimization settings, and it will also work in higher settings if you never change the info pointer to by v4 and v4_full. It will work, but it's still a bug and still "against the rules".

like image 43
rabensky Avatar answered Nov 10 '22 00:11

rabensky


If v4full is a pointer then the line

uint32_t *v4full;
v4full=( uint32_t)&v4;

Should throw an error or at least a compiler warning. Maybe you mean to do

uint32_t *v4full;
v4full=( uint32_t *) v4;

Where I assume v4 is itself a pointer to a uint8 array. I realize I am extrapolating from incomplete information…

EDIT since the above appears to have addressed a typo, let's try again.

The following snippet of code works as expected - and as I think you want your code to work. Please comment on this - how is this code not doing what you want?

#include <stdio.h>
#include <inttypes.h>

int main(void) {
    uint8_t v4[4] = {1,2,3,4};
    uint32_t *allOfIt;
    allOfIt = (uint32_t*)v4;
    printf("the number is %08x\n", *allOfIt);
}

Output:

the number is 04030201

Note - the order of the bytes in the printed number is reversed - you get 04030201 instead of 01020304 as you might have expected / wanted. This is because my machine (x86 architecture) is little-endian. If you want to make sure that the order of the bytes is the way you want it (in other words, that element [0] corresponds to the most significant byte) you are better off using @bvj's solution - shifting each of the four bytes into the right position in your 32 bit integer.

Incidentally, you can see this earlier answer for a very efficient way to do this, if needed (telling the compiler to use a built in instruction of the CPU).

like image 29
Floris Avatar answered Nov 10 '22 00:11

Floris


One other issue that makes this code non-portable is that many architectures require a uint32_t to be aligned on a four-byte boundary, but allow uint8_t to have any address. Calling this code on an improperly-aligned array would then cause undefined behavior, such as crashing the program with SIGBUS. On these machines, the only way to cast an arbitrary uint8_t[] to a uint32_t[] is to memcpy() the contents. (If you do this in four-byte chunks, the compiler should optimize to whichever of an unaligned load or two-loads-and-a-shift is more efficient on your architecture.)

If you have control over the declaration of the source array, you can #include <stdalign.h> and then declare alignas(uint32_t) uint8_t bytes[]. The classic solution is to declare both the byte array and the 32-bit values as members of a union and type-pun between them. It is also safe to use pointers obtained from malloc(), since these are guaranteed to be suitably-aligned.

like image 26
Davislor Avatar answered Nov 09 '22 23:11

Davislor