Tell me more ×
Electrical Engineering Stack Exchange is a question and answer site for electronics and electrical engineering professionals, students, and enthusiasts. It's 100% free, no registration required.

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?

share|improve this question
5  
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
1  
This would be a much better fit for Stack Overflow. – sharptooth Sep 19 '12 at 7:28

5 Answers

up vote 17 down vote accepted

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

share|improve this answer
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.

share|improve this answer

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.

share|improve this answer
1  
... 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.

share|improve this answer

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.