2

Is it ok to use a pointer which point to the address of a variable declared inside "if" statement ? Example as below:

...
int *pTest = 0;
if (...)
{
  int x = 10;
  pTest = &x;
}
else
{
  int x = 100;
  pTest = &x;
}
...
// use pTest 
chen ming
  • 37
  • 4
  • It is valid, but I guess declare pTest as nullptr first make it better. – Steve Deng Zishi Feb 09 '17 at 05:31
  • this atleast works, so shouldn't be a problem – Pbd Feb 09 '17 at 05:33
  • I tested the code and it works as expected. But I am not sure if this is safe. – chen ming Feb 09 '17 at 05:34
  • As long as the object being pointed to doesnt outlive the pointer its fine but if you can avoid doing it, then go for it. – Gambit Feb 09 '17 at 05:35
  • 6
    In the given example, no. in both cases `x` is out of scope and invalid by the time the program reaches `// use pTest`. This be Undefined Behaviour. Even if it looks like it works now, the most insidious of Undefined Behaviour, it may not when your boss goes to do a [demo at comdex](https://www.youtube.com/watch?v=yHxj-47csUU). – user4581301 Feb 09 '17 at 05:42
  • 1
    I just found [this question](http://stackoverflow.com/questions/30694069/is-it-legal-to-compare-dangling-pointers) on comparing dangling pointers. It looks pretty relevant. – chris Feb 09 '17 at 06:30
  • 2
    What does "use pTest" mean? There's a huge difference between using `pTest` and using `*pTest`, for example. In general, both uses are invalid. But the specific details might differ greatly. In any case the answer to both is: no, it is not even remotely OK. You have a classic orphaned pointer here. – AnT stands with Russia Feb 09 '17 at 06:50
  • @AnT, I don't know if I'd classify implementation-defined behaviour as "not even remotely OK". – chris Feb 09 '17 at 16:23
  • @chris: And again: what specific behavior are you referring to? – AnT stands with Russia Feb 09 '17 at 17:14
  • @AnT, Lvalue-to-rvalue conversion of the pointer itself. Your comment says both `pTest` and `*pTest` are not even remotely okay. – chris Feb 09 '17 at 20:42

4 Answers4

6

The problem is the life span of the pointer is greater than that of the object pointed to. This smells bad and you should rethink what you're trying to do.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • I believe this is a bug. Actually, it is found during code review. But I don't understand why it doesn't cause any fault. – chen ming Feb 09 '17 at 05:41
  • 3
    @chenming because you get the dreaded *Undefined Behaviour*. Anything can happen. It could work. The program can crash. Your fridge may be raided! – Paul Evans Feb 09 '17 at 05:43
  • 1
    @chenming It compiles just fine because it COULD be a valid application of a pointer (the syntax is correct), unfortunately the logic is broken. You have either been luck or unlucky. Hard to say which. Just because the program has not crashed or output obviously flawed results, who can say that it hasn't computed something that is off by ownly a few bits? – user4581301 Feb 09 '17 at 05:46
  • 4
    @chenming - C++ doesn't do anything in particular when you create an invalid program. The compiler doesn't have to tell you, the system can do whatever works for it. On your usual system where automatic variables are on a "stack" what's somewhat likely is that values in memory for variables popped off the stack are not going to be overwritten or given back to the OS. So you're free to access the garbage there and that happens to be the last thing you wrote to that location. You might not be declaring any new variables, perhaps the compiler reserved them all and hasn't unwound that variable – Edward Strange Feb 09 '17 at 05:49
2

The short answer is that it is not safe to use that pointer. The long answer is more complicated. You can do it if you do this in a very specific way, that honestly, I don't think you ever should. The pointer pTest will in all likeliness point to a position relative to the stack pointer. As long as nothing else clobbers that location, you are safe to use it. This is why it might "work" in certain situations. That said, here are some of the things that could cause that to fail:

  1. Another variable might occupy the same space because once that variable is out of scope, another variable might take up the same space. This is similar to using memory that you have freed.

  2. The compiler may do a transformation on your code making the assumption that it is not relying on undefined behavior (which this is). This could, for example, manifest that the compiler simply optimizes out this line:

    pTest = &x;

The reason that the compiler may optimize out that particular line is that it concludes that any code that relies on reading that pointer from there on is relying on undefined behavior and so (ideally) the programmer knows this and wouldn't write code that depends on undefined behavior. The optimizer will then conclude that the most efficient solution is to nothing. That won't break any code that is not relying on undefined behavior.

Community
  • 1
  • 1
Mouna Apperson
  • 1,178
  • 6
  • 15
2

UPDATE: I found this relevant question. Thanks to the standard citations in there, I can say that there is indeed a paragraph causing the pointer value to become invalid when the object's lifetime ends. In addition, performing an lvalue-to-rvalue conversion on a pointer with an invalid value is implementation-defined behaviour.

Thus, my below answer should ultimately be changed to implementation-defined. This means that your implementation is required to document what it will do when performing this lvalue-to-rvalue conversion, if that matters to you.


Old Answer

I believe this is okay by the standard if your definition of use means lvalue-to-rvalue conversion (e.g., printing the value, copying the value into another pointer, not dereferencing it¹).

[cov.lval]/3.3-3.4:

  • Otherwise, if the object to which the glvalue refers contains an invalid pointer value ([basic.stc.dynamic.deallocation], [basic.stc.dynamic.safety]), the behavior is implementation-defined.

  • Otherwise, the value contained in the object indicated by the glvalue is the prvalue result.

[basic.stc.dynamic.safety]/4:

An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value. Alternatively, an implementation may have strict pointer safety, in which case a pointer value referring to an object with dynamic storage duration that is not a safely-derived pointer value is an invalid pointer value unless the referenced complete object has previously been declared reachable ([util.dynamic.safety]).

So if we have strict pointer safety, it's still valid - this piece doesn't make it invalid because it doesn't refer to an object with dynamic storage duration.

In short, I claim it's safe to use in the context of lvalue-to-rvalue conversions. If there is another paragraph in the standard making its value invalid because the referred-to object no longer exists, or some obscure trouble with "the value contained in the object indicated by the glvalue", I would love to see it.


¹: There's some great discussion in this general area here. I simplified this part since most uses of *p would be undefined behaviour.

Community
  • 1
  • 1
chris
  • 60,560
  • 13
  • 143
  • 205
0

It would have been okay if you allocated memory to variable x using "new" and directed a pointer towards it . Because when the scope of variable x ends (as outside if) , the VALUE of x will still remain on heap (dynamic memory allocated to your program) and your pointer will still be pointing to some valid value . but in your case, since the memory allocated to variable x wasn't allocated dynamically, so once the scope of x ends outside its if's block, VALUE of x also wipes out and so in this case the pointer is now NOT pointing to variable's value, rather it is now pointing to some garbage value

Aisha Javed
  • 169
  • 13