Bugs in C++ — returned memory refs

One of the major causes of bugs in C++ is bad memory pointers and references. This is one reason people like garbage collectors so much, along with memory leaks and code simplification.

The compilers do try to watch for obvious memory mistakes. For example, all of the following functions generate warnings compiling with VC++9. The warnings are not errors; they do not stop you from linking and running. You can live dangerously if you want.

// these compile with warnings
const int& unsafe_ref_a( ) { int a; return a; }
const int& unsafe_ref_b( ) { const int a = 1; return a; }
const int& unsafe_ref_c( ) { return 1; }
const int& unsafe_ref_e( ) { return (1 + 2); }

const int* unsafe_ptr_a( ) { int a; return &a; }

Oddly, the following ARE errors — they do not compile. This seems inconsistent with the functions above being only warnings, since they’re the same with pointers used instead of refs. But the rules of pointers and refs are different, and the functions are obviously wrong anyway.

// these don't compile
const int* unsafe_ptr_b( ) { const int a = 1; return &a; }
const int* unsafe_ptr_c( ) { return &1; }
const int* unsafe_ptr_e( ) { return &(1 + 2); }

You can get around all the warnings and errors though, and write code with all the mistakes displayed above and with none of the compiler complaints. C++ tries hard to be type safe, but hardly tries at all to be memory safe.

// this code has all the same bugs as the code above, and the
// VC++9 compiler does not complain at all.

template< typename T >
    const T* get_ptr( const T& a) { return &a; }
template< typename T >
    const T& get_ref( const T& a) { return a; }

const int& unsafe_ref_a( ) { int a; return get_ref( a); }
const int& unsafe_ref_b( ) { const int a = 1;
                             return get_ref( a); }
const int& unsafe_ref_c( ) { return get_ref( 1); }
const int& unsafe_ref_e( ) { return get_ref( (1 + 2)); }

const int* unsafe_ptr_a( ) { int a; return get_ptr( a); }
const int* unsafe_ptr_b( ) { const int a = 1;
                             return get_ptr( a); }
const int* unsafe_ptr_c( ) { return get_ptr( 1); }
const int* unsafe_ptr_e( ) { return get_ptr( (1 + 2)); }

These errors are less obvious but just as bad as the earlier ones. I’ve seen mistakes like this many times when reviewing code.

The challenge is that a function that returns a ref or pointer type does not make assumptions about the object’s lifetime. Sometimes it is up to the caller to be know the relationship between passed-in parameters and returned values.

The problem is somewhat exacerbated by r-values (unnamed temporary variables), which can be passed as “const T&”. R-value (&&) types in C++0x may help the compiler a little in catching these kinds of problems, but these dangers are inherent in C++. You just have to watch out.

Comments

Leave a Reply