34

Most of experienced programmer knows data alignment is important for program's performance. I have seen some programmer wrote program that allocate bigger size of buffer than they need, and use the aligned pointer as begin. I am wondering should I do that in my program, I have no idea is there any guarantee of alignment of address returned by C++'s new operation. So I wrote a little program to test

for(size_t i = 0; i < 100; ++i) {
    char *p = new char[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
    short *p = new short[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
    float *p = new float[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
system("pause");

The compiler I am using is Visual C++ Express 2008. It seems that all addresses the new operation returned are aligned. But I am not sure. So my question is: are there any guarantee? If they do have guarantee, I don't have to align myself, if not, I have to.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Fang-Pen Lin
  • 13,420
  • 15
  • 66
  • 96

6 Answers6

27

The alignment has the following guarantee from the standard (3.7.3.1/2):

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 (until the storage is explicitly deallocated by a call to a corresponding deallocation function).

EDIT: Thanks to timday for highlighting a bug in gcc/glibc where the guarantee does not hold.

EDIT 2: Ben's comment highlights an intersting edge case. The requirements on the allocation routines are for those provided by the standard only. If the application has it's own version, then there's no such guarantee on the result.

Community
  • 1
  • 1
Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • In theory. In practice, if you're using gcc+glibc and SSE types on a 32-bit system, be aware of http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15795 . – timday Feb 03 '09 at 11:44
  • @timday: This problem with SSE types was also true of recent versions of MSVC++ (e.g. .NET 2003). Haven't tested on the latest version but I suspect it's still the case. – j_random_hacker Feb 03 '09 at 13:34
  • The value returned from the `operator new[]()` helper allocation function, which that requirement concerns, is not the same pointer you get from the `new[]` operator. This answer is wrong. – Ben Voigt Dec 04 '12 at 21:33
  • 2
    @BenVoigt: Do you have more detail? Are you saying that there is a guarantee but that it's given elsewhere? The draft I have (N3337), under 5.3.4/10 it discusses requirements for the return of (unsigned) char arrays and has a comment starting: "Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment...". – Richard Corden Dec 17 '12 at 12:49
  • 1
    @RichardCorden: The pointer returned from the allocation function `operator new[]()` is well aligned. But `new T[n]` is not required to, and usually does not, return `operator new[](n * sizeof (T))`. Almost every compiler adds metadata in front to track the number of elements (so that the right number of destructors are called), and sticking a header in front obviously changes the alignment. – Ben Voigt Dec 17 '12 at 16:04
  • @RichardCorden: In fact, instead of quoting the note from 5.3.4p10, look at the whole requirement. The offset added to store the metadata is required to maintain alignment *only for arrays of type* `char` (or `unsigned char`) – Ben Voigt Dec 17 '12 at 16:08
  • 1
    @BenVoigt: IMHO, adding 5.3.4/10 highlights effort by the committee to explicitly guarantee correct alignment in all (legal) cases. The compiler can easily add metadata to the front of an array and leave a gap. On further reading, 5.3.4/5 has: `the new-expression yields a pointer to the initial element (if any) of the array`. So `new[]` is returning the element of the array - not just a memory location. It must therefore have the correct alignment etc. – Richard Corden Dec 17 '12 at 17:25
  • 2
    @Richard: The other thing is that minimal correct alignment which you get from `new[]` is not the same as optimal alignment. For performance reasons you want certain structures aligned to the cache. As such, even if you update your answer to cite the correct section of the Standard, it still doesn't address the question asked. Also, some instructions (notably SSE) *require* more stringent alignment. – Ben Voigt Dec 17 '12 at 18:04
21

This is a late answer but just to clarify the situation on Linux - on 64-bit systems memory is always 16-byte aligned:

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

The address of a block returned by malloc or realloc in the GNU system is always a multiple of eight (or sixteen on 64-bit systems).

The new operator calls malloc internally (see ./gcc/libstdc++-v3/libsupc++/new_op.cc) so this applies to new as well.

The implementation of malloc which is part of the glibc basically defines MALLOC_ALIGNMENT to be 2*sizeof(size_t) and size_t is 32bit=4byte and 64bit=8byte on a x86-32 and x86-64 system, respectively.

$ cat ./glibc-2.14/malloc/malloc.c:
...
#ifndef INTERNAL_SIZE_T
#define INTERNAL_SIZE_T size_t
#endif
...
#define SIZE_SZ                (sizeof(INTERNAL_SIZE_T))
...
#ifndef MALLOC_ALIGNMENT
#define MALLOC_ALIGNMENT       (2 * SIZE_SZ)
#endif
user1059432
  • 7,518
  • 3
  • 19
  • 16
12

C++17 changes the requirements on the new allocator, such that it is required to return a pointer whose alignment is equal to the macro __STDCPP_DEFAULT_NEW_ALIGNMENT__ (which is defined by the implementation, not by including a header).

This is important because this size can be larger than alignof(std::max_align_t). In Visual C++ for example, the maximum regular alignment is 8-byte, but the default new always returns 16-byte aligned memory.

Also, note that if you override the default new with your own allocator, you are required to abide by the __STDCPP_DEFAULT_NEW_ALIGNMENT__ as well.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    I mean, you can always call the overload where you explicitly provide the alignment. `new` does it automatically when the type has new-extended alignment. – Krystian S Aug 12 '19 at 14:08
  • "Also, note that if you override the default new with your own allocator, you are required to abide by the __STDCPP_DEFAULT_NEW_ALIGNMENT__ as well." What is the source of this? – kula85 Jan 20 '21 at 20:04
8

Incidentally the MS documentation mentions something about malloc/new returning addresses which are 16-byte aligned, but from experimentation this is not the case. I happened to need the 16-byte alignment for a project (to speed up memory copies with enhanced instruction set), in the end I resorted to writing my own allocator...

jheriko
  • 3,043
  • 1
  • 21
  • 28
5

The platform's new/new[] operator will return pointers with sufficient alignment so that it'll perform good with basic datatypes (double,float,etc.). At least any sensible C++ compiler+runtime should do that.

If you have special alignment requirements like for SSE, then it's probably a good idea use special aligned_malloc functions, or roll your own.

mfazekas
  • 5,589
  • 1
  • 34
  • 25
4

I worked on a system where they used the alignment to free up the odd bit for there own use!

They used the odd bit to implement a virtual memory system.

When a pointer had the odd bit set they used that to signify that it pointed (minus the odd bit) to the information to get the data from the database not the data itself.

I thought this a particulary nasty bit of coding which was far to clever for its own good!!

Tony

AnthonyLambert
  • 8,768
  • 4
  • 37
  • 72
  • 2
    They're called tagged pointers, and they're not at all uncommon. Lots of programming language implementations use this trick to differentiate between a pointer and a integer. – geocar Feb 03 '09 at 12:19
  • 1
    And ARM interworking uses it - where applicable, ARM mode code addresses are even, thumb mode addresses are odd. I've seen an AVL tree implementation that used the bottom two bits to store the height difference of a node's subtrees. On limited systems, you stuff flag bits wherever you can :-) – Steve Jessop Feb 03 '09 at 19:43
  • On the early versions of MAC OS (Classic) they used the top 8 bits for the memory manager. The 68000's ptrs where only a max of 24 bits while the Address registers where 32 bit. – AnthonyLambert Jan 28 '14 at 16:48