> Assume I have a vector of points vector<obj*> v and I need to free the > memory. > More over, some of the element in v are already deleted (null > pointer).
a) Deleting the pointers will not invalidate the iterator. Thus, the idea in itself is sound.
b) The check for 0 before the delete is unnecessary: deleting a 0 pointer is a null-op.
c) You might want to do "*it = 0" after the delete: for one thing, you seem to be using the convention of signalling by 0 that memory has been deallocated (although that is a questionable practice); also it can be problematic to keep singular pointer values inside the vector: when the vector reallocates those values will be moved around and that causes undefined behavior.
zl2k <kdsfin...@gmail.com>, on 02/09/2010 11:48:41, wrote:
> hi, there,
> Assume I have a vector of points vector<obj*> v and I need to free the > memory. > More over, some of the element in v are already deleted (null > pointer).
>> Assume I have a vector of points vector<obj*> v and I need to free the >> memory. >> More over, some of the element in v are already deleted (null >> pointer).
> a) Deleting the pointers will not invalidate the iterator. Thus, the idea in > itself is sound.
> b) The check for 0 before the delete is unnecessary: deleting a 0 pointer is > a null-op.
> c) You might want to do "*it = 0" after the delete: for one thing, you seem > to be using the convention of signalling by 0 that memory has been > deallocated (although that is a questionable practice); also it can be > problematic to keep singular pointer values inside the vector: when the > vector reallocates those values will be moved around and that causes > undefined behavior.
Having invalid pointers into a vector of pointers and reallocating the vector really does call for UB? I'm not sure I'm catching the point (I've read that assertion before, but I still haven't wrapped my mind around this, I'd like to make it clear once for all).
Besides, the advice of zeroing the pointer is good, but the best would be to simply drop the vector or to clear it out, since all the pointers it contains are dangling or null, by now.
Francesco S. Carta wrote: > Kai-Uwe Bux <jkherci...@gmx.net>, on 02/09/2010 20:59:32, wrote:
>> zl2k wrote:
>>> hi, there,
>>> Assume I have a vector of points vector<obj*> v and I need to free the >>> memory. >>> More over, some of the element in v are already deleted (null >>> pointer).
>> a) Deleting the pointers will not invalidate the iterator. Thus, the idea >> in itself is sound.
>> b) The check for 0 before the delete is unnecessary: deleting a 0 pointer >> is a null-op.
>> c) You might want to do "*it = 0" after the delete: for one thing, you >> seem to be using the convention of signalling by 0 that memory has been >> deallocated (although that is a questionable practice); also it can be >> problematic to keep singular pointer values inside the vector: when the >> vector reallocates those values will be moved around and that causes >> undefined behavior.
> Having invalid pointers into a vector of pointers and reallocating the > vector really does call for UB?
It does in C++03. I am not sure about C++0x as that will have move semantics. As of now, reallocating a vector involves assignment or copy- construction, either of which necessitate an lvalue-rvalue conversion (maybe a move will do that too). Although the language in [4.1/1] leaves out singular values, it seems pretty clear that the intent is to make it UB to invoke an lvalue-rvalue conversion on an invalid pointer. Under the hoods, that happens when a vector of pointers reallocates and an invalid pointer is involved.
There is some discussion / disagreement / open question as to whether _purely and absolutely formally_ even the mere existence of singular values in a pointer triggers undefined behavior. A common sense reaction is, however, to not bother unless actual operations on the vector are performed that may reasonably invoke UB. This suffices to stay clear of UB on all implementations known to man (or at least in this forum, nobody ever pointed out a less sane implementation).
> I'm not sure I'm catching the point > (I've read that assertion before, but I still haven't wrapped my mind > around this, I'd like to make it clear once for all).
> Besides, the advice of zeroing the pointer is good, but the best would > be to simply drop the vector or to clear it out, since all the pointers > it contains are dangling or null, by now.
I felt the urge to point out possible pitfalls since we do not know what happens to the vector afterwards. I agree that the cleanest would be, to clear out the vector or to have it destroyed.
>> Kai-Uwe Bux<jkherci...@gmx.net>, on 02/09/2010 20:59:32, wrote:
>>> zl2k wrote:
>>>> hi, there,
>>>> Assume I have a vector of points vector<obj*> v and I need to free the >>>> memory. >>>> More over, some of the element in v are already deleted (null >>>> pointer).
>>> a) Deleting the pointers will not invalidate the iterator. Thus, the idea >>> in itself is sound.
>>> b) The check for 0 before the delete is unnecessary: deleting a 0 pointer >>> is a null-op.
>>> c) You might want to do "*it = 0" after the delete: for one thing, you >>> seem to be using the convention of signalling by 0 that memory has been >>> deallocated (although that is a questionable practice); also it can be >>> problematic to keep singular pointer values inside the vector: when the >>> vector reallocates those values will be moved around and that causes >>> undefined behavior.
>> Having invalid pointers into a vector of pointers and reallocating the >> vector really does call for UB?
> It does in C++03. I am not sure about C++0x as that will have move > semantics. As of now, reallocating a vector involves assignment or copy- > construction, either of which necessitate an lvalue-rvalue conversion (maybe > a move will do that too). Although the language in [4.1/1] leaves out > singular values, it seems pretty clear that the intent is to make it UB to > invoke an lvalue-rvalue conversion on an invalid pointer. Under the hoods, > that happens when a vector of pointers reallocates and an invalid pointer is > involved.
> There is some discussion / disagreement / open question as to whether > _purely and absolutely formally_ even the mere existence of singular values > in a pointer triggers undefined behavior. A common sense reaction is, > however, to not bother unless actual operations on the vector are performed > that may reasonably invoke UB. This suffices to stay clear of UB on all > implementations known to man (or at least in this forum, nobody ever pointed > out a less sane implementation).
I see, I've also read some threads I've found hanging here and there, it seems that this issue gets raised from time to time and the debates that it generates might not be worth the time spent on it - as the practical advantages of such a case being "well defined" would be around zero.
Now I've understood the issue and I'm happy considering as "definitive UB" the fact of doing anything with a dangling pointer other than zeroing it or assigning a valid pointer to it, thank you for your explanation Kai-Uwe.
> It does in C++03. I am not sure about C++0x as that will have move > semantics. As of now, reallocating a vector involves assignment or copy- > construction, either of which necessitate an lvalue-rvalue conversion (maybe > a move will do that too). Although the language in [4.1/1] leaves out > singular values, it seems pretty clear that the intent is to make it UB to > invoke an lvalue-rvalue conversion on an invalid pointer. Under the hoods, > that happens when a vector of pointers reallocates and an invalid pointer is > involved.
> There is some discussion / disagreement / open question as to whether > _purely and absolutely formally_ even the mere existence of singular values > in a pointer triggers undefined behavior. A common sense reaction is, > however, to not bother unless actual operations on the vector are performed > that may reasonably invoke UB. This suffices to stay clear of UB on all > implementations known to man (or at least in this forum, nobody ever pointed > out a less sane implementation).
>> I'm not sure I'm catching the point >> (I've read that assertion before, but I still haven't wrapped my mind >> around this, I'd like to make it clear once for all).
>> Besides, the advice of zeroing the pointer is good, but the best would >> be to simply drop the vector or to clear it out, since all the pointers >> it contains are dangling or null, by now.
> I felt the urge to point out possible pitfalls since we do not know what > happens to the vector afterwards. I agree that the cleanest would be, to > clear out the vector or to have it destroyed.
That's curious. And it doesn't fit with my mental model of how the language works under the hood.
I have no trouble understanding why accessing through an invalid pointer is UB.
In the case of the vector of pointers (vector<Obj*> in the OP) why would copying an entry cause the pointer to be de-referenced?
And what would happen if the data in the vector was a union, say of an int and a pointer - which are the same size in most architectures?
>> It does in C++03. I am not sure about C++0x as that will have move >> semantics. As of now, reallocating a vector involves assignment or copy- >> construction, either of which necessitate an lvalue-rvalue conversion >> (maybe >> a move will do that too). Although the language in [4.1/1] leaves out >> singular values, it seems pretty clear that the intent is to make it >> UB to >> invoke an lvalue-rvalue conversion on an invalid pointer. Under the >> hoods, >> that happens when a vector of pointers reallocates and an invalid >> pointer is >> involved.
>> There is some discussion / disagreement / open question as to whether >> _purely and absolutely formally_ even the mere existence of singular >> values >> in a pointer triggers undefined behavior. A common sense reaction is, >> however, to not bother unless actual operations on the vector are >> performed >> that may reasonably invoke UB. This suffices to stay clear of UB on all >> implementations known to man (or at least in this forum, nobody ever >> pointed >> out a less sane implementation).
>>> I'm not sure I'm catching the point >>> (I've read that assertion before, but I still haven't wrapped my mind >>> around this, I'd like to make it clear once for all).
>>> Besides, the advice of zeroing the pointer is good, but the best would >>> be to simply drop the vector or to clear it out, since all the pointers >>> it contains are dangling or null, by now.
>> I felt the urge to point out possible pitfalls since we do not know what >> happens to the vector afterwards. I agree that the cleanest would be, to >> clear out the vector or to have it destroyed.
> That's curious. And it doesn't fit with my mental model of how the > language works under the hood.
> I have no trouble understanding why accessing through an invalid pointer > is UB.
> In the case of the vector of pointers (vector<Obj*> in the OP) why would > copying an entry cause the pointer to be de-referenced?
> And what would happen if the data in the vector was a union, say of an > int and a pointer - which are the same size in most architectures?
Dereferencing an invalid pointer is not the only UB with regards to invalid pointers. Simply reading the value is UB, AIUI.
The only defined operations on an invalid pointer: set it to null, or set it to a valid location.
Andy Champ wrote: > On 02/09/2010 20:36, Kai-Uwe Bux wrote:
>> It does in C++03. I am not sure about C++0x as that will have move >> semantics. As of now, reallocating a vector involves assignment or copy- >> construction, either of which necessitate an lvalue-rvalue conversion >> (maybe a move will do that too). Although the language in [4.1/1] leaves >> out singular values, it seems pretty clear that the intent is to make it >> UB to invoke an lvalue-rvalue conversion on an invalid pointer. Under the >> hoods, that happens when a vector of pointers reallocates and an invalid >> pointer is involved.
>> There is some discussion / disagreement / open question as to whether >> _purely and absolutely formally_ even the mere existence of singular >> values in a pointer triggers undefined behavior. A common sense reaction >> is, however, to not bother unless actual operations on the vector are >> performed that may reasonably invoke UB. This suffices to stay clear of >> UB on all implementations known to man (or at least in this forum, nobody >> ever pointed out a less sane implementation).
>>> I'm not sure I'm catching the point >>> (I've read that assertion before, but I still haven't wrapped my mind >>> around this, I'd like to make it clear once for all).
>>> Besides, the advice of zeroing the pointer is good, but the best would >>> be to simply drop the vector or to clear it out, since all the pointers >>> it contains are dangling or null, by now.
>> I felt the urge to point out possible pitfalls since we do not know what >> happens to the vector afterwards. I agree that the cleanest would be, to >> clear out the vector or to have it destroyed.
> That's curious. And it doesn't fit with my mental model of how the > language works under the hood.
> I have no trouble understanding why accessing through an invalid pointer > is UB.
> In the case of the vector of pointers (vector<Obj*> in the OP) why would > copying an entry cause the pointer to be de-referenced?
It does not. However, dereferencing an invalid pointer is not the only way of using an invalid pointer to yield UB. See [4.1/1].
Consider:
int* p = new int(); delete p; int* q ( p );
That is UB even though p is not dereferenced. Similarly, and for essentially the same reason, the following is UB:
int* p; int* q ( p );
> And what would happen if the data in the vector was a union, say of an > int and a pointer - which are the same size in most architectures?
Still UB. If you write a pointer to the union, even reading the value as an int is UB. If the pointer value is singular, copying is UB even if the value, interpreted as an int, would not be singular. The point is that the union at this point holds an object of type int* with a singular value. That triggers [4.1/1]. Anyhow, a conforming program can not see that the value as an int would be regular for reading that value is UB.
> > Assume I have a vector of points vector<obj*> v and I need to free the > > memory. > > More over, some of the element in v are already deleted (null > > pointer).
> a) Deleting the pointers will not invalidate the iterator. Thus, the idea in > itself is sound.
> b) The check for 0 before the delete is unnecessary: deleting a 0 pointer is > a null-op.
> c) You might want to do "*it = 0" after the delete: for one thing, you seem > to be using the convention of signalling by 0 that memory has been > deallocated (although that is a questionable practice); also it can be > problematic to keep singular pointer values inside the vector: when the > vector reallocates those values will be moved around and that causes > undefined behavior.
> Best
> Kai-Uwe Bux
You said : also it can be problematic to keep singular pointer values inside the vector
> You said : also it can be > problematic to keep singular pointer values inside the vector
> How can this be solved?
By not doing it :-)
Depending on the case, this ranges from easy to very hard. The easy case is when the vector contains pointers whose values occur nowhere else in the program (or the vector is the sole owner of all pointees of its elements). In this case, you have control over invalidating operations and you can remove singular values from the vector as (or before) they arise.
Problematic is when pointer values inside the vector can be invalidated by operations elsewhere. This is a general problem with pointers. If you have two pointer object sharing the same value, calling delete on one of them also invalidates the value of the other. Even if you destroy or null the pointer which you delete, the other hangs around just waiting to cause UB. This is especially tricky as you have no way of telling by looking at a pointer whether its value is singular. So, the best thing is to take care of all "aliases" of a pointer at the time you want to delete one of them. How to handle this problem, however, depends on the application. The main lesson here is that the vector<T*> case is not really special.
> Assume I have a vector of points vector<obj*> v and I need to free the > memory. > More over, some of the element in v are already deleted (null > pointer).
> Is it ok if I do like this:
> for (vector<Obj*>::iterator it(v.begin()), ite(v.end()); it!= ite; + > +it{ > if (*it != 0){ > delete *it; > }
> }
I know of no implementation where this won't work, but I think a better thing is:
// Everyone and their mother already have // a variant of code below, right? RIGHT? ;-) template<typename ptr> void del_null(ptr& val) { ptr temp = val; val = 0; delete temp;
}
template<class container> void del_null_container_elements(container& c) { std::for_each(c.begin(), c.end(), &del_null<container::value_type>);
}
template<class container> void del_null_container_elements(container& c) { delete_container_elements(c); c.clear();
> Besides, the advice of zeroing the pointer is good, but the best would > be to simply drop the vector or to clear it out, since all the pointers > it contains are dangling or null, by now.
It is not. As other said, it is UB.
Better way would be to use something like SharedPtr instead of raw pointer. Then you can remove the element, and let the smart pointer do the deallocation.
Vladimir Jovic <vladasp...@gmail.com>, on 03/09/2010 10:18:51, wrote:
> Francesco S. Carta wrote:
>> Besides, the advice of zeroing the pointer is good, but the best would >> be to simply drop the vector or to clear it out, since all the >> pointers it contains are dangling or null, by now.
> It is not. As other said, it is UB.
What "it is not", and what "it is UB", regarding the text you quoted from me? What I suggested above is exactly about _avoiding_ UB.
> Better way would be to use something like SharedPtr instead of raw > pointer. Then you can remove the element, and let the smart pointer do > the deallocation.
Francesco S. Carta wrote: > Vladimir Jovic <vladasp...@gmail.com>, on 03/09/2010 10:18:51, wrote:
>> Francesco S. Carta wrote:
[Snipped too much - restoring now]
> c) You might want to do "*it = 0" after the delete: for one thing, > you seem to be using the convention of signalling by 0 that memory
> has been deallocated (although that is a questionable practice);
> also it can be problematic to keep singular pointer values inside > the vector: when the vector reallocates those values will be moved > around and that causes undefined behavior.
>>> Besides, the advice of zeroing the pointer is good, but the best would >>> be to simply drop the vector or to clear it out, since all the >>> pointers it contains are dangling or null, by now.
>> It is not. As other said, it is UB.
> What "it is not", and what "it is UB", regarding the text you quoted > from me? What I suggested above is exactly about _avoiding_ UB.
Advise to use raw pointers is bad. Specially to do the suggestion c, as that is UB (nicely explained by Kai-Uwe Bux). It would most likely work as expected, but using raw pointers complicates code.
> Francesco S. Carta wrote: >> Vladimir Jovic <vladasp...@gmail.com>, on 03/09/2010 10:18:51, wrote:
>>> Francesco S. Carta wrote:
> [Snipped too much - restoring now]
>> c) You might want to do "*it = 0" after the delete: for one thing, >> you seem to be using the convention of signalling by 0 that memory > > has been deallocated (although that is a questionable practice); >> also it can be problematic to keep singular pointer values inside the >> vector: when the vector reallocates those values will be moved around >> and that causes undefined behavior.
>>>> Besides, the advice of zeroing the pointer is good, but the best would >>>> be to simply drop the vector or to clear it out, since all the >>>> pointers it contains are dangling or null, by now.
>>> It is not. As other said, it is UB.
>> What "it is not", and what "it is UB", regarding the text you quoted >> from me? What I suggested above is exactly about _avoiding_ UB.
> Advise to use raw pointers is bad. Specially to do the suggestion c, as > that is UB (nicely explained by Kai-Uwe Bux). It would most likely work > as expected, but using raw pointers complicates code.
Ah, I understand now. Yes, raw pointers are dangerous, but are sometimes a necessary evil, paraphrasing the C++ FAQ.
I think I've found something interesting about all of this stuff, I'll post it as a separate topic because I think it needs the appropriate attention.
> it can be > problematic to keep singular pointer values inside the vector: when the > vector reallocates those values will be moved around and that causes > undefined behavior.
Here the term "singular value" is used to mean "invalid pointer", as it also is some places in the standard.
However AFAICS it's not defined in the standard.
Is it defined? And what the heck does it /mean/ (that leads to its use for invalid pointer or iterator values)?
Seungbeom Kim <musip...@bawi.org>, on 06/09/2010 01:50:09, wrote:
> On 2010-09-02 15:50, Daniel Pitts wrote:
>> The only defined operations on an invalid pointer: set it to null, or >> set it to a valid location.
> ... or destroy it, probably. :) > (Otherwise, { int* p; } would invoke UB... or does it?)
No it doesn't, because there is no lvalue to rvalue conversion in your example.
> So, do we really need to set the pointers to null before destroying > the vector?
As it seems, formally, yes [*], we need to nullify them, because the actions performed by the vector during reallocation or during destruction might lead to that lvalue to rvalue conversion, thus calling UB into play.
In practice there seems to be no danger on most architectures, but read all the other posts to get deeper insight, and read the "UB while dealing with invalid raw pointers, the std::uninitialized_fill case" thread too (Bo Persson's posts in particular).
[*] In layman terms, the point is that a pointer pointing to appropriately allocated space (even if that space doesn't contain any valid object) is safe to be copied and compared (but not dereferenced), while a pointer pointing to {unallocated/random/deallocated} space is a timed bomb. But once more, see the other posts from more knowledgeable ones.
On Sep 6, 10:16 am, "Francesco S. Carta" <entul...@gmail.com> wrote:
> Seungbeom Kim <musip...@bawi.org>, on 06/09/2010 01:50:09, wrote: > > So, do we really need to set the pointers to null before destroying > > the vector? > As it seems, formally, yes [*], we need to nullify them, > because the actions performed by the vector during > reallocation or during destruction might lead to that lvalue > to rvalue conversion, thus calling UB into play.
Formally, you need to nullify the pointer in the vector before deleting it, since nullifying it (assigning NULL to it) involves accessing into the vector, which could theoretically lead to an lvalue to rvalue conversion. Practically, of course, there won't be any problems, even on architectures where the lvalue to rvalue conversion can cause problems.
On Sep 3, 11:12 am, "Francesco S. Carta" <entul...@gmail.com> wrote:
> Vladimir Jovic <vladasp...@gmail.com>, on 03/09/2010 12:01:45, wrote:
[...]
> Ah, I understand now. Yes, raw pointers are dangerous, but are > sometimes a necessary evil, paraphrasing the C++ FAQ.
This is a widespread myth. Globally, raw pointers are probably less dangerous than std::shared_ptr; at least, the object doesn't get deleted unless you explicitly ask for it. Other shared pointers fare better, but in any reasonable application, most pointers are used for navigation, and there's absolutely no reason to use a smart pointer for that.
James Kanze <james.ka...@gmail.com>, on 06/09/2010 10:10:33, wrote:
> On Sep 6, 10:16 am, "Francesco S. Carta"<entul...@gmail.com> wrote: >> Seungbeom Kim<musip...@bawi.org>, on 06/09/2010 01:50:09, wrote:
>>> So, do we really need to set the pointers to null before destroying >>> the vector?
>> As it seems, formally, yes [*], we need to nullify them, >> because the actions performed by the vector during >> reallocation or during destruction might lead to that lvalue >> to rvalue conversion, thus calling UB into play.
> Formally, you need to nullify the pointer in the vector before > deleting it, since nullifying it (assigning NULL to it) involves > accessing into the vector, which could theoretically lead to an > lvalue to rvalue conversion.
So to be formally fine I need something like this...
...or variations thereof. Thank you for the correction.
> Practically, of course, there > won't be any problems, even on architectures where the lvalue to > rvalue conversion can cause problems.
And you say that because you're confident that most decent implementations out there will not perform the conversion during the element assignment - otherwise, your mention about program crashes, else-thread, would conflict with this statement.
Francesco S. Carta wrote: > James Kanze <james.ka...@gmail.com>, on 06/09/2010 10:10:33, wrote:
>> On Sep 6, 10:16 am, "Francesco S. Carta"<entul...@gmail.com> wrote: >>> Seungbeom Kim<musip...@bawi.org>, on 06/09/2010 01:50:09, wrote:
>>>> So, do we really need to set the pointers to null before destroying >>>> the vector?
>>> As it seems, formally, yes [*], we need to nullify them, >>> because the actions performed by the vector during >>> reallocation or during destruction might lead to that lvalue >>> to rvalue conversion, thus calling UB into play.
>> Formally, you need to nullify the pointer in the vector before >> deleting it, since nullifying it (assigning NULL to it) involves >> accessing into the vector, which could theoretically lead to an >> lvalue to rvalue conversion.
> So to be formally fine I need something like this...
> ...or variations thereof. Thank you for the correction.
Well, that is a somewhat contested issue. More importantly, though, it is based upon an interpretation of language that is subject to heavy changes in C++0x. My preliminary understanding of is that a contorted work-around like the above is not necessary under the C++0x rules.
>> Practically, of course, there >> won't be any problems, even on architectures where the lvalue to >> rvalue conversion can cause problems.
> And you say that because you're confident that most decent > implementations out there will not perform the conversion during the > element assignment - otherwise, your mention about program crashes, > else-thread, would conflict with this statement.
An implementation would have to be deliberately mean and waste resources on being mean to trigger lvalue-rvalue conversions for no reason.
>> James Kanze<james.ka...@gmail.com>, on 06/09/2010 10:10:33, wrote:
>>> On Sep 6, 10:16 am, "Francesco S. Carta"<entul...@gmail.com> wrote: >>>> Seungbeom Kim<musip...@bawi.org>, on 06/09/2010 01:50:09, wrote:
>>>>> So, do we really need to set the pointers to null before destroying >>>>> the vector?
>>>> As it seems, formally, yes [*], we need to nullify them, >>>> because the actions performed by the vector during >>>> reallocation or during destruction might lead to that lvalue >>>> to rvalue conversion, thus calling UB into play.
>>> Formally, you need to nullify the pointer in the vector before >>> deleting it, since nullifying it (assigning NULL to it) involves >>> accessing into the vector, which could theoretically lead to an >>> lvalue to rvalue conversion.
>> So to be formally fine I need something like this...
>> ...or variations thereof. Thank you for the correction.
> Well, that is a somewhat contested issue. More importantly, though, it is > based upon an interpretation of language that is subject to heavy changes in > C++0x. My preliminary understanding of is that a contorted work-around like > the above is not necessary under the C++0x rules.
For your very words, it has to be proved formally necessary even in C++03, for that matter.
>>> Practically, of course, there >>> won't be any problems, even on architectures where the lvalue to >>> rvalue conversion can cause problems.
>> And you say that because you're confident that most decent >> implementations out there will not perform the conversion during the >> element assignment - otherwise, your mention about program crashes, >> else-thread, would conflict with this statement.
> An implementation would have to be deliberately mean and waste resources on > being mean to trigger lvalue-rvalue conversions for no reason.
Uhm... the standard does not forbid implementations to be mean, does it? ehehehehe ;-)
Kidding apart, I'm just interested in the process of clarifying dark corners of the standard, in these very posts, and about this, I'd like to read some elaboration on your first sentence above.
If I understand you correctly, your reading of the standard does not allow a std::vector implementation to perform lvalue to rvalue conversions during element assignment, is that what you meant?