views:

206

answers:

10

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 of cases of programs working in debug but not release being due to undefined behavior. It would be nice if there were a tool to at least help spot UB, so we know there's the potential for problems.

A: 

A good compiler, such as the Intel C++ compiler, should be able to spot 99% of cases of undefined behaviour. You'll need to investigate the flags and switches to use. As ever, read the manual.

High Performance Mark
99%? False sense of security. A good compiler can catch more issues than a bad one, but it's still only a fraction of all the possible cases.
jalf
+2  A: 

Well, this article covers most aspects..

Unknown
+1  A: 

I'm not aware of any software tool to detect all forms of UB. Obviously using your compiler's warnings and possibly lint or another static code checker can help a lot.

The other thing that helps a lot is simply experience: The more you program the language, the more you'll see constructs that appear suspect and be able to catch them earlier in the process.

Mark B
+2  A: 

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.

LB
+1, hadn't seen the coverity product before. Any idea how it compares with PC-Lint?
Shane MacLaughlin
coverity is really well-known in the academia world... They release each year a report of bugs found in free softwares.. I don't have much experience with PC-Lint or coverity (it seems that coverity claims to be better, but PC-Lint does the same thing -on their front-page). The only thing I know is that huge corporations require pieces of software to be coverity certified
LB
+3  A: 

Static code analysis tools such as PC-Lint can help a lot here

Shane MacLaughlin
+1: for really specific needs, you can also use Coccinelle. (love this tool). You can patch but also detect patterns
LB
@LB thanks, hadn't heard of Coccinelle, must look it up. Always keeping an eye out for new tools
Shane MacLaughlin
for coccinelle, http://coccinelle.lip6.fr/ ... there's splint too...
LB
+8  A: 

Good coding standards. Protect yourself from yourself. Here are some ideas:

1) The code MUST compile at the highest warning level without warnings. Turn on the error on warning flag for all projects. (This does mean some extra work when you use other people's libraries since they may not have done this. You'll also find there are some warnings that are just pointless...turn those off individually as your team decides)

2) ALWAYS use RAII. ALWAYS.

3) Never use C style casts! NEVER! - I think there's like a couple rare cases when you have to break this but you'll probably never find them.

4) 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'll 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.

5) ALWAYS initialize your variables at the point of declaration (or in constructor initializers when in a class).

There are more but there are some very important ones to start with. Nobody can memorize the standard. What we intermediate->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 that is universal across all languages: make your constructs easy to use correctly and difficult to use incorrectly.

Noah Roberts
+1 sound advice
FredOverflow
+2 - very sound advice.
obelix
+3- I'd say more than sound...survival advice ? but still what does happen if 6you have an identified behavior ?
LB
@LB - I assume you meant that to be "unidentified behavior" to go back to the main question? Look for violations of these coding standards. If you have UB it's almost certainly somewhere that you didn't do these things.
Noah Roberts
A: 

clang tries hard to produce warnings when undefined behavior is encountered.

Dummy00001
+3  A: 

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.

David Thornley
A: 

Simple: Don't do things that you don't know that you can do.

  • When you are unsure or have a fishy feeling, check the reference
piotr
This only works if you already know you don't know what you can do. Many people are [taught the wrong things](http://stackoverflow.com/questions/2979209/using-fflushstdin) or don't know the literally [thousands](https://connect.microsoft.com/VisualStudio/feedback/details/547423) [of](https://connect.microsoft.com/VisualStudio/feedback/details/519445) [edge](http://stackoverflow.com/questions/1831316) [cases](http://stackoverflow.com/questions/367633/367662#367662) - there is simply no way to *check the reference* for every non-trivial piece of code you write...
BlueRaja - Danny Pflughoeft
@BlueRaja: If people are taught the wrong things, they'll write bad programs no matter what. If people are willing to hit the edge without looking, then they will run afoul of edge cases, no matter what. In either of those cases, it doesn't really matter if you're talking about undefined behavior or behavior that's defined but not necessarily what you expected. It's not that difficult to learn to program in C or C++ safely, and if you're smart you check when you leave your comfort zone in any language.
David Thornley
A: 

Unfortunately, there is no way way to detect all UB. You'd have to solve the Halting Problem to do that.

The best you can do is to know as many of the rules as possible, look it up when you're in doubt, and check with other programmers (through pair programming, code reviews or just SO questions)

Compiling with as many warnings as possible, and under multiple compilers can help. And running the code through static analysis tools such as Valgrind can detect many issues.

But ultimately, no tool can detect it all.

An additional problem is that many programs actually have to rely on UB. Some API's require it, and just assume that "it works on all sane compilers". OpenGL does that in one or two cases. The Win32 API won't even compile under a standards compliant compiler.

So even if you had a magic UB-detecting tool, it would still be tripped up by the cases that aren't under your control.

jalf
I realize that, but you would also have to solve the halting problem to detect all buffer overflows - that hasn't stopped people from developing programs that do a good job of it. Interesting bit about Win32 API requiring UB though, didn't know that.
BlueRaja - Danny Pflughoeft