我有一个用于保存4D矢量的结构
struct {
float x;
float y;
float z;
float w;
} vector4f
我正在使用一个库,它有一些函数可以对向量进行操作,但是以浮点指针作为参数。
调用类似doSomethingWithVectors( (float *) &myVector)
的内容是否合法?
答案 0 :(得分:9)
它可能有效,但它不可移植,编译器可以自由地对齐事物,以便一个浮点数不会立即跟随另一个浮点数。
答案 1 :(得分:8)
您可以编写试图将其视为数组的代码,但该语言不保证该代码的功能。行为未定义。
在C语言中,将一个类型的值占用的存储区域重新解释为另一种类型几乎总是非法的。该规则有一些例外(这就是为什么我说“几乎”),就像你可以将任何对象重新解释为char数组一样,但一般来说它显然是非法的。
此外,可能的危险并不纯粹是理论上的,而且不仅仅是数组和结构之间可能的对齐差异。现代编译器可能(并且确实)依赖上述语言规则来执行别名优化(例如,在GCC中阅读严格别名语义)。简而言之,允许编译器在假设struct
占用的内存永远不会与float
数组占用的内存重叠的情况下翻译代码。当人们开始使用你帖子中的技巧时,这通常会导致意想不到的结果。
答案 2 :(得分:6)
哇。有很多答案说它会起作用。 C标准不保证。在回复要求提供真实示例的评论时,来自comp.lang.c
的{{3}}来自excellent post。最后的引用是:这里的教训与以往一样:“如果你撒谎到编译器,它就会报复。”
对于那些认为可以将struct
同构成员视为数组的人来说,绝对必须阅读。
答案 3 :(得分:3)
让我们抛出关于Right Way™的所有争论,在窗外做一些事情。
将该结构视为数组是否有效?是。 它是否适用于所有编译器和平台的所有情况?否。
浮点数往往是32位,即使在我的64位机器上,它们也会在32位字边界上对齐。
#include <stdio.h>
struct {
float x;
float y;
float z;
float w;
} vector4f;
float array4f[4];
int main(int argc, char **argv) {
printf("Struct: %lu\n", sizeof(vector4f)); // 16
printf(" Array: %lu\n", sizeof(array4f)); // 16
return (0);
}
答案 4 :(得分:1)
不,这不合法。几乎在你发现自己使用演员表时,你应该怀疑你的代码存在严重问题。我怀疑有人会很快建议使用工会,但这也不合法。
当然,无论合法与否,您在问题中建议的方法或使用工会的方法都可能“有效” - 但这不是您所要求的。
答案 5 :(得分:1)
如果你想要一个数组,为什么不在你的struct中放一个数组并使用它呢?如果您仍然希望使用vector4f.x vector4f.y vector4f.z
以相同方式访问它,我可以添加指针struct {
float vect[4];
float* x;
float* y;
float* z;
float* w;
} vector4f
当然,这会使你的结构更大,更复杂的初始化。
答案 6 :(得分:0)
如果我遇到这种情况,我会这样做:C++ member variable aliases?
在你的情况下,它看起来像这样:
struct {
float& x() { return values[0]; }
float& y() { return values[1]; }
float& z() { return values[2]; }
float& w() { return values[3]; }
float operator [] (unsigned i) const { return this->values_[i]; }
float& operator [] (unsigned i) { return this->values_[i]; }
operator float*() const { return this->values_; }
private:
float[4] values_;
} vector4f;
但是,这并没有解决将结构视为数组的问题,如果这是您想要了解的具体内容。
答案 7 :(得分:-1)
是的,这适用于遵循C99标准的任何编译器。它也可能适用于使用早期不太清晰的标准的编译器。
原因如下:
6.2.5.20将数组定义为“连续分配”元素,而结构体是“顺序分配”的成员,只要所有结构成员都是相同的类型,这实际上是相同的。
6.5.2.3 guarentees如果类型在目前可见的联合中使用,你可以使用透明地以相同顺序排列的东西。6.5清楚地表明,对数组元素或结构字段的任何访问的'有效类型'都是'float',因此任何两个访问都可以是别名。对整个结构的访问实际上是依次访问每个成员,因此适用相同的别名规则。
'union'的要求是最奇怪的,但是因为联合可见的声明类型必须与没有联合的另一个编译单元中相同定义的类型兼容,所以即使联合不可见,相同的规则也会适用。
修改强>
这里的关键点是未定义的事物与实现定义的事物之间的区别。对于未定义的东西,编译器可能会做任何事情,并且在重新编译相同的源文件时可能会做不同的事情。对于实现定义的东西,您可能不确切知道它的作用,但它必须在编译单元之间一致地执行相同的操作。因此,即使规范没有明确说明必须如何完成某些事情,不同事物之间的交互以及事物必须在具有可兼容类型声明的编译单元之间保持一致的事实极大地限制了编译器在此处可以做的事情。