How to do c++ aligned array allocation? - c++

I'd like to modify an array allocation:
float * a = new float[n] ;
to use an aligned allocator. I was inclined to try to use placement new and posix_memalign (or the new c++11 equivalent), but see that placement new with arrays is problematic with array allocations, because the compiler may need to have additional storage for count or other metadata.
I tried:
int main()
{
float * a = new alignas(16) float[3] ;
a[2] = 0.0 ;
return a[2] ;
}
but the compiler seems to indicate that the alignas is ignored:
$ g++ -std=c++11 t.cc -Werror
t.cc: In function ‘int main()’:
t.cc:4:39: error: attribute ignored [-Werror=attributes]
float * a = new alignas(16) float[3] ;
^
t.cc:4:39: note: an attribute that appertains to a type-specifier is ignored
It looks like the proper way to use alignas is in a structure declaration declare a structure with alignas, but that will only work with a fixed size.
There is also a aligned_storage template, but I think that will also only work with fixed sizes.
Is there any standard way to do an aligned array allocation that will invoke the constructor on all the elements?

As other people said, overaligned types are not required to be supported. Check your compiler documentation before using it.
You can try to solve your problem using one of the following approaches:
1) Overallocate your array (by (desired aligment / sizeof element) - 1) and use std::align. A link to libstdc++ implementation.
2) declare a struct containing array of desired aligment / sizeof element elements and aligned by desired aligment. It should give you compact representation in memory if you use array of such structs, but you will not be able to use normal array notation or pointer arithmetics (as it (a) undefined behaviour, (b) there is a small chance that they will not be placed as you want)
3) Write your own aligned allocation function. Notice that you can add your own versions of operator new and delete.
namespace my
{
struct aligned_allocator_tag {};
aligned_allocator_tag aligned;
}
void* operator new( std::size_t count, my::aligned_allocator_tag, std::size_t aligment);
void* operator new[]( std::size_t count, my::aligned_allocator_tag, std::size_t aligment)
{
return ::operator new(count, my::aligned, aligment);
}
//Usage
foo* c = new(my::aligned, 16) foo[20];
You will need to allocate memory, reserve enough space to store original pointer (returned by malloc/whatever) or amount of bytes pointer was displaced, so subsequent delete will free corect pointer, align pointer to needed size and return it.
Here is an answer, and another one, which shows how to align memory.
Notice that both of these answers uses implementation-defined behaviour of doing bitwise arithmetic on pointers converted to integers and converting them back. The only really completely standard way would be to cast memory to char* and add difference between its value and next aligned address.
If you can use some nonstandard memory allocation functions, you can wrap them into custom operator new too.

Basically, you're stuck because, in [expr.new]:
It is implementation-defined whether over-aligned types are supported.
There is a proposal to support this better. Until then, if you want to do what you're trying to do, you'll have to use aligned_alloc instead of new.
If you stick your array in a struct:
struct S {
alignas(16) float _[3];
};
then new S will give you the right alignment for _, though not necessarily for S itself. That may suffice. If it doesn't, then you can overload operator new() and operator delete() on S itself to guarantee the correct behavior.

Native support for alignment in C++ is still dismal. You're aligning to a 4-float vector, from the look of it, so you're missing the hazard where C++14 can't do better than 16 byte alignments, but even so, it's not a feature reliably supported by all C++14 compilers. If you need portable code that uses new float[], you've lost the race right out of the gate.
I think the closest you could get, within the current standard, would be to create a vector-sized and vector-aligned data type (e.g. with std::aligned_storage) and then get in the habit of using that, rather than arrays of individual floats, for your vector math. If you need variable-length vectors, then you'd have to round up to the nearest 4 floats.
(Sorry, it's not the answer you wanted, but I think it's the answer you need in order to move forward.)

Related

What is the correct usage/syntax for the c++17 alignas() specifier for dynamically allocated arrays of fundamental types?

This must be a repeat question, but I have not found it after searching for 2 days ...
I'm using MSVC with /std:c17 /std:c++17 and trying to get alignas(64) to work with arrays of doubles. The syntax in the code below is the only one I have found that compiles, but it's not aligning ... typically, the array is unaligned about 75% of the time. I know there are many ways to do this with more complicated syntax, but isn't there a way that "just works" with alignas(), as it would for a structure or class?
double* AR;
int count=0, asize=10;
for (int i = 0; i < 1000; i++)
{
AR = new double alignas(64)[asize];
if (((uintptr_t)AR & 63) != 0) count++;
//if (((uintptr_t)AR % 64) != 0) count++;
delete[] AR;
}
While C++17 does have the means for operator new to be given an alignment for the memory it allocates, there is no mechanism in C++ to specify the alignment for memory allocated by a new expression outside of the alignment of the type being allocated. That is, if you perform a new T or new T[], the alignment of the allocated pointer will be alignof(T). C++17 added alignment-based operator new allocators, which allows them to support over-aligned types.
This is fine if you have control over T and can specify its alignment at definition time via alignas. But if you're using someone else's type or a fundamental type like double, you can't change the alignment of those types. So there's no way to directly use a new expression to allocate such memory.
You will have to use ::operator new directly to allocate sufficient memory at the desired alignment, and then use placement-new to actually create the objects there. Of course, placement-new on arrays has a number of issues.
I suspect this compiles only because alignas is considered an attribute and it is grammatically legal to shove an attribute before the [] in a new expression. It isn't intended to actually work, as there is no statement in the section on new expressions that allows it to get the alignment of the allocation from anything other than alignof(T).

treating memory returned by operator new(sizeof(T) * N) as an array

In C one can allocate dynamic arrays using malloc(sizeof(T) * N) and then use pointer arithmetic to get elements at i offset in this dynamic array.
In C++ one can do similar using operator new() in the same way as malloc() and then placement new (for an example one can see solution for item 13 in a book "Exceptional C++: 47 engineering puzzles, programming problems, and solutions" by Herb Sutter). If you don't have one, the summary of the solution for this question would be:
T* storage = operator new(sizeof(T)*size);
// insert element
T* p = storage + i;
new (p) T(element);
// get element
T* element = storage[i];
For me this looked legit since I'm asking for a chunk of memory with enough memory to hold N aligned elements of size = sizeof(T). Since sizeof(T) should return a size of element which is aligned, and they are laid one after another in a chunk of memory, using pointer arithmetic is OK here.
However I was then pointed to links like: http://eel.is/c++draft/expr.add#4 or http://eel.is/c++draft/intro.object#def:object and claiming that in C++ operator new() does not return an array object, so pointer arithmetic over what it has returned and using it as an array is undefined behavior as opposed to ANSI C.
I'm not this good at such low level stuff and I'm really trying to understand by reading this: https://www.ibm.com/developerworks/library/pa-dalign/ or this: http://jrruethe.github.io/blog/2015/08/23/placement-new/ but I still fail to understand if Sutter was just plain wrong?
I do understand that alignas make sense in constructions such as:
alignas(double) char array[sizeof(double)];
(c) http://georgeflanagin.com/alignas.php
If array appears to be not in a boundary of double (perhaps following char in a structure ran at 2-byte reading processor).
But this is different - I've requested memory from the heap/free storage especially requested operator new to return memory which will hold elements aligned to sizeof(T).
To summarize in case this was TL;DR:
Is it possible to use malloc() for dynamic arrays in C++?
Is it possible to use operator new() and placement new for dynamic arrays in older C++ which has no alignas keyword?
Is pointer arithmetic undefined behavior when used over memory returned by operator new()?
Is Sutter advising code which might break on some antique machine?
Sorry if this is dumb.
The issue of pointer arithmetic on allocated memory, as in your example:
T* storage = static_cast<T*>(operator new(sizeof(T)*size));
// ...
T* p = storage + i; // precondition: 0 <= i < size
new (p) T(element);
being technically undefined behaviour has been known for a long time. It implies that std::vector can't be implemented with well-defined behaviour purely as a library, but requires additional guarantees from the implementation beyond those found in the standard.
It was definitely not the intention of the standards committee to make std::vector unimplementable. Sutter is, of course, right that such code is intended to be well-defined. The wording of the standard needs to reflect that.
P0593 is a proposal that, if accepted into the standard, may be able to solve this problem. In the meantime, it is fine to keep writing code like the above; no major compiler will treat it as UB.
Edit: As pointed out in the comments, I should have stated that when I said storage + i will be well-defined under P0593, I was assuming that the elements storage[0], storage[1], ..., storage[i-1] have already been constructed. Although I'm not sure I understand P0593 well enough to conclude that it wouldn't also cover the case where those elements hadn't already been constructed.
The C++ standards contain an open issue that underlying representation of objects is not an "array" but a "sequence" of unsigned char objects. Still, everyone treats it as an array (which is intended), so it is safe to write the code like:
char* storage = static_cast<char*>(operator new(sizeof(T)*size));
// ...
char* p = storage + sizeof(T)*i; // precondition: 0 <= i < size
new (p) T(element);
as long as void* operator new(size_t) returns a properly aligned value. Using sizeof-multiplied offsets to keep the alignment is safe.
In C++17, there is a macro STDCPP_DEFAULT_NEW_ALIGNMENT, which specifies the maximum safe alignment for "normal" void* operator new(size_t), and void* operator new(std::size_t size, std::align_val_t alignment) should be used if a larger alignment is required.
In earlier versions of C++, there is no such distinction, which means that void* operator new(size_t) needs to be implemented in a way that is compatible with the alignment of any object.
As to being able to do pointer arithmetic directly on T*, I am not sure it needs to be required by the standard. However, it is hard to implement the C++ memory model in such a way that it would not work.
To all of the widely used recent posix-compatible systems, that is, Windows, Linux (& Android ofc.), and MacOSX the followings apply
Is it possible to use malloc() for dynamic arrays in C++?
Yes it is. Using reinterpret_cast to convert the resulting void* to the desired pointer type is the best practice, and it yields in a dynamically allocated array like this: type *array = reinterpret_cast<type*>(malloc(sizeof(type)*array_size);
Be careful, that in this case constructors are not called on array elements, therefore it is still an uninitialized storage, no matter what type is. Nor destructors are called when free is used for deallocations
Is it possible to use operator new() and placement new for dynamic arrays in older C++ which has no alignas keyword?
Yes, but you need to be aware of alignment in case of placement new, if you feed it with custom locations (i.e ones that do not come from malloc/new). Normal operator new, as well as malloc, will provide native word aligned memory areas (at least whenever allocation size >= wordsize). This fact and the one that structure layouts and sizes are determined so that alignment is properly considered, you don't need to worry about alignment of dyn arrays if malloc or new is used.
One might notice, that word size is sometimes significantly smaller than the greatest built-in data type (which is typically long double), but it must be aligned the same way, since alignment is not about data size, but the bit width of addresses on memory bus for different access sizes.
Is pointer arithmetic undefined behavior when used over memory returned by operator new()?
Nope, as long as you respect the process' memory boundaries -- from this point of view new basically works the same way as malloc, moreover, new actually calls malloc in the vast majority of implementations in order to acquire the required area.
As a matter of fact, pointer arithmetic as such is never invalid. However, result of an arithmetic expression that evaluates to a pointer might point to a location outside of permitted areas, but this is not the fault of pointer arithmetic, but of the flawed expression.
Is Sutter advising code which might break on some antique machine?
I don't think so, provided the right compiler is used. (don't compile avr instructions or 128-bit-wide memory mov's into a binary that's intended to run on a 80386)
Of course, on different machines with different memory sizes and layouts the same literal address may access areas of different purpose/status/existence, but why would you use literal addresses unless you write driver code to a specific hardware?... :)
You can do it with "old fashioned" malloc, which gives you a block of memory that fulfils the most restrictive alignment on the respective platform (e.g. that of a long long double). So you will be able to place any object into such a buffer without violating any alignment requirements.
Given that, you can use placement new for arrays of your type based on such a memory block:
struct MyType {
MyType() {
cout << "in constructor of MyType" << endl;
}
~MyType() {
cout << "in destructor of MyType" << endl;
}
int x;
int y;
};
int main() {
char* buffer = (char*)malloc(sizeof(MyType)*3);
MyType *mt = new (buffer)MyType[3];
for (int i=0; i<3; i++) {
mt[i].~MyType();
}
free(mt);
}
Note that - as always with placement new - you'll have to take care of calling the destructors explicitly and freeing the memory in a distinct step; You must not use the delete or delete[]-functions, which combine these two steps and thereby would free memory that they don't own.

Allocate n bytes by new[] and fill it with any type?

I want to dynamically allocate known size of memory (just memory, not bothering about type) and fill it with exactly the same amount of data but any type (I'm only sure it will be primitive type). Ofc later I'm going to free it.
Is it ok? :
auto dest = new int8_t[n];
std::memcpy(dest, src, n);
delete[] dest;
src is ptr to an array of size n (Bytes).
I've ofc chosen int8_t becuase it's the clearest way to allocate certain amount of memory.
In fact the code above isn't exaclt what it will be. delete[] will be called on pointer of type which actually it points to.
So for example if src was an array of floats (forget about last line of above code):
float * ptr = dest;
delete[] ptr;
So again. Will it be ok?
It is ok, but only if you use a char array or an unsigned char array, because of the special alignment guarantees for these types:
5.3.4 New
11 When a new-expression calls an allocation function and that
allocation has not been extended, the new- expression passes the
amount of space requested to the allocation function as the first
argument of type std::size_t. That argument shall be no less than the
size of the object being created; it may be greater than the size of
the object being created only if the object is an array. For arrays of
char and unsigned char, the difference between the result of the
new-expression and the address returned by the allocation function
shall be an integral multiple of the strictest fundamental alignment
requirement (3.11) of any object type whose size is no greater than
the size of the array being created. [ Note: Because allocation
functions are assumed to return pointers to storage that is
appropriately aligned for objects of any type with fundamental
alignment, this constraint on array allocation overhead permits the
common idiom of allocating character arrays into which objects of
other types will later be placed. — end note ]
emphasis by me.
Another requirement is that you only use primitive types or PODs, because you don't call a constructor, but the (trivial) destructor (through delete).
According to the specification of the new expression execution of new int8_t[n] calls operator new[](sizeof(int8_t)*n + overhead).
Important note about overhead:
Many implementations use the array overhead to store the number of objects in the array which is used by the delete[] expression to call the correct number of destructors.
In its turn, operator new[](std::size_t count) returns the void* pointer to the allocated memory block. This pointer further should be used in delete [] expression.
You have simple types with no destructors, so delete[] only need to deallocate memory block. It can do it safely with both int8_t* dest or float* ptr.
UPDATE:
It's better to change array type from int8_t to char or unsigned char. For arrays with such types C++ standard garantees that you will get the proper aligment for further data placement. Thanks #alain. More details together with standard quote can be found in his answer.
Maybe will be better to use the std vector? No need to manually free memory..
std::vector<uint8_t> vdest(n);
uint8_t * dest = vdest.data();
std::memcpy(dest, src, n);
float * ptr = reinterpret_cast<float*>(dest);
...
EDIT:
As #alain pointed out: "There is no guarantee that an array of uint8_t is properly aligned for the new type"
So if you want use this approach It will be a good idea to look this question: Is it good practice to use std::vector as a simple buffer?
I understand this is common scenario in Real Time programming. To make sure that each structure is aligned to uint8_t/unsigned char(any custom defined Byte size datatype), make sure each of you structure (apart from primitive data Structure) use the concept #pragma pack.

Alternative to zero-sized array in embedded C++

The Multiboot Specification has structures like this:
struct multiboot_tag_mmap
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t entry_size;
multiboot_uint32_t entry_version;
struct multiboot_mmap_entry entries[0];
};
The intent seems to be that the array size can vary. The information is not known until passed along by the boot loader. In hosted C++, the suggested advice is to "use vector". Well, I can't do that. The alternative is to use dynamic allocation, but that would require implementing a significant chunk of the kernel (paging, MMU, etc.) before I even have the memory map information. A bit of a chicken or egg problem.
The "hack" is to just enable extensions with gnu++11. But I try to avoid using extensions as much as possible to avoid C-ish code or code that could potentially lead to undefined behavior. The more portable the code is, the less chance of bugs in my opinion.
Finally, you iterate over the memory map like this:
for (mmap = ((struct multiboot_tag_mmap *) tag)->entries;
(multiboot_uint8_t *) mmap
< (multiboot_uint8_t *) tag + tag->size;
mmap = (multiboot_memory_map_t *)
((unsigned long) mmap
+ ((struct multiboot_tag_mmap *) tag)->entry_size))
So the size of the structure is tag->size.
I can modify the multiboot header so long as the semantics are the same. The point is how it looks to the bootloader. What can I do?
Instead of 0-sized array, you can use 1-sized array:
struct multiboot_tag_mmap
{
...
struct multiboot_mmap_entry entries[1];
};
This will change only result of sizeof(struct multiboot_tag_mmap), but it shouldn't be used in any case: size of allocated structure should be computed as
offsetof(struct multiboot_tag_mmap, entries) + <num-of-entries> * sizeof(struct multiboot_mmap_entry)
Alignment of the map structure doesn't depends on the number of elements in the entries array, but on the entry type.
Strictly confirming alternative:
If there is known bounary for array size, one can use this boundary for type declaration:
struct multiboot_tag_mmap
{
...
struct multiboot_mmap_entry entries[<UPPER-BOUNDARY>];
};
For such declaration all possible issues described below are not applied.
NOTE about elements accessing:
For accessing elements (above the first one) in such flexible array one need to declare new pointer variable:
struct multiboot_mmap_entry* entries = tag->entries;
entries[index] = ...; // This is OK.
instead of using entries field directly:
tag->entries[index] = ...; // WRONG! May spuriously fail!
The thing is that compiler, knowing that the only one element exists in the entries field array, may optimize last case it to:
tag->entries[0] = ...; // Compiler is in its rights to assume index to have the only allowed value
Issues about standard confirmance: With flexible array approach, there is no object of type struct multiboot_tag_mmap in the memory(in the heap or on the stack). All we have is a pointer of this type, which is never dereferenced (e.g. for making full copy of the object). Similarly, there is no object of the array type struct multiboot_mmap_entry[1], corresponded to the entries field of the structure, this field is used only for conversion to generic pointer of type struct multiboot_mmap_entry*.
So, phrase in the C standard, which denotes Undefine Behavior
An object is assigned to an inexactly overlapping object or to an exactly overlapping object with incompatible type
is inapplicable for accessing entries array field using generic pointer: there is no overlapping object here.

What is the correct way to allocate and use an untyped memory block in C++?

The answers I got for this question until now has two exactly the opposite kinds of answers: "it's safe" and "it's undefined behaviour". I decided to rewrite the question in whole to get some better clarifying answers, for me and for anyone who might arrive here via Google.
Also, I removed the C tag and now this question is C++ specific
I am making an 8-byte-aligned memory heap that will be used in my virtual machine. The most obvious approach that I can think of is by allocating an array of std::uint64_t.
std::unique_ptr<std::uint64_t[]> block(new std::uint64_t[100]);
Let's assume sizeof(float) == 4 and sizeof(double) == 8. I want to store a float and a double in block and print the value.
float* pf = reinterpret_cast<float*>(&block[0]);
double* pd = reinterpret_cast<double*>(&block[1]);
*pf = 1.1;
*pd = 2.2;
std::cout << *pf << std::endl;
std::cout << *pd << std::endl;
I'd also like to store a C-string saying "hello".
char* pc = reinterpret_cast<char*>(&block[2]);
std::strcpy(pc, "hello\n");
std::cout << pc;
Now I want to store "Hello, world!" which goes over 8 bytes, but I still can use 2 consecutive cells.
char* pc2 = reinterpret_cast<char*>(&block[3]);
std::strcpy(pc2, "Hello, world\n");
std::cout << pc2;
For integers, I don't need a reinterpret_cast.
block[5] = 1;
std::cout << block[5] << std::endl;
I'm allocating block as an array of std::uint64_t for the sole purpose of memory alignment. I also do not expect anything larger than 8 bytes by its own to be stored in there. The type of the block can be anything if the starting address is guaranteed to be 8-byte-aligned.
Some people already answered that what I'm doing is totally safe, but some others said that I'm definitely invoking undefined behaviour.
Am I writing correct code to do what I intend? If not, what is the appropriate way?
The global allocation functions
To allocate an arbitrary (untyped) block of memory, the global allocation functions (§3.7.4/2);
void* operator new(std::size_t);
void* operator new[](std::size_t);
Can be used to do this (§3.7.4.1/2).
§3.7.4.1/2
The allocation function attempts to allocate the requested amount of storage. If it is successful, it shall return the address of the start of a block of storage whose length in bytes shall be at least as large as the requested size. There are no constraints on the contents of the allocated storage on return from the allocation function. The order, contiguity, and initial value of storage allocated by successive calls to an allocation function are unspecified. The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) 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).
And 3.11 has this to say about a fundamental alignment requirement;
§3.11/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).
Just to be sure on the requirement that the allocation functions must behave like this;
§3.7.4/3
Any allocation and/or deallocation functions defined in a C++ program, including the default versions in the library, shall conform to the semantics specified in 3.7.4.1 and 3.7.4.2.
Quotes from C++ WD n4527.
Assuming the 8-byte alignment is less than the fundamental alignment of the platform (and it looks like it is, but this can be verified on the target platform with static_assert(alignof(std::max_align_t) >= 8)) - you can use the global ::operator new to allocate the memory required. Once allocated, the memory can be segmented and used given the size and alignment requirements you have.
An alternative here is the std::aligned_storage and it would be able to give you memory aligned at whatever the requirement is.
typename std::aligned_storage<sizeof(T), alignof(T)>::type buffer[100];
From the question, I assume here that the both the size and alignment of T would be 8.
A sample of what the final memory block could look like is (basic RAII included);
struct DataBlock {
const std::size_t element_count;
static constexpr std::size_t element_size = 8;
void * data = nullptr;
explicit DataBlock(size_t elements) : element_count(elements)
{
data = ::operator new(elements * element_size);
}
~DataBlock()
{
::operator delete(data);
}
DataBlock(DataBlock&) = delete; // no copy
DataBlock& operator=(DataBlock&) = delete; // no assign
// probably shouldn't move either
DataBlock(DataBlock&&) = delete;
DataBlock& operator=(DataBlock&&) = delete;
template <class T>
T* get_location(std::size_t index)
{
// https://stackoverflow.com/a/6449951/3747990
// C++ WD n4527 3.9.2/4
void* t = reinterpret_cast<void*>(reinterpret_cast<unsigned char*>(data) + index*element_size);
// 5.2.9/13
return static_cast<T*>(t);
// C++ WD n4527 5.2.10/7 would allow this to be condensed
//T* t = reinterpret_cast<T*>(reinterpret_cast<unsigned char*>(data) + index*element_size);
//return t;
}
};
// ....
DataBlock block(100);
I've constructed more detailed examples of the DataBlock with suitable template construct and get functions etc., live demo here and here with further error checking etc..
A note on the aliasing
It does look like there are some aliasing issues in the original code (strictly speaking); you allocate memory of one type and cast it to another type.
It may probably work as you expect on your target platform, but you cannot rely on it. The most practical comment I've seen on this is;
"Undefined behaviour has the nasty result of usually doing what you think it should do, until it doesn’t” - hvd.
The code you have probably will work. I think it is better to use the appropriate global allocation functions and be sure that there is no undefined behaviour when allocating and using the memory you require.
Aliasing will still be applicable; once the memory is allocated - aliasing is applicable in how it is used. Once you have an arbitrary block of memory allocated (as above with the global allocation functions) and the lifetime of an object begins (§3.8/1) - aliasing rules apply.
What about std::allocator?
Whilst the std::allocator is for homogenous data containers and what your are looking for is akin to heterogeneous allocations, the implementation in your standard library (given the Allocator concept) offers some guidance on raw memory allocations and corresponding construction of the objects required.
Update for the new question:
The great news is there's a simple and easy solution to your real problem: Allocate the memory with new (unsigned char[size]). Memory allocated with new is guaranteed in the standard to be aligned in a way suitable for use as any type, and you can safely alias any type with char*.
The standard reference, 3.7.3.1/2, 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
Original answer for the original question:
At least in C++98/03 in 3.10/15 we have the following which pretty clearly makes it still undefined behavior (since you're accessing the value through a type that's not enumerated in the list of exceptions):
If a program attempts to access the stored value of an object through
an lvalue of other than one of the following types the behavior is
undefined):
— the dynamic type of the object,
— a cvqualified version of the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cvqualified version of the dynamic type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
— a type that is a (possibly cvqualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
pc pf and pd are all different types that access memory specified in block as uint64_t, so for say 'pf the shared types are float and uint64_t.
One would violate the strict aliasing rule were once to write using one type and read using another since the compile could we reorder the operations thinking there is no shared access. This is not your case however, since the uint64_t array is only used for assignment, it is exactly the same as using alloca to allocate the memory.
Incidentally there is no issue with the strict aliasing rule when casting from any type to a char type and visa versa. This is a common pattern used for data serialization and deserialization.
A lot of discussion here and given some answers that are slightly wrong, but making up good points, I just try to summarize:
exactly following the text of the standard (no matter what version) ... yes, this is undefined behaviour. Note the standard doesn't even have the term strict aliasing -- just a set of rules to enforce it no matter what implementations could define.
understanding the reason behind the "strict aliasing" rule, it should work nicely on any implementation as long as neither float or double take more than 64 bits.
the standard won't guarantee you anything about the size of float or double (intentionally) and that's the reason why it is that restrictive in the first place.
you can get around all this by ensuring your "heap" is an allocated object (e.g. get it with malloc()) and access the aligned slots through char * and shifting your offset by 3 bits.
you still have to make sure that anything you store in such a slot won't take more than 64 bits. (that's the hard part when it comes to portability)
In a nutshell: your code should be safe on any "sane" implementation as long as size constraints aren't a problem (means: the answer to the question in your title is most likely no), BUT it's still undefined behaviour (means: the answer to your last paragraph is yes)
I'll make it short: All your code works with defined semantics if you allocate the block using
std::unique_ptr<char[], std::free>
mem(static_cast<char*>(std::malloc(800)));
Because
every type is allowed to alias with a char[] and
malloc() is guaranteed to return a block of memory sufficiently aligned for all types (except maybe SIMD ones).
We pass std::free as a custom deleter, because we used malloc(), not new[], so calling delete[], the default, would be undefined behaviour.
If you're a purist, you can also use operator new:
std::unique_ptr<char[]>
mem(static_cast<char*>(operator new[](800)));
Then we don't need a custom deleter. Or
std::unique_ptr<char[]> mem(new char[800]);
to avoid the static_cast from void* to char*. But operator new can be replaced by the user, so I'm always a bit wary of using it. OTOH; malloc cannot be replaced (only in platform-specific ways, such as LD_PRELOAD).
Yes, because the memory locations pointed to by pf could overlap depending on the size of float and double. If they didn't, then the results of reading *pd and *pf would be well defined but not the results of reading from block or pc.
The behavior of C++ and the CPU are distinct. Although the standard provides memory suitable for any object, the rules and optimizations imposed by the CPU make the alignment for any given object "undefined" - an array of short would reasonably be 2 byte aligned, but an array of a 3 byte structure may be 8 byte aligned. A union of all possible types can be created and used between your storage and the usage to ensure no alignment rules are broken.
union copyOut {
char Buffer[200]; // max string length
int16 shortVal;
int32 intVal;
int64 longIntVal;
float fltVal;
double doubleVal;
} copyTarget;
memcpy( copyTarget.Buffer, Block[n], sizeof( data ) ); // move from unaligned space into union
// use copyTarget member here.
If you tag this as C++ question,
(1) why use uint64_t[] but not std::vector?
(2) in term of memory management, your code lack of management logic, which should keep track of which blocks are in use and which are free and the tracking of contiguoous blocks, and of course the allocate and release block methods.
(3) the code shows an unsafe way of using memory. For example, the char* is not const and therefore the block can be potentially be written to and overwrite the next block(s). The reinterpret_cast is consider danger and should be abstract from the memory user logic.
(4) the code doesn't show the allocator logic. In C world, the malloc function is untyped and in C++ world, the operator new is typed. You should consider something like the new operator.