C memcpy到结构体分配

3

我写了一个简单的程序,用memcpy测试从字节缓冲区向结构体复制字节。然而,我没有得到预期的结果。

我分配了一个100字节的缓冲区,并设置值为0、1、2…99。然后将字节复制到一个16字节的结构体中。

在某个地方,字节0x01丢失了。我正在努力理解发生了什么。

#include <stdio.h>
#include <stdint.h>

struct Test {

    uint8_t a;
    uint16_t b;
    uint32_t c;
    uint64_t d;
    uint8_t e;
};

int main(int argc, char** argv) {

    uint8_t buffer[100];
    for (uint8_t i = 0; i < 100; i++) {
        buffer[i] = i;
    }

    struct Test test;
    memcpy(&test, buffer, sizeof(struct Test));

    printf("A is %ld and should be %ld\n", test.a, 0x00);
    printf("B is %ld and should be %ld\n", test.b, 0x0201);
    printf("C is %ld and should be %ld\n", test.c, 0x06050403);
    printf("D is %ld and should be %ld\n", test.d, 0x1413121110090807);
    printf("E is %ld and should be %ld\n", test.e, 0x15);
}

我得到了一个结果:
A is 0 and should be 0
B is 770 and should be 513
C is 117835012 and should be 100992003
D is 1084818905618843912 and should be 1446519769808832519
E is 16 and should be 21

770(B 值)是字节 0302,而不是 0201(int 513)。


4
你需要了解结构体中的填充(padding)问题。 - Eugene Sh.
是的,如果你以十六进制输出 %lx,那么效果会更容易看到。 - Weather Vane
1
请参阅#pragma pack效果 - harold
1个回答

5
这是因为编译器为了确保b在16位边界上对齐而向你的结构体添加了填充。因此,第二个字节是未使用的。
如果您开始重新排列结构体,您将看到更大的差异。同样,32位和64位值将需要适合其类型的对齐方式。因此,由于填充而导致的显着“空洞”会出现。试试吧!
通常,您会看到具有reserved成员的结构体(尤其是针对二进制格式),而不是依赖于填充。

啊,这很有道理。新手错误。 所以我猜大家的共识是不要使用memcpy来执行这种操作。最好手动分配每个变量(a = buffer [0],b = buffer [2] << 8 | bufer [1])。 - user2840470
这完全取决于你想要实现什么。使用memcpy从/到缓冲区读写结构体非常常见。但是,在进行假设时,必须小心处理与执行环境外部相关的内容(例如,在代码中硬编码内存内容或从文件中读取)。 - paddy
@user2840470 不一定。将字节流 memcopied到结构体中的常见实际用途是当您接收网络数据并且必须将第N级协议的有效负载转发到(N + 1)级协议时(例如,IP数据包有效负载被转发到TCP)。在这种情况下,这只是一个设计问题:结构体被设计为紧凑数据友好,并且PACK / UNPACK宏始终存在于移植层中以处理特殊情况。所以问题是:您的结构是否可以更改为紧凑数据友好? :) (顺便说一句:+1对于问答都很有趣) - Roberto Caboni
1
@Cubo78,现在我明白了为什么会以某种方式布置结构体。我肯定可以改变结构体的顺序以更好地匹配单词边界。谢谢你的解释! - user2840470

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接