By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,341 Members | 1,395 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,341 IT Pros & Developers. It's quick & easy.

assigning a pointer the address of local variable

P: n/a
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?

Aug 23 '06 #1
Share this Question
Share on Google+
25 Replies


P: n/a
"Sourav" <so*********@gmail.comwrites:
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.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 23 '06 #2

P: n/a

Sourav wrote:
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

Aug 23 '06 #3

P: n/a
Sourav wrote:
>
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.

--
Chuck F (cb********@yahoo.com) (cb********@maineline.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.netUSE maineline address!

Aug 23 '06 #4

P: n/a
On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
wrote:
>"Sourav" <so*********@gmail.comwrites:
>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.)
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;
}

--
jay
Aug 23 '06 #5

P: n/a
jaysome <ja*****@spamcop.netwrites:
On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
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.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 23 '06 #6

P: n/a
Keith Thompson wrote:
>
"Sourav" <so*********@gmail.comwrites:
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:Th*************@gmail.com>
Aug 23 '06 #7

P: n/a
Keith Thompson wrote:
jaysome <ja*****@spamcop.netwrites:
>On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
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?
Aug 23 '06 #8

P: n/a
>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?

Aug 23 '06 #9

P: n/a
"Ancient_Hacker" <gr**@comcast.netwrites:
>>But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?
The above was posted by Sourav <so*********@gmail.com>. Please don't
snip attributions.
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).

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 23 '06 #10

P: n/a

Keith Thompson wrote:
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.
>
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 23 '06 #11

P: n/a
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..

================================================== ====
jaysome wrote:
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;
}

--
jay
Aug 23 '06 #12

P: n/a

Keith Thompson wrote:
"Ancient_Hacker" <gr**@comcast.netwrites:
>But is it correct
to assign a global pointer the address of a local variable which does
not exist after the function has ended?

The above was posted by Sourav <so*********@gmail.com>. Please don't
snip attributions.
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).
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...

Aug 24 '06 #13

P: n/a

Harald van Dijk wrote:
Keith Thompson wrote:
jaysome <ja*****@spamcop.netwrites:
On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
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.

Aug 24 '06 #14

P: n/a
en******@yahoo.com wrote:
Harald van Dijk wrote:
>Keith Thompson wrote:
>>jaysome <ja*****@spamcop.netwrites:
On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
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.
--
Flash Gordon
Aug 24 '06 #15

P: n/a
en******@yahoo.com wrote:
[...]
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:Th*************@gmail.com>
Aug 24 '06 #16

P: n/a
Ancient_Hacker wrote:
>
Keith Thompson wrote:
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).
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:Th*************@gmail.com>
Aug 24 '06 #17

P: n/a

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.

Aug 24 '06 #18

P: n/a
Flash Gordon wrote:
en******@yahoo.com wrote:
Harald van Dijk wrote:
Keith Thompson wrote:
jaysome <ja*****@spamcop.netwrites:
On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
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?

Aug 24 '06 #19

P: n/a

Harald van Dijk wrote:
Flash Gordon wrote:
en******@yahoo.com wrote:
Harald van Dijk wrote:
>Keith Thompson wrote:
>>jaysome <ja*****@spamcop.netwrites:
>>>On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
>>>wrote:
>>[...]
>>>>No, it isn't. When s reaches the end of its lifetime (at the endof
>>>>foo(), the value of p becomes indeterminate. Dereferencing p, oreven
>>>>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 thevalue
>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.

Aug 24 '06 #20

P: n/a
en******@yahoo.com wrote:
Harald van Dijk wrote:
Flash Gordon wrote:
en******@yahoo.com wrote:
Harald van Dijk wrote:
Keith Thompson wrote:
>jaysome <ja*****@spamcop.netwrites:
>>On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
>>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.
So the result of an expression-statement must not be read if it is a
function call, and must be read if it is a variable (and all exceptions
are results of the as-if rule)? Where does the standard make this
distinction?

Aug 25 '06 #21

P: n/a
[Originally, we had code equivalent to the following:

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

I myself do not know whether this code is well-defined. We can
say for sure that "i" does exist at the point of the "return"
statement that takes its address, and no longer exists by the
time foo() has returned to main(), so the "value" returned by
the function has undefined behavior if it is used -- but it is
not "used".]
>>Harald van Dijk wrote:
>>>Thanks. Now for my followup question: how does this affect an
expression statement consisting of a single volatile variable? Is a
read allowed?
>en******@yahoo.com wrote:
>Not just allowed, but required.
In article <11**********************@p79g2000cwp.googlegroups .com>,
Harald van Dijk <tr*****@gmail.comwrote:
>So the result of an expression-statement must not be read if it is a
function call, and must be read if it is a variable (and all exceptions
are results of the as-if rule)? Where does the standard make this
distinction?
This is an interesting point. Here is Program P:

extern volatile int hardware_reg;

int main(void) {
volatile int *p = &hardware_reg;

*p; /* or, equivalently, "hardware_reg;" (without quotes) */
return 0;
}

When run (after compiling with special Linker Magic to make the
external variable "hardware_reg" correspond to a hardware register
on the system), this program clears the register -- it is one of
those "self-clearing upon read" registers -- and is thus useful
after some particular kinds of errors. (Perhaps the error it clears
has to do with the audio subsystem, so it makes the sound work on
the machine again.)

Now we modify Program P a bit, giving Program Q:

extern volatile int hardware_reg;
volatile int *f(void) { return &hardware_reg; }
int main(void) { *f(); return 0; }

This obviously does the same as Program P. But note that we used
the unary "*" operator in main(). If we had done that with the
original program, now modified to become Program R:

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

we would definitely (I believe) have undefined behavior. So now
we might write Program S:

volatile int *foo(void) { int i; return &i; }
int main(void) { foo(); return 0; }

The key seems, in this case, to be the unary "*" operator. It
is OK to skip "reading" the value returned by some function,
even if it is a "pointer to volatile", as long as we do not
actually follow the pointer.

If a function could return a volatile-qualified type (and it cannot),
and if the volatile-qualified type were a C++-like reference (which
C lacks), we could rewrite one of the above to produce Program T:

volatile int &f(void) { return hardware_reg; }
int main(void) { f(); return 0; }

In this case the actual read of the hardware register would occur
(logically at least) in main(), rather than in f(), giving rise to
the potential conflict that Harald is asking about. Fortunately,
we cannot express the problem in C in the first place. :-)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Aug 25 '06 #22

P: n/a

Chris Torek wrote:
[Originally, we had code equivalent to the following:

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

I myself do not know whether this code is well-defined. We can
say for sure that "i" does exist at the point of the "return"
statement that takes its address, and no longer exists by the
time foo() has returned to main(), so the "value" returned by
the function has undefined behavior if it is used -- but it is
not "used".]
Here's why it's defined:

The value &i is valid when the expression is evaluated
before doing the return.

What the return does with the value is defined in
6.8.6.4 paragraph 3 - "the value is returned to the
caller".

An indeterminate value isn't illegal in and of itself;
it's only certain uses of indeterminate values that
cause undefined behavior:

6.2.6.1#5 - being read out of an object
6.2.6.1#5 - being stored into an object (perhaps indirectly)
6.2.6.2#4 - logical operators that would produce one
6.5.3.2#4 - operand of * operator

What a return does isn't any of these things; in
particular, a value of an expression isn't an object,
since it doesn't have an address. The implicit
conversion to (void) then discards the value, which
also doesn't violate any of the UB's.

Since the semantics are defined, and don't transgress any
provisions that would make them undefined, the result is defined.

Aug 26 '06 #23

P: n/a

Harald van Dijk wrote:
en******@yahoo.com wrote:
Harald van Dijk wrote:
Flash Gordon wrote:
en******@yahoo.com wrote:
Harald van Dijk wrote:
>Keith Thompson wrote:
>>jaysome <ja*****@spamcop.netwrites:
>>>On Wed, 23 Aug 2006 06:32:54 GMT, Keith Thompson <ks***@mib.org>
>>>wrote:
>>[...]
>>>>No, it isn't. When s reaches the end of its lifetime (at theend 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 youshould
>>>>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)? Isthe 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 leastif
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.

So the result of an expression-statement must not be read if it is a
function call, and must be read if it is a variable (and all exceptions
are results of the as-if rule)? Where does the standard make this
distinction?
Chris Torek gave already a good answer to this, but let
me see if I can add to it.

My earlier answer was misleading, because "read" was being
used in two different senses. In both cases:

foo();
volatile_variable;

a value is produced, and in both cases a value is discarded.
The difference is, in the case of foo(), producing the
value is ok because the value is valid at the time it's
produced, but in the case of volatile_variable, the value
in the variable is already invalid when the variable is
accessed.

To say that another way, in neither case is the value of
the expression "read", but in the variable case an object
with an invalid value must be accessed, and it is this
access ("read" in the other sense) that causes the
undefined behavior. It isn't the value of the expression
that's read, but reading the variable to produce the
value of the expression, that causes the undefined
behavior; the value is subsequently discarded, but by
then it's too late, the UB has already happened.

Incidentally, the variable doesn't have to be volatile
to get undefined behavior.

See sections 6.3.2.1 #2, 6.2.6.1 #5 in the Standard.

Aug 26 '06 #24

P: n/a
en******@yahoo.com wrote:
Chris Torek wrote:
[Originally, we had code equivalent to the following:

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

I myself do not know whether this code is well-defined. We can
say for sure that "i" does exist at the point of the "return"
statement that takes its address, and no longer exists by the
time foo() has returned to main(), so the "value" returned by
the function has undefined behavior if it is used -- but it is
not "used".]

Here's why it's defined:

The value &i is valid when the expression is evaluated
before doing the return.

What the return does with the value is defined in
6.8.6.4 paragraph 3 - "the value is returned to the
caller".

An indeterminate value isn't illegal in and of itself;
it's only certain uses of indeterminate values that
cause undefined behavior:

6.2.6.1#5 - being read out of an object
6.2.6.1#5 - being stored into an object (perhaps indirectly)
6.2.6.2#4 - logical operators that would produce one
6.5.3.2#4 - operand of * operator

What a return does isn't any of these things; in
particular, a value of an expression isn't an object,
since it doesn't have an address. The implicit
conversion to (void) then discards the value, which
also doesn't violate any of the UB's.

Since the semantics are defined, and don't transgress any
provisions that would make them undefined, the result is defined.
Thanks for the clear explanation, I think you're right. Now here's yet
another question:

int *foo(void) { int i; return &i; }
int *bar(void) { int i; return &i; }
int main(void) { return foo() == bar(); }

The invalid pointer values are not read by an lvalue expression, so
6.2.6.1p5 doesn't apply, and 6.5.9p6 says:

"Two pointers compare equal if and only if both are null pointers, both
are pointers to the same object (including a pointer to an object and a
subobject at its beginning) or function, both are pointers to one past
the last element of the same array object, or one is a pointer to one
past the end of one array object and the other is a pointer to the
start of a different array object that happens to immediately follow
the first array object in the address space.92)"

Neither value is a null pointer, neither value points to an object, and
neither value points to one past the last element of an array. Because
of the "if and only if", it seems that the above program is strictly
conforming and is guaranteed to return 0. That can't possibly be
intended, and at least three different compilers can all be convinced
to make the program return 1 in at least one mode which aims to conform
to C89 or C99.

Aug 26 '06 #25

P: n/a

Harald van Dijk wrote:
en******@yahoo.com wrote:
Chris Torek wrote:
[Originally, we had code equivalent to the following:
>
int *foo(void) { int i; return &i; }
int main(void) { foo(); return 0; }
>
I myself do not know whether this code is well-defined. We can
say for sure that "i" does exist at the point of the "return"
statement that takes its address, and no longer exists by the
time foo() has returned to main(), so the "value" returned by
the function has undefined behavior if it is used -- but it is
not "used".]
Here's why it's defined:

The value &i is valid when the expression is evaluated
before doing the return.

What the return does with the value is defined in
6.8.6.4 paragraph 3 - "the value is returned to the
caller".

An indeterminate value isn't illegal in and of itself;
it's only certain uses of indeterminate values that
cause undefined behavior:

6.2.6.1#5 - being read out of an object
6.2.6.1#5 - being stored into an object (perhaps indirectly)
6.2.6.2#4 - logical operators that would produce one
6.5.3.2#4 - operand of * operator

What a return does isn't any of these things; in
particular, a value of an expression isn't an object,
since it doesn't have an address. The implicit
conversion to (void) then discards the value, which
also doesn't violate any of the UB's.

Since the semantics are defined, and don't transgress any
provisions that would make them undefined, the result is defined.

Thanks for the clear explanation, I think you're right. Now here's yet
another question:

int *foo(void) { int i; return &i; }
int *bar(void) { int i; return &i; }
int main(void) { return foo() == bar(); }

The invalid pointer values are not read by an lvalue expression, so
6.2.6.1p5 doesn't apply, and 6.5.9p6 says:

"Two pointers compare equal if and only if both are null pointers, both
are pointers to the same object (including a pointer to an object and a
subobject at its beginning) or function, both are pointers to one past
the last element of the same array object, or one is a pointer to one
past the end of one array object and the other is a pointer to the
start of a different array object that happens to immediately follow
the first array object in the address space.92)"

Neither value is a null pointer, neither value points to an object, and
neither value points to one past the last element of an array. Because
of the "if and only if", it seems that the above program is strictly
conforming and is guaranteed to return 0. That can't possibly be
intended, and at least three different compilers can all be convinced
to make the program return 1 in at least one mode which aims to conform
to C89 or C99.
Yes, I think your analysis is right, and this
shows a weakness in the existing wording. You
might want to post the question in comp.std.c
in a new thread and see what other people think.
"If any pointer value is a trap representation
the behavior is undefined," or words to that
effect, should be added somewhere in 6.5.9,
and probably a few other places.

Aug 27 '06 #26

This discussion thread is closed

Replies have been disabled for this discussion.