Undefined, unspecified and implementation-defined behavior

70,756

Solution 1

Undefined behavior is one of those aspects of the C and C++ language that can be surprising to programmers coming from other languages (other languages try to hide it better). Basically, it is possible to write C++ programs that do not behave in a predictable way, even though many C++ compilers will not report any errors in the program!

Let's look at a classic example:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

The variable p points to the string literal "hello!\n", and the two assignments below try to modify that string literal. What does this program do? According to section 2.14.5 paragraph 11 of the C++ standard, it invokes undefined behavior:

The effect of attempting to modify a string literal is undefined.

I can hear people screaming "But wait, I can compile this no problem and get the output yellow" or "What do you mean undefined, string literals are stored in read-only memory, so the first assignment attempt results in a core dump". This is exactly the problem with undefined behavior. Basically, the standard allows anything to happen once you invoke undefined behavior (even nasal demons). If there is a "correct" behavior according to your mental model of the language, that model is simply wrong; The C++ standard has the only vote, period.

Other examples of undefined behavior include accessing an array beyond its bounds, dereferencing the null pointer, accessing objects after their lifetime ended or writing allegedly clever expressions like i++ + ++i.

Section 1.9 of the C++ standard also mentions undefined behavior's two less dangerous brothers, unspecified behavior and implementation-defined behavior:

The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine.

Certain aspects and operations of the abstract machine are described in this International Standard as implementation-defined (for example, sizeof(int)). These constitute the parameters of the abstract machine. Each implementation shall include documentation describing its characteristics and behavior in these respects.

Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine.

Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer). [ Note: this International Standard imposes no requirements on the behavior of programs that contain undefined behavior.end note ]

Specifically, section 1.3.24 states:

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).

What can you do to avoid running into undefined behavior? Basically, you have to read good C++ books by authors who know what they're talking about. Avoid internet tutorials. Avoid bullschildt.

Solution 2

Well, this is basically a straight copy-paste from the standard

3.4.1 1 implementation-defined behavior unspecified behavior where each implementation documents how the choice is made

2 EXAMPLE An example of implementation-defined behavior is the propagation of the high-order bit when a signed integer is shifted right.

3.4.3 1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

2 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).

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.

3.4.4 1 unspecified behavior use of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance

2 EXAMPLE An example of unspecified behavior is the order in which the arguments to a function are evaluated.

Solution 3

Maybe easy wording could be easier for understanding than the rigorous definition of the standards.

implementation-defined behavior
The language says that we have data-types. The compiler vendors specify what sizes shall they use, and provide a documentation of what they did.

undefined behavior
You are doing something wrong. For example, you have a very large value in an int that doesn't fit in char. How do you put that value in char? actually there is no way! Anything could happen, but the most sensible thing would be to take the first byte of that int and put it in char. It is just wrong to do that to assign the first byte, but thats what happens under the hood.

unspecified behavior
Which function of these two is executed first?

void fun(int n, int m);

int fun1() {
    std::cout << "fun1";
    return 1;
}
int fun2() {
    std::cout << "fun2";
    return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

The language doesn't specify the evaluation, left to right or right to left! So an unspecified behavior may or mayn't result in an undefined behavior, but certainly your program should not produce an unspecified behavior.


@eSKay I think your question is worth editing the answer to clarify more :)

for fun(fun1(), fun2()); isn't the behaviour "implementation defined"? The compiler has to choose one or the other course, after all?

The difference between implementation-defined and unspecified, is that the compiler is supposed to pick a behavior in the first case but it doesn't have to in the second case. For example, an implementation must have one and only one definition of sizeof(int). So, it can't say that sizeof(int) is 4 for some portion of the program and 8 for others. Unlike unspecified behavior, where the compiler can say OK I am gonna evaluate these arguments left-to-right and the next function's arguments are evaluated right-to-left. It can happen in the same program, that's why it is called unspecified. In fact, C++ could have been made easier if some of the unspecified behaviors were specified. Take a look here at Dr. Stroustrup's answer for that:

It is claimed that the difference between what can be produced giving the compiler this freedom and requiring "ordinary left-to-right evaluation" can be significant. I'm unconvinced, but with innumerable compilers "out there" taking advantage of the freedom and some people passionately defending that freedom, a change would be difficult and could take decades to penetrate to the distant corners of the C and C++ worlds. I am disappointed that not all compilers warn against code such as ++i+i++. Similarly, the order of evaluation of arguments is unspecified.

IMO far too many "things" are left undefined, unspecified, that's easy to say and even to give examples of, but hard to fix. It should also be noted that it is not all that difficult to avoid most of the problems and produce portable code.

Solution 4

From the official C Rationale Document

The terms unspecified behavior, undefined behavior, and implementation-defined behavior are used to categorize the result of writing programs whose properties the Standard does not, or cannot, completely describe. The goal of adopting this categorization is to allow a certain variety among implementations which permits quality of implementation to be an active force in the marketplace as well as to allow certain popular extensions, without removing the cachet of conformance to the Standard. Appendix F to the Standard catalogs those behaviors which fall into one of these three categories.

Unspecified behavior gives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program.

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.

Implementation-defined behavior gives an implementor the freedom to choose the appropriate approach, but requires that this choice be explained to the user. Behaviors designated as implementation-defined are generally those in which a user could make meaningful coding decisions based on the implementation definition. Implementors should bear in mind this criterion when deciding how extensive an implementation definition ought to be. As with unspecified behavior, simply failing to translate the source containing the implementation-defined behavior is not an adequate response.

Solution 5

Undefined Behavior vs. Unspecified Behavior has a short description of it.

Their final summary:

To sum up, unspecified behavior is usually something you shouldn't worry about, unless your software is required to be portable. Conversely, undefined behavior is always undesirable and should never occur.

Share:
70,756
Thecruz
Author by

Thecruz

Updated on December 23, 2021

Comments

  • Thecruz
    Thecruz over 2 years

    What is undefined behavior (UB) in C and C++? What about unspecified behavior and implementation-defined behavior? What is the difference between them?

  • Thecruz
    Thecruz over 14 years
    What's the difference between implementation-defined and unspecified behaviour?
  • AnT stands with Russia
    AnT stands with Russia over 14 years
    @Zolomon: Just like it says: basucally the same thing, except that in case of implementation-defined the implementation is requred to document (to guarantee) what exactly is going to happen, while in case of unspecified the implementation is not required to document or guarantee anything.
  • sbi
    sbi over 14 years
    @Zolomon: It's reflected in the difference between 3.4.1 and 2.4.4.
  • Lazer
    Lazer over 14 years
    for fun(fun1(), fun2()); isn't the behaviour "implementation defined"? The compiler has to choose one or the other course, after all?
  • Lazer
    Lazer over 14 years
    @AraK: thanks for the explaining. I understand it now. Btw, "I am gonna evaluate these arguments left-to-right and the next function's arguments are evaluated right-to-left" I understand this can happen. Does it really, with compilers that we use these days?
  • Khaled Alshaya
    Khaled Alshaya over 14 years
    @eSKay You have to ask a guru about this who got his hands dirty with many compilers :) AFAIK VC evaluates arguments right-to-left always.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    It's a weird fact that resulted from the merge that this answer only covers C++ but this question's tags includes C. C has a different notion of "undefined behavior": It will still require the implementation to give diagnostic messages even if behavior is also stated to be undefined for certain rule violations (constraint violations).
  • sbi
    sbi over 13 years
    @Johannes: That's bad, indeed. Why not link to several answers from the question?
  • supercat
    supercat over 13 years
    @Lazer: It can definitely happen. Simple scenario: foo(bar, boz()) and foo(boz(), bar), where bar is an int and boz() is a function returning int. Assume a CPU where parameters are expected to be passed in registers R0-R1. Function results are returned in R0; functions may trash R1. Evaluating "bar" before "boz()" would require saving a copy of bar somewhere else before calling boz() and then loading that saved copy. Evaluating "bar" after "boz()" will avoid a memory store and re-fetch, and is an optimization many compilers would do regardless of their order in the argument list.
  • Nikolai Ruhe
    Nikolai Ruhe over 11 years
    I don't know about C++ but the C standard says that a conversion of an int to a char is either implementation defined or even well defined (depending on the actual values and signedness of types). See C99 §6.3.1.3 (unchanged in C11).
  • Benoit
    Benoit over 11 years
    In the code snippet, is this case (modifying a string litteral) an undefined behaviour because the string litteral might have been allocated to a read-only text segment?
  • fredoverflow
    fredoverflow over 11 years
    @Benoit It is undefined behavior because the standard says it's undefined behavior, period. On some systems, indeed string literals are stored in the read-only text segment, and the program will crash if you try to modify a string literal. On other systems, the string literal will indeed appear change. The standard does not mandate what has to happen. That's what undefined behavior means.
  • Nicholas Wilson
    Nicholas Wilson about 11 years
    I suggest mentioning compiler optimisations: the compilers can and do completely remove entire blocks with undefined behaviour, whereas relying on unspecified behaviour is stupid because the compiled code might not do what you expect, but it can't be removed. (GCC at -O2 uses certain undefined behaviours to mark other code in the block as dead!)
  • Pacerier
    Pacerier over 10 years
    @FredOverflow, Why does a good compiler allow us to compile code that gives undefined behavior? Exactly what good can compiling this kind of code give? Why didn't all good compilers give us a huge red warning sign when we are trying to compile code that gives undefined behavior?
  • AJMansfield
    AJMansfield over 10 years
    So could a (standard-compliant) compiler output a binary that nukes the hard drive/bricks your device/installs malware when you give it a program with undefined behavior?
  • Tim Seguine
    Tim Seguine over 10 years
    @Pacerier There are certain things that are not checkable at compile time. For example it is not always possible to guarantee that a null pointer is never dereferenced, but this is undefined.
  • slebetman
    slebetman about 10 years
    Beyond just being proficient at C++ legalese/spec (read good books) you also need to read your compiler manuals to understand what implementation defined and unspecified behavior you're working in. Especially helpful for microcontrollers since they often use proprietary compilers which makes it harder to google for help when you encounter something weird.
  • Celeritas
    Celeritas about 10 years
    Is it possible for a compiler to leave undefined behavior un implemented? What would happen? For example a[i] = i++ is undefined. Is it possible for a compiler to be sophisticated enough to be programed for this event? What would happen, it would just output random assembly?
  • Deduplicator
    Deduplicator almost 10 years
    @Celeritas: If the compiler recognizes UB, it can prune that branch of execution. If it does not, whatever it outputs might be non-sensical and give the processor severe indigestion.
  • Celeritas
    Celeritas over 9 years
    " those aspects of the C++ language that can be surprising to programmers coming from other languages. Basically, it is possible to write C++ programs that do not behave in a predictable way" this is not a very good sentence because saying this makes it sound like undefined behaviour is a synonym for non-deterministic.
  • supercat
    supercat about 9 years
    I think it's important to note that the practical meaning of "undefined" has changed over the last few years. It used to be that given uint32_t s;, evaluating 1u<<s when s is 33 could be expected to maybe yield 0 or maybe yield 2, but not do anything else wacky. Newer compilers, however, evaluating 1u<<s may cause a compiler to determine that because s must have been less than 32 beforehand, any code before or after that expression which would only be relevant if s had been 32 or greater may be omitted.
  • supercat
    supercat about 9 years
    @Celeritas: Hyper-modern compilers can do better than that. Given int foo(int x) { if (x >= 0) launch_missiles(); return x << 1; } a compiler can determine that since all means of invoking the function that don't launch the missiles invoke Undefined Behavior, it can make the call to launch_missiles() unconditional.
  • Destructor
    Destructor almost 9 years
    @AnT: what does it mean that "this international Standard imposes no requirements" in undefined behavior?
  • Mark
    Mark over 8 years
    @Celeritas, undefined behavior can be non-deterministic. For example, it is impossible to know ahead of time what the contents of uninitialized memory will be, eg. int f(){int a; return a;}: the value of a may change between function calls.
  • Martin Bonner supports Monica
    Martin Bonner supports Monica over 8 years
    @AJMansfield: Absolutely. More to the point, it can produce a program which has a security vulnerability, which allows an attacker to install malware. Probably most security vulnerabilities are a result of exploiting undefined behaviour.
  • Tom Swirly
    Tom Swirly about 8 years
    But handling undefined behavior in a nice way doesn't come for free. The whole reason that modern compilers exhibit such bizarre behavior in some cases of UB is that they are relentlessly optimizing, and to do the best job at that, they have to be able to assume that UB never occurs.
  • Tom Swirly
    Tom Swirly about 8 years
    But the fact that << is UB on negative numbers is a nasty little trap and I'm glad to be reminded of that!
  • supercat
    supercat about 8 years
    @TomSwirly: Unfortunately, compiler writers don't care that offering loose behavioral guarantees beyond those mandated by the Standard can often allow a massive speed boost compared with requiring that code avoid at all costs anything not defined by the Standard. If a programmer doesn't care whether i+j>k yields 1 or 0 in cases where the addition overflows, provided it has no other side effects, a compiler may be able to make some massive optimizations that would not be possible if the programmer wrote the code as (int)((unsigned)i+j) > k.
  • supercat
    supercat about 8 years
    @TomSwirly: To them, if compiler X can take a strictly-conforming program to do some task T and yield an executable that is 5% more efficient than compiler Y would yield with that same program, that means X is better, even if Y could generate code that did the same task three times as efficiently given a program that exploits behaviors that Y guarantees but X does not.
  • supercat
    supercat about 8 years
    Hyper-modern compiler writers also regard "undefined behavior" as giving compiler writers license to assume that programs will never receive inputs that would cause Undefined Behavior, and to arbitrarily change all aspects of how the programs behave when they receive such inputs.
  • supercat
    supercat almost 8 years
    @MartinBonner: Even more to the point, it can lead a compiler to omit bounds checks which would only be relevant if the program would invoke Undefined Behavior, with the effect that even if the original UB would have been benign (e.g. an overflow in a computation whose result ends up getting ignored) the omitted bounds check could allow the out-of-range value to trigger dangerous behavior.
  • supercat
    supercat almost 8 years
    @Destructor: Given that C was in wide use for years before the first Standard was written, the phrase "the Standard imposes no requirements" used to mean that implementations similar to those that predated the standard should behave similarly to such pre-existing implementations, absent a compelling reason to do otherwise. In many cases, things were left UB to avoid mandating that existing implementations to change in ways that might make them less efficient or break existing code.
  • supercat
    supercat almost 8 years
    Another point I just noticed: C89 did not use the term "extension" to describe features that were guaranteed on some implementations but not others. The authors of C89 recognized that the majority of then-current implementations would treat signed arithmetic and unsigned arithmetic identically except when the results were used in certain ways, and such treatment applied even in case of signed overflow; they did not list that as a common extention in Annex J2, however, which suggests to me they viewed it as a natural state of affairs, rather than an extension.
  • northerner
    northerner over 7 years
    @AnT then what is the difference between unspecified and undefined behaviour, if neither guarantee any particular thing to happen? Is it that for unspecified, the same thing must happen each time the program is run on the same machine?
  • AnT stands with Russia
    AnT stands with Russia over 7 years
    @northerner As the quote states, unspecified behavior is usually restricted to a limited set of possible behaviors. In some cases you might even come to conclusion that all of these possibilities are acceptable in the given context, in which cases unspecified behavior is not a problem at all. Undefined behavior is completely unrestricted (e.b. "the program may decide to format your hard drive"). Undefined behavior is always a problem.
  • AnT stands with Russia
    AnT stands with Russia over 7 years
    @northerner: Doesn't say what exactly? By "a limited set of possible behaviors" I'm referring to the part where the quite says "provides two or more possibilities".
  • northerner
    northerner over 7 years
    @AnT That seems basically the same to undefined behavior. Assuming a computer has a finite number of states, the point "limited set of possible behaviors" is moot. I guess the implicit distinction is unspecified behavior has a much smaller set of possibilities than undefined.
  • AnT stands with Russia
    AnT stands with Russia over 7 years
    @northerner : Nope. The key point of in case of "unspecified behavior" is the fact that the set of possible behaviors is actually specified and restricted. And typically it is an easily overseeable set. What's unspecified is which specific possibility (out of that set) will be chosen by the compiler. The implicit distinctions is that the set of posibilities is typically restgricted and [fairly] platform-independent. In case of undefined behavior is unrestricted (and therefore is platform-dependent under your "finite number of states" reasoning).
  • AnT stands with Russia
    AnT stands with Russia over 7 years
    The language specification does not mention "formatting your hard drive" anywhere. Yet, it is not a possibility undfer any of unspecified behaviors, but a possibility under any of undefined behaviors. This emphasizes the distinction rather well.
  • northerner
    northerner over 7 years
    @AnT I advise to remove this info from the comments and copy it into the answer. I would myself but edit is disabled.
  • supercat
    supercat almost 7 years
    There are two kinds of compilers: those which, unless explicitly documented otherwise, interpret most of the Standard's forms of Undefined Behavior as falling back upon characteristic behaviors documented by the underlying environment, and those which by default only usefully expose behaviors which the Standard characterizes as Implementation-Defined. When using compilers of the first type, many things of the first type can be done efficiently and safely using UB. Compilers for the second type will only be suitable for such tasks if they provide options to guarantee behavior in such cases.
  • Jenny T-Type
    Jenny T-Type over 6 years
    AFAIK, Unspecified: Here's a list of things you can do if you find this, pick the one you like the most. Implementation-defined: do what you want to do to solve this problem, as long as it's effective. Undefined: do what you want. or don't do anything, I don't care. I'm not here to babysit careless programmers.
  • UKMonkey
    UKMonkey about 6 years
    @fredoverflow continuing your string literal example - if a string literal is changed; it could impact other variables - take the example above and add auto q= "hello!\n"; q now may or may not be "hello!\n" or "yellow\n" and worse; q may or may not be in the same function as p.
  • supercat
    supercat almost 6 years
    @JennyT-Type: There is nothing "careless" about using non-portable constructs on quality compilers that support them. I'm not sure why people read the phrase "nonportable or erroneous" as implying "erroneous", or why compiler writers don't see the ability to usefully and efficiently process a wide range of non-portable programs as a trait of quality compilers.
  • Jenny T-Type
    Jenny T-Type over 5 years
    @supercat. Well, I could agree, C is a language for people that know what they're doing after all. right?. The thing is, chances are that we don't always know exactly what we're doing, otherwise the 'C' tag in SO wouldn't make sense at all. I've even came across questions here that make me thing the op was learning C by reading "C for Dummies" or similar material. Those people have no idea, whatsoever, of what they're doing. If you know your platform well enough to know what to expect every time you invoke undefined behavior, then congrats, you're a better programmer than me.
  • supercat
    supercat over 5 years
    @JennyT-Type: The Standard suggests as a typical behavior "Behave in a documented fashion characteristic of the environment". Historically, that used to mean that in most cases where an environment had characteristic behaviors that might be useful, implementations would expose them. Unfortunately, the Standard has never bothered to specify any means of saying "give me the platform behavior" since they figured that implementations where that would be useful would naturally do so without the programmer having to do anything special.
  • supercat
    supercat almost 5 years
    @AnT: I wouldn't describe the distinction as "platform independent", but quite the opposite. On a platform where reads of various addresses trigger hardware actions (many I/O cards for the Apple II, including the disk controller card, work that way), it should not be surprising if stray reads trigger unwanted hardware actions, including erasing disks. That does not imply programmers should be required to avoid stray reads at all costs when targeting platforms where they would, at worst, yield a meaningless value.
  • mtraceur
    mtraceur over 3 years
    @Pacarier Because undefined behavior was deliberate room left for useful or intended variations between systems. Systems existed where dereferencing the zero address gets zero values, and dereferencing the null pointer (undefined behavior!) to get automatic zero values was very useful on those systems. Systems exist where the hardware does saturating arithmetic, and overflowing or underflowing signed integers (undefined behavior!) is useful on that hardware. C abstracts hardware variations, but a major use of C is to access functionality and tradeoffs unique to various hardware.
  • Abhishek Mane
    Abhishek Mane about 3 years
    @fredoverflow code int a; char s[20]; cin>>a; cin>>s; input: 23bonapart + Enter key hit now, code cout<<a; cout<<s; Output: 23 and bonapart here I am not separating input data with delimiters but output is correct, so my question is it really distinguishing inputs or is it case of Undefined behavior "If there is a "correct" behavior according to your mental model of the language, that model is simply wrong " ?
  • Ekrem Dinçel
    Ekrem Dinçel about 3 years
    @mark A good compiler might just delete every code path that calls the f function because it invokes undefined behaviour: reading an uninitialized variable. Your example doesn't seems non-deterministic to me.
  • supercat
    supercat about 3 years
    @EkremDinçel: For some definitions of "good", maybe. On the other hand, on many platforms, a compiler that guarantees that reading an uninitialized value of type int will simply yield an unspecified value of that type without side effects, given a program that exploits that guarantee, may be able to generate more efficient code to accomplish a task than could be generated for a program that didn't exploit it.
  • Ekrem Dinçel
    Ekrem Dinçel about 3 years
    @supercat I don't remember why exactly I wrote my above comment. Looking at it again, undefined behavior can absolutely cause non-determinism at runtime. I guess I just wanted to point out that what Mark wrote is not the only possible outcome of that code; and since the code includes UB, one shouldn't rely on it unless they know what they are doing.
  • supercat
    supercat about 3 years
    @EkremDinçel: A fundamental problem with the evolution of the C language is that the authors of the Standard expected that compilers writers would want people to buy their products in a competitive marketplace, and there was thus no need to forbid them from behaving in ways that would be universally recognized as obtuse. It was never intended that programmers should have to jump through hoops to prevent obtuse nln-commercial compilers breaking code that all commercial compilers would have processed identically.
  • supercat
    supercat about 3 years
    Constructs which the Standard characterizes as Undefined Behavior are "non-portable or erroneous", but the Standard makes no attempt to distinguish those which are erroneous from those which are non-portable but correct when processed by the implementations for which they were written or others that are compatible with them.
  • Gabriel Staples
    Gabriel Staples over 2 years
    I wish you had a traceable reference to the source document for these quotes so they could be more-easily verified and tracked for changes.
  • PSkocik
    PSkocik over 2 years
    @supercat If I may ask, what is the optimization applicable to i+j>k in such a case?
  • supercat
    supercat over 2 years
    @PSkocik: Consider as a simple scenario a situation where a i, j, and k are arguments to a function a compiler is expanding in line for a function call foo(x, y, x). In that scenario, a compiler could replace i+j > k with x+y > x, which it could in turn replaced with y > 0, skipping the addition entirely, eliminating any dependency on the value of x, and possibly allowing a compiler to eliminate the comparison and any dependency upon the exact value of y if it can determine that y will always be positive.
  • PSkocik
    PSkocik over 2 years
    Thanks. That's an interesting take I haven't seen before: Optimize as if signed overflow didn't happen but don't make the allow the program to become malformed if you do see it happen. (BTW, I think it's a better example than the one in your answer (v << pow can be losslesly rewritten into (int)((unsigned)v<<pow))).
  • supercat
    supercat over 2 years
    @PSkocik: I was feeling a bit snarky when I wrote that answer, but regard as obtuse and dangerous the notion that the Standard's failure to mandate behavior in a particular situation is an invitation for compilers to assume that nothing they might do would be viewed by their users as unacceptable. The Standard deliberately gives implementations which are intended for specialized tasks great latitude to behave in ways that would make them unsuitable for most others; the fact that the Standard allows implementations to do something does not imply any judgement that any implementations should.
  • supercat
    supercat over 2 years
    @PSkocik: As for v << pow, should an implementation be allowed to, or forbidden from, replacing e.g. (v << pow) > 0 with v > 0 if v is signed? If the programmer had cast v to unsigned, such substitution would be forbidden, but all cases where the former expression's behavior wouldn't match the latter are regarded as UB under C99 and later. I think that allowing programmers to write v << pow in cases where C89 would define the behavior but the substitution would be useful, would make more sense than requiring them to block the optimization by using the unsigned cast.
  • supercat
    supercat over 2 years
    @GabrielStaples: I think the latest published Rationale document is at open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf (from what I can tell, no subsequent version of the Standard has a published Rationale document).