Are evil byteblock reinterpretations valid C++? - c++

Let us not discuss the badness of the following code, it's not mine, and I fully
agree with you in advance that it's not pretty, rather C-ish and potentially
very dangerous:
void * buf = std::malloc(24 + sizeof(int[3]));
char * name = reinterpret_cast<char *>(buf);
std::strcpy(name, "some name");
int * values = reinterpret_cast<int *>(name + 24);
values[0] = 0; values[1] = 13; values[2] = 42;
Its intent is quite clear; it's a "byte block" storing two arrays of different
types. To access elements not in front of the block, it interprets the block as
char * and increments the pointer by sizeof(type[len]).
My question is however, is it legal C++(11), as in "is it guaranteed that it
will work on every standard conforming compiler"? My intuition says it's not, however g++ and clang seem to be fine
with it.
I would appreciate a standard quote on this one; unfortunately I
was not able to find a related passage myself.

This is perfectly valid C++ code (not nice though, as you noted yourself). As long as the string is not longer than 23 characters, it is not even in conflict with the strict aliasing rules, because you never access the same byte in memory through differently typed pointers. However, if the string exceeds the fixed limit, you have undefined behaviour like any other out of bounds bug.
Still, I'd recommend to use a structure, at the very least:
typedef struct whatever {
char name[24];
int [3];
} whatever;
whatever* myData = new myData;
std::strcpy(myData->name, "some name");
myData->values[0] = 0; myData->values[1] = 13; myData->values[2] = 42;
This is 100% equivalent to the code you gave, except for a bit more overhead in the new operator as opposed to directly calling malloc(). If you are worried about performance, you can still do whatever* myData = (whatever*)std::malloc(sizeof(*myData)); instead of using new.

Related

Strict aliasing rule for character pointer C++

I am trying to understand the strict aliasing rule. By reading the answer to What is the strict aliasing rule , I have some further questions. Here I borrow the example from that post:
struct Msg { uint32_t a, b; }
void SendWord(uint32_t);
void SendMessage(uint32_t* buff, uint32_t buffSize)
{
for (uint32_t i = 0; i < buffSize; ++i) SendWord(buff[i]);
}
Now consider the following code (A):
uint32_t *buff = (uint32_t*)malloc(sizeof(Msg));
std::memset(buff, 0, sizeof(Msg));
Msg *msg = (Msg*)buff; // Undefined behavior.
for (uint32_t i = 0; i < 10; ++i)
{
msg->a = i;
msg->b = i + 1;
SendMessage(buff, 2);
}
free(buff);
For the above code, the author explained what might happen due to the UB. The message sent could contain all 0s: during optimization, the compiler may assume msg and buff point to disjoint memory blocks, and then decide the writes to msg do not affect buff.
But how about the following code (B):
uint32_t *buff = (uint32_t*)malloc(sizeof(Msg));
std::memset(buff, 0, sizeof(Msg));
unsigned char *msg = (unsigned char*)buff; // Compliant.
for (uint32_t i = 0; i < sizeof(Msg); ++i)
{
msg[i] = i + 1;
SendMessage(buff, 2);
}
free(buff);
Is the sent message guaranteed to be as intended (as if the strict aliasing complier flag is off) ? If so, is it simply because a *char (msg) points to the same place as buff, the compiler should and will notice it, and refrain from the possible, aforementioned optimization in (A) ?
Yet then I read another comment under the post Strict aliasing rule and 'char *' pointers, saying that it is UB to use the *char pointer to write the referenced object. So again, code (B) could still result in similar, unexpected behavior ?
First of all , the answer https://stackoverflow.com/a/99010/1505939 is applying to C (and is in fact completely wrong, but that's another story). You ask about C++ and the strict aliasing rule is set up differently in C than C++. So that answer has nothing to do with this question.
Prior to C++20, both versions of your code cause undefined behaviour (by omission) as the behaviour of the assignment operator is only defined for the case of writing to an object. The malloc function in C++ allocates space but does not create objects within that space. The task should be approached using various other constructs such as new, or higher level containers, which do both allocate space and create objects within the space ready for writing.
Trying to analyze this code (pre-C++20) in context of the C++ strict aliasing rule is not possible because the definition of the rule is about accessing the stored value of an object but in this code it is not accessing any object, since no object was created.
Since C++20 there is a new provision (N4860 [intro.object]/10) that objects can be implicitly created by the assignment operator, if there exists such a possible combination of objects that would make the code well-defined. (Otherwise the behaviour remains undefined).
Under this change to the object model, both of your code samples are well-defined. In (A) there can be implicitly-created uint32_t objects in the space, and in (B) there can be implicitly-created unsigned char objects in the space. Since your code does not write as one type and then read as a different type, there is no possibility of an aliasing violation.
The existence of intermediate pointers of various types such as buff has no bearing on strict aliasing (in either language) -- the rule is strictly about how the space is read and written; not about how we got to the space.

Can char* and void* be used interchangeably in all cases as buffers? c++

Say we declared a char* buffer:
char *buf = new char[sizeof(int)*4]
//to delete:
delete [] buf;
or a void* buffer:
void *buf = operator new(sizeof(int)*4);
//to delete:
operator delete(buf);
How would they differ if they where used exclusively with the purpose of serving as pre-allocated memory?- always casting them to other types(not dereferencing them on their own):
int *intarr = static_cast<int*>(buf);
intarr[1] = 1;
Please also answer if the code above is incorrect and the following should be prefered(only considering the cases where the final types are primitives like int):
int *intarr = static_cast<int*>(buf);
for(size_t i = 0; i<4; i++){
new(&intarr[i]) int;
}
intarr[1] = 1;
Finally, answer if it is safe to delete the original buffer of type void*/char* once it is used to create other types in it with the latter aproach of placement new.
It is worth clarifying that this question is a matter of curiosity. I firmly believe that by knowing the bases of what is and isnt possible in a programming language, I can use these as building blocks and come up with solutions suitable for every specific use case when I need to in the future. This is not an XY question, as I dont have a specific implementation of code in mind.
In any case, I can name a few things I can relate to this question off the top of my head(pre-allocated buffers specifically):
Sometimes you want to make memory buffers for custom allocation. Sometimes even you want to align these buffers to cache line boundaries or other memory boundaries. Almost always in the name of more performance and sometimes by requirement(e.g. SIMD, if im not mistaken). Note that for alignment you could use std::aligned_alloc()
Due to a technicality, delete [] buf; may be considered to be UB after buf has been invalidated due to the array of characters being destroyed due to the reuse of the memory.
I wouldn't expect it to cause problems in practice, but using raw memory from operator new doesn't suffer from this technicality and there is no reason to not prefer it.
Even better is to use this:
T* memory = std::allocator<T>::allocate(n);
Because this will also work for overaligned types (unlike your suggestions) and it is easy to replace with a custom allocator. Or, simply use std::vector
for(size_t i = 0; i<4; i++){
new(&intarr[i]) int;
}
There is a standard function for this:
std::uninitialized_fill_n(intarr, 4, 0);
OK, it's slightly different that it initialises with an actual value, but that's probably better thing to do anyway.

Strict aliasing, and alignment questions when partitioning memory

My goal is to allocate a single chunk of memory and then partition it into smaller arrays of different types. I have a few questions about the code I've written here:
#include <iostream>
#include <cstdint>
#include <cstdlib>
int main() {
constexpr std::size_t array_count = 5;
constexpr std::size_t elements_size = sizeof(std::uint32_t) + sizeof(std::uint16_t);
void* const pv = std::calloc(array_count, elements_size);
//Partition the memory. p32 array starts at pv, p16 array starts after the 20 byte buffer for the p32 array.
std::uint32_t* const p32 = (std::uint32_t *) pv;
std::uint16_t* const p16 = (std::uint16_t *)((char *) pv + sizeof(std::uint32_t) * array_count);
//Initialize values.
for(std::size_t i = 0; i < array_count; ++i) {
p32[i] = i;
p16[i] = i * 2;
}
//Read them back.
for(std::size_t i = 0; i < array_count; ++i) {
std::cout << p32[i] << std::endl;
std::cout << p16[i] << std::endl;
std::cout << std::endl;
}
std::free(pv);
}
Does this code violate c++'s strict aliasing rules? I'm having trouble finding resources on aliasing when casting pointers from a malloc or calloc call. The p32 and p16 pointers should never overlap.
If I reverse the positioning of the two arrays where p16 started at pv, and p32 had a 10 byte offset from pv this could cause a segfault because uint32_t is aligned to the 4 byte boundary pv + 10 could be on the 2 byte boundary, right?
Is this program unsafe, or introduce any undefined behavior that I'm missing in general? I get the expected output on my local machine, but of course that doesn't mean my code is correct.
Yes, the program is UB. When you do this:
for(std::size_t i = 0; i < array_count; ++i) {
p32[i] = i;
p16[i] = i * 2;
}
There are no uint32_t or uint16_t objects that p32 or p16 point to. calloc just gives you bytes, not objects. You can't just reinterpret_cast objects into existence. On top of that, indexing is only defined for arrays, and p32 does not point to an array.
To make it well defined, you'd have to create an array object. However, placement-new for arrays is broken, so you're left with manually initializing a bunch of uint32_ts like:
auto p32 = reinterpret_cast<uint32_t*>(pv);
for (int i = 0; i < array_count; ++i) {
new (p32+i) uint32_t; // NB: this does no initialization, but it does satisfy
// [intro.object] in actually creating an object
}
This would then run into a separate issue: CWG 2182. Now we have array_count uint32_ts, but we don't have a uint32_t[array_count] so indexing is still UB. Basically, there's just no way in purely-by-the-letter-of-the-standard C++ to write this. See also my similar question on the topic.
That said, the amount of code that does this in the wild is tremendous and every implementation will allow you to do it.
I am only going to address Strict Aliasing part of the question.
C++ standard talks very little about malloc - mostly mentions it has semantic defined in C. In strict reading of C++ standard, there is no aliasing rule violation because there is no object which is aliased - in C++, lifetime of the object begins after it has been constructed, and no object has been constructed by malloc call.
As a result, this is something which is simply unspecified by Standard (as opposed by undefined).
No it's just normal cast of malloc return value and handling allocated memory. You can reinterpret these bytes to whatever datatype you want. Until you touch memory behind borders of allocated block.
No
I know you are just asking and trying, but you should allocate memory typically.
std::uint32_t* const p32 = std::calloc(array_count, sizeof(std::uint32_t));
std::uint16_t* const p16 = std::calloc(array_count, sizeof(std::uint16_t));
Is this program unsafe?
In your example, I think there is no problem with exceptions at all, but I suppose you intend to use it in another context. If your code throws an exception, you probably are going to leak some memory.
Do you really need to use calloc/free? In the cppcore guidelines you can find some guidelines about resource management in C++:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#S-resource
For instance, R10 and R11 say "not to call explicitly malloc/calloc/free, new/delete": https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-mallocfree
Only if you are doing something that really needs very low level code, you need to call new and delete explicitly, but the C++ way is to encapsulate all this calls inside a class, so the user of the class doesn't need to call explicitly new and delete.
But if you are not doing some low level stuff, have you consider to use std::array<>? or std::vector?

Which is faster, pointer access or reference access?

In the example code below I allocate some instances of the struct Chunk. In the for loops I then iterate through the memory block and access the different instances by using either pointer or references, and assign them some random data.
But which for loop will execute the fastest? By my knowledge I'd say the reference-loop will be the fastest because it will require no dereferencing and has direct access to the instance in memory. How wrong / right am I?
struct Chunk {
unsigned int a;
float b;
const char* c;
};
int main() {
Chunk* pData = new Chunk[8];
for( unsigned int i = 0; i < 8; ++i ) {
Chunk* p = &pData[i];
p->a = 1;
p->b = 1.0f;
p->c = "POINTERS";
}
for( unsigned int i = 0; i < 8; ++i ) {
Chunk& r = pData[i];
r.a = 1;
r.b = 1.0f;
r.c = "REFERENCES";
}
delete [] pData;
return 0;
}
They should be the same (not about the same, but exactly the same) with any non-idiotic compiler. Under the hood, references are pointers (on 99% of compilers). There's no reason for any difference.
Pedantic: the second loop could be faster (probably not) because the data is in the cache already, but that's it. :)
I'm tempted to say: who cares? Any difference in speed will be
negligible, and you should chose the most readable. In this particular
case, I would expect to see exactly the same code generated in both
case. In more complicated cases, the compiler may not be able to
determine later in the loop that the pointer has not been reseated, and
may have to reread it. But for this to be the case, you'd have to be
doing enough other things that the difference wouldn't be measurable.
There should be no difference in code produced by any decent compiler.
When you hesitate between two versions of the code like those in your example, you should choose the one more readable. Possible optimization of the kind you propose is supposed to be done by the compiler.
The more readable in your case is rather the version with references (actually, maybe not really more readable, but the consensus is to prefer the usage of references because pointers are more "dangerous").
But back to the effeciency: (please if someone knows assembler, better stop reading or you risk a laugh attack...) In my opinion, as the pData is alocated on the heap, the compiler will have to compile it using pointers anyway. I think that your reasoning could be kind of correct if your structure was allocated on the stack just with "Chunk data[8];". But at latest when the compiler optimizations are on the difference should be deleted anyway.
The execution time is almost the same but using references in less hectic.

Am I using new operator correctly?

I have the following pointer.
char **x = NULL;
x is will point to an array of pointers. So is the following code correct?
x = new (nothrow) (*char)[20];
and we will dealocate it using
delete[] x;
Is
x = (char **) malloc(sizeof(char **) * 20);
and
x = new (nothrow) (*char)[20];
equivalent?
Apart from the pointer-syntax mentioned by unwind, it is equivalent: an array of 20 char* will be allocated and deleted in both cases.
C++-adept warning: use std::vector< std::string > instead :) No memory management needed.
No, that code has syntax errors. The asterisk goes after the type name, to form a pointer to that type. So it's:
char*
not:
*char
It's weird that you have this right in the "C-style" example using malloc(), but not in C++.
As many commenters have kindly enough pointed out, there are other issues with the malloc() and its use of sizeof, though. But at least it got the type name right. Personally I'm against repeating type names in malloc() calls if at all possible, so I would write that version like this, to allocate a dynamic array of 20 character pointers:
char **x;
x = malloc(20 * sizeof *x);
This way:
Should be read as "20 times the size of whatever x points at", i.e. 20 times the size of a single char * pointer.
Contains the magical constant 20 in one place only.
Doesn't repeat any part of the type, if you were to change to wchar_t **x this would still work, and not by chance.
Is written in C, since I felt that is more natural when discussing malloc(). In C++, you need to cast the return value. In C, you should never do that.
New was introduced in C++. Malloc is C.
You shouldnt mix and match them... i.e. dont use delete on something you have used malloc on. Check this article.
I'd question why you are allocating such a thing in the first place. In C++, a std::vector of std::string is much more likely to be what you need.