The C language standard precisely specifies the observable behavior of C language programs, except for the ones in the following categories:
(Note: Strictly conforming programs do not depend on any unspecified, undefined, or implementation-defined behavior)
The compilers are required to issue diagnostic messages (either errors or warnings) for any programs that violates any C syntax rule or semantic constraint, even if its behavior is specified as undefined or implementation-defined or if the compiler provides a language extension that allows it to accept such program. Diagnostics for undefined behavior are not otherwise required.
[edit] UB and optimizationBecause correct C programs are free of undefined behavior, compilers may produce unexpected results when a program that actually has UB is compiled with optimization enabled:
For example,
[edit] Signed overflowint foo(int x) { return x + 1 > x; // either true or UB due to signed overflow }
may be compiled as (demo)
[edit] Access out of boundsint table[4] = {0}; int exists_in_table(int v) { // return 1 in one of the first 4 iterations or UB due to out-of-bounds access for (int i = 0; i <= 4; i++) if (table[i] == v) return 1; return 0; }
May be compiled as (demo)
exists_in_table: mov eax, 1 ret[edit] Uninitialized scalar
_Bool p; // uninitialized local variable if (p) // UB access to uninitialized scalar puts("p is true"); if (!p) // UB access to uninitialized scalar puts("p is false");
May produce the following output (observed with an older version of gcc):
size_t f(int x) { size_t a; if (x) // either x nonzero or UB a = 42; return a; }
May be compiled as (demo)
[edit] Invalid scalarint f(void) { _Bool b = 0; unsigned char* p = (unsigned char*)&b; *p = 10; // reading from b is now UB return b == 0; }
May be compiled as (demo)
[edit] Null pointer dereferenceint foo(int* p) { int x = *p; if (!p) return x; // Either UB above or this branch is never taken else return 0; } int bar() { int* p = NULL; return *p; // Unconditional UB }
may be compiled as (demo)
foo: xor eax, eax ret bar: ret[edit] Access to pointer passed to realloc
Choose clang to observe the output shown
#include <stdio.h> #include <stdlib.h> int main(void) { int *p = (int*)malloc(sizeof(int)); int *q = (int*)realloc(p, sizeof(int)); *p = 1; // UB access to a pointer that was passed to realloc *q = 2; if (p == q) // UB access to a pointer that was passed to realloc printf("%d%d\n", *p, *q); }
Possible output:
[edit] Infinite loop without side-effectsChoose clang to observe the output shown
#include <stdio.h> int fermat() { const int MAX = 1000; // Endless loop with no side effects is UB for (int a = 1, b = 1, c = 1; 1;) { if (((a * a * a) == ((b * b * b) + (c * c * c)))) return 1; ++a; if (a > MAX) { a = 1; ++b; } if (b > MAX) { b = 1; ++c; } if (c > MAX) c = 1; } return 0; } int main(void) { if (fermat()) puts("Fermat's Last Theorem has been disproved."); else puts("Fermat's Last Theorem has not been disproved."); }
Possible output:
Fermat's Last Theorem has been disproved.[edit] References
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4