6
votes

If new[] expression is used to create an array of objects having destructors, the objects in the array may not be properly alligned

#include <stdint.h>
#include <stdio.h>

#pragma pack(8)
struct A{
  int64_t i;
  char dummy;
  ~A(){}
};

int main(){
  A* pa= new A[2];
  printf("sizeof(A)= %d, pointer= %p", sizeof(A), pa);
}

(I build 32-bit target with VC++ 2010 express)

The output (on my computer) is:

 sizeof(A)= 16 pointer= 00344f4c

(sizeof(A)= 16 shows that compiler undrstand the alignment requirements for A and the struct is padded with 7 bytes [ edited: __alignof(A) also returns 8 ])

I understand why it happens: new[] needs to store array length and it use for this purpose first 4 bytes of allocated memory, then it allocates the array itself without proper padding.

From a practical viewpoint such a behaviour is definitely poor, but is it standard compliant or not?

8
That's unbelievable. Even our in-house custom implementation of operator new respects alignment. - sharptooth
isn't this the result of your #pragma pack(8) still in effect in your main ? - Adrien Plisson
@sharptooth custom operator new simply allocates memory which is used by compiler to generate code for new expression. Allocated memory is always properly aligned, but objects are placed by compiler to wrong place inside this memory - user396672
@Adrien Plisson sizof(A)==16 shows that pragma pack take effect - user396672
Th standard grantees alignment of the returned result. I doubt that Visual Studio is broken. So your hypothesis must be incorrect. I don't understand why you think it is not aligned. - Martin York

8 Answers

8
votes

You should use __declspec for this purpose. Your code generated misaligned objects on my computer too (using VS2010) but when I changed to __declspec(align(8)), the pointers were correctly aligned.

I believe that pragma pack only changes the size of the struct and doesn't make any guarantees about it's location.

3
votes

The standard guarantees that the memory is correctly aligned.

I doubt that Visual Studio got something so basic wrong.

Currently I don't follow why you think it is not aligned?

Are you trying to suggest it should be aligned to 16 byte boundaries?
That is not required by the standard. It may only need to align to 4 byte boundaries.

3.11 Alignment [basic.align]

2 A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to alignof(std::max_align_t) (18.2).

Thus the following code should print out the maximum required alignment of the implementation:

#include <iostream>
#include <cstddef>
#include <cstdalign>

int main()
{
        std::cout << alignof(std::max_align_t) << "\n";
}
2
votes

Interesting. My first question would be: what happens without the #pragma pack? What is the native alignement for this struct. The operator new has no means of knowing what options you might have set with the pragma, so its presence or absense has no effect here. And formally, there's no violation of the standard, since Intel doesn't require any alignment for anything (but maintaining proper alignment sure helps performance). All in all, it doesn't seem like a desirable "feature", but it's a QoI issue, and not a standard conformance one.

1
votes

Well to answer your question literally I don't think the standard says anything about #pragma pack at all. So I would say that your code has undefined behavour according to the standard.

0
votes

IIRC the default packing is on byte alignment, use /Zpn to set to another packing, then you get your padding. (or #pragma pack(n) )

EDIT: come to think about I think the default packing even varies between VS versions

0
votes
 printf("sizeof(A)= %d, pointer= %08x", sizeof(A), pa);

As a token of caution, use %p when you want to print memory address:

printf("sizeof(A)= %d, pointer= %p", sizeof(A), pa);

%x expects unsigned int whose size may be different from the size of a pointer.

0
votes

If struct A just contains plain old data, you can use aligned_malloc (see Microsoft's documentation). If it needs to be constructed, you can combine this with the placement new operator. Something like this (I don't have Microsoft VC++, so it might not compile):

void* buf = _aligned_malloc (2 * sizeof (struct A), 8) ;
A* pa = new (buf) A[2];

Deleting this is a pain: you have to call pa[i].~A() explicitly for each array element, and then _aligned_free (buf).

0
votes

operator new() which keyword new calls under the hood, has no knowledge of type's alignment. The only alignment requirement is that operator new() has to allocate memory suitably aligned for a built-in type with the largest alignment requirement, which is often 8 on a 32-bit platform (the alignment requirement of double).

Having said that, the example you posted allocates memory aligned on 8-byte boundary as it should.