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 poiner 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.
c++ arrays language-lawyer new-operator alignas
|
show 8 more comments
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 poiner 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.
c++ arrays language-lawyer new-operator alignas
1
Is it possible to use malloc() for dynamic arrays in C? – you wanted to write C++?
– Swordfish
Nov 23 '18 at 19:08
1
Can you put the whole code here? Because the code you've presented would not even compile (at the first line, there is a missing cast at least).
– geza
Nov 23 '18 at 19:20
1
@Galik It says that in eel.is/c++draft/expr.add#footnote-85. I don't think the pointer is considered pointing to single object though. No object was constructed in the allocated memory.
– user10605163
Nov 23 '18 at 19:41
1
Yes, as I see, that code has UB indeed. But, in my opinion, it's the standard which need to be fixed, not Herb's code. It would be interesting to know, why do we have such a restricting rule about pointer arithmetics.
– geza
Nov 23 '18 at 20:00
1
@Galik I don't really know, but in my amateur readingpossibly-hypothetical
refers to thehypothetical x[n]
after the actual array and the qualifyingif
of 4.2 does not use it either.
– user10605163
Nov 23 '18 at 20:01
|
show 8 more comments
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 poiner 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.
c++ arrays language-lawyer new-operator alignas
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 poiner 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.
c++ arrays language-lawyer new-operator alignas
c++ arrays language-lawyer new-operator alignas
edited Jan 5 at 7:17
Language Lawyer
73111
73111
asked Nov 23 '18 at 18:59
xor256xor256
445
445
1
Is it possible to use malloc() for dynamic arrays in C? – you wanted to write C++?
– Swordfish
Nov 23 '18 at 19:08
1
Can you put the whole code here? Because the code you've presented would not even compile (at the first line, there is a missing cast at least).
– geza
Nov 23 '18 at 19:20
1
@Galik It says that in eel.is/c++draft/expr.add#footnote-85. I don't think the pointer is considered pointing to single object though. No object was constructed in the allocated memory.
– user10605163
Nov 23 '18 at 19:41
1
Yes, as I see, that code has UB indeed. But, in my opinion, it's the standard which need to be fixed, not Herb's code. It would be interesting to know, why do we have such a restricting rule about pointer arithmetics.
– geza
Nov 23 '18 at 20:00
1
@Galik I don't really know, but in my amateur readingpossibly-hypothetical
refers to thehypothetical x[n]
after the actual array and the qualifyingif
of 4.2 does not use it either.
– user10605163
Nov 23 '18 at 20:01
|
show 8 more comments
1
Is it possible to use malloc() for dynamic arrays in C? – you wanted to write C++?
– Swordfish
Nov 23 '18 at 19:08
1
Can you put the whole code here? Because the code you've presented would not even compile (at the first line, there is a missing cast at least).
– geza
Nov 23 '18 at 19:20
1
@Galik It says that in eel.is/c++draft/expr.add#footnote-85. I don't think the pointer is considered pointing to single object though. No object was constructed in the allocated memory.
– user10605163
Nov 23 '18 at 19:41
1
Yes, as I see, that code has UB indeed. But, in my opinion, it's the standard which need to be fixed, not Herb's code. It would be interesting to know, why do we have such a restricting rule about pointer arithmetics.
– geza
Nov 23 '18 at 20:00
1
@Galik I don't really know, but in my amateur readingpossibly-hypothetical
refers to thehypothetical x[n]
after the actual array and the qualifyingif
of 4.2 does not use it either.
– user10605163
Nov 23 '18 at 20:01
1
1
Is it possible to use malloc() for dynamic arrays in C? – you wanted to write C++?
– Swordfish
Nov 23 '18 at 19:08
Is it possible to use malloc() for dynamic arrays in C? – you wanted to write C++?
– Swordfish
Nov 23 '18 at 19:08
1
1
Can you put the whole code here? Because the code you've presented would not even compile (at the first line, there is a missing cast at least).
– geza
Nov 23 '18 at 19:20
Can you put the whole code here? Because the code you've presented would not even compile (at the first line, there is a missing cast at least).
– geza
Nov 23 '18 at 19:20
1
1
@Galik It says that in eel.is/c++draft/expr.add#footnote-85. I don't think the pointer is considered pointing to single object though. No object was constructed in the allocated memory.
– user10605163
Nov 23 '18 at 19:41
@Galik It says that in eel.is/c++draft/expr.add#footnote-85. I don't think the pointer is considered pointing to single object though. No object was constructed in the allocated memory.
– user10605163
Nov 23 '18 at 19:41
1
1
Yes, as I see, that code has UB indeed. But, in my opinion, it's the standard which need to be fixed, not Herb's code. It would be interesting to know, why do we have such a restricting rule about pointer arithmetics.
– geza
Nov 23 '18 at 20:00
Yes, as I see, that code has UB indeed. But, in my opinion, it's the standard which need to be fixed, not Herb's code. It would be interesting to know, why do we have such a restricting rule about pointer arithmetics.
– geza
Nov 23 '18 at 20:00
1
1
@Galik I don't really know, but in my amateur reading
possibly-hypothetical
refers to the hypothetical x[n]
after the actual array and the qualifying if
of 4.2 does not use it either.– user10605163
Nov 23 '18 at 20:01
@Galik I don't really know, but in my amateur reading
possibly-hypothetical
refers to the hypothetical x[n]
after the actual array and the qualifying if
of 4.2 does not use it either.– user10605163
Nov 23 '18 at 20:01
|
show 8 more comments
4 Answers
4
active
oldest
votes
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.
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
5
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
|
show 9 more comments
The issue of pointer arithmetic on allocated memory, as in your example:
T* storage = 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.
1
Hmm, why is P0593 relevant here?T
can be any type. I think that proposal won't solve this problem.
– geza
Nov 23 '18 at 19:32
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.
– Pete Becker
Nov 23 '18 at 19:42
1
@PeteBecker That's what I meant.std::vector
was not meant to be unimplementable in user code.
– Brian
Nov 23 '18 at 19:50
1
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of typeT
lined up in memory, then it takes no additional code to create an array ofT
.
– Brian
Nov 23 '18 at 20:16
1
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
|
show 5 more comments
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?... :)
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
What exactly do you mean withwidely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?
– user10605163
Nov 23 '18 at 20:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@xor256 It's simply not true... every allocation will eventually lead to amalloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...
– Géza Török
Nov 24 '18 at 9:06
1
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
|
show 4 more comments
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.
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53451770%2ftreating-memory-returned-by-operator-newsizeoft-n-as-an-array%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
5
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
|
show 9 more comments
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.
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
5
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
|
show 9 more comments
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.
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.
answered Nov 23 '18 at 20:52
Kit.Kit.
52939
52939
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
5
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
|
show 9 more comments
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
5
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
Thanks! That sort of clarifies it a little bit. Looks like the standard is more complicated than tax laws.
– xor256
Nov 23 '18 at 21:14
5
5
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
You can argue and reason with accountants and lawyers. No such luck with compilers.
– user4581301
Nov 23 '18 at 23:35
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
CWG 1701 has nothing related to the problem in the question. CWG 1701 is about object representation. The problem with allocation functions that they do not create objects. How resolution of the issue should help here?
– Language Lawyer
Jan 10 at 9:28
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
everyone treats it as an array (which is intended) Where this "which is intended" is coming from? Note from the issue: An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves. How this is intended to be an array when its elements should not be objects?
– Language Lawyer
Jan 10 at 9:33
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
@LanguageLawyer, it's not true that allocation functions don't create objects. See the standard. Intended by the authors of the language, which follows from how they (and everyone else) use such constructs; if in doubt, you can ask them directly, their emails are not secret.
– Kit.
Jan 10 at 17:52
|
show 9 more comments
The issue of pointer arithmetic on allocated memory, as in your example:
T* storage = 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.
1
Hmm, why is P0593 relevant here?T
can be any type. I think that proposal won't solve this problem.
– geza
Nov 23 '18 at 19:32
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.
– Pete Becker
Nov 23 '18 at 19:42
1
@PeteBecker That's what I meant.std::vector
was not meant to be unimplementable in user code.
– Brian
Nov 23 '18 at 19:50
1
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of typeT
lined up in memory, then it takes no additional code to create an array ofT
.
– Brian
Nov 23 '18 at 20:16
1
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
|
show 5 more comments
The issue of pointer arithmetic on allocated memory, as in your example:
T* storage = 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.
1
Hmm, why is P0593 relevant here?T
can be any type. I think that proposal won't solve this problem.
– geza
Nov 23 '18 at 19:32
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.
– Pete Becker
Nov 23 '18 at 19:42
1
@PeteBecker That's what I meant.std::vector
was not meant to be unimplementable in user code.
– Brian
Nov 23 '18 at 19:50
1
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of typeT
lined up in memory, then it takes no additional code to create an array ofT
.
– Brian
Nov 23 '18 at 20:16
1
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
|
show 5 more comments
The issue of pointer arithmetic on allocated memory, as in your example:
T* storage = 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 issue of pointer arithmetic on allocated memory, as in your example:
T* storage = 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.
edited Nov 23 '18 at 19:58
answered Nov 23 '18 at 19:28
BrianBrian
64.1k795182
64.1k795182
1
Hmm, why is P0593 relevant here?T
can be any type. I think that proposal won't solve this problem.
– geza
Nov 23 '18 at 19:32
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.
– Pete Becker
Nov 23 '18 at 19:42
1
@PeteBecker That's what I meant.std::vector
was not meant to be unimplementable in user code.
– Brian
Nov 23 '18 at 19:50
1
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of typeT
lined up in memory, then it takes no additional code to create an array ofT
.
– Brian
Nov 23 '18 at 20:16
1
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
|
show 5 more comments
1
Hmm, why is P0593 relevant here?T
can be any type. I think that proposal won't solve this problem.
– geza
Nov 23 '18 at 19:32
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.
– Pete Becker
Nov 23 '18 at 19:42
1
@PeteBecker That's what I meant.std::vector
was not meant to be unimplementable in user code.
– Brian
Nov 23 '18 at 19:50
1
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of typeT
lined up in memory, then it takes no additional code to create an array ofT
.
– Brian
Nov 23 '18 at 20:16
1
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
1
1
Hmm, why is P0593 relevant here?
T
can be any type. I think that proposal won't solve this problem.– geza
Nov 23 '18 at 19:32
Hmm, why is P0593 relevant here?
T
can be any type. I think that proposal won't solve this problem.– geza
Nov 23 '18 at 19:32
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.– Pete Becker
Nov 23 '18 at 19:42
std::vector
is not unimplementable. It is arguably unimplementable in user code. The standard library can't be implemented in portable code, much less user-written code. That's one of the reasons that it comes with the compiler -- it can take advantage of known behavior of that compiler and the target OS.– Pete Becker
Nov 23 '18 at 19:42
1
1
@PeteBecker That's what I meant.
std::vector
was not meant to be unimplementable in user code.– Brian
Nov 23 '18 at 19:50
@PeteBecker That's what I meant.
std::vector
was not meant to be unimplementable in user code.– Brian
Nov 23 '18 at 19:50
1
1
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of type
T
lined up in memory, then it takes no additional code to create an array of T
.– Brian
Nov 23 '18 at 20:16
@geza It says that an array type of any element type is an implicit lifetime type (regardless of whether the element type is an implicit lifetime type). Because if you already have a bunch of objects of type
T
lined up in memory, then it takes no additional code to create an array of T
.– Brian
Nov 23 '18 at 20:16
1
1
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
Thanks, that actually makes sense! Now it's time to re-read that proposal for the 35th time :)
– geza
Nov 23 '18 at 20:18
|
show 5 more comments
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?... :)
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
What exactly do you mean withwidely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?
– user10605163
Nov 23 '18 at 20:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@xor256 It's simply not true... every allocation will eventually lead to amalloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...
– Géza Török
Nov 24 '18 at 9:06
1
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
|
show 4 more comments
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?... :)
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
What exactly do you mean withwidely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?
– user10605163
Nov 23 '18 at 20:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@xor256 It's simply not true... every allocation will eventually lead to amalloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...
– Géza Török
Nov 24 '18 at 9:06
1
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
|
show 4 more comments
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?... :)
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?... :)
edited Nov 24 '18 at 8:51
answered Nov 23 '18 at 19:47
Géza TörökGéza Török
1,225515
1,225515
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
What exactly do you mean withwidely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?
– user10605163
Nov 23 '18 at 20:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@xor256 It's simply not true... every allocation will eventually lead to amalloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...
– Géza Török
Nov 24 '18 at 9:06
1
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
|
show 4 more comments
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
What exactly do you mean withwidely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?
– user10605163
Nov 23 '18 at 20:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@xor256 It's simply not true... every allocation will eventually lead to amalloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...
– Géza Török
Nov 24 '18 at 9:06
1
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
But why in the @Brian answer link for the exact same example as Sutters (except for the implementation details) of a home-brewed vector it states that: "In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object."? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html
– xor256
Nov 23 '18 at 20:11
What exactly do you mean with
widely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?– user10605163
Nov 23 '18 at 20:31
What exactly do you mean with
widely used posix-based systems
?. Are you basing your answers on some standardization or implementation's guarantee?– user10605163
Nov 23 '18 at 20:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@eukaryota none of them, but sheer experience. Applies to at least recent Windows, Linux (and Android ofc), and Mac OSX versions.
– Géza Török
Nov 23 '18 at 23:31
@xor256 It's simply not true... every allocation will eventually lead to a
malloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...– Géza Török
Nov 24 '18 at 9:06
@xor256 It's simply not true... every allocation will eventually lead to a
malloc
call, and malloc implementation is provided by the platform's C library which has to solve these problems itself, otherwise you wouldn't be able to use struct arrays in C either...– Géza Török
Nov 24 '18 at 9:06
1
1
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
@GézaTörök giphy.com/gifs/bbcamerica-doctor-who-dr-TgF6GIhJZtOkzj9NAi
– xor256
Nov 26 '18 at 18:38
|
show 4 more comments
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.
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
add a comment |
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.
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
add a comment |
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.
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.
answered Nov 23 '18 at 19:32
Stephan LechnerStephan Lechner
27k22140
27k22140
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
add a comment |
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
But can I use operator new() in place of malloc() in your example?
– xor256
Nov 23 '18 at 20:09
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
As far as I understand the array placement-new may require an unspecified memory overhead. So whether this code has defined behavior is implementation-dependent. See stackoverflow.com/questions/8720425/…
– user10605163
Nov 23 '18 at 20:20
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@eukaryota note that both Sutter and open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html examples to not use array placement new. They only use placement new for each element which they create inside of a memory chunk.
– xor256
Nov 23 '18 at 20:40
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
@xor256 Yes, I am referring specifically to the code example in this answer. The non-array placement-new is not allowed to have this overhead.
– user10605163
Nov 23 '18 at 20:42
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53451770%2ftreating-memory-returned-by-operator-newsizeoft-n-as-an-array%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Is it possible to use malloc() for dynamic arrays in C? – you wanted to write C++?
– Swordfish
Nov 23 '18 at 19:08
1
Can you put the whole code here? Because the code you've presented would not even compile (at the first line, there is a missing cast at least).
– geza
Nov 23 '18 at 19:20
1
@Galik It says that in eel.is/c++draft/expr.add#footnote-85. I don't think the pointer is considered pointing to single object though. No object was constructed in the allocated memory.
– user10605163
Nov 23 '18 at 19:41
1
Yes, as I see, that code has UB indeed. But, in my opinion, it's the standard which need to be fixed, not Herb's code. It would be interesting to know, why do we have such a restricting rule about pointer arithmetics.
– geza
Nov 23 '18 at 20:00
1
@Galik I don't really know, but in my amateur reading
possibly-hypothetical
refers to thehypothetical x[n]
after the actual array and the qualifyingif
of 4.2 does not use it either.– user10605163
Nov 23 '18 at 20:01