15

Turns out many innocently looking things are undefined behavior in C++. For example, once a non-null pointer has been delete'd even printing out that pointer value is undefined behavior.

Now memory leaks are definitely bad. But what class situation are they - defined, undefined or what other class of behavior?

Community
  • 1
  • 1
sharptooth
  • 159,303
  • 82
  • 478
  • 911
  • See also http://stackoverflow.com/questions/9971559/why-is-not-deleting-an-object-that-has-a-destructor-with-a-side-effect-undefined – M.M Sep 01 '14 at 05:31

14 Answers14

29

Memory leaks.

There is no undefined behavior. It is perfectly legal to leak memory.

Undefined behavior: is actions the standard specifically does not want to define and leaves upto the implementation so that it is flexible to perform certain types of optimizations without breaking the standard.

Memory management is well defined.
If you dynamically allocate memory and don't release it. Then the memory remains the property of the application to manage as it sees fit. The fact that you have lost all references to that portion of memory is neither here nor there.

Of course if you continue to leak then you will eventually run out of available memory and the application will start to throw bad_alloc exceptions. But that is another issue.

Community
  • 1
  • 1
Martin York
  • 234,851
  • 74
  • 306
  • 532
  • The 2003 standard 3.7.3.2/4 says "The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined.33)" depending on your definition of "using" (which may not mean just dereferencing), then it could be considered invalid to just inspect the value of an invalid pointer. – Evan Teran Dec 30 '09 at 07:28
  • @Evan: Agreed, that is what the standard says. But (the way I read it) in this context using means using the object that was pointed at by the pointer. It does not imply that using the pointer 'value' is undefined behavior. If you take your argument to a natural conclusion and apply it to the NULL pointer! The NULL pointer is technically an invalid pointer, yet as long as you do not de-reference the pointer using its 'value' is well defined. – Martin York Dec 30 '09 at 07:37
  • I agree with what you are saying (and lets face it, i've never seen a machine where printing an invalid pointer did anything negative)... but, the standard also has a ton of rules spelling out how the NULL pointer may be compared and converted. I think this is simply an area where they could have been far more specific (especially since there are tons of people in the "you can't even look at an invalid pointer portably" camp. Oh well. – Evan Teran Dec 30 '09 at 07:46
  • 1
    @Evan: Withdraw my objects. After reading the accepted answer to the other question refereed to in the question. I now see how it can be undefined behavior and am removing that part of my answer (completely embarrassed). I am leaving the comments as they provide usefull information for others. – Martin York Dec 30 '09 at 07:49
  • *"Of course if you continue to leak then you will eventually run out of available memory and the application will start to throw bad_alloc exceptions."* In my experience, what really happens is that the process gets bigger and bigger and bigger and the system slowly grinds to a halt. But, yeah. – Jason Orendorff Dec 30 '09 at 15:52
  • @Jason: Then you have a very limited experience. – Martin York Dec 30 '09 at 19:21
  • I'm not quite sure how to interpret it, but does paragraph 3.8.4 of C++03 (and the latest draft) not state this is undefined behaviour? – Anton Golov Mar 04 '14 at 15:06
  • @AntonGolov: What version of the standard are you looking at? Looking at `n3797 (the latest version I have downloaded)`. There is no section 3.8.4. There is Section 3.8. In which paragraph 4 has `the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior` which is not the same as saying leaking is `undefined behavior`. But maybe your version said something else its hard to tell. – Martin York Mar 05 '14 at 01:46
  • @AntonGolov: Latest version [n3936](http://stackoverflow.com/a/4653479/14065) is now available. and says the same thing – Martin York Mar 05 '14 at 01:53
  • @LokiAstari: Yes, I meant that paragraph (which seems to have gone unchanged). It seems to claim that under certain conditions, not explicitly `delete`ing the object (nor calling the destructor) is UB; I'm still unsure how to interpret it, though, so I'm probably misreading. – Anton Golov Mar 05 '14 at 08:33
  • @AntonGolov: It may have gone unchanged but that's hard to know without going back and checking and not knowing where to start from a fruitless exercise. Best to always quote the version of the standard. – Martin York Mar 05 '14 at 17:50
  • @AntonGolov: I think the statement is clear. Leaking memory is **NOT** undefined behavior. But it also means that destructors will not get run. If you really on side affects from destructors and they are not run then you are in undefined behavior. Which is the same as saying if your code relies on a method being called and you don't call it then its undefined behavior. – Martin York Mar 05 '14 at 17:54
  • @Loki: That isn't undefined behavior either. The Standard clearly defines that code which isn't called, doesn't execute. It is simply a *logic bug*. – Ben Voigt Jun 10 '14 at 15:46
  • @BenVoigt: If you are referring too: `If you really on side affects from destructors and they are not run then you are in undefined behavior.` Yep I agree. I would rephrase as "undefined territory" :-). – Martin York Jun 10 '14 at 18:13
7

Memory leaks are definitely defined in C/C++.

If I do:

int *a = new int[10];

followed by

a = new int[10]; 

I'm definitely leaking memory as there is no way to access the 1st allocated array and this memory is not automatically freed as GC is not supported.

But the consequences of this leak are unpredictable and will vary from application to application and from machine to machine for a same given application. Say an application that crashes out due to leaking on one machine might work just fine on another machine with more RAM. Also for a given application on a given machine the crash due to leak can appear at different times during the run.

gameover
  • 10,862
  • 16
  • 55
  • 69
  • 1
    An application is not going to crash with the code you have given above. It will eventually run out of memory, though. – Tarydon Dec 30 '09 at 07:08
  • Unknown and arbitrary consequences sound like the very definition of Undefined Behavior. – joshperry Dec 30 '09 at 07:10
  • 1
    @Joeshperry: Undefined Behavior: is a very specific term defined in the standard. It means the standard specifically does not specify the meaning of what will happen so that the implementation has enough flexibility to perform optimizations and generate appropriate optimal code. Thus arbitrary consequences is not related to undefined behavior (as defined by the standard). – Martin York Dec 30 '09 at 07:25
  • 1
    @avakar: gameover is sort of on to something, though. Leaking memory is not undefined behavior, and running out of memory is also not undefined behavior. But *in practice* OSes frequently cause the C++ implementation to violate the standard if they run out of memory. For example they might over-commit memory, or the OS might inexplicably grind to a halt or fail internally as a consequence of application-level memory use. That's nothing to do with leaks in particular, though, just using all the memory. – Steve Jessop Dec 30 '09 at 14:16
  • Tarydon: In extreme situations the Linux kernel can kill a process that's just using too much memory and not doing anything else wrong. http://linux-mm.org/OOM_Killer It doesn't crash exactly; the system shoots it down. – Jason Orendorff Dec 30 '09 at 15:56
  • Steve Jessop, you are completely correct, but I don't see your point. The fact that Linux tends to kill processes for using too much memory has little to do with memory leaks. – avakar Dec 30 '09 at 19:08
  • The consequence of a persistent memory leak is that you eventually run out of memory. The consequence of running out of memory in practice might be unpredictable behaviour, because OSes are sometimes flakey in that respect. Being shot down cleanly is close to the "good" end of the spectrum. If A causes B, I'm not sure whether that means B has anything to do with A or not, but my point is just that gameover's warning is worth knowing about. It may or may not strictly be an answer to the question posed. – Steve Jessop Dec 30 '09 at 20:02
  • Steve, sorry, I explained myself poorly: the leaked and properly managed memory are the same from the perspective of the Linux out-of-memory killer. If you keep piling up leaks, the result will be the same as if you keep enlarging a vector. Leaks are not the only possible cause of the process being terminated on Linux. – avakar Dec 30 '09 at 22:39
  • Yes, there are other ways to guarantee that you'll run out of memory eventually. A persistent memory leak is just the most obvious ;-) – Steve Jessop Dec 30 '09 at 23:46
  • Yes, so it's my point that it's not the leak that's making the execution "unpredictable" on Linux. – avakar Dec 31 '09 at 00:23
  • It is if the app would not otherwise use up all the memory. Likewise, if someone puts a 10M array on the stack, you could say that's not what causes them to run out of stack, because maybe their app would have run out of stack anyway. Since it's a purely hypothetical app, with no known properties other than a memory leak, it's really impossible to speculate what "the" cause is of anything it does. So I can't say you're wrong... – Steve Jessop Dec 31 '09 at 01:03
  • Well, you convinced me. You probably would have convinced me yesterday, if I were thinking clearly enough :) – avakar Dec 31 '09 at 09:30
7

If you leak memory, execution proceeds as if nothing happens. This is defined behavior.

Down the track, you may find that a call to malloc fails due to there not being enough available memory. But this is a defined behavior of malloc, and the consequences are also well-defined: the malloc call returns NULL.

Now this may cause a program that doesn't check the result of malloc to fail with a segmentation violation. But that undefined behavior is (from the POV of the language specs) due to the program dereferencing an invalid pointer, not the earlier memory leak or the failed malloc call.

Stephen C
  • 632,615
  • 86
  • 730
  • 1,096
6

My interpretation of this statement:

For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

is as follows:

If you somehow manage to free the storage which the object occupies without calling the destructor on the object that occupied the memory, UB is the consequence, if the destructor is non-trivial and has side-effects.

If new allocates with malloc, the raw storage could be released with free(), the destructor would not run, and UB would result. Or if a pointer is cast to an unrelated type and deleted, the memory is freed, but the wrong destructor runs, UB.

This is not the same as an omitted delete, where the underlying memory is not freed. Omitting delete is not UB.

alain
  • 11,488
  • 2
  • 28
  • 47
  • 1
    The key phrase is "any program that depends on the side effects produced by the destructor". Most programs don't, and the destructor of `std::string` has no observable side effects, so there's no way undefined behavior can occur in the example. (Freeing memory is _not_ an observable side effect.) – James Kanze Jun 10 '14 at 10:51
  • "if you somehow manage to ***free*** the storage which the object occupies ***without*** calling the ***destructor*** on the object that occupied the memory, UB is the consequence" - how can that reconcile with "***not required*** to call the ***destructor*** explicitly ***before*** the storage which the object occupies is reused or ***released***". IMHO, this part of the Standard is intended to permit reclaiming custom memory pools without calling individual object destructors - the exact thing you're saying is UB. – Tony Delroy Jun 10 '14 at 11:05
  • I think it means it's ok to free the storage without calling the destructor **only if** the destructor is trivial, or has no side-effects. I'm adding that to the answer. – alain Jun 10 '14 at 11:27
  • I find the wording and intent horribly unclear so it's fair enough you have your take on it, but for whatever it's worth, summarising leads me to: "if !trivial explicit-destruction-not-required before release", i.e. destructor isn't required *even when* non-trivial, and there's nothing at all about the trivial destructor case; the entire paragraph seems to address only objects with non-trivial destructors. I can't reconcile this with your interpretation. – Tony Delroy Jun 10 '14 at 12:03
  • The abstract machine that a C++ program represents ceases to exist when the program ends; as such, dynamically-allocated memory is absolutely released back into the universe (in our implementations, our OS) at that time, even if only implicitly. 3.8/4 _is_ relevant. – Lightness Races in Orbit Jun 10 '14 at 12:19
  • The underlying memory is always freed... at process exit. – Ben Voigt Jun 10 '14 at 22:27
  • I think I get your point now, Ben Voigt and @LightnessRacesinOrbit. Are you saying: a program that fails to call every destructor with side-effects on which it depends, has UB? I would have assumed the authors would have made this more explicit, if this was what they meant, but that's only speculation, of course. And granted, I can not explain how it could make sense to have "memory reuse, no destructor call" being UB, but "no mem reuse, no destructor call" not being UB. It just seemed to me this is what the paragraph says. – alain Jun 11 '14 at 10:31
  • @alain: Yes, and it's perfectly explicit. In fact it says it pretty much the same way you just did! – Lightness Races in Orbit Jun 11 '14 at 11:25
  • Heads-up: this answer has been moved here from http://stackoverflow.com/questions/24137006/does-a-memory-leak-cause-undefined-behaviour – Shog9 Jul 11 '14 at 18:34
4

(Comment below "Heads-up: this answer has been moved here from Does a memory leak cause undefined behaviour?" - you'll probably have to read that question to get proper background for this answer O_o).

It seems to me that this part of the Standard explicitly permits:

  • having a custom memory pool that you placement-new objects into, then release/reuse the whole thing without spending time calling their destructors, as long as you don't depend on side-effects of the object destructors.

  • libraries that allocate a bit of memory and never release it, probably because their functions/objects could be used by destructors of static objects and registered on-exit handlers, and it's not worth buying into the whole orchestrated-order-of-destruction or transient "phoenix"-like rebirth each time those accesses happen.

I can't understand why the Standard chooses to leave the behaviour undefined when there are dependencies on side effects - rather than simply say those side effects won't have happened and let the program have defined or undefined behaviour as you'd normally expect given that premise.

We can still consider what the Standard says is undefined behaviour. The crucial part is:

"depends on the side effects produced by the destructor has undefined behavior."

The Standard §1.9/12 explicitly defines side effects as follows (the italics below are the Standards, indicating the introduction of a formal definition):

Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

In your program, there's no dependency so no undefined behaviour.

One example of dependency arguably matching the scenario in §3.8 p4, where the need for or cause of undefined behaviour isn't apparent, is:

struct X
{
    ~X() { std::cout << "bye!\n"; }
};

int main()
{
     new X();
}

An issue people are debating is whether the X object above would be considered released for the purposes of 3.8 p4, given it's probably only released to the O.S. after program termination - it's not clear from reading the Standard whether that stage of a process's "lifetime" is in scope for the Standard's behavioural requirements (my quick search of the Standard didn't clarify this). I'd personally hazard that 3.8p4 applies here, partly because as long as it's ambiguous enough to be argued a compiler writer may feel entitled to allow undefined behaviour in this scenario, but even if the above code doesn't constitute release the scenario's easily amended ala...

int main()
{
     X* p = new X();
     *(char*)p = 'x';   // token memory reuse...
}

Anyway, however main's implemented the destructor above has a side effect - per "calling a library I/O function"; further, the program's observable behaviour arguably "depends on" it in the sense that buffers that would be affected by the destructor were it to have run are flushed during termination. But is "depends on the side effects" only meant to allude to situations where the program would clearly have undefined behaviour if the destructor didn't run? I'd err on the side of the former, particularly as the latter case wouldn't need a dedicated paragraph in the Standard to document that the behaviour is undefined. Here's an example with obviously-undefined behaviour:

int* p_;

struct X
{
    ~X() { if (b_) p_ = 0; else delete p_; }
    bool b_;
};

X x{true};

int main()
{
     p_ = new int();
     delete p_; // p_ now holds freed pointer
     new (&x){false};  // reuse x without calling destructor
}

When x's destructor is called during termination, b_ will be false and ~X() will therefore delete p_ for an already-freed pointer, creating undefined behaviour. If x.~X(); had been called before reuse, p_ would have been set to 0 and deletion would have been safe. In that sense, the program's correct behaviour could be said to depend on the destructor, and the behaviour is clearly undefined, but have we just crafted a program that matches 3.8p4's described behaviour in its own right, rather than having the behaviour be a consequence of 3.8p4...?

More sophisticated scenarios with issues - too long to provide code for - might include e.g. a weird C++ library with reference counters inside file stream objects that had to hit 0 to trigger some processing such as flushing I/O or joining of background threads etc. - where failure to do those things risked not only failing to perform output explicitly requested by the destructor, but also failing to output other buffered output from the stream, or on some OS with a transactional filesystem might result in a rollback of earlier I/O - such issues could change observable program behaviour or even leave the program hung.

Note: it's not necessary to prove that there's any actual code that behaves strangely on any existing compiler/system; the Standard clearly reserves the right for compilers to have undefined behaviour... that's all that matters. This is not something you can reason about and choose to ignore the Standard - it may be that C++14 or some other revision changes this stipulation, but as long as it's there then if there's even arguably some "dependency" on side effects then there's the potential for undefined behaviour (which of course is itself allowed to be defined by a particular compiler/implementation, so it doesn't automatically mean that every compiler is obliged do something bizarre).

Community
  • 1
  • 1
Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
  • I think you're generally on the right track, *but* the given example program does not depend on the destructor. – Cheers and hth. - Alf Jun 10 '14 at 09:37
  • 1
    @Cheersandhth.-Alf you assert that with no explanation, despite my having explained the shared interaction with buffering - so what do you expect me to make of your objection? Note that program output is clearly a tangible behaviour of the program that would be affected by the side effects of the destructor, so I'd say the Standard grants the compiler the right to have undefined behaviour in this case. – Tony Delroy Jun 10 '14 at 09:42
  • On the contrary, the program presented here depends on the guarantee that the destructor is not called (the standard guarantees that it's not called implicitly) so that no text is presented. Standard stream buffers are flushed anyway for normal termination. Not that it matters. – Cheers and hth. - Alf Jun 10 '14 at 09:44
  • 1
    "the destructor shall not be implicitly called" Note that such a guarantee would be **meaningless** if there were UB. The standard isn't into meaningless guarantees, in general. – Cheers and hth. - Alf Jun 10 '14 at 09:47
  • @Cheersandhth.-Alf that's the weird thing about this particular part of the Standard though... the destructor is *never* called in these scenarios, but the side effects in the hypothetical scenerario that it were called are documented as relevant to the determination of undefined behaviour. – Tony Delroy Jun 10 '14 at 09:57
  • on the one hand, the assumption of no UB yields a consistent and meaningful interpretation, which also is practically useful, on the other hand, the assumption of UB yields a meaningless guarantee, a self-contradiction, and would break portability of a gargantuan amount of existing code. So let's see, consistent, meaningful and practical, versus inconsistent, meaningless and huge cost impractical. Since that's pretty clear, I think those not convinced up-front cannot be convinced except by having an explicit statement about it added to the standard. Huh, that's my *answer*! :) – Cheers and hth. - Alf Jun 10 '14 at 10:24
  • I tend to agree with Alf here. In fact, it is clearly defined in the standard when the destructor is called; here, is is clearly defined that it is never called. If any code depends on a destructor being called when the standard says it isn't called, I would call that incorrect program logic, and not undefined behavior. – James Kanze Jun 10 '14 at 10:53
  • I don't see how this example triggers the clause; the standard never says that the destructor should be called at any point here, so there can't be any code which depends on this destructor call. – M.M Jun 10 '14 at 10:54
  • @Cheersandhth.-Alf so, the Standard says "any program that depends on the side effects produced by the destructor has undefined behavior." and you interpret this as meaning what, exactly? Something like "oops no, that doesn't make sense so I proclaim it's defined"? Do you have any actual interpretation of what's being described as undefined here? And it doesn't help to say "well C++14 could clarify nothing's undefined" - meanwhile if it's ambiguous enough that a compiler writer could reasonably say a scenario was described here, they're within their rights to make it undefined. – Tony Delroy Jun 10 '14 at 11:34
  • @JamesKanze: I understand your point and agree with it at a logic level, but still see no better explanation for the Standard: what would you say is the behaviour that's being made undefined? – Tony Delroy Jun 10 '14 at 11:37
  • @TonyD Re "oops no, that doesn't make sense so I proclaim it's defined", yes that's essentially it, a simple [proof by contradiction](http://en.wikipedia.org/wiki/Proof_by_contradiction). You do have a point regarding what that passage about dependency is really about, that it's more than just a bit unclear. I am not sure, and will have to come back to you about that, as Arnold so memorably stated. – Cheers and hth. - Alf Jun 10 '14 at 11:38
  • @MattMcNabb: there's no formal definition of "depends" in the Standard (apart from in the unrelated context of templates) - so it's hard to clarify that point... in this Q's context I take it to depends to mean "would be affected by the execution of". If you disagree, I'd all ears for a more plausible explanation for exactly what behaviour *is* being classified as undefined in this part of the Standard. – Tony Delroy Jun 10 '14 at 11:41
  • 1
    @TonyD To tell the truth, I find it difficult to find an example where the behavior would be undefined. This particular statement in the standard seems meaningless, since it is impossible for a program to depend on behavior which is guaranteed not to occur. – James Kanze Jun 10 '14 at 11:44
  • @Cheersandhth.-Alf it's possible for something to be technically undefined behaviour but universally defined by the implementation in any real system - just like David Butenhof's classic explanation of why `volatile` isn't necessary or useful in certain multithreading contexts ([see here](http://www.lambdacs.com/cpt/FAQ.html#Q56))... at question here is not whether it's actually unreasonable to write a program that technically has this type of undefined behaviour, but whether the code I've listed is indeed the scenario under discussion. – Tony Delroy Jun 10 '14 at 11:51
  • @TonyD: The language "depends upon" seems very vague. It would seem much clearer to say "If a destructor is not called at a well-defined time, but this standard would allow the object to be destroyed a garbage collector or other such means, the standard imposes no restrictions upon if or when any particular side-effects from such destruction will be visible to outside code. For a program's behavior to be defined, outside code must be able to cope with any combination or sequence of side effects becoming visible without causing any Undefined Behavior". – supercat Jun 10 '14 at 18:12
  • The example seems a crystal clear example of a simple memory leak (better than the one in the question which takes effort to write a `delete` statement and then jumps through hoops in order to not execute it). There seems to be concensus that a memory leak is not undefined behaviour. Dependency on side effects of something that doesn't happen, what nonsense! My program depends on (the side effects of) the [Mayan apocalypse](https://en.wikipedia.org/wiki/Mayan_apocalypse), since if that happened, my program would not run. But it doesn't (didn't) happen. So my program has undefined behaviour??? – Marc van Leeuwen Jun 11 '14 at 12:52
  • @MarcvanLeeuwen: all that because it may seem to you like a "simple" memory leak, which is just fixating on a totally irrelevant aspect of the example; it's an illustration of an object for which the destructor isn't run. And it's not me saying it's undefined - that would be the Standard. If you can come up with a better hypothesis about the scenario the Standard's trying to describe - and aren't too busy daydreaming about the Mayans - feel free to contribute it and I'll be all ears.... – Tony Delroy Jun 11 '14 at 14:51
  • I've described an hypothesis in my answer, which I think also has been put forward by Alf, namely to read "depends" (a term that is nowhere formally defined) so that it reinforces (maybe needlessly) rather than contradicts what precedes. Just let "depends on" mean "requires in order to succeed", as in "our plan depends on your complete cooperation", and the passage becomes entirely coherent. Clearly depending in this sense on something that is not going to happen is a recipe for failure; it still would be nice to illustrate the phrase by an example, but one can understand what is meant. – Marc van Leeuwen Jun 12 '14 at 05:29
  • @MarcvanLeeuwen: "the passage becomes completely coherent" + "in my opinion the standard is contradicting itself in this passage" - interest definition of coherent. Anyway, now you're just shifting the terminology problem from "depends on" to "succeed"... does a program "succeed" if it doesn't output the same thing when the destructor doesn't run? – Tony Delroy Jun 12 '14 at 06:02
  • @TonyD: What I ways saying: if "depends on" means "is in any way affected by", then the passage is contradictory; if however it means "requires for succeeding", then it becomes coherent. And I agree with Alf's excellent answer that (with the former reading) this passage "can't mean literally what it says". And yes, succeeding is as much a vague term as depending on is; taking it to mean "having well defined behaviour" the final phrase becomes tautological. IMO the standard could be immediately and obviously improved by simply removing the phrase from "and any program that depends" onward. – Marc van Leeuwen Jun 12 '14 at 07:23
  • @MarcvanLeeuwen ...if it is indeed intended to be tautological, which is not established. I disagree with "can't mean literally what it says". Have you any take on the possibility advanced in my answer that the Standard may be intended to reserve the right to have undefined behaviour when e.g. an object with a file stream member doesn't have its destructor called, and the implementation on the execution environment in question chose to resort to some kind of background I/O thread or reference counter for shutdown behaviours...? – Tony Delroy Jun 12 '14 at 07:31
  • My take one that is that it seems implausible that the standard would _at this place_ be trying to cover the backs of implementations that make the assumption that destructors _always_ eventually get called. While the standard in many ways tries to facilitate meeting that condition by a program, it _does not have to_ do so, and so an implementation should not make that assumption; also there are many other ways (than to overwrite memory) to not have constructors called, so this is not the right place to address that possibility. – Marc van Leeuwen Jun 12 '14 at 07:50
  • ... OTOH, if the program logic itself depends on (second sense) the fact that constructors always eventually get called, and the program reuses memory of a non-destructed variable whose destructor has a side effect, then it is in trouble, since (1) calling the constructor would violate the standard, while (2) not calling the constructor would invalidate the program logic. Maybe the standard is trying to make (under this scenario) the behaviour undefined already _at the point of reusing memory_. But it shouldn't, because the program logic depending on ... is beyond the scope of the standard. – Marc van Leeuwen Jun 12 '14 at 07:58
  • @MarcvanLeeuwen: I assume you mean constructors in a few places above...? Re "at this place" is 3.8 Object Lifetime - seems ideal to me. "does not have to do so" - per 3.8p4, right? "also there are many other ways" - there are *no* other ways for a destructor *not* to get called if memory release to the O.S. at program termination is one of the forms of "release" for the purposes of interpreting 3.8p4 (which has been disputed by some comments but without anything from the Standard to prove it either way). – Tony Delroy Jun 12 '14 at 08:16
  • @MarcvanLeeuwen I can't follow your scenario here: "since (1) calling the constructor would violate the standard" - did you mean to list that the *compiler* can't call the *destructor* per 3.8p4? – Tony Delroy Jun 12 '14 at 08:25
  • 1
    Heads-up: this answer has been moved here from http://stackoverflow.com/questions/24137006/does-a-memory-leak-cause-undefined-behaviour – Shog9 Jul 11 '14 at 18:34
  • Perhaps this answer should have been merged to [another thread](http://stackoverflow.com/questions/9971559/why-is-not-deleting-an-object-that-has-a-destructor-with-a-side-effect-undefined). This thread (the memory leak one) covers all memory leaks, whereas that other thread is more specific to objects with destructors with side-effects. – M.M Sep 01 '14 at 05:28
3

The language specification says nothing about "memory leaks". From the language point of view, when you create an object in dynamic memory, you are doing just that: you are creating an anonymous object with unlimited lifetime/storage duration. "Unlimited" in this case means that the object can only end its lifetime/storage duration when you explicitly deallocate it, but otherwise it continues to live forever (as long as the program runs).

Now, we usually consider a dynamically allocated object become a "memory leak" at the point in program execution when all references (generic "references", like pointers) to that object are lost to the point of being unrecoverable. Note, that even to a human the notion of "all references being lost" is not very precisely defined. What if we have a reference to some part of the object, which can be theoretically "recalculated" to a reference to the entire object? Is it a memory leak or not? What if we have no references to the object whatsoever, but somehow we can calculate such a reference using some other information available to the program (like precise sequence of allocations)?

The language specification doesn't concern itself with issues like that. Whatever you consider an appearance of "memory leak" in your program, from the language point of view it is a non-event at all. From the language point of view a "leaked" dynamically allocated object just continues to live happily until the program ends. This is the only remaining point of concern: what happens when program ends and some dynamic memory is still allocated?

If I remember correctly, the language does not specify what happens to dynamic memory which is still allocated the moment of program termination. No attempts will be made to automatically destruct/deallocate the objects you created in dynamic memory. But there's no formal undefined behavior in cases like that.

AnT
  • 291,388
  • 39
  • 487
  • 734
3

The burden of evidence is on those who would think a memory leak could be C++ UB.

Naturally no evidence has been presented.

In short for anyone harboring any doubt this question can never be clearly resolved, except by very credibly threatening the committee with e.g. loud Justin Bieber music, so that they add a C++14 statement that clarifies that it's not UB.


At issue is C++11 §3.8/4:

For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

This passage had the exact same wording in C++98 and C++03. What does it mean?

  • the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released
     
    – means that one can grab the memory of a variable and reuse that memory, without first destroying the existing object.

  • if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called
     
    – means if one does not destroy the existing object before the memory reuse, then if the object is such that its destructor is automatically called (e.g. a local automatic variable) then the program has Undefined Behavior, because that destructor would then operate on a no longer existing object.

  • and any program that depends on the side effects produced by the destructor has undefined behavior
     
    – can't mean literally what it says, because a program always depends on any side effects, by the definition of side effect. Or in other words, there is no way for the program not to depend on the side effects, because then they would not be side effects.

Most likely what was intended was not what finally made its way into C++98, so that what we have at hand is a defect.

From the context one can guess that if a program relies on the automatic destruction of an object of statically known type T, where the memory has been reused to create an object or objects that is not a T object, then that's Undefined Behavior.


Those who have followed the commentary may notice that the above explanation of the word “shall” is not the meaning that I assumed earlier. As I see it now, the “shall” is not a requirement on the implementation, what it's allowed to do. It's a requirement on the program, what the code is allowed to do.

Thus, this is formally UB:

auto main() -> int
{
    string s( 666, '#' );
    new( &s ) string( 42, '-' );    //  <- Storage reuse.
    cout << s << endl;
    //  <- Formal UB, because original destructor implicitly invoked.
}

But this is OK with a literal interpretation:

auto main() -> int
{
    string s( 666, '#' );
    s.~string();
    new( &s ) string( 42, '-' );    //  <- Storage reuse.
    cout << s << endl;
    //  OK, because of the explicit destruction of the original object.
}

A main problem is that with a literal interpretation of the standard's paragraph above it would still be formally OK if the placement new created an object of a different type there, just because of the explicit destruction of the original. But it would not be very in-practice OK in that case. Maybe this is covered by some other paragraph in the standard, so that it is also formally UB.

And this is also OK, using the placement new from <new>:

auto main() -> int
{
    char* storage   = new char[sizeof( string )];
    new( storage ) string( 666, '#' );
    string const& s = *(
        new( storage ) string( 42, '-' )    //  <- Storage reuse.
        );
    cout << s << endl;
    //  OK, because no implicit call of original object's destructor.
}

As I see it – now.

Cheers and hth. - Alf
  • 135,616
  • 15
  • 192
  • 304
  • 6
    I'm not sure of your burden of evidence. C++03, at least, is quite clear that undefined behavior is the default; it may be expected any time the standard doesn't clearly specify the behavior. (In this case, of course, the behavior is clearly specified, and there is no undefined behavior. But that's because the burden of proof has been met by those saying that there is no undefined behavior: the standard clearly specifies what happens in this case.) – James Kanze Jun 10 '14 at 10:49
  • The evidence presented is the quote, which does mention undefined behavior. However, since the program can't do anything after it's terminated and those lifetimes implicitly ended, it can't possibly depend on anything either. So the interpretation is in error. – Potatoswatter Jun 10 '14 at 13:56
  • You explain "destructor shall not be implicitly called" by describing a scenario (automatic variable) where "then the program has Undefined Behavior, because that destructor would then operate..." - can't be reconciled. Re "program always depends on any side effects" - no, programs depend on observable side effects... side effect has a clear definition that includes e.g. object state modifications even if that object is not later used (which leads back in to the as-if rule, which I'd say doesn't apply because the behavioural requirements are undefined so can't be reasoned about). – Tony Delroy Jun 10 '14 at 15:26
  • @TonyD: Re "can't be reconciled", could you explain what you mean. I don't get it. The rest of what you write I understand even less, but let's start at the first place I fall off your train of thought, the "reconciled", which indicates that you envision some conflict. – Cheers and hth. - Alf Jun 10 '14 at 15:29
  • @Cheersandhth.-Alf sure... we've got a scenario in which the Standard guarantees the "destructor shall not be implicitly called"... you're saying you think it describes automatic variables where you imagine the problem being the exact opposite - that the destructor *is* later called implicitly. – Tony Delroy Jun 10 '14 at 15:37
  • @TonyD: First, automatic local variable is *one* way to have a destructor called implicitly. And what I'm saying (now) is that the "shall" is not a guarantee. It is a requirement. – Cheers and hth. - Alf Jun 10 '14 at 15:40
  • Oh sorry - I see where you're coming from now... that's another plausible perspective on the Standard's wording, though it's not "clicking" for me - I don't see that scenario being any more problematic for the compiler/implementation than a later explicit destruction of reused memory - e.g. if it was heap or shared mem rather than an automatic variable. (Sometimes you've almost got to be the compiler writer to twig to some potential implementation detail or freedom though... ;-)) – Tony Delroy Jun 10 '14 at 16:13
  • That said, a throw from within a placement new of an automatic variable that's not caught and handled with a successful placement new at that location could result in "destruction of a non-object" - clearly that's a potential issue in your scenario, for types without a non-throwing constructor.... – Tony Delroy Jun 10 '14 at 16:22
  • 2
    Your examples are covered explicitly in the Standard just a couple paragraphs later: "If a program ends the lifetime of an object of type `T` with static (3.7.1), thread (3.7.2), or automatic (3.7.3) storage duration and if `T` has a non-trivial destructor, the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined." – Ben Voigt Jun 10 '14 at 22:33
  • @BenVoigt: Thanks! That dispenses with the problem I noted for the middle example, in very reasonable way. – Cheers and hth. - Alf Jun 10 '14 at 23:33
  • 2
    @Alf: I think it also makes the example immediately above well-defined, if leaky. – Ben Voigt Jun 11 '14 at 01:14
  • @BenVoigt: well-defined only if you assume that conclusion already, i.e. interpretation of "shall" as a guarantee rather than a requirement. but as a guarantee it's broken in this example: it doesn't hold. so. – Cheers and hth. - Alf Jun 11 '14 at 20:48
  • @Alf: that interpretation is incompatible with the meaning of "implicit", wouldn't you say. An implicit destructor call is one injected by the implementation. – Ben Voigt Jun 11 '14 at 21:52
  • @BenVoigt: an implicit destructor call is injected by the implementation, yes, in a number of situations (that include automatic local variables). a requirement that there shall be no implicit destructor call is simply a requirement that the code is not such that there is an implicit destructor call. easy. ;-) – Cheers and hth. - Alf Jun 11 '14 at 22:04
  • @Alf: Now I see the point you're making, but I think your interpretation is wrong. See my quote from the Standard six comments up, it says code with an implicit destructor call *is* allowed when so long as an object of the correct type occupies the memory location when the lifetime automatically ends and the implicit destructor call is emitted. – Ben Voigt Jun 11 '14 at 22:44
  • @BenVoigt: The para you quoted only places a requirement of ending up with original type object in the storage. It doesn't say that that's automatically well-defined. The para under discussion, quoted in this answer, lays down the rule that if you have implicit destructor call, then before reusing the storage the old object must be destroyed (and it enumerates the two possible ways to do it). so there is no conflict. (with my interpretation, which I think with good margin is the least contradictory one) the 2 standard's paragraphs complement each other instead of contradicting each other. – Cheers and hth. - Alf Jun 11 '14 at 22:53
  • @Alf: Not possible. The implicit destructor call affects the now-existing object. It's completely unconnected to the object whose lifetime ended by reuse of the memory. Trying to claim it is undefined behavior if any object previously overlapping that memory was freed without a destructor call at any time in history seems... ridiculous? – Ben Voigt Jun 11 '14 at 22:56
  • BTW the quote in your answer is clearly a defect. "No explicit call to the destructor OR a *delete-expression* is not used" describes every object ever. (By DeMorgan's Theorem, the inverse is calling the explicit destructor AND also using `delete`, which would invoke the destructor twice -- not permitted) – Ben Voigt Jun 11 '14 at 22:58
  • @BenVoigt: Yes I agree that this consequence is ridiculous (although pretty harmless: only a requirement to do things properly), and no matter how we wring this there is a defect in the standard. – Cheers and hth. - Alf Jun 11 '14 at 23:00
  • Heads-up: this answer has been moved here from http://stackoverflow.com/questions/24137006/does-a-memory-leak-cause-undefined-behaviour – Shog9 Jul 11 '14 at 18:34
  • There _does_ exist programs not depending on side effects (by strict definition of C++), e.g. `int main(){}`. How do the programs behave is another story. – FrankHB Aug 26 '15 at 01:52
2

Its definately defined behaviour.

Consider a case the server is running and keep allocating heap memory and no memory is released even if there's no use of it. Hence the end result would be that eventually server will run out of memory and definately crash will occur.

Ashish
  • 7,621
  • 11
  • 48
  • 89
  • but before that a poorly written driver may assume memory it allocated is avaliable, when the allocation failed, and forge ahead causing a blue screen of death. Meanwhile Microsoft prints out a helpful error message prompting you to replace the driver without any indication of a memory leak. – fupsduck Dec 30 '09 at 07:29
  • and by the way - no new driver is available! – fupsduck Dec 30 '09 at 07:30
2

Adding to all the other answers, some entirely different approach. Looking at memory allocation in § 5.3.4-18 we can see:

If any part of the object initialization described above76 terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object’s memory to be freed. [ Note: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. —end note ]

Would it cause UB here, it would be mentioned, so it is "just a memory leak".

In places like §20.6.4-10, a possible garbage collector and leak detector is mentioned. A lot of thought has been put into the concept of safely derived pointers et.al. to be able to use C++ with a garbage collector (C.2.10 "Minimal support for garbage-collected regions").

Thus if it would be UB to just lose the last pointer to some object, all the effort would make no sense.

Regarding the "when the destructor has side effects not running it ever UB" I would say this is wrong, otherwise facilities such as std::quick_exit() would be inherently UB too.

PlasmaHH
  • 14,585
  • 5
  • 39
  • 55
  • 1
    Heads-up: this answer has been moved here from http://stackoverflow.com/questions/24137006/does-a-memory-leak-cause-undefined-behaviour – Shog9 Jul 11 '14 at 18:34
1

If the space shuttle must take off in two minutes, and I have a choice between putting it up with code that leaks memory and code that has undefined behavior, I'm putting in the code that leaks memory.

But most of us aren't usually in such a situation, and if we are, it's probably by a failure further up the line. Perhaps I'm wrong, but I'm reading this question as, "Which sin will get me into hell faster?"

Probably the undefined behavior, but in reality both.

JohnMcG
  • 8,283
  • 5
  • 36
  • 49
0

defined, since a memory leak is you forgetting to clean up after yourself.

of course, a memory leak can probably cause undefined behaviour later.

Oren Mazor
  • 4,313
  • 2
  • 25
  • 28
  • 1
    It what why can a memory leak cause undefined behavior! – Martin York Dec 30 '09 at 07:03
  • What undefined behavior will a memory leak cause, other than eventually running out of memory. Dereferencing a pointer that is already freed will cause undefined behavior (like a segfault), but a memory leak is not immediately dangerous. – Tarydon Dec 30 '09 at 07:09
  • 2
    A memory leak CANNOT cause undefined behavior. For instance, in C too many memory leaks may eventually result in a `malloc` call returning `NULL`. But that is a defined behavior of `malloc`. – Stephen C Dec 30 '09 at 07:36
  • thats what I meant. the act of memory leaking is absolutely defined. running out of memory, for example, is not. – Oren Mazor Dec 31 '09 at 15:11
0

Straight forward answer: The standard doesn't define what happens when you leak memory, thus it is "undefined". It's implicitly undefined though, which is less interesting than the explicitly undefined things in the standard.

Terry Mahaffey
  • 11,079
  • 32
  • 44
0

This obviously cannot be undefined behaviour. Simply because UB has to happen at some point in time, and forgetting to release memory or call a destructor does not happen at any point in time. What happens is just that the program terminates without ever having released memory or called the destructor; this does not make the behaviour of the program, or of its termination, undefined in any way.

This being said, in my opinion the standard is contradicting itself in this passage. On one hand it ensures that the destructor will not be called in this scenario, and on the other hand it says that if the program depends on the side effects produced by the destructor then it has undefined behaviour. Suppose the destructor calls exit, then no program that does anything can pretend to be independent of that, because the side effect of calling the destructor would prevent it from doing what it would otherwise do; but the text also assures that the destructor will not be called so that the program can go on with doing its stuff undisturbed. I think the only reasonable way to read the end of this passage is that if the proper behaviour of the program would require the destructor to be called, then behaviour is in fact not defined; this then is a superfluous remark, given that it has just been stipulated that the destructor will not be called.

Marc van Leeuwen
  • 3,327
  • 20
  • 34
  • There is also undefined behaviour that is "happening" at compile time, that is not really convincing argument. – PlasmaHH Jun 10 '14 at 16:08
  • 2
    @PlasmaHH: I don't believe that. Are you really saying there could be some program such that merely compiling it (or trying to) without ever attempting to run it produces undefined behaviour? It is my understanding that the standard tries to describe what behaviour executing a well formed program should have, including in which cases such behaviour is not defined, but if execution is not attempted, there is simply nothing the standard has to say about behaviour (although it might stipulate that certain errors should be signalled at compile time). – Marc van Leeuwen Jun 10 '14 at 17:19
  • 1
    Take for example translation phase 2 (line continuation in the preprocessor) which contains "If, as a result, a character sequence that matches the syntax of a universal-character-name is produced, the behavior is undefined" – PlasmaHH Jun 10 '14 at 19:12
  • @PlasmaHH So you win. But this strikes me as rather strange, because when dealing with preprocessing there is not yet even a program whose behaviour one could be talking about. Maybe they're just saying the behaviour of the preprocessor is undefined (so it might decide, without signalling an error, to spit out a "Hello world" program for compilation, instead of whatever was written), but I don't see why they don't just say the program is ill-formed. – Marc van Leeuwen Jun 10 '14 at 19:30
  • Keep in mind that a lot of rules have their roots in the early 90s, where resources were limited, and where a lot of stuff that we can today easily do with some static analysis was far too costly to do. In those times often when someone couldn't come up with a good implementation that all compilers could easily do, the default for something ill-formed was to make it UB so no one has to spend too much resources. And until today we have not revised those rules. I am sure for the cited one, modern compilers will just error out nicely. – PlasmaHH Jun 10 '14 at 19:35
  • `Simply because UB has to happen at some point in time, and forgetting to release memory or call a destructor does not happen at any point in time.` Sorry but that's total nonsense! A program's behaviour is either defined or undefined; this property does not blink into existence at any one point in time and then blink out again, and you _certainly_ can't come to a "this is not UB" conclusion based on some piece of code not being executing. That's like saying omitting a `return` statement in a function returning non-`void` cannot possibly be UB because it's the lack of a thing, yet it _is_ UB. – Lightness Races in Orbit Jun 11 '14 at 11:27
  • 1
    @LightnessRacesinOrbit: Although I (now) admit some exceptions, most undefined behaviour comes about where the standard described the execution of code; whether UB occurs need not be decidable by pure inspection of the program. For instance `n=n++;` type UB is only so if that statment actually gets executed; burying it in a never-called function would not cause UB. In the case at hand, it seems that in the cited text a moment is indicated: "the storage which the object occupies is reused or released". If that never happens, as in the given example, then there is certainly no UB. – Marc van Leeuwen Jun 11 '14 at 11:48
  • @MarcvanLeeuwen: I suggest that those examples are actually the rarity. Strictly speaking there are near-infinite ways to make your program's behaviour undefined by the C++ standard with the catch-all rule alone, and only a [relatively small] finite number of UB cases that are explicitly described. – Lightness Races in Orbit Jun 11 '14 at 12:30
  • @MarcvanLeeuwen As for your last comment, [as I said on Alain's answer the storage _is_ released so this _would_ be UB if there were side effects](http://stackoverflow.com/questions/24137006/does-a-memory-leak-cause-undefined-behaviour/24143792?noredirect=1#comment37250292_24137969). – Lightness Races in Orbit Jun 11 '14 at 12:31
  • "For instance n=n++; type UB is only so if that statment actually gets executed; burying it in a never-called function would not cause UB." - how can you know that? Given it's illegal code that the compiler's not required to detect or protect itself from, it could e.g. leave the optimiser in some "unstable" state that results in incorrect compilation of unrelated code. – Tony Delroy Jun 12 '14 at 05:41
  • @TonyD: I just posted [a question](http://stackoverflow.com/q/24186681/1436796) to see whether your point of view is shared by others. So far there are no answers, but you might want to give one. – Marc van Leeuwen Jun 12 '14 at 14:53
  • @MarcvanLeeuwen "does the smell of UB mandate a conforming implementation to decide that the whole program stinks, and refuse to execute correctly even the parts of the program for which the behaviour is perfectly well defined" - if you ask a question like that, what kind of answers do you expect? It's not a question of actively refusing to do anything - it's whether the compiler is obliged to be bullet-proofed against side effects of trying to compile the code with undefined behaviour, such as it not invalidating the optimisation logic, tracking of register usage etc. for other code paths. – Tony Delroy Jun 12 '14 at 15:53
  • Heads-up: this answer has been moved here from http://stackoverflow.com/questions/24137006/does-a-memory-leak-cause-undefined-behaviour – Shog9 Jul 11 '14 at 18:34
  • The wording of "some point in time" is totally wrong. WG21/N4527 1.9/5 :However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input _(not even with regard to operations preceding the first undefined operation)_. (Emphasized mine.)This allows the implementation to do some aggressive optimization (e.g. full program transformation) at translation time, without having assumption of the concept "time" of runtime. – FrankHB Aug 26 '15 at 01:45
  • @FrankHB: Maybe my wording can be improved. But your quote says "if any such _execution_ contains an undefined operation..."; the point is, the program/input combination must, following the behavior stipulated by the standard, lead to some execution that has UB (in which case the implementation need not even perform prior well defined operations). Forgetting to call a destructor is a global property of a program, it does not correspond to any specific execution of any part of the program; that is what I meant. – Marc van Leeuwen Aug 26 '15 at 07:38
  • The word "execution" means one set of possible semantics properties allowed by the standard. It is not necessarily mean "being run". By definition, the undefined operation could occur at any time, i.e. it can occur during translation. Even omitting a specific call is an obvious static property for programs compiled by traditional ahead-of-time compilers, the standard requires no such guarantee of "static", since it explicitly "places no requirement on the structure of conforming implementations". For an interpreter or an online partial evaluator, it is reasonable to avoid such assumptions. – FrankHB Sep 07 '15 at 06:54
  • I think it would better move the further discussion here: http://stackoverflow.com/questions/24186681/does-an-expression-with-undefined-behaviour-that-is-never-actually-executed-make. There is also another related question: http://stackoverflow.com/questions/21110059/observable-behavior-and-undefined-behavior-what-happens-if-i-dont-call-a-des, but not so specific to the problem concerned with your view. – FrankHB Sep 07 '15 at 07:29
-1

Undefined behavior means, what will happen has not been defined or is unknown. The behavior of memory leaks is definitly known in C/C++ to eat away at available memory. The resulting problems, however, can not always be defined and vary as described by gameover.

fupsduck
  • 2,991
  • 17
  • 18