2

I have a C program that receives a 64 byte array of char (which is passed via USB). Depending on the first byte (which indicates the command type) I want to 'impose' a structure over the char array to make the code clearer.

For example, if the command code is 10 I would expect something like:

struct
{
    uint8_t commandNumber;
    uint16_t xPos;
    uint16_t yPos;
    int32_t identificationNumber;
 } commandTen;

So I would like to cast my char packet[64] 'onto' commandTen and then access the fields using something like:

localVar = commandTenPacket->xPos;

How can this be achieved in C?

Thanks in advance!

Simon Inns
  • 108
  • 1
  • 5
  • 2
    You're gonna need to "pack" the struct to get rid of the alignment padding. – Mysticial Aug 19 '12 at 06:23
  • 1
    This can't be done portably. You would probably be better off reading the bytes and building the values from the bytes directly. – Vaughn Cato Aug 19 '12 at 06:25
  • 1
    Sounds to me like keyword `union` will go a long way towards handling this. – abelenky Aug 19 '12 at 06:28
  • You might find this post useful: http://stackoverflow.com/q/371371/951890 – Vaughn Cato Aug 19 '12 at 06:34
  • Perhaps I should add that the code is intended for a very resource limited AVR microcontroller so portability is less important that saving RAM. Since I have the 64 byte buffer I am looking for a way to access it in a more readable manner without using any more RAM if possible (which is why using a library or copying to local variables is undesirable). – Simon Inns Aug 19 '12 at 06:40
  • ...and don't forget about if there is and endianess you need to lookout for. – epatel Aug 19 '12 at 07:50

3 Answers3

8

First, as others said you'd have to ensure that your struct has no padding. Your compiler probably has an extension for that, #pragma pack or so.

Define a struct for each of your use cases, not a variable as in your example.

Then define a union

typedef union overlay overlay;

union overlay {
 uint8_t raw[64];
 struct type10 command10;
 struct type42 command42;
};

Now create a buffer of that type

overlay buffer;

Feed the "raw" part to your function that receives the data: getit(buffer.raw). And then

switch (buffer.raw[0]) {
 case 10: {
   // use buffer.command10
   break;
 }
 case 42: {
   // use buffer.command42
 }
}

This is guaranteed by the C standard to work well, since you are reading everything as uint8_t AKA unsigned char. In fact, the principle use case for unions is just this sort of "type puning". The whole network layer with it different types of IPv4, IPv6 etc addresses works in a similar way.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • You could also add the `uint8_t` with the command code to the `union` directly; it could be a bit more elegant checking that way. – Michał Górny Aug 19 '12 at 07:45
  • @MichałGórny, yes, but these things then are really a question of taste. I personally would prefer to always "see" `raw[0]` to be reminded that there is a byte transfer somewhere. – Jens Gustedt Aug 19 '12 at 07:47
1

Don't cast. Use memcpy

char packet[64];
...

assert(sizeof commandTen <= sizeof packet);
memcpy(&commandTen, packet, sizeof commandTen);

This assumes that the sizes and memory layouts match properly (the same assumption a cast-based solution would use).

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

There is a way using pointers. Let me try to explain it this way:

struct commandX {
   int x;
};
struct commandMessage {
   char[8] msg;
};

char* message = (char*) malloc(sizeof(char)*9); // this will allow us a struct 
                                                // size of 8 bytes, since the
                                                // first byte will store the
                                                // information on how to inter-
                                                // the memory (or you do it in
                                                // a different way).
// 1. determine your offset
//    in this case, it would be the length of the command on how to interpret.
//    basically, it has to be the count of bytes from the
//    beginning of your message to the start of your struct
int offset = sizeof(char) * 1;
// 2. read the command (that would be: message[0])
// 3. now get rid of the type information, so that you can address every byte
void* temp = (void*) message;
// 4.1 if it is commandX:
struct commandX *x_temp = (struct commandX*) (temp+offset);
    // use x_temp->x
// 4.2 if it is commandMessage:
struct commandMessage *msg_temp = (struct commandMessage*) (temp+offset)
   // use msg_temp->msg
xQuare
  • 1,293
  • 1
  • 12
  • 21