Getting size of dynamic C-style array vs. use of delete[]. Contradiction? [duplicate]
This question already has an answer here:
C++ doesn't tell you the size of a dynamic array. But why?
7 answers
I read everywhere that in C++ it is not possible to get the size of a dynamic array just from the pointer pointing to that chunk of memory.
How is it possible that there is no way of getting the size of a dynamic array just from the pointer, and at the same time it is possible to free all the memory allocated by using delete
just on the pointer, without the need of specifying the array size?
delete
must know the size of the array, right? Therefore this information must exist somewhere. Shouldn't it?
What is wrong in my reasoning?
c++ arrays heap
marked as duplicate by Ben Voigt
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Feb 21 at 4:42
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
|
show 9 more comments
This question already has an answer here:
C++ doesn't tell you the size of a dynamic array. But why?
7 answers
I read everywhere that in C++ it is not possible to get the size of a dynamic array just from the pointer pointing to that chunk of memory.
How is it possible that there is no way of getting the size of a dynamic array just from the pointer, and at the same time it is possible to free all the memory allocated by using delete
just on the pointer, without the need of specifying the array size?
delete
must know the size of the array, right? Therefore this information must exist somewhere. Shouldn't it?
What is wrong in my reasoning?
c++ arrays heap
marked as duplicate by Ben Voigt
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Feb 21 at 4:42
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
7
The underlying memory allocator knows the size of blocks it allocates, but there's no standard function that returns that information. There are sometimes non-standard malloc implementation specific ways but they're inherently non portable.
– Shawn
Feb 20 at 10:20
5
Why is this ever interesting? Usestd::vector
and never worry aboutdelete
.
– n.m.
Feb 20 at 10:21
20
@n.m. I don't get answers that tell you "don't bother". If one asks a question, there are reasons behind it. And the reasons don't have necessarily to be "practical reasons". It might be curiosity, it might be that I want to implement a compiler in machine code, or whatever. Even if we suppose one should always be backed by practical reasons in order to ask questions, I'm sure it is always possible to come up with at least one or two cases in which new knowledge could be used. There might be cases in which I cannot or don't want to use STL.
– Michele Piccolini
Feb 20 at 10:42
3
"If one asks a question, there are reasons behind it." These reasons are not necessarily valid. If you want to learn how to use C++ efficiently then not bothering with delete and pointers is the right thing. They are too low level and there are gazillion of these low level things of zero importance. Trying to learn them all is a waste of time. If you are in a situation where you cannot usestd::vector
(which should be extremely rare) you might want to look inside a typical implementation to learn how it works in order to replicate some of it.
– n.m.
Feb 20 at 11:10
12
@n.m. I don't think SO is limited to people "learning how to use C++ efficiently". Otherwise the language-lawyer tag would be pretty useless. Someone has to deal with all these "low level things", so why is asking about them on SO unreasonable?
– Max Langhof
Feb 20 at 11:50
|
show 9 more comments
This question already has an answer here:
C++ doesn't tell you the size of a dynamic array. But why?
7 answers
I read everywhere that in C++ it is not possible to get the size of a dynamic array just from the pointer pointing to that chunk of memory.
How is it possible that there is no way of getting the size of a dynamic array just from the pointer, and at the same time it is possible to free all the memory allocated by using delete
just on the pointer, without the need of specifying the array size?
delete
must know the size of the array, right? Therefore this information must exist somewhere. Shouldn't it?
What is wrong in my reasoning?
c++ arrays heap
This question already has an answer here:
C++ doesn't tell you the size of a dynamic array. But why?
7 answers
I read everywhere that in C++ it is not possible to get the size of a dynamic array just from the pointer pointing to that chunk of memory.
How is it possible that there is no way of getting the size of a dynamic array just from the pointer, and at the same time it is possible to free all the memory allocated by using delete
just on the pointer, without the need of specifying the array size?
delete
must know the size of the array, right? Therefore this information must exist somewhere. Shouldn't it?
What is wrong in my reasoning?
This question already has an answer here:
C++ doesn't tell you the size of a dynamic array. But why?
7 answers
c++ arrays heap
c++ arrays heap
edited Feb 20 at 14:51
Peter Mortensen
13.7k1986112
13.7k1986112
asked Feb 20 at 10:16
Michele PiccoliniMichele Piccolini
454210
454210
marked as duplicate by Ben Voigt
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Feb 21 at 4:42
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
marked as duplicate by Ben Voigt
StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;
$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');
$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Feb 21 at 4:42
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
7
The underlying memory allocator knows the size of blocks it allocates, but there's no standard function that returns that information. There are sometimes non-standard malloc implementation specific ways but they're inherently non portable.
– Shawn
Feb 20 at 10:20
5
Why is this ever interesting? Usestd::vector
and never worry aboutdelete
.
– n.m.
Feb 20 at 10:21
20
@n.m. I don't get answers that tell you "don't bother". If one asks a question, there are reasons behind it. And the reasons don't have necessarily to be "practical reasons". It might be curiosity, it might be that I want to implement a compiler in machine code, or whatever. Even if we suppose one should always be backed by practical reasons in order to ask questions, I'm sure it is always possible to come up with at least one or two cases in which new knowledge could be used. There might be cases in which I cannot or don't want to use STL.
– Michele Piccolini
Feb 20 at 10:42
3
"If one asks a question, there are reasons behind it." These reasons are not necessarily valid. If you want to learn how to use C++ efficiently then not bothering with delete and pointers is the right thing. They are too low level and there are gazillion of these low level things of zero importance. Trying to learn them all is a waste of time. If you are in a situation where you cannot usestd::vector
(which should be extremely rare) you might want to look inside a typical implementation to learn how it works in order to replicate some of it.
– n.m.
Feb 20 at 11:10
12
@n.m. I don't think SO is limited to people "learning how to use C++ efficiently". Otherwise the language-lawyer tag would be pretty useless. Someone has to deal with all these "low level things", so why is asking about them on SO unreasonable?
– Max Langhof
Feb 20 at 11:50
|
show 9 more comments
7
The underlying memory allocator knows the size of blocks it allocates, but there's no standard function that returns that information. There are sometimes non-standard malloc implementation specific ways but they're inherently non portable.
– Shawn
Feb 20 at 10:20
5
Why is this ever interesting? Usestd::vector
and never worry aboutdelete
.
– n.m.
Feb 20 at 10:21
20
@n.m. I don't get answers that tell you "don't bother". If one asks a question, there are reasons behind it. And the reasons don't have necessarily to be "practical reasons". It might be curiosity, it might be that I want to implement a compiler in machine code, or whatever. Even if we suppose one should always be backed by practical reasons in order to ask questions, I'm sure it is always possible to come up with at least one or two cases in which new knowledge could be used. There might be cases in which I cannot or don't want to use STL.
– Michele Piccolini
Feb 20 at 10:42
3
"If one asks a question, there are reasons behind it." These reasons are not necessarily valid. If you want to learn how to use C++ efficiently then not bothering with delete and pointers is the right thing. They are too low level and there are gazillion of these low level things of zero importance. Trying to learn them all is a waste of time. If you are in a situation where you cannot usestd::vector
(which should be extremely rare) you might want to look inside a typical implementation to learn how it works in order to replicate some of it.
– n.m.
Feb 20 at 11:10
12
@n.m. I don't think SO is limited to people "learning how to use C++ efficiently". Otherwise the language-lawyer tag would be pretty useless. Someone has to deal with all these "low level things", so why is asking about them on SO unreasonable?
– Max Langhof
Feb 20 at 11:50
7
7
The underlying memory allocator knows the size of blocks it allocates, but there's no standard function that returns that information. There are sometimes non-standard malloc implementation specific ways but they're inherently non portable.
– Shawn
Feb 20 at 10:20
The underlying memory allocator knows the size of blocks it allocates, but there's no standard function that returns that information. There are sometimes non-standard malloc implementation specific ways but they're inherently non portable.
– Shawn
Feb 20 at 10:20
5
5
Why is this ever interesting? Use
std::vector
and never worry about delete
.– n.m.
Feb 20 at 10:21
Why is this ever interesting? Use
std::vector
and never worry about delete
.– n.m.
Feb 20 at 10:21
20
20
@n.m. I don't get answers that tell you "don't bother". If one asks a question, there are reasons behind it. And the reasons don't have necessarily to be "practical reasons". It might be curiosity, it might be that I want to implement a compiler in machine code, or whatever. Even if we suppose one should always be backed by practical reasons in order to ask questions, I'm sure it is always possible to come up with at least one or two cases in which new knowledge could be used. There might be cases in which I cannot or don't want to use STL.
– Michele Piccolini
Feb 20 at 10:42
@n.m. I don't get answers that tell you "don't bother". If one asks a question, there are reasons behind it. And the reasons don't have necessarily to be "practical reasons". It might be curiosity, it might be that I want to implement a compiler in machine code, or whatever. Even if we suppose one should always be backed by practical reasons in order to ask questions, I'm sure it is always possible to come up with at least one or two cases in which new knowledge could be used. There might be cases in which I cannot or don't want to use STL.
– Michele Piccolini
Feb 20 at 10:42
3
3
"If one asks a question, there are reasons behind it." These reasons are not necessarily valid. If you want to learn how to use C++ efficiently then not bothering with delete and pointers is the right thing. They are too low level and there are gazillion of these low level things of zero importance. Trying to learn them all is a waste of time. If you are in a situation where you cannot use
std::vector
(which should be extremely rare) you might want to look inside a typical implementation to learn how it works in order to replicate some of it.– n.m.
Feb 20 at 11:10
"If one asks a question, there are reasons behind it." These reasons are not necessarily valid. If you want to learn how to use C++ efficiently then not bothering with delete and pointers is the right thing. They are too low level and there are gazillion of these low level things of zero importance. Trying to learn them all is a waste of time. If you are in a situation where you cannot use
std::vector
(which should be extremely rare) you might want to look inside a typical implementation to learn how it works in order to replicate some of it.– n.m.
Feb 20 at 11:10
12
12
@n.m. I don't think SO is limited to people "learning how to use C++ efficiently". Otherwise the language-lawyer tag would be pretty useless. Someone has to deal with all these "low level things", so why is asking about them on SO unreasonable?
– Max Langhof
Feb 20 at 11:50
@n.m. I don't think SO is limited to people "learning how to use C++ efficiently". Otherwise the language-lawyer tag would be pretty useless. Someone has to deal with all these "low level things", so why is asking about them on SO unreasonable?
– Max Langhof
Feb 20 at 11:50
|
show 9 more comments
5 Answers
5
active
oldest
votes
TL;DR The operator delete
destructs the objects and deallocates the memory. The information N ("number of elements") is required for destructing. The information S ("size of allocated memory") is required for deallocating. S is always stored and can be queried by compiler extensions. N is only stored if destructing objects requires calling destructors. If N is stored, where it is stored is implementation-dependent.
The operator delete
has to do two things:
a) destructing the objects (calling destructors, if necessary) and
b) deallocating the memory.
Let's first discuss (de)allocation, which
is delegated to the C functions malloc
and free
by many compilers (like GCC). The function malloc
takes the number of bytes to be allocated as a parameter and returns a pointer. The function free
takes only a pointer; the number of bytes is not necessary. This means that the memory allocating functions have to keep track how many bytes have been allocated. There could be a function to query how many bytes have been allocated (in Linux this can be done with malloc_usable_size
, in Windows with _msize
). This is not what you want because this does not tell you the size of an array but the amount of memory allocated. Since malloc
is not necessarily giving you exactly as much memory as you have asked for, you cannot compute the array size from the result of malloc_usable_size
:
#include <iostream>
#include <malloc.h>
int main()
{
std::cout << malloc_usable_size(malloc(42)) << std::endl;
}
This example gives you 56, not 42: http://cpp.sh/2wdm4
Note that applying malloc_usable_size
(or _msize
) to the result of new
is undefined behavior.
So, let's now discuss construction and destruction of objects. Here, you have two ways of delete: delete
(for single objects) and delete
(for arrays). In very old versions of C++, you had to pass the size of the array to the delete
-operator. As you mentioned, nowadays, this is not the case. The compiler tracks this information. GCC adds a small field prior the beginning of the array, where the size of the array is stored such that it knows how often the destructor has to be called. You might query that:
#include <iostream>
struct foo {
char a;
~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
This code gives you 42: http://cpp.sh/7mbqq
Just for the protocol: This is undefined behavior, but with the current version of GCC it works.
So, you might ask yourself why there is no function to query this information. The answer is that GCC doesn't always store this information. There might be cases where destruction of the objects is a no-operation (and the compiler is able to figure that out). Consider the following example:
#include <iostream>
struct foo {
char a;
//~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
Here, the answer is not 42 any more: http://cpp.sh/2rzfb
The answer is just garbage - the code was undefined behavior again.
Why? Because the compiler does not need to call a destructor, so it does not need to store the information. And, yes, in this case the compiler does not add code that keeps track how many objects have been created. Only the number of allocated bytes (which might be 56, see above) is known.
1
Very interesting answer! I do wonder now whatmalloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?
– Max Langhof
Feb 20 at 13:22
1
You can try this out but this is undefined behavior. For gcc, wherenew
is based onmalloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example),ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect thatmalloc_usable_size
does not work.
– Handy999
Feb 20 at 13:28
3
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?
– Michele Piccolini
Feb 20 at 17:22
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
1
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
|
show 1 more comment
It does - the allocator, or some implementation detail behind it, knows exactly what the size of the block is.
But that information is not provided to you or to the "code layer" of your program.
Could the language have been designed to do this? Sure! It's probably a case of "don't pay for what you don't use" — it's your responsibility to remember this information. After all, you know how much memory you asked for! Often times people will not want the cost of a number being passed up the call stack when, most of the time, they won't need it to be.
There are some platform-specific "extensions" that may get you what you want, like malloc_usable_size
on Linux and _msize
on Windows, though these assume that your allocator used malloc
and didn't do any other magic that may extend the size of the allocated block at the lowest level. I'd say you're still better off tracking this yourself if you really need it… or using a vector.
4
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
4
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
1
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctlydelete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size thatdelete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.
– Max Langhof
Feb 20 at 11:45
2
@MichelePiccolini You shouldn't really be usingdelete
ordelete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.
– Lightness Races in Orbit
Feb 20 at 11:57
2
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
|
show 9 more comments
I think the reason for this is a confluence of three factors.
- C++ has a "you only pay for what you use" culture
- C++ started its life as a pre-processor for C and hence had to be built on top of what C offered.
- C++ is one of the most widely ported languages around. Features that make life difficult for existing ports are unlikely to get added.
C allows the programmer to free memory blocks without specifying the size of the memory block to free, but does not provide the programmer with any standard way to access the size of the allocation. Furthermore the actual amount of memory allocated may well be larger than the amount the programmer asked for.
Following the principle of "you only pay for what you use", C++ implementations implement new
differently for different types. Typically they only store the size if it is necessary to do so, usually because the type has a non-trivial destructor.
So while yes, enough information is stored to free the memory block, it would be very difficult to define a sane and portable API for accessing that information. Depending on the data type and platform, the actual requested size may be available (for types where the C++ implementation has to store it), only the actual allocated size may be available (for types where the C++ implementation does not have to store it on platforms where the underlying memory manager has an extension to get the allocated size), or the size may not be available at all (for types where the C++ implementation does not have to store it on platforms that don't provide access to the information from the underlying memory manager).
add a comment |
This answer applies to Microsoft Visual Studio only.
There is a function called _msize, which will return the malloced / calloced / realloced size of a pointer.
It can be found in the malloc.h header, and the parameters are:
size_t _msize(
void *memblock
);
I am not sure if there is an equivalent in gcc. There probably should be.
1
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
2
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
add a comment |
If delete
doesn't have to know the size of the array at the time it is called, your entire argument falls apart. And delete
doesn't have to know the size of the array at the time it is called. It only needs to know the size to make the block available for use by others, and absolutely nothing requires it to make the block available for use by others at the time delete
is called.
For example, delete
my dice a large block into some number of smaller blocks. Each of those blocks but one need only have a pointer to the control block that knows the size. If any block but the control block is passed to delete
first, then delete
has no idea how big the block that was just freed is and won't know until later.
That it is not absolutely required that delete
know the size of a block at every arbitrary point during its lifetime is sufficient to invalidate your argument.
add a comment |
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
TL;DR The operator delete
destructs the objects and deallocates the memory. The information N ("number of elements") is required for destructing. The information S ("size of allocated memory") is required for deallocating. S is always stored and can be queried by compiler extensions. N is only stored if destructing objects requires calling destructors. If N is stored, where it is stored is implementation-dependent.
The operator delete
has to do two things:
a) destructing the objects (calling destructors, if necessary) and
b) deallocating the memory.
Let's first discuss (de)allocation, which
is delegated to the C functions malloc
and free
by many compilers (like GCC). The function malloc
takes the number of bytes to be allocated as a parameter and returns a pointer. The function free
takes only a pointer; the number of bytes is not necessary. This means that the memory allocating functions have to keep track how many bytes have been allocated. There could be a function to query how many bytes have been allocated (in Linux this can be done with malloc_usable_size
, in Windows with _msize
). This is not what you want because this does not tell you the size of an array but the amount of memory allocated. Since malloc
is not necessarily giving you exactly as much memory as you have asked for, you cannot compute the array size from the result of malloc_usable_size
:
#include <iostream>
#include <malloc.h>
int main()
{
std::cout << malloc_usable_size(malloc(42)) << std::endl;
}
This example gives you 56, not 42: http://cpp.sh/2wdm4
Note that applying malloc_usable_size
(or _msize
) to the result of new
is undefined behavior.
So, let's now discuss construction and destruction of objects. Here, you have two ways of delete: delete
(for single objects) and delete
(for arrays). In very old versions of C++, you had to pass the size of the array to the delete
-operator. As you mentioned, nowadays, this is not the case. The compiler tracks this information. GCC adds a small field prior the beginning of the array, where the size of the array is stored such that it knows how often the destructor has to be called. You might query that:
#include <iostream>
struct foo {
char a;
~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
This code gives you 42: http://cpp.sh/7mbqq
Just for the protocol: This is undefined behavior, but with the current version of GCC it works.
So, you might ask yourself why there is no function to query this information. The answer is that GCC doesn't always store this information. There might be cases where destruction of the objects is a no-operation (and the compiler is able to figure that out). Consider the following example:
#include <iostream>
struct foo {
char a;
//~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
Here, the answer is not 42 any more: http://cpp.sh/2rzfb
The answer is just garbage - the code was undefined behavior again.
Why? Because the compiler does not need to call a destructor, so it does not need to store the information. And, yes, in this case the compiler does not add code that keeps track how many objects have been created. Only the number of allocated bytes (which might be 56, see above) is known.
1
Very interesting answer! I do wonder now whatmalloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?
– Max Langhof
Feb 20 at 13:22
1
You can try this out but this is undefined behavior. For gcc, wherenew
is based onmalloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example),ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect thatmalloc_usable_size
does not work.
– Handy999
Feb 20 at 13:28
3
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?
– Michele Piccolini
Feb 20 at 17:22
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
1
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
|
show 1 more comment
TL;DR The operator delete
destructs the objects and deallocates the memory. The information N ("number of elements") is required for destructing. The information S ("size of allocated memory") is required for deallocating. S is always stored and can be queried by compiler extensions. N is only stored if destructing objects requires calling destructors. If N is stored, where it is stored is implementation-dependent.
The operator delete
has to do two things:
a) destructing the objects (calling destructors, if necessary) and
b) deallocating the memory.
Let's first discuss (de)allocation, which
is delegated to the C functions malloc
and free
by many compilers (like GCC). The function malloc
takes the number of bytes to be allocated as a parameter and returns a pointer. The function free
takes only a pointer; the number of bytes is not necessary. This means that the memory allocating functions have to keep track how many bytes have been allocated. There could be a function to query how many bytes have been allocated (in Linux this can be done with malloc_usable_size
, in Windows with _msize
). This is not what you want because this does not tell you the size of an array but the amount of memory allocated. Since malloc
is not necessarily giving you exactly as much memory as you have asked for, you cannot compute the array size from the result of malloc_usable_size
:
#include <iostream>
#include <malloc.h>
int main()
{
std::cout << malloc_usable_size(malloc(42)) << std::endl;
}
This example gives you 56, not 42: http://cpp.sh/2wdm4
Note that applying malloc_usable_size
(or _msize
) to the result of new
is undefined behavior.
So, let's now discuss construction and destruction of objects. Here, you have two ways of delete: delete
(for single objects) and delete
(for arrays). In very old versions of C++, you had to pass the size of the array to the delete
-operator. As you mentioned, nowadays, this is not the case. The compiler tracks this information. GCC adds a small field prior the beginning of the array, where the size of the array is stored such that it knows how often the destructor has to be called. You might query that:
#include <iostream>
struct foo {
char a;
~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
This code gives you 42: http://cpp.sh/7mbqq
Just for the protocol: This is undefined behavior, but with the current version of GCC it works.
So, you might ask yourself why there is no function to query this information. The answer is that GCC doesn't always store this information. There might be cases where destruction of the objects is a no-operation (and the compiler is able to figure that out). Consider the following example:
#include <iostream>
struct foo {
char a;
//~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
Here, the answer is not 42 any more: http://cpp.sh/2rzfb
The answer is just garbage - the code was undefined behavior again.
Why? Because the compiler does not need to call a destructor, so it does not need to store the information. And, yes, in this case the compiler does not add code that keeps track how many objects have been created. Only the number of allocated bytes (which might be 56, see above) is known.
1
Very interesting answer! I do wonder now whatmalloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?
– Max Langhof
Feb 20 at 13:22
1
You can try this out but this is undefined behavior. For gcc, wherenew
is based onmalloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example),ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect thatmalloc_usable_size
does not work.
– Handy999
Feb 20 at 13:28
3
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?
– Michele Piccolini
Feb 20 at 17:22
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
1
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
|
show 1 more comment
TL;DR The operator delete
destructs the objects and deallocates the memory. The information N ("number of elements") is required for destructing. The information S ("size of allocated memory") is required for deallocating. S is always stored and can be queried by compiler extensions. N is only stored if destructing objects requires calling destructors. If N is stored, where it is stored is implementation-dependent.
The operator delete
has to do two things:
a) destructing the objects (calling destructors, if necessary) and
b) deallocating the memory.
Let's first discuss (de)allocation, which
is delegated to the C functions malloc
and free
by many compilers (like GCC). The function malloc
takes the number of bytes to be allocated as a parameter and returns a pointer. The function free
takes only a pointer; the number of bytes is not necessary. This means that the memory allocating functions have to keep track how many bytes have been allocated. There could be a function to query how many bytes have been allocated (in Linux this can be done with malloc_usable_size
, in Windows with _msize
). This is not what you want because this does not tell you the size of an array but the amount of memory allocated. Since malloc
is not necessarily giving you exactly as much memory as you have asked for, you cannot compute the array size from the result of malloc_usable_size
:
#include <iostream>
#include <malloc.h>
int main()
{
std::cout << malloc_usable_size(malloc(42)) << std::endl;
}
This example gives you 56, not 42: http://cpp.sh/2wdm4
Note that applying malloc_usable_size
(or _msize
) to the result of new
is undefined behavior.
So, let's now discuss construction and destruction of objects. Here, you have two ways of delete: delete
(for single objects) and delete
(for arrays). In very old versions of C++, you had to pass the size of the array to the delete
-operator. As you mentioned, nowadays, this is not the case. The compiler tracks this information. GCC adds a small field prior the beginning of the array, where the size of the array is stored such that it knows how often the destructor has to be called. You might query that:
#include <iostream>
struct foo {
char a;
~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
This code gives you 42: http://cpp.sh/7mbqq
Just for the protocol: This is undefined behavior, but with the current version of GCC it works.
So, you might ask yourself why there is no function to query this information. The answer is that GCC doesn't always store this information. There might be cases where destruction of the objects is a no-operation (and the compiler is able to figure that out). Consider the following example:
#include <iostream>
struct foo {
char a;
//~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
Here, the answer is not 42 any more: http://cpp.sh/2rzfb
The answer is just garbage - the code was undefined behavior again.
Why? Because the compiler does not need to call a destructor, so it does not need to store the information. And, yes, in this case the compiler does not add code that keeps track how many objects have been created. Only the number of allocated bytes (which might be 56, see above) is known.
TL;DR The operator delete
destructs the objects and deallocates the memory. The information N ("number of elements") is required for destructing. The information S ("size of allocated memory") is required for deallocating. S is always stored and can be queried by compiler extensions. N is only stored if destructing objects requires calling destructors. If N is stored, where it is stored is implementation-dependent.
The operator delete
has to do two things:
a) destructing the objects (calling destructors, if necessary) and
b) deallocating the memory.
Let's first discuss (de)allocation, which
is delegated to the C functions malloc
and free
by many compilers (like GCC). The function malloc
takes the number of bytes to be allocated as a parameter and returns a pointer. The function free
takes only a pointer; the number of bytes is not necessary. This means that the memory allocating functions have to keep track how many bytes have been allocated. There could be a function to query how many bytes have been allocated (in Linux this can be done with malloc_usable_size
, in Windows with _msize
). This is not what you want because this does not tell you the size of an array but the amount of memory allocated. Since malloc
is not necessarily giving you exactly as much memory as you have asked for, you cannot compute the array size from the result of malloc_usable_size
:
#include <iostream>
#include <malloc.h>
int main()
{
std::cout << malloc_usable_size(malloc(42)) << std::endl;
}
This example gives you 56, not 42: http://cpp.sh/2wdm4
Note that applying malloc_usable_size
(or _msize
) to the result of new
is undefined behavior.
So, let's now discuss construction and destruction of objects. Here, you have two ways of delete: delete
(for single objects) and delete
(for arrays). In very old versions of C++, you had to pass the size of the array to the delete
-operator. As you mentioned, nowadays, this is not the case. The compiler tracks this information. GCC adds a small field prior the beginning of the array, where the size of the array is stored such that it knows how often the destructor has to be called. You might query that:
#include <iostream>
struct foo {
char a;
~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
This code gives you 42: http://cpp.sh/7mbqq
Just for the protocol: This is undefined behavior, but with the current version of GCC it works.
So, you might ask yourself why there is no function to query this information. The answer is that GCC doesn't always store this information. There might be cases where destruction of the objects is a no-operation (and the compiler is able to figure that out). Consider the following example:
#include <iostream>
struct foo {
char a;
//~foo() {}
};
int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}
Here, the answer is not 42 any more: http://cpp.sh/2rzfb
The answer is just garbage - the code was undefined behavior again.
Why? Because the compiler does not need to call a destructor, so it does not need to store the information. And, yes, in this case the compiler does not add code that keeps track how many objects have been created. Only the number of allocated bytes (which might be 56, see above) is known.
edited Feb 21 at 7:08
answered Feb 20 at 12:42
Handy999Handy999
55417
55417
1
Very interesting answer! I do wonder now whatmalloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?
– Max Langhof
Feb 20 at 13:22
1
You can try this out but this is undefined behavior. For gcc, wherenew
is based onmalloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example),ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect thatmalloc_usable_size
does not work.
– Handy999
Feb 20 at 13:28
3
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?
– Michele Piccolini
Feb 20 at 17:22
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
1
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
|
show 1 more comment
1
Very interesting answer! I do wonder now whatmalloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?
– Max Langhof
Feb 20 at 13:22
1
You can try this out but this is undefined behavior. For gcc, wherenew
is based onmalloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example),ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect thatmalloc_usable_size
does not work.
– Handy999
Feb 20 at 13:28
3
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?
– Michele Piccolini
Feb 20 at 17:22
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
1
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
1
1
Very interesting answer! I do wonder now what
malloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?– Max Langhof
Feb 20 at 13:22
Very interesting answer! I do wonder now what
malloc_usable_size
would give you for that second pointer though? And if it's the correct answer, where this information would have been stored?– Max Langhof
Feb 20 at 13:22
1
1
You can try this out but this is undefined behavior. For gcc, where
new
is based on malloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example), ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect that malloc_usable_size
does not work.– Handy999
Feb 20 at 13:28
You can try this out but this is undefined behavior. For gcc, where
new
is based on malloc
, it could work. However, if you consider an array of a not-trivally-destructible type (first example), ptr
does for sure not point to the beginning of the memory that has been allocated by malloc (because of the hidden field in the beginning). Thus, I expect that malloc_usable_size
does not work.– Handy999
Feb 20 at 13:28
3
3
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *
delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?– Michele Piccolini
Feb 20 at 17:22
Wonderful answer! So, the gist is: * The information N ("number of elements") may not be stored (it is only in case of dynamic arrays of things with a destructor). For this reason we need to keep track of the length manually, if we want it. * What is instead always stored is S ("size of allocated memory"). (S can be queried but results are implementation-dependent). *
delete
only needs S in order to deallocate memory (treats everything like a blob). It needs N only to know how many times to call destructors. * If N is stored, where it is stored is implementation-dependent. Correct?– Michele Piccolini
Feb 20 at 17:22
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
@Michele Piccolini: yes, excatly.
– Handy999
Feb 20 at 18:15
1
1
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
@Michele Piccolini: do you like it like this?
– Handy999
Feb 21 at 7:10
|
show 1 more comment
It does - the allocator, or some implementation detail behind it, knows exactly what the size of the block is.
But that information is not provided to you or to the "code layer" of your program.
Could the language have been designed to do this? Sure! It's probably a case of "don't pay for what you don't use" — it's your responsibility to remember this information. After all, you know how much memory you asked for! Often times people will not want the cost of a number being passed up the call stack when, most of the time, they won't need it to be.
There are some platform-specific "extensions" that may get you what you want, like malloc_usable_size
on Linux and _msize
on Windows, though these assume that your allocator used malloc
and didn't do any other magic that may extend the size of the allocated block at the lowest level. I'd say you're still better off tracking this yourself if you really need it… or using a vector.
4
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
4
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
1
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctlydelete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size thatdelete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.
– Max Langhof
Feb 20 at 11:45
2
@MichelePiccolini You shouldn't really be usingdelete
ordelete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.
– Lightness Races in Orbit
Feb 20 at 11:57
2
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
|
show 9 more comments
It does - the allocator, or some implementation detail behind it, knows exactly what the size of the block is.
But that information is not provided to you or to the "code layer" of your program.
Could the language have been designed to do this? Sure! It's probably a case of "don't pay for what you don't use" — it's your responsibility to remember this information. After all, you know how much memory you asked for! Often times people will not want the cost of a number being passed up the call stack when, most of the time, they won't need it to be.
There are some platform-specific "extensions" that may get you what you want, like malloc_usable_size
on Linux and _msize
on Windows, though these assume that your allocator used malloc
and didn't do any other magic that may extend the size of the allocated block at the lowest level. I'd say you're still better off tracking this yourself if you really need it… or using a vector.
4
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
4
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
1
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctlydelete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size thatdelete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.
– Max Langhof
Feb 20 at 11:45
2
@MichelePiccolini You shouldn't really be usingdelete
ordelete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.
– Lightness Races in Orbit
Feb 20 at 11:57
2
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
|
show 9 more comments
It does - the allocator, or some implementation detail behind it, knows exactly what the size of the block is.
But that information is not provided to you or to the "code layer" of your program.
Could the language have been designed to do this? Sure! It's probably a case of "don't pay for what you don't use" — it's your responsibility to remember this information. After all, you know how much memory you asked for! Often times people will not want the cost of a number being passed up the call stack when, most of the time, they won't need it to be.
There are some platform-specific "extensions" that may get you what you want, like malloc_usable_size
on Linux and _msize
on Windows, though these assume that your allocator used malloc
and didn't do any other magic that may extend the size of the allocated block at the lowest level. I'd say you're still better off tracking this yourself if you really need it… or using a vector.
It does - the allocator, or some implementation detail behind it, knows exactly what the size of the block is.
But that information is not provided to you or to the "code layer" of your program.
Could the language have been designed to do this? Sure! It's probably a case of "don't pay for what you don't use" — it's your responsibility to remember this information. After all, you know how much memory you asked for! Often times people will not want the cost of a number being passed up the call stack when, most of the time, they won't need it to be.
There are some platform-specific "extensions" that may get you what you want, like malloc_usable_size
on Linux and _msize
on Windows, though these assume that your allocator used malloc
and didn't do any other magic that may extend the size of the allocated block at the lowest level. I'd say you're still better off tracking this yourself if you really need it… or using a vector.
edited Feb 20 at 10:28
answered Feb 20 at 10:19
Lightness Races in OrbitLightness Races in Orbit
292k52475808
292k52475808
4
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
4
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
1
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctlydelete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size thatdelete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.
– Max Langhof
Feb 20 at 11:45
2
@MichelePiccolini You shouldn't really be usingdelete
ordelete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.
– Lightness Races in Orbit
Feb 20 at 11:57
2
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
|
show 9 more comments
4
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
4
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
1
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctlydelete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size thatdelete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.
– Max Langhof
Feb 20 at 11:45
2
@MichelePiccolini You shouldn't really be usingdelete
ordelete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.
– Lightness Races in Orbit
Feb 20 at 11:57
2
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
4
4
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
@MaxLanghof "If I have to keep track of it, then I have to pay extra at some point" The key word is if.
– Lightness Races in Orbit
Feb 20 at 10:26
4
4
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
@MaxLanghof Admittedly I would be vaguely interested in finding out whether there's some crucial reason that this wasn't made part of the stdlib's allocation interface(s), that go beyond "we cba and don't think you should need this"
– Lightness Races in Orbit
Feb 20 at 10:32
1
1
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctly
delete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size that delete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.– Max Langhof
Feb 20 at 11:45
@MichelePiccolini The first question in your comment is unrelated to the topic at hand - correctly
delete
ing a pointer to an array if you already know it is a pointer to an array and being able to infer whether a pointer points to an array or a single object are two different, unrelated things. In other words, if there was an operator to infer the size that delete
uses it would most certainly have UB if used on non-array allocations, thus it wouldn't allow you to decide whether the pointer is to an array allocation or not.– Max Langhof
Feb 20 at 11:45
2
2
@MichelePiccolini You shouldn't really be using
delete
or delete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.– Lightness Races in Orbit
Feb 20 at 11:57
@MichelePiccolini You shouldn't really be using
delete
or delete
at all, though; that's for the insides of containers and smart pointers, which do have all this information nicely tucked away.– Lightness Races in Orbit
Feb 20 at 11:57
2
2
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
It might not know exactly, it might only know the size of the block it chose, which might tell it the size rounded up to the next multiple of 16 bytes, for example. Depending on the allocator of course. xD, @Handy999 already posted this as an answer before I commented.
– Peter Cordes
Feb 20 at 17:38
|
show 9 more comments
I think the reason for this is a confluence of three factors.
- C++ has a "you only pay for what you use" culture
- C++ started its life as a pre-processor for C and hence had to be built on top of what C offered.
- C++ is one of the most widely ported languages around. Features that make life difficult for existing ports are unlikely to get added.
C allows the programmer to free memory blocks without specifying the size of the memory block to free, but does not provide the programmer with any standard way to access the size of the allocation. Furthermore the actual amount of memory allocated may well be larger than the amount the programmer asked for.
Following the principle of "you only pay for what you use", C++ implementations implement new
differently for different types. Typically they only store the size if it is necessary to do so, usually because the type has a non-trivial destructor.
So while yes, enough information is stored to free the memory block, it would be very difficult to define a sane and portable API for accessing that information. Depending on the data type and platform, the actual requested size may be available (for types where the C++ implementation has to store it), only the actual allocated size may be available (for types where the C++ implementation does not have to store it on platforms where the underlying memory manager has an extension to get the allocated size), or the size may not be available at all (for types where the C++ implementation does not have to store it on platforms that don't provide access to the information from the underlying memory manager).
add a comment |
I think the reason for this is a confluence of three factors.
- C++ has a "you only pay for what you use" culture
- C++ started its life as a pre-processor for C and hence had to be built on top of what C offered.
- C++ is one of the most widely ported languages around. Features that make life difficult for existing ports are unlikely to get added.
C allows the programmer to free memory blocks without specifying the size of the memory block to free, but does not provide the programmer with any standard way to access the size of the allocation. Furthermore the actual amount of memory allocated may well be larger than the amount the programmer asked for.
Following the principle of "you only pay for what you use", C++ implementations implement new
differently for different types. Typically they only store the size if it is necessary to do so, usually because the type has a non-trivial destructor.
So while yes, enough information is stored to free the memory block, it would be very difficult to define a sane and portable API for accessing that information. Depending on the data type and platform, the actual requested size may be available (for types where the C++ implementation has to store it), only the actual allocated size may be available (for types where the C++ implementation does not have to store it on platforms where the underlying memory manager has an extension to get the allocated size), or the size may not be available at all (for types where the C++ implementation does not have to store it on platforms that don't provide access to the information from the underlying memory manager).
add a comment |
I think the reason for this is a confluence of three factors.
- C++ has a "you only pay for what you use" culture
- C++ started its life as a pre-processor for C and hence had to be built on top of what C offered.
- C++ is one of the most widely ported languages around. Features that make life difficult for existing ports are unlikely to get added.
C allows the programmer to free memory blocks without specifying the size of the memory block to free, but does not provide the programmer with any standard way to access the size of the allocation. Furthermore the actual amount of memory allocated may well be larger than the amount the programmer asked for.
Following the principle of "you only pay for what you use", C++ implementations implement new
differently for different types. Typically they only store the size if it is necessary to do so, usually because the type has a non-trivial destructor.
So while yes, enough information is stored to free the memory block, it would be very difficult to define a sane and portable API for accessing that information. Depending on the data type and platform, the actual requested size may be available (for types where the C++ implementation has to store it), only the actual allocated size may be available (for types where the C++ implementation does not have to store it on platforms where the underlying memory manager has an extension to get the allocated size), or the size may not be available at all (for types where the C++ implementation does not have to store it on platforms that don't provide access to the information from the underlying memory manager).
I think the reason for this is a confluence of three factors.
- C++ has a "you only pay for what you use" culture
- C++ started its life as a pre-processor for C and hence had to be built on top of what C offered.
- C++ is one of the most widely ported languages around. Features that make life difficult for existing ports are unlikely to get added.
C allows the programmer to free memory blocks without specifying the size of the memory block to free, but does not provide the programmer with any standard way to access the size of the allocation. Furthermore the actual amount of memory allocated may well be larger than the amount the programmer asked for.
Following the principle of "you only pay for what you use", C++ implementations implement new
differently for different types. Typically they only store the size if it is necessary to do so, usually because the type has a non-trivial destructor.
So while yes, enough information is stored to free the memory block, it would be very difficult to define a sane and portable API for accessing that information. Depending on the data type and platform, the actual requested size may be available (for types where the C++ implementation has to store it), only the actual allocated size may be available (for types where the C++ implementation does not have to store it on platforms where the underlying memory manager has an extension to get the allocated size), or the size may not be available at all (for types where the C++ implementation does not have to store it on platforms that don't provide access to the information from the underlying memory manager).
edited Feb 20 at 19:01
Toby Speight
16.9k134265
16.9k134265
answered Feb 20 at 15:06
plugwashplugwash
4,5361226
4,5361226
add a comment |
add a comment |
This answer applies to Microsoft Visual Studio only.
There is a function called _msize, which will return the malloced / calloced / realloced size of a pointer.
It can be found in the malloc.h header, and the parameters are:
size_t _msize(
void *memblock
);
I am not sure if there is an equivalent in gcc. There probably should be.
1
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
2
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
add a comment |
This answer applies to Microsoft Visual Studio only.
There is a function called _msize, which will return the malloced / calloced / realloced size of a pointer.
It can be found in the malloc.h header, and the parameters are:
size_t _msize(
void *memblock
);
I am not sure if there is an equivalent in gcc. There probably should be.
1
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
2
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
add a comment |
This answer applies to Microsoft Visual Studio only.
There is a function called _msize, which will return the malloced / calloced / realloced size of a pointer.
It can be found in the malloc.h header, and the parameters are:
size_t _msize(
void *memblock
);
I am not sure if there is an equivalent in gcc. There probably should be.
This answer applies to Microsoft Visual Studio only.
There is a function called _msize, which will return the malloced / calloced / realloced size of a pointer.
It can be found in the malloc.h header, and the parameters are:
size_t _msize(
void *memblock
);
I am not sure if there is an equivalent in gcc. There probably should be.
answered Feb 20 at 10:25
OwlOwl
656511
656511
1
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
2
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
add a comment |
1
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
2
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
1
1
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Ahh there is malloc_usable_size() for linux.
– Owl
Feb 20 at 10:42
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
Do you know if those are reliable or what could be some cases that might arise and might make the usage of these non reliable?
– Michele Piccolini
Feb 20 at 11:01
2
2
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
Well one of the things I noticed when I tested it, I called a malloc on an array of chars 25 long, then called malloc_usable_size on the array. It reported 40 bytes used. This suggests to me that it's actually reporting the container size allocated that is large enough to contain the array.
– Owl
Feb 20 at 14:37
add a comment |
If delete
doesn't have to know the size of the array at the time it is called, your entire argument falls apart. And delete
doesn't have to know the size of the array at the time it is called. It only needs to know the size to make the block available for use by others, and absolutely nothing requires it to make the block available for use by others at the time delete
is called.
For example, delete
my dice a large block into some number of smaller blocks. Each of those blocks but one need only have a pointer to the control block that knows the size. If any block but the control block is passed to delete
first, then delete
has no idea how big the block that was just freed is and won't know until later.
That it is not absolutely required that delete
know the size of a block at every arbitrary point during its lifetime is sufficient to invalidate your argument.
add a comment |
If delete
doesn't have to know the size of the array at the time it is called, your entire argument falls apart. And delete
doesn't have to know the size of the array at the time it is called. It only needs to know the size to make the block available for use by others, and absolutely nothing requires it to make the block available for use by others at the time delete
is called.
For example, delete
my dice a large block into some number of smaller blocks. Each of those blocks but one need only have a pointer to the control block that knows the size. If any block but the control block is passed to delete
first, then delete
has no idea how big the block that was just freed is and won't know until later.
That it is not absolutely required that delete
know the size of a block at every arbitrary point during its lifetime is sufficient to invalidate your argument.
add a comment |
If delete
doesn't have to know the size of the array at the time it is called, your entire argument falls apart. And delete
doesn't have to know the size of the array at the time it is called. It only needs to know the size to make the block available for use by others, and absolutely nothing requires it to make the block available for use by others at the time delete
is called.
For example, delete
my dice a large block into some number of smaller blocks. Each of those blocks but one need only have a pointer to the control block that knows the size. If any block but the control block is passed to delete
first, then delete
has no idea how big the block that was just freed is and won't know until later.
That it is not absolutely required that delete
know the size of a block at every arbitrary point during its lifetime is sufficient to invalidate your argument.
If delete
doesn't have to know the size of the array at the time it is called, your entire argument falls apart. And delete
doesn't have to know the size of the array at the time it is called. It only needs to know the size to make the block available for use by others, and absolutely nothing requires it to make the block available for use by others at the time delete
is called.
For example, delete
my dice a large block into some number of smaller blocks. Each of those blocks but one need only have a pointer to the control block that knows the size. If any block but the control block is passed to delete
first, then delete
has no idea how big the block that was just freed is and won't know until later.
That it is not absolutely required that delete
know the size of a block at every arbitrary point during its lifetime is sufficient to invalidate your argument.
answered Feb 21 at 1:24
David SchwartzDavid Schwartz
138k14144226
138k14144226
add a comment |
add a comment |
7
The underlying memory allocator knows the size of blocks it allocates, but there's no standard function that returns that information. There are sometimes non-standard malloc implementation specific ways but they're inherently non portable.
– Shawn
Feb 20 at 10:20
5
Why is this ever interesting? Use
std::vector
and never worry aboutdelete
.– n.m.
Feb 20 at 10:21
20
@n.m. I don't get answers that tell you "don't bother". If one asks a question, there are reasons behind it. And the reasons don't have necessarily to be "practical reasons". It might be curiosity, it might be that I want to implement a compiler in machine code, or whatever. Even if we suppose one should always be backed by practical reasons in order to ask questions, I'm sure it is always possible to come up with at least one or two cases in which new knowledge could be used. There might be cases in which I cannot or don't want to use STL.
– Michele Piccolini
Feb 20 at 10:42
3
"If one asks a question, there are reasons behind it." These reasons are not necessarily valid. If you want to learn how to use C++ efficiently then not bothering with delete and pointers is the right thing. They are too low level and there are gazillion of these low level things of zero importance. Trying to learn them all is a waste of time. If you are in a situation where you cannot use
std::vector
(which should be extremely rare) you might want to look inside a typical implementation to learn how it works in order to replicate some of it.– n.m.
Feb 20 at 11:10
12
@n.m. I don't think SO is limited to people "learning how to use C++ efficiently". Otherwise the language-lawyer tag would be pretty useless. Someone has to deal with all these "low level things", so why is asking about them on SO unreasonable?
– Max Langhof
Feb 20 at 11:50