当前位置: 首页 > 知识库问答 >
问题:

严格的混淆现象和二进制I/O

湛光明
2023-03-14

让我们考虑以下(简化)代码来读取二进制文件的内容:

struct Header
{
    char signature[8];
    uint32_t version;
    uint32_t numberOfSomeChunks;
    uint32_t numberOfSomeOtherChunks;
};

void readFile(std::istream& stream)
{
    // find total size of the file, in bytes:
    stream.seekg(0, std::ios::end);
    const std::size_t totalSize = stream.tellg();

    // allocate enough memory and read entire file
    std::unique_ptr<std::byte[]> fileBuf = std::make_unique<std::byte[]>(totalSize);
    stream.seekg(0);
    stream.read(reinterpret_cast<char*>(fileBuf.get()), totalSize);

    // get the header and do something with it:
    const Header* hdr = reinterpret_cast<const Header*>(fileBuf.get());

    if(hdr->version != expectedVersion) // <- Potential UB?
    {
        // report the error
    }

    // and so on...
}

在我看来,下面一行:

if(hdr->version != expectedVersion) // <- Potential UB?

包含未定义的行为:我们正在读取uint32_t类型的version成员,它覆盖在std::byte对象的数组之上,并且编译器可以自由地假设uint32_t对象不别名。

问题是:我的解释正确吗?如果是,可以做什么来修复此代码?如果没有,为什么这里没有UB?

注1:我理解严格别名规则的目的(允许编译器避免不必要的内存加载)。此外,我知道在这种情况下,使用<code>std::memcpy</code>是一个安全的解决方案,但使用<code>std::memcpi</code>意味着我们必须进行额外的内存分配(在堆栈上,或者在堆上,如果对象的大小未知)。

共有2个答案

卫仲卿
2023-03-14

如何修复这段代码?

等待,直到http://wg21.link/P0593或者类似的允许在<code>char</code>/<code>unsigned char</code>/<code>std::byte</code>的数组中创建隐式对象的方法。

闻人河
2023-03-14

问题是:我的解释正确吗?

如果是,可以做些什么来修复此代码?

您已经知道 memcpy 是一种解决方案。但是,您可以通过直接读取标头对象来跳过 memcpy 和额外的内存分配:

Header h;
stream.read(reinterpret_cast<char*>(&h), sizeof h);

注意,以这种方式读取二进制文件意味着文件的整数表示必须与CPU的表示相匹配。这意味着该文件不能移植到具有不同CPU体系结构的系统。

 类似资料:
  • 这是一个典型的严格混叠违规示例: 但假设我们添加第二: 这段代码正确吗(不调用未定义的行为)? 标准[expr.reinterpret.cast]如下: 注意:将“指针到<code>T1</code>”类型的prvalue转换为“指针到 和<code>T2是对象类型,并且<code>T2的对齐要求不比<code>T 1 我们使用 类型的原始指针值来访问 类型。 当优化打开时,GCC和Clang都会

  • 我不知道为什么下面的代码运行得很好,没有< code>gcc错误(< code >-f strict-aliasing-Wstrict-aliasing = 1 )。 如果我遵循严格的别名规则: n1570,§6.5表达式 对象的存储值只能由具有以下类型之一的左值表达式访问: -与对象的有效类型兼容的类型, — 与对象的有效类型兼容的类型的限定版本, -与对象的有效类型对应的有符号或无符号类型的类

  • 下面的getValue()成员函数是否违反了c严格别名规则? 根据该标准,我认为setValue()违反了严格的混淆现象,因为Double既不是聚合类型,也不是IEEE754_64的基类。 getValue()呢?当数据成员采用位字段形式时,它是否是一种未定义的行为,如下例所示? 我正在一个大型项目中使用类似的代码。GCC-O2和-O3输出错误值。如果我添加-fno严格的别名,问题就不存在了。此外

  • 在这些注释中,user @Deduplicator坚持认为,如果别名指针或别名指针是指向字符的指针类型(限定或非限定,有符号或无符号< code>char *),则严格的别名规则允许通过不兼容的类型进行访问。所以,他的观点基本上是 和 符合并具有定义的行为。 然而,在我看来,只有第一种形式是有效的,即当别名指针是指向char的指针时;然而,在另一个方向上却不能,即当别名指针指向不兼容的类型(而不是

  • 我和一位同事正试图实现一个简单的多态类层次结构。我们正在开发嵌入式系统,并且仅限于使用C编译器。我们有一个基本的设计理念,可以在没有警告的情况下编译(-Wall -Wextra -fstrict-aliasing -pedantic),并且在gcc 4.8.1下运行良好。 然而,我们有点担心别名问题,因为我们并不完全了解这何时会成为一个问题。 为了演示,我们编写了一个带有“接口”IHello和两个

  • 当违反严格的混淆现象规则时,我试图掌握未定义的行为。为了理解它,我读了很多关于SO的文章。然而,还有一个问题:我并不真正理解两种类型的非法别名。cpp-参考指出: 类型混淆现象 每当尝试通过AliasedType类型的glvalue读取或修改DynamicType类型对象的存储值时,除非以下之一为真,否则行为未定义: AliasedType和DynamicType类似 AliasedType是Dy