473,692 Members | 2,411 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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(voi d);

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

return 0;
}

static Person make_person(voi d) {
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().n ame); with
printf("%s\n", &make_person(). name[0]); everything works as expected.

Why does this happen? Isn't make_person().n ame 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().n ame 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 5838
Keith Thompson wrote:
>
.... snip ...
>
But C99 6.3.2.1p3 says:

Except when it is the operand of the sizeof operator or the
unary & operator, or is a string literal used to initialize
an array, an expression that has type "array of _type_" is
converted to an expression with type "pointer to _type_"
that points to the initial element of the array object and
is not an lvalue. If the array object has register storage
class, the behavior is undefined.

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?

It seems that the new wording in n1336, creating an object with
"temporary lifetime" (6.2.4p7) is intended to avoid the need for
non-lvalue arrays.

My assumption is that you're right and that I've missed something.
I don't think so. Remember that n1336 is not the C99 standard, but
a draft for a new C0x system. I don't believe that any 'temporary
lifetime' storage for function results will survive - it will
involve too many ugly inefficiencies.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home .att.net>
Try the download section.
Oct 7 '08 #31
Martin Ambuhl wrote:
Nick Keighley wrote:
><lovecreatesbe a...@gmail.comw rote:
>>Keith Thompson <ks...@mib.orgw rote:
<lovecreates bea...@gmail.co mwrites:
DiAvOl <dia...@freemai l.grwrote:
>
>int main(void) {
> printf("%s\n", make_person().n ame);
> return 0;
>}
>
why don't you code it in this way.
int main(void){
Person p;
p = make_person();
printf("%s\n", p.name);
/*printf("%s\n", make_person().n ame);*/
return 0;
}

Because that doesn't illustrate the point.

The op's code is wrong and my revision is right, isn't it

the point is that it is not obvious why the OPs code is
wrong. It surprised me.

It still surprises me, since _my_ copy of gcc 4.2.3 with -W -Wall
0std=c99 -pedantic neither reports the diagnostic nor eegfaults.
Therefore it would be useful for DiAv01 to report the compiler
version he used, and on what system it was running.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home .att.net>
Try the download section.
Oct 7 '08 #32
Nick Keighley wrote:
Martin Ambuhl <mamb...@earthl ink.netwrote:
.... snip ...
>
>It still surprises me, since _my_ copy of gcc 4.2.3 with -W -Wall
0std=c99 -pedantic neither reports the diagnostic nor eegfaults

so is the program incorrect? Or did a particular version of gcc
have a bug? If the program is exhibiting UB then translating it and
running it to produce the "expected" behaviour is perfectly ok!
The program is incorrect. There is no requirement for UB to cause
an error message.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home .att.net>
Try the download section.
Oct 7 '08 #33
jacob navia wrote:
DiAvOl wrote:
.... snip ...
>
>PS. I tried the lcc compiler which compiled the code without
warnings/errors

Yes, lcc-win compiles and executes correctly your code. As does
MSVC, that correctly executes it.
No, lcc-win's UB is such as to hide the problem.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home .att.net>
Try the download section.
Oct 7 '08 #34
On Tue, 07 Oct 2008 19:12:22 -0400,
CBFalconer <cb********@yah oo.comwrote:
DiAvOl wrote:
The OP wrote, which you snipped, but responded to:
>PS. I tried the lcc compiler which compiled the code without
warnings/errors
Your alleged 'good' experience with lcc shows a bug in lcc. I
don't know if you mean lcc-win32
Can you explain why that is a bug in lcc or lcc-win32?

It seems to me after reading this thread -- particularly the posts by
Larry Jones -- that the behaviour under c89 for that code is undefined,
which means that producing the expected behaviour is perfectly valid. It
also appears that under c99 the code is defined, and is supposed to
produce the expected behaviour.

Since the lcc compiler that the OP used produces the expected behaviour
(printing the string "alexander" ), which is valid behaviour under both
c89 and c99, how can there be a bug?

Did you mean that a diagnostic was required? If so, can you explain why?

Martien
--
|
Martien Verbruggen | If at first you don't succeed, try again.
| Then quit; there's no use being a damn fool
| about it.
Oct 7 '08 #35
CBFalconer <cb********@yah oo.comwrites:
DiAvOl wrote:
>>
#include <stdio.h>

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

static Person make_person(voi d);

int main(void) {
printf("%s\n", make_person().n ame);
return 0;
}

static Person make_person(voi d) {
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().n ame); with
printf("%s\n ", &make_person(). name[0]); everything works as
expected.

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

No. make_person() returns a struct by value, which has a field
identified by .name. That field is an array of 40 chars. It is a
portion of the return struct, which has never been put in
accessible memory.

Your alleged 'good' experience with lcc shows a bug in lcc. I
don't know if you mean lcc-win32 (which has quite a few known
insects) or lcc (which is less well known here).
What bug are you referring to? In another followup in this thread,
you said that the program's behavior is undefined; if so, anything
lcc-win does is permitted, and in this particular case its behavior
seems reasonable.

Larry Jones says that the stated behavior of lcc (or lcc-win) is what
was intended for C99. I'm skeptical that the C99 standard actually
states this, but N1336, the first draft for C1X, makes it explicit
that there is a temporary object. (It might arguably be a constraint
violation in C90, but lcc-win doesn't claim to support C90.)

--
Keith Thompson (The_Other_Keit h) 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 7 '08 #36
CBFalconer <cb********@yah oo.comwrites:
Keith Thompson wrote:
>>
... snip ...
>>
But C99 6.3.2.1p3 says:

Except when it is the operand of the sizeof operator or the
unary & operator, or is a string literal used to initialize
an array, an expression that has type "array of _type_" is
converted to an expression with type "pointer to _type_"
that points to the initial element of the array object and
is not an lvalue. If the array object has register storage
class, the behavior is undefined.

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?

It seems that the new wording in n1336, creating an object with
"temporary lifetime" (6.2.4p7) is intended to avoid the need for
non-lvalue arrays.

My assumption is that you're right and that I've missed something.

I don't think so. Remember that n1336 is not the C99 standard, but
a draft for a new C0x system.
Yes, I know what n1336 is. (It's C201X, BTW, or C1X if you want to be
terse; the final document presumably won't be ready before the end of
next year.)
I don't believe that any 'temporary
lifetime' storage for function results will survive - it will
involve too many ugly inefficiencies.
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.

The current C standard doesn't mention such an object, so it doesn't
necessarily exist *as an object*. In the abstract machine, there's
just a struct value floating around somewhere. This causes serious
conceptual problems for this corner case. where a reference to an
array member of this struct value *needs* the object to exist.

The proposed change for C201X makes this object explicit, but only
when the struct (or union; I just noticed that) has an array member.

As far as I can tell, this only affects a function returning a struct
with an array member, something that I think is fairly rare; it's more
common to deal with structs, especially large ones, by passing
pointers around. And it's likely to mandate what many compilers
already do.

Where are the "ugly inefficiencies" ? And what's your proposed
alternative to the temporary object? Would you leave the behavior of
this corner case undefined?

--
Keith Thompson (The_Other_Keit h) 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 8 '08 #37
CBFalconer wrote:
Martin Ambuhl wrote:
>Nick Keighley wrote:
>><lovecreatesb ea...@gmail.com wrote:
Keith Thompson <ks...@mib.orgw rote:
<lovecreate sbea...@gmail.c omwrites:
>DiAvOl <dia...@freemai l.grwrote:
>>
>>int main(void) {
>> printf("%s\n", make_person().n ame);
>> return 0;
>>}
>why don't you code it in this way.
>int main(void){
> Person p;
> p = make_person();
> printf("%s\n", p.name);
> /*printf("%s\n", make_person().n ame);*/
> return 0;
>}
Because that doesn't illustrate the point.
The op's code is wrong and my revision is right, isn't it
the point is that it is not obvious why the OPs code is
wrong. It surprised me.
It still surprises me, since _my_ copy of gcc 4.2.3 with -W -Wall
0std=c99 -pedantic neither reports the diagnostic nor eegfaults.

Therefore it would be useful for DiAv01 to report the compiler
version he used, and on what system it was running.
No, I misunderstood. He is *not* using -std=c99 and attempting to use a
c99 construct. I misread 'without' as 'with' in
The above small program when compiled without the -std=c99 option
This reading error was probably triggered by my inability to realize
that someone might try to use a c99-only construct in a c89 environment
and then ask why it didn't work.
Oct 8 '08 #38
On Oct 8, 3:24*am, Martin Ambuhl <mamb...@earthl ink.netwrote:
CBFalconer wrote:
Martin Ambuhl wrote:
Nick Keighley wrote:
<lovecreatesbe a...@gmail.comw rote:
Keith Thompson <ks...@mib.orgw rote:
<lovecreates bea...@gmail.co mwrites:
DiAvOl <dia...@freemai l.grwrote:
>>>>>int main(void) {
>* printf("%s\n", make_person().n ame);
>* return 0;
>}
why don't you code it in this way.
int main(void){
* * * * Person p;
* * * * p = make_person();
* * * * printf("%s\n", p.name);
* * * * /*printf("%s\n", make_person().n ame);*/
* * * * return 0;
}
Because that doesn't illustrate the point.
The op's code is wrong and my revision is right, isn't it
the point is that it is not obvious why the OPs code is
wrong. It surprised me.
It still surprises me, since _my_ copy of gcc 4.2.3 with -W -Wall
0std=c99 -pedantic neither reports the diagnostic nor eegfaults.
Therefore it would be useful for DiAv01 to report the compiler
version he used, and on what system it was running.

No, I misunderstood. *He is *not* using -std=c99 and attempting to use a
c99 construct. *I misread 'without' as 'with' in
*The above small program when compiled without the -std=c99 option
This reading error was probably triggered by my inability to realize
that someone might try to use a c99-only construct in a c89 environment
and then ask why it didn't work.
That is exactly the reason I asked, to understand why it works for C99
and not for C89

I won't use such a construct in C89 of course, I'm trying to
understand (and I think i do understand now) why it works (or does
not!!) this way in C89

Why is this hard to understand?
Oct 8 '08 #39
DiAvOl <di****@freemai l.grwrites:
On Oct 8, 3:24*am, Martin Ambuhl <mamb...@earthl ink.netwrote:
>CBFalconer wrote:
Martin Ambuhl wrote:
Nick Keighley wrote:
<lovecreatesb ea...@gmail.com wrote:
Keith Thompson <ks...@mib.orgw rote:
<lovecreate sbea...@gmail.c omwrites:
>DiAvOl <dia...@freemai l.grwrote:
>>>>>>int main(void) {
>>* printf("%s\n", make_person().n ame);
>>* return 0;
>>}
>why don't you code it in this way.
>int main(void){
>* * * * Person p;
>* * * * p = make_person();
>* * * * printf("%s\n", p.name);
>* * * * /*printf("%s\n", make_person().n ame);*/
>* * * * return 0;
>}
Because that doesn't illustrate the point.
The op's code is wrong and my revision is right, isn't it
the point is that it is not obvious why the OPs code is
wrong. It surprised me.
It still surprises me, since _my_ copy of gcc 4.2.3 with -W -Wall
0std=c99 -pedantic neither reports the diagnostic nor eegfaults.
Therefore it would be useful for DiAv01 to report the compiler
version he used, and on what system it was running.

No, I misunderstood. *He is *not* using -std=c99 and attempting to use a
c99 construct. *I misread 'without' as 'with' in
*The above small program when compiled without the -std=c99 option
This reading error was probably triggered by my inability to realize
that someone might try to use a c99-only construct in a c89 environment
and then ask why it didn't work.

That is exactly the reason I asked, to understand why it works for C99
and not for C89

I won't use such a construct in C89 of course, I'm trying to
understand (and I think i do understand now) why it works (or does
not!!) this way in C89
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.

Your array expression "make_person(). name" is not an lvalue, so the
conversion doesn't occur. It's unclear what happens next. I *think*
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. But when you assign the result of
make_person() to the object p, the expression p.name *is* an lvalue,
the conversion does occur, and everything works.

C99 drops the requirement for the array expression to be an lvalue, so
the array-to-pointer conversion does occur, even for
"make_person(). name". The problem, though, is that it's not at all
clear what "the array object" is. Arguably if there's no lvalue, then
there's no array object, and the standard's requirement is
meaningless.

C1X proposes to create an implicit temporary object in this case, so
"make_person(). name" *is* an lvalue. It's been suggested that this
was the intent for C99, but I'm not convinced -- but perhaps the
authors of gcc were convinced.

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.

Whether accidentally or deliberately, you've run into an obscure
corner of the language where even the experts don't necessarily agree
on what's supposed to happen. Your best bet, if you're actually
trying to get some work done, is to avoid the issue and use an
explicit temporary.

--
Keith Thompson (The_Other_Keit h) 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 8 '08 #40

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

17
3246
by: I.M. !Knuth | last post by:
Hi. I'm more-or-less a C newbie. I thought I had pointers under control until I started goofing around with this: ================================================================================ /* A function that returns a pointer-of-arrays to the calling function. */ #include <stdio.h> int *pfunc(void);
0
8968
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
8810
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8810
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
7633
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
6462
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5822
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4562
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2978
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2242
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

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.