代码之家  ›  专栏  ›  技术社区  ›  geza

STD::不同的可复制类型之间的MimcPy未定义行为吗?

  •  51
  • geza  · 技术社区  · 6 年前

    我一直在用 std::memcpy 规避 严格混叠 很长一段时间。

    例如,检查 float ,像 this :

    float f = ...;
    uint32_t i;
    static_assert(sizeof(f)==sizeof(i));
    std::memcpy(&i, &f, sizeof(i));
    // use i to extract f's sign, exponent & significand
    

    但是,这次,我检查了标准,没有发现任何能验证这一点的东西。我只找到了 this :

    对于任何对象(除了潜在重叠的子对象),不管对象是否持有T类型的有效值,构成对象的底层字节([内存])都可以复制到char、unchar字符或STD::字节([CSTDDEF.SYN])数组中。 四十 如果该数组的内容被复制回该对象,则该对象随后应保持其原始值。[示例:

    #define N sizeof(T)
    char buf[N];
    T obj;                          // obj initialized to its original value
    std::memcpy(buf, &obj, N);      // between these two calls to std​::​memcpy, obj might be modified
    std::memcpy(&obj, buf, N);      // at this point, each subobject of obj of scalar type holds its original value
    

    结束示例]

    this :

    对于任何平凡的可复制类型t,如果指向t的两个指针指向不同的t对象obj1和obj2,其中obj1和obj2都不是潜在的重叠子对象,如果构成obj1的基础字节([intro.memory])被复制到obj2, 四十一 obj2随后应保持与obj1相同的值。[示例:

    T* t1p;
    T* t2p;
    // provided that t2p points to an initialized object ...
    std::memcpy(t1p, t2p, sizeof(T));
    // at this point, every subobject of trivially copyable type in *t1p contains
    // the same value as the corresponding subobject in *t2p
    

    结束示例]

    所以, STD:MimcPy A型 浮动 收件人/发件人 char[] 允许,以及 STD:MimcPy 也允许在相同的琐碎类型之间进行交互。

    我的第一个例子(以及相关的答案)是否定义得很好?或检查 浮动 是为了 STD:MimcPy 它变成一个 unsigned char[] 缓冲,并使用 shift S和 or S构建A uint32_t 从那里?


    注:查看 STD:MimcPy 的担保可能无法回答这个问题。据我所知,我可以替换 STD:MimcPy 通过一个简单的字节复制循环,问题将是相同的。

    3 回复  |  直到 6 年前
        1
  •  21
  •   user743382    6 年前

    标准可能没有正确地说这是允许的,但几乎可以肯定,据我所知,所有的实现都将把这视为定义的行为。

    为了便于复制到实际 char[N] 对象,组成 f 可以像访问 字符[N] . 我相信这一部分没有争议。

    来自的字节 字符[N] 代表一个 uint32_t 值可以复制到 uint32英寸 对象。我相信,这一部分也没有争议。

    我认为,同样无可争议的是,例如, fwrite 可能在程序的一次运行中写入了字节,并且 fread 可能在另一次运行中读取了它们,甚至完全读取了另一个程序。

    因为最后一部分,我相信字节从哪里来并不重要,只要它们构成了一些 uint32英寸 对象。你 能够 已经循环通过所有 float 值,使用 memcmp 在你得到你想要的陈述之前,你知道的每一个都与 uint32英寸 你解释为的价值。你 能够 甚至在另一个程序中也做过,这是编译器从未见过的程序。那应该是有效的。

    如果从实现的角度来看,您的代码与明确有效的代码是不可区分的,那么您的代码必须被视为有效的。

        2
  •  18
  •   Toby Speight    6 年前

    我的第一个例子(以及相关的答案)是否定义得很好?

    行为不是未定义的(除非目标类型具有陷阱表示 阿西 它不是由源类型共享的,但整数的结果值是由实现定义的。标准并不能保证浮点数是如何表示的,所以没有办法用可移植的方式从整数中提取尾数等——也就是说,现在使用系统限制自己使用IEEE754并不能限制你。

    便携性问题:

    • IEEE 754不是由C++保证的
    • float的字节结尾不能保证与integer结尾匹配。
    • 带陷阱表示的系统 阿西 )

    你可以用 std::numeric_limits::is_iec559 以验证您对表示的假设是否正确。

    阿西 不过,看起来 uint32_t 不能有陷阱(见评论),所以你不必担心。通过使用 uint32英寸 您已经排除了对深奥系统的可移植性——标准一致性系统不需要定义该别名。

        3
  •  13
  •   Hatted Rooster    6 年前

    您的示例定义良好,不会破坏严格的别名。 std::memcpy 清楚地说明:

    副本 count 从SRC指向对象的对象到对象的字节数 由目标指向。 这两个对象都被重新解释为 unsigned char .

    该标准允许通过 (signed/unsigned) char* std::byte 因此,您的示例不显示ub。但是,如果得到的整数是任何值,则是另一个问题。


    use i to extract f's sign, exponent & significand

    然而,标准并不能保证这是 float 是否定义了实现(在IEEE754的情况下,它将起作用)。