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
).
我读到 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
).