offsetof 的这种使用保证是正确的吗?

Is this use of offsetof guaranteed to be correct?

我读到 offsetof 宏通常实现为:

#define offsetof(st, m) \
    ((size_t)&(((st *)0)->m))

根据维基百科,关于这是否是未定义的行为存在争议,因为它可能取消引用指针。我有一个信息缓冲区,需要发送到不同的地方,但缓冲区只占用结构的一部分。我想知道以下是否可以保证我的结构的正确大小直到缓冲区的末尾,即最后一个成员。

struct PerObjBuffer
{
    mat4 matrix;
    mat4 otherMatrix;
    vec4 colour;
    int sampleType = 0;

    size_t getBufferSize() { return offsetof(PerObjBuffer, sampleType) + sizeof(sampleType); }
    void sendToGPU() { memcpy(destination, this, getBufferSize()); }
    String shaderStringCode =

        R"(layout(binding = 2, std140) uniform perObjectBuffer
    {
        mat4 WVPMatrix;
        mat4 worldMatrix;
        vec4 perObjColour;
        int texSampleFormat;
        };
    )";

}

如果使用 offsetof 不是一个好主意,那么做我想做的事情的最佳方法是什么?

编辑:sizeof(PerObjBuffer) - sizeof(String) 怎么样?我需要考虑填充问题吗?

该标准指定了事物的行为,而不是它们是如何实现的。实现部分留给实现者——编写编译器和库的人——他们必须按照标准指定的方式工作来实现。如果标准库的实现本身具有未定义的行为是无关紧要的——它必须与指定的编译器一起工作,所以特定的编译器将以它具有实现者想要的行为的方式解释它。未定义的行为意味着标准对代码的行为没有规定任何要求。您的编译器文档可能会指定额外的要求,指定根据标准未定义的代码行为 - 因此代码可能未被标准定义,并且在 特定的编译器 上是完全合理的required/written 以这种方式解释它。

C++ 语言在使用适当的 #include 时使用 C 语言中的宏。 C 标准说 C99 7.17p3:

 The macros are [...] and

        offsetof(type, member-designator)

which expands to an integer constant expression that has type size_t, the value of which is the offset in bytes, to the structure member (designated by member-designator), from the beginning of its structure (designated by type). The type and member designator shall be such that given

        static type t;

then the expression &(t.member-designator) evaluates to an address constant. (If the specified member is a bit-field, the behavior is undefined.)

作为该语言的用户,您不关心它是如何实现的。如果您使用符合标准的编译器,无论它在幕后做什么,它都应该导致标准指定的行为。您对 offsetof(PerObjBuffer, sampleType) 的使用是有效的 - static PerObjBuffer t; 然后 &(t.member-sampleType) 计算为地址常量 - 因此 offsetof(PerObjBuffer, sampleType) 计算为整数常量表达式。你喜欢不关心编译器如何得出结果——它可以使用黑魔法来做到这一点——重要的是编译器做到这一点,结果代表以字节为单位的偏移量。 (我认为著名的例子是 memcpy - 以符合标准的方式实现 memcpy 是不可能的,但功能......存在)。

不过,我担心您代码的其他部分会非常无效。 memcpy 很可能会导致未定义的行为 - 您将在成员之间复制填充,并且您似乎想将其发送到某个硬件位置。无论如何,我建议对 C++ 对象的生命周期和表示、结构内的填充、对象类型(即 POD/标准 layout/trivial)以及如何使用它们(即.什么时候可以用 mem* functions/placement new/std::launder).