memcpy从一种类型到另一种类型。之后我们如何访问目的地?

时间:2016-09-20 13:12:53

标签: c++11 strict-aliasing

uint32_t u32 = 0;
uint16_t u16[2];
static_assert(sizeof(u32) == sizeof(u16), "");
memcpy(u16, &u32, sizeof(u32)); // defined?
// if defined, how to we access the data from here on?

这是定义的行为吗?并且,如果是这样,我们可以使用什么类型的指针来访问memcpy之后的目标数据?

我们必须使用uint16_t*,因为它适用于声明的类型u16吗?

或者我们必须使用uint32_t*,因为源数据的类型(从memcpy复制的源数据)是uint_32

(个人对C ++ 11 / C ++ 14感兴趣。但是对C等相关语言的讨论也会很有趣。)

2 个答案:

答案 0 :(得分:1)

  

这定义的行为是什么?

是。 memcpy进入pod是明确定义的,并确保大小调整是正确的。

  

我们必须使用uint16_t*,因为它适用于u16的声明类型吗?

是的,当然。 u16是一个包含两个uint16_t的数组,因此必须按原样访问它。通过uint32_t*访问它将是严格别名规则的未定义行为。

源类型是什么并不重要。重要的是你有一个uint16_t[2]类型的对象。

另一方面, this

uint32_t p;
new (&p) uint16_t(42);
std::cout << p;

是未定义的行为,因为现在有一个不同类型的对象,其生命周期从&p开始,我们通过错误的类型访问它。

答案 1 :(得分:1)

C ++标准委托给C标准:

  

标题<cstring>的内容和含义与C标准库标题<string.h>相同。

C标准规定:

  

7.24.1 / 3对于本子条款中的所有函数,每个字符都应解释为它具有unsigned char类型(因此每个可能的对象表示都是有效的并且具有不同的值)。

所以,回答你的问题:是的,行为是定义的。

是的,uint16_t*是合适的,因为uint16_t是对象的类型。

不,来源的类型并不重要。

C ++标准没有指定没有声明类型的对象或它的行为方式。我将其解释为有效类型是为没有声明类型的对象定义的实现。

即使在C中,在这种情况下,来源并不重要。您关注的C标准(草案,N1570)引用的更完整版本,强调我的:

  

6.5 / 6 [...]如果使用memcpy或memmove将值复制到没有声明类型的对象,或者将其复制为字符类型数组,则有效类型为该访问的修改对象以及不修改该值的后续访问是从中复制值的对象的有效类型(如果有)。 [...]

此规则不适用,因为u16中的对象确实具有声明的类型