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;
}
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);
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);
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!