&gt; The main reason I keep coming back to assembly language with C, is that C cannot do anything that assembly language cannot do.<p>That&#x27;s basically true.  Except that an optimizer is allowed to transform things as long as the observable behavior remains the same.  And undefined behavior means more than &quot;just that one line is undefined&quot; ( <a href="http:&#x2F;&#x2F;blog.llvm.org&#x2F;2011&#x2F;05&#x2F;what-every-c-programmer-should-know.html" rel="nofollow">http:&#x2F;&#x2F;blog.llvm.org&#x2F;2011&#x2F;05&#x2F;what-every-c-programmer-should-...</a> , <a href="http:&#x2F;&#x2F;blog.llvm.org&#x2F;2011&#x2F;05&#x2F;what-every-c-programmer-should-know_14.html" rel="nofollow">http:&#x2F;&#x2F;blog.llvm.org&#x2F;2011&#x2F;05&#x2F;what-every-c-programmer-should-...</a> , <a href="http:&#x2F;&#x2F;blog.llvm.org&#x2F;2011&#x2F;05&#x2F;what-every-c-programmer-should-know_21.html" rel="nofollow">http:&#x2F;&#x2F;blog.llvm.org&#x2F;2011&#x2F;05&#x2F;what-every-c-programmer-should-...</a> ).  These two facts conspire to make undefined behavior surprising to many programmers.<p>For instance -- using one of Lattner&#x27;s examples -- on some architectures it&#x27;s a little expensive to check if a loop variable had wrapped around.  So the optimizer will omit the check for wraparound if it can prove wraparound is impossible.  That is, if it can prove that n + 1 &gt; n.  But the Standard only requires wraparound for unsigned integer types.  So the optimizer can also omit the check if it can only prove <i>either</i> n + 1 &gt; n or n is a signed integer.  In this case, a bounded loop turns into an infinite loop, but &quot;undefined behavior&quot; includes that kind of transformation.<p>More to the point, Linux had a severe security bug where they dereferenced a pointer, then checked if the pointer was NULL before returning the value ( <a href="https:&#x2F;&#x2F;lwn.net&#x2F;Articles&#x2F;342330&#x2F;" rel="nofollow">https:&#x2F;&#x2F;lwn.net&#x2F;Articles&#x2F;342330&#x2F;</a> ).  The optimizer removed the check for NULL because if the pointer was valid when it was dereferenced, the check was unnecessary; and if the pointer was NULL when it was dereferenced, then dereferencing it was undefined behavior and &quot;remove an NULL check&quot; is a valid transformation in undefined behavior.  Then moving that load to a different point in the function is also a valid transformation (as long as it doesn&#x27;t affect observable behavior), and a few more transformations could make the NULL dereference do something completely different from what you expect.