mugshot, larkspur

g13n


Gopal Venkatesan's Former Journal

My writings on free/open source software and other technologies


Previous Entry Share Next Entry
A “well defined” undefined behaviour in C++
mugshot, larkspur
g13n

This content has been updated and moved to a new place.


Having found this one very recently by first-hand experience, I was quite stumped by the behaviour of C++ (compilers) described below. It is a surprise because of my understanding that C++ is very strict compared to its blood brother C. And of course C++ is more strict compared to C, but there are places where the standard has left the behaviour to the implementation.

The “well defined” undefined behaviour that I’m referring to in this post is about return values from functions. We all know that, in C++ the return value from a function (or method) should match its declared return type. But its true that most (not all) C++ compilers don’t check all code paths for the return from within a function. Isn’t this surprising? Well, take a look at the following code that generates a Fibonacci series.

  1: #include <iostream>
  2: #include <vector>
  3: 
  4: using std::cout;
  5: using std::endl;
  6: using std::vector;
  7: 
  8: vector<int> fibo(const int max);
  9: 
 10: int main()
 11: {
 12:         vector<int> f = fibo(30);
 13:         for (vector<int>::iterator i = f.begin(); i != f.end(); ++i)
 14:                 cout << *i << ' ';
 15:         cout << endl;
 16:         return 0;
 17: }
 18: 
 19: vector<int> fibo(const int max)
 20: {
 21:         vector<int> rv;
 22: 
 23:         int i = 1, sum, j;
 24: 
 25:         sum = j = 0;
 26:         rv.push_back(i);
 27:         
 28:         while (sum <= max) {
 29:                 sum = i + j;
 30:                 if (sum <= max)
 31:                         rv.push_back(sum);
 32:                 j = i;
 33:                 i = sum;
 34:         }
 35: }

 


If you look closely, you’ll notice that I missed the “return” statement in the “fibo” function. If you think that compiling this will produce an error, you’re wrong. Well it depends on the compiler that you used to compile this program. On most Unix and Unix-like systems (including GNU/Linux) the default system compiler is the GNU project C++ compiler. So, if you tried compiling this with the GNU project C++ compiler, at most you might get a warning (depending upon the version and the compiler flags), but it will produce the binary!


If you think GNU C++ compiler sucks, even Sun Studio Express C++ compiler produces the same result i.e., it emits a warning but produces “a.out”.


I was stumped because I didn’t expect this. Of course you don’t realize the mistake until you the run the program and see disastrous results. On the systems which I tried, almost all of them ran for a few seconds before the program “segfaulted”!


What is the reason for this?


The C++ standard says



Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior
in a value-returning function


Most compilers tend to avoid this check because checking every code path for return may prove to be difficult.


Java compiler does this explicit checking because the standard mandates this check. But that makes it a little brittle because you can easily fool the compiler and the compiler will annoy you at times. The following example will refuse to compile with a Java compiler.

  1: public class ReturnTest {
  2:     public static void main(String[] args) {
  3:         if (args.length == 1) {
  4:             String greeting = (new ReturnTest()).greet(args[0]);
  5:             System.out.println(greeting);
  6:         }
  7:     }
  8: 
  9:     public String greet(final String name) {
 10:         if (name.length() < 3) {
 11:             System.err.println("Name should be at least 3 characters");
 12:             System.exit(1);
 13:         } else {
 14:             return "Hello, " + name;
 15:         }
 16:     }
 17: }

 


Agreed that its a bad program, but I wanted to show an example. Even though the control flow will not go to line 16, Java compiler will refuse to compile this program without you adding a dummy return statement.


A better C++ compiler?


Did I say that not all compilers behaved the same way. Even though I rarely use Windows for developing software and/or try out such things, I do have the free Visual C++ Express Edition installed. If you try compiling the above program using Visual C++ compiler it fails with a fatal error reporting the function “fibo must return a value”.


  • 1
You don't need a complex code path to demonstrate this. A simple "int f() {} " is also legitimate code. And if you really want gcc to stop you from doing this, go for the infamous "-Wall -Werror" ;-)

Of course I know the simplest example would suffice, I had posted the example where it segfaulted and that is when I knew about this.

On gcc's "-Wall -Werror" yes it will, but my point is that MSVC is a better C++ compiler than GNU G++ *cough*.

Yeah, that's true. MSVC is indeed the gold standard in x86 based compilers. I think they really took off when they hired the top C++ guys like Herb Sutter (maybe it was just him, but I think they snagged a few more) and produced MSVC 7.

It's not surprising either, considering their critical dependence on C++. You can't realistically write all of the Office Suite, Windows OS, MSSQL, etc. in a bytecode interpreted language, right?

  • 1
?

Log in