使用指针访问类的成员 我在C++中看到了一个现代OpenGL的例子,其中一个结构用于保存顶点坐标和3坐标颜色矢量< /P>的数据。 struct VertexData{ float x, y; float r, g, b; } std::vector<VertexData> myData = { {.x = 0, .y = 0, .r = 0, .g = 0, .b = 0}, {.x = 0, .y = 0, .r = 0, .g = 0, .b = 0} } float *ptr = (float*) &myData[0];

使用指针访问类的成员 我在C++中看到了一个现代OpenGL的例子,其中一个结构用于保存顶点坐标和3坐标颜色矢量< /P>的数据。 struct VertexData{ float x, y; float r, g, b; } std::vector<VertexData> myData = { {.x = 0, .y = 0, .r = 0, .g = 0, .b = 0}, {.x = 0, .y = 0, .r = 0, .g = 0, .b = 0} } float *ptr = (float*) &myData[0];,c++,C++,使用ptr变量访问此向量中的所有元素是否仍然安全(也是一个好主意…)?我知道,在存在静态变量、不同类型、继承等的情况下,这可能会很棘手,但在这种情况下,如果只有普通变量和函数作为成员,使用指针会是一种可靠的方法吗 >根据C++标准,该技术具有未定义行为>。编译器可以在成员之间自由添加填充。因此,根据标准的原则,这是一个坏主意 不过 我知道所有编译器(我在过去的15年中和8个不同的C++编译器一起工作过),在这个例子中不要添加填充。为什么会这样?所以,如果你敢进入未定义的行为领域,你可以使用这种技

使用
ptr
变量访问此向量中的所有元素是否仍然安全(也是一个好主意…)?我知道,在存在静态变量、不同类型、继承等的情况下,这可能会很棘手,但在这种情况下,如果只有普通变量和函数作为成员,使用指针会是一种可靠的方法吗

>根据C++标准,该技术具有<强>未定义行为>。编译器可以在成员之间自由添加填充。因此,根据标准的原则,这是一个坏主意

不过

我知道所有编译器(我在过去的15年中和8个不同的C++编译器一起工作过),在这个例子中不要添加填充。为什么会这样?所以,如果你敢进入未定义的行为领域,你可以使用这种技术。即使在复杂的情况下。继承、虚拟函数等。我所知道的所有编译器都执行逻辑操作,并将浮点成员彼此相邻地放入内存中,在这种情况下,它们的优化器不会失败。注意:该标准允许编译器在访问说明符之间对成员进行重新排序,因此如果您想安全起见(您已经离开了),请将所有成员放在同一个访问说明符下(尽管如此,我还没有看到编译器,它实际上在访问说明符之间对成员进行重新排序)

所以可靠吗?根据标准:。根据目前的做法:以我的经验,是的。我还没有看到这样的代码失败的编译器。我鼓励任何了解这种编译器的人不要沉默,并将其作为答案添加


注意:这是当前状态。它随时可能坏。只有当你知道自己在做什么,并且意识到可能的后果时,我才建议你使用这种技术。不要忘了在你的代码中注释这个事实。

正如其他人已经提到的,你的这种方法是不可靠的,因为编译器可能会选择填充你的
结构。例如,如果您在64位平台上,编译器可能会选择将
结构填充为8字节对齐

但是,使用指针访问类成员的想法仍然可行。您可以使用
sizeof
获取
结构的实际大小(以字节为单位)

在第一个示例中,它如下所示:

float *ptr = (float*) &myData[0].x;
ptr = (float*)(((char*)ptr) + sizeof(VertexData));
然后访问下一个
x
值,如下所示:

float *ptr = (float*) &myData[0].x;
ptr = (float*)(((char*)ptr) + sizeof(VertexData));
或者,如果您确定
sizeof(VertextData)
sizeof(float)
的倍数,则可以是:

ptr += sizeof(VertexData) / sizeof(float);
这有点复杂,但很可靠。此外,如果愿意,还可以将内容包装到宏中

这种方法是否实用取决于你的具体情况。我想说,在某些情况下,它可能是有用的


编辑:

然而,使用这种方法存在一些危险

要记住的最重要的一点是,如果在向量中插入一些元素,整个存储可能会重新分配,因此以前的指针将不再有效


编辑:

还有另一种方法,避免使用
sizeof
。要访问下一个
x
值,您可以使用:

ptr = (float*) (((VertexData*) ptr) + 1);

这应该编译成相同的结果。

即使是第一个版本也不安全。第二种方法也一样。是的,这种方法可以用于类,但前提是对类成员使用1字节对齐方式(对于
struct
示例也是如此),以避免成员之间的填充。但是,这在技术上是未定义的行为,因为它违反了严格的别名规则。因此,请检查您的特定编译器,以确保其工作正常expected@MalancheC++中没有“结构”。您可以使用两个不同的关键字定义类。就布局而言,您的两个示例之间没有区别,除了一个有5个浮点数,另一个有3个。@Malanche:我一直在使用一种技术,其中我放置了一个
浮点数c[3]
结构{float x,y,z}编码到一个联合中(这样我就可以使用
c[1]
y
访问一个元素)超过10年,使用许多不同的编译器。这项技术适用于所有人。根据标准,这是UB。根据实践。。。好吧,它是有效的(我可以随时中断,但这不太可能)。@geza我希望你在头文件中放一个可怕的警告,提醒你(更重要的是,任何其他使用代码的人)对数据成员的任何更改(如添加新成员)都会以令人兴奋和有趣的方式中断代码:)不,根据目前的做法,这是不可靠的。C++实现在这种情况下不插入填充并不意味着它的优化器将不识别未定义的行为,并且将未定义的代码转换成完全不同的东西。我已经使用这项技术15年了,有8种不同的编译器,它们都可以使用。可靠性来自于某些规范的保证,即行为符合规定。除非C++标准或实现文档指定代码将以期望的方式运行,否则可靠性就不存在。如果愿意,您可以进行测试,但您无法知道明天的编译器版本或源代码中的某些细微更改不会产生不同的结果。当前的做法包括源代码中的更改。你不可能知道。你只知道你测试过的情况有效(或者至少错误没有被发现)。如果没有规范的保证,您就无法知道您尚未测试的其他情况也会起作用。我手边没有,但我知道我不能依赖于没有具体说明的行为,我知道因为