On 22/03/2019 11:20, Allan Sandfeld Jensen wrote: > On Freitag, 22. März 2019 11:02:39 CET Andrew Haley wrote: >> On 3/21/19 10:19 PM, Allan Sandfeld Jensen wrote: >>> From having fixed UBSAN warnings, I have seen many cases where undefined >>> behavior was performed, but where the code was aware of it and the final >>> result of the expression was well defined nonetheless. >> >> Is this belief about undefined behaviour commonplace among C programmers? >> There's nothing in the standard to justify it: any expression which contains >> UB is undefined. > > Yes, even GCC uses undefined behavior when it is considered defined for > specific architecture, whether it be the result of unaligned access, negative > shifts, etc. There is a lot of the warnings that UBSAN warns about that you > will find both in GCC itself, the Linux kernel and many other places. >
You are mixing up several things here. Behaviour can be undefined by the C standard, but defined elsewhere - in the implementation (i.e., compiler - with whatever flags are choosen), in the target ABI, in additional standards such as POSIX. If you compile C code with "gcc -fwrapv", then signed integer overflow is fully defined (as wrapping behaviour), regardless of what is written in the C standards. If you compile a "hello, world!" C program, then the C standards do not define how those words get on your screen. As far as /C/ is concerned, the workings of "printf" are undefined behaviour - because the C standard does not define them. Clearly, virtually any C program relies strongly on behaviour that is not defined by the C standards. However, expressions such as an overflowing signed shift are generally not defined /anywhere/. (It is, of course, possible for a particular C compiler to define them.) Unfortunately, it is true that some C programmers think it is fine to rely on undefined behaviour if it all seems to work fine in their tests - even when they /know/ it is undefined and unsafe. All we can do here is try to educate them - teach them that this is not good practice. It is also unfortunately the case that with older and weaker compilers, you could rely on their undocumented treatment of some kinds of undefined behaviour, and that this was the only way to get efficient results for certain coding problems. Correct, fully defined code was much slower on such compilers, while on better compilers the safe code works efficiently. If you can't avoid such old compilers, then at least use conditional compilation and pre-processor checks to ensure that the bad code is only used on the poor tools. And of course there are also lots of programmers whose knowledge of C is imperfect, or who make mistakes (and very, very few who don't!) - some code accidentally relies on the results of undefined behaviour. Even the gcc and Linux authors are not immune to the occasional bug in their code.