Returning a struct from a function - strange behavior

C

CBFalconer

jacob said:
Well, if I passed an array of characters finished by a zero
byte to printf("%s") the normal behavior is to print a character
string. What else?

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.
My compiler conforms to C99. It will compile C89 code without any
problems and nit conforms fully to the C89 standard also. The only
"problem" is that it does NOT emit diagnostics when it sees a C99
syntax. It will correctly compile C99 constructs without emitting
any diagnostic.

You have mentioned various C99 things at which it fails.
Non-emission of non-required diagnostics is not a fault. If it was
correct, or even if it was documented properly, you wouldn't hear
the muttering.
 
C

CBFalconer

James said:
.... snip ...

That's not my logic. My logic was that the low priority you've
attached to standard conformance in the past suggests that you
implemented this feature this way because it made sense to you,
and not because you were aware of the intention to add wording
to the next version of the C standard to make it mandatory. I
would have guessed that you implemented this feature long before
it was even proposed for the next version of the standard. Am I
wrong about that?

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.
 
F

Flash Gordon

jacob navia wrote, On 08/10/08 21:11:
When I said that lcc-win compiles and executes that
code correctly they could not stand that.

Actually it was pointed out that what lcc-win does is allowed and that
Chuck was wrong to say it was a bug in lcc-win.
Denigrating lcc-win is the only thing they all agree with.

Wrong. They complain about *you* claiming it conforms to C99 even though
you know that you have not yet finished implementing C99. Personally I
would like to be able to use varidac macros as they would allow me to
greatly increase the debug facilities available to me on a significant
code-base without doing major edits to it, but that is one feature you
think is unimportant (an opinion you are entitled to but one I disagree
with).
Why?

It is the only compiler that is not a C++ compiler that
happens to compile C.

I used to use a compiler that is only a C compiler and have posted
comments about it here without anyone denigrating it so that can't be
the reason.
It has several enhancements and innovations that make
this people scream like mad dogs:
o Operator overloading
o generic functions
o default arguments

No, they complain about you discussing those extensions here.
The prototype figure for them is CB Falconer.

Hmm. In this very thread it has been pointed out that Chuck was wrong in
his claim that it was a bug in your compiler. People have even posted
that what your compiler does in this instance makes sense.
I agree with you. It would be better if I just ignore
those. Normally I stopped posting here.

Just stop taking everything as personal attacks.
 
L

lawrence.jones

jacob navia said:
I do not care about your opinion.

It's not my opinion -- the standard contains a list of requirements for
a conforming implementation; if lcc-win does not meet all of those
requirements (which it doesn't), then it's not conforming.
Ditto. If you use C99 constructs in your code it will accept
them. Pedants notwithstanding.

Conversely, if you use C89 constructs that look like C99 constructs, it
will either misinterpret them or reject them. The chance of such
constructs occurring "by accident" is small, but not zero.

Your compiler is an impressive and useful piece of work. As far as I
can tell, nobody hates it or you, we just don't want you confusing
people by misrepresenting it. Call it nearly conforming or say that
it's intended to be fully conforming but isn't quite there yet and no
one will argue with you. Just don't call it fully conforming when it's
not.
 
K

Keith Thompson

CBFalconer said:
Don't think about the complex returned values. They already need
special handling (after all, they were even forbidden to be
returned in early C). Consider the return value from, say,
strlen. This is of type size_t, which will be some form of
integer. It is normally returned in a register, and is a value.
What if you write:

&strlen(thing);

in your code somewhere. What are you taking the address of? Why?
Why did you want to force saving all such returns in a memory
location (and possibly letting the optimizer take them all out
again)? If you really want this you can write:

thinglen = strlen(thing);
use(&thinglen);

Here's what n1336 6.2.4p7 says (I think it's already been quoted in
this thread):

A non-lvalue expression with structure or union type, where the
structure or union contains a member with array type (including,
recursively, members of all contained structures and unions)
refers to an object with automatic storage duration and _temporary
lifetime_. 29) Its lifetime begins when the expression is
evaluated and its initial value is the value of the
expression. Its lifetime ends when the evaluation of the
containing full expression or full declarator ends. Any attempt to
modify an object with temporary lifetime results in undefined
behavior.

And footnote 29:

The address of such an object is taken implicitly when an array
member is accessed.

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.
 
H

Harald van Dijk

Harald said:
Keith Thompson wrote:
... snip ...
Where does C99 say that the array may be accessed *before* the next
sequence point?

6.2.4p1:
"An object has a storage duration that determines its lifetime.
There are three storage durations: static, automatic, and allocated.
[...]"
[...]

And why do you think that a functions returned value is a C object, if
you haven't stored it in such an object?

C does not have such a thing as a pointer to a value. If you can get a
pointer value of type pointer-to-object, and it is not null or pointing
just past the end of an object, it is a pointer to an object. Or are you
saying that the automatic conversion of array values to pointers results
(can result) in pointer non-values?
 
J

jacob navia

CBFalconer said:
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.

When you write

foo f;
f = getfoo();

what gets generated is:

getfoo(&f);

When you do NOT use any variable in the assignment, lcc-win
creates an automatic temporary variable, that is why all these things
work.
 
C

CBFalconer

jacob said:
.... snip ...

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.

WRONG. But not of interest.
It has several enhancements and innovations that make
this people scream like mad dogs:
o Operator overloading

NOT a C99 (nor C90) feature
o generic functions

NOT a C99 (nor C90) feature
o default arguments

NOT a C99 (nor C90) feature

so what has that to do with the subject?
 
C

CBFalconer

(e-mail address removed) 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.
 
J

jameskuyper

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.
 
K

Keith Thompson

Your compiler is an impressive and useful piece of work. As far as I
can tell, nobody hates it or you, we just don't want you confusing
people by misrepresenting it. Call it nearly conforming or say that
it's intended to be fully conforming but isn't quite there yet and no
one will argue with you. Just don't call it fully conforming when it's
not.

jacob, the above is an excellent summary of what I (and several
others) have been trying to tell you for a very long time.
 
C

CBFalconer

Keith said:
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.
 
C

CBFalconer

Flash said:
jacob navia wrote, On 08/10/08 21:11:
.... snip ...


No, they complain about you discussing those extensions here.


Hmm. In this very thread it has been pointed out that Chuck was
wrong in his claim that it was a bug in your compiler. People
have even posted that what your compiler does in this instance
makes sense.

I don't think I ever claimed that that was a bug. I am not going
over the thread to check. However I did point out that failure to
point out the occurence of undefined behaviour was NOT a bug.
 
C

CBFalconer

CBFalconer wrote:
...

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.
 
C

CBFalconer

jacob said:
CBFalconer said:
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.
 
K

Keith Thompson

CBFalconer said:
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.
 
M

Martien Verbruggen

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?

Martien
 
K

Keith Thompson

CBFalconer said:
jacob said:
CBFalconer said:
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.

BTW, &(getfoo().a) is the address of the array, not the address of one
of the characters. And that address would become meaningless after
the printf finishes.

Please see my other recent followup in this thread. If I've missed
something, please explain it to me.
 
I

Ian Collins

CBFalconer said:
jacob said:
CBFalconer said:
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?
 
N

Nate Eldredge

CBFalconer said:
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?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top