最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

University assignment in C - data transmission - Stack Overflow

programmeradmin5浏览0评论

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?

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?

Share Improve this question edited Mar 25 at 14:30 chux 155k16 gold badges151 silver badges301 bronze badges asked Mar 25 at 14:28 VokunVokun 335 bronze badges 4
  • 4 Does it work? yes -> codereview.stackexchange ; no -> what's wrong with it? (expected vs. actual? error messages?) – teapot418 Commented Mar 25 at 14:30
  • 1 @Vokun Can we see the code of the reception? – chux Commented Mar 25 at 14:32
  • I don't have reception part, we don't doing actual transmission, just simulating it as a exercise. I think program work, but im struggling with C so i dont know is it good or not – Vokun Commented Mar 25 at 14:53
  • "Float is 4 bytes. But data bus is only 8 bits" what if the data bus is only 1 bit? – 0___________ Commented Mar 25 at 14:57
Add a comment  | 

3 Answers 3

Reset to default 5

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.

I wonder if this could be done better?

  • The most common concern is the the sending and receiving ends are not certainly using the same endian.

To make a robust solution that works with various machines, the usual solution is to htonl() on the 32-bits as an integer before transmitting the integer and nltoh() on reception from 32-bit data types. These routines are not part of the standard C library, yet commonly available.


  • 2nd, and somewhat outdated, is the the endian of an integer type differs from a floating point type. See there are a number of hardware architectures where floating-point numbers are represented in big-endian form while integers are represented in little-endian form, yet those are increasing less common today.

Not addressed here as it is increasing uncommon today.


  • Floating point encoding has variations on different systems.

OP's approach assumes that the float is the same size and encodes the same as the transmitter and receiving ends of the transmission. This is not so rare an issue in 2025 and obliges additional considerations that are likely beyond what OP has to deal with now.

2 mitigations strategies: 1) Force the network transmission to use binary32 even if that differs on the host machine - which implies a local conversion. 2) Use a text format - which adds additional length issues.

Converting between members of a union like this is not guaranteed to work at all. That part is broken. If your teacher disagrees he can feel free to reply here, but he should keep in mind he is a rank amateur.

A C conforming way to convert is

uint32_t n;
float f = 7.7;
* (float *) &n = f;

That’s conforming with C. It will not compile if there are no 32 bit unsigned integers which is good. It will not detect if float is 3 or 5 bytes. It will not detect if float and integer have different byte order. So you get a 32 bit number with reasonable portability.

You then process (n>>24), (n>>16), (n>>8) and n, each converted to unsigned char. You do this using arithmetic, not by reading bytes, because this makes your code work if both processors have different byte order.

In reality, you will most likely just convert the number to text. This is slightly less efficient, but much easier to get right, so you don’t compare about some extra nanoseconds.

发布评论

评论列表(0)

  1. 暂无评论