为什么 std::memcpy(作为类型双关语的替代方案)不会导致未定义的行为?

Why does std::memcpy (as an alternative to type-punning) not cause undefined behaviour?

本文关键字:未定义 方案 memcpy std 类型 为什么 双关语      更新时间:2023-10-16

在寻找将sizeof(double)chars组合到double的方法时,我在几篇文章中读到,使用std::memcpy是推荐的方法:

char bytes[sizeof(double)];
// fill array
double d;
std::memcpy(&d, bytes, sizeof(double));

但是,我想知道为什么可以定义d的进一步使用行为。

如果它不是一个double,而是一个复杂的类对象,那么访问它肯定也不会被定义,不是吗?那么,为什么double会这样.

编辑:为了明确我的问题,我想指定我的目标:我想找到一种方法将几个char组合到一个double并进一步使用这个双精度,而不会引起未定义的行为。我不希望指定double的值。无论如何,我认为这是不可能的,因为标准甚至没有说明大小,更不用说double位布局了。但是,我要求d有一些有效的(即"可访问"(double值。

为什么使用 std::memcpy 的类型双关语不会导致未定义的行为?

Beause语言是这样说的(最新草案(:

[基本类型]

对于任何可复制类型 T 的对象(可能重叠的子对象除外(,无论该对象是否具有 T 类型的有效值,组成该对象的基础字节([intro.memory](都可以复制到 char、无符号字符或 std::byte ([cstddef.syn]( 数组中。如果将该数组的内容复制回对象,则对象随后应保持其原始值。

但是,请注意该规则的条件。您的代码可能具有未定义的行为,但如果复制的值最初是从另一个双精度值复制的,或者实际上,如果该值可能是从双精度值复制的,则不会(除非其他规则如此规定(。

如果它不是一个双精度,而是一个复杂的类对象,那么访问它肯定也不会被定义,不是吗?

取决于你所说的复杂性是什么意思。这适用的条件在引号规则中。

禁止使用类型双关语,因为它的想法是对C++对象模型的嘲弄。一段内存存储了一个对象,如果你开始访问它,就好像它存储了其他对象一样,那么这甚至意味着什么?如果你可以随意地从内存中读取作为int,作为float写入它,然后作为short从中读取,那么让一个对象存在意味着什么?

在易于复制的对象之间复制字节只是设置该对象值的另一种方法。事实上,这就是一个对象"微不足道的可复制"的逻辑含义:该对象的含义仅由构成其对象表示的字节序列定义(复杂对象并非如此(。但是什么记忆属于哪些对象的神圣性被保留下来。没有"双关语";只是复制数据。

标准中有一个特殊的例外,用于与字节缓冲区之间的memcpy,因为如果没有明确定义的方法,某些操作将是不可能的。

如果您从一种类型复制到字节,然后复制到另一种类型,则肯定会获得未定义的行为。