467,893 Members | 1,898 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

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

Passing structs by value


Does the following program exhibit undefined behavior? Specifically,
does passing a struct by value cause undefined behavior if that struct
has as a member a pointer that has been passed to free()?

#include <stdlib.h>

struct stype
{
int *foo;
};

void bar( struct stype foo )
{
}

int main( void )
{
struct stype baz;
baz.foo=malloc( sizeof *baz.foo );
free( baz.foo );
bar( baz ); /* UB or not? */
return 0;
}

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Dec 5 '05 #1
  • viewed: 2472
Share:
17 Replies
On Mon, 5 Dec 2005 05:20:20 +0000 (UTC), Christopher Benson-Manica
<at***@nospam.cyberspace.org> wrote in comp.lang.c:

Does the following program exhibit undefined behavior? Specifically,
does passing a struct by value cause undefined behavior if that struct
has as a member a pointer that has been passed to free()?
I'm not sure why you make a distinction between a pointer in a
structure passed by value, and a bare pointer passed by value. Also
I'm not sure why you make a distinction between a pointer that has an
indeterminate value because it is uninitialized, or because it points
to previously allocated storage that has been freed.
#include <stdlib.h>

struct stype
{
int *foo;
};

void bar( struct stype foo )
{
}

int main( void )
{
struct stype baz;
baz.foo=malloc( sizeof *baz.foo );
free( baz.foo );
bar( baz ); /* UB or not? */
return 0;
}


In several places, the C standard refers to function call arguments
being stored into the function's parameters by assignment. So passing
anything to a function in C is essentially the same as assigning it to
the function's local parameter value.

Assignment in C is always based on value, so passing anything with
indeterminate value, other than an unsigned char, to a function is
undefined behavior, as is any other use of the value of such an
object. It makes no difference whether the object with indeterminate
value is part of a structure, nor does it make any difference how the
value became indeterminate.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Dec 5 '05 #2
Jack Klein <ja*******@spamcop.net> writes:
On Mon, 5 Dec 2005 05:20:20 +0000 (UTC), Christopher Benson-Manica
<at***@nospam.cyberspace.org> wrote in comp.lang.c:
Does the following program exhibit undefined behavior? Specifically,
does passing a struct by value cause undefined behavior if that struct
has as a member a pointer that has been passed to free()?


I'm not sure why you make a distinction between a pointer in a
structure passed by value, and a bare pointer passed by value. Also
I'm not sure why you make a distinction between a pointer that has an
indeterminate value because it is uninitialized, or because it points
to previously allocated storage that has been freed.
#include <stdlib.h>

struct stype
{
int *foo;
};

void bar( struct stype foo )
{
}

int main( void )
{
struct stype baz;
baz.foo=malloc( sizeof *baz.foo );
free( baz.foo );
bar( baz ); /* UB or not? */
return 0;
}


In several places, the C standard refers to function call arguments
being stored into the function's parameters by assignment. So passing
anything to a function in C is essentially the same as assigning it to
the function's local parameter value.

Assignment in C is always based on value, so passing anything with
indeterminate value, other than an unsigned char, to a function is
undefined behavior, as is any other use of the value of such an
object. It makes no difference whether the object with indeterminate
value is part of a structure, nor does it make any difference how the
value became indeterminate.


But DR #222 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_222.htm>
says:

The value of a struct or union object is never a trap
representation, even though the value of a member of a struct or
union object may be a trap representation.

This was published in TC2 and in N1124.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 5 '05 #3
Keith Thompson wrote:

Jack Klein <ja*******@spamcop.net> writes:

Assignment in C is always based on value, so passing anything with
indeterminate value, other than an unsigned char, to a function is
undefined behavior, as is any other use of the value of such an
object.
It makes no difference whether the object with indeterminate
value is part of a structure,
nor does it make any difference how the
value became indeterminate.


But DR #222 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_222.htm>
says:

The value of a struct or union object is never a trap
representation, even though the value of a member of a struct or
union object may be a trap representation.

This was published in TC2 and in N1124.


I don't think that the undefined behavior
associated with accessing freed pointer values
has anything to do with traps.

--
pete
Dec 5 '05 #4
pete <pf*****@mindspring.com> writes:
Keith Thompson wrote:
Jack Klein <ja*******@spamcop.net> writes:
> Assignment in C is always based on value, so passing anything with
> indeterminate value, other than an unsigned char, to a function is
> undefined behavior, as is any other use of the value of such an
> object.
> It makes no difference whether the object with indeterminate
> value is part of a structure,
> nor does it make any difference how the
> value became indeterminate.


But DR #222 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_222.htm>
says:

The value of a struct or union object is never a trap
representation, even though the value of a member of a struct or
union object may be a trap representation.

This was published in TC2 and in N1124.


I don't think that the undefined behavior
associated with accessing freed pointer values
has anything to do with traps.


A freed pointer has an indeterminate value, defined as "either an
unspecified value or a trap representation". (The term "trap
representation" doesn't necessarily imply a trap.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 5 '05 #5
Keith Thompson wrote:

pete <pf*****@mindspring.com> writes:

I don't think that the undefined behavior
associated with accessing freed pointer values
has anything to do with traps.


A freed pointer has an indeterminate value, defined as "either an
unspecified value or a trap representation". (The term "trap
representation" doesn't necessarily imply a trap.)


I don't think trap representations
have anything to do with freed pointers.

I think we're dealing with the case of
accessing a pointer with unspecified value,
and that access being undefined.

"An unspecified value cannot be a trap representation."

"The value of a pointer becomes indeterminate when
the object it points to reaches the end of its lifetime."

5
Certain object representations need not represent
a value of the object type. If the stored
value of an object has such a representation and is
read by an lvalue expression that does not have character type,
the behavior is undefined. If such a representation is produced
by a side effect that modifies all or any part of the object by an
lvalue expression that does not have character type, the behavior is
undefined. Such a representation is called a trap representation.

--
pete
Dec 5 '05 #6
Keith Thompson <ks***@mib.org> wrote:
Jack Klein <ja*******@spamcop.net> writes:
On Mon, 5 Dec 2005 05:20:20 +0000 (UTC), Christopher Benson-Manica
<at***@nospam.cyberspace.org> wrote in comp.lang.c:
Does the following program exhibit undefined behavior? Specifically,
does passing a struct by value cause undefined behavior if that struct
has as a member a pointer that has been passed to free()?


I'm not sure why you make a distinction between a pointer in a
structure passed by value, and a bare pointer passed by value. Also
I'm not sure why you make a distinction between a pointer that has an
indeterminate value because it is uninitialized, or because it points
to previously allocated storage that has been freed.
#include <stdlib.h>

struct stype
{
int *foo;
};

void bar( struct stype foo )
{
}

int main( void )
{
struct stype baz;
baz.foo=malloc( sizeof *baz.foo );
free( baz.foo );
bar( baz ); /* UB or not? */
return 0;
}


In several places, the C standard refers to function call arguments
being stored into the function's parameters by assignment. So passing
anything to a function in C is essentially the same as assigning it to
the function's local parameter value.

Assignment in C is always based on value, so passing anything with
indeterminate value, other than an unsigned char, to a function is
undefined behavior, as is any other use of the value of such an
object. It makes no difference whether the object with indeterminate
value is part of a structure, nor does it make any difference how the
value became indeterminate.


But DR #222 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_222.htm>
says:

The value of a struct or union object is never a trap
representation, even though the value of a member of a struct or
union object may be a trap representation.


That's weird. I would've thought this would only make sense for unions,
not structs.
In a union, you can easily have the situation that a non-trap value in
one member means that another member does have a trap value (e.g., with
an int and a float: bytes that represent a valid int can be a non-valid
float). This would make passing a union safely impossible in the general
case; one could not guarantee that any non-trap value assigned to any
member of a union would leave the whole union in a passable, non-trap
state.
For a struct, this is much simpler: all members must be valid for it to
be a valid object. Assigning a non-trap value to a single struct member
is already guaranteed not to assign a trap value to any other member of
the struct, unlike in unions.

Richard
Dec 5 '05 #7
pete <pf*****@mindspring.com> writes:
Keith Thompson wrote:

pete <pf*****@mindspring.com> writes:
> I don't think that the undefined behavior
> associated with accessing freed pointer values
> has anything to do with traps.


A freed pointer has an indeterminate value, defined as "either an
unspecified value or a trap representation". (The term "trap
representation" doesn't necessarily imply a trap.)


I don't think trap representations
have anything to do with freed pointers.

I think we're dealing with the case of
accessing a pointer with unspecified value,
and that access being undefined.


The standard doesn't say the value is unspecified; it says it's
indeterminate.
"An unspecified value cannot be a trap representation."
Right, so the two cases are mutually exclusive (presumably at the whim
of the implementation).
"The value of a pointer becomes indeterminate when
the object it points to reaches the end of its lifetime."


Right, meaning it's *either* unspecified *or* a trap representation.
The standard could have been more specific, requiring it to be a trap
representation, but the same bit pattern (address) could later be
returned by another call to malloc(), and a program could detect this
using memcmp().

So:

int *ptr = malloc(sizeof *ptr); /* assume ptr != NULL */
free(ptr);
/*
* ptr now has an indeterminate value, possibly a trap representation
*/
ptr; /* undefined behavior */

The tricky thing is that the value can be unspecified rather than a
trap representation. For example, this program *might* examine the
indeterminate value of ptr1 without invoking undefined behavior:

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
int main(void)
{
void *ptr1;
void *ptr2;

ptr1 = malloc(32);
assert(ptr1 != NULL);
free(ptr1);

ptr2 = malloc(32);
assert(ptr2 != NULL);

if (memcmp(&ptr1, &ptr2, sizeof ptr1) == 0) {
printf("The same address was re-used\n");
printf("ptr1 = %p\n", ptr1);
}
else {
printf("The address was not re-used\n");
printf("Examining ptr1 may invoke undefined behavior\n");
}

return 0;
}

But if we drop the second malloc() call, after free(ptr1) the object
ptr1 *could* hold a trap representation. (The set of representations
that are trap representations can vary over time during the execution
of the program.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 5 '05 #8
Keith Thompson wrote:
(The set of representations
that are trap representations can vary over time during the execution
of the program.)


How do you know that?

--
pete
Dec 5 '05 #9
On 2005-12-05, pete <pf*****@mindspring.com> wrote:
Keith Thompson wrote:
(The set of representations
that are trap representations can vary over time during the execution
of the program.)


How do you know that?


Yeah - that is one of the more controversial of the "DS9K claims" - IMO
up there with the "padding bits of DOOM" one elsethread.
Dec 5 '05 #10
pete <pf*****@mindspring.com> writes:
Keith Thompson wrote:
(The set of representations
that are trap representations can vary over time during the execution
of the program.)


How do you know that?


Because an implementation on which they can vary can be conforming.
(I'm not claiming that they'll do so on every implementation.)

For example:

void *ptr = malloc(32);
assert(ptr != NULL);
/*
* ptr has a valid value.
*/
free(ptr);
/*
* ptr may now contain a trap representation, even though the bits
* haven't changed.
*/

How does this violate the standard? If the standard intends that ptr
can't have a trap representation, why does it say the value is
indeterminate rather than unspecified? (The only difference between
indeterminate and unspecified is that the former includes trap
representations.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 5 '05 #11
Jordan Abel wrote:

On 2005-12-05, pete <pf*****@mindspring.com> wrote:
Keith Thompson wrote:
(The set of representations
that are trap representations
can vary over time during the execution
of the program.)


How do you know that?


Yeah -
that is one of the more controversial of the "DS9K claims" - IMO
up there with the "padding bits of DOOM" one elsethread.


I can find a reference for that one.

"Some combinations of padding bits might generate trap
representations, for example, if one padding bit is a parity bit."

But I don't see anything in the standard about trap representations,
which even suggests that trap representations
can change during the execution of a program.

The only reference to trap representations
connected with pointers is"

5 An integer may be converted to any pointer type.
Except as previously specified,
the result is implementation-defined,
might not be correctly aligned,
might not point to an entity of the referenced type,
and might be a trap representation.

I read the commas and the "and" of
"this, that, and the other"
as
"this, and that, and the other"
rather than as
"this, or (that, and the other)"

--
pete
Dec 5 '05 #12
Keith Thompson wrote:
free(ptr);
/*
* ptr may now contain a trap representation, even though the bits
* haven't changed.
*/

How does this violate the standard? If the standard intends that ptr
can't have a trap representation, why does it say the value is
indeterminate rather than unspecified?
I don't know.
(The only difference between
indeterminate and unspecified is that the former includes trap
representations.)


You might be right.
I believe they (the comp.std.c crowd and others here)
also say that a machine can trap on a pointer
over running an array,
which could be an example of a trap representation
that can change during the execution of a program.

--
pete
Dec 6 '05 #13
On 2005-12-05, pete <pf*****@mindspring.com> wrote:
Jordan Abel wrote:

On 2005-12-05, pete <pf*****@mindspring.com> wrote:
> Keith Thompson wrote:
>
>> (The set of representations
>> that are trap representations
>> can vary over time during the execution
>> of the program.)
>
> How do you know that?
Yeah -
that is one of the more controversial of the "DS9K claims" - IMO
up there with the "padding bits of DOOM" one elsethread.


I can find a reference for that one.

"Some combinations of padding bits might generate trap
representations, for example, if one padding bit is a parity bit."


the "padding bits of doom" claim was that if you read out the
representation as unsigned chars, then copy it into another variable at
a later date, those padding bits might be valid anymore - i.e. the DS9K
might suddenly flip all padding bits to 1 and places with 0s would
suddenly become trap representations.
But I don't see anything in the standard about trap representations,
which even suggests that trap representations can change during the
execution of a program.

Dec 6 '05 #14
On Mon, 05 Dec 2005 23:35:11 GMT, in comp.lang.c , pete
<pf*****@mindspring.com> wrote:

But I don't see anything in the standard about trap representations,
which even suggests that trap representations
can change during the execution of a program.
Absence of a mention merely means that the standard places no
requirements on it.
In this case, the definition of trap representation (6.2.6.1p5)
doesn't say that it may /not/ change during execution, so you can't
assume it remains constant.
The only reference to trap representations
connected with pointers is"

5 An integer may be converted to any pointer type.
Except as previously specified,
the result is implementation-defined,
might not be correctly aligned,
might not point to an entity of the referenced type,
and might be a trap representation.

I read the commas and the "and" of
"this, that, and the other"
as
"this, and that, and the other"


I believe it means that at least zero of the conditions might apply to
any instance of such a conversion.

----== Posted via Newsfeeds.Com - Unlimited-Unrestricted-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Dec 6 '05 #15
"pete" <pf*****@mindspring.com> wrote in message
news:43***********@mindspring.com...
[snip]
5 An integer may be converted to any pointer type.
Except as previously specified,
the result is implementation-defined,
might not be correctly aligned,
might not point to an entity of the referenced type,
and might be a trap representation.

I read the commas and the "and" of
"this, that, and the other"
as
"this, and that, and the other"
rather than as
"this, or (that, and the other)"


I read the above as:

Except as previously specified,
the result is implementation defined,
[the result] might not be correctly aligned,
[the result] might not point to an entity of the referenced type, and
[the result] might be a trap representation.

Alex
Dec 6 '05 #16
Jordan Abel wrote:
the "padding bits of doom" claim was that if you read out the
representation as unsigned chars,
then copy it into another variable at
a later date, those padding bits might be valid anymore - i.e.
the DS9K
might suddenly flip all padding bits to 1 and places with 0s would
suddenly become trap representations.


The value of the padding bits wouldn't change.
The problem would be if the trap representation changed.

--
pete
Dec 6 '05 #17
rl*@hoekstra-uitgeverij.nl (Richard Bos) writes:
Keith Thompson <ks***@mib.org> wrote:
Jack Klein <ja*******@spamcop.net> writes:
On Mon, 5 Dec 2005 05:20:20 +0000 (UTC), Christopher Benson-Manica
<at***@nospam.cyberspace.org> wrote in comp.lang.c:
> Does the following program exhibit undefined behavior? Specifically,
> does passing a struct by value cause undefined behavior if that struct
> has as a member a pointer that has been passed to free()?

I'm not sure why you make a distinction between a pointer in a
structure passed by value, and a bare pointer passed by value. Also
I'm not sure why you make a distinction between a pointer that has an
indeterminate value because it is uninitialized, or because it points
to previously allocated storage that has been freed.

> #include <stdlib.h>
>
> struct stype
> {
> int *foo;
> };
>
> void bar( struct stype foo )
> {
> }
>
> int main( void )
> {
> struct stype baz;
> baz.foo=malloc( sizeof *baz.foo );
> free( baz.foo );
> bar( baz ); /* UB or not? */
> return 0;
> }

In several places, the C standard refers to function call arguments
being stored into the function's parameters by assignment. So passing
anything to a function in C is essentially the same as assigning it to
the function's local parameter value.

Assignment in C is always based on value, so passing anything with
indeterminate value, other than an unsigned char, to a function is
undefined behavior, as is any other use of the value of such an
object. It makes no difference whether the object with indeterminate
value is part of a structure, nor does it make any difference how the
value became indeterminate.


But DR #222 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_222.htm>
says:

The value of a struct or union object is never a trap
representation, even though the value of a member of a struct or
union object may be a trap representation.


That's weird. I would've thought this would only make sense for unions,
not structs. [...stuff about unions...]

For a struct, this is much simpler: all members must be valid for it to
be a valid object. Assigning a non-trap value to a single struct member
is already guaranteed not to assign a trap value to any other member of
the struct, unlike in unions.


Pshaw. Do you mean to say that for

struct small_stack_of_int {
int values[100];
int first_empty_slot;
};

that all elements of the 'values' array must have been assigned, even
if the relation 'first_empty_slot == 0' is true?

It makes perfect sense for struct objects to be partially valid, with
one field (or sometimes more) indicating which other fields may be
accessed. The pattern is quite common, showing up in various kinds
of buffers all the time.

The rule in the DR, and now in n1124, is IMO the most sensible
expression for how non-completely-valid structs should be
expected to behave.
Dec 9 '05 #18

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by Seung-Uk Oh | last post: by
19 posts views Thread by Method Man | last post: by
8 posts views Thread by kalinga1234 | last post: by
9 posts views Thread by Just Me | last post: by
5 posts views Thread by Chris Ashley | last post: by
3 posts views Thread by iu2 | last post: by
reply views Thread by MrMoon | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.