这是将 struct/int/float... 类型传递给 memcpy 以复制到 char 数据区域的合法用途吗?
Is this a legal use of passing struct/int/float... type to memcpy to copy on char data area?
我可以使用memcpy
将一种数据类型的数据复制到另一种数据类型吗...
假设我有 char buffer[1024]
,现在我想将大小为 306(例如)的 struct abc x;
复制到 buffer
..
所以这个 legal/possible 使用没有任何意外行为吗?
memcpy(buffer, &x, sizeof(x));
然后如果我将该数据复制回 struct abc y;
?
会有任何意外行为
memcpy(&y, &buffer, sizeof(struct abc));
如果我有 struct abc *z
,
z = (struct abc *) buffer;
我尝试从缓冲区中提取 struc abc
。
我已经尽力解释了,如果您发现任何错误,请忽略或建议...
这个:
char buffer[1024];
struct abc x;
// Init x
memcpy(buffer, &x, sizeof(x));
struct abc y;
memcpy(&y, buffer, sizeof(y));
将确保如果你有一个函数 T foo(struct abc arg)
其中 T
可以是任何类型,那么 T a = foo(x);
和 T a = foo(y);
将表现相同。除非 sizeof(struct abc)
大于缓冲区。
但是,如果您这样做:
puts(buffer);
不保证提供相同的打印输出。这意味着如果您将缓冲区写入文件或通过网络将其发送到另一台机器,那么另一台机器上的程序 运行 可能无法正确读取它。
memcpy
方法是正确的,它不会调用未定义的行为。大多数现代编译器都知道 memcpy
函数的工作原理,但在许多情况下它们不会调用它。
示例:
struct abc
{
int a,b,c;
double d,e,f;
}x;
void foo(void *ptr)
{
memcpy(&x, ptr, sizeof(x));
}
foo:
movdqu xmm0, XMMWORD PTR [rdi]
movaps XMMWORD PTR x[rip], xmm0
movdqu xmm1, XMMWORD PTR [rdi+16]
movaps XMMWORD PTR x[rip+16], xmm1
mov rax, QWORD PTR [rdi+32]
mov QWORD PTR x[rip+32], rax
ret
指针双关不安全,你不应该使用它(如果你想知道为什么 - 请阅读严格的别名规则 What is the strict aliasing rule?)。
其他答案已经解决了 memcpy
部分。是的,可以使用 memcpy
来 将类型 T 的对象复制到 char 缓冲区中,然后使用 memcpy
将数据从 char 缓冲区复制回来到类型 T 的对象。没问题。
在这个回答中,我将解决问题的最后一部分,即可以这样做吗:
memcpy(buffer, &x, sizeof(x));
z = (struct abc *) buffer;
答案是:不,根据 C 标准,它不安全
一个问题是缓冲区不能保证保持结构成员的对齐要求。
在现代系统(至少我知道的所有现代系统)上它会工作得很好,但如果你想严格遵守 C 标准,你不应该做这样的事情。
我可以使用memcpy
将一种数据类型的数据复制到另一种数据类型吗...
假设我有 char buffer[1024]
,现在我想将大小为 306(例如)的 struct abc x;
复制到 buffer
..
所以这个 legal/possible 使用没有任何意外行为吗?
memcpy(buffer, &x, sizeof(x));
然后如果我将该数据复制回 struct abc y;
?
memcpy(&y, &buffer, sizeof(struct abc));
如果我有 struct abc *z
,
z = (struct abc *) buffer;
我尝试从缓冲区中提取 struc abc
。
我已经尽力解释了,如果您发现任何错误,请忽略或建议...
这个:
char buffer[1024];
struct abc x;
// Init x
memcpy(buffer, &x, sizeof(x));
struct abc y;
memcpy(&y, buffer, sizeof(y));
将确保如果你有一个函数 T foo(struct abc arg)
其中 T
可以是任何类型,那么 T a = foo(x);
和 T a = foo(y);
将表现相同。除非 sizeof(struct abc)
大于缓冲区。
但是,如果您这样做:
puts(buffer);
不保证提供相同的打印输出。这意味着如果您将缓冲区写入文件或通过网络将其发送到另一台机器,那么另一台机器上的程序 运行 可能无法正确读取它。
memcpy
方法是正确的,它不会调用未定义的行为。大多数现代编译器都知道 memcpy
函数的工作原理,但在许多情况下它们不会调用它。
示例:
struct abc
{
int a,b,c;
double d,e,f;
}x;
void foo(void *ptr)
{
memcpy(&x, ptr, sizeof(x));
}
foo:
movdqu xmm0, XMMWORD PTR [rdi]
movaps XMMWORD PTR x[rip], xmm0
movdqu xmm1, XMMWORD PTR [rdi+16]
movaps XMMWORD PTR x[rip+16], xmm1
mov rax, QWORD PTR [rdi+32]
mov QWORD PTR x[rip+32], rax
ret
指针双关不安全,你不应该使用它(如果你想知道为什么 - 请阅读严格的别名规则 What is the strict aliasing rule?)。
其他答案已经解决了 memcpy
部分。是的,可以使用 memcpy
来 将类型 T 的对象复制到 char 缓冲区中,然后使用 memcpy
将数据从 char 缓冲区复制回来到类型 T 的对象。没问题。
在这个回答中,我将解决问题的最后一部分,即可以这样做吗:
memcpy(buffer, &x, sizeof(x));
z = (struct abc *) buffer;
答案是:不,根据 C 标准,它不安全
一个问题是缓冲区不能保证保持结构成员的对齐要求。
在现代系统(至少我知道的所有现代系统)上它会工作得很好,但如果你想严格遵守 C 标准,你不应该做这样的事情。