I have an assignment in C language covering hypothetical situation: I have data transmission between two devices, and they are sending information by float data type. Float is 4 bytes. But data bus is only 8 bits. So they need to separate it to 4 unsigned char's, send it and combine data. At the end i should get number 7.7. Here is some code provided by teacher:
transmision(unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3)
{
float f;
......
//change: 4 bajty c0,c1,c2,c3 to float}
.....
printf ("after transmision f=%f\n",f)
// Should be printed 7.7
I found this solution:
#include <stdio.h>
typedef union {
float f;
unsigned char bajty[4];
} FloatUnion;
void transmisja(unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3) {
FloatUnion data;
// Składamy float z otrzymanych bajtów
data.bajty[0] = c0;
data.bajty[1] = c1;
data.bajty[2] = c2;
data.bajty[3] = c3;
// Wypisujemy otrzymaną wartość
printf("po transmisji f=%f\n", data.f);
}
int main(void) {
FloatUnion data;
// Ustawiamy wartość float na 7.7
data.f = 7.7;
// "Transmisja" – wysyłamy kolejno cztery bajty
transmisja(data.bajty[0], data.bajty[1], data.bajty[2], data.bajty[3]);
return 0;
}
But I wonder if this could be done better?
What you are attempting to do is often called serialization/deserialization. That is: take a larger type and iterate through it as a stream of bytes, then on the receiver side de-serialize it again by unpacking it into the same larger type as the original.
C allows a couple of rather well-defined ways of serialization/deserialization:
Inspect the larger type byte by byte. This can be done by casting the address of any object type into a pointer to character. We may then treat that pointer to character as if it is pointing to a character array of size sizeof(the_larger_type)
. (C24 6.3.3.3)
Example:
float f = 7.7f;
unsigned char* ptr = (unsigned char*)&f;
for(size_t i=0; i<sizeof(f); i++)
send(ptr[i]);
Union type punning, which is the method you picked. This too is mostly well-defined in C (but not in C++) and relies on conversion from one type to the other (C24 6.5.3.4). There are cases where this is poorly-defined but not when going to/from a character array member, since such a character array doesn't have trap representations etc. So that part of your code is just fine.
Copy the original type into a character array using memcpy
, or the other way around. This is the most portable but also inefficient since we need to execute the copy code and store the data at several locations in memory.
Another thing we have to keep in mind, which is fundamental to all data communication, is that the sender has a CPU endianness (What is CPU endianness?), the bus where the data is transported usually has a network endianness and the receiver also has a CPU endianness. This means that both nodes need to convert their byte order to/from the network endianness. On mainstream systems today, the majority of CPU archtectures are little endian and the majority of network protocols are big endian.
So your homework can't really be done, because the teacher didn't specify what network endianness that the communication uses. If we know that the sender and receiver have the same CPU endianness, maybe we might get away with it. Same thing if the bus is some very low-level bus like for example SPI, but we don't know that.