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

Address syntax

P: n/a
Hi all,

In the code snippet below I successfully determine the address of val1:*

struct o val1=l_SYM_2B(&a).o[0];
print_aesthetic(&val1);

The structure o is heavyweight. I understand (hopefully correctly) that
(barring compiler optimisations) C will shallow copy the structure into
val1.

As I merely wished to pass the structure to print_aesthetic I combined the
statements to avoid creating an intermediate copy of the structure. This
was my attempt:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!

Regards,
Adam

* Footnote:
a is a local variable of type struct o.
l_SYM_2B(struct o * arg0) returns struct v1, defined as { struct o o[1]; };
print_aesthetic has the prototype struct v1 print_aesthetic(struct o *);
Nov 14 '05 #1
Share this Question
Share on Google+
27 Replies


P: n/a
Adam Warner wrote:
In the code snippet below I successfully determine the address of val1:*

struct o val1=l_SYM_2B(&a).o[0];
print_aesthetic(&val1);

The structure o is heavyweight. I understand (hopefully correctly) that
(barring compiler optimisations) C will shallow copy the structure into
val1.

As I merely wished to pass the structure to print_aesthetic I combined the
statements to avoid creating an intermediate copy of the structure. This
was my attempt:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!
...
* Footnote:
a is a local variable of type struct o.
l_SYM_2B(struct o * arg0) returns struct v1, defined as { struct o o[1]; };
print_aesthetic has the prototype struct v1 print_aesthetic(struct o *);


If l_SYM_2B is a function, you may get an intermediate copy of the
structure anyway. If it is a macro, it might work without ():

print_aesthetic(&l_SYM_2B(&a).o[0]);
Nov 14 '05 #2

P: n/a
Adam Warner <us****@consulting.net.nz> writes:
In the code snippet below I successfully determine the address of val1:*

struct o val1=l_SYM_2B(&a).o[0];
print_aesthetic(&val1);

The structure o is heavyweight. I understand (hopefully correctly) that
(barring compiler optimisations) C will shallow copy the structure into
val1.

As I merely wished to pass the structure to print_aesthetic I combined the
statements to avoid creating an intermediate copy of the structure. This
was my attempt:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!


I think the "+" it's complaining about is the one implicit in the
indexing operator. x[y] is equivalent to *(x+y). (Usually x is an
address, often resulting from the implicit conversion of an array
name, and y is an integer.)

--
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.
Nov 14 '05 #3

P: n/a
Hi Dietmar Schindler,
On Wed, 29 Dec 2004 15:27:27 +0100, Dietmar Schindler wrote:
* Footnote:
a is a local variable of type struct o.
l_SYM_2B(struct o * arg0) returns struct v1, defined as { struct o o[1]; };
print_aesthetic has the prototype struct v1 print_aesthetic(struct o *);


If l_SYM_2B is a function, you may get an intermediate copy of the
structure anyway. If it is a macro, it might work without ():

print_aesthetic(&l_SYM_2B(&a).o[0]);


l_SYM_2B is a function (l_SYM is a namespace prefix and underscore is the
escape character for non-alphanumerics. 0x2B is the ASCII code for +. It
is to be my dynamic Lisp function +).

v1, v2, ... are structures for stack allocating multiple return values:

struct v1 {
struct o o[1];
};

struct v2 {
struct o o[2];
};

struct v3 {
struct o o[3];
};

l_SYM_2B() returns the structure v1, i.e. leaves room for one object o on
the stack, aligned to the same position as if struct o itself was returned.

I sincerely hope no compiler will ever create a copy of struct o before
returning its address. I am only using the struct v1 for symmetry with v2,
v3, etc. The address of the first object o is expected to be the same as
the v1 structure. As I am only obtaining the address of o[0] within the
structure v1 I cannot see how C semantics could result in an intermediate
copy of o.

Regards,
Adam
Nov 14 '05 #4

P: n/a
Adam Warner wrote:
In the code snippet below I successfully determine the address of val1:*

struct o val1=l_SYM_2B(&a).o[0];
print_aesthetic(&val1);

The structure o is heavyweight. I understand (hopefully correctly) that
(barring compiler optimisations) C will shallow copy the structure into
val1.

As I merely wished to pass the structure to print_aesthetic I combined the
statements to avoid creating an intermediate copy of the structure. This
was my attempt:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!


AFAIK the code is supposed to be ill-formed in both C89/C90 and C99
because you are trying to apply the address-of operator to a non-lvalue
argument. (Although I couldn't find an exact place in either document
that would explicitly state that function return value is not an lvalue
or prohibit this application of '&' in some other way. Anyone?).

However, even if we'd assume for a second that this code is well-formed,
we'd still have to come to a conclusion that the code is broken at least
in C99. According to C99 (6.5.2.2/5), an attempt to access the returned
value after the next sequence point results in undefined behavior. If
inside 'print_aesthetic' you are actually trying to access the value
pointed by the argument (i.e. the return value of 'l_SYM_2B'), you are
in violation of that rule, since 'print_aesthetic' has a sequence point
at the entry. (Again, I couldn't find an equivalent statement in C89/C90.)

--
Best regards,
Andrey Tarasevich
Nov 14 '05 #5

P: n/a
Andrey Tarasevich wrote:
Adam Warner wrote:
In the code snippet below I successfully determine the address of val1:*

struct o val1=l_SYM_2B(&a).o[0];
print_aesthetic(&val1);

The structure o is heavyweight. I understand (hopefully correctly) that
(barring compiler optimisations) C will shallow copy the structure into
val1.

As I merely wished to pass the structure to print_aesthetic I combined the
statements to avoid creating an intermediate copy of the structure. This
was my attempt:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!


AFAIK the code is supposed to be ill-formed in both C89/C90 and C99
because you are trying to apply the address-of operator to a non-lvalue
argument. (Although I couldn't find an exact place in either document
that would explicitly state that function return value is not an lvalue
or prohibit this application of '&' in some other way. Anyone?).

However, even if we'd assume for a second that this code is well-formed,
we'd still have to come to a conclusion that the code is broken at least
in C99. According to C99 (6.5.2.2/5), an attempt to access the returned
value after the next sequence point results in undefined behavior. If
inside 'print_aesthetic' you are actually trying to access the value
pointed by the argument (i.e. the return value of 'l_SYM_2B'), you are
in violation of that rule, since 'print_aesthetic' has a sequence point
at the entry. (Again, I couldn't find an equivalent statement in C89/C90.)


On the second thought, the code should probably circumvent the "lvalue
check" since it actually accesses an element of an array aggregated by
the returned struct. Array element access in C decays to pointer
arithmetics, which will formally turn the '&'s argument into an lvalue
(i.e. 'l_SYM_2B(&a).o[0]' is '*(l_SYM_2B(&a).o + 0)' and the result of
'*' is always an lvalue).

However, the second problem - UB - is still there.

--
Best regards,
Andrey Tarasevich
Nov 14 '05 #6

P: n/a
Hi Andrey Tarasevich,
On Wed, 29 Dec 2004 16:27:26 -0800, Andrey Tarasevich wrote:
Adam Warner wrote:
In the code snippet below I successfully determine the address of val1:*

struct o val1=l_SYM_2B(&a).o[0];
print_aesthetic(&val1);

The structure o is heavyweight. I understand (hopefully correctly) that
(barring compiler optimisations) C will shallow copy the structure into
val1.

As I merely wished to pass the structure to print_aesthetic I combined the
statements to avoid creating an intermediate copy of the structure. This
was my attempt:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!


AFAIK the code is supposed to be ill-formed in both C89/C90 and C99
because you are trying to apply the address-of operator to a non-lvalue
argument. (Although I couldn't find an exact place in either document
that would explicitly state that function return value is not an lvalue
or prohibit this application of '&' in some other way. Anyone?).

However, even if we'd assume for a second that this code is well-formed,
we'd still have to come to a conclusion that the code is broken at least
in C99. According to C99 (6.5.2.2/5), an attempt to access the returned
value after the next sequence point results in undefined behavior. If
inside 'print_aesthetic' you are actually trying to access the value
pointed by the argument (i.e. the return value of 'l_SYM_2B'), you are
in violation of that rule, since 'print_aesthetic' has a sequence point
at the entry. (Again, I couldn't find an equivalent statement in C89/C90.)


I've been considering similar issues after I tried to compile this to show
the addresses were the same:

printf ("&(l_SYM_2B(&a)) is %p\n", &(l_SYM_2B(&a)));
printf ("&(l_SYM_2B(&a).o[0]) is %p\n", &(l_SYM_2B(&a).o[0]));

The first statement correctly generates the error: "invalid lvalue in
unary `&'" according to 6.5.3.2 (a meta question is *why* it should be
prohibited to obtain the address of a return value on the stack. Sometimes
C feels frustratingly high level :-)

The second one may however be legitimate in C99. The semantics state (note
that this the final draft): <http://dev.unicals.com/c99-draft.html#6.5.3.2>

Similarly, if the operand is the result of a [] operator, neither the &
operator nor the unary * that is implied by the [] is evaluated and the
result is as if the & operator were removed and the [] operator were
changed to a + operator. Otherwise, the result is a pointer to the
object or function designated by its operand.

The (l_SYM_2B(&a).o[0]) operand appears to be the result of a [] operator.
So the result is evaluated as if the & operator were removed and the []
operator were changed to a + operator.

print_aesthetic does try to access the value pointed by the argument.
It has the prototype: struct v1 print_aesthetic(struct o *);

There's no reason this can't become struct v1 print_aesthetic(struct o[])
because arrays are also passed by reference. If you believe this change to
print_aesthetic is necessary to be conforming I can make the change. I'd
like to avoid it because the syntax is messier.

With the function argument "struct o * arg" I can use structure
deferencing notation, e.g. arg->type, and follow pointer members to the
next object. With the function argument "struct o * arg[]" I have to
reference members as arg[0].type, arg[1].type, etc.

Regards,
Adam
Nov 14 '05 #7

P: n/a
Adam Warner wrote:
...
AFAIK the code is supposed to be ill-formed in both C89/C90 and C99
because you are trying to apply the address-of operator to a non-lvalue
argument. (Although I couldn't find an exact place in either document
that would explicitly state that function return value is not an lvalue
or prohibit this application of '&' in some other way. Anyone?).

However, even if we'd assume for a second that this code is well-formed,
we'd still have to come to a conclusion that the code is broken at least
in C99. According to C99 (6.5.2.2/5), an attempt to access the returned
value after the next sequence point results in undefined behavior. If
inside 'print_aesthetic' you are actually trying to access the value
pointed by the argument (i.e. the return value of 'l_SYM_2B'), you are
in violation of that rule, since 'print_aesthetic' has a sequence point
at the entry. (Again, I couldn't find an equivalent statement in C89/C90.)
I've been considering similar issues after I tried to compile this to show
the addresses were the same:

printf ("&(l_SYM_2B(&a)) is %p\n", &(l_SYM_2B(&a)));
printf ("&(l_SYM_2B(&a).o[0]) is %p\n", &(l_SYM_2B(&a).o[0]));

The first statement correctly generates the error: "invalid lvalue in
unary `&'" according to 6.5.3.2 (a meta question is *why* it should be
prohibited to obtain the address of a return value on the stack. Sometimes
C feels frustratingly high level :-)

The second one may however be legitimate in C99. The semantics state (note
that this the final draft): <http://dev.unicals.com/c99-draft.html#6.5.3.2>

Similarly, if the operand is the result of a [] operator, neither the &
operator nor the unary * that is implied by the [] is evaluated and the
result is as if the & operator were removed and the [] operator were
changed to a + operator. Otherwise, the result is a pointer to the
object or function designated by its operand.

The (l_SYM_2B(&a).o[0]) operand appears to be the result of a [] operator.
So the result is evaluated as if the & operator were removed and the []
operator were changed to a + operator.


I found one key difference between the C89/C90 and C99 that must be at
play here.

The property of "being a non-lvalue" propagates from the left side of
'.' operator to its result in both C89/90 and C99. This means that the
following expression

l_SYM_2B(&a).o

is not an lvalue in both C89/90 and C99.

Now, the key moment: in C89/C90 the array-to-pointer conversion was only
applicable to _lvalues_ of array type (see 6.2.2.1). In C99 this
limitation was removed (see 6.3.2.1/3), and now the array-to-pointer
conversion is applicable to any values of array type.

This means that the following expression

l_SYM_2B(&a).o[0]

is ill-formed in C89/90 (there's no way to apply operator '[]' to the
result of previous expression) and well-formed in C99. For example, the
following simple code will be rejected by Comeau Online compiler in
C89/C90 mode and accepted in C99 mode for this very reason

struct S { int i[5]; };

struct S foo() { struct S s = { 0 }; return s; }

int main() { int i = foo().i[1]; }

Note, that this code does not involve operator '&' at all.

However, this also means that you original code (with intermediate
variable 'val1') was also ill-formed in C89/C90. But you said that you
could compile it without any problems. Apparently, some quirks of
concrete compilers are also at play here.

print_aesthetic does try to access the value pointed by the argument.
It has the prototype: struct v1 print_aesthetic(struct o *);
Once again, if I'm not missing something, that definitely leads to UB in
both C89/C90 and C99 for reasons explained in my previous messages.
There's no reason this can't become struct v1 print_aesthetic(struct o[])
because arrays are also passed by reference.
This is an exact equivalent of the previous declaration. It won't make
any difference.
If you believe this change to
print_aesthetic is necessary to be conforming I can make the change. I'd
like to avoid it because the syntax is messier. With the function argument "struct o * arg" I can use structure
deferencing notation, e.g. arg->type, and follow pointer members to the
next object. With the function argument "struct o * arg[]" I have to
reference members as arg[0].type, arg[1].type, etc.


Did you mean 'struct o arg[]'? If yes, then you are wrong. The following
two declarations

struct v1 print_aesthetic(struct o *arg)
struct v1 print_aesthetic(struct o arg[])

are exactly equivalent in C. In both cases you can use 'arg->type' and
'arg[0].type'.

--
Best regards,
Andrey Tarasevich
Nov 14 '05 #8

P: n/a
Hi Andrey Tarasevich,
On Wed, 29 Dec 2004 16:37:05 -0800, Andrey Tarasevich wrote:
On the second thought, the code should probably circumvent the "lvalue
check" since it actually accesses an element of an array aggregated by
the returned struct. Array element access in C decays to pointer
arithmetics, which will formally turn the '&'s argument into an lvalue
(i.e. 'l_SYM_2B(&a).o[0]' is '*(l_SYM_2B(&a).o + 0)' and the result of
'*' is always an lvalue).
I'm glad we agree that this part is legal C99.
However, the second problem - UB - is still there.


6.5.2.2/5: "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."

Therefore I have to copy the return value into a local variable so I can
pass it by reference to a function, thereby defeating the benefits of pass
by reference (which were to avoid copying heavyweight structures and
permit mutation of all objects via function calls).

My mistake is that I can't rely upon a return value remaining on the stack
until the _calling_ function exits (and when you think about it the stack
could blow up horribly otherwise!)

<http://madchat.org/osdevl/stack.html>

As you can see, the data still is on the stack, but once the pop
operation is completed, we consider that part of the data invalid.
Thus, the next push operation overwrites this data. But that's OK,
because we assume that after a pop operation, the data that's popped
off is considered garbage.

If you've ever made the error of returning a pointer to a local
variable or to a parameter that was passed by value and wondered why
the value stayed valid initially, but later on got corrupted, you
should now know the reason.

The data still stays on the garbage part of the stack until the next
push operation overwrites it (that's when the data gets corrupted).

My method, while supremely fast and apparently correct with simple
benchmarks, would have resulted in hideously difficult to track down
data corruption.

Thanks for enlightening me.

Regards,
Adam
Nov 14 '05 #9

P: n/a
Hi Andrey Tarasevich,
On Wed, 29 Dec 2004 17:45:01 -0800, Andrey Tarasevich wrote:
Did you mean 'struct o arg[]'?
I did, sorry!
If yes, then you are wrong. The following two declarations

struct v1 print_aesthetic(struct o *arg)
struct v1 print_aesthetic(struct o arg[])

are exactly equivalent in C. In both cases you can use 'arg->type' and
'arg[0].type'.


That's great to know! Thanks for helping me appreciate the undefined
behaviour of calling an (at least not inlined) function with the address
of the return value of a nested function.

Regards,
Adam
Nov 14 '05 #10

P: n/a
Adam Warner wrote:
Hi Andrey Tarasevich,
On Wed, 29 Dec 2004 16:37:05 -0800, Andrey Tarasevich wrote:
On the second thought, the code should probably circumvent the "lvalue
check" since it actually accesses an element of an array aggregated by
the returned struct. Array element access in C decays to pointer
arithmetics, which will formally turn the '&'s argument into an lvalue
(i.e. 'l_SYM_2B(&a).o[0]' is '*(l_SYM_2B(&a).o + 0)' and the result of
'*' is always an lvalue).
I'm glad we agree that this part is legal C99.


Well, I'm sorry to say that, but it appears that my above statement is
incorrect in C89/C90 and irrelevant in C99. The reason why it is
incorrect in C89/C90 is explained in my other message (in short,
'l_SYM_2B(&a).o' is an rvalue of array type and '[]' cannot be applied
to it). The reason why it is irrelevant in C99 is explained in your
other message ('&' and '[]' cancel each other out).

However, this doesn't change the fact that we agree that this part is
well-formed in C99 :)

I would still like to know whether the following code

struct S { int i[5]; };

struct S foo() { struct S s = { 0 }; return s; }

int main() { int i = foo().i[1]; }

is valid C89/C90? Comeau thinks it is not (and the standard, as I
understand it, seems to support this position). GCC, on the other hand,
accepts it. Can anyone comment on it?
However, the second problem - UB - is still there.


6.5.2.2/5: "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."

Therefore I have to copy the return value into a local variable so I can
pass it by reference to a function, thereby defeating the benefits of pass
by reference (which were to avoid copying heavyweight structures and
permit mutation of all objects via function calls).


It looks like you have to do that, yes.

Another approach would be to pass a pointer to the destination structure
to 'l_SYM_2B' function as a separate parameter and fill it in inside
'l_SYM_2B' instead of returning it from 'l_SYM_2B' by value. In other
words, you can (if you can) change the signature of 'l_SYM_2B' to
something like

void l_SYM_2B(<whatever>* lp_a, struct v1* lp_v1)
{
/* fill in '*lp_v1' accordingly */
}

Your original code in this case would look as follows

struct v1 val1;
l_SYM_2B(&a, &val1);
print_aesthetic(&val1.o[0]);

No heavy copying is performed.
My mistake is that I can't rely upon a return value remaining on the stack
until the _calling_ function exits (and when you think about it the stack
could blow up horribly otherwise!)
That's an interesting question. In C++ you wouldn't have to worry about
the lifetime of temporary objects (assuming that you switch from using
pointers to using references) because the lifetime of temporary objects
extends at least to the end of the expression. I don't see any reason
why it should be different in C (except for the fact that C doesn't have
references).
<http://madchat.org/osdevl/stack.html>

As you can see, the data still is on the stack, but once the pop
operation is completed, we consider that part of the data invalid.
Thus, the next push operation overwrites this data. But that's OK,
because we assume that after a pop operation, the data that's popped
off is considered garbage.

If you've ever made the error of returning a pointer to a local
variable or to a parameter that was passed by value and wondered why
the value stayed valid initially, but later on got corrupted, you
should now know the reason.

The data still stays on the garbage part of the stack until the next
push operation overwrites it (that's when the data gets corrupted).


I don't think this is relevant in your case. The text you quoted talks
about dangers of returning _pointers_ to local data from functions. You
are not returning any pointers to local data from functions in your
code. Your code return the data _by_ _value_. This is a significantly
different situation.

--
Best regards,
Andrey Tarasevich
Nov 14 '05 #11

P: n/a
Adam Warner wrote:
I've been considering similar issues after I tried to compile this to show the addresses were the same:

printf ("&(l_SYM_2B(&a)) is %p\n", &(l_SYM_2B(&a)));
printf ("&(l_SYM_2B(&a).o[0]) is %p\n", &(l_SYM_2B(&a).o[0]));
A minor point: %p expects a (void *), you are causing UB
by passing a different pointer type to it. You need to cast
to (void *) in case you are on a platform where (void *) is
bigger than pointer-to-struct.

The first statement correctly generates the error: "invalid lvalue
in unary `&'" according to 6.5.3.2 (a meta question is *why* it
should be prohibited to obtain the address of a return value on
the stack. Sometimes C feels frustratingly high level :-)


What stack? (Some implementations don't have one). The C90
logic seems clear: the return value might be in a register, so
how can you take the address of it? (or of parts of it).
Reading Andrey Tarasevich's replies, it seems that C99 fixes
this problem by forcing the return value to be an lvalue if
you use operator* (or equivalently, operator[]) on it.
My question then: suppose we have in C99

struct t1 t = l_SYM_2B(&x).o[0];

does that mean that there must be an lvalue object created to be
the return-value (so that [0] can be applied to its .o),
and then that object is copied to t ? ie. there is a wasted
copy?

Nov 14 '05 #12

P: n/a
Hi Andrey Tarasevich,
On Wed, 29 Dec 2004 18:31:17 -0800, Andrey Tarasevich wrote:
However, this doesn't change the fact that we agree that this part is
well-formed in C99 :)

I would still like to know whether the following code

struct S { int i[5]; };

struct S foo() { struct S s = { 0 }; return s; }

int main() { int i = foo().i[1]; }

is valid C89/C90? Comeau thinks it is not (and the standard, as I
understand it, seems to support this position). GCC, on the other hand,
accepts it. Can anyone comment on it?
However, the second problem - UB - is still there.


6.5.2.2/5: "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."

Therefore I have to copy the return value into a local variable so I can
pass it by reference to a function, thereby defeating the benefits of pass
by reference (which were to avoid copying heavyweight structures and
permit mutation of all objects via function calls).


It looks like you have to do that, yes.

Another approach would be to pass a pointer to the destination structure
to 'l_SYM_2B' function as a separate parameter and fill it in inside
'l_SYM_2B' instead of returning it from 'l_SYM_2B' by value. In other
words, you can (if you can) change the signature of 'l_SYM_2B' to
something like

void l_SYM_2B(<whatever>* lp_a, struct v1* lp_v1)
{
/* fill in '*lp_v1' accordingly */
}

Your original code in this case would look as follows

struct v1 val1;
l_SYM_2B(&a, &val1);
print_aesthetic(&val1.o[0]);

No heavy copying is performed.


Ingenious. Thanks for the tip. In other words I make it the responsibility
of the caller to supply the data structure to fill in. The caller can
therefore create the value or reference semantics it desires. For example
I could supply a reference to a new object to an increment function for
functional semantics. Or I could supply a reference to the old object and
it would be mutated instead.
My mistake is that I can't rely upon a return value remaining on the stack
until the _calling_ function exits (and when you think about it the stack
could blow up horribly otherwise!)


That's an interesting question. In C++ you wouldn't have to worry about
the lifetime of temporary objects (assuming that you switch from using
pointers to using references) because the lifetime of temporary objects
extends at least to the end of the expression.


That would be useful. It wouldn't cause the stack to blow up like I was
imagining (repeatedly calling a function within a loop) as the references
to the return values would become invalid at the end of each expression.
But it would permit me to nest references to return values.

Regards,
Adam
Nov 14 '05 #13

P: n/a
On Thu, 30 Dec 2004 13:58:27 +1300, Adam Warner wrote:
The second one may however be legitimate in C99. The semantics state (note
that this the final draft): <http://dev.unicals.com/c99-draft.html#6.5.3.2>

Similarly, if the operand is the result of a [] operator, neither the &
operator nor the unary * that is implied by the [] is evaluated and the
result is as if the & operator were removed and the [] operator were
changed to a + operator. Otherwise, the result is a pointer to the
object or function designated by its operand.


I've come across "Rationale for International Standard--Programming
Languages--C" which explains some of the decisions of C99:
<http://careferencemanual.com/>
<http://wwwold.dkuug.dk/JTC1/SC22/WG14/www/docs/n897.pdf>

In relation to 6.5.3.2 it states:

6.5.3.2 Address and indirection operators

Some implementations have not allowed the & operator to be applied to
an array or a function. (The construct was permitted in early versions
of C, then later made optional.) The C89 Committee endorsed the
construct since it is unambiguous, and since data abstraction is
enhanced by allowing the important & operator to apply uniformly to any
addressable entity.

Regards,
Adam
Nov 14 '05 #14

P: n/a
Andrey Tarasevich wrote:
Another approach would be to pass a pointer to the destination structure
to 'l_SYM_2B' function as a separate parameter and fill it in inside
'l_SYM_2B' instead of returning it from 'l_SYM_2B' by value. In other
words, you can (if you can) change the signature of 'l_SYM_2B' to
something like

void l_SYM_2B(<whatever>* lp_a, struct v1* lp_v1)
{
/* fill in '*lp_v1' accordingly */
}

Your original code in this case would look as follows

struct v1 ;
l_SYM_2B(&a, &val1);
print_aesthetic(&val1.o[0]);

No heavy copying is performed.


Still l_SYM_2B has to copy an entire struct v1 into *lp_v1. That is
heavy copying (and, moreover, leaving compilers less opportunities to
optimize it away).

Regards,
Dietmar Schindler
Nov 14 '05 #15

P: n/a
Dietmar Schindler wrote:
Andrey Tarasevich wrote:
Another approach would be to pass a pointer to the destination structure
to 'l_SYM_2B' function as a separate parameter and fill it in inside
'l_SYM_2B' instead of returning it from 'l_SYM_2B' by value. In other
words, you can (if you can) change the signature of 'l_SYM_2B' to
something like

void l_SYM_2B(<whatever>* lp_a, struct v1* lp_v1)
{
/* fill in '*lp_v1' accordingly */
}

Your original code in this case would look as follows

struct v1 ;
l_SYM_2B(&a, &val1);
print_aesthetic(&val1.o[0]);

No heavy copying is performed.


Still l_SYM_2B has to copy an entire struct v1 into *lp_v1. That is
heavy copying (and, moreover, leaving compilers less opportunities to
optimize it away).
...


As I understood it, the OP is not actually doing any dumb "copying" of
anything into the resultant structure. But even if he does, that's still
beside the point. As far as I'm concerned, inside 'l_SYM_2B' he is
somehow _building_ the new 'struct v1' from 'a' (and I don't know what
the type of 'a' is). How exactly he is doing it inside 'l_SYM_2B' - I
don't know, but it is irrelevant anyway.

Note that OP's original code with intermediate value 'val1' did two
things: firstly, it had to form the result of 'l_SYM_2B' function (it is
done inside 'l_SYM_2B' and I, once again, don't know and don't care how
it is done, by "heavy copying" or in some other way), and secondly, the
returned result was copied to variable 'val1', which is definitely a
"heavy copying". The approach that I propose eliminates the second heavy
copying, it builds the value of 'val1' "in place" (through a pointer).
That's the entire point of this approach.

A smart optimizing compiler might be good enough to do the same thing
with the original code automatically. But just to be sure one can do it
manually. I don't see how this can impede any compiler optimizations.
Can you please elaborate?

Whether the first step (forming of the new 'struct v1' value from 'a')
can be optimized is a separate question. So far we didn't even touch it
in this discussion.

--
Best regards,
Andrey Tarasevich

Nov 14 '05 #16

P: n/a
Hi, Adam!

Adam Warner wrote:
On Wed, 29 Dec 2004 15:27:27 +0100, Dietmar Schindler wrote:
* Footnote:
a is a local variable of type struct o.
l_SYM_2B(struct o * arg0) returns struct v1, defined as { struct o o[1]; };
print_aesthetic has the prototype struct v1 print_aesthetic(struct o *);
If l_SYM_2B is a function, you may get an intermediate copy of the
structure anyway. ...

l_SYM_2B() returns the structure v1, i.e. leaves room for one object o on
the stack, aligned to the same position as if struct o itself was returned.

I sincerely hope no compiler will ever create a copy of struct o before
returning its address. I am only using the struct v1 for symmetry with v2,
v3, etc. The address of the first object o is expected to be the same as
the v1 structure. As I am only obtaining the address of o[0] within the
structure v1 I cannot see how C semantics could result in an intermediate
copy of o.


I had already typed in a different reply, when I realized where our
misunderstanding might be. By "intermediate copy of the structure", I
meant your abovementioned object o (since this is already a copy of v1)
itself, not a copy of o. If you mean it reversely, then we agree.

Regards,
Dietmar
Nov 14 '05 #17

P: n/a
Andrey Tarasevich wrote:
Dietmar Schindler wrote:
Andrey Tarasevich wrote:
Another approach would be to pass a pointer to the destination structure
to 'l_SYM_2B' function as a separate parameter and fill it in inside
'l_SYM_2B' instead of returning it from 'l_SYM_2B' by value. In other
words, you can (if you can) change the signature of 'l_SYM_2B' to
something like

void l_SYM_2B(<whatever>* lp_a, struct v1* lp_v1)
{
/* fill in '*lp_v1' accordingly */
}

Your original code in this case would look as follows

struct v1 ;
l_SYM_2B(&a, &val1);
print_aesthetic(&val1.o[0]);

No heavy copying is performed.
Still l_SYM_2B has to copy an entire struct v1 into *lp_v1. That is
heavy copying (and, moreover, leaving compilers less opportunities to
optimize it away).
...


As I understood it, the OP is not actually doing any dumb "copying" of
anything into the resultant structure. But even if he does, that's still
beside the point. As far as I'm concerned, inside 'l_SYM_2B' he is
somehow _building_ the new 'struct v1' from 'a' (and I don't know what
the type of 'a' is). How exactly he is doing it inside 'l_SYM_2B' - I
don't know, but it is irrelevant anyway.


According to what Adam Warner (the OP) wrote in a followup about
l_SYM_2B ("It
is to be my dynamic Lisp function +"), it seems you understood it right.
I was referring to the (in the original post not ruled out) case where
l_SYM_2B just returned an existing object, and that was not "beside the
point" (see below).
Note that OP's original code with intermediate value 'val1' did two
things: firstly, it had to form the result of 'l_SYM_2B' function (it is
done inside 'l_SYM_2B' and I, once again, don't know and don't care how
it is done, by "heavy copying" or in some other way), and secondly, the
returned result was copied to variable 'val1', which is definitely a
"heavy copying". The approach that I propose eliminates the second heavy
copying, it builds the value of 'val1' "in place" (through a pointer).
That's the entire point of this approach.

A smart optimizing compiler might be good enough to do the same thing
with the original code automatically. But just to be sure one can do it
manually. I don't see how this can impede any compiler optimizations.
Can you please elaborate?

Whether the first step (forming of the new 'struct v1' value from 'a')
can be optimized is a separate question. So far we didn't even touch it
in this discussion.


I meant exactly this "first step". Since we had no conversation about
dividing the original problem (which included avoiding the creation of
intermediate copies of the structure) into distinct questions or steps,
I don't think we can say that we "didn't even touch it in this
discussion" - I certainly did.

You, too, seem to suspect that forming of the new 'struct v1' value from
'a' can be optimized in certain cases (which, as we know now, are
probably different from the matter in hand). May I assume that you do
see how passing a pointer to a to-be-filled structure can impede an
optimization in such cases, and there is no need to elaborate on that?

Best regards,
Dietmar Schindler
Nov 14 '05 #18

P: n/a
On Wed, 29 Dec 2004 18:31:17 -0800, Andrey Tarasevich wrote:

....
I would still like to know whether the following code

struct S { int i[5]; };

struct S foo() { struct S s = { 0 }; return s; }

int main() { int i = foo().i[1]; }

is valid C89/C90? Comeau thinks it is not (and the standard, as I
understand it, seems to support this position). GCC, on the other hand,
accepts it. Can anyone comment on it?


Here gcc -ansi -pedantic gives:

retval.c:5: warning: ISO C89 forbids subscripting non-lvalue array

So gcc also generates an appropriate diagnostic.

Lawrence

Nov 14 '05 #19

P: n/a
Dietmar Schindler wrote:
>> ...
>> Your original code in this case would look as follows
>>
>> struct v1 val1;
>> l_SYM_2B(&a, &val1);
>> print_aesthetic(&val1.o[0]);
>>
>> No heavy copying is performed.
>
> Still l_SYM_2B has to copy an entire struct v1 into *lp_v1. That is
> heavy copying (and, moreover, leaving compilers less opportunities to
> optimize it away).
> ...
As I understood it, the OP is not actually doing any dumb "copying" of
anything into the resultant structure. But even if he does, that's still
beside the point. As far as I'm concerned, inside 'l_SYM_2B' he is
somehow _building_ the new 'struct v1' from 'a' (and I don't know what
the type of 'a' is). How exactly he is doing it inside 'l_SYM_2B' - I
don't know, but it is irrelevant anyway.


According to what Adam Warner (the OP) wrote in a followup about
l_SYM_2B ("It
is to be my dynamic Lisp function +"), it seems you understood it right.
I was referring to the (in the original post not ruled out) case where
l_SYM_2B just returned an existing object, and that was not "beside the
point" (see below).


Oh, if 'l_SYM_2B' returns an exact copy of an existing object (i.e. an
object that is not local to function 'l_SYM_2B'), then, of course, the
situation can be perceived differently. In that case, maybe, it could be
made to return a [const-qualified] pointer to the object

const struct v1* l_SYM_2B(<whatever>* lp_a);

and the original code would look as follows

print_aesthetic(&l_SYM_2B(&a)->o[0]);

thus eliminating all copying entirely.

But frankly, I don't see where Adam says that this function returns an
existing object (I did re-read his follow-up message you mention). All
the time I was assuming that every invocation of 'l_SYM_2B' is supposed
to build and return a new instance of 'struct v1'. I could be wrong, of
course.
...
You, too, seem to suspect that forming of the new 'struct v1' value from
'a' can be optimized in certain cases (which, as we know now, are
probably different from the matter in hand). May I assume that you do
see how passing a pointer to a to-be-filled structure can impede an
optimization in such cases, and there is no need to elaborate on that?
...


I'm not saying that it can't impede optimization, but at the same time
I'd be glad to see a [simplified] example that would illustrate the
particular optimization you have in mind.

If you are talking about the above situation with 'l_SYM_2B' always
returning "an existing object", then, of course, it can be optimized.
However, I can only see how it can be done manually, and somehow I'm not
sure that we can realistically expect this kind of optimization from the
compiler.

--
Best regards,
Andrey Tarasevich
Nov 14 '05 #20

P: n/a
Hi Andrey Tarasevich,
On Thu, 30 Dec 2004 10:42:13 -0800, Andrey Tarasevich wrote:
But frankly, I don't see where Adam says that this function returns an
existing object (I did re-read his follow-up message you mention). All
the time I was assuming that every invocation of 'l_SYM_2B' is supposed
to build and return a new instance of 'struct v1'. I could be wrong, of
course.


You assumed right. `l_SYM_2B' built and returned a new instance of
`struct v1'. Now the caller sets up the return object or objects and
passes its address to each function (which returns void).

Regards,
Adam
Nov 14 '05 #21

P: n/a
Andrey Tarasevich wrote:

Dietmar Schindler wrote:
According to what Adam Warner (the OP) wrote in a followup about
l_SYM_2B ("It
is to be my dynamic Lisp function +"), it seems you understood it right.
I was referring to the (in the original post not ruled out) case where
l_SYM_2B just returned an existing object, and that was not "beside the
point" (see below).
...
But frankly, I don't see where Adam says that this function returns an
existing object (I did re-read his follow-up message you mention). All


As I tried to express before, Adam didn't say that in his original
message, but he neither said anything contrary, so at that time both
assumptions were possible.
the time I was assuming that every invocation of 'l_SYM_2B' is supposed
to build and return a new instance of 'struct v1'. I could be wrong, of
course.


Now we know that you were right.
You, too, seem to suspect that forming of the new 'struct v1' value from
'a' can be optimized in certain cases (which, as we know now, are
probably different from the matter in hand). May I assume that you do
see how passing a pointer to a to-be-filled structure can impede an
optimization in such cases, and there is no need to elaborate on that?
...


I'm not saying that it can't impede optimization, but at the same time
I'd be glad to see a [simplified] example that would illustrate the
particular optimization you have in mind.

If you are talking about the above situation with 'l_SYM_2B' always
returning "an existing object", then, of course, it can be optimized.
However, I can only see how it can be done manually, and somehow I'm not
sure that we can realistically expect this kind of optimization from the
compiler.


I have in mind an implementation where functions which are defined to
return a struct are compiled to code that just returns an address (of an
existing struct object or of a statically allocated return area) and
where it is left to the calling code to copy the struct at that address
into another object, if needed. Since the calling code here has to do
the copying, it can be optimized away in cases where no copy is needed.

But I must admit that I know of no real compiler doing this, so you may
again be right by not expecting this kind of optimization.

Best regards,
Dietmar Schindler
Nov 14 '05 #22

P: n/a
Andrey Tarasevich <an**************@hotmail.com> writes:
Adam Warner wrote:

print_aesthetic(&(l_SYM_2B(&a).o[0]));

I discovered that the only way to get GCC 3.4 to accept this syntax was to
append the -std=c99 compiler option. Otherwise GCC produced the error
message "invalid operands to binary +", i.e. it appeared to be trying to
interpret the address operator as a binary AND.

Is this a bug in GCC's pre-C99 support or has the syntax of C been changed
in C99 to support the code I wrote above? If so, thank goodness!


AFAIK the code is supposed to be ill-formed in both C89/C90 and C99
because you are trying to apply the address-of operator to a non-lvalue
argument. (Although I couldn't find an exact place in either document
that would explicitly state that function return value is not an lvalue
or prohibit this application of '&' in some other way. Anyone?).


The calling conventions of the underlying architecture may require that
at least some function arguments are passed in registers. Registers
don't necessarily work with the address-of operator, so foo(&bar()) may
be undefined.

ISO/IEC 9899:1999 pp. 78, 6.5.3.2 (Unary operators // Address and
indirection operators), constraint 1 refers only to expressions that
use the 'register' keyword.

pp. 71 6.5.2.2 (4) says that "An argument may be an expression of any
object type", but I'm not sure if this means that anything can be
assumed about the storage of argument values before the function call
happens.
Nov 14 '05 #23

P: n/a
(I realize these are a bit old, but I have been away for some time...)

[Given a function l_SYM_2B() that returns a struct that contains
an array...]
Adam Warner wrote:
The (l_SYM_2B(&a).o[0]) operand appears to be the result of a [] operator.
So the result is evaluated as if the & operator were removed and the []
operator were changed to a + operator.

In article <10*************@news.supernews.com>
Andrey Tarasevich <an**************@hotmail.com> wrote:I found one key difference between the C89/C90 and C99 that must be at
play here.

The property of "being a non-lvalue" propagates from the left side of
'.' operator to its result in both C89/90 and C99. This means that the
following expression

l_SYM_2B(&a).o

is not an lvalue in both C89/90 and C99.

Now, the key moment: in C89/C90 the array-to-pointer conversion was only
applicable to _lvalues_ of array type (see 6.2.2.1). In C99 this
limitation was removed (see 6.3.2.1/3), and now the array-to-pointer
conversion is applicable to any values of array type.

This means that the following expression

l_SYM_2B(&a).o[0]

is ill-formed in C89/90 (there's no way to apply operator '[]' to the
result of previous expression) and well-formed in C99.
Right.

This is where "The Rule" is illustrative. I always say that The
Rule describes how array objects are treated in a value context:

In a value context, an object of type "array N of T" is converted
to a value of type "pointer to T", pointing to the first element
of that array, i.e., the one with subscript 0.

When we apply The Rule in typical C code, we find that arrays never
survive to the "value" stage: arrays are only ever objects, and
when you attempt to do anything with their value, you get instead
a pointer value, stripping off the "array N of" part.

Functions that return a "struct" that contains an array create a
problem. A function's return value is, by definition, a value,
not an object.

C handles values of type "struct S" a bit clumsily, but
consistently: they exist, you can put them on the right hand side
of assignments, you can pass them to functions, and so on:

struct S a, b;
struct T x;
...
a = b; /* valid, copies all the fields of b to a */
x = b; /* ERROR: type mismatch; diagnostic required */
f(b); /* valid, passes the value of the entire struct */

And of course, you can return them from functions as well:

struct S sfunc(void);
b = sfunc();

(Many compilers actually implement this by passing a "secret"
argument to the function, &b in this case, to let the function fill
in the provided struct -- but this is an implementation detail.
Note that in this case, if the return value is discarded, the
compiler has to pass a pointer anyway. Typically this is a pointer
to a compiler-created temporary that is then discarded.)

Because of The Rule, this "works as a value" does not hold for
arrays -- you put an array object in a value context and get a
pointer:

int arr[10];
int *p;

p = a; /* valid, sets p to point to &a[0]; array NOT copied */
g(a); /* valid, passes &a[0] to g(); array NOT copied */

and, by fiat, C simply forbids functions from returning arrays, so
that there is no problem with "function returning array N of T".
But if a function returns a struct and the struct contains an array,
we have the impossible: an array value. Logically, the value is
the value of the entire array -- all the elements at once, as it
were.

Because C applies The Rule to all *other* cases, it "wants", in an
odd nonsentient fashion, to apply it here as well -- but The Rule
converts an array object to a pointer to that object's first element,
and we do not *have* an object to point into. (A number of C89
compilers have bugs here, and apply The Rule anyway, giving you a
pointer to -- well, something. Whether that "something" lasts even
until the next expression is tough to predict, at least not without
digging into the compiler innards or experimenting with disassembling
the compiler's output. But this *is* a bug; it comes about because
the compiler fails to enforce the "object" part of The Rule. After
all, the compiler writer probably forgot that array values exist
at all -- they only occur in this one special case, of a function
returning a struct containing an array.)
For example, the
following simple code will be rejected by Comeau Online compiler in
C89/C90 mode and accepted in C99 mode for this very reason

struct S { int i[5]; };

struct S foo() { struct S s = { 0 }; return s; }

int main() { int i = foo().i[1]; }

Note, that this code does not involve operator '&' at all.
C99 once again solves the "what does it mean to get a pointer to
the nonexistent object" problem by fiat, restricting what you can
do: "i = foo().i[index]" is OK; "p = foo().i" is not.
However, this also means that you original code (with intermediate
variable 'val1') was also ill-formed in C89/C90. But you said that you
could compile it without any problems. Apparently, some quirks of
concrete compilers are also at play here.


Indeed. In general, it compiles (because, unlike the Comeau
compiler, the compiler fails to check the "must be an object" part)
and then produces unreliable machine code, often in weird and
unpredictable fashions (e.g., change the declaration order of local
variables and it sometimes works).
--
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.
Nov 14 '05 #24

P: n/a
[Again, we are dealing with a function that returns a struct that
contains an array. Here the function is named l_SYM_2B() and the
array in the struct is named "o", and is the first (and in fact
only) member of the struct.]
Adam Warner wrote:
[corrected for types, but still inherently "wrong" :-) ]
I've been considering similar issues after I tried to compile
this to show the addresses were the same:

printf ("&(l_SYM_2B(&a)) is %p\n", (void *)&(l_SYM_2B(&a)));
printf ("&(l_SYM_2B(&a).o[0]) is %p\n", (void *)&(l_SYM_2B(&a).o[0]));

The first statement correctly generates the error: "invalid lvalue
in unary `&'" according to 6.5.3.2 (a meta question is *why* it
should be prohibited to obtain the address of a return value on
the stack. Sometimes C feels frustratingly high level :-)


In article <11**********************@c13g2000cwb.googlegroups .com>
Old Wolf <ol*****@inspire.net.nz> wrote:What stack? (Some implementations don't have one). The C90
logic seems clear: the return value might be in a register, so
how can you take the address of it? (or of parts of it).
Indeed: the return value is a value, not an object, and in C, only
objects have addresses. (Well, OK, functions have addresses too.)
Not all objects have addresses -- "register int i; ... &i" is invalid
and requiers a diagnostic -- but values *definitely* do not have
addresses.
Reading Andrey Tarasevich's replies, it seems that C99 fixes
this problem by forcing the return value to be an lvalue if
you use operator* (or equivalently, operator[]) on it.
That is not quite right. Instead, C99 says that, given an array
value (which only exists in this special weird case in which a
function returns a struct that contains an array), you *can* use
indirection and/or pointer arithmetic -- i.e., the usual kinds of
subscripting -- to access an element of the array, but this element
is also a value and is as ephemeral as any other value, so that
the only thing you can do is copy the value to some other object.
In particular, you may not take its address.

(All of this implies that:

struct S { int a[3][4]; };
struct S f(void);
...
f().a[i] /* or even f().a[i][j] */

is probably a fruitful source of bugs in C99 compilers, because
we have multiple levels of "value arrays" going on.)
My question then: suppose we have in C99

struct t1 t = l_SYM_2B(&x).o[0];

does that mean that there must be an lvalue object created to be
the return-value (so that [0] can be applied to its .o),
and then that object is copied to t ? ie. there is a wasted
copy?


Not necessarily. The details are up to the implementation. But
this is just where all those details can get hairy and sticky and
otherwise unpleasant; and indeed, this is the reason for much of
the complexity in C++, with its copy constructors and rules about
lifetimes of temporary objects. C has, so far, avoided having to
encode such rules in the language itself, leaving the details up
to the implementor, by making sure that you are not allowed to
"grab hold" of the temporary object-or-value-or-whatever-it-is.
(In C++, some things are passed by reference, so: is that a copy
of the temporary, or the original?, and so on.)

(A typical C implementation will have an entire temporary
"struct v1", and compile:

struct T1 t = l_SYM_2B(&x).o[0];

as if it read:

struct v1 temp;
struct T1 t;
l_SYM_2B(&temp, &x);
t = temp.o[0];

with the lifetime of the "temp" variable local to the entire
function, or a block created just to enclose the assignment to "t".
In this particular case, since the array o has size 1 and is the
only member, so that sizeof(temp)==sizeof(o), it is possible that
the optimization phase of the compiler will then discard the
temporary "temp" and rewrite the call as:

l_SYM_2B((struct v1 *)&t1, &x);

Of course, the very existence of the secret first argument is
compiler-specific as well, and if the struct fits in a register,
that argument may be eliminated in favor of returning the struct
in that register and just using the return-value-register to hold
the variable "t"! This is the best possible case, and I suspect
relatively few compilers will achieve it.)
--
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.
Nov 14 '05 #25

P: n/a
Chris Torek wrote:
When we apply The Rule in typical C code, we find that arrays never
survive to the "value" stage: arrays are only ever objects, and
when you attempt to do anything with their value, you get instead
a pointer value, stripping off the "array N of" part.


A string literal is not converted to a pointer,
when it is used to initialize an array.

char array[] = "string literal";

--
pete
Nov 14 '05 #26

P: n/a
>Chris Torek wrote:
When we apply The Rule in typical C code, we find that arrays never
survive to the "value" stage: arrays are only ever objects, and
when you attempt to do anything with their value, you get instead
a pointer value, stripping off the "array N of" part.

In article <41***********@mindspring.com>
pete <pf*****@mindspring.com> wrote:A string literal is not converted to a pointer,
when it is used to initialize an array.

char array[] = "string literal";


This is true; but note that in this case the string literal never
becomes an anonymous array in the first place. *Other* occurrences
of string literals *do* produce an array object, and then that object
undergoes The Rule.
--
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.
Nov 14 '05 #27

P: n/a
On Tue, 11 Jan 2005 16:15:32 +0000, Chris Torek wrote:
Chris Torek wrote:
When we apply The Rule in typical C code, we find that arrays never
survive to the "value" stage: arrays are only ever objects, and
when you attempt to do anything with their value, you get instead
a pointer value, stripping off the "array N of" part.


In article <41***********@mindspring.com>
pete <pf*****@mindspring.com> wrote:
A string literal is not converted to a pointer,
when it is used to initialize an array.

char array[] = "string literal";


This is true; but note that in this case the string literal never
becomes an anonymous array in the first place.


Strictly it does, according to C99 6.4.5 any instance of a string-literal
in translation phase 7 causes an array of static storage duration to be
initialised. It is just that in the case above that array cannot be
otherwise accessed by the program which provides an opportunity for an
as-if optimisation. But it does exist in the abstract machine. And you get
the feeling that there's something behind the scenes when you execute an
initialiser for an automatic array repeatedly.

Lawrence
Nov 14 '05 #28

This discussion thread is closed

Replies have been disabled for this discussion.