I have an existing array of bytes that I transmit between 2 Arduinos using I2C. I need to read the bytes from a double and add them to a specific location in the existing array. At the receiving end I need to convert the bytes back to a double.
union {
byte array[4];
float bigNum;
} myUnion;
// bunch of code...
myUnion.bigNum = 3.14;
for (int i = 0; i < sizeof(float); i++) {
SendSomeplace(myUnion.array[i]); // Send to wherever
}
// on the receiving end...
for (int i = 0; i < sizeof(float); i++) {
myUnion.array[i] = readBytes(); // Whatever is takes to read the incoming bytes
}
float pi = myUnion.bigNum;
PhoenixFIF:
That worked. The union command is a simple way to do it. Thanks!
it does work but for the record, this is unchartered territory when it comes to the language specification.
The union is only as big as necessary to hold its largest data member. The other data members are allocated in the same bytes as part of that largest member. The details of that allocation are implementation-defined, and it's undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union
here you are in a case where the compiler does what you expect - One "right" way to do it is to find the address of your double and read the bytes from there
if you test this code
double value = 123.456;
void setup() {
Serial.begin(115200);
byte * valuePtr = (byte *) &value; // we find the address of the first byte
Serial.print(F("Value uses "));
Serial.print(sizeof(value));
Serial.print(F(" bytes in Memory starting at address 0x"));
Serial.println((uint16_t) valuePtr, HEX);
for (size_t i = 0; i < sizeof(value); i++) {
Serial.print(F("Memory address 0x"));
Serial.print((uint16_t) (valuePtr + i), HEX);
Serial.print(F("= 0x"));
Serial.println(*(valuePtr + i), HEX);
}
}
void loop() {}
on a UNO, you should see this in the Serial monitor (set at 115200 bauds)
J-M-L:
it does work but for the record, this is unchartered territory when it comes to the language specification. here you are in a case where the compiler does what you expect - One "right" way to do it is to find the address of your double and read the bytes from there
From K & R (2nd edition):
the results are implementation-dependent if something is stored as one type and extracted as another.
Also, 'union' is a data structure, not a "command".
This is for The C Programming Language... we use a C++ compiler, so behavior "could" be different
also the second edition dates from 1988, we are 30 years later - the specs have evolved.
gfvalvo:
Also, 'union' is a data structure, not a "command".
technically I'd argue a union is a user defined data type rather than structure
// I have an existing array of bytes that I transmit between 2 Arduinos using I2C.
byte ArrayOfBytes[193];
// I need to read the bytes from a double and add them to a specific location in the existing array.
const int SpecificLocation = 37;
double foo = 123.456;
void loop1()
{
for (size_t i = 0; i < sizeof foo; i++)
ArrayOfBytes[SpecificLocation + i] = ((byte *)&foo)[i];
}
// At the receiving end I need to convert the bytes back to a double.
void loop2()
{
byte ReceivedArrayOfBytes[193];
const int SpecificLocation = 37;
double foo;
for (size_t i = 0; i < sizeof foo; i++)
((byte *)&foo)[i] = ReceivedArrayOfBytes[SpecificLocation + i];
}
PS: I learned C in college, too. I graduated in 1980.
johnwasser:
PS: I learned C in college, too. I graduated in 1980.
I'm wearing shoes older than that.
@J-M-L: Yep, it is implementation specific, but if the code on both ends is compiled with the same compiler, I've yet to find an instance where it didn't work.
econjack: @J-M-L: Yep, it is implementation specific, but if the code on both ends is compiled with the same compiler, I've yet to find an instance where it didn't work.
Yes GCC for simple union like this is pretty predictable as there is not much optimization that can happen but you could imagine that the compiler caches some value in registers and when you try to access the bytes another way then the data has not been sync-ed yet in the union data as the compiler could see that as a distinct variable access (and of course you have major caveat about endian-ness across systems)
Also if you have code like this
union {
uint32_t val;
char code;
} var;
you can't be certain which byte will be used to store the code, the compiler in certain architecture could make decisions based on where in memory the variable would be
but overall yes, it's OK, just remember you are using and relying on a non standard compiler extension when it works