c++ - 转换为仅由 char[] 组成的结构并从该数组读取是否定义良好?

标签 c++ strict-aliasing type-punning

reinterpret_cast有很多用途可以编译,但是是 UB。它仅在少数情况下得到明确定义。目前对我来说重要的,正在转换到:

  • 在其元素或非静态数据成员中包含上述类型之一的聚合或 union 类型(递归地包括子聚合或包含的 union 的元素或非静态数据成员)
  • char 或 unsigned char 类型

假设我有一个缓冲区,其中包含我想要解析的二进制结构。我用struct其中仅包含 char a[100]并使用方法来提取 uint32_t位于 a[3] 。将缓冲区转换为结构,然后以这种方式访问​​结构的数组是否定义良好?

如果是,我想那是因为上面的两条规则;然而,我也有一半的期望这些规则不适用于 char[] ,或者我可能会遇到某种对齐问题,因为它是一个数组而不仅仅是 char*

一个小代码示例,使用例更加清晰:

struct Overlay {
    char a[100];

    uint32_t parseSomeField() const {
        return my_char_to_uint32_t_function(&a[3]);
    }
};

int main() {
    std::vector<char> buffer(1024);
    // fill buffer
    auto overlay = reinterpret_cast<const Overlay*>(&buffer[0]);
    std::cout << overlay->parseSomeField() << std::endl;
}

我假设替换 char a[100]简单地 char *a肯定没问题,但是给Overlay我想要解析的结构的大小,它也允许我执行以下操作:

Overlay overlay;
// fill overlay by writing into it directly
std::cout << overlay.parseSomeField() << std::endl;

这节省了一些代码行。


编辑:

感谢您的回答和评论,我很清楚 reinterpret_cast 的使用是UB。以下内容支持使用现有缓冲区直接复制到结构中。你可以做sizeof以及,这很好。另外,这应该得到很好的定义:

struct VersatileOverlay {
    char a[100];

    static uint32_t parseSomeField(const char *a) {
        return some_char_to_uint32_t_function(a + 3);
    }

    uint32_t parseSomeField() const {
        return parseSomeField(&a[0]);
    }
};

int main() {
    std::vector<char> buffer(1024);
    // fill buffer
    std::cout << VersatileOverlay::parseSomeField(&buffer[0]) << std::endl;

    VersatileOverlay vo;
    memcpy(&vo, /*source ptr*/, sizeof(VersatileOverlay));
    std::cout << vo.parseSomeField() << std::endl;
}

parseSomeField()它的 sibling 将简单地调用它们的静态对应项,并将内部缓冲区传递给它们。

最佳答案

Is casting to a struct that consists solely of a char[] and reading from that array well defined?

根据以下规则,它没有明确定义:

[basic.lval]

If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
  • a char, unsigned char, or std::byte type.

当底层对象是char动态数组时,这些列出的类型都不是Overlay


这里看起来是一个明智的解决方案,实际上只是一个免费(或静态成员)函数:

std::uint32_t
parseSomeField(const char* a) const {
    return my_char_to_uint32_t_function(a + 3);
}

您可以使用它来解析 vector :

parseSomeField(buffer->data());

或者,如果您确实有一个类似于 Overlay 的类:

parseSomeField(overlay.a);

关于c++ - 转换为仅由 char[] 组成的结构并从该数组读取是否定义良好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57066877/

相关文章:

c++ - 我该如何进行这种类型转换?

c++ - 在 C++ 中,如何访问多维数组中的值?

c++ - 打破 int 和 float 之间严格别名的实际后果

c - 重新解释适当对齐的指向具有声明类型的对象的指针

c++ - union 'punning' 结构 w/ "common initial sequence": Why does C (99+), 但不是 C++,规定 'visible declaration of the union type' ?

c++ - 可以按序号挂接 IAT 函数吗?

c++ - 是否使用 auto 声明变量并使用原始文字定义的行为进行初始化?

c - gcc、严格别名和恐怖故事

c++ - reinterpret_cast<char *> 是 reinterpret_cast 的唯一有效用法吗?

c++ - 什么是双关语,它的目的是什么?