trying to copy struct members to byte array in c
Asked Answered
D

7

10

I am attempting to copy the members of a struct containing a mixture of ints, char's and arrays of chars into a byte array to send to a serial line. So far I have

struct msg_on_send
{
    char descriptor_msg[5];
    int  address;
    char space;
    char cmdmsg[5];
    char CR;
    char LF;
};

void switch_output_on()
{
    int member;
    struct msg_on_send SendMsg_on[sizeof member] =
     {

    };
    unsigned char buffer [ sizeof SendMsg_on[0] ];
    showbytes(buffer, serialize(buffer, SendMsg_on));
}

/*************************************************************************** 
*   Function:   ArrayBuild                                                 *
*   Purpose:    Uses memcopy to transfer the struct members sequentially   *
*               into an array of char                                      *
*   Arguments:                                                             *
*   Returns:    size_t i = a count of the number of bytes in the array     *
***************************************************************************/    

size_t ArrayBuild(unsigned char *dst, const struct msg_on_send *object)
{
    size_t i = 0;

    memcpy(&dst[i], &object->descriptor_msg, sizeof object->descriptor_msg);
    i += sizeof object->descriptor_msg;

    memcpy(&dst[i], &object->address, sizeof object->address);
    i += sizeof object->address;

    memcpy(&dst[i], &object->space, sizeof object->space);
    i += sizeof object->space;

    memcpy(&dst[i], &object->cmdmsg, sizeof object->cmdmsg);
    i += sizeof object->cmdmsg;

    memcpy(&dst[i], &object->CR, sizeof object->CR);
    i += sizeof object->CR;

    memcpy(&dst[i], &object->LF, sizeof object->LF);
    i += sizeof object->LF;

    return i;
}

/*********************************************************************** 
*   Function:   USARTWrite                                             *
*   Purpose:    Writes the array data to the USART data register       *
*   Arguments:  void *object = struct member                           *
*               size_t size =  size of array remaining                 *
*   Returns:    None                                                   *
***********************************************************************/

void USARTWrite(const void *object, size_t size)        
{
    const unsigned char *byte;
    for ( byte = object; size--; ++byte )
    {
        printf("%02X", *byte);
    }
    putchar('\n');
}

As I obtained this code, I don't fully understand how it works. I can see that the memcpy takes each element of the struct and makes it into a serial stream indexed by the 'i' variable, but I don't know how the USARTWrite function packetises this into a string, or how to load the array with my struct initialisation.

Sorry this post is a bit long, but I'm just starting this programming lark, and trying to get my head around this concept.

Thanks Dave

EDIT:

wow, many good answers quickly - thanks guys.

slaz: That seems logical to me, I hadn't really thought about that approach as I haven't really got my head around pointers yet, but I am beginning to see that they are an essential part of C, so I duly will have a look.

  • This line of code sends the data to my UART, what would I replace the array containing the message contents with? It seems like I am missing a logical step here where I have a variable telling me where my structure starts and how big it is, but no array to send

    USART_SendData(USART1, message_on_contents[array_count]);
    

Harper Shelby: Thank you for that description, it makes it much clearer in my mind.

rgds

Dave

Directorate answered 27/1, 2009 at 17:29 Comment(2)
The work you're looking for is "serialization". Re-tagged.Morris
not being picky, but this side of the atlantic we use an 's' instead of a 'z. Just semantics of english really, fair enough. DaveDirectorate
G
9

Sorry, I didn't see your comment until just now. The code below compiles on Linux just fine, so I hope it works for you.
printf() is printing in hex, you will get 2 characters for each byte.

#include <stdio.h>

struct msg_on_send
{
char descriptor_msg[5];
int  address;
char space;
char cmdmsg[5];
char CR; 
char LF; 
};

void USARTWrite(const void *object, size_t size)    
{
    const unsigned char *byte;
      for ( byte = object; size--; ++byte )                                     
      {   
          printf("%02X", *byte);
      }   
      putchar('\n');
}

int main (int argc, char**argv)
{
    struct msg_on_send myMsg;
    unsigned char* ptr= (unsigned char*)&myMsg;

    USARTWrite(ptr, sizeof(myMsg));

    return 0;
}

I hope this helps.

~
~

Guenevere answered 28/1, 2009 at 22:50 Comment(1)
since USARTWrite takes a const void *, there should be a need for the ptr variable in main. You could just do: USARTWrite(&myMsg, sizeof(myMsg));Judicature
G
9

You don't have to actually copy the struct into an array of bytes. You could optionally do this:

struct msg_on_send myMessage;

// code to set myMessage to whatever values...

// get a byte pointer that points to the beginning of the struct    
uint8_t *bytePtr = (uint8_t*)&myMessage;

// pass that into the write function, and it will write the amount of bytes passed in
USARTWrite(bytePtr, sizeof(myMessage));

The power of pointers! :)

Guenevere answered 27/1, 2009 at 18:13 Comment(6)
This is quite common and I see it used all the time to zero out large window api structs. I wouldn't make the temp variable bytePtr though.Wristwatch
why not make this temp variable?Directorate
You must have attribute ((packed)) for the struct in order to guarantee that it will always work as expected!Anandrous
I'm sorry, but I don't really understand what you mean, would you be able to explain in a simpler way - I am just starting in programming.Directorate
to slaz: would it be too much to ask for a more complete worked example, as I have just spend 6 hours trying to figure out how to implement this. Several of these concepts are new to me. If thats too cheeky, then sorry! DaveDirectorate
If you use this technique, make sure the receiving end has the same endian as the sending device. Otherwise, you'd get strange integer values.Tko
G
9

Sorry, I didn't see your comment until just now. The code below compiles on Linux just fine, so I hope it works for you.
printf() is printing in hex, you will get 2 characters for each byte.

#include <stdio.h>

struct msg_on_send
{
char descriptor_msg[5];
int  address;
char space;
char cmdmsg[5];
char CR; 
char LF; 
};

void USARTWrite(const void *object, size_t size)    
{
    const unsigned char *byte;
      for ( byte = object; size--; ++byte )                                     
      {   
          printf("%02X", *byte);
      }   
      putchar('\n');
}

int main (int argc, char**argv)
{
    struct msg_on_send myMsg;
    unsigned char* ptr= (unsigned char*)&myMsg;

    USARTWrite(ptr, sizeof(myMsg));

    return 0;
}

I hope this helps.

~
~

Guenevere answered 28/1, 2009 at 22:50 Comment(1)
since USARTWrite takes a const void *, there should be a need for the ptr variable in main. You could just do: USARTWrite(&myMsg, sizeof(myMsg));Judicature
S
2

Full complete example. Perfectly works. Tested under X-CODE 9 Objective-C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Person
{
    char name[20];
    int age;
};

int main()
{
    //structure variable declaratio with initialisation
    struct Person person={"Deniss Ritchie", 60};
    //declare character buffer (byte array)
    unsigned char *buffer=(char*)malloc(sizeof(person));
    int i;

    //copying....
    memcpy(buffer,(const unsigned char*)&person,sizeof(person));

    //printing..
    printf("Copied byte array is:\n");
    for(i=0;i<sizeof(person);i++)
        printf("%02X ",buffer[i]);
    printf("\n");

    //freeing memory..
    free(buffer);
    return 0;
}

Output:

Copied byte array is:
44 65 6E 69 73 73 20 52 69 74 63 68 69 65 00 00 00 00 00 00 3C 00 00 00
Sterrett answered 27/3, 2018 at 11:18 Comment(0)
P
1

It's fairly straightforward: 1. ArrayBuild takes a pointer to a msg_on_send structure, and for each member in there, uses memcpy to copy the bytes into a char array that was passed in like so -

char byteArray[17]; // This assumes 4-byte ints
                    // be careful though, the length *must* be long enough, or 
                    // Bad Things will happen
size_t msgSize; // Holds the size of the message built by ArrayBuild,
                // passed to USARTWrite
struct msg_on_send myMessage;
// Code to fill up myMessage appropriately

msgSize = ArrayBuild(byteArray, &myMessage); // need the & to pass a pointer as required

USARTWrite(myMessage, msgSize);

USARTWrite is just given a char array and a size - it grabs each char in turn and prints it to the screen as a hex value with printf().

The 'magic' is in the ArrayBuild - memcpy does a literal copy of bytes from source to destination, with no translation. Assuming 4-byte ints, the array built by the function will look like so:

                     1 1 1 1 1 1 1 
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
|   A     |   B   |C|    D    |E|F|

A = descriptor_msg (char[5])
B = address (int)
C = space (char)
D = cmdmsg (char[5])
E = CR (char)
F = LF (char)

I'd assume that in the 'real' application, the printf() call would be replaced by a call to a serial port write.

Prom answered 27/1, 2009 at 17:50 Comment(0)
B
1

If I want to treat a structure as an array of bytes I typically use a union to combined the structure with a byte array. For example:

typedef union
{
    struct
    { 
        char descriptor_msg[5]; 
        int  address; 
        char space; 
        char cmdmsg[5]; 
        char CR; 
        char LF; 
    };
    BYTE bytes[];
} msg_on_send;
Brogle answered 8/2, 2010 at 18:56 Comment(0)
G
0

Your struct here is just array of bytes, it contains no pointers that point out of it.

Member-to-member copy is most likely performed to cope with alignment, as (char*) &address will likely be greater than ((char*) &descriptor_msg) + 5.

USARTWrite sends HEX codes of the bytes of your struct to stdout, but discards alignment. Compiling this code with different alignment strategies will lead to different outputs.

Enclose your structure declaration into #pragma pack(push, n) and #pragma pack(pop) to force alignment, and just copy your structure byte-to-byte.

Glabrate answered 27/1, 2009 at 17:44 Comment(1)
The question does not specify platform or compiler, and neither do the tags. Pragmas are not standard, by definition, and vary between compilers. When suggesting pragmas, please at least qualify by compiler.Payroll
I
0

A good solution:

typedef union
{
  unsigned char byte_array[14];
  struct
  {
    uint8_t descriptor_msg[5];
    uint8_t address;
    uint8_t space;
    uint8_t cmdmsg[5];
    uint8_t CR;
    uint8_t LF;
  };
} msg_to_send_t;

#define ARRAY_OF_5_BYTE {0x00, 0x01, 0x02, 0x03, 0x04}

#define MSG_TO_SEND_DEFAULT \
{ \
   .descriptor_msg = ARRAY_OF_5_BYTE, \
   .address = 0x01, \
   .space = 0x02, \
   .cmdmsg = ARRAY_OF_5_BYTE,\
   .CR = 0x03, \
   .LF = 0x04, \
} 

msg_to_send_t msg = MSG_TO_SEND_DEFAULT;

for(i=0; i<sizeof(msg.byte_array); i++)
    printf("%02x", msg.byte_array[i]);
printf("\r\n");
Influx answered 14/5, 2021 at 10:55 Comment(1)
Could you provide more of an explanation? What is so different about your code that isn't in other answers?Visor

© 2022 - 2024 — McMap. All rights reserved.