Returning a struct from a function - strange behavior

C

CBFalconer

Keith said:
.... snip ...


How so? It seems to me that the most natural way to implement
a function returning a struct value involves, in effect,
creating an object of the struct type somewhere in memory and
setting it to the value to be returned. In fact, unless the
struct is no bigger than a machine word, I'm having trouble
thinking of an plausible implementation scheme that doesn't do
this.

Because most C systems simply return function values in a specified
register. If the user wants to ignore that value he just doesn't
use it. If he wants to use it, it is already in place. The
storage feature would require storing all those values somewhere,
just to all a field access. Now the optimizer will have to work
like mad to eliminate all those additions. This is very rough, and
just as I see it immediately. Notice that the lack of action
(ignoring the return value) involves releasing that storage. Ugh.
 
N

Nate Eldredge

CBFalconer said:
Because most C systems simply return function values in a specified
register. If the user wants to ignore that value he just doesn't
use it. If he wants to use it, it is already in place. The
storage feature would require storing all those values somewhere,
just to all a field access. Now the optimizer will have to work
like mad to eliminate all those additions. This is very rough, and
just as I see it immediately. Notice that the lack of action
(ignoring the return value) involves releasing that storage. Ugh.

I'm trying to follow this discussion and not succeeding. If I
understand you correctly, what you're trying to avoid is what already
happens. Calling conventions usually do specify that the return value
goes in a certain register, *if* it is of a type appropriate to fit in
that register. But most structs will not fit in a register, so there
is no alternative to storing the return value in memory somewhere.
Normally the calling function allocates space on the stack.
"Releasing the storage" is very easy because it happens automatically
when the function returns.

You are correct in that it needs to allocate this space even if the
return value is going to be ignored. But again, this is already the
case. As I understand it, the only difference between the current
state of affairs and what's proposed for C1x is how long the compiler
has to keep the return value intact before it can write over it.

Are you proposing to do away entirely with the ability to return
structs from C functions? Given the problems that arise, one might
argue that adding them to C was ill-considered, but I think it's too
late to take them back now.
 
N

Nick Keighley

Nick Keighleywrote:


The program is incorrect.  There is no requirement for UB to cause
an error message.

sorry? If its UB it can anything it likes. There may be no
requirement
to produce an error message (a diagnostic) but there can't be
anything
wrong with producing a diagnostic. So why is (this version) of gcc
buggy?
 
N

Nick Keighley

If I've understood the rest of the thread. This is Undefined
Behaviour.

how can it be "correct" if its Undefined Behaviour?
Is lcc operating in C89 or C99 mode?

No, lcc-win's UB is such as to hide the problem.

what problem? If it's UB it can what it likes. That's what
UB is for, to make life easy for implementors.

And to be honest printing the string seems like the sanest
possible implementation.
 
L

lovecreatesbeauty

DiAvOl said:
Keith Thompson said:
...make_person().name reveals an anomaly in the C type
system; it should be a value of type char[40], which
should decay to a pointer to the first element of the
corresponding array object, but there is no array object,
just a value.
My question then is if there is no array object why does
the &make_person().name[0] works?

It doesn't, since name doesn't decay to a pointer. Even

It seems it's still an array :)

int main(void) {

/* printf("%s\n", make_person().name); */

size_t n = sizeof make_person().name;
size_t i;

printf("%d\n", n);
for (i = 0; i < n; i++)
printf("%c", make_person().name);
return 0;
}

$ gcc --version
gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

$ make && ./a.out
gcc -ansi -pedantic -Wall -W -c -o a.o a.c
a.c: In function ‘main’:
a.c:24: warning: ISO C90 forbids subscripting non-lvalue array
gcc a.o -o a.out
40
alexander$
$
if it did, & requires an object. In other words, it works
by ub. Segfault is one example of ub, 'works just fine' is
another.

The question is why people went out of their way to cause
your sample to suddenly segfault. [Why they did a half
job is lesser question.]
 
A

Antoninus Twink

I'm trying to follow this discussion and not succeeding.

That's because it's bullshit, for the reasons you go on to describe very
articulately.

You should bear in mind that CBF is a senile old fool who doesn't have
the first clue what he's talking about. No one here takes him seriously
- even the usually mild-mannered KT is currently laying into his
stupidity in repeating replies others have made hours earlier.
 
J

James Kuyper

Nick said:
sorry? If its UB it can anything it likes. There may be no
requirement
to produce an error message (a diagnostic) but there can't be
anything
wrong with producing a diagnostic. So why is (this version) of gcc
buggy?

He said "the program is incorrect". How did you manage to misinterpret
that as a statement that gcc is buggy?
 
J

James Kuyper

Nick said:
If I've understood the rest of the thread. This is Undefined
Behaviour.


how can it be "correct" if its Undefined Behaviour?

All behavior is correct when the behavior is not defined by the
standard. However, what jacob means by "correct" is somewhat more
restrictive: it behaves in the manner that a reasonable person (as
judged from jacob's perspective) would expect it to work.
Is lcc operating in C89 or C99 mode?

He's talking about lcc-win, not lcc. lcc-win has no mode in which it
fully conforms to either standard. Incidentally, the behavior produced
by lcc-win is the same as that specified by the proposed draft for the
next version of the standard, and arguably the same behavior that was
intended to be correct in C99. That gives the term "correct" a little
more appropriateness. However, I suspect that this is just a coincidence.
 
J

jacob navia

James said:
All behavior is correct when the behavior is not defined by the
standard. However, what jacob means by "correct" is somewhat more
restrictive: it behaves in the manner that a reasonable person (as
judged from jacob's perspective) would expect it to work.

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?

He's talking about lcc-win, not lcc. lcc-win has no mode in which it
fully conforms to either standard.

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.

Pedans in this group like to say then that "it doesn't conform to any
standard" since they are just that

PEDANTS
Incidentally, the behavior produced
by lcc-win is the same as that specified by the proposed draft for the
next version of the standard, and arguably the same behavior that was
intended to be correct in C99. That gives the term "correct" a little
more appropriateness.

Correct. :)
However, I suspect that this is just a coincidence.


Obviously if I have something correct it can ONLY be a coincidence,
since I am unable to produce anything correct since I am a jerk
by definition.

Quite a clear logic isn't it?

These people are unable to think clearly beyond their pedantics ramblings
 
J

James Kuyper

jacob said:
James Kuyper wrote: ....

My compiler conforms to C99.

I was very careful to use the phrase "fully conforms", because I knew
that you misunderstand the unmodified term "conform" as allowing less
than full conformance. Or is this an assertion that you've finally
finished implementing even those C99 features that you had previously
dismissed as "unimportant"? If so, you should have announced that fact.
... 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.

Since those diagnostics are mandatory for C89, that makes it
non-conforming, and in a way that's pretty important to me.

I currently work under a requirement that my code "conform" to C90 (more
precisely, C90 + the applicable TCs). The idiots who wrote that
requirement were unaware of the definition used by the C standard for
conforming code, so it's trivial to meet the letter of that requirement.
However, to satisfy the spirit of that requirement, I need a compiler
that actually produces the diagnostics that C90 mandates. lcc-win32 doesn't.
Pedans in this group like to say then that "it doesn't conform to any
standard" since they are just that

PEDANTS

I'm glad you noticed. Pedants hate to be mis-identified. Coming from
you, the term is a compliment, precisely because you do not intend it to
be one.
Correct. :)



Obviously if I have something correct it can ONLY be a coincidence,
since I am unable to produce anything correct since I am a jerk
by definition.

Quite a clear logic isn't it?

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?
 
L

lawrence.jones

No, support for non-lvalue arrays was added in C99.

It's been pointed out to me in e-mail that the original code passes the
array to printf which then attempts to access it after the next sequence
point (which would be the one right before the call), so it actually
*is* undefined behavior in C99, despite the support for non-lvalue
arrays. The current rules in C1X say the temporary object exists until
the end of the full expression that creates it, which does make it well
defined. As has been pointed out elsethread, there's no guarantee that
those rules won't change, although I sincerely doubt that they're going
to go away completely.
 
L

lawrence.jones

Keith Thompson said:
Arrays in C are almost always treated as second-class objects. It's
almost impossible to obtain an expression of array type that doesn't
refer to an array object. I believe this can *only* occur when the
array is a member of a struct or union, and that struct or union is
returned from a function -- which is itself the only way (I think) to
obtain an expression of struct or union type that doesn't refer to an
object of struct or union type.

Non-lvalue structs/unions can also occur as the result of an assignment,
comma, or conditional operator.
 
L

lawrence.jones

jacob navia said:
My compiler conforms to C99.

No, it doesn't. It *mostly* conforms, if you like, but until you
implement *all* of the requirements, not just the "important" ones, it
does not (fully) conform.
It will compile C89 code without any
problems and nit conforms fully to the C89 standard also.

That is blatantly untrue. There are obscure C89 constructs that it
miscompiles because it interprets them as containing C99 constructs.
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.

That's not a "problem", it's a *problem*. C89 *requires* diagnostics in
certain circumstances. If your compiler doesn't emit those diagnostics,
then it does *not* fully conform. You can say it *mostly* conforms if
you like, but please stop lying to people.
Obviously if I have something correct it can ONLY be a coincidence,
since I am unable to produce anything correct since I am a jerk
by definition.

No, you seem uninterested in the details of what the standards require
and you haven't given any indication that you've even looked at the C1X
draft, so conforming to something new in it is more likely coincidental
than intentional.
 
N

Nick Keighley

He said "the program is incorrect". How did you manage to misinterpret
that as a statement that gcc is buggy

having re-read this part of the thread. I have no idea how I managed
to read that gcc was buggy. I was quite confused at the time. I
understood
Chuck to be saying that it *should* produce a diagnostic.

Also, Lawrence said that gcc was incorrect.
 
K

Keith Thompson

jacob navia 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?

If you actually manage to pass an *array* to printf, presumably the
behavior is undefined. If you pass a pointer to the first element of
such an array, then of course it will behave as expected.

No doubt you'll think I'm playing "word games", but the specific
possibility of passing an actual array value rather than a pointer
value to printf has been discussed in this thread.
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've said that there are some "unimportant" features of C99 that
lcc-win does not yet implement. Are you saying that's no longer the
case? Does lcc-win FULLY CONFORM to C99? For example, do variadic
macros work? If so, that's great news.

As for C89 conformance, are the diagnostics required by the standard
also "unimportant"? They're quite important to some of us.

How does lcc-win handle this program?

#include <stdio.h>
int main(void)
{
int restrict = 6;
int inline = 7;
printf("%d\n", restrict * inline);
return 0;
}
Pedans in this group like to say then that "it doesn't conform to any
standard" since they are just that

PEDANTS

Yes, because we are pedants and because it's a true statement.
Correct. :)



Obviously if I have something correct it can ONLY be a coincidence,
since I am unable to produce anything correct since I am a jerk
by definition.

Quite a clear logic isn't it?

These people are unable to think clearly beyond their pedantics ramblings

Did you notice the word "suspect"? I notice that you don't attempt to
refute his statement, just ridicule him personally.
 
K

Keith Thompson

It's been pointed out to me in e-mail that the original code passes the
array to printf which then attempts to access it after the next sequence
point (which would be the one right before the call), so it actually
*is* undefined behavior in C99, despite the support for non-lvalue
arrays. The current rules in C1X say the temporary object exists until
the end of the full expression that creates it, which does make it well
defined. As has been pointed out elsethread, there's no guarantee that
those rules won't change, although I sincerely doubt that they're going
to go away completely.

Where does C99 say that the array may be accessed *before* the next
sequence point?
 

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

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top