简体   繁体   中英

Memcpy in C - Using arrays in for loops then assigning to a struct

I am using a for loop to copy an array from a UART RX buffer to memory.

This looks like as follows:

UART2_readTimeout(uartR2, rxBuf3, 54, NULL, 500);
GPIO_toggle(CONFIG_GPIO_LED_3);

if ((rxBuf3[4] == 0x8C) && (rxBuf3[10] != 0x8C)) {
    int i;
    for (i = 0; i < 47; i++) {
        sioR2[i]=rxBuf3[i];
    }

I want to then use a struct such as the following to make it possible to use dot notation when working with and organizing the data:

typedef struct
{
  uint16_t voltage;
  uint16_t current;
  uint16_t outTemp;
  uint16_t inTemp;
  uint16_t status;
  uint32_t FaultA;
  uint32_t FaultB;
  uint32_t FaultC;
  uint32_t FaultD;
  uint8_t softwareMode;
  uint8_t logicLoad;
  uint8_t outputBits;
  uint16_t powerOut;
  uint32_t runHours;
  uint16_t unitAddresses[6];
} unitValues;

Assuming the total length of these are the same, is it possible to perform a memcpy on the entire array to a single instance of the struct?

Array  : 001110101....110001
         |||||||||||||||||||   <- memcpy
         vvvvvvvvvvvvvvvvvvv
Struct : 001110101....110001

Provided that your C implementation offers a way to ensure that the layout of your structure is the same as the layout that the driver in question uses for writing the buffer, a pretty good way to go about this would be to have the driver write directly into the structure. I'm inferring the signature of the driver function here, but that would probably be something like:

UART2_readTimeout(uartR2, (uint8_t *) &values, 54, NULL, 500);

Assuming that uint8_t is an alias for unsigned char or maybe char , it is valid to write into the representation of the structure via a pointer of type uint8_t * . Thus, this avoids you having to make a copy.

The trick, however, is the structure layout. Supposing that you expect the data to be laid out as the structure members given, in the order given, with no gaps, such a structure layout would prevent structure instances being positioned in memory so that all members are aligned on addresses that are multiples of their sizes. Depending on the alignment rules of your hardware, this might be perfectly fine, but probably either it would slow accesses to some of the members, or it would make attempts to access some of the members crash the program.

if you still want to proceed then you will need to check your compiler's documentation for information about how to get the wanted layout of your structure. You might look for references to structure "packing", structure layout, or structure member alignment. There is no standard way to do this -- if your C implementation supports it at all then that constitutes an extension, with implementation-specific details.

All the same issues and caveats would apply to using memcpy to copy the buffer contents onto an instance of your structure type, so if you don't multiple copies of the data and you can arrange to make bulk copy onto the structure work, then you're better off writing directly onto the structure than writing into a separate buffer and then copying.


On the other hand, the safe and standard alternative would be to allow your implementation to lay out the structure however it thinks is best, And to copy the data out of your buffer into the structure in member-by-member fashion, with per-member memcpy() s. Yes, the code will be a bit tedious, but it will not be sensitive to alignment-related issues, nor even to reordering structure members or adding new ones.

You have to change the packing align to 1 byte for the structure.

#pragma pack(1) /* change */
typedef struct {
...
}
#pragma pack() /* restore */

In theory , you can use memcpy() to set the member fields of a struct from the elements of a byte array. However, you will need to be very careful to prevent your compiler from adding 'empty' fields to your struct (see: Structure padding and packing ) unless those empty fields are taken into account when loading the data into the source array. (The elements of the source array will be packed into contiguous memory.)

Different compilers use different command-line and/or #pragma options to control structure packing but, for the MSVC compiler, you can use the #pragma pack(n) directives in your source code or the /Zp command-line switch.

Using the MSVC compiler, the structure you have provided will have a total size of 47 bytes only if you have single-byte packing ; for default packing, the size will be 52 bytes.

The following code block shows where these 'extra' bytes will be inserted for different packing sizes.

#pragma pack(push, 1) // This saves the current packing level then sets it to "n" (1, here)
typedef struct {
    uint16_t voltage;
    uint16_t current;
    uint16_t outTemp;
    uint16_t inTemp;
    uint16_t status;
    // 4+ byte packing will insert two bytes here
    uint32_t FaultA;
    uint32_t FaultB;
    uint32_t FaultC;
    uint32_t FaultD;
    uint8_t softwareMode;
    uint8_t logicLoad;
    uint8_t outputBits;
    // 2+ byte packing will insert one byte here
    uint16_t powerOut;
    // 4+ byte packing will insert two bytes here
    uint32_t runHours;
    uint16_t unitAddresses[6];
} unitValues;
#pragma pack(pop) // This restores the previous packing level

So, the sizeof(unitValues) will be:

  • 47 bytes when using #pragma pack(1)
  • 48 bytes when using #pragma pack(2)
  • 52 bytes when using #pragma pack(4) (or any higher/default value)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM