doubt in USING POINTERS

G

Gabriel Dos Reis

| >> (I'm not quite sure what gcc *should* do with this.)
| >
| >decay func().arr to a pointer, successfully complete the translation,
| >and generate a sensible executable.
|
| The problem here is that there is no really sensible answer.
|
| According to others who should know, the expression "func().arr"
| is *not* an lvalue in C89/C90, but *is* an lvalue in C99.

It has been repeatedly shown that func().arr is not an lvalue in C99.
Which chapter and verse of the standard make you believe it is an
lvalue in C99?

-- Gaby
 
J

Jeremy Yallop

[...]
+ The way I prefer to describe situations involving arrays uses
+ somewhat different terminology from that in the C standards. In
+ my notation, we have "objects" and "values", rather than "lvalues"
+ and "values".

Agreed. C89/90 attempted to define an "lvalue" to be an object-valued
expression but ran afoul of the fact that to maintain sanity we must
accept "*0" as an lvalue.

I don't see why. "*0" violates a constraint.

Jeremy.
 
T

thp

+ (e-mail address removed) wrote:
+> [...]
+> + The way I prefer to describe situations involving arrays uses
+> + somewhat different terminology from that in the C standards. In
+> + my notation, we have "objects" and "values", rather than "lvalues"
+> + and "values".
+>
+> Agreed. C89/90 attempted to define an "lvalue" to be an object-valued
+> expression but ran afoul of the fact that to maintain sanity we must
+> accept "*0" as an lvalue.
+
+ I don't see why. "*0" violates a constraint.

The expression "*0" doesn't denote an object.

Tom Payne
 
J

James Kuyper

CBFalconer said:
(e-mail address removed) wrote: ....

Since C, unlike Pascal, allows a return value to be ignored, such
needs to be (in practice) easily done. The easiest method is
returning it in a register, and failing to store that register.
Registers do not have an address in the usual memory space. Thus
I see no reason for allowing taking the address of a returned
value, or a component thereof.

If C99 allows this I consider it an unnecessary burden on code
generators.

As I understand the argument, it only applies to the case where the
address being taken is the address of a member of structure. It's only
rarely possible to squeeze a structure into a register; I don't think
it's a major burden on code generators to effectively prohibit that
option.
 
J

James Kuyper

Keith Thompson wrote:
....
I still wonder about the semantics of this. I've just tried another
test program that assigns a value to *ptr and then prints it out.
This doesn't, of course, imply that doing so is valid; it could still
be undefined behavior.

The question is, what is the lifetime of the int object that ptr
points to? It's part of the value returned by a function, so I
normally wouldn't expect it to outlive the expression containing the
call, but being able to grab a pointer to it makes it all very
confusing.

You're correct. The lifetime of that int object is the same as the
lifetime of the structure object it's a member of: the full-expression
containing the function call. It outlasts the function call itself, and
can therefore be used in parts of the same full-expression that are
guaranteed to be evaluated after the function call, but it cannot be
used in the next statement after the one containing the function call.
 
G

Gabriel Dos Reis

| Keith Thompson wrote:
| ...
| > I still wonder about the semantics of this. I've just tried another
| > test program that assigns a value to *ptr and then prints it out.
| > This doesn't, of course, imply that doing so is valid; it could still
| > be undefined behavior.
| >
| > The question is, what is the lifetime of the int object that ptr
| > points to? It's part of the value returned by a function, so I
| > normally wouldn't expect it to outlive the expression containing the
| > call, but being able to grab a pointer to it makes it all very
| > confusing.
|
| You're correct. The lifetime of that int object is the same as the
| lifetime of the structure object it's a member of: the full-expression
| containing the function call. It outlasts the function call itself, and
| can therefore be used in parts of the same full-expression that are
| guaranteed to be evaluated after the function call, but it cannot be
| used in the next statement after the one containing the function call.

Do you have formal reference(s) for that claim?

-- Gaby
 
D

Douglas A. Gwyn

CBFalconer said:
I see no reason for allowing taking the address of a returned
value, or a component thereof.

Actually I agree with you, but this seems a minor matter.
In one case the compiler does a little extra work and in
the other case the programmer does a little extra work.
 
D

Douglas A. Gwyn

Chris said:
Function return values, however, have no defined storage location.

Actually, results of expressions generally don't have defined
storage locations (i.e. correspond to what the C standard calls
"objects"). Whether or not they must is essentially what
lvalueness is about, and the decision is made on an operator-
by-operator basis judging on the basis of how useful it would
be to the programmer versus the potential cost in the generated
code.
 
D

Douglas A. Gwyn

Agreed. C89/90 attempted to define an "lvalue" to be an object-valued
expression but ran afoul of the fact that to maintain sanity we must
accept "*0" as an lvalue. We might slip out of that embarrassment by
positing a "null object" that *0 designates, but what's its type?
Obviously, the matter gets even more difficult when there are
reference types.

That's completely wacky. "*0" is not allowed in strictly conforming
programs, does not occur in any program I know of, and is perforce
irrelevant to the C standard.
 
K

Kevin Bracey

In message <[email protected]>
James Kuyper said:
As I understand the argument, it only applies to the case where the
address being taken is the address of a member of structure. It's only
rarely possible to squeeze a structure into a register; I don't think
it's a major burden on code generators to effectively prohibit that
option.

For what it's worth, the ARM calling standard mandates that some word-sized
structures/unions are returned in an integer register. Changing the calling
convention would be an even worse burden than getting the compiler to cope
with taking the address of a register-returned value.

And why are we only worried about structure returns here? Doesn't C99's new
lvalue definition also allow weird things like

int *x = &abs(1);
*&abs(3) = 2

I suppose the difference is that such cases cannot be required to actually
do anything useful?
 
D

Douglas A. Gwyn

James said:
... It's only
rarely possible to squeeze a structure into a register; I don't think
it's a major burden on code generators to effectively prohibit that
option.

However, it is precisely for small structures that one gets the
greatest benefit. The compilers and applications for the Blit
family of programmable bit-map graphics terminals (5620, 630, 730)
took advantage of a similar capability, in particular for
struct point { short x, y; }
for purposes of speed, which was quite important in that context.
 
G

Gabriel Dos Reis

| |
| >
| > [...]
| >
| >| I still wonder about the semantics of this. I've just tried another
| >| test program that assigns a value to *ptr and then prints it out.
| >| This doesn't, of course, imply that doing so is valid; it could still
| >| be undefined behavior.
| >
| > If it does something like
| >
| > print_value(set_value(func().arr, 4));
| >
| > I would expect it to be valid -- I do not know any Standard text that
| > implies so, though.
|
| It's not valid in C99, assuming set_value() does the
| obvious. C99 6.5.2.2p5 says (in part):
|
| If an attempt is made to modify the result of a function
| call or to access it after the next sequence point, the
| behavior is undefined.
|
| So set_value cannot modify the result of func(). What is
| more, C99 6.5.2.2p10 says:
|
| The order of evaluation of the function designator, the
| actual arguments, and subexpressions within the actual
| arguments is unspecified, but there is a sequence point
| before the actual call.
|
| So there is a sequence point after func() returns but before
| the call to set_value(), and therefore set_value() is not
| even allowed to access the result of func().

Thanks for the remind.

By the same token, print_value(func().arr) is undefined behaviour.

Well, the notion is of limited usefulness.

Yet, another incompatibility with C++.

-- Gaby
 
P

Phil Tregoning

[...]

| I still wonder about the semantics of this. I've just tried another
| test program that assigns a value to *ptr and then prints it out.
| This doesn't, of course, imply that doing so is valid; it could still
| be undefined behavior.

If it does something like

print_value(set_value(func().arr, 4));

I would expect it to be valid -- I do not know any Standard text that
implies so, though.

It's not valid in C99, assuming set_value() does the
obvious. C99 6.5.2.2p5 says (in part):

If an attempt is made to modify the result of a function
call or to access it after the next sequence point, the
behavior is undefined.

So set_value cannot modify the result of func(). What is
more, C99 6.5.2.2p10 says:

The order of evaluation of the function designator, the
actual arguments, and subexpressions within the actual
arguments is unspecified, but there is a sequence point
before the actual call.

So there is a sequence point after func() returns but before
the call to set_value(), and therefore set_value() is not
even allowed to access the result of func().
 
G

Gabriel Dos Reis

| CBFalconer wrote:
| > I see no reason for allowing taking the address of a returned
| > value, or a component thereof.
|
| Actually I agree with you, but this seems a minor matter.
| In one case the compiler does a little extra work and in
| the other case the programmer does a little extra work.

And I would refer having the compiler do the little extra work.

-- Gaby
 
L

lawrence.jones

In comp.std.c Gabriel Dos Reis said:
|
| (I'm not quite sure what gcc *should* do with this.)

decay func().arr to a pointer, successfully complete the translation,
and generate a sensible executable.

And a warning that the pointer you stored is useless.

-Larry Jones

Rats. I can't tell my gum from my Silly Putty. -- Calvin
 
L

lawrence.jones

In comp.std.c Chris Torek said:
According to others who should know, the expression "func().arr"
is *not* an lvalue in C89/C90, but *is* an lvalue in C99.

No, it's not an lvalue in C99, either. It's just that "The Rule"
applies only to lvalues in C89 but applies to any expression in C99.
Since it is not an operand of the unary "&" or sizeof operators,
the array lvalue will become a pointer rvalue, so in C99 the
assignment appears to be legal but useless, and a warning would be
appropriate.
Exactly.

In C89/C90, however, this is the one unique case in which an array
is an "rvalue", and the whole thing appears to be an error with a
required diagnostic, or perhaps an error with no required diagnostic.

It's a constraint violation (on the assignment operator), so a
diagnostic is required.
But writing:
int (*p)[N] = &(func().arr);
would definitely require a diagnostic, because unary & can only
be applied to lvalues.

True in both C89/C90 and C99.
Ultimately, I think there are two reasonable answers: forbid this
entirely -- make a claim that an array value, in this one case in
which it occurs, has no address and cannot be used in any way
(including to subscript it) -- or state that the value of an "array
value" is the value of the address of the first element of that
array, just as with an array object, so that the assignment to p
is valid and well-defined, and so that subscripting works in the
usual way. If the second answer is chosen (as I think it is in
C99), we must then define the lifetime of the object to which this
pointer points. I think that lifetime should be "until the next
sequence point", so that the assignment to p, while valid, is
useless and probably deserves a warning.

The folks defining C seem to have chosen both answers (at different
times in different standards). :)

Correct. The former was the decision made for C89, the latter
(including the lifetime of the object) was the decision made for C99.

-Larry Jones

My upbringing is filled with inconsistent messages. -- Calvin
 
L

lawrence.jones

In comp.std.c Keith Thompson said:
The question is, what is the lifetime of the int object that ptr
points to?

Until the next sequence point.

-Larry Jones

I thought my life would seem more interesting with a musical
score and a laugh track. -- Calvin
 
L

lawrence.jones

In comp.std.c James Kuyper said:
You're correct. The lifetime of that int object is the same as the
lifetime of the structure object it's a member of: the full-expression
containing the function call.

Not in C; it's only valid until the next sequence point.

-Larry Jones

I've got to start listening to those quiet, nagging doubts. -- Calvin
 
J

Jeremy Yallop

+ (e-mail address removed) wrote:
+> [...]
+> + The way I prefer to describe situations involving arrays uses
+> + somewhat different terminology from that in the C standards. In
+> + my notation, we have "objects" and "values", rather than "lvalues"
+> + and "values".
+>
+> Agreed. C89/90 attempted to define an "lvalue" to be an object-valued
+> expression but ran afoul of the fact that to maintain sanity we must
+> accept "*0" as an lvalue.
+
+ I don't see why. "*0" violates a constraint.

The expression "*0" doesn't denote an object.

"*0" is not a valid expression at all. No C implementation is
required to translate a program containing such an expression, because
"0" is not an acceptable operand for unary "*". You may be thinking
of "*(int *)0" or similar.

Jeremy.
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
474,083
Messages
2,570,591
Members
47,212
Latest member
RobynWiley

Latest Threads

Top