Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to detect references to members of temporary objects

Tags:

c++

My colleague recently compiled our program in Windows, and discovered a bug of the sort:

  std::string a = "hello "; 
  std::string b = "world"; 
  const char *p = (a+b).c_str();
  printf("%s\n", p);

which for some reason did not crash in our Linux executables.

None of our compilers give any kind of warning, so we are now worried that this error might exist in the code.

Although we can grep for c_str() occurrences and do a visual inspection, there is a possibility that one might have also done the following:

struct I {
     int num;
     I()   { num=0; }
};

struct X { 
     I *m;
     X()  { m = new I; }
     ~X() { delete m; }
     I  get() { return *m; }   // version 1, or
     I& get() { return *m; }   // version 2
};

and accessed it like:

  I& a = X().get();         // will get a reference to a temporary, or a valid copy?
  cout << a.num;

instead of :

 cout << X().get().num;   

which is safe (isn't it?)

Question: Is there a way I can catch such errors (perhaps using the compiler, or even an assertion) ?
I need to be sure that if author of struct X changes get() between version 1 and 2 that the program will warn for the error

like image 811
Grim Fandango Avatar asked Nov 29 '11 17:11

Grim Fandango


2 Answers

Simple answer: In general you cannot catch those errors, and the reason is that there are similar constructs that might be perfectly fine, so the compiler would have to know the semantics of all the functions to be able to warn you.

In simpler cases, like obtaining the address of a temporary, many compilers already warn you, but in the general case, it is quite difficult if not impossible for the compiler to know.

For some similar example to the .c_str() consider:

std::vector< const char * > v;
v.push_back( "Hi" );
const char* p = *v.begin();

The call to begin returns a temporary, similar to the expression (a+b), and you are calling a member function of that temporary (operator*) that returns a const char*, which is quite similar to your original case (from the point of view of the types involved). The problem is that in this case the pointee is still valid after the call, while in yours (.c_str()) it isn't, but it is part of the semantics of the operation, not the syntax that the compiler can check for you. The same goes for the .get() example, the compiler does not know if the returned reference is to an object that will be valid after the expression or not.

All these fall under the category of Undefined Behavior.

like image 177
David Rodríguez - dribeas Avatar answered Nov 15 '22 20:11

David Rodríguez - dribeas


Check out this question's solution, I think it does something similar to what you're looking for:

C++ catching dangling reference

There are runtime based solutions which instrument the code to check invalid pointer accesses. I've only used mudflap so far (which is integrated in GCC since version 4.0). mudflap tries to track each pointer (and reference) in the code and checks each access if the pointer/reference actually points to an alive object of its base type. Here is an example: {...}

like image 34
John Humphreys Avatar answered Nov 15 '22 19:11

John Humphreys