In our italian newsgroup dedicated to the C language
it has been asked if the following code is legal:
typedef struct ST { int x[10]; } S;
S foo() { S s; s.x[2]=3; return s; }
void bar()
{
printf("%i\n",foo().x[2]);
}
Now, the documents say that a member of a structure
returned from a function is not an lvalue; does this
imply the existence of non-lvalue arrays ? If this
is the case how can x[y] be evaluated if that is
equivalent to *(x+y) and such an array has no address ?
gcc 2.95 gives an error if asked to compile
*(foo().x+2)
but compiles fine
foo().x[2]
TIA for any clarification on this point ...
Andrea
Yes, there are non-lvalue arrays. They weren't useful prior to C99
because the array to pointer conversion only happened to lvalue arrays;
C99 performs the conversion even for non-lvalue arrays, so your code is
valid C99 (but not valid C90). Non-lvalue arrays do have addresses, but
the address is only valid until the next sequence point. See 6.3.2.1p3
and 6.5.2.2p5.
-Larry Jones
Oh yeah? You just wait! -- Calvin
Interesting.
What about the following in C90?
foo().x; /* invalid or not? (I surmise it's invalid) */
If the above is valid, what's the result of the following?
sizeof(foo().x);
Thanks in advance.
--
Jun, Woong (myco...@hanmail.net)
Dept. of Physics, Univ. of Seoul
hello,
does c99 said anything about allocation in a temporary memory for structure
returned by function or non-lvalue array?
Thanks.
I believe it's valid. Although it has an array type rather than the
pointer type most C programmers would expect, it's in a void expression
context so the array value (whatever it is) is discarded. I don't see
any provisions in the Standard that are being violated.
> If the above is valid, what's the result of the following?
>
> sizeof(foo().x);
The declared size of the array member. sizeof doesn't require an
lvalue, and the lvalue to pointer conversion never occurs in a sizeof
context.
-Larry Jones
I like Mom to be impressed when I fulfill the least of my obligations.
-- Calvin
Not explicitly, it just says that attempting to modify such a value or
attempting to access it after the next sequence point produces undefined
behavior.
-Larry Jones
I keep forgetting that rules are only for little nice people. -- Calvin
C90 (structure and union member operators):
] The value is that of the named member ...
But, there is no provision which gives its interpretation; there is
nothing to say about the non-lvalue array (i.e., the array value) in
C90, which seems to mean it's undefined behavior to me.
C90 (definition of undefined behavior):
] Undefined behavior is otherwise indicated in this International
] Standard by the words "undefined behavior" or by the omission of any
] explicit definition of behavior.
C90 (definition of array type):
] An array type describes a contiguously allocated non-empty set of
] *objects* with a particular member object type. called "the element
] rype." [the emphasis is mine]
All array types has to be an object by definition.
> > If the above is valid, what's the result of the following?
> >
> > sizeof(foo().x);
>
> The declared size of the array member. sizeof doesn't require an
> lvalue, and the lvalue to pointer conversion never occurs in a sizeof
> context.
I believe the same interpretation as above goes for the this case.
C90 allows the sizeof operator to be applied to an array whose
declaration uses the storage class specifier "register".
register int x[10];
sizeof(x); /* 10 * sizeof(int) */
But the "register" object is an lvalue whose address cannot be
obtained. Even if the sizeof operator doesn't evaluate the operand,
the type of the operand "foo().x" is not an array (by definition). So
what is its type? I have no idea, but the Standard also seems not to
give the exact answer for this.
Most compilers agree with you; they doesn't fail to translate both
codes above. But, at the present time I think the result of both is
undefined behavior.
Please enlighten me on this old and curious problem.
I canceled my clumsy posting, so if you happen to see it, please
ignore.
Let me follow the reasoning of interpreting the expression in question.
The result of the function call is a value, not an lvalue. So the
result of the call to the function "foo()" is a value of type
"struct ST."
foo() /* a value of type struct ST */
The structure member operator . requires its left operand to be of
stucture type. So it's allowed to apply the operator to the above
result in both C90 and C99.
foo(). [...];
The right operand of the . operator has to be a member designator, and
"x" is a member of "struct ST", which is allowed.
foo().x;
This expression has the same type as that of the deisgnated member -
"array of int", and it's not an lvalue since the left operand is not
an lvalue; so it's, say, "an array value".
Now, a question: AFAICK, a value doesn't exist on the storage in C,
thus it doesn't meet the definition of "object". If I am right, how
can the above expression has the type "array"? By definition, an array
describes a set of objects.
] An array type describes a contiguously allocated nonempty set of
] *objects* with a particular member object type, called the element
] type.
C99 doesn't change in this regard since C90. So I think it results in
undefined behavior in both C90 and C99. Am I missing something here?
If the expression can't have the type "array", the sizeof operation
applied to it would also be undefined behavior, I believe.
I'm having a hard time understanding that first sentence, but I think
the answer is that the storage *does* have a value. It's an array
value, which doesn't occur in any other context, but it's analogous to a
struct value. There are objects there even though there's no lvalue,
strange as that may appear. If there were an lvalue, it would open the
door to more horrible consequences.
-Larry Jones
I thought my life would seem more interesting with a musical
score and a laugh track. -- Calvin
Are you sure that
i = ++f().count; /* where f returns a struct by value */
yields undefined behavior under C99? (IIUC, under C++, the behavior
would be defined.)
Tom Payne
Yes, I'm sure, because Lawrence Jones wasn't just making it up, he was
making an almost word-for-word citation of section 6.5.2.2p5 from the
C99 standard:
"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."
This code have also an error: the ++ operatore must have a l-value
exspression while f().cout is a r-value.
If the left operand of . operator is an r-value (like f()) then the right
operand isn't a l-value.
> (IIUC, under C++, the behavior
> would be defined.)
It's Right, but in C++ there are the temporary object.
> Tom Payne
Best regards.
This seems to imply the existence of the temporary object as in C++.
AFAICK C doesn't have such a concept for the return value of a
function. Why didn't the concept be introduced when putting the
structure and union type into the nearly first-class citizen?
> It's an array
> value, which doesn't occur in any other context, but it's analogous to a
> struct value. There are objects there even though there's no lvalue,
> strange as that may appear. If there were an lvalue, it would open the
> door to more horrible consequences.
>
If the official interpretation can be derived from the current
definition of "array," or it is revised to include the concept of
"array value", I'd fully agree with your interpretation.
I'm sorry for not trying to find more elaborated interpretation
due to lack of time. :(
Thanks.
I presume that the same would be true of f().count[2] if count were an
array member of the returned value. Right?
Tom Payne
<t...@cs.ucr.edu> wrote:
>I presume that the same would be true of f().count[2] if count were an
>array member of the returned value. Right?
The [] operator in C doesn't require l-values for either of it's operands.
Ross Ridge
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] rri...@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/u/rridge/
db //
Presumably the array can be passed as a variadic argument without
violating either of these conditions?
>> Are you sure that
>>
>> i = ++f().count; /* where f returns a struct by value */
>>
>> yields undefined behavior under C99?
>
>This code have also an error: the ++ operatore must have a l-value
>exspression while f().cout is a r-value.
It is perfectly valid if count is of a user defined class
that implements an operator++ (no matter if const or not).
In C++ you can even write the apparently surprising
X() = X();
where you're calling the assignment operator on a temporary
object. "l" and "r" in lvalue and rvalue lost much of their
meaning for user defined classes.
Andrea
I'm not entirely clear what you're suggesting. Could you provide example
code?
You can't pass arrays as arguments to functions. You can pass a pointer
to the array, or you can pass a structure containing the array, but not
the array itself. If you pass a pointer to the array into a function,
then if that function uses that pointer to modify the array, it's in
violation of 6.5.2.2p5. If you pass the structure to a function, what
that function actually recieves is a copy of the original structure; and
it's perfectly free to modify the copy; that doesn't qualify as
modifying the original.
yes, but this situations is possible only in C++. In C99 it doesn't respect
the semantic of ++ operator.
Fra.
In fact, the function can't even use the pointer to access the array,
due
to the sequence point after evaluation of the arguments.
In C90:
struct S { char a[6]; };
struct S g(void);
void f(int, ...);
void h() { f(0, g().a); }
Where does C90 say that this either converts to pointer or is illegal?
C90 6.2.2.1:
] An lvalue is an expression (with an object type or an incomplete
] type other than void) that designates an object.
and the return value of a function is not an lvalue, which means that
the return value of the function designates no object.
The definition of the array type says:
] An /array/ type describes a contiguously allocated nonempty set of
] objects ...
Now, the question is:
can the value which designates no object have the type which describe
the set of objects?
If the answer to this question is yes, I think your interpretation is
correct even if it seems very strange. But if the answer is no, then
the type of g().a is undefined. And C99 also has the same problem in
fact.
Thanks.
I can't speak to C90, I don't have a copy of the old standard. I've no
particular interest in the C90 standard, with the C99 standard being
fully three years old by now. Searching backwards through this thread, I
find that it broke into two parts. The one intiated by Jun Woong is
asking about C90, but the one initiated by Zaza has always been about
C99; every comment I've made on this thread was in Zaza's part. In
particular, the citation I made which you quoted above was explicitly
labelled as a citation from C99.
With reference to the C99 standard, the answer to your question is:
6.3.2.1p3: 'Except when it is the operand of the sizeof operator or the
unary & operator, or is a string literal used to initialize an array, an
expression that has type "array of type" is converted to an expression
with type "pointer to type" that points to the initial element of the
array object and is not an lvalue.'
It's my understanding that the C99 committee dropped this definition
of "lvalue" because it did not include such syntactically correct
expressions as "*(int*)0" --- in fact, it left a situation where the
lvalueness of an expression could not be determined at compile time.
(Unfortunately, the C99 remedy makes "1+2" an lvalue.)
+ The definition of the array type says:
+ ] An /array/ type describes a contiguously allocated nonempty set of
+ ] objects ...
+
+ Now, the question is:
+ can the value which designates no object have the type which describe
+ the set of objects?
+
+ If the answer to this question is yes, I think your interpretation is
+ correct even if it seems very strange. But if the answer is no, then
+ the type of g().a is undefined. And C99 also has the same problem in
+ fact.
The distinction between rvalue and lvalue breaks down when structs are
returned by value. The C++ committee recognized this and has tried to
side-step the issues with the notion of temporary objects. That
attempt isn't elegant, but as far as I can tell it works.
Tom Payne
It follows that "g().a" is a pointer rvalue (that designates a pointer
value) which points to the initial element of an array object that is
the sole member of the non-object of type S returned by g(). Yuck!
Tom Payne
In C99 g().a is decayed to a pointer, but the same thing doesn't go
for C90; there is no woding to say about the conversion of the array
type to the pointer in that case. Thus, if the type of g().a can be
of type "array", then it remains as an array even when it occur within
the function call expression.
> + The definition of the array type says:
> + ] An /array/ type describes a contiguously allocated nonempty set of
> + ] objects ...
> +
> + Now, the question is:
> + can the value which designates no object have the type which describe
> + the set of objects?
> +
> + If the answer to this question is yes, I think your interpretation is
> + correct even if it seems very strange. But if the answer is no, then
> + the type of g().a is undefined. And C99 also has the same problem in
> + fact.
>
> The distinction between rvalue and lvalue breaks down when structs are
> returned by value.
This is what I'd like to claim as I said in other branch of this
thread.
You'd have to ask DMR -- he's the one who made structs and unions into
nearly first-class citizens (in 1978 or thereabouts).
> If the official interpretation can be derived from the current
> definition of "array," or it is revised to include the concept of
> "array value", I'd fully agree with your interpretation.
I misspoke when I said that array values don't occur in any other
context, they do. In particular, when you take sizeof an array, an
array value (albeit unevaluated) is what you're taking the size of.
-Larry Jones
That gives me a FABULOUS idea. -- Calvin
The sizeof operator decides its size from the *type*, not the value.
sizeof(foo().a);
In order for this expression to be well-defined, you have to be able
to say the type of foo().a. Now, the same question again:
Can the value which designates no object have the type which describe
the set of objects?
If the answer to this question is yes, all problems in C99 which I
can think of in this regard are resolved. But the answer makes a
horrible monster in C90 as we can see in Al Grant's example.
But please note that it's not my real intention to object your
previous interpretation.
Thanks.
Hmmmmm. C90 6.2.2.1, paragraph 3:
Except when it is the operand of the sizeof operatior, the unary &
operator, or is a character string literal used to initialize an array
of character type, or is a wide string literal used to initialize an
array with element type compatible with wchar_t, an lvalue that has
type "array of type" is converted to an expression that has type
"pointer to type" that points to the initial element of the arrray
object and is not an lvalue.
But you knew that already, so your point must be that "g().a" is not
an lvalue. That view seems to be supported by C90 6.3.2.3:
A postfix expression followed by a dot . and an identifier
designates a member of a structure or union object. The value is that
of the named member, and is an lvalue if the first expression is an
lvalue.
But if g().a isn't an array lvalue and isn't a pointer-to-char rvalue,
what is it? An array rvalue?
Tom Payne
I have no idea. That's what I'm asking now. With the same definition
of the type "array", in order to make C99's intention work well we
have to agree that there is an "array value". But this also make an
"array value" monster which is never decayed to a pointer in C90. I
guess that the Committee wouldn't care for this since C90 has already
been superseded.
True, but what is it the type *of*? The (unevaluated) array value.
> Can the value which designates no object have the type which describe
> the set of objects?
Yes, just as a value that is not an object can nonetheless have an
object type.
-Larry Jones
These findings suggest a logical course of action. -- Calvin
I don't think it does. It should be clear that it doesn't convert to a
pointer, but I don't see anything making it invalid, either. I consider
that a defect in C90.
-Larry Jones
My upbringing is filled with inconsistent messages. -- Calvin
Exactly.
-Larry Jones
Hello, local Navy recruitment office? Yes, this is an emergency... -- Calvin
And, since subscripting is an operation on pointer rvalues, then
the expression "g().a[0]" is ill formed. Right?
Tom Payne
Okay, I missed that point.
I can accept your argument without doubt.
Yes. In g().a[0] the indirection operator is applying to an array
value, not a pointer value, which requires a diagnostic. This can be
true only with C90.