Home > Net >  Why memcpy() is able to convert values between Numbering Systems
Why memcpy() is able to convert values between Numbering Systems

Time:11-10

Please take a look at this snippet of code:

uint16_t buffer_array[2]; //Two uint16_t = 4 bytes

temp = swSerial.read(); // Populate with serial readings coming from the sensor 
switch(received_counter)
{
  case 3: //when we receive the fourth byte from the sensor
     buffer_array[0] = temp;
     break;
  case 4: //fifth byte
     buffer_array[0]  = temp;
     break;
  case 5: //sixth byte
     buffer_array[1] = temp;
     break;
  case 6: //seventh byte
     buffer_array[1]  = temp;
     break;
  default:
     break;
}

This code populates the array I declared with readings from a source. When this ends, the array buffer_array[2] will have its 4 bytes populated (or 8 hexadecimal values). This is straightforward.

But now we have four raw bytes stored in the array. These four bytes represent a floating point value (i know that from documentation). So we want these 4 bytes (8 hex values) converter to a floating point value.

I can do this with:

float voltage;
memcpy(&voltage, &buffer_array, sizeof(float));

My question is why does this work? memcpy() copies n characters from memory area src to memory area dest. Does this imply an implicit casting when the values in the memory area is not the same as the value in the destination variable? Am i missing something?

CodePudding user response:

Does this imply an implicit casting when the values in the memory area is not the same as the value in the destination variable?

No, memcpy doesn't even know the source and destination types. It just takes two void pointers.

Afterwards, the destination contains the same bit pattern as the source - that's all.

Types are only how we interpret a bit pattern. Presumably your serial device has contrived to encode a valid IEEE 754 single-precision binary32 in the protocol it sends you.

This use of memcpy is the easiest correct way to convert between the type convenient for deserializing the bit pattern, and the type convenient for interpreting it. The bit pattern itself is unchanged.

If you just hexdump (or otherwise inspect in your debugger) the contents of buffer_array and voltage, you'll can verify this.

Note that there are older popular approaches, but they rely on type-punning and have problems:

  1. union { uint16_t x[2]; float y; };

    prohibited by strict aliasing (ie, allowing this to work would inhibit loads of other optimizations) and union subobject lifetime rules

  2. float *f = reinterpret_cast<float *>(buffer_array);

    problems with both strict aliasing and possibly alignment, if alignof(uint16_t[2]) < alignof(float)

CodePudding user response:

memcpy() works strictly with bytes, it has no clue what they represent, or what is or isn't valid in the context of the destination. You point it at some memory, you point it at a destination, and you tell it how many bytes to copy. It does that as quickly as it can.

If you want to convert an arbitrary chunk of memory into a floating point value you can just recast it as such, at least if you're confident the result is valid.1 This is as easy as:

float x = *((float*) &buffer_array[0]);

Where buffer_array is a pointer to your assembled data.

By the way, I'm not sure the floating point value you're constructing is valid as the format is not split evenly into two 16-bit chunks. What you have there looks like some kind of fixed point representation.

--

1 Don't forget to account for any endian issues for both the source data, and your system's architecture.

  • Related