C中的Memcpy-在for循环中使用数组,然后分配给结构

C中的Memcpy-在for循环中使用数组,然后分配给结构,c,memory,embedded,memcpy,C,Memory,Embedded,Memcpy,我使用for循环将阵列从UART RX缓冲区复制到内存 如下所示: 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];

我使用
for
循环将阵列从UART RX缓冲区复制到内存

如下所示:

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];
    }
假设它们的总长度相同,是否可以对结构的单个实例的整个数组执行
memcpy

Array  : 001110101....110001
         |||||||||||||||||||   <- memcpy
         vvvvvvvvvvvvvvvvvvv
Struct : 001110101....110001
阵列:001110101…110001

|||||||||||||||||||必须将结构的packing align更改为1字节

#pragma pack(1) /* change */
typedef struct {
...
}
#pragma pack() /* restore */
理论上,可以使用
memcpy()
从字节数组的元素设置
struct
的成员字段。但是,您需要非常小心地防止编译器将“空”字段添加到
结构中(请参见:),除非在将数据加载到源数组时考虑到这些空字段。(源数组的元素将打包到连续内存中。)

不同的编译器使用不同的命令行和/或
#pragma
选项来控制结构打包,但对于MSVC编译器,您可以在源代码中使用
#pragma pack(n)
指令或
/Zp
命令行开关

使用MSVC编译器,仅当使用单字节打包时,您提供的结构的总大小为47字节;对于默认打包,大小将为52字节

下面的代码块显示了这些“额外”字节将插入不同包装大小的位置

#pragma pack(push,1)//这将保存当前的打包级别,然后将其设置为“n”(1,此处)
类型定义结构{
uint16_t电压;
uint16_t电流;
uint16输出温度;
uint16_t inTemp;
uint16_t状态;
//4+字节打包将在此处插入两个字节
uint32断层;
uint32_t故障B;
uint32_t断层c;
uint32断层;
uint8_t软件模式;
uint8_t逻辑负载;
uint8_t输出位;
//2+字节打包将在此处插入一个字节
uint16_t断电;
//4+字节打包将在此处插入两个字节
uint32_t运行小时;
uint16_t单元地址[6];
}单位价值;
#pragma pack(pop)//这将恢复以前的打包级别
因此,
sizeof(unitValues)
将是:

  • 使用
    #pragma pack(1)
  • 使用
    #pragma pack(2)
  • 使用
    #pragma pack(4)
    (或任何更高/默认值)时为52字节

如果您的C实现提供了一种方法来确保您的结构的布局与所讨论的驱动程序用于写入缓冲区的布局相同,那么一种很好的方法就是让驱动程序直接写入结构。我在这里推断驱动程序函数的签名,但可能是这样的:

UART2_readTimeout(uartR2, (uint8_t *) &values, 54, NULL, 500);
假设
uint8\u t
unsigned char
的别名,或者可能是
char
,通过
uint8\u t*
类型的指针写入结构的表示是有效的。因此,这避免了您必须复制

然而,诀窍在于结构布局。假设您希望数据以给定的顺序排列为给定的结构成员,并且没有间隙,这样的结构布局将防止结构实例定位在内存中,以便所有成员都在其大小的倍数上对齐。根据硬件的对齐规则,这可能很好,但可能会减慢对某些成员的访问速度,或者尝试访问某些成员会使程序崩溃

如果仍要继续,则需要检查编译器的文档,以获取有关如何获得所需结构布局的信息。您可以查找对结构“打包”、结构布局或结构杆件对齐的引用。没有标准的方法可以做到这一点——如果您的C实现完全支持它,那么这就构成了一个扩展,包含了实现特定的细节

所有相同的问题和注意事项都适用于使用
memcpy
将缓冲区内容复制到结构类型的实例上,因此,如果您没有数据的多个副本,您可以安排在结构上进行批量复制,然后,最好直接写入结构,而不是写入单独的缓冲区,然后进行复制



另一方面,安全和标准的替代方案是允许您的实现以其认为最好的方式布局结构,并以每个成员
memcpy()
s的方式将数据从缓冲区复制到结构中。是的,代码会有点乏味,但它对对齐相关问题不敏感,甚至对重新排序结构成员或添加新成员也不敏感。

可能。您能告诉我们您为编译器/平台设置了哪些结构打包选项吗?在结构的中间,奇数个<代码> UTI8*T 看起来有点问题,这只是一个例子,我没有把我给的两个对齐。我的rxBuf3在uint8中,所以理论上我可以做任何数字?我正在使用Code Composer Studio,不确定结构打包选项。在多个位置连续的uint16\u t成员的奇数也可能会出现问题。此外,您似乎正在将54字节的数据复制到数组中,但是结构成员的大小之和只有47。是否同时需要数组和结构?如果是我,我希望能将数据直接读取到结构中,一个成员一个成员地读取。回答得很好,谢谢!然而,如果他们能够安排它,使
memcpy()
做正确的事情,那么让驱动程序直接写入结构,而不是只作为暂存区的中间缓冲区,这将对他们有利。@JohnBollinger
UART2_readTimeout(uartR2, (uint8_t *) &values, 54, NULL, 500);