1

From reading various other Stack Overflow questions, it seems that double values may sometimes only be 4-byte aligned instead of 8 (hence the existence of GCC's -malign-double). Does this apply to the heap as well as the stack?

It seems to me that if double values on the heap were not always 8-byte aligned, then it would be impossible to perform pointer arithmetic with double* values since incrementing a double* advances it by 8 bytes, but the difference between two arbitrary double* values might not be a multiple of 8 bytes.

If you're curious about the use case I'm interested in, I have something like:

#include <iostream>

#include <iostream>
#include <vector>

struct EmptyBase1
{
};

struct EmptyBase2
{
};

struct Point : public EmptyBase1, public EmptyBase2
{
    double x;
    double y;
    double z;
};

int main() {
    std::vector<Point> points(10);
    int stride = &points[1].x - &points[0].x;
    std::cout << stride << std::endl;
    return 0;
}

Visual Studio does not fully apply the empty base class optimization in this case (see https://stackoverflow.com/a/12714226/953078), so it is not guaranteed that sizeof(Point) == 3 * sizeof(double). Is stride guaranteed to always give a valid memory offset between &points[1].x and &points[2].x, &points[5].y and &points[4].y etc.?

Community
  • 1
  • 1
  • Even if it's not aligned on an 8-byte boundary, wouldn't it still be contiguous? Contiguous memory should guarantee valid pointer arithmetic. – pzed Jul 29 '14 at 18:19
  • 4
    Subtracting two pointers is only well-defined if both pointers point to elements of the same array (or one past the end). Pointers to subobjects of array elements don't count, even if the memory is "contiguous". Though, who knows, it might work on VS, which is probably good enough for your purposes. – Brian Bi Jul 29 '14 at 18:21
  • `new T` is naturally aligned but you may have misaligned type (with packing directive or `reinterpret_cast`). – Jarod42 Jul 29 '14 at 18:21
  • @Brian: I didn't know that restriction on pointer subtraction, that pretty much just answers the question for me right there. Is that in the standard somewhere? I only mentioned VS because that's the only one that has issues with multiple base classes; I do need my code to be portable across multiple compilers and platforms. – Ian Mackenzie Jul 29 '14 at 19:24
  • @pzed: Imagine if `sizeof(Point)` was 28 (e.g., Visual Studio added an extra byte due to multiple base classes for whatever reason). Then what should `&points[1].x - &points[0].x` be? The offset in memory between those two doubles would be 28 bytes, which is 3 and a half times the size of a `double`, so...3.5? That's clearly silly, so I was wondering if padding would always be added to make sure the doubles were always aligned to 8-byte boundaries, but it sounds like the answer is instead that the pointer subtraction is invalid. – Ian Mackenzie Jul 29 '14 at 19:44
  • 1
    @IanMackenzie It's [expr.add]/6. – Brian Bi Jul 29 '14 at 21:33

1 Answers1

1

From 3.7.3.1 we learn about allocation functions The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type and then used to access the object or array in the storage allocated. All this says is that it must be appropriately aligned for the hardware. So you could conceive of hardware on which there are no alignment constraints and a double could be byte aligned out of new.

Your subtraction is not legal as the two doubles are not part of the same array/contiguous chunk of doubles. It should be perfectly legal however to reinterpret_cast the addresses to char* and then subtract those pointers. But what real problem are you trying to solve?

EDIT: As pointed out in a comment I'm now actually fairly sure even converting to char* and doing the subtraction is illegal.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I may indeed have to do the `char*` subtraction as you suggest - I was hoping to avoid that. I currently have a `MatrixView` class used to allow matrix operations on arbitrary blocks of `double` values in memory. It is currently implemented as a pointer to the first item, a row count, a column count, and a column stride. The column stride is currently stored as an integer pointer offset. I was hoping to be able to construct a valid `MatrixView` pointing to the component data in a `std::vector`, but it sounds like I will have to change the column stride to be in bytes instead. – Ian Mackenzie Jul 29 '14 at 19:32
  • I don't think the conversion to `char*` is magically going to eliminate the requirement that pointer arithmetic is valid only inside arrays. The classical example is the 8086 where an array is limited to one segment and pointer arithmetic doesn't account for segment boundaries. – MSalters Jul 30 '14 at 07:21
  • Perhaps - but at least in my case all the doubles _are_ within the same array, it just happens to be an array of `Point` objects instead of an array of doubles. In that case can you see any way that `char*` based pointer arithmetic between the various double components would not be valid? – Ian Mackenzie Jul 31 '14 at 19:08