Is there any way to know if you program has undefined behavior in C++ (or even C), short of memorizing the entire spec?
The reason I ask is that I\'ve noticed a lot
Well, this article covers most aspects..
Static code analysis tools such as PC-Lint can help a lot here
It's not possible to detect undefined behavior in all cases. For example, consider x = x++ + 1;
. If you're familiar with the language, you know it's UB. Now, *p = (*p)++ + 1;
is obviously also UB, but what about *q = (*p)++ + 1;
? That's UB if q == p
, but other than that it's defined (if awkward-looking). In a given program, it might well be possible to prove that p
and q
will never be equal when reaching that line, but that can't be done in general.
To help spot UB, use all of the tools you've got. Good compilers will warn for at least the more obvious cases, although you may have to use some compiler options for best coverage. If you have further static analysis tools, use them.
Code reviews are also very good for spotting such problems. Use them, if you've got more than one developer available.
clang
tries hard to produce warnings when undefined behavior is encountered.
Good coding standards. Protect you from yourself. Here are some ideas:
The code must compile at the highest warning level... without warnings. (In other words, your code must not set off any warnings at all when set to the highest level.) Turn on the error on warning flag for all projects.
This does mean some extra work when you use other peoples' libraries since they may not have done this. You will also find there are some warnings which are pointless... turn those off individually as your team decides.
Always use RAII.
Never use C style casts! Never! - I think there's like a couple rare cases when you have to break this but you will probably never find them.
If you must reinterpret_cast
or cast to void
then use a wrapper to make sure you're always casting to/from the same type. In other words, wrap your pointer/object in a boost::any
and cast a pointer to it into whatever you need and on the other side do the same. Why? Because you will always know what type to reinterpret_cast
from and the boost::any
will enforce that you've cast to the correct type after that. It's the safest you can get.
Always initialize your variables at the point of declaration (or in constructor initializers when in a class).
There are more but those are some very important ones to start with.
Nobody can memorize the standard. What we intermediate to advanced C++ programmers do is use constructs we know are safe and protect ourselves from our human nature... and we don't use constructs that are not safe unless we have to and then we take extra care to make sure the danger is all wrapped up in a nice safe interface that is tested to hell and back.
One important thing to remember which is universal across all languages is to:
make your constructs easy to use correctly and difficult to use incorrectly
I think you can use one tool from coverity to spot bugs which are going to lead to undefined behavior.
I guess you could use theorem provers (i only know Coq) to be sure your program does what you want.