32

For example I have this struct

struct  A {
    float x;
    float y;
    float z;
};

Can I do this? A a; float* array = (float*)&a; And use a as float array?

11
  • 11
    No, there might be a padding between variables. Commented Aug 26, 2017 at 17:53
  • 4
    I mean place a static_assert(sizeof (A) == sizeof (float) * 3, "blah") somewhere. Then, if your compiler decides to add padding, you will get a nice error. But I'm 99% sure it won't do so. Commented Aug 26, 2017 at 18:21
  • 4
    @HolyBlackCat - It may cause more errors then you'd think. On a 64 bit machine, with 4 byte floats, the compiler may very well add padding at the end of the structure to align the structure on a machine word boundary. Commented Aug 26, 2017 at 18:37
  • 1
    @StoryTeller I think there are no problems if compiler adds padding only at the end, are they? Commented Aug 26, 2017 at 18:39
  • 1
    @SemyonTikhonenko - It is a problem, because that static assertion will fire. Even though all the "ad-hoc" guys will be correct in saying your hack will work. Commented Aug 26, 2017 at 18:39

5 Answers 5

28

No, typecasting a struct to an array will not work. Compilers are allowed to add padding between members.

Arrays have no padding between members.

Note: there is nothing stopping you from casting, however, using value after cast results in undefined behavior.

Sign up to request clarification or add additional context in comments.

9 Comments

In a general sense you may be right, but from a practical point of view this will work on virtually all compilers under windows, linux and embedded platforms like PowerPC and arm I work with. I would certainly like to see an example where it fails :)
@Sil Typecasting the struct to an array may work. To Thomas's point though, I wouldn't call doing that safe and the OP did in part ask if it can safely be done.
Incidentally, I wish Thomas had said that typecasting this way wasn't safe instead of saying that it wouldn't work.
@Louis he asked if he can do that.... from one point of view he can, but in the general/strictly/formal sense, no he can't. the same way you can ask "is 1/x defined on the real numbers?" well, most of the time it is (my answer). But a strict mathematical answer would be no, it isn't (your answer) just because of the 0 value in the see of all other values :P Also in our case you can't find the 0, can you? (unless you do some pragma pack 8, then it will fail :P)
@Sil It's called "undefined behaviour". The compiler can go postal on you, with strong support by Mr. Murphy. But only if you turn on the optimiser for a production version of your code. So it will work fine on your development machine, and take down production. Or destroy all data of your most important customer.
|
27

In a practical sense, yes you can do that and it will work in all the mostly used architectures and compilers.

See "Typical alignment of C structs on x86" section on Wikipedia.

More details:

  • floats are 4 bytes and no padding will be inserted (in practically all cases).

  • also most compilers have the option to specify the packing of structures and you can enforce that no padding is inserted( i.e. #pragma pack from visual studio )

  • arrays are guarantied to be contiguous in memory.

Can you guarantee that it will work in all CPUs in the world with all the compilers? No.. but I would definitely like to see a platform where this fails :)

EDIT: adding static_assert(sizeof(A) == 3*sizeof(float)) will make this code not compile if there are padding bytes. So then you'll be sure it works when it compiles.

6 Comments

I like this answer because it prefers pragmatism over pedantic application of the spec.
I could imagine one of the more obscure 64-bit platforms doing strange things for alignment reasons, and you can have issues if any of the data is uninitialized (yes, even if you turn off sNaNs globally). You certainly cannot assume that "typical alignment on x86" is going to describe any architecture other than x86, of course.
+1. It might be undefined behavior, but you will find a lot of old C code written that way. Microsoft even includes example code like that in the VC++ documentation.
@Kevin nice article (i'm not familiar with ia64) but it doesn't apply here. Adding a static_assert(sizeof(A) == 3*sizeof(float)) will make the code portable if it compiles :)
More clear version of the static_assert: static_assert(sizeof(A) == sizeof(float[3]))
|
19

This is a strict aliasing violation, plain and simple. Any access with that pointer, to any element but the first, is undefined behavior in your case. And in more complex cases it's just plain undefined behavior regardless of which element you access.

If you need an array, use an array. std::array also has an overload for for std::get, so you can use that to name each individual array member:

using A = std::array<float, 3>;

enum AElement { X, Y, Z };

int main() {
  A a;
  get<X>(a) = 3.0f; // sets X;

  float* array = a.data(); // perfectly well defined
}

7 Comments

I don't think this is a strict aliasing violation since both the pointer and the struct elements are floats
@Jeff - This isn't a matter of opinion. The C++ standard mentions that only certain type may alias each other, and all other aliasing is UB unless otherwise specified. The case of the first member variable here is only one exception, because it's first byte is coincidentally the first byte of the structure (in this case).
@Jeff - Erm, no. Accessing the first member, is only allowed because this is a standard layout type. That clause doesn't mean one may treat structures as arrays, even when there is no padding. Of relevance here is the description of reinterpret_cast, which is the cast going on here.
@PasserBy - Hence my point about the first member being okay due to the standard layout type guarantees. But that's as far as the standard itself will take you.
@PasserBy: The standard lets you take a pointer to that first member, and further allows you to treat any pointer like a one-element array. But OP doesn't want a one-element array, they want a three-element array. Nothing in the standard allows for that, because now we're not talking about the first member any more.
|
7

For g++ you can use attribute for your struct, like this:

struct  A {
    float x;
    float y;
    float z;
}__attribute__((__packed__));

It's disable struct alignment.

Comments

4

You can do that, until the compiler starts optimising, and then things go wrong.

Accessing any but the first element of a struct by using a pointer to the first element is undefined behaviour. "Undefined behaviour" means that anything can happen. The compiler may assume that there is no undefined behaviour.

There are many consequences that the compiler can deduce from that: If the compiler knows that your float* points to the first element of the struct, then it can deduce that every index to that array equals 0 (because anything else is undefined behaviour). If the compiler doesn't know this, then it can deduce that the array pointer cannot point to the struct, and changing array elements can't change struct elements and vice versa.

Can you see how this will go wrong?

2 Comments

One of the things that allowed C to win the "language wars" in the 1980s and 1990s is that implementations supported useful semantics which weren't available in other languages. Unfortunately, because implementations where such semantics would be useful supported them in the obvious way even though the Standard didn't require it, the authors of the Standard saw no need to define an "official" way to demand such semantics. I think your post might benefit from a more specific and realistic example, however, such as assuming that it can keep the value of one structure member cached...
...in a register across an access to the same storage using an address computed from a different member. C and C++ have somewhat different rules, but since C++ was intended to be a superset of C, the same historical issues remain relevant in C++.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.