# What's wrong with this casting in C code for AVR?

I defined two variables:

uint8_t a[2];
uint16_t b;


Next I want to use a as variable of type uint16_t, e. g.

b = (uint16_t)a;


But this is wrong! My programs doesn't works correctly with such code. All is OK when I replace b to uint8_t b[2] and use elementwise operations.

Why?

-
Why don't you throw some values into your example and tell us what your expectation of "correct" is so that we can actually help without speculating about your semantic intent. –  vicatcu Sep 18 '12 at 17:26
This would be a much better fit for Stack Overflow. –  sharptooth Sep 19 '12 at 7:28

a is a pointer to an array of bytes. If you cast it to a uint16_t and assign it to b, then b will contain the address of the base of the array (where it is stored) in SRAM. If you want to treat the two bytes of the array a as an integer, then use a union as suggested by user14284, but be aware that the union will represent the byte array in the memory byte ordering of the architecture (in AVR that would be little-endian, which means byte 0 is the least significant byte). The way to write that in code is:

union{
uint8_t a[2];
uint16_t b;
} x;

x.b[0] = 0x35;
x.b[1] = 0x4A;

// by virtue of the above two assignments
x.a == 0x4A35 // is true


Another way to do this without using a union is to cast a to a uint16_t pointer and then dereference it like so:

uint8_t a[2] = {0x35, 0x4A};
uint16_t b = *((uint16_t *) a);
b == 0x4A35; // because AVR is little endian


If you are using the buffer to store big endian data (e.g. network byte order), then you'll need to byte-swap to use either of these techniques. A way to do that without any branches or temporary variables is:

uint8_t a[2] = {0x35, 0x4A};
a[0] ^= a[1];
a[1] ^= a[0];
a[0] ^= a[1];

a[0] == 0x4A; // true
a[1] == 0x35; // true


Incidentally this is not an AVR or even an embedded-only problem. Application level networking code written for PCs typically calls functions called htonl, htons (host to network, 32- and 16-bit variants) and ntohl, ntohs (network to host, 32- and 16-bit variants) whose implementations are target architecture dependent as to whether they swap the bytes or not (under the assumption that bytes as transmitted 'on the wire' are always big-endian when they are part of multi-byte words).

-
This is a great answer. The key being 'a' on its own is a pointer. –  Jon L Sep 18 '12 at 18:23
"Another way to do this without using a union is to cast a to a uint16_t pointer and then dereference it" -- Actually, this type of casting often breaks strict aliasing rules. You shouldn't do it unless you're compiling with -fno-strict-aliasing. –  Jim Paris Sep 18 '12 at 18:24

If your intent is to concatenate the two 8-bit variables into a 16-bit variable, use a union. If you want to cast a single member of a into b, then specify which element of the array you want to use.

-

On your code you are casting only the pointer to the array.

You need to cast the value pointed by a.

b = (uint16_t)*a;


I never used AVR but if you are working with a 16 bit architecture you have to make sure that a is word aligned. Failing to do this may result in an exception.

-
... this is absolutely not his intent... he wants b to be related to both elements of a (this would totally discregard a[1]), also there are no exceptions or restrictions on what you can cast in avr-gcc –  vicatcu Sep 18 '12 at 17:28

Each member of a is an 8-bit number. It can't hold anything larger. Casting it to 16-bits does not do anything to a. It merely extracts whatever value a might be able to hold, and converts it to 16 bits so that it matches up with the format of b when the value is stored there.

You didn't even refer to a member of a. You must use a[0] or a[1] (and not a[2]!). If you use a by itself, you just get the address of it. (Good catch, Bruno).

Declaring a to be an array of two 8-bit numbers doesn't make it a 16-bit number, either. You can do some things programmatically to store and retrieve 16-bit values using sequences of 8-bit, but not the way you were thinking.

-

If you want to convert the bytes in a into a 16 bit value, and the representation is little-endian (the lower 8 bits of the value come in the first byte), do

uint16_t b = a[0] | (a[1] << 8);


For a big-endian representation do

uint16_t b = (a[0] << 8) | a[1];


Avoid using pointer casts or unions to do this, as that leads to portability problems.

-