469,330 Members | 1,375 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,330 developers. It's quick & easy.

Returning a struct from a function - strange behavior

Hello everyone,

Please take a look at the following code:

#include <stdio.h>

typedef struct person {
char name[40];
int age;
} Person;

static Person make_person(void);

int main(void) {
printf("%s\n", make_person().name);

return 0;
}

static Person make_person(void) {
static Person p = { "alexander", 18 };

return p;
}

The above small program when compiled without the -std=c99 option
(using gcc 4.2.3) gives me a warning:
"warning: format ‘%s’ expects type ‘char *’, but argument 2 has type
‘char[40]’"
and also fails with a segmentation fault when executed.

If I replace the line printf("%s\n", make_person().name); with
printf("%s\n", &make_person().name[0]); everything works as expected.

Why does this happen? Isn't make_person().name a pointer to the
array's first element?

Someone replied to this (in the gcc bugzilla), I am quoting the
answer:

"make_person().name is a non-lvalue array, so it only decays to a
pointer
for C99, not for C90. If you use -std=c99/-std=gnu99 then the
program
works.

The program does not, however, have defined behavior for C99, only
for
C1x. In C99 the lifetime of the array ends at the next sequence
point,
before the call to printf. In C1x it instead ends at the end of the
evaluation of the containing full expression, which is the call to
printf.

I do not believe any changes to GCC are needed to implement this
particular C1x requirement, since GCC discards information about
variables
lifetimes smaller than a function for gimplification and tree
optimizations that may change those lifetimes, so it will in practice
treat the lifetime as being anywhere it cannot show the temporary not
to
be live."

I can't understand why make_person().name is not an lvalue array and
only decays to a pointer for C99. Can someone please explain this?

Also what does this guy mean with the line "In C99 the lifetime of the
array ends at the next sequence point,
before the call to printf"? A function call is a sequence point?

I am having a hard time understanding this one, any help appreciated
Thanks for your time

PS. I tried the lcc compiler which compiled the code without warnings/
errors
Oct 7 '08
160 5087
CBFalconer wrote:
jacob navia wrote:
>CBFalconer wrote:
>>I doubt it will always work. For example:

typedef struct foo {char a[20]}; foo;
char ca, cb;
foo getfoo(void);
...
printf("'%c' \"%s\" '%c'\n", ca, &(getfoo().a), cb);

Think about why I picked this test.
When I correct your code and write this:

typedef struct foo {char a[20];} foo;
char ca, cb;
foo getfoo(void) { foo f; f.a[0] = 'a'; f.a[1] = 0; return f; }
int main(void) {
printf("'%c' \"%s\" '%c'\n", ca, &(getfoo().a), cb);
}

I obtain
' ' "a" ' '

Why?

Because lcc-win (as lcc) creates always a temporary object
when a function returns a structure. lcc-win passes a hidden
first argument to the function that contains a pointer to the
temporary variable created in the calling function.
Well done, you're on the way to c C++ compiler!
Maybe in your system you can handle it for structures. But what if
you wanted the address of one of those characters? i.e.:

printf("%p \%s\" %p\n", (void*)&ca, &(getfoo().a),
(void*)&cb);
You repeated the same typo \%s\" should be \"%s\".
That n1336 proposal would require that to work. Ugh.
Why should that be any different?

--
Ian Collins.
Oct 9 '08 #101
CBFalconer <cb********@yahoo.comwrites:
ja*********@verizon.net wrote:
>CBFalconer wrote:
...
>>This discussion started a few days ago when I mentioned that I
didn't think that provision would make it into the next C standard
(so far it only exists in a discussion draft) because it would
force too many inefficiencies. I have been defending that opinion
since then. Successfully, IMO.

I don't think so. As far as I can tell, nobody has even conceded the
existence of those inefficiencies, much less agreed that they are
problematic.

What's so inefficient about storing a copy of a struct in a
temporarily reserved piece of memory? That's often what happens anyway
when a function returns a struct type, for completely unrelated
reasons. The only ineffiiciency you've been able to identify applies
only to return values small enough to fit in a single register; a case
highly unlikely to be relevant to most structs or unions containing
arrays. I suppose a struct value containing an array of 8 chars might
be stored in a 64 bit register, but it's not the most obvious
implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur inside
of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in.
I don't follow.

For concreteness, one implementation I'm familiar with is amd64, where
structs of 8 bytes or less are returned in a register, structs of 8 to
16 bytes are returned in two registers, and larger structs are copied
into a space which is passed (invisibly) by the caller. This
convention is fixed by an ABI, which a C1x compiler would likely
conform to.

The draft standard requires a temporary object to exist, but as far as
I can tell there's no need for it to be stored in memory unless
something actually looks for it there. If we have

struct foo { long a; long b; }; /* long is 8 bytes on amd64 */
struct foo blah(void);
void qux(long);
void argle(long *);

then doing

qux(blah().a)

would not require the return value of blah() to be stored in memory at
any time. The compiler could simply move the value of blah().a from
the register where it was returned into the appropriate register to
pass it to qux(). It's the same as if you have an auto variable of
type long; unless you take its address (or perhaps declare it
volatile), the compiler is free to keep it in a register, or even
optimize it completely out of existence.

Now it also seems that under the draft standard, we can do

argle(&blah().a)

and argle will be able to dereference its argument. In this case you
are correct that some storage would be needed. But seeing as under
C99 this code apparently results in undefined behavior (as the
lifetime of the object containing the returned value from blah() isn't
long enough), you can't really say it's less efficient.

If you have something else in mind, could you give an example of a
piece of code where a compiler conforming to the draft standard would
be required to do something less efficient than a C99 compiler?
Also consider the interactions.
What interactions are you referring to?
Oct 9 '08 #102
Keith Thompson wrote:
CBFalconer <cb********@yahoo.comwrites:
>Keith Thompson wrote:
[...]
>>Here's what n1336 6.2.4p7 says (I think it's already been quoted in
this thread):

Yes, it has. And it has also been pointed out that n1336 is a very
preliminary draft for a future C1x standard. My comments have been
to the effect that I don't think this particular proposal will
live, and why not.

And the point of my reply was to refute your arguments. You're
claiming that the proposal "will involve too many ugly
inefficiencies". You snipped the paragraph in which I explained why
this is not the case:

| The proposed change in n1336 applies *only* to a structure or union
| that contains (directly or indirectly) an array member. In the
| majority of such cases, the struct or union is already too big to fit
| in a register. For any other function results, the returned value is
| not stored in an object, either implicitly or explicitly (though of
| course the compiler is still free to do the equivalent of storing it
| in an object internally).
|
| "&strlen(thing)" is still a constraint violation.
Certainly (the violation).

Why do you want to treat some returned values in one manner, and
others in another manner? C has enough silly complexities, and
doesn't need another one. IMO. The simplest thing is to insist on
storing that value somewhere, after which you are free to play with
it. That can be a universal rule.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #103
Martien Verbruggen wrote:
CBFalconer <cb********@yahoo.comwrote:
.... snip ...
>
>But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

The storage of which small values? Which interactions? Could you
be a bit more verbose and maybe try to explain what you mean?
I'm not a good teacher. The point is that making all function
returned values addressable requires storing them somewhere. This
is not normally done, since values are (usually) returned in
registers, and if not used the register is free. The other
possibility is to store it and let the optimizer take it out, which
seems inefficient and error prone to me.

As far as interactions are concerned, what if you want to pass two
or more functional results to another routine? Does the second one
overwrite the first one? If so how do you make it work. If not,
you have a non-minor problem in allocating and freeing storage.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #104
Keith Thompson wrote:
CBFalconer <cb********@yahoo.comwrites:
>jacob navia wrote:
>>CBFalconer wrote:

I doubt it will always work. For example:

typedef struct foo {char a[20]}; foo;
char ca, cb;
foo getfoo(void);
...
printf("'%c' \"%s\" '%c'\n", ca, &(getfoo().a), cb);

Think about why I picked this test.

When I correct your code and write this:

typedef struct foo {char a[20];} foo;
char ca, cb;
foo getfoo(void) { foo f; f.a[0] = 'a'; f.a[1] = 0; return f; }
int main(void) {
printf("'%c' \"%s\" '%c'\n", ca, &(getfoo().a), cb);
}

I obtain
' ' "a" ' '

Why?

Because lcc-win (as lcc) creates always a temporary object
when a function returns a structure. lcc-win passes a hidden
first argument to the function that contains a pointer to the
temporary variable created in the calling function.

Maybe in your system you can handle it for structures. But what if
you wanted the address of one of those characters? i.e.:

printf("%p \%s\" %p\n", (void*)&ca, &(getfoo().a),
(void*)&cb);

That n1336 proposal would require that to work. Ugh.

I don't see the problem. The n1336 proposal simply requires the
creation of a temporary object of type foo, which must survive at
least until the end of the statement. In many implementations,
apparently including lcc-win, that temporary object is already created
as part of the protocol for returning a structure value.
I guess I should have made the ca and cb items functional results.
The point is horrible complexity and time wasted executing it. C
is basically simple - keep it that way.

I see lots of problems. I am almost certainly not making them
plain.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #105
Keith Thompson wrote:
CBFalconer <cb********@yahoo.comwrites:
>Keith Thompson wrote:
[...]
>>Here's what n1336 6.2.4p7 says (I think it's already been quoted in
this thread):
Yes, it has. And it has also been pointed out that n1336 is a very
preliminary draft for a future C1x standard. My comments have been
to the effect that I don't think this particular proposal will
live, and why not.

And the point of my reply was to refute your arguments. You're
claiming that the proposal "will involve too many ugly
inefficiencies". You snipped the paragraph in which I explained why
this is not the case:

| The proposed change in n1336 applies *only* to a structure or union
| that contains (directly or indirectly) an array member. In the
| majority of such cases, the struct or union is already too big to fit
| in a register. For any other function results, the returned value is
| not stored in an object, either implicitly or explicitly (though of
| course the compiler is still free to do the equivalent of storing it
| in an object internally).
|
| "&strlen(thing)" is still a constraint violation.
Even if it were broader, there isn't any need for added inefficiencies
as Jacob has explained. It hasn't been a problem for C++ compilers.

--
Ian Collins.
Oct 9 '08 #106
On Oct 8, 9:41 am, Keith Thompson <ks...@mib.orgwrote:
In C89/C90, the implicit conversion of an expression of array type to
a pointer to the array object's first element occurs only
when the array expression is an lvalue. Quoting the C90 standard:

Except when it is the operand of the sizeof operator or 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 array object and is not an lvalue.
The quote from C89 draft in Peter Nilsson's first post in this thread
is slightly different from this. I prefer the C89 version :) Quoted
again:

"Except when it is the operand of the sizeof
operator or 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
member of the array object and is not an lvalue.
Your array expression "make_person().name" is not an lvalue, so the
conversion doesn't occur. It's unclear what happens next. I *think*
Thank you, Keith. I read your first post again, and it helps me
understand this language feature a lot.

The expression make_person() is a rvalue type of struct Person.
make_person().name also doesn't occur in programmer declared memory,
so it's not a lvalue and remains a array.
you're passing the array by value to printf, which normally isn't
possible; since printf is expecting a char* due to the "%s" format,
the behavior is undefined.
Is the UB triggered by the mismatch of %s and make_person().name or by
accessing make_person().name after the sequence point of evaluating
this argument in the printf call? (But does such an access occur in
that single printf call in the original post?)
Oct 9 '08 #107
On Oct 9, 1:03*pm, "lovecreatesbea...@gmail.c0m"
<lovecreatesbea...@gmail.comwrote:
On Oct 8, 9:41 am, Keith Thompson <ks...@mib.orgwrote:
In C89/C90, the implicit conversion of an expression of array type to
a pointer to the array object's first element occurs only
when the array expression is an lvalue. *Quoting the C90 standard:
* * Except when it is the operand of the sizeof operator or the unary
* * & operator, or is a character string literal used to initializean
* * 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 array object and is not an lvalue.

The quote from C89 draft in Peter Nilsson's first post in this thread
is slightly different from this. I prefer the C89 version :) *Quoted
again:

* * * * "Except when it is the operand of the sizeof
* * * * operator or 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
* * * * member of the array object and is not an lvalue.
Sorry, they are the same. My bad.
Oct 9 '08 #108
DiAvOl <di****@freemail.grwrote:
I think that the last 20-30 posts (or more) has nothing to do with the
subject of this thread, maybe creating a new subject for lcc-win32 and
post there would be a better idea
No, it bloody wouldn't. Thanks to jacob, we have too many of those
already. In fact, I would not be surprised if he himself created a new
thread just to "make his point".
Just a thought
I've always found that the most shallow remark to end a post with.

Richard
Oct 9 '08 #109
vi******@gmail.com wrote:
On Oct 8, 8:53 pm, jameskuy...@verizon.net wrote:
jacob navia wrote:

[ about jacob claiming conformance ]
Nonetheless, a paying customer who actually needs C90 conformance
could sue you for misrepresentation if you claimed it, and because of
the absence of these mandatory diagnostics, such a customer would have
a valid case.
I'm thinking in terms of American law; I'm not sure if this would be
case in France. The complaint would still be valid, but I don't know
how easy it would be to file a lawsuit based upon it.

I don't think such case would have any luck. Jacob could just claim
it's a bug he wasn't aware of.
Not any more, he couldn't. He is very well aware of it; he just doesn't
give a damn.

Richard
Oct 9 '08 #110
CBFalconer <cb********@yahoo.comwrites:
Martien Verbruggen wrote:
>CBFalconer <cb********@yahoo.comwrote:
... snip ...
>>
>>But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

The storage of which small values? Which interactions? Could you
be a bit more verbose and maybe try to explain what you mean?

I'm not a good teacher. The point is that making all function
returned values addressable requires storing them somewhere.
[...]

Yes, it would, if that were what's being proposed. It isn't. The
vast majority of functions are not affected in any way by the proposed
change.

The proposed change in n1336 *only* affects structs and unions with
array members. The language as it's currently defined, by either C90
or C99, has a glitch in this case: it's possible to have a non-lvalue
expression of array type, which causes serious problems for the usual
implicit array-to-pointer conversion. The added paragraph in n1336
corrects this problem, and it does so with, as far as I can tell,
minimal cost.

With the possible exception of the code in the article that started
this discussion, or code written specifically to illustrate this
issue, I don't think I've ever seen any C code that would be affected.

You say it would cause "too many ugly inefficiencies". I believe
you're wrong. If you want to convince anyone that you're right,
you'll need to offer specific examples.

And what's your alternative? If I write a function that returns a
struct that contains an array, and I refer to the array member of the
result of a call to that function, what should happen? Constraint
error? Undefined behavior? Mandatory nasal demons?

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Oct 9 '08 #111
"lo***************@gmail.c0m" <lo***************@gmail.comwrites:
On Oct 9, 1:03*pm, "lovecreatesbea...@gmail.c0m"
<lovecreatesbea...@gmail.comwrote:
>On Oct 8, 9:41 am, Keith Thompson <ks...@mib.orgwrote:
In C89/C90, the implicit conversion of an expression of array type to
a pointer to the array object's first element occurs only
when the array expression is an lvalue. *Quoting the C90 standard:
[snip]
>The quote from C89 draft in Peter Nilsson's first post in this thread
is slightly different from this. I prefer the C89 version :) *Quoted
again:
[snip]
>
Sorry, they are the same. My bad.
In fact, the ANSI C89 and ISO C90 standards are identical, apart from
some (non-normative) introductory material and different numbering of
the sections.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Oct 9 '08 #112
On 8 Oct, 19:21, vipps...@gmail.com wrote:
On Oct 8, 8:53 pm, jameskuy...@verizon.net wrote:
jacob navia wrote:

[ about jacob claiming conformance ]
Nonetheless, a paying customer who actually needs C90 conformance
could sue you for misrepresentation if you claimed it, and because of
the absence of these mandatory diagnostics, such a customer would have
a valid case.
I'm thinking in terms of American law; I'm not sure if this would be
case in France. The complaint would still be valid, but I don't know
how easy it would be to file a lawsuit based upon it.

I don't think such case would have any luck. Jacob could just claim
it's a bug he wasn't aware of. Else everyone would be able to sue any
buggy software and get money.
and hopefully the person sueing wouldn't read clc

--
Nick Keighley
Oct 9 '08 #113
On 8 Oct, 19:46, Antoninus Twink <nos...@nospam.invalidwrote:
On *8 Oct 2008 at 18:21, vipps...@gmail.com wrote:
On Oct 8, 8:53 pm, jameskuy...@verizon.net wrote:
Nonetheless, a paying customer who actually needs C90 conformance
could sue you for misrepresentation if you claimed it, and because of
the absence of these mandatory diagnostics, such a customer would have
a valid case.
I don't think such case would have any luck. Jacob could just claim
it's a bug he wasn't aware of.

Hardly necessary - the courts would probably be a lot less legalistic
then the average clc Heathfield-wannabe. The law has the notion of a
"reasonable person", which is a foreign concept to most clc'ers.
yes and there are terms like "Fir For Purpose" and "Of Merchantable
Quality". If someone could demonstrate they had suffered material
loss because the compiler wasn't fully conforming then I'd say
they had some sort of case. IANAL

--
Nick Keighley
Oct 9 '08 #114
On Oct 9, 3:17*pm, Keith Thompson <ks...@mib.orgwrote:
"lovecreatesbea...@gmail.c0m" <lovecreatesbea...@gmail.comwrites:
On Oct 9, 1:03*pm, "lovecreatesbea...@gmail.c0m"
<lovecreatesbea...@gmail.comwrote:
The quote from C89 draft in Peter Nilsson's first post in this thread
is slightly different from this. I prefer the C89 version :) *Quoted
again:
Sorry, they are the same. My bad.

In fact, the ANSI C89 and ISO C90 standards are identical, apart from
some (non-normative) introductory material and different numbering of
the sections.
Thanks. I misread one as C99. I should post less and just read great
articles of yours and other regulars here more often.
Oct 9 '08 #115
CBFalconer wrote, On 09/10/08 05:51:
Keith Thompson wrote:
>CBFalconer <cb********@yahoo.comwrites:
>>jacob navia wrote:
CBFalconer wrote:

I doubt it will always work. For example:
>
typedef struct foo {char a[20]}; foo;
char ca, cb;
foo getfoo(void);
...
printf("'%c' \"%s\" '%c'\n", ca, &(getfoo().a), cb);
>
Think about why I picked this test.
When I correct your code and write this:

typedef struct foo {char a[20];} foo;
char ca, cb;
foo getfoo(void) { foo f; f.a[0] = 'a'; f.a[1] = 0; return f; }
int main(void) {
printf("'%c' \"%s\" '%c'\n", ca, &(getfoo().a), cb);
}

I obtain
' ' "a" ' '

Why?

Because lcc-win (as lcc) creates always a temporary object
when a function returns a structure. lcc-win passes a hidden
first argument to the function that contains a pointer to the
temporary variable created in the calling function.
Maybe in your system you can handle it for structures. But what if
you wanted the address of one of those characters? i.e.:

printf("%p \%s\" %p\n", (void*)&ca, &(getfoo().a),
(void*)&cb);

That n1336 proposal would require that to work. Ugh.
I don't see the problem. The n1336 proposal simply requires the
creation of a temporary object of type foo, which must survive at
least until the end of the statement. In many implementations,
apparently including lcc-win, that temporary object is already created
as part of the protocol for returning a structure value.

I guess I should have made the ca and cb items functional results.
The point is horrible complexity and time wasted executing it.
As Keith just pointed out above the object is *already* created in most
implementations, so the extra complexity and time wasted is extra
complexity and time that was added way back in 1989 (or earlier) when
the ability to return structures/unions was added to the language.
C
is basically simple - keep it that way.
It is. Making one more thing defined instead of undefined makes it
simpler from the perspective of someone using the language.
I see lots of problems. I am almost certainly not making them
plain.
You don't seem to be making yourself clear. You don't seem to be
understanding what others are saying either.
--
Flash Gordon
If spamming me sent it to sm**@spam.causeway.com
If emailing me use my reply-to address
See the comp.lang.c Wiki hosted by me at http://clc-wiki.net/
Oct 9 '08 #116
Richard Bos said:
DiAvOl <di****@freemail.grwrote:
<snip>
>
>Just a thought

I've always found that the most shallow remark to end a post with.
It is precisely because of this "shallowness" that I sometimes use it to
end a hurriedly written article. It is no secret that my writing style is
a little on the didactic side, and that isn't something that's about to
change. I therefore find it useful, on occasion, to use phrases such as
"just a thought" (or "HTH", which is rather similar in spirit), just to
lighten things up a little.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Oct 9 '08 #117

"Nate Eldredge" <na**@vulcan.lanwrote in message
news:86************@vulcan.lan...
CBFalconer <cb********@yahoo.comwrites:
>But that provision would force the storage of all those small
values. That is where the inefficiency comes in.

I don't follow.

For concreteness, one implementation I'm familiar with is amd64, where
structs of 8 bytes or less are returned in a register, structs of 8 to
16 bytes are returned in two registers, and larger structs are copied
into a space which is passed (invisibly) by the caller. This
convention is fixed by an ABI, which a C1x compiler would likely
conform to.

The draft standard requires a temporary object to exist, but as far as
I can tell there's no need for it to be stored in memory unless
something actually looks for it there. If we have

struct foo { long a; long b; }; /* long is 8 bytes on amd64 */
struct foo blah(void);
void qux(long);
void argle(long *);

then doing

qux(blah().a)

would not require the return value of blah() to be stored in memory at
any time. The compiler could simply move the value of blah().a from
the register where it was returned into the appropriate register to
pass it to qux(). It's the same as if you have an auto variable of
type long; unless you take its address (or perhaps declare it
volatile), the compiler is free to keep it in a register, or even
optimize it completely out of existence.
I remember writing a little compiler which could pass and return structs
(and in this case arrays).

Small structs were simply returned in registers as you said, and larger ones
on the stack (no heap storage was used).

However, to access fields of a struct returned from a function (or to index
such an array), my compiler required it to be an l-value.

But it considered any sort of value returned from a function to be a
temporary value (or transient, as I called it), even if it happened to be a
struct which happened to exist in stack memory, so it couldn't be an
l-value. There are too many issues otherwise.

So it seems to me quite reasonable for C to allow structs to be returned but
to disallow field accesses (or indexing of embedded arrays), even if some
hairy compilers try to implement this.

And yes in some cases it does sound inefficient, not very C-like at all:
imagine a function constructing a 1000-element array, embedding it in a
struct, and returning the entire struct, only for the ungrateful caller to
merely select one element of it..

If the caller stored the struct locally, one might hope it would then be
able to use several other elements without re-calling the function.

--
Bartc

Oct 9 '08 #118
"Bartc" <bc@freeuk.comwrites:
I remember writing a little compiler which could pass and return structs
(and in this case arrays).

Small structs were simply returned in registers as you said, and larger ones
on the stack (no heap storage was used).

However, to access fields of a struct returned from a function (or to index
such an array), my compiler required it to be an l-value.

But it considered any sort of value returned from a function to be a
temporary value (or transient, as I called it), even if it happened to be a
struct which happened to exist in stack memory, so it couldn't be an
l-value. There are too many issues otherwise.

So it seems to me quite reasonable for C to allow structs to be returned but
to disallow field accesses (or indexing of embedded arrays), even if some
hairy compilers try to implement this.
I agree it would have been reasonable 20 years ago. Indeed, K&R I
didn't allow structs to be passed or returned by value at all, and
arguably things would be better if it had been left that way. But we
are now stuck with it as part of the language, field access and all.
The present discussion is over whether the new language in the draft
standard makes matters worse. In my opinion, it doesn't hurt
efficiency for anything presently allowed, and the new things it
allows make it more consistent.
And yes in some cases it does sound inefficient, not very C-like at all:
imagine a function constructing a 1000-element array, embedding it in a
struct, and returning the entire struct, only for the ungrateful
caller to merely select one element of it..

If the caller stored the struct locally, one might hope it would then be
able to use several other elements without re-calling the function.
That's up to the caller, of course. But I know what you mean:
anything to do with structs beyond K&R I (apply the & or . operators)
seems non-C-like to me. I have a subconscious feeling when dealing
with C that each line should compile into a small number of
instructions, approximately proportional to the number of operators.
The notion that a single assignment statement can copy a huge struct
and take thousands of cycles bothers me somehow. You're supposed to
have to write a loop for that! (Or call memcpy, which is morally the
same thing.) And if you want to pass structs to and from functions,
you pass them by reference.
Oct 9 '08 #119
jacob navia wrote:
DiAvOl wrote:
>I think that the last 20-30 posts (or more) has nothing to do with the
subject of this thread, maybe creating a new subject for lcc-win32 and
post there would be a better idea

Just a thought

When I said that lcc-win compiles and executes that
code correctly they could not stand that.

Denigrating lcc-win is the only thing they all agree with.

Why?

It is the only compiler that is not a C++ compiler that
happens to compile C.
While compilers that handle both languages have become commonplace,
they're not universal. For instance, the MIPSpro IRIX C compiler that I
use on the SGI machines at work isn't a C++ compiler, either. The C++
compiler is a separate program called "CC" rather than "cc".

I also find it odd that you present this as if it were a significant
advantage. I personally need both a C compiler and a C++ compiler.
However, even if all I really needed was a C compiler, I don't see it as
a significant disadvantage if the compiler also has other capabilities
that I don't happen to use. Not providing C++ support presumably makes
the compiler smaller, but disk space is too cheap nowadays for that to
be a significant issue. If not providing C++ support makes the compiler
cheaper, than that is an advantage - but in that case it's the low price
itself that is the advantage, not the lack of C++ support.
Oct 9 '08 #120
Nick Keighley wrote:
On 8 Oct, 19:46, Antoninus Twink <nos...@nospam.invalidwrote:
>On 8 Oct 2008 at 18:21, vipps...@gmail.com wrote:
>>On Oct 8, 8:53 pm, jameskuy...@verizon.net wrote:
Nonetheless, a paying customer who actually needs C90 conformance
could sue you for misrepresentation if you claimed it, and because of
the absence of these mandatory diagnostics, such a customer would have
a valid case.
I don't think such case would have any luck. Jacob could just claim
it's a bug he wasn't aware of.
Hardly necessary - the courts would probably be a lot less legalistic
then the average clc Heathfield-wannabe. The law has the notion of a
"reasonable person", which is a foreign concept to most clc'ers.

yes and there are terms like "Fir For Purpose" and "Of Merchantable
Quality". If someone could demonstrate they had suffered material
loss because the compiler wasn't fully conforming then I'd say
they had some sort of case. IANAL

--
Nick Keighley
Sure, sure.

MORON: Your Honor, the compiler doesn't conform to C89!

JUDGE: What was the problem?

MORON: I compiled a program containing comments written in another
standard and the compiler did NOT complain.

JUDGE: But the generated program executed OK?

MORON: Well... yes your Honor.

JUDEGE: Well, then there are no damages?

MORON: No, but I feel cheated because I expected those errors
from the compiler.

JUDGE: Comptent of the court. You are guilty of abusive procedure.
Pay US$ 1 to Mr Navia and a fine of 10.000 to the court!

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
Oct 9 '08 #121
CBFalconer wrote:
ja*********@verizon.net wrote:
....
>implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur inside
of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.
It doesn't apply to them, so how could it force them to be stored?
Oct 9 '08 #122
CBFalconer wrote:
vi******@gmail.com wrote:
>jameskuy...@verizon.net wrote:

[ about jacob claiming conformance ]
>>Nonetheless, a paying customer who actually needs C90 conformance
could sue you for misrepresentation if you claimed it, and because
of the absence of these mandatory diagnostics, such a customer
would have a valid case.

I'm thinking in terms of American law; I'm not sure if this would
be case in France. The complaint would still be valid, but I don't
know how easy it would be to file a lawsuit based upon it.
I don't think such case would have any luck. Jacob could just claim
it's a bug he wasn't aware of. Else everyone would be able to sue
any buggy software and get money.

Can't be a valid case. If it was, Microsoft would have gone broke
about 2 decades ago.
Well, the quality and quantity of Microsoft's lawyers has something to
do with that, as does the product disclaimer that is industry standard,
essentially absolving them of all responsibility for actually delivering
a usable product. If jacob follows that same industry practice, he would
probably be safe from a customer lawsuit.

However, I think he could still be charged with false advertising if he
claimed C90 conformance, depending upon how he advertises it. I'm pretty
sure that simply saying so in this newsgroup, as he has actually done,
doesn't qualify as advertising for this purpose.

The key difference between Microsoft and jacob is that it is a matter of
public record that jacob has been informed of this non-conformance, and
that it is deliberate on his part. Microsoft normally keeps tighter
control over it's public statements than he does. I would be surprised
if there's comparable publicly available evidence that any particular
Microsoft product deliberate fails to conform to a standard that it was
officially claimed to conform to. Not very surprised; just surprised.
Oct 9 '08 #123
In article <gc**********@aioe.org>, jacob navia <ja***@nospam.orgwrote:
>MORON: Your Honor, the compiler doesn't conform to C89!

JUDGE: What was the problem?

MORON: I compiled a program containing comments written in another
standard and the compiler did NOT complain.

JUDGE: But the generated program executed OK?

MORON: Well... yes your Honor.

JUDEGE: Well, then there are no damages?
Sure, there's no damage to someone who wants a program that doesn't
conform to C89 to work.

But that's not always the situation. Typically I want to compile
a program that is supposed to conform to C89 (at least in respect
of C89/C99 differences), and it's a bug that I need to correct if
it doesn't conform to C89. And that's because I want my program
to be portable to other C89 implementations.

So here's the alternative scenario:

Programmer: Your Honour, the compiler doesn't conform to C89!

Judge: What was the problem?

Programmer: I inadvertently used a // comment and a C99-style
compound literal, and the compiler didn't produce any
diagnostics, so when I sold my program I got
complaints from customers who couldn't compile it.
Some of them demanded their money back and bought
a competing product instead.

Now the programmer here is clearly unwise, since all compilers contain
bugs, and you can't rely on one compiler to detect all errors. But
that fact remains that such a compiler wouldn't conform to C89.

Aside: C is actually quite liberal in this respect. All it requires
is a diagnostic. XML on the other hand requires that an XML processor
MUST NOT continue normal processing after a well-formedness error.
This was prompted by the "browser wars" of the mid-1990, when each
vendor added their own extensions to HTML with the obvious
consequences for interoperability. The XML authors were determined
that this sort of thing should not happen with XML.

-- Richard
--
Please remember to mention me / in tapes you leave behind.
Oct 9 '08 #124
jacob navia wrote:
....
MORON: Your Honor, the compiler doesn't conform to C89!

JUDGE: What was the problem?

MORON: I compiled a program containing comments written in another
standard and the compiler did NOT complain.

JUDGE: But the generated program executed OK?

MORON: Well... yes your Honor.

JUDEGE: Well, then there are no damages?
Not until he delivered the code to his client, who's using a different
compiler which actually conforms to C90, and didn't accept the code.
Then there were damages.

Oct 9 '08 #125
Keith Thompson wrote:
>
.... snip ...
>
And what's your alternative? If I write a function that returns
a struct that contains an array, and I refer to the array member
of the result of a call to that function, what should happen?
Constraint error? Undefined behavior? Mandatory nasal demons?
Create a location that can hold the function result. Write:

foo result;
...
result = getfoo(params);
/* operate on foo.whatever */

No demons in sight.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #126
James Kuyper wrote:
CBFalconer wrote:
>ja*********@verizon.net wrote:
...
>>implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur
inside of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

It doesn't apply to them, so how could it force them to be stored?
Oh, so you want special handling, depending on the type of function
result?

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #127
CBFalconer <cb********@yahoo.comwrites:
Keith Thompson wrote:
>>
... snip ...
>>
And what's your alternative? If I write a function that returns
a struct that contains an array, and I refer to the array member
of the result of a call to that function, what should happen?
Constraint error? Undefined behavior? Mandatory nasal demons?

Create a location that can hold the function result. Write:

foo result;
...
result = getfoo(params);
/* operate on foo.whatever */

No demons in sight.
That does not answer Keith Thompson's question. What meaning would
give the code in question? In other words he was asking how would you
re-word the standard, not asking how would you re-write the code to
avoid the issue.

--
Ben.
Oct 9 '08 #128
Ben Bacarisse <be********@bsb.me.ukwrites:
CBFalconer <cb********@yahoo.comwrites:
>Keith Thompson wrote:
>>>
... snip ...
>>>
And what's your alternative? If I write a function that returns
a struct that contains an array, and I refer to the array member
of the result of a call to that function, what should happen?
Constraint error? Undefined behavior? Mandatory nasal demons?

Create a location that can hold the function result. Write:

foo result;
...
result = getfoo(params);
/* operate on foo.whatever */

No demons in sight.

That does not answer Keith Thompson's question. What meaning would
give the code in question? In other words he was asking how would you
re-word the standard, not asking how would you re-write the code to
avoid the issue.
Exactly.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Oct 9 '08 #129
CBFalconer <cb********@yahoo.comwrites:
James Kuyper wrote:
>CBFalconer wrote:
>>ja*********@verizon.net wrote:
...
>>>implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur
inside of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

It doesn't apply to them, so how could it force them to be stored?

Oh, so you want special handling, depending on the type of function
result?
Why not? I certainly don't want the compiler to do the same thing
when it calls

int rand(void);

as it does when it calls

struct huge foo(void);

I want it to know how to make the best use of my machine's
architecture and that almost certainly means doing "special handling
depending on the type of the function result".

--
Ben.
Oct 9 '08 #130
CBFalconer <cb********@yahoo.comwrites:
James Kuyper wrote:
>CBFalconer wrote:
>>ja*********@verizon.net wrote:
...
>>>implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur
inside of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

It doesn't apply to them, so how could it force them to be stored?

Oh, so you want special handling, depending on the type of function
result?
Why not?

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Oct 9 '08 #131
Keith Thompson <ks***@mib.orgwrites:
la************@siemens.com writes:
Keith Thompson <ks***@mib.orgwrote:
The phrase "the array object" implies that there must be an array
object somewhere. In the case we're considering (the return value of
a function that returns a struct with an array member), there is no
array object whose initial element we can point to. Is there other
(normative) wording that clarifies this?
Yes, in N1336. :-)

In C90, the section you quoted said that only lvalues with array type
are converted to pointers; C99 relaxed that to allow all array type
expressions to be converted. As you note, that leads to a bit of
cognitive dissonance since an object suddenly appears in the midst of a
value, which doesn't otherwise happen. Nonetheless, you can safely
assume that it's magically created out of the luminiferous aether as
required and mysteriously evaporates again at the next sequence point.
The committee was loathe to say anything more about such objects since
it opens a can of worms (e.g., what storage duration and lifetime they
have), but we bit the bullet for C1X.

[... C90 stuff ...]

I decline to believe that the phrase "the array object" in the C99
standard actually causes such an object to be created (or, more
precisely, imposes a requirement on implementers to arrange for such
an object to be created). In particular, I see no implied guarantee
that "the array object" will continue to exist until the next sequence
point. If the called function returns the value of an object of
struct type, then "the array object" could plausibly refer to the
array member of that object, which could be local to the function and
therefore nonexistent after the function returns.
An object is just a region of memory that can hold values.
The value returned by a function must be held somewhere;
since where ever that is can hold a value, it is an object.
The only question, as you point out, is what its lifetime is.

One interpretation (only slightly perverse) is that in C99
(and probably C90 as well) the lifetime for such an object
must be either static or automatic. (I don't mean to make
the argument, since it's not really a strong argument, but
I think it's worth noting.) The point of the language in
N1336 is not that the result is an object (because it already
must be), but to clarify what the lifetimes of such objects
are.
Oct 9 '08 #132
CBFalconer <cb********@yahoo.comwrites:
ja*********@verizon.net wrote:
CBFalconer wrote:
...
This discussion started a few days ago when I mentioned that I
didn't think that provision would make it into the next C standard
(so far it only exists in a discussion draft) because it would
force too many inefficiencies. I have been defending that opinion
since then. Successfully, IMO.
I don't think so. As far as I can tell, nobody has even conceded the
existence of those inefficiencies, much less agreed that they are
problematic.

What's so inefficient about storing a copy of a struct in a
temporarily reserved piece of memory? That's often what happens anyway
when a function returns a struct type, for completely unrelated
reasons. The only ineffiiciency you've been able to identify applies
only to return values small enough to fit in a single register; a case
highly unlikely to be relevant to most structs or unions containing
arrays. I suppose a struct value containing an array of 8 chars might
be stored in a 64 bit register, but it's not the most obvious
implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur inside
of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.
I think if you work through what's actually required, you'll
see there really is no extra burden. If f is a function
that takes a (struct s), and g is a function that returns
a (struct s), then

f( g() )

has the same storage requirements as

f( s )

presuming s is a variable of type (struct s). If we're going
to accept f(s) then it isn't any more costly to accept f(g()).
Oct 9 '08 #133
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
Keith Thompson <ks***@mib.orgwrites:
>la************@siemens.com writes:
Keith Thompson <ks***@mib.orgwrote:
The phrase "the array object" implies that there must be an array
object somewhere. In the case we're considering (the return value of
a function that returns a struct with an array member), there is no
array object whose initial element we can point to. Is there other
(normative) wording that clarifies this?

Yes, in N1336. :-)

In C90, the section you quoted said that only lvalues with array type
are converted to pointers; C99 relaxed that to allow all array type
expressions to be converted. As you note, that leads to a bit of
cognitive dissonance since an object suddenly appears in the midst of a
value, which doesn't otherwise happen. Nonetheless, you can safely
assume that it's magically created out of the luminiferous aether as
required and mysteriously evaporates again at the next sequence point.
The committee was loathe to say anything more about such objects since
it opens a can of worms (e.g., what storage duration and lifetime they
have), but we bit the bullet for C1X.

[... C90 stuff ...]

I decline to believe that the phrase "the array object" in the C99
standard actually causes such an object to be created (or, more
precisely, imposes a requirement on implementers to arrange for such
an object to be created). In particular, I see no implied guarantee
that "the array object" will continue to exist until the next sequence
point. If the called function returns the value of an object of
struct type, then "the array object" could plausibly refer to the
array member of that object, which could be local to the function and
therefore nonexistent after the function returns.

An object is just a region of memory that can hold values.
The value returned by a function must be held somewhere;
since where ever that is can hold a value, it is an object.
The only question, as you point out, is what its lifetime is.

One interpretation (only slightly perverse) is that in C99
(and probably C90 as well) the lifetime for such an object
must be either static or automatic. (I don't mean to make
the argument, since it's not really a strong argument, but
I think it's worth noting.) The point of the language in
N1336 is not that the result is an object (because it already
must be), but to clarify what the lifetimes of such objects
are.
I disagree.

C, or at least the C abstract machine, distinguishes strongly between
objects and values. An object may hold a value, but a value does not
necessarily require an object to hold it.

A value is the result of evaluating an expresssion. Given, for
example:

int y = 2 * x + 1;

the expression "2 * x" and "2 * x + 1" have values, but those values
don't have objects associated with them. (They might be stored in
memory, but that's an implementation detail.)

And in the following:

int func(void) { return 41; }
...
int x = func() + 1;

there is no object whose value is 41. A function result is just
another expression value. The value specified in a function's return
statement must be communicated somehow to the caller, so that it can
become the result of the function call expression, but again, there's
no implied object. Whether the value happens to be a simple scalar or
some huge structure makes no conceptual difference -- in most cases.

And it wouldn't make any difference in the case under discussion
(accessing an array member of struct value returned from a function)
except for C's (rather odd) rule that an expression of array type is
usually converted to a pointer value. (Note that this isn't a
conversion in the usual sense; an array value consists of the values
of the array elements, and there's no way to get a pointer value from
that other than my compiler magic.)

But since an attempt to refer to an array value implies, because of
The Rule, the existence of an array object, there has to be an object
in this one bizarre case.
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Oct 9 '08 #134
CBFalconer wrote:
James Kuyper wrote:
CBFalconer wrote:
ja*********@verizon.net wrote:
...
>implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur
inside of structs and unions.

But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.
It doesn't apply to them, so how could it force them to be stored?

Oh, so you want special handling, depending on the type of function
result?
Yes, I do want to retain the current state of affairs. Handling return
values of different types in different fashions is the norm, not the
exception. It is commonplace for functions that return small scalar
values to store the return value in a register; a strategy that is
just plain impossible for most structs that contain arrays. It is no
horrendous inefficiency to require a change in the handling of those
few array-containing structs that happen to be small enough to fit in
a register.
Oct 9 '08 #135
ja*********@verizon.net wrote:
CBFalconer wrote:
>James Kuyper wrote:
>>CBFalconer wrote:
ja*********@verizon.net wrote:
...
implementation. The change planned for the next release of the C
standard has an impact only lvalue arrays, which can only occur
inside of structs and unions.
But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.
It doesn't apply to them, so how could it force them to be stored?
Oh, so you want special handling, depending on the type of function
result?

Yes, I do want to retain the current state of affairs. Handling return
values of different types in different fashions is the norm, not the
exception. It is commonplace for functions that return small scalar
values to store the return value in a register; a strategy that is
just plain impossible for most structs that contain arrays. It is no
horrendous inefficiency to require a change in the handling of those
few array-containing structs that happen to be small enough to fit in
a register.
This could be more difficult than you imagine.

For instance under windows, the ABI specifies that structures
smaller or equal than intptr_t should be returned in a register
anyway. For instance

struct s { char a[sizeof(intptr_t)];};

will be returned in a register. It has an array of chars OK but
its whole size is <= EAX.

Sometimes, bigger quantities are returned in the EAX:EDX register
pair, with 64 bits worth of data.

In 64 bits the situation is even "worse" since structures with up to
128 bits can be returned in RDX:RAX. Lcc-win uses those for returning
a 128 bit integer, for instance.

Floating point data is returned in the FPU under windows 32 bits,
but returned in the XMM register 0 in 64 bit windows.

Since the register file has increased enormously in the last decade,
machines like the PC can return up an array of 32 double precision data
in the floating point register file. If memory serves, the Power PC
architecture has a 32 double precision data register file.

This is a can of worms since specifying how to return data for every
processor could be well an impossible task.

To carry on your idea, the standard should stay at a very high level
of specifications leaving the details to be filled by the
implementation.
--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
Oct 9 '08 #136
jacob navia wrote:
ja*********@verizon.net wrote:
....
Yes, I do want to retain the current state of affairs. Handling return
values of different types in different fashions is the norm, not the
exception. It is commonplace for functions that return small scalar
values to store the return value in a register; a strategy that is
just plain impossible for most structs that contain arrays. It is no
horrendous inefficiency to require a change in the handling of those
few array-containing structs that happen to be small enough to fit in
a register.

This could be more difficult than you imagine.
Yes, I'm sure it can be. However, you yourself have already done it,
so it must be doable; and you have only a small staff, so there's an
upper limit to how difficult it must have been to do it.

More to the point, C++ already implements a more complicated version
of the same concept, so there's a large body of relevant practical
experience that can be drawn on. The C concept is more limited in both
applicability and in the lifetime of the temporary object, so it
should be, if anything, substantially easier to implement than the C++
concept.
To carry on your idea, the standard should stay at a very high level
of specifications leaving the details to be filled by the
implementation.
I agree - the proposed change only defines the behavior of a given
code construct; it's entirely up to the implementor to decide how to
make that behavior happen.
Oct 9 '08 #137
Ben Bacarisse wrote:
CBFalconer <cb********@yahoo.comwrites:
>Keith Thompson wrote:
>>>
... snip ...
>>>
And what's your alternative? If I write a function that returns
a struct that contains an array, and I refer to the array member
of the result of a call to that function, what should happen?
Constraint error? Undefined behavior? Mandatory nasal demons?

Create a location that can hold the function result. Write:

foo result;
...
result = getfoo(params);
/* operate on foo.whatever */

No demons in sight.

That does not answer Keith Thompson's question. What meaning would
give the code in question? In other words he was asking how would
you re-word the standard, not asking how would you re-write the
code to avoid the issue.
Return to the specification of C99. Maybe add a comment somewhere
pointing out that a function result has to be used in whole.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #138
Ben Bacarisse wrote:
CBFalconer <cb********@yahoo.comwrites:
.... snip ...
>
>Oh, so you want special handling, depending on the type of
function result?

Why not? I certainly don't want the compiler to do the same
thing when it calls

int rand(void);

as it does when it calls

struct huge foo(void);

I want it to know how to make the best use of my machine's
architecture and that almost certainly means doing "special
handling depending on the type of the function result".
Well, I think there is no melding of our attitude to compilers and
how and what they should translate. Note that I did not object to
things done during optimization. Don't forget that this whole
argument is about fooling around with sub-components of that
function result.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #139
ja*********@verizon.net wrote:
jacob navia wrote:
>ja*********@verizon.net wrote:
...
>>Yes, I do want to retain the current state of affairs. Handling
return values of different types in different fashions is the
norm, not the exception. It is commonplace for functions that
return small scalar values to store the return value in a
register; a strategy that is just plain impossible for most
structs that contain arrays. It is no horrendous inefficiency
to require a change in the handling of those few
array-containing structs that happen to be small enough to fit
in a register.

This could be more difficult than you imagine.

Yes, I'm sure it can be. However, you yourself have already done,
it so it must be doable; and you have only a small staff, so
there's an upper limit to how difficult it must have been to do it.
No he hasn't. What he has done is implement it on one type of CPU
running one OS. This is much different from being able to do it on
any machine anywhere. It's still an accomplishment, but it is not
definitive.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #140
Tim Rentsch wrote:
CBFalconer <cb********@yahoo.comwrites:
.... snip ...
>
>But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

I think if you work through what's actually required, you'll
see there really is no extra burden. If f is a function
that takes a (struct s), and g is a function that returns
a (struct s), then

f( g() )

has the same storage requirements as

f( s )

presuming s is a variable of type (struct s). If we're going
to accept f(s) then it isn't any more costly to accept f(g()).
But what if you have a function f that takes two (struct s)s as
parameters. You want to write:

f(g(1).a, g(2).b)

(remember that the limitation is against using sub-portions of
those returned structures). I maintain you should write:

gv1 = g(1); gv2 = g(2);
f(gv1.a, gv2.b);

and nobody is confused, including the compiler.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 9 '08 #141
CBFalconer <cb********@yahoo.comwrites:
Ben Bacarisse wrote:
>CBFalconer <cb********@yahoo.comwrites:
>>Keith Thompson wrote:

... snip ...

And what's your alternative? If I write a function that returns
a struct that contains an array, and I refer to the array member
of the result of a call to that function, what should happen?
Constraint error? Undefined behavior? Mandatory nasal demons?

Create a location that can hold the function result. Write:

foo result;
...
result = getfoo(params);
/* operate on foo.whatever */

No demons in sight.

That does not answer Keith Thompson's question. What meaning would
give the code in question? In other words he was asking how would
you re-word the standard, not asking how would you re-write the
code to avoid the issue.

Return to the specification of C99. Maybe add a comment somewhere
pointing out that a function result has to be used in whole.
Could we rephrase this as: "forbid the application of the . operator to
a struct value returned from a function" ?

This would break backwards compatibility, of course. Code like

struct foo { int a; int b; };
struct foo blah(void);
void bar(void) {
int x = blah().a;
/* ... */
}

is perfectly legal in C90 and C99, as far as I can tell, but would be
illegal under your proposed change. You're willing to go that far?

I also think it's somewhat un-C-like to force the programmer to use data
she doesn't care about. You can discard an entire return value without
a peep from the compiler, why shouldn't you be able to discard half of
it?
Oct 10 '08 #142
CBFalconer wrote:
ja*********@verizon.net wrote:
>jacob navia wrote:
>>ja*********@verizon.net wrote:
...
>>>Yes, I do want to retain the current state of affairs. Handling
return values of different types in different fashions is the
norm, not the exception. It is commonplace for functions that
return small scalar values to store the return value in a
register; a strategy that is just plain impossible for most
structs that contain arrays. It is no horrendous inefficiency
to require a change in the handling of those few
array-containing structs that happen to be small enough to fit
in a register.
This could be more difficult than you imagine.
Yes, I'm sure it can be. However, you yourself have already done,
it so it must be doable; and you have only a small staff, so
there's an upper limit to how difficult it must have been to do it.

No he hasn't. What he has done is implement it on one type of CPU
running one OS. This is much different from being able to do it on
any machine anywhere. It's still an accomplishment, but it is not
definitive.
The "it" I'm talking about involves just one platform at a time; there's
not need to tackle all possible machines at the same time. Leave
something for the next person to do! He did "it" for one machine, and
that's all I was talking about.

Oct 10 '08 #143
CBFalconer <cb********@yahoo.comwrites:
Tim Rentsch wrote:
>CBFalconer <cb********@yahoo.comwrites:
... snip ...
>>
>>But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.

I think if you work through what's actually required, you'll
see there really is no extra burden. If f is a function
that takes a (struct s), and g is a function that returns
a (struct s), then

f( g() )

has the same storage requirements as

f( s )

presuming s is a variable of type (struct s). If we're going
to accept f(s) then it isn't any more costly to accept f(g()).

But what if you have a function f that takes two (struct s)s as
parameters. You want to write:

f(g(1).a, g(2).b)

(remember that the limitation is against using sub-portions of
those returned structures). I maintain you should write:

gv1 = g(1); gv2 = g(2);
f(gv1.a, gv2.b);

and nobody is confused, including the compiler.
You're being a bit unclear on what the types of gv1, gv2, f(), and g()
are. Given the code you presented, I think that there have to be two
struct types. If f() takes two arguments of type struct s, the g must
return a result of type, let's say, struct t, which has two members of
type struct s, called a and b.

Adding these declarations, here's a complete program that's consistent
with your code snippet. It's a bit more vertically compressed than my
usual style. In practice, both structs could have more members.

#include <stdio.h>

struct s { int x; };

struct t { struct s a; struct s b; };

void f(struct s arg1, struct s arg2) {
printf("%d %d\n", arg1.x, arg2.x);
}

struct t g(int n) {
struct t result;
result.a.x = n;
result.b.x = n + 1;
return result;
}

int main(void) {
f(g(1).a, g(2).b);
return 0;
}

Are you saying that, *as a matter of programming style*, it would be
better to assign the values returned by g() to temporaries before
passing their members to f()? If so, I'm not necessarily going to
disagree. But it really has nothing to do with what's being
discussed. This code is perfectly valid and unambiguous, and has been
at least since C89. I'd be surprised if any compiler were "confused"
by it. There are no arrays (ignoring the format string), and no
function results or parts thereof used as lvalues.

Using a member of a struct value returned by a function is already
legal, and is not the problem we're discussing.

If you're suggesting that an implementation should be allowed to
reject or mis-handle this code, then you're suggesting a change that
would break existing C90 and C99 code. (I don't believe you really
mean that.)

This whole discussion isn't about programming style, it's about what
the language requires compilers to implement. And it's about a very
specific case, a function returning a struct or union that has an
array member, and a caller that refers to that array member within the
expression containing the call. If you want to make a relevant point,
you're going to have to use a relevant example -- and I'd appreciate
it if you'd provide any necessary declarations so we don't have to
reconstruct them.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Oct 10 '08 #144
On Wed, 08 Oct 2008 13:17:00 -0700, ja*********@verizon.net wrote:
jacob navia wrote:
>>
You do not give a dam about my work if it is not to denigrate it,

Note: that's 'damn', not 'dam'. That is NOT a criticism, my French is
far worse than your English; I just thought you should know. Swearing
like that looses a lot of it's intended impact when you misspell the
words.
My English is very likely not as good as yours, but ITYM "... that
/loses/ a lot ...".

- Anand
Oct 10 '08 #145
Anand Hariharan <zn********************@tznvy.pbzwrote:
On Wed, 08 Oct 2008 13:17:00 -0700, ja*********@verizon.net wrote:
jacob navia wrote:
>
You do not give a dam about my work if it is not to denigrate it,
Note: that's 'damn', not 'dam'. That is NOT a criticism, my French is
far worse than your English; I just thought you should know. Swearing
like that looses a lot of it's intended impact when you misspell the
words.

My English is very likely not as good as yours, but ITYM "... that
/loses/ a lot ...".
Skitt's Law: any usenet post correcting a spelling error shall itself
contain a spelling error. (And the corrollary: If no error occurs
inadvertently, it is incumbent on the corrector to provide it manually.)

Richard
Oct 10 '08 #146
Richard Bos wrote:
) Anand Hariharan <zn********************@tznvy.pbzwrote:
)
)On Wed, 08 Oct 2008 13:17:00 -0700, ja*********@verizon.net wrote:
)>
) jacob navia wrote:
)>
)You do not give a dam about my work if it is not to denigrate it,
)
) Note: that's 'damn', not 'dam'. That is NOT a criticism, my French is
) far worse than your English; I just thought you should know. Swearing
) like that looses a lot of it's intended impact when you misspell the
) words.
)>
)My English is very likely not as good as yours, but ITYM "... that
)/loses/ a lot ...".
)
) Skitt's Law: any usenet post correcting a spelling error shall itself
) contain a spelling error. (And the corrollary: If no error occurs
) inadvertently, it is incumbent on the corrector to provide it manually.)

Apostrophe! Apostrophe!

It's "... a lot of its intended impact ..." (it's is a contraction of it is).

And a case could be made for 'looses' being correct, although it would have
a slightly different meaning.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
Oct 10 '08 #147
On 10 Oct, 00:39, CBFalconer <cbfalco...@yahoo.comwrote:
jameskuy...@verizon.net wrote:
jacob navia wrote:
jameskuy...@verizon.net wrote:
>Yes, I do want to retain the current state of affairs. Handling
return values of different types in different fashions is the
norm, not the exception. It is commonplace for functions that
return small scalar values to store the return value in a
register; a strategy that is just plain impossible for most
structs that contain arrays. It is no horrendous inefficiency
to require a change in the handling of those few
array-containing structs that happen to be small enough to fit
in a register.
This could be more difficult than you imagine.
Yes, I'm sure it can be. However, you yourself have already done,
it so it must be doable; and you have only a small staff, so
there's an upper limit to how difficult it must have been to do it.

No he hasn't. *What he has done is implement it on one type of CPU
running one OS. *This is much different from being able to do it on
any machine anywhere. *It's still an accomplishment, but it is not
definitive.
Yes but it sounds generally applicable "small" arguments go in
registers "larger" values go in the memory used for arguments and
return values (the stack in other words).

When it comes to implementing compilers Jacob is probably
more knowledgable than you.

I think you need to explain why you consider the proposed
change to C1x to be ineffiecient. Perhaps with some sort
of virtual machine. You need to be more concrete.

--
Nick Keighley

Oct 10 '08 #148
CBFalconer <cb********@yahoo.comwrites:
Tim Rentsch wrote:
CBFalconer <cb********@yahoo.comwrites:
... snip ...
But that provision would force the storage of all those small
values. That is where the inefficiency comes in. Also consider
the interactions.
I think if you work through what's actually required, you'll
see there really is no extra burden. If f is a function
that takes a (struct s), and g is a function that returns
a (struct s), then

f( g() )

has the same storage requirements as

f( s )

presuming s is a variable of type (struct s). If we're going
to accept f(s) then it isn't any more costly to accept f(g()).

But what if you have a function f that takes two (struct s)s as
parameters. You want to write:

(remember that the limitation is against using sub-portions of
those returned structures). I maintain you should write:

gv1 = g(1); gv2 = g(2);
f(gv1.a, gv2.b);

and nobody is confused, including the compiler.
My point was about the amount of storage required. The
expression

f(g(1).a, g(2).b)

won't take any more storage than

gv1 = g(1); gv2 = g(2);
f(gv1.a, gv2.b);

and if fact there's a good chance that the shorter form will have
a smaller memory footprint than the longer form.

In terms of style - if you find 'f(g(1).a, g(2).b)' confusing
then feel free to use the longer form. I don't find it confusing
and compilers won't either, since the way space is assigned in
the stack frame is just the same as it is non-struct parameters.

Oct 10 '08 #149
Keith Thompson <ks***@mib.orgwrites:
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
Keith Thompson <ks***@mib.orgwrites:
la************@siemens.com writes:
Keith Thompson <ks***@mib.orgwrote:
The phrase "the array object" implies that there must be an array
object somewhere. In the case we're considering (the return value of
a function that returns a struct with an array member), there is no
array object whose initial element we can point to. Is there other
(normative) wording that clarifies this?

Yes, in N1336. :-)

In C90, the section you quoted said that only lvalues with array type
are converted to pointers; C99 relaxed that to allow all array type
expressions to be converted. As you note, that leads to a bit of
cognitive dissonance since an object suddenly appears in the midst of a
value, which doesn't otherwise happen. Nonetheless, you can safely
assume that it's magically created out of the luminiferous aether as
required and mysteriously evaporates again at the next sequence point.
The committee was loathe to say anything more about such objects since
it opens a can of worms (e.g., what storage duration and lifetime they
have), but we bit the bullet for C1X.

[... C90 stuff ...]

I decline to believe that the phrase "the array object" in the C99
standard actually causes such an object to be created (or, more
precisely, imposes a requirement on implementers to arrange for such
an object to be created). In particular, I see no implied guarantee
that "the array object" will continue to exist until the next sequence
point. If the called function returns the value of an object of
struct type, then "the array object" could plausibly refer to the
array member of that object, which could be local to the function and
therefore nonexistent after the function returns.
An object is just a region of memory that can hold values.
The value returned by a function must be held somewhere;
since where ever that is can hold a value, it is an object.
The only question, as you point out, is what its lifetime is.

One interpretation (only slightly perverse) is that in C99
(and probably C90 as well) the lifetime for such an object
must be either static or automatic. (I don't mean to make
the argument, since it's not really a strong argument, but
I think it's worth noting.) The point of the language in
N1336 is not that the result is an object (because it already
must be), but to clarify what the lifetimes of such objects
are.

I disagree.

C, or at least the C abstract machine, distinguishes strongly between
objects and values. An object may hold a value, but a value does not
necessarily require an object to hold it.

A value is the result of evaluating an expresssion. Given, for
example:

int y = 2 * x + 1;

the expression "2 * x" and "2 * x + 1" have values, but those values
don't have objects associated with them. (They might be stored in
memory, but that's an implementation detail.)

And in the following:

int func(void) { return 41; }
...
int x = func() + 1;

there is no object whose value is 41. A function result is just
another expression value. The value specified in a function's return
statement must be communicated somehow to the caller, so that it can
become the result of the function call expression, but again, there's
no implied object. Whether the value happens to be a simple scalar or
some huge structure makes no conceptual difference -- in most cases.
You keep saying there is no object. However, the value must
be stored in the execution environment /somewhere/, and where ever
that storage is fits the definition of object (in 3.14), since it
is storing data, and can represent values.

At some level it doesn't matter (in most cases, as you say) whether
there is an object or not, since either assumption doesn't affect
anything. But I don't see anything wrong or inconsistent with
an interpretation that puts all expression values in objects (of
mostly unspecified lifetimes).

And it wouldn't make any difference in the case under discussion
(accessing an array member of struct value returned from a function)
except for C's (rather odd) rule that an expression of array type is
usually converted to a pointer value. (Note that this isn't a
conversion in the usual sense; an array value consists of the values
of the array elements, and there's no way to get a pointer value from
that other than my compiler magic.)

But since an attempt to refer to an array value implies, because of
The Rule, the existence of an array object, there has to be an object
in this one bizarre case.
I find it simpler to think of all values as residing in objects,
of various lifetimes. Since that interpretation is consistent
with the text of the Standard, it seems more appropriate, at
least to me. The question of whether or not values reside in
objects isn't really an important one, since it's a distinction
without a difference.
<editorial>

It would be nice if the Standard were more consistent in its
usage of the term "object". For example, 6.3.2.1 p 3 uses
the phrase "if the array object has register storage class."
Objects don't have a storage class; variables do.

</editorial>
Oct 10 '08 #150

This discussion thread is closed

Replies have been disabled for this discussion.

By using this site, you agree to our Privacy Policy and Terms of Use.