Others have already talked about the details of your specific example, so I will add some general information and tools to help you catch undefined behavior.
There is no final tool or method to catch undefined behavior, so even if you use all of these tools, there is no guarantee that there is something undefined in your code. But IME they will understand quite a few common problems. I do not list standard software development best practices, such as unit-testing, that you should use anyway.
clang (-analyze) has several parameters that can help catch undefined behavior, both at compile time and at run time. He has -ftrapv, he has new support for the canary, its disinfectant for the address, --fcatch- undefined -behaviour, etc.
gcc also has several options to catch undefined behavior, such as mud flaps, its address sanitizer, and stack protector.
valgrind is a fantastic tool for finding undefined runtime related memory behavior.
frama-c is a static analysis tool that can find and visualize undefined behavior. The ability to find dead code (undefined behavior can often lead to the loss of other parts of the code) is a pretty useful tool for tracking potential security issues. frama-c has many more advanced features, but it can be harder to use than ...
Other commercial static analysis tools that can catch undefined behavior exist, for example, PVS-studio, klocwork, etc. Usually they are expensive.
Compilation with various compilers and for strange architectures. If you can, why not compile and run your code on an 8-bit AVR chip? Raspberry Pi (32-bit ARM)? Compile it in javascript using emscripten and run it in V8? Doing this tends to be a practical way of catching undefined behavior, which can lead to line malfunctions (but little or nothing to catch a hiding UB, which can, for example, cause security problems).
Now, regarding the ontological reasons why undefined behavior exists ... It mainly concerns performance and usability. Many things that are UB in C allow the compiler to optimize some things that other languages cannot optimize. If you, for example, compare how java, python and C handle overflow of signed integer types, you can see that at one extreme end python completely defines it in a way convenient for the programmer - ints can actually become infinitely large. C at the other end of the spectrum leaves it undefined - your responsibility is never to overflow your integers. Java is somewhat inbetween.
But on the other hand, this means that in python it is not known what kind of work "int + int" will actually do when executed. It can execute many hundreds of instructions, make a circuit through the operating system to allocate some memory, etc. This is very bad if you care about performance, or rather, about constant performance. C at the other end of the spectrum allows the compiler to map the “+” to the main CPU instruction, which adds integers (if they exist). Of course, different CPUs can handle overflows in different ways, but since C leaves it undefined, that's fine - you, as a programmer, have to take care not to overflow your goals. This means that C gives the compiler the ability to compile your "int + int" operations with one machine instruction on almost all processors - something compilers can and can use.
Note that C does not guarantee that + actually maps directly to the native CPU instruction, it just leaves the compiler free to make it open this way - and, obviously, that any writer or compiler would be willing to take advantage. The Javas method for determining signed integer overflows is less unpredictable (in terms of performance) than pythons, but cannot cause + to turn into a single processor instruction in many types of processors where C would allow this.
Thus, C tries to embrace undefined behavior and chooses (consistent) speed and ease of implementation when other languages choose security or predictable behavior (from the point of view of programmers). This is not necessarily a good solution, for example in terms of safety / security, but in what C means. It comes down to “knowing the appropriate tool at hand”, and there are definitely many cases where C predictability gives you absolutely necessary information.