<p>Certain constructs or conditions in programming just are not allowed. Languages such as Java or Swift handle these by raising an error when encountered. C and C++ on the other hand say <em>'Anything could happen, might error, might work, might work some of the time.'</em></p>
<p>What are the advantages of leaving behavior undefined as opposed to raising an error on an invalid program?</p>

<c><error-handling><c++><undefined-behavior>
Why would a language have a concept of undefined behavior instead of raising an error?
<p>The term &quot;Undefined Behavior&quot; is a catch-all used to refer to any of three kinds of situations without trying to distinguish among them:</p>
<ol>
<li><p>An implementation is called upon to process a program which, while non-portable, is correct on that platform.</p>
</li>
<li><p>An implementation is called upon to render a program which is erroneous, rendering questions of portability irrelevant.</p>
</li>
<li><p>An implementations is called upon to process a correct and portable program, but the input is erroneous in ways that cannot be reliably meaningfully handled via any standard means (e.g. using &quot;r&quot; mode with <code>fopen</code> to open a file that some other program has written using &quot;wb&quot; mode).</p>
</li>
</ol>
<p>For an implementation to be able to flag an error in response to some erroneous construct, it would need to have some means of determining whether the construct was erroneous rather than correct but non-portable.  In many cases, it would be impossible to specify language constructs in a manner that would flag all cases where they were used erroneously, without flagging cases where they were used in correct-but-non-portable fashion to accomplish tasks not provided for by the Standard.</p>

CC BY-SA 4.0
2023-07-11T23:05:29.493Z
2023-07-11T23:05:29.493Z
supercat
<p>From <a href="https://isocpp.org/wiki/faq/big-picture#undefined" rel="noreferrer">the ISO C++ wiki's FAQ item, &quot;Why are some things left undefined in C++?&quot;</a>:</p>
<blockquote>
<p>Because machines differ and because C left many things undefined. For details, including definitions of the terms “undefined”, “unspecified”, “implementation defined”, and “well-formed”; see the ISO C++ standard. Note that the meaning of those terms differ from their definition of the ISO C standard and from some common usage. You can get wonderfully confused discussions when people don’t realize that not everybody shares definitions.</p>
<p>This is a correct, if unsatisfactory, answer. Like C, C++ is meant to exploit hardware directly and efficiently. This implies that C++ must deal with hardware entities such as bits, bytes, words, addresses, integer computations, and floating-point computations the way they are on a given machine, rather than how we might like them to be.</p>
</blockquote>
<p>The rest of that FAQ goes on to give examples of where UB can be useful for performance reasons, to relieve standard-implementation-tooling from having to try to diagnose (report errors for) ill-formed code that is difficult to diagnose due to the C++ compilation model, and sometimes just due to implementations making use of already standardized UB to perform certain purported optimizations that they are now unwilling to change.</p>
<p>If you're interested in the work that the C++ committee is doing on reducing the number of things it specifies as UB, check out Study Group 12.</p>
<p>Also related: <a href="https://en.cppreference.com/w/cpp/language/ub" rel="noreferrer">https://en.cppreference.com/w/cpp/language/ub</a>.</p>
<p>You can find some other &quot;useful everyday examples&quot; of UB in C++ at <a href="https://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html#optimizations" rel="noreferrer">https://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html#optimizations</a>, including UB from using uninitialized variables (where allowing uninitialized variables is useful for performance), signed integer overflow being UB being useful for certain compiler optimizations, etc.</p>
<p>Sometimes it's done just to reserve space for future changes to the types of code that are labelled UB. Titus Winters <a href="https://youtu.be/BWvSSsKCiAw?t=259" rel="noreferrer">has done a talk on this subject</a>, and his paper <a href="https://wg21.link/p0921" rel="noreferrer">P0921</a>, which is a user-facing document about &quot;Rights the Standard Library Reserves for Itself&quot; (in the form of certain kinds of UB, mostly to carve out room for itself to change in the future).</p>

CC BY-SA 4.0
2023-05-22T07:59:03.590Z
2023-05-22T07:59:03.590Z
starball
CC BY-SA 4.0
2023-07-11T04:18:26.777Z
Note that there should be a distinction between "reserved for future versions of the Standard" versus "UB", in that the former should allow for the possibility of a programmer writing `#if XXXX` `#define __future_identifier [[...form compatible with old-compiler, but not necessarily optimal...]]` `#endif` to write code that will benefit from new compiler features if available, but still work on old compilers.
supercat
<h1>Because there are no portable semantics for doing so, or specifying such semantics would unnecessarily constrain implementations.</h1>
<p>Performance is oft-cited as the reason for undefined behavior but this tends to hide the real reason.</p>
<p>Undefined behavior comes from the fact that to specify everything in a programming language would be incredibly infeasible and would also not allow differentiation of implementations.</p>
<p>This is why some things are left undefined in programming language standards rather than them being explicitly spelled out. It both encourages a diversity of implementations that the language can support while letting those that want to target a specific superset do so by opting into certain features at the expense of loss of portability.</p>
<p>Signed integer overflow is a classic example. What do you get when you do</p>
<pre class="lang-c prettyprint-override"><code>int i = INT_MAX + 1;
</code></pre>
<p>It certainly... is C code<sup>1</sup>, but what does it actually do. Well... it's not defined because there's no <strong>portable</strong> semantics for such an operation. Depending on the implementation, it may wraparound (as many programmers on two's complement machines expect), it may saturate (as on some DSPs), it may trap, or do any number of things. You could also conceive of an implementation that checked all additions it couldn't prove or aborted translation if it could prove such a thing (at least within some set of assumptions!).</p>
<p>Especially in languages that gained popularity before standardization (such as C (or languages that inherited such concerns from those languages, like C++), many compilers had and have differing ways of expressing certain idioms.</p>
<sup>
1. There are certainly some who would argue that this isn't C code at all, but it's a view that, for all intents and purposes, is an unnecessarily prescriptivist (and perhaps useless) view of what a C program is, one which is unsupported by the standard itself.
</sup>

CC BY-SA 4.0
2024-04-20T07:37:03.403Z
2024-04-20T07:46:30.843Z
2024-04-20T07:46:30.843Z
Justine Krejcha
<p>Actively terminating the program may be the worst possible outcome anyway, when the broken run still gives some chances for it to accomplish the mission.</p>
<p>One of the cases would be a rocket controller. If the controller crashes completely, the rocket fails and this is the worst that can happen anyway. Otherwise in some cases a single error may be survivable, like in <a href="https://en.wikipedia.org/wiki/Ariane_flight_V88" rel="nofollow noreferrer">Ariane V88 crash</a> where it happened in the rocket alignment task no longer important at the given stage of the flight.</p>
<p>Of course, the range of such cases is rather limited. A more typical consumer program would benefit more from printing diagnostics and terminating, making the bugs easier to detect.</p>

CC BY-SA 4.0
2023-07-10T07:58:04.513Z
2023-07-10T13:57:04.153Z
2023-07-10T13:57:04.153Z
Audrius Meškauskas
CC BY-SA 4.0
2023-07-12T12:27:16.557Z
The problem is that this "may" be the worst possible outcome; in another situation this might be the best outcome. How can the language and/or compiler know that?
G. Sliepen
CC BY-SA 4.0
2023-07-12T12:32:56.047Z
Some languages have compile time switches to enable or disable assertions. All testing is normally done with assertions enabled, but for production, depends.
Audrius Meškauskas
CC BY-SA 4.0
2023-07-22T19:14:44.570Z
On something like the Ariane, it may be possible in retrospect to determine that the craft would have behaved acceptably if the error had been ignored, but the time required to ascertain that almost certainly exceeded the time that would generally have been available to avoid having the rocket lose control and destroy something on the ground.
supercat
<h2>This is a controversial topic</h2>
<p><a href="https://web.archive.org/web/20200909074736if_/https://www.pdf-archive.com/2014/10/02/ansi-iso-9899-1990-1/ansi-iso-9899-1990-1.pdf" rel="noreferrer">The C89 standard</a>, section 3.16, defines:</p>
<blockquote>
<p><strong>undefined behavior</strong>: Behavior, upon use of a nonportable or erroneous
program construct, or of erroneous data, or of indeterminately-valued
objects, for which this International Standard imposes no requirements.
Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).</p>
</blockquote>
<p>And the <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n897.pdf" rel="noreferrer">1999 <em>C Rationale</em></a> explains why:</p>
<blockquote>
<p><em>Undefined behavior</em> gives the implementor license not to catch certain
program errors that are difficult to diagnose. It also identifies
areas of possible conforming language extension: the implementor may
augment the language by providing a definition of the officially
undefined behavior.</p>
</blockquote>
<p><a href="https://www.open-std.org/JTC1/sc22/wg14/www/docs/n1256.pdf" rel="noreferrer">The C99 standard</a>, section 3.4.3, words it slightly differently, turning the second sentence into an explanatory note:</p>
<blockquote>
<p><strong>undefined behavior</strong>
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements</p>
<p>NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).</p>
<p>EXAMPLE An example of undefined behavior is the behavior on integer overflow.</p>
</blockquote>
<p>Note that in the second sentence, the word &quot;permissible&quot; was changed to the word &quot;possible&quot;.</p>
<p>This has resulted in two schools of thought in the C and C++ community.</p>
<p>The first group, largely made of C programmers, argues that the second sentence in the C89 standard was normative: it described the set of permissible behaviours. So &quot;undefined behaviour&quot; is only &quot;undefined&quot; in the sense that the standard does not require which of the permissible behaviours an implementation may do.</p>
<p>The second group, largely made of &quot;standards lawyers&quot; and open source C compiler implementors, point out that, under ISO rules, moving the second sentence to an explanatory note and changing the word &quot;permissible&quot; to &quot;possible&quot; means that it is <em>not</em> normative. These are merely possible behaviours, but because the standard imposes no requirements, <em>any</em> behaviour is possible.</p>
<p>This is also known as a &quot;<a href="http://www.catb.org/jargon/html/N/nasal-demons.html" rel="noreferrer">nasal demon</a>&quot;, because the compiler making demons fly out of your nose is also a possible behaviour.</p>
<p>This is a problem for many C programmers, since it meant that WG14 declared a lot of customary C usage to be undefined behaviour. Chris Lattner of LLVM put it this way: &quot;huge bodies of C code are land mines just waiting to explode.&quot;</p>
<p>So, for example, if you ever write this:</p>
<pre><code>i &lt;&lt; 32
</code></pre>
<p>If <code>i</code> is a 32-bit integer, shifting it left by 32 is undefined behaviour. According to the second interpretation, any instance of this anywhere on an execution path renders the <em>whole</em> program semantically meaningless. And specifically, a compiler may assume that undefined behaviour can never happen and optimise your code accordingly.</p>
<p>This brings us to SPECint, the standard suite of integer benchmarks maintained by the Standard Performance Evaluation Corporation. This set of benchmarks is how C compiler vendors evaluate and market their code-generation and optimisation performance.</p>
<p>One of the benchmarks is 464.h264ref, which is a reference implementation of H.264/AVC video coding. It contains the following interestingly-written function:</p>
<pre class="lang-c prettyprint-override"><code>int d[16]; 
int SATD (void) 
{ 
    int satd = 0, dd, k; 
    for (dd=d[k=0]; k&lt;16; dd=d[++k]) { 
        satd += (dd &lt; 0 ? -dd : dd); 
    } 
    return satd; 
} 
</code></pre>
<p>Please ignore the code quality for a moment. What I want you to notice is the for loop, specifically <code>dd=d[++k]</code>. On the last iteration of the loop, this reads one element past the end of the array. The value is never used, so the read is actually safe. Nonetheless, <em>this is technically undefined behaviour</em>.</p>
<p>Apparently nobody noticed until a pre-release version of GCC 4.8 optimised it. The compiler reasoned:</p>
<ul>
<li>The out-of-bounds access of <code>d</code> cannot possibly happen.</li>
<li>Therefore <code>k</code> cannot be greater than 15 <em>before</em> the array access <code>d[++k]</code>.</li>
<li>Therefore the loop test <code>k&lt;16</code> is <em>always</em> true.</li>
<li>Therefore this is an infinite loop.</li>
<li>Therefore all the other code in this function is dead code.</li>
</ul>
<p>And the final generated code was this:</p>
<pre><code>SATD: 
.L2: 
    jmp .L2  
</code></pre>
<p>It was fixed before GCC was released, and it now gives a warning. Let it also be a warning to you.</p>

CC BY-SA 4.0
2023-05-18T06:28:35.317Z
2023-07-22T00:17:14.980Z
2023-07-22T00:17:14.980Z
Pseudonym
CC BY-SA 4.0
2023-05-19T02:06:25.477Z
"the compiler making demons fly out of your nose is also a possible behaviour." -- Aw man, I much prefer my [free GCC pizza](https://codegolf.stackexchange.com/questions/101638/shortest-code-to-produce-non-deterministic-output).
Bbrk24
CC BY-SA 4.0
2023-05-20T19:24:21.403Z
Why is reading one element beyond the end of the array technically safe? If it were sitting on the edge of a memory page, it could access unmapped memory, or in less safe systems, I/O mapped memory or past the end of memory.
prosfilaes
CC BY-SA 4.0
2023-05-21T00:34:49.310Z
It's true, there are circumstances where it could be potentially unsafe. You'd have to be extremely unlucky. Nonetheless, this was an accepted C idiom at the time this code was written.
Pseudonym
CC BY-SA 4.0
2023-07-10T12:26:35.247Z
`i << 32` is a very good example, because quite a few architectures have a shift instruction with a five bit immediate field for the shift width. The compiler can either generate invalid assembler code that is rejected by the assembler, or work around the problem by loading the shift count into a register, and the shift instruction that gets the shift count from a register can either ignore higher bits or decide that all shifts larger than 32 give a result of zero. The question is: how much effort should compiler writers invest into this?
Simon Richter
CC BY-SA 4.0
2023-07-10T13:52:22.550Z
@Simon, I agree it's a good example, particularly if the `32` is a computed value.  A common choice is to simple use the lowest five bits of the value; the mathematically-correct alternative involves emitting code to test the range of the value and choose between shift and assigning zero to the destination (perhaps also handling negative values as right-shift or reset).  That can be measurably slower, which would annoy (some) users.
Toby Speight
CC BY-SA 4.0
2023-07-10T14:36:43.040Z
There's a difference between "this operation may do whatever the target machine does in this situation" and "this operation renders the whole program semantically meaningless".
Pseudonym
CC BY-SA 4.0
2023-07-10T16:14:48.030Z
The GCC bug is [this one](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53073)
corvus_192
CC BY-SA 4.0
2023-07-12T22:41:22.213Z
@SimonRichter: What's funny about the shift-by-exact-word-size example is that in the most common constructs where such a shift would occur, having the expression operator yield the left operand unmodified would yield behavior indistinguishable from yielding zero.
supercat
CC BY-SA 4.0
2023-07-21T17:06:22.257Z
"any instance of this anywhere in your code base renders the whole program semantically meaningless" is just wrong.  Time-travel effects of UB only apply if the flow of execution will reach the construct causing UB.
Ben Voigt
CC BY-SA 4.0
2023-07-22T00:16:26.103Z
@BenVoigt That's fair. I'll update the text to reflect that.
Pseudonym
<p>Besides <strong>performance</strong> and <strong>ease of writing an implementation</strong>, there is one other reason for undefined behavior.</p>
<p>If some property is impossible to check for and cannot be defined because it's unpredictable by nature, you <em>have</em> to make it undefined.</p>
<p>The best example is data races and race conditions. You can't check for them in the compiler (without designing the language around it, like Rust), and they can have unpredictable results, so you must make their behavior undefined.</p>

CC BY-SA 4.0
2023-05-18T05:29:02.710Z
2023-05-18T05:29:02.710Z
Gavin D. Howard
CC BY-SA 4.0
2023-05-18T05:42:44.900Z
Even in rust race conditions are possible, even only in safe code
mousetail 'he-him'
CC BY-SA 4.0
2023-05-18T21:00:19.223Z
Thank you for the clarification. I was not sure since I don't use it.
Gavin D. Howard
CC BY-SA 4.0
2023-07-17T15:50:22.057Z
Many languages recognize the existence of race conditions, but offer some behavioral guarantees with regard to their effects.  In Java, for example, if following the last sequencing barrier an `int` or an object reference that is written by one or more threads and read by one or more threads, each read may independently yield either the value held at the barrier, or any value that was written after it, and at the next sequencing barrier the object may receive any of the values that was written, but those are the only possible things that can happen.
supercat
CC BY-SA 4.0
2023-07-17T15:52:37.383Z
This allows things like Java's string `hashCode` function to safely cache its result without memory barriers.  If one thread hashes a string and then another does so later, but there hasn't been any memory barriers between the operations, the latter thread might not see the cached result from the first and thus perform a redundant hash calculation, but the lack of memory barriers would have no consequence beyond the wasted effort of computing the hash again.
supercat
CC BY-SA 4.0
2023-07-17T19:39:07.473Z
@supercat those are data races, not race conditions.
Gavin D. Howard
CC BY-SA 4.0
2023-07-19T00:00:50.623Z
Those limitations on the behavior of data races (which you do mention in your post) also limit the behavior of race conditions more generally. Because the program will always take one of some set of possibilities, you do not have undefined behavior- only nondeterminism.
rpjohnst
CC BY-SA 4.0
2023-07-19T00:34:26.233Z
@rpjohnst that's mostly true. Unfortunately, the concept of Turing-completeness means that if anything nondeterministic happens, we can't tell what will happen afterward.
Gavin D. Howard
CC BY-SA 4.0
2023-07-19T05:43:19.600Z
@GavinD.Howard This is a common misunderstanding of Turing-completeness and Rice’s theorem, but [it isn’t true](https://cs.stackexchange.com/questions/2811/perplexed-by-rices-theorem).
Alexis King
CC BY-SA 4.0
2023-07-19T05:49:12.720Z
@GavinD.Howard Undefined behavior means there are no constraints on what the program might do (arguably it no longer even "is a program"). Nondeterminism very specifically gives you a set of things that the program must choose from- for instance, the set of possible orderings and values you might observe.
rpjohnst
CC BY-SA 4.0
2023-07-19T13:08:40.987Z
@rpjohnst it is true that nondeterminism gives you specific constraints, and most of the time, that does not spill into UB. But when that nondeterminism changes the path through the code that is executed, it is possible that anything can happen.
Gavin D. Howard
CC BY-SA 4.0
2023-07-19T13:11:50.330Z
@AlexisKing I'm not misunderstanding at all. I'm speaking of programs in the wall world, that execute on real machines. Rice's theorem is about functions, which are the CS equivalent of spherical cows.
Gavin D. Howard
CC BY-SA 4.0
2023-07-19T13:24:42.000Z
Okay, it's not about functions per se, but it's about Turing machines, which are another form of spherical cow.
Gavin D. Howard
<p>The premise of undefined behavior is assuming that erroneous conditions <strong>never happen</strong>. The responsibility of checking and avoiding such conditions is placed on the programmer and not on the implementation. While many languages check for exceptional conditions every time one may happen, C and C++ do not. This removes safety, because the underlying behavior of the undefined construct can be unpredictable, but improves <strong>performance</strong> and <strong>ease of writing an implementation</strong>, as less (redundant?) checks are being made by the implementation, because it is assumed that the programmer did their job.</p>
<p>If C were to <em>require</em> that dereferencing NULL yielded a runtime error, the compiler may have to spend time tracking which pointers can be NULL and/or insert NULL tests that manually crash the program at every point a pointer is dereferenced. Instead, the <em>anything goes</em> aspect of undefined behavior permits the compiler to assume these will never happen and omit checks. Because any possible unpredictable behavior result from omitting the test would be valid <em>'undefined behavior.'</em></p>

CC BY-SA 4.0
2023-05-18T04:59:03.773Z
2023-05-18T04:59:03.773Z
CPlus
CC BY-SA 4.0
2023-07-11T04:10:50.280Z
Relatively few programs are run in circumstances where they can be guaranteed never to receive erroneous inputs, and implementaitons which assume all possible actions in response to invalid inputs would be equally acceptable shoudl be recognized as unsuitable for use outside such circumstances.
supercat
CC BY-SA 4.0
2023-07-17T15:55:06.763Z
Note also that according to the C Standard, the term "Undefined Behavior" is used to describe "non-portable or erroneous program constructs, or erroneous inputs".  For many kinds of program, an assumption that no "non-portable" constructs would be executed would be patently absurd, as would be the notion that all possible behaviors to erroneous inputs would equally satisfy *application requirements*.  The C Standard can't exercise jurisdiction over what will happen if a program uses `fopen(name,"r")` to read a file that some other program wrote in binary mode, but...
supercat
CC BY-SA 4.0
2023-07-17T15:59:55.950Z
...the fact that a strictly conforming program might attempt to open a file in such a case can hardly be viewed as implying the program itself is "erroneous".  If an environment would make it easy for an `fopen` implementation to return null, or else translate the file in a documented fashion, an implementation that does one of those things would be preferable to one that would arbitrarily corrupt the memory, but if the underlying OS would corrupt memory when attempting to perform such an action, a C implementation can't very well be required to prevent that.
supercat
<p>The point about UB is that the <em>language</em> –  the compiler, or the interpreter – does not have to worry about extreme or edge cases. It's the <em>programmer</em> that has to worry about such situations. In other words, &quot;Undefined Behaviour&quot; takes a lot of worrying away from the language itself, and puts it back onto the shoulders of the user of the language. Whether that is a good idea or not is up for discussion.</p>
<p>Consider the following C function. Suppose this function is put into a separate file (separate &quot;compilation unit&quot;):</p>
<pre class="lang-c prettyprint-override"><code>void f(int *x) {
    x[100] = 25;
}
</code></pre>
<p>This compiles without problems, even though at compile time it is not known what <code>x</code> points to. But the compiler is happy to oblige anyway: it takes the pointer <code>x</code>, it adds to it 100 times the size of an integer, and it attempts to write &quot;25&quot; into the resulting memory location. Whether this succeeds or not, and what the actual result is, depends on how this function is called.</p>
<p>It could be that (in the application that uses <code>f</code>) we have something like</p>
<pre class="lang-c prettyprint-override"><code>int a[1000];
f(a);
</code></pre>
<p>in which case invoking <code>f</code> won't do anything unusual or undefined. But it also could be that the function is invoked as</p>
<pre class="lang-c prettyprint-override"><code>int a[10];
f(a);
</code></pre>
<p>In this case we simply cannot say what the result of this function call is. It could attempt to write to memory which the current process doesn't have access to; this would result in an &quot;Access violation&quot; or &quot;Segmentation fault&quot; error, or something similar; possibly resulting in program termination. But it is also possible that this function will overwrite some other memory belonging to the same process – with completely unpredictable results!</p>
<p>And that's the whole point of &quot;undefined behaviour&quot;. The language itself (the compiler or the interpreter) does not have to worry about special cases – for example it does not have to check that a pointer points to a valid memory area (that the current process has access to). This shifts a lot of responsibility from the language itself onto its users!</p>

CC BY-SA 4.0
2023-07-21T07:43:46.583Z
2023-07-21T16:26:51.547Z
2023-07-21T16:26:51.547Z
printf
CC BY-SA 4.0
2023-12-03T12:56:18.547Z
To make it defined behaviour, you would have to define what happens in the second case. Presumably the caller would tell f that indices 0 to 9 are valid for read/write and you are allowed to take the address at index 10, and f would check that and stop executing at the assignment with index 100.
gnasher729
<h2>Performance</h2>
<p>Checking for errors at runtime is quite expensive. Branching operators massively are slow instructions, especially since they prevent many optimizations. If every basic operator needed a sanity check it would massively add to the runtime of your program.</p>
<p>On the other hand, a lot of things that are &quot;undefined behavior&quot; are quite easily checked by the programmer. Languages like C and C++ choose to trust the programmer to check these things properly and therefore benefit from faster code.</p>
<p>Languages like Rust do do more checks by default but even then the checks can be disabled for developers who know for sure they would pass.</p>
<h2>Flexibility of implementation</h2>
<p>Languages can have multiple implementations, and multiple versions of the same implementation. Implementing some functionality in one version might be much harder than another, possibly because of platform constraints.</p>
<p>You could specify the behavior of one version and force other implementations to implement it but it would be slow, inconvenient, and might mean your language can't even run on some platforms.</p>
<p>Implementors also want freedom to update the implementation over time. Maybe a faster algorithm is discovered but it behaves slightly differently in situations where it shouldn't really be used anyways. (eg. sorting a array with a non-transitive comparison function)</p>
<p>Thus, just providing some constraints for how some functionality must be handled allows implementors to choose the best implementation without fear of breaking existing programs.</p>
<h2>Optimization (Performance, again)</h2>
<p>Compiled languages often have a optimizer. Optimizers massively increase performance but need to make assumptions about the code to work. CPUs nowadays are able to execute many instructions at once so long as they use different parts of the chip and a whole bunch of very complex rules are also followed. Compilers will rearrange your instruction so that multiple instructions can be run at once.</p>
<p>To do this, the compiler will build a graph of the dependencies of each instruction so it can guarantee nothing will be run before the data it depends on. However, the compiler needs to make assumptions to do this.</p>
<p>For example it needs to know none of the data it needs will be written to by another thread. It may assume that no pointers alias. It may also assume no integers overflows.</p>
<p>These things are very hard to check for a compiler but relatively easy to check for a programmer. Considering the huge performance benefit this is usually considered worth it.</p>

CC BY-SA 4.0
2023-05-18T05:34:10.573Z
2023-08-18T14:45:49.637Z
2023-08-18T14:45:49.637Z
mousetail 'he-him'
# Why would a language have a concept of undefined behavior instead of raising an error?
Posted by: username_0 on 2023-05-18
Certain constructs or conditions in programming just are not allowed. Languages such as Java or Swift handle these by raising an error when encountered. C and C++ on the other hand say *'Anything could happen, might error, might work, might work some of the time.'*

What are the advantages of leaving behavior undefined as opposed to raising an error on an invalid program?
**Tags:** c error-handling c++ undefined-behavior
---
## 9 answers
### Answer by username_6 on 2023-05-18. Score: 33 | Accepted answer
This is a controversial topic
The C89 standard, section 3.16, defines:

undefined behavior: Behavior, upon use of a nonportable or erroneous
program construct, or of erroneous data, or of indeterminately-valued
objects, for which this International Standard imposes no requirements.
Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

And the 1999 C Rationale explains why:

Undefined behavior gives the implementor license not to catch certain
program errors that are difficult to diagnose. It also identifies
areas of possible conforming language extension: the implementor may
augment the language by providing a definition of the officially
undefined behavior.

The C99 standard, section 3.4.3, words it slightly differently, turning the second sentence into an explanatory note:

undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
EXAMPLE An example of undefined behavior is the behavior on integer overflow.

Note that in the second sentence, the word "permissible" was changed to the word "possible".
This has resulted in two schools of thought in the C and C++ community.
The first group, largely made of C programmers, argues that the second sentence in the C89 standard was normative: it described the set of permissible behaviours. So "undefined behaviour" is only "undefined" in the sense that the standard does not require which of the permissible behaviours an implementation may do.
The second group, largely made of "standards lawyers" and open source C compiler implementors, point out that, under ISO rules, moving the second sentence to an explanatory note and changing the word "permissible" to "possible" means that it is not normative. These are merely possible behaviours, but because the standard imposes no requirements, any behaviour is possible.
This is also known as a "nasal demon", because the compiler making demons fly out of your nose is also a possible behaviour.
This is a problem for many C programmers, since it meant that WG14 declared a lot of customary C usage to be undefined behaviour. Chris Lattner of LLVM put it this way: "huge bodies of C code are land mines just waiting to explode."
So, for example, if you ever write this:
i << 32

If i is a 32-bit integer, shifting it left by 32 is undefined behaviour. According to the second interpretation, any instance of this anywhere on an execution path renders the whole program semantically meaningless. And specifically, a compiler may assume that undefined behaviour can never happen and optimise your code accordingly.
This brings us to SPECint, the standard suite of integer benchmarks maintained by the Standard Performance Evaluation Corporation. This set of benchmarks is how C compiler vendors evaluate and market their code-generation and optimisation performance.
One of the benchmarks is 464.h264ref, which is a reference implementation of H.264/AVC video coding. It contains the following interestingly-written function:
int d[16]; 
int SATD (void) 
{ 
    int satd = 0, dd, k; 
    for (dd=d[k=0]; k<16; dd=d[++k]) { 
        satd += (dd < 0 ? -dd : dd); 
    } 
    return satd; 
} 

Please ignore the code quality for a moment. What I want you to notice is the for loop, specifically dd=d[++k]. On the last iteration of the loop, this reads one element past the end of the array. The value is never used, so the read is actually safe. Nonetheless, this is technically undefined behaviour.
Apparently nobody noticed until a pre-release version of GCC 4.8 optimised it. The compiler reasoned:

The out-of-bounds access of d cannot possibly happen.
Therefore k cannot be greater than 15 before the array access d[++k].
Therefore the loop test k<16 is always true.
Therefore this is an infinite loop.
Therefore all the other code in this function is dead code.

And the final generated code was this:
SATD: 
.L2: 
    jmp .L2  

It was fixed before GCC was released, and it now gives a warning. Let it also be a warning to you.
> **Comments:**
> **username_7** (2023-05-19): "the compiler making demons fly out of your nose is also a possible behaviour." -- Aw man, I much prefer my [free GCC pizza](https://codegolf.stackexchange.com/questions/101638/shortest-code-to-produce-non-deterministic-output).  
> **username_8** (2023-05-20): Why is reading one element beyond the end of the array technically safe? If it were sitting on the edge of a memory page, it could access unmapped memory, or in less safe systems, I/O mapped memory or past the end of memory.  
> **username_6** (2023-05-21): It's true, there are circumstances where it could be potentially unsafe. You'd have to be extremely unlucky. Nonetheless, this was an accepted C idiom at the time this code was written.  
> **username_9** (2023-07-10): `i << 32` is a very good example, because quite a few architectures have a shift instruction with a five bit immediate field for the shift width. The compiler can either generate invalid assembler code that is rejected by the assembler, or work around the problem by loading the shift count into a register, and the shift instruction that gets the shift count from a register can either ignore higher bits or decide that all shifts larger than 32 give a result of zero. The question is: how much effort should compiler writers invest into this?  
> **username_10** (2023-07-10): @Simon, I agree it's a good example, particularly if the `32` is a computed value.  A common choice is to simple use the lowest five bits of the value; the mathematically-correct alternative involves emitting code to test the range of the value and choose between shift and assigning zero to the destination (perhaps also handling negative values as right-shift or reset).  That can be measurably slower, which would annoy (some) users.  
> **username_6** (2023-07-10): There's a difference between "this operation may do whatever the target machine does in this situation" and "this operation renders the whole program semantically meaningless".  
> **username_11** (2023-07-10): The GCC bug is [this one](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53073)  
> **username_1** (2023-07-12): @SimonRichter: What's funny about the shift-by-exact-word-size example is that in the most common constructs where such a shift would occur, having the expression operator yield the left operand unmodified would yield behavior indistinguishable from yielding zero.  
> **username_12** (2023-07-21): "any instance of this anywhere in your code base renders the whole program semantically meaningless" is just wrong.  Time-travel effects of UB only apply if the flow of execution will reach the construct causing UB.  
> **username_6** (2023-07-22): @BenVoigt That's fair. I'll update the text to reflect that.  
---
### Answer by username_14 on 2023-05-18. Score: 19
## Performance

Checking for errors at runtime is quite expensive. Branching operators massively are slow instructions, especially since they prevent many optimizations. If every basic operator needed a sanity check it would massively add to the runtime of your program.

On the other hand, a lot of things that are "undefined behavior" are quite easily checked by the programmer. Languages like C and C++ choose to trust the programmer to check these things properly and therefore benefit from faster code.

Languages like Rust do do more checks by default but even then the checks can be disabled for developers who know for sure they would pass.

## Flexibility of implementation

Languages can have multiple implementations, and multiple versions of the same implementation. Implementing some functionality in one version might be much harder than another, possibly because of platform constraints.

You could specify the behavior of one version and force other implementations to implement it but it would be slow, inconvenient, and might mean your language can't even run on some platforms.

Implementors also want freedom to update the implementation over time. Maybe a faster algorithm is discovered but it behaves slightly differently in situations where it shouldn't really be used anyways. (eg. sorting a array with a non-transitive comparison function)

Thus, just providing some constraints for how some functionality must be handled allows implementors to choose the best implementation without fear of breaking existing programs.

## Optimization (Performance, again)

Compiled languages often have a optimizer. Optimizers massively increase performance but need to make assumptions about the code to work. CPUs nowadays are able to execute many instructions at once so long as they use different parts of the chip and a whole bunch of very complex rules are also followed. Compilers will rearrange your instruction so that multiple instructions can be run at once.

To do this, the compiler will build a graph of the dependencies of each instruction so it can guarantee nothing will be run before the data it depends on. However, the compiler needs to make assumptions to do this.

For example it needs to know none of the data it needs will be written to by another thread. It may assume that no pointers alias. It may also assume no integers overflows.

These things are very hard to check for a compiler but relatively easy to check for a programmer. Considering the huge performance benefit this is usually considered worth it.
---
### Answer by username_13 on 2023-05-18. Score: 14
Besides **performance** and **ease of writing an implementation**, there is one other reason for undefined behavior.

If some property is impossible to check for and cannot be defined because it's unpredictable by nature, you *have* to make it undefined.

The best example is data races and race conditions. You can't check for them in the compiler (without designing the language around it, like Rust), and they can have unpredictable results, so you must make their behavior undefined.
> **Comments:**
> **username_14** (2023-05-18): Even in rust race conditions are possible, even only in safe code  
> **username_13** (2023-05-18): Thank you for the clarification. I was not sure since I don't use it.  
> **username_1** (2023-07-17): Many languages recognize the existence of race conditions, but offer some behavioral guarantees with regard to their effects.  In Java, for example, if following the last sequencing barrier an `int` or an object reference that is written by one or more threads and read by one or more threads, each read may independently yield either the value held at the barrier, or any value that was written after it, and at the next sequencing barrier the object may receive any of the values that was written, but those are the only possible things that can happen.  
> **username_1** (2023-07-17): This allows things like Java's string `hashCode` function to safely cache its result without memory barriers.  If one thread hashes a string and then another does so later, but there hasn't been any memory barriers between the operations, the latter thread might not see the cached result from the first and thus perform a redundant hash calculation, but the lack of memory barriers would have no consequence beyond the wasted effort of computing the hash again.  
> **username_13** (2023-07-17): @supercat those are data races, not race conditions.  
> **username_15** (2023-07-19): Those limitations on the behavior of data races (which you do mention in your post) also limit the behavior of race conditions more generally. Because the program will always take one of some set of possibilities, you do not have undefined behavior- only nondeterminism.  
> **username_13** (2023-07-19): @rpjohnst that's mostly true. Unfortunately, the concept of Turing-completeness means that if anything nondeterministic happens, we can't tell what will happen afterward.  
> **username_16** (2023-07-19): @GavinD.Howard This is a common misunderstanding of Turing-completeness and Rice’s theorem, but [it isn’t true](https://cs.stackexchange.com/questions/2811/perplexed-by-rices-theorem).  
> **username_15** (2023-07-19): @GavinD.Howard Undefined behavior means there are no constraints on what the program might do (arguably it no longer even "is a program"). Nondeterminism very specifically gives you a set of things that the program must choose from- for instance, the set of possible orderings and values you might observe.  
> **username_13** (2023-07-19): @rpjohnst it is true that nondeterminism gives you specific constraints, and most of the time, that does not spill into UB. But when that nondeterminism changes the path through the code that is executed, it is possible that anything can happen.  
> **username_13** (2023-07-19): @AlexisKing I'm not misunderstanding at all. I'm speaking of programs in the wall world, that execute on real machines. Rice's theorem is about functions, which are the CS equivalent of spherical cows.  
> **username_13** (2023-07-19): Okay, it's not about functions per se, but it's about Turing machines, which are another form of spherical cow.  
---
### Answer by username_0 on 2023-05-18. Score: 9
The premise of undefined behavior is assuming that erroneous conditions **never happen**. The responsibility of checking and avoiding such conditions is placed on the programmer and not on the implementation. While many languages check for exceptional conditions every time one may happen, C and C++ do not. This removes safety, because the underlying behavior of the undefined construct can be unpredictable, but improves **performance** and **ease of writing an implementation**, as less (redundant?) checks are being made by the implementation, because it is assumed that the programmer did their job.

If C were to *require* that dereferencing NULL yielded a runtime error, the compiler may have to spend time tracking which pointers can be NULL and/or insert NULL tests that manually crash the program at every point a pointer is dereferenced. Instead, the *anything goes* aspect of undefined behavior permits the compiler to assume these will never happen and omit checks. Because any possible unpredictable behavior result from omitting the test would be valid *'undefined behavior.'*
> **Comments:**
> **username_1** (2023-07-11): Relatively few programs are run in circumstances where they can be guaranteed never to receive erroneous inputs, and implementaitons which assume all possible actions in response to invalid inputs would be equally acceptable shoudl be recognized as unsuitable for use outside such circumstances.  
> **username_1** (2023-07-17): Note also that according to the C Standard, the term "Undefined Behavior" is used to describe "non-portable or erroneous program constructs, or erroneous inputs".  For many kinds of program, an assumption that no "non-portable" constructs would be executed would be patently absurd, as would be the notion that all possible behaviors to erroneous inputs would equally satisfy *application requirements*.  The C Standard can't exercise jurisdiction over what will happen if a program uses `fopen(name,"r")` to read a file that some other program wrote in binary mode, but...  
> **username_1** (2023-07-17): ...the fact that a strictly conforming program might attempt to open a file in such a case can hardly be viewed as implying the program itself is "erroneous".  If an environment would make it easy for an `fopen` implementation to return null, or else translate the file in a documented fashion, an implementation that does one of those things would be preferable to one that would arbitrarily corrupt the memory, but if the underlying OS would corrupt memory when attempting to perform such an action, a C implementation can't very well be required to prevent that.  
---
### Answer by username_17 on 2023-07-21. Score: 9
The point about UB is that the language –  the compiler, or the interpreter – does not have to worry about extreme or edge cases. It's the programmer that has to worry about such situations. In other words, "Undefined Behaviour" takes a lot of worrying away from the language itself, and puts it back onto the shoulders of the user of the language. Whether that is a good idea or not is up for discussion.
Consider the following C function. Suppose this function is put into a separate file (separate "compilation unit"):
void f(int *x) {
    x[100] = 25;
}

This compiles without problems, even though at compile time it is not known what x points to. But the compiler is happy to oblige anyway: it takes the pointer x, it adds to it 100 times the size of an integer, and it attempts to write "25" into the resulting memory location. Whether this succeeds or not, and what the actual result is, depends on how this function is called.
It could be that (in the application that uses f) we have something like
int a[1000];
f(a);

in which case invoking f won't do anything unusual or undefined. But it also could be that the function is invoked as
int a[10];
f(a);

In this case we simply cannot say what the result of this function call is. It could attempt to write to memory which the current process doesn't have access to; this would result in an "Access violation" or "Segmentation fault" error, or something similar; possibly resulting in program termination. But it is also possible that this function will overwrite some other memory belonging to the same process – with completely unpredictable results!
And that's the whole point of "undefined behaviour". The language itself (the compiler or the interpreter) does not have to worry about special cases – for example it does not have to check that a pointer points to a valid memory area (that the current process has access to). This shifts a lot of responsibility from the language itself onto its users!
> **Comments:**
> **username_18** (2023-12-03): To make it defined behaviour, you would have to define what happens in the second case. Presumably the caller would tell f that indices 0 to 9 are valid for read/write and you are allowed to take the address at index 10, and f would check that and stop executing at the assignment with index 100.  
---
### Answer by username_2 on 2023-05-22. Score: 8
From [the ISO C++ wiki's FAQ item, "Why are some things left undefined in C++?"](https://isocpp.org/wiki/faq/big-picture#undefined):

> Because machines differ and because C left many things undefined. For details, including definitions of the terms “undefined”, “unspecified”, “implementation defined”, and “well-formed”; see the ISO C++ standard. Note that the meaning of those terms differ from their definition of the ISO C standard and from some common usage. You can get wonderfully confused discussions when people don’t realize that not everybody shares definitions.
>
> This is a correct, if unsatisfactory, answer. Like C, C++ is meant to exploit hardware directly and efficiently. This implies that C++ must deal with hardware entities such as bits, bytes, words, addresses, integer computations, and floating-point computations the way they are on a given machine, rather than how we might like them to be.

The rest of that FAQ goes on to give examples of where UB can be useful for performance reasons, to relieve standard-implementation-tooling from having to try to diagnose (report errors for) ill-formed code that is difficult to diagnose due to the C++ compilation model, and sometimes just due to implementations making use of already standardized UB to perform certain purported optimizations that they are now unwilling to change.

If you're interested in the work that the C++ committee is doing on reducing the number of things it specifies as UB, check out Study Group 12.

Also related: <https://en.cppreference.com/w/cpp/language/ub>.

You can find some other "useful everyday examples" of UB in C++ at <https://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html#optimizations>, including UB from using uninitialized variables (where allowing uninitialized variables is useful for performance), signed integer overflow being UB being useful for certain compiler optimizations, etc.

Sometimes it's done just to reserve space for future changes to the types of code that are labelled UB. Titus Winters [has done a talk on this subject](https://youtu.be/BWvSSsKCiAw?t=259), and his paper [P0921](https://wg21.link/p0921), which is a user-facing document about "Rights the Standard Library Reserves for Itself" (in the form of certain kinds of UB, mostly to carve out room for itself to change in the future).
> **Comments:**
> **username_1** (2023-07-11): Note that there should be a distinction between "reserved for future versions of the Standard" versus "UB", in that the former should allow for the possibility of a programmer writing `#if XXXX` `#define __future_identifier [[...form compatible with old-compiler, but not necessarily optimal...]]` `#endif` to write code that will benefit from new compiler features if available, but still work on old compilers.  
---
### Answer by username_1 on 2023-07-11. Score: 4
The term "Undefined Behavior" is a catch-all used to refer to any of three kinds of situations without trying to distinguish among them:

1. An implementation is called upon to process a program which, while non-portable, is correct on that platform.
2. An implementation is called upon to render a program which is erroneous, rendering questions of portability irrelevant.
3. An implementations is called upon to process a correct and portable program, but the input is erroneous in ways that cannot be reliably meaningfully handled via any standard means (e.g. using "r" mode with `fopen` to open a file that some other program has written using "wb" mode).

For an implementation to be able to flag an error in response to some erroneous construct, it would need to have some means of determining whether the construct was erroneous rather than correct but non-portable. In many cases, it would be impossible to specify language constructs in a manner that would flag all cases where they were used erroneously, without flagging cases where they were used in correct-but-non-portable fashion to accomplish tasks not provided for by the Standard.
---
### Answer by username_4 on 2023-07-10. Score: 3
Actively terminating the program may be the worst possible outcome anyway, when the broken run still gives some chances for it to accomplish the mission.

One of the cases would be a rocket controller. If the controller crashes completely, the rocket fails and this is the worst that can happen anyway. Otherwise in some cases a single error may be survivable, like in [Ariane V88 crash](https://en.wikipedia.org/wiki/Ariane_flight_V88) where it happened in the rocket alignment task no longer important at the given stage of the flight.

Of course, the range of such cases is rather limited. A more typical consumer program would benefit more from printing diagnostics and terminating, making the bugs easier to detect.
> **Comments:**
> **username_5** (2023-07-12): The problem is that this "may" be the worst possible outcome; in another situation this might be the best outcome. How can the language and/or compiler know that?  
> **username_4** (2023-07-12): Some languages have compile time switches to enable or disable assertions. All testing is normally done with assertions enabled, but for production, depends.  
> **username_1** (2023-07-22): On something like the Ariane, it may be possible in retrospect to determine that the craft would have behaved acceptably if the error had been ignored, but the time required to ascertain that almost certainly exceeded the time that would generally have been available to avoid having the rocket lose control and destroy something on the ground.  
---
### Answer by username_3 on 2024-04-20. Score: 2
Because there are no portable semantics for doing so, or specifying such semantics would unnecessarily constrain implementations.
Performance is oft-cited as the reason for undefined behavior but this tends to hide the real reason.
Undefined behavior comes from the fact that to specify everything in a programming language would be incredibly infeasible and would also not allow differentiation of implementations.
This is why some things are left undefined in programming language standards rather than them being explicitly spelled out. It both encourages a diversity of implementations that the language can support while letting those that want to target a specific superset do so by opting into certain features at the expense of loss of portability.
Signed integer overflow is a classic example. What do you get when you do
int i = INT_MAX + 1;

It certainly... is C code1, but what does it actually do. Well... it's not defined because there's no portable semantics for such an operation. Depending on the implementation, it may wraparound (as many programmers on two's complement machines expect), it may saturate (as on some DSPs), it may trap, or do any number of things. You could also conceive of an implementation that checked all additions it couldn't prove or aborted translation if it could prove such a thing (at least within some set of assumptions!).
Especially in languages that gained popularity before standardization (such as C (or languages that inherited such concerns from those languages, like C++), many compilers had and have differing ways of expressing certain idioms.

1. There are certainly some who would argue that this isn't C code at all, but it's a view that, for all intents and purposes, is an unnecessarily prescriptivist (and perhaps useless) view of what a C program is, one which is unsupported by the standard itself.
---
