I have a struct containing a byte array and several typecast references to various points in the array. Bytes 4:7 may be interpreted as a float, int32_t, or uint32_t as determined by other fields in the packet being received over a serial connection. To make access simple (e.g. message.argument.F
for a float interpretation), I made multiple references to indirected typecast pointers. But when I ran the program, I got a segfault trying to write to the references in the struct. As near as I can tell, the problem has to do with the container, as illustrated by this example snippet (cpp shell: http://cpp.sh/3vmoy):
#include <iostream>
#include <cstring>
using namespace std;
#define PACKET_SIZE 9
#define ARG_I 4
struct Message{
uint8_t bytes[PACKET_SIZE];
uint8_t* argbytes = static_cast<uint8_t*>(argp);
float& argf = *static_cast<float*>(argp);
void* argp = &bytes[ARG_I];
} message;
int main(){
// USING STRUCT
cout << "Using message struct" << endl;
cout << message.argp << endl; // the pointer at index stored in struct
cout << static_cast<float*>(message.argp) << endl; // casting the pointer to a float* - should be the same
cout << &message.argf << endl; // the address of the float reference cast from argp, ** should be the same BUT IS NOT **
// RAW VARS
uint8_t bytes[PACKET_SIZE];
void* argp = &bytes[ARG_I];
float& argf = *static_cast<float*>(argp);
cout << endl << "using raw vars" << endl;
cout << argp << endl; // a pointer to a byte in an array of bytes.
cout << static_cast<float*>(argp) << endl; // the same pointer cast as a float*
cout << &argf << endl; // the address of a float reference cast from argp, **should be the same AND IS.**
}
I expect to see the same address for the pointer, a typecast pointer, and the address of the reference for the indirected pointer. I do see that if I create an array and the pointer/reference as standalone variables, but not for the same declarations in a struct. What arcane knowledge do I lack to explain this behavior (or what silly thing have I overlooked?)
My thoughts for fixing this are to a) ignore it and just typecast the pointer as necessary instead, or b) make some setter/getter functions to access the argument portion of the serial "packet".
CodePudding user response:
There are two major, fundamental differences between the two alternative chunks of code.
void* argp = &bytes[ARG_I];
float& argf = *static_cast<float*>(argp);
Here, this constructs and initializes argp
first, then argf
.
float& argf = *static_cast<float*>(argp);
void* argp = &bytes[ARG_I];
And here, it does not.
This initializes argf
first, then argp
. The consequences of this should be quite apparent.
Note: I'm ignoring all the aliasing rule violations here, that are likely to be a source of further undefined behavior.