472,145 Members | 1,605 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,145 software developers and data experts.

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 5482
Anand Hariharan wrote:
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 ...".
My spelling has been getting a lot worse lately; I'm getting a bit
worried about that. The spell checker is useless when my error is a
legal word, just not the one I intended.
Someone recently accused me of thinking that I was incapable of making a
mistake. I'd love to be that unaware of my own faults!

Oct 10 '08 #151
Nick Keighley wrote:
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.
I have written back ends fvor Power PC, for linux (x86) for
a Analog Devices DSP (16 bits), and have done ports to
Sun Sparc. But those versions are for paying customers, not
distributed for free.
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).
Yes, small structures will be returned in registers
when possible, and registers are getting bigger and
bigger all the time.

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
Oct 10 '08 #152
Keith Thompson wrote:
CBFalconer <cb********@yahoo.comwrites:
.... snip ...
>
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.
Yes. And I am unaware (but admit it may be so) that present
compilers can handle it. However I consider it unnecessary, and
error prone.
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.
I always consider KISS to be valuable advice.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 10 '08 #153
James Kuyper wrote:
>
.... snip ...
>
Someone recently accused me of thinking that I was incapable of
making a mistake. I'd love to be that unaware of my own faults!
Should we tell your wife about this desire? :-)

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 10 '08 #154
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
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.
I think in this discussion, people are using "object" to mean something
that can be assumed to reside in memory, and whose address can be taken.

For instance, given

int foo(void);
void bar(int *p);

you cannot do

bar(&(foo()));

The value returned from foo() is a value but not an object. If you want
this value to reside in an object you have to explicitly make one.

int x;
x = foo();
bar(&x);

Now of course, an object whose address is never taken need not actually
be stored in memory, or could be optimized out of existence altogether.
But at least conceptually, it resides in memory. If you need it to do
so, the compiler will ensure that it does.
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).
There's nothing wrong with it abstractly, but it isn't the way C works.
Oct 10 '08 #155
CBFalconer <cb********@yahoo.comwrites:
Keith Thompson wrote:
>CBFalconer <cb********@yahoo.comwrites:
... snip ...
>>
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.

Yes. And I am unaware (but admit it may be so) that present
compilers can handle it. However I consider it unnecessary, and
error prone.
Ok, as I said, I have no argument with that as a programming style
point -- but it's not what we were talking about. (To be clear, I
wouldn't hesitate to write something like ``func().member'' if it were
convenient, but if you prefer to write ``temp = func(); temp.member'',
that's fine with me. Any concern that a compiler won't handle it
properly is unfounded, but as a style point it's not worth arguing
about.)
>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.

I always consider KISS to be valuable advice.
(For those few who might not be familiar with the term, KISS stands
for "Keep It Simple, Stupid".)

KISS is often very valuable advice, but in this case it has led you
completely astray of the point. The *only* thing being discussed in
this thread, going back to the initial post, is accessing an array
member of a struct (or union) returned from a function, and how that
interacts with the usual array-to-pointer conversion. Not only have
you missed that point, you have seemed to be unaware that you've
missed the point.

Do you have anything to say about the actual issue we're discussing?

--
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 #156
Nate Eldredge wrote:
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
>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.

I think in this discussion, people are using "object" to mean something
that can be assumed to reside in memory, and whose address can be taken.

For instance, given

int foo(void);
void bar(int *p);

you cannot do

bar(&(foo()));

The value returned from foo() is a value but not an object. If you want
this value to reside in an object you have to explicitly make one.
It would also be handy to be able to force the compiler to create a
temporary object by taking the address of a return value.

--
Ian Collins
Oct 10 '08 #157
On Fri, 10 Oct 2008 11:06:34 +0000, James Kuyper <ja*********@verizon.net>
wrote:
Someone recently accused me of
thinking that I was incapable of making a mistake. I'd love to be that
unaware of my own faults!
That makes for a great signature! :)

- Anand
--
"Someone recently accused me of thinking that I was incapable of making a
mistake. I'd love to be that unaware of my own faults!"
- James Kuyper in comp.lang.c
Oct 10 '08 #158
Keith Thompson wrote:
CBFalconer <cb********@yahoo.comwrites:
.... snip ...
>
>I always consider KISS to be valuable advice.

(For those few who might not be familiar with the term, KISS
stands for "Keep It Simple, Stupid".)

KISS is often very valuable advice, but in this case it has led
you completely astray of the point. The *only* thing being
discussed in this thread, going back to the initial post, is
accessing an array member of a struct (or union) returned from a
function, and how that interacts with the usual array-to-pointer
conversion. Not only have you missed that point, you have
seemed to be unaware that you've missed the point.
The thing I am trying to keep simple is the compiler. The more
things it has to take care of, the greater the probability of an
error.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Oct 11 '08 #159
CBFalconer <cb********@yahoo.comwrites:
Keith Thompson wrote:
>CBFalconer <cb********@yahoo.comwrites:
... snip ...
>>
>>I always consider KISS to be valuable advice.

(For those few who might not be familiar with the term, KISS
stands for "Keep It Simple, Stupid".)

KISS is often very valuable advice, but in this case it has led
you completely astray of the point. The *only* thing being
discussed in this thread, going back to the initial post, is
accessing an array member of a struct (or union) returned from a
function, and how that interacts with the usual array-to-pointer
conversion. Not only have you missed that point, you have
seemed to be unaware that you've missed the point.

The thing I am trying to keep simple is the compiler. The more
things it has to take care of, the greater the probability of an
error.
Ok. And how exactly do you propose to keep things simple for the
compiler?

Keep in mind that all C compilers already have to handle returning
structs by value, and allowing the caller to access just one member of
the returned value, so there's no opportunity for simplification
there.

Keep in mind also that the case under discussion, dealing with the
implicit array-to-pointer conversion for an array member of a struct
value returned from a function, doesn't seem to be all that difficult
for compilers to handle. And defining its behavior clearly in the
langauge, as n1336, makes things simpler *for the programmer*, by
making one less special case for the programmer to worry about. I
think that making things a little more complicated for compilers and a
little bit simpler for progammers is an excellent tradeoff. Why do
you think it isn't?

Finally, do you understand that the proposed change has *no effect* on
structures that don't contain arrays? Many of your previous comments
seem to have assumed that it does.

--
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 11 '08 #160
Nate Eldredge <na**@vulcan.lanwrites:
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
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.

I think in this discussion, people are using "object" to mean something
that can be assumed to reside in memory, and whose address can be taken.
The term "object" is defined in the Standard - section 3.14.

For instance, given

int foo(void);
void bar(int *p);

you cannot do

bar(&(foo()));

The value returned from foo() is a value but not an object. If you want
this value to reside in an object you have to explicitly make one.

int x;
x = foo();
bar(&x);

Now of course, an object whose address is never taken need not actually
be stored in memory, or could be optimized out of existence altogether.
But at least conceptually, it resides in memory. If you need it to do
so, the compiler will ensure that it does.
It seems like you're confusing "object" and "lvalue". An lvalue
is a means of referring to an object, but objects can exist
whether or not there are lvalues that refer to them.

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

There's nothing wrong with it abstractly, but it isn't the way C works.
The point of my comment is that it isn't possible to tell if
that's the way C works. Either interpretation is consistent
with the text of the Standard.
Oct 11 '08 #161

This discussion thread is closed

Replies have been disabled for this discussion.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.