可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a struct for holding a 4D vector
struct {
float x;
float y;
float z;
float w;
} vector4f
And I'm using a library that has some functions that operate on vectors but take float pointers as their arguments.
Is it legal to call something like doSomethingWithVectors( (float *) &myVector)
?
回答1:
It might work but it is not portable, the compiler is free to align things so that one float does not neccessarily immediately follow the other.
回答2:
You can write code that would make an attempt to treat it as an array, but the language makes no guarantees about the functionality of that code. The behavior is undefined.
In C language taking a storage region occupied by a value of one type and reinterpreting it as another type is almost always illegal. There are a few exceptions from that rule (which is why I said "almost"), like you can reinterpret any object as a char array, but in general it is explicitly illegal.
Moreover, the possible dangers are not purely theoretical, and it is not just about the possible alignment differences between arrays and structs. Modern compilers might (and do) rely on the aforementioned language rule in order to perform aliasing optimizations (read about strict aliasing semantics in GCC, for one example). In short, the compler is allowed to translate code under the assumption that memory occupied by a struct
can never overlap memory occupied by an array of float
. This often leads to unexpected results when people start using tricks like in your post.
回答3:
Whoa. There are a lot of answers saying it will work. It is not guaranteed by the C standard. In response to a comment asking for a real-world example, there is this excellent post by Chris Torek from comp.lang.c
. The final quote is: The lesson here is the same as always: "If you lie to the compiler, it will get its revenge."
Definitely a must read for those who think it's OK to treat a struct
of homogeneous members as an array.
回答4:
Let's just throw all arguments about the Right Way™ to do something out the window for a minute.
Does it work to treat that struct as an array? Yes.
Will it work in all cases, across all compilers and platforms? No.
Floats tend to be 32-bit, and even on my 64-bit machine, they get aligned on 32-bit word boundaries.
#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);
}
回答5:
No, it is not legal. And almost any time you find yourself using a cast, you should suspect there is something deeply wrong with your code. I suspect that someone will shortly suggest using a union, but that will not be legal either.
Of course, legal or not, either the approach you suggest in your question or the use of a union will probably "work" - but that is not what you asked.
回答6:
If you want an array why don't you put an array inside of your struct and use that? I suppse you could add pointers if you still wanted to access it in the same way using vector4f.x vector4f.y vector4f.z
struct {
float vect[4];
float* x;
float* y;
float* z;
float* w;
} vector4f
Of course that would make your struct bigger and more complicated initialization.
回答7:
If I had this situation, this is what I would do: C++ member variable aliases?
In your case it would look like this:
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;
However, this doesn't address the question of treating a struct as an array if that's specifically what you wanted to know about.
回答8:
Yes, this will work on any compiler that follows the C99 standard. It will also probably work with compilers using earlier, less clear standards.
Here's why:
6.2.5.20 defines arrays as 'contiguously allocated' elements, while structs are 'sequentially allocated' members, and as long as all the struct members are the same type, this is really the same thing.
6.5.2.3 guarentees that if the types are used in a union visible currently, you can use things that are laid out in the same order transparently.
6.5 makes it clear that the 'effective type' for any access to an element of the array, or a field of the struct is 'float', so any two accesses may alias. An access to the struct as a whole is actually an access to each member in turn, so the same aliasing rules apply.
The 'union' requirement is most odd, but since types declared where a union is visible must be compatable with types defined identically in another compilation unit without the union, the same rules end up applying even where the union is not visible.
Edit
The key point here is the distinction between things that are undefined vs things that are implementation defined. For undefined things, the compiler might to anything, and might do different things when recompiling the same source file. For implementation defined things, you might not know exactly what it does, but it must do the same thing consistently across compilation units. So even where the spec doesn't exactly spell out how something must be done, the interaction between different things and the fact that things must be consistent across compilation units with compatable type declarations greatly restricts what the compiler can do here.