assigning a pointer the address of local variable

S

Sourav

Suppose I have a code like this,

#include <stdio.h>

int *p;
void foo(int);

int main(void){
foo(3);
printf("%p %d\n",p,*p);
return 0;
}

void foo(int r){
int s=r+1;
p=&s;
}

In most of the compilers I use (GCC, MSVC++, lcc..) this program runs
allright printing an address and the correct value 4. But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?
 
K

Keith Thompson

Sourav said:
Suppose I have a code like this,

#include <stdio.h>

int *p;
void foo(int);

int main(void){
foo(3);
printf("%p %d\n",p,*p);
return 0;
}

void foo(int r){
int s=r+1;
p=&s;
}

In most of the compilers I use (GCC, MSVC++, lcc..) this program runs
allright printing an address and the correct value 4. But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?

No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)
 
B

Bill Pursell

Sourav said:
Suppose I have a code like this,

#include <stdio.h>

int *p;
void foo(int);

int main(void){
foo(3);
printf("%p %d\n",p,*p);
return 0;
}

void foo(int r){
int s=r+1;
p=&s;
}

In most of the compilers I use (GCC, MSVC++, lcc..) this program runs
allright printing an address and the correct value 4. But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?

No. consider:

#include <stdio.h>

int *p;
void foo(int);
int baz(int);

int main(void){
foo(3);
baz(5);
printf("%p %d\n",(void*)p,*p);
return 0;

}

void foo(int r){
int s=r+1;
p=&s;

}

int baz(int r) {
int t=19;
int k=4;

return t + k + t;
}

This prints:
[tmp]$ ./a.out
0xfee768a4 19
 
C

CBFalconer

Sourav said:
Suppose I have a code like this,

#include <stdio.h>

int *p;
void foo(int);

int main(void){
foo(3);
printf("%p %d\n",p,*p);
return 0;
}

void foo(int r){
int s=r+1;
p=&s;
}

In most of the compilers I use (GCC, MSVC++, lcc..) this program
runs allright printing an address and the correct value 4. But is
it correct to assign a global pointer the address of a local
variable which does not exist after the function has ended?

It is correct to assign it. It is not correct to use it after the
function has exited.
 
J

jaysome

No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)

The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}
 
K

Keith Thompson

jaysome said:
[...]
No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)

The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}

Any reference to the value of p after foo() returns invokes undefined
behavior. For that matter, I think assigning the result of foo() to p
in the first place invokes UB.
 
K

Kenneth Brody

Keith said:
Sourav said:
Suppose I have a code like this, [... code which stores &localvar in globalvar ...]

In most of the compilers I use (GCC, MSVC++, lcc..) this program runs
allright printing an address and the correct value 4. But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?

No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)

<mode pedant=on>

"To assign a global variable the address of a local variable" is fine.
It's the use of that variable after the function exits that is UB.

</mode>

Imagine a situation where the function calls other functions which use
the global variable. This is not a problem. (Well, some people would
argue that it's a "problem" in the sense that "you shouldn't use global
variables". But that's a different issue entirely.)

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
H

Harald van =?UTF-8?B?RMSzaw==?=

Keith said:
jaysome said:
[...]
No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)

The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}

Any reference to the value of p after foo() returns invokes undefined
behavior. For that matter, I think assigning the result of foo() to p
in the first place invokes UB.

A question: what about just "foo();" (without an assignment)? Is the value
allowed to be read before discarding it?
 
A

Ancient_Hacker

But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?

Imagine yesterday you went to a motel and asked for a room. The guy at
the counter says "no problem, I'll give you room 0x00244528" (this is a
big motel). You and your "date", Quadrophenia, go up to the room and
have a good old time. Reading the Bible, that is.

The next morning you exit the motel, but you take the door key wiith
you.

Now the $50,000 question: Is it likely you can go back to the motel
the next year, open the door to room 0x00244528, and find Quadrophenia
there?
 
K

Keith Thompson

Ancient_Hacker said:
Imagine yesterday you went to a motel and asked for a room. The guy at
the counter says "no problem, I'll give you room 0x00244528" (this is a
big motel). You and your "date", Quadrophenia, go up to the room and
have a good old time. Reading the Bible, that is.

The next morning you exit the motel, but you take the door key wiith
you.

Now the $50,000 question: Is it likely you can go back to the motel
the next year, open the door to room 0x00244528, and find Quadrophenia
there?

Now the $51,000 question. After you've checked out, can you even
*look* at the key? In the motel key analogy, of course you can; in C,
accessing the value of the pointer (without even dereferencing it /
using it to open the door) invokes undefined behavior.

Sticking to the the motel key model, if you take the key to a
locksmith to have it duplicated, the locksmith *might* recognize that
you've checked out of the room and refuse to duplicate it, or even
call the police (who will then make demons fly out of your nose).
 
A

Ancient_Hacker

Keith said:
Now the $51,000 question. After you've checked out, can you even
*look* at the key? In the motel key analogy, of course you can; in C,
accessing the value of the pointer (without even dereferencing it /
using it to open the door) invokes undefined behavior.

very perceptive of the standards folks. The x86 when in protect mode
implements this behavior-- you can't even load a "bad" address into a
segment register. Just the sight of it makes the CPU sick (or
actually, causes a segment violation interrrupt).

Perhaps unfortunately, most x86 OS's use a "flat" model, ignoring the
segment registers pretty much. There would be a lot more unset pointer
errors caught dead in their tracks if compilers would have a mode that
more agressively used segments.

Sticking to the the motel key model, if you take the key to a
locksmith to have it duplicated, the locksmith *might* recognize that
you've checked out of the room and refuse to duplicate it, or even
call the police (who will then make demons fly out of your nose).

Ah, that would be heavenly, if bad addresses couldnt be passed around.
When I think of the number of dangling-pointer erros I've fixed in my
and others code, all that wasted time.
 
W

Walter Banks

After "return &i;" in foo is executed there are no other references to "i" and many compilers will de-allocate "i" and release the local space allowing it to be re-used

Many compilers will not allocate space for p until the execution of " p = foo();" when it will do that can vary. It is possible the order of execution is
1) call to foo()
- make space for i
- set return value as &i
- release space for i
2) create space for p
3) store the return value in p

Smart compiler/linkers will note that foo and main are not re-entrant in this case and will optimize out the stack locals to compiled stack.

Using the above sequence it is possible that structure tracking will result in p will being assigned assigned to the same location as i


As several others have said assume "i" is dead in your example as soon as the return is executed in foo.

=======================================================

Changing the local int to a static would change the rules. The variable "i" would survive the execution of the return

static int* foo(void)
{
static int i;
return &i;
}


w..

======================================================
 
E

ena8t8si

Keith said:
Now the $51,000 question. After you've checked out, can you even
*look* at the key? In the motel key analogy, of course you can; in C,
accessing the value of the pointer (without even dereferencing it /
using it to open the door) invokes undefined behavior.

Sticking to the the motel key model, if you take the key to a
locksmith to have it duplicated, the locksmith *might* recognize that
you've checked out of the room and refuse to duplicate it, or even
call the police (who will then make demons fly out of your nose).

I always go to locksmiths who use memcpy() to avoid those
nasty nasal demons.

On the other hand my sinuses have been somewhat congested
lately...
 
E

ena8t8si

Harald said:
Keith said:
jaysome said:
[...]
No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)

The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}

Any reference to the value of p after foo() returns invokes undefined
behavior. For that matter, I think assigning the result of foo() to p
in the first place invokes UB.

A question: what about just "foo();" (without an assignment)? Is the value
allowed to be read before discarding it?

No, I would say it isn't; otherwise the implementation would
be doing something not done by the abstract machine, violating
the as-if rule. To say it another way, the implementation is
allowed to read the value, but must behave as if it doesn't.
 
F

Flash Gordon

Harald said:
Keith said:
[...]
No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)
The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}
Any reference to the value of p after foo() returns invokes undefined
behavior. For that matter, I think assigning the result of foo() to p
in the first place invokes UB.
A question: what about just "foo();" (without an assignment)? Is the value
allowed to be read before discarding it?

No, I would say it isn't; otherwise the implementation would
be doing something not done by the abstract machine, violating
the as-if rule. To say it another way, the implementation is
allowed to read the value, but must behave as if it doesn't.

I agree with ena8t8si and would add in support that in C89 at least if
you fell off the end of the function without returning a value the
program would still be strictly conforming *if* you did not use the
value. So if "foo();" is fine if nothing is returned it must surely
still be fine if a value you are not allowed to use is returned.
 
K

Kenneth Brody

Ancient_Hacker said:
very perceptive of the standards folks. The x86 when in protect mode
implements this behavior-- you can't even load a "bad" address into a
segment register. Just the sight of it makes the CPU sick (or
actually, causes a segment violation interrrupt).

Of course, "in real life", the address of a local variable is still
likely to be valid after the function returns. (At least "valid"
in the sense that it is in the process' address space.) Imagine the
overhead of moving the fence twice on every function call.
Perhaps unfortunately, most x86 OS's use a "flat" model, ignoring the
segment registers pretty much. There would be a lot more unset pointer
errors caught dead in their tracks if compilers would have a mode that
more agressively used segments.

As I recall, at least on the 286, loading a segment register was a
very slow operation in protected mode.

[...]

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
K

Kenneth Brody

I always go to locksmiths who use memcpy() to avoid those
nasty nasal demons.

On the other hand my sinuses have been somewhat congested
lately...

You see? UB is a good thing at times. Without it, all of those
demons stay in your sinuses. Runnig a UB program every now and
then clears them right up.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
A

Ancient_Hacker

Kenneth Brody wrote:

Of course, "in real life", the address of a local variable is still
likely to be valid after the function returns. (At least "valid"
in the sense that it is in the process' address space.) Imagine the
overhead of moving the fence twice on every function call.

No explicit moving necessary, just disallow any references to the stack
segment "below" the stack pointer. It would take a 16 to 32-bit
magnitude comparator added to the CPU, but that should be cheap
nowdays.

CPU's never seem to be designed by programmers, otherwise there would
be plenty of debugging features. Well, a few CPU's have "come from"
stacks, so you can see how you got to address FFFFFFF0, a few have
hardware breakpoints, not always well supported by the OS. Almost none
have "not set" attributes on each memory address. Sigh.

As I recall, at least on the 286, loading a segment register was a
very slow operation in protected mode.

Yep, slow, so you might not want to distribute your debugged program
with that addressing mode selected. But it sure was *quick* at
finding dangling pointers!

Then again, turning off debugging features on the released vesion is a
little like wearing a parachute on the ground and taking it off while
flying. Or having a strategy of crashing or generating bad results
very quickly.
 
G

Guest

Flash said:
Harald said:
Keith Thompson wrote:
[...]
No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)
The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}
Any reference to the value of p after foo() returns invokes undefined
behavior. For that matter, I think assigning the result of foo() to p
in the first place invokes UB.
A question: what about just "foo();" (without an assignment)? Is the value
allowed to be read before discarding it?

No, I would say it isn't; otherwise the implementation would
be doing something not done by the abstract machine, violating
the as-if rule. To say it another way, the implementation is
allowed to read the value, but must behave as if it doesn't.

I agree with ena8t8si and would add in support that in C89 at least if
you fell off the end of the function without returning a value the
program would still be strictly conforming *if* you did not use the
value. So if "foo();" is fine if nothing is returned it must surely
still be fine if a value you are not allowed to use is returned.

Thanks. Now for my followup question: how does this affect an
expression statement consisting of a single volatile variable? Is a
read allowed?
 
E

ena8t8si

Harald said:
Flash said:
Harald van Dijk wrote:
Keith Thompson wrote:
[...]
No, it isn't. When s reaches the end of its lifetime (at the end of
foo(), the value of p becomes indeterminate. Dereferencing p, or even
looking at its value, invokes undefined behavior. (The latter isn't
likely to cause any visible problems on most systems, but you should
still avoid it.)
The word "looking" is vague. Does it mean that I can't do something
like this?

#include <stdio.h>

static int* foo(void);

int main(void)
{
int *p;
p = foo();/* legal? */
printf("Pointer was %p\n", (void*)p);/* legal? */
return 0;
}

static int* foo(void)
{
int i;
return &i;
}
Any reference to the value of p after foo() returns invokes undefined
behavior. For that matter, I think assigning the result of foo() to p
in the first place invokes UB.
A question: what about just "foo();" (without an assignment)? Is the value
allowed to be read before discarding it?

No, I would say it isn't; otherwise the implementation would
be doing something not done by the abstract machine, violating
the as-if rule. To say it another way, the implementation is
allowed to read the value, but must behave as if it doesn't.

I agree with ena8t8si and would add in support that in C89 at least if
you fell off the end of the function without returning a value the
program would still be strictly conforming *if* you did not use the
value. So if "foo();" is fine if nothing is returned it must surely
still be fine if a value you are not allowed to use is returned.

Thanks. Now for my followup question: how does this affect an
expression statement consisting of a single volatile variable? Is a
read allowed?

Not just allowed, but required.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top