12

Let's say I have an array of doubles, is it safe to reinterpret_cast it to an array of structs each containing 4 doubles, with regards to alignment?

Consider this example code:

double *edges = ...; // Each edge is defined by 4 doubles in the array.

struct Edge { double vals[4]; };
Edge *asStruct = reinterpret_cast<Edge*>(edges);

std::sort(asStruct, asStruct + edgesCount, EdgeLengthComparator());

Is it guaranteed by the standard that sizeof(Edge) == sizeof(double)*4 always be true and if not, is it true in practice for Intel and ARM?

Edit: I found there is an __attribute__((__packed__)) for GCC, would that guarantee it for GCC at least and does MSVC have a similar option?

sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • 1
    Instead of this low level non-portable hack, I prefer to copy all the data to a well-written C++ construct before doing any computation. –  Dec 23 '14 at 07:56
  • 1
    @NickyC Performance, performance, performance :) – sashoalm Dec 23 '14 at 07:59
  • I would just use pointer inside `Edge struct`. `struct Edge { double * vals; }`. – mip Dec 23 '14 at 08:00
  • 2
    If I want performance, I prefer replacing the C style code with a well-written C++ construct in the first place. –  Dec 23 '14 at 08:03
  • @NickyC Well, the code that outputs the array is not written by me. And it's big and complicated. Trust me on this, I've thought about the other solutions, and all of them have drawbacks. – sashoalm Dec 23 '14 at 08:10
  • 2
    @doc: How would that help? Even ignoring the cost of creating your extra array of pointers, you won't end up sorting the array of doubles as required. – Mike Seymour Dec 23 '14 at 08:17
  • @MikeSeymour I would write iterator for `struct Edge` to jump block by block (4 doubles if I understand). `reinterpret_cast` is totally bad idea. – mip Dec 23 '14 at 08:22
  • @doc Wouldn't std::sort still swap only the first double? – sashoalm Dec 23 '14 at 08:26
  • 1
    @sashoalm let iterator point to your own type. Define copy assignment for that type, so that it swaps 4 doubles. – mip Dec 23 '14 at 08:31
  • If copying and rewriting are not feasible, I prefer writing a `EdgeView` class and a `EdgeArrayView` class and a `EdgeArrayView::iterator` class, which is what @doc seems pointing out. Such wrapper should give you no overhead if well-written. –  Dec 23 '14 at 08:38
  • @NickyC exactly. I apologize for my awkward wording. – mip Dec 23 '14 at 08:41
  • @doc I've actually given a bounty on that other question, so do post it there. If your solution works (I'll test it), I'll award it to you :) – sashoalm Dec 23 '14 at 08:57
  • @sashoalm gladly, if I'll find some time during Christmas, which is always a time filled with all sorts of activities in my area. – mip Dec 23 '14 at 09:13
  • Just add a `static_assert(sizeof(Edge) == 4 *sizeof(double))` to be sure. In this way even if you find a weird architecture where this is not true you have some protection – sbabbi Dec 23 '14 at 09:13

3 Answers3

5

Is it guaranteed by the standard?

Formally, no; in practice, yes. The closest to a guarantee is C++11 9.2/20:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note ]

So the cast will correctly reinterpret the first array elements as a single structure; but I don't think there's a formal guarantee that it won't expect extra padding between structures.

If not, is it true in practice for Intel, AMD, and ARM?

At least on mainstream architectures like the ones you list, there's no need to add padding for alignment in this case, and no other reason to do so; so in practice, this should work, but without guaranteed portability.

I found there is an __attribute__((__packed__)) for GCC, would that guarantee it for GCC at least?

Yes. According to the documentation, this "specifie[s] that the minimum required memory be used to represent the type", so it will prevent any padding being added after the array member.

Does MSVC have a similar option?

It has a compiler flag /Zp1 and a pragma #pragma pack(push, 1) (with #pragma pack(pop) to restore default alignment), but they don't seem to offer the guarantee you need; they control the alignment of "each structure member after the first is stored", with no mention of the overall structure size.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
4

The C Standard does not guarantee this. In fact, it explicitly says (in 6.7.2.1):

There may be unnamed padding at the end of a structure or union

However, in practice, you can use implementation-specific features to force zero padding for the struct. If you do so, I recommend utilizing a static assertion technique to ensure sizeof(Edge) == sizeof(double)*4 when the code is being compiled.

Update (re C++):

A series of comments asked if I can verify this for C++. In the draft copy of C++11, page 220, it says the following (emphasis mine):

  1. A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note ]
Community
  • 1
  • 1
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
1

It may work, or may not, or any kind of weird things may happen, as this invokes undefined behavior. You may want to have a look at https://stackoverflow.com/a/5398498/2144471 also, for info related to struct padding.

However, what you might do in that way is to refactor your code in order to leverage the following language feature (http://en.cppreference.com/w/cpp/language/reinterpret_cast)

the resulting pointer or reference may only be accessed if one of the following is true:
...
T2 is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to cast from the first member of a struct and from an element of a union to the struct/union that contains it.

Community
  • 1
  • 1
Marco Veglio
  • 332
  • 1
  • 8