Copying from one struct to another, simple assignment?

B

Ben Bacarisse

Keith Thompson said:
Anyway, the paragraph in question is:

A postfix expression followed by the . operator and an identifier
designates a member of a structure or union object. The value is
that of the named member,82) and is an lvalue if the first
expression is an lvalue. If the first expression has qualified
type, the result has the so-qualified version of the type of the
designated member.

where the "82)" is a superscript reference to the quoted footnote.
The only difference is the reference to the footnote, which was added
by TC3 in response to DR #283,
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm>, which in
turn isolated one of the points from DR #257,
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_257.htm>.

Thank you.
 
R

Richard Bos

Anthony Fremont said:
Of course it doesn't "need" to know, but the standard implies that it does
since the padding isn't guaranteed to be copied.

No. The Standard implies that it _may_, or may not, know. That's why the
padding is neither guaranteed to be copied, nor guaranteed not to be
copied.

The real problem is that even if copying is done using (possibly an
internal equivalent of) memcpy(), it is quite possible to start out with
two different structs with different padding, and then assign identical
values to the members of those structs, potentially leaving only the
padding different. Any reasonable programmer would expect those two
structs to compare equal; and that requires that the comparison operator
ignores padding, and therefore must know where it is.
Of course, it's easily possible to write an implementation which works
around this by carefully keeping all padding bytes at, say, zero, at all
times. Requiring this of all implementations, though, defeats the
purpose of leaving the value of padding undefined.

Richard
 
J

JosephKK

No. The Standard implies that it _may_, or may not, know. That's why the
padding is neither guaranteed to be copied, nor guaranteed not to be
copied.

The real problem is that even if copying is done using (possibly an
internal equivalent of) memcpy(), it is quite possible to start out with
two different structs with different padding, and then assign identical
values to the members of those structs, potentially leaving only the
padding different. Any reasonable programmer would expect those two
structs to compare equal; and that requires that the comparison operator
ignores padding, and therefore must know where it is.
Of course, it's easily possible to write an implementation which works
around this by carefully keeping all padding bytes at, say, zero, at all
times. Requiring this of all implementations, though, defeats the
purpose of leaving the value of padding undefined.

Richard

Arrgh. Does the standard require struct assignment to work across
implementations? If so, why cannot equals comparison work within an
implementation? Well ordering obviously does not work cleanly.
 
K

Keith Thompson

JosephKK said:
[...]
The real problem is that even if copying is done using (possibly an
internal equivalent of) memcpy(), it is quite possible to start out with
two different structs with different padding, and then assign identical
values to the members of those structs, potentially leaving only the
padding different. Any reasonable programmer would expect those two
structs to compare equal; and that requires that the comparison operator
ignores padding, and therefore must know where it is.
Of course, it's easily possible to write an implementation which works
around this by carefully keeping all padding bytes at, say, zero, at all
times. Requiring this of all implementations, though, defeats the
purpose of leaving the value of padding undefined.

Arrgh. Does the standard require struct assignment to work across
implementations?

The first time I read that sentence, I thought you were talking about
assigning a struct in one implementation to a struct in another
implementation; of course the standard doesn't require that. But to
answer the question I think you were asking, yes, the standard
requires each implementation to support struct assignment.
If so, why cannot equals comparison work within an
implementation? Well ordering obviously does not work cleanly.

I thought that was fairly clear from the discussion so far, but ...

The C standard *could* have required equality comparison to work for
structs. The most straightforward way to define it would be that two
structs are equal if their corresponding members are equal; the rule
would be applied recursively for members that are structs. This
breaks down if any of the members are unions; probably comparison of
unions, or of structs containing unions, would be disallowed.

I can think of at least a couple of reasons assignment is required but
equality comparison isn't.

First, assignment is much simpler to implement; all you need is the
equivalent of memcpy(). Comparison would have to ignore any padding
bytes, would have to deal specially with bit fields, and might require
specialized code if pointer and/or floating-point comparison isn't
just bitwise comparison. If you compare two bitwise identical
structs, but they both contain a floating-point member whose current
value is a NaN, are they equal? At the time when struct assignment
was added to the language, struct comparison would have been a
substantial burden on compilers.

Second, struct comparison just isn't all that useful. When was the
last time you really needed to compare two structs for equality? And
even if you did, would the obvious member-by-member comparison really
do what you want? I think it's more common to need to ignore certain
members depending on the values of other members, or to do a deep
comparison that dereferences pointers (and determines whether to
compare just a single element or an array of some size that has to be
computed).
 
F

Flash Gordon

Keith Thompson wrote:

Second, struct comparison just isn't all that useful. When was the
last time you really needed to compare two structs for equality? And
even if you did, would the obvious member-by-member comparison really
do what you want? I think it's more common to need to ignore certain
members depending on the values of other members, or to do a deep
comparison that dereferences pointers (and determines whether to
compare just a single element or an array of some size that has to be
computed).

I can give you real world examples where struct comparison is useful...

struct coord {
unsigned int x;
unsigned int y;
}

Now you want to check two points for equality. I've worked on a number
of real-world systems where you use integer representation of either
angular or polar coordinate systems. This can be due to mappings to
bit-mapped displays (or other things with similar discrete properties),
for performance on processors without FPUs (using custom trig functions
that work on scaled angles), because that is the interface, or some
combination. Some times comparing for equality in such systems is
useful, for example to avoid division-by-zero (in a brief switch to
floating point, having done analysis to prove you only get division by
zero for identical coordinates).

Another example would be to save having to repeat some fancy
calculations if the incoming aircraft attitude data has not changed (the
processor was fast enough not to need this, but it might have been a
sensible optimisation under some conditions).

Of course, these things may well be the exception rather than the rule,
but they are *real* situations where the "obvious" implementation of
struct comparison (i.e. the equivalent of member by member comparison)
is useful and sensible.

I think that I could come up with other examples from code-bases I still
have access to.
 
K

Keith Thompson

Flash Gordon said:
Keith Thompson wrote:

Second, struct comparison just isn't all that useful. When was the
last time you really needed to compare two structs for equality? And
even if you did, would the obvious member-by-member comparison really
do what you want? I think it's more common to need to ignore certain
members depending on the values of other members, or to do a deep
comparison that dereferences pointers (and determines whether to
compare just a single element or an array of some size that has to be
computed).

I can give you real world examples where struct comparison is useful... [snip stuff *I* agree with]
Of course, these things may well be the exception rather than the
rule, but they are *real* situations where the "obvious"
implementation of struct comparison (i.e. the equivalent of member by
member comparison) is useful and sensible.

I think that I could come up with other examples from code-bases I
still have access to.

I probably overstated that point a bit. Yes, there certainly are
cases where struct comparison is useful. The question is whether
those cases are worth the complication of adding the feature to the
language. For some languages (such as Ada), the answer has been yes.
For C, I suggest that the answer is no. That's not to say that I'd
object if C201X added struct comparison, but I don't expect it to do
so.

Note that in the cases where comparison is useful, it's easy enough to
write a macro or function to do the job:

#define COORD_EQUAL(a, b) ((a).x == (b).x && (a).y == (b).y)
 
F

Flash Gordon

Keith said:
Flash Gordon said:
Keith Thompson wrote:

Second, struct comparison just isn't all that useful. When was the
last time you really needed to compare two structs for equality? And
even if you did, would the obvious member-by-member comparison really
do what you want? I think it's more common to need to ignore certain
members depending on the values of other members, or to do a deep
comparison that dereferences pointers (and determines whether to
compare just a single element or an array of some size that has to be
computed).
I can give you real world examples where struct comparison is useful... [snip stuff *I* agree with]
Of course, these things may well be the exception rather than the
rule, but they are *real* situations where the "obvious"
implementation of struct comparison (i.e. the equivalent of member by
member comparison) is useful and sensible.

I think that I could come up with other examples from code-bases I
still have access to.

I probably overstated that point a bit. Yes, there certainly are
cases where struct comparison is useful.

OK, so now it's just a matter of degrees, which is a much more
subjective thing.
The question is whether
those cases are worth the complication of adding the feature to the
language. For some languages (such as Ada), the answer has been yes.
For C, I suggest that the answer is no.

I would disagree, for the reasons I've said it would be useful and the
possibilities for easy compiler optimisations (where they would be
correct) below.
That's not to say that I'd
object if C201X added struct comparison, but I don't expect it to do
so.

I'm not expecting it in C201x, but I think it would be a nice, useful
and simple addition. I think the compilers could implement it fairly
easily, but I'm not a compiler writer so I could be wrong about that.
Note that in the cases where comparison is useful, it's easy enough to
write a macro or function to do the job:

#define COORD_EQUAL(a, b) ((a).x == (b).x && (a).y == (b).y)

I know, I've done it. Obviously :)
I'm not, however, convinced about how easy it is for the compiler to
optimise this. A straight structure comparison, on the other hand, it
just has to check the definition of the struct to see if it has put in
any padding or if there are floating point or pointer fields (some
processors would not have to consider pointers). A far simpler task I
think than identifying that you are doing several comparisons of
adjacent fields of types where it would work.
 
K

Keith Thompson

Flash Gordon said:
OK, so now it's just a matter of degrees, which is a much more
subjective thing.


I would disagree, for the reasons I've said it would be useful and the
possibilities for easy compiler optimisations (where they would be
correct) below.

Sure, it's easy (for the compiler) to handle the easy cases. The
trouble is that the standard would have to define all the corner
cases, and compilers would have to implement them correctly. That's
not an impossible burden, of course (Ada record comparisonn can be
substantially more difficult, but every Ada compiler implements them).
I'm not expecting it in C201x, but I think it would be a nice, useful
and simple addition. I think the compilers could implement it fairly
easily, but I'm not a compiler writer so I could be wrong about that.

For a structure with a lot of members, including nested structures and
arrays, the compiler would have to generate a function that compares
each member and, for reasonable efficiency, returns 0 as soon as it
finds a member that's unequal. There would have to be a loop for each
array member. In some cases, of course, it can just generate a call
to memcmp(), or even more efficient inline code -- but the amount of
code generated for a given comparison could vary widely from one
implementation to another.

Some would say that this amount of implicit code would violate the
"spirit of C".

It would also introduce an incompatibility with C++, which allows
overloaded operators. If I recall correctly, in C++ you can't declare
an overloaded "==" operator for a type that has one built-in. A C
program that uses struct equality would not be a valid C++ program,
and it would be non-trivial to write a C++ equivalent.

I'm not saying either of these concerns is a show-stopper, but they're
issues that should be considered.
 
C

CBFalconer

Flash said:
Keith Thompson wrote:



I can give you real world examples where struct comparison is
useful...

struct coord {
unsigned int x;
unsigned int y;
}

So why not write a comparison function?

int eqcoord(coord a, coord b) {

if ((a.x == b.x) && (a.y == b.y)) return 1;
return 0;
}

Notice that that survives even if you add items to 'coord'.
 
C

CBFalconer

Richard said:
.... snip ...

So where am I going with all this? The upshot is that a
programming style that uses small structs rather than pointers to
small structs may make for better code. Given that assumption,
structs should be first class citizens, and equality testing is
part of the deal.

I disagree. structs ARE first class citizens. What they are not
is primitive objects, and that is the reason they are not subject
to comparisons.
 
K

Keith Thompson

CBFalconer said:
Flash Gordon wrote: [...]
I can give you real world examples where struct comparison is
useful...

struct coord {
unsigned int x;
unsigned int y;
}

So why not write a comparison function?

int eqcoord(coord a, coord b) {

if ((a.x == b.x) && (a.y == b.y)) return 1;
return 0;
}

Notice that that survives even if you add items to 'coord'.

That depends on what you mean by "survives". If you add a z member,
you might forget to add the extra comparison to eqcoord; the compiler
won't complain, but your function will no longer be correct.

Incidentally, I'd use a single return statement:

return a.x == b.x && a.y == b.y;
 
F

Flash Gordon

Keith said:
Sure, it's easy (for the compiler) to handle the easy cases. The
trouble is that the standard would have to define all the corner
cases, and compilers would have to implement them correctly. That's
not an impossible burden, of course (Ada record comparisonn can be
substantially more difficult, but every Ada compiler implements them).

I don't think it is that hard to define all the corner cases.
For a structure with a lot of members, including nested structures and
arrays,

struct comparison is defined as producing the same result as comparing
each element individually.
It is a constraint violation if the struct contains a member that could
not be compared outside of a struct (this bans arrays and unions)
volatile elements of either struct will always be accessed.
These rules are applied recursively to nested structs.

Note that the same result as comparing individually defines the result
in the presence of NaNs, i.e. if either (or both) structs contain a NaN
the result is unequal. It would also render the result undefined in any
instance where comparing the individual members is undefined. In other
words, it is the same as a pre-parser for the C which did a simple,
dumb, expansion of the struct comparison to a series of element comparisons!

Any other corner cases?
the compiler would have to generate a function that compares
each member and, for reasonable efficiency, returns 0 as soon as it
finds a member that's unequal.

The standard does not define efficiency ;-)
There would have to be a loop for each
array member.

I would probably not allow comparison of structs containing arrays since
you cannot compare arrays.
In some cases, of course, it can just generate a call
to memcmp(), or even more efficient inline code -- but the amount of
code generated for a given comparison could vary widely from one
implementation to another.

The same applies for other things in C as well. Going right back to the
start using float, double or long double generates a significant amount
of code on some processors! The cos function in the library can be
anything from a couple of assembler instructions to a large function.
Some would say that this amount of implicit code would violate the
"spirit of C".
OK.

It would also introduce an incompatibility with C++, which allows
overloaded operators. If I recall correctly, in C++ you can't declare
an overloaded "==" operator for a type that has one built-in. A C
program that uses struct equality would not be a valid C++ program,
and it would be non-trivial to write a C++ equivalent.

Does C++ define struct comparison at all? I suspect not in which case
they could choose to define it in the same way ;-)
I'm not saying either of these concerns is a show-stopper, but they're
issues that should be considered.

Well, yes, it would require serious debate by the committee.

Maybe if I have the time/energy I'll put together something a bit more
complete for comp.std.c, I don't think there is that much to consider.
Arrays, unions, volatiles, floating point, pointers and uninitialised
elements. I think it can all be specified in a couple of simple paragraphs.
 
P

Phil Carmody

Flash Gordon said:
struct comparison is defined as producing the same result as comparing
each element individually.
It is a constraint violation if the struct contains a member that
could not be compared outside of a struct (this bans arrays and
unions)
volatile elements of either struct will always be accessed.
These rules are applied recursively to nested structs.

I think you've nailed the single best reason why struct
comparison should never be a primitive in the language!
What really is the 'do what most people want' behaviour
for volatile members of the struct? Surely, if the primitive
didn't exist, people would stop the comparison as soon as
a difference is found, and so later volatiles would not
be accessed. So the 'struct is accessed => all volatiles
within in are accessed', while having its own consistent
logic, clashes with what people would probably want.

Phil
 
E

Eric Sosman

Flash said:
[... comparing structs for equality ...]
I don't think it is that hard to define all the corner cases.

struct { char *p; } x = { "foo" }, y = { "foo" };
puts (x == y ? "Which" : "outcome?");

This could, of course, be implementation-defined, but that's
a rather loose way to "define" a corner case.
struct comparison is defined as producing the same result as comparing
each element individually.

Another point to be taken from my simple example is that
an element-by-element comparison may not be what's wanted.
Continuing the above:

y.p = malloc(strlen(x.p) + 1); // assume success
strcpy (y.p, x.p);
assert (y != x); // despite "identical" content

True, a programmer who wants a "customized" or "data-aware"
notion of equality could still write his own comparisons as
he does in today's C. So the question comes down to whether
the naive element-by-element comparison would be used frequently
enough to justify the effort in changing the language. Has
anyone heard of any attempts to measure this frequency?
I would probably not allow comparison of structs containing arrays since
you cannot compare arrays.

Given arrays u[] and v[] of the same element type, you can
legitimately use `u == v' as an obfuscated way of writing `0'.
So there might not be a need to detect and disallow comparison
of structs containing arrays; the uselessness of the outcome
would carry its own disincentive.
 
K

Keith Thompson

Eric Sosman said:
Flash said:
[... comparing structs for equality ...]
I don't think it is that hard to define all the corner cases.

struct { char *p; } x = { "foo" }, y = { "foo" };
puts (x == y ? "Which" : "outcome?");

This could, of course, be implementation-defined, but that's
a rather loose way to "define" a corner case.

puts (x.p == y.p ? "Which" : "outcome?");

Allowing pointer comparison as part of struct comparison doesn't
introduce anything new.

[...]

I would probably not allow comparison of structs containing structs
since you cannot compare structs. :cool:}

You can't assign arrays, but the introduction of struct assignment
allowed arrays to be assigned indirectly. Similarly, I suggest that
the introduction of struct comparison should allow arrays to be
compared indirectly. The semantics are straightforward. (Of course
the straightforward semantics might not always be what you want, since
typically not all elements of an array members are valid, but we can't
do everyone's programming for them.)
Given arrays u[] and v[] of the same element type, you can
legitimately use `u == v' as an obfuscated way of writing `0'.
So there might not be a need to detect and disallow comparison
of structs containing arrays; the uselessness of the outcome
would carry its own disincentive.

If we're not going to allow structs containing arrays to be compared
meaningfully, it should be a constraint violation.
 
K

Keith Thompson

Flash Gordon said:
Keith Thompson wrote: [...]
It would also introduce an incompatibility with C++, which allows
overloaded operators. If I recall correctly, in C++ you can't declare
an overloaded "==" operator for a type that has one built-in. A C
program that uses struct equality would not be a valid C++ program,
and it would be non-trivial to write a C++ equivalent.

Does C++ define struct comparison at all? I suspect not in which case
they could choose to define it in the same way ;-)
[...]

C++ itself doesn't define struct comparison, but C++ programmers can.

For example, this is valid C++:

struct foo {
int x;
double y;
};

bool operator==(foo a, foo b) {
return a.x == b.x && a.y == b.y;
}

If C++ defined a predefined "==" operator for structs, it would at
least cause some confusion.

But I just learned that, at least according to g++, C++ does allow
"==" for enumerated types to be overloaded, even though it's
predefined, so perhaps it's not as much of an issue as I thought.
 
F

Flash Gordon

Keith said:
Eric Sosman said:
Flash said:
[... comparing structs for equality ...]
I don't think it is that hard to define all the corner cases.
struct { char *p; } x = { "foo" }, y = { "foo" };
puts (x == y ? "Which" : "outcome?");

This could, of course, be implementation-defined, but that's
a rather loose way to "define" a corner case.

puts (x.p == y.p ? "Which" : "outcome?");

Allowing pointer comparison as part of struct comparison doesn't
introduce anything new.

Agreed. Sometimes with pointers if actually makes sense to compare them.
I would probably not allow comparison of structs containing structs
since you cannot compare structs. :cool:}

Are, but with this change to the language you could compare structs, so
allowing to compare structs inside structs make sense :)
You can't assign arrays, but the introduction of struct assignment
allowed arrays to be assigned indirectly. Similarly, I suggest that
the introduction of struct comparison should allow arrays to be
compared indirectly. The semantics are straightforward. (Of course
the straightforward semantics might not always be what you want, since
typically not all elements of an array members are valid, but we can't
do everyone's programming for them.)

Will, I would be happy to allow comparison of arrays within structs.
Although based on Larry's answer of comp.std.c there is unlikely to be a
struct comparison added to the language.
Given arrays u[] and v[] of the same element type, you can
legitimately use `u == v' as an obfuscated way of writing `0'.
So there might not be a need to detect and disallow comparison
of structs containing arrays; the uselessness of the outcome
would carry its own disincentive.

If we're not going to allow structs containing arrays to be compared
meaningfully, it should be a constraint violation.

I would agree that anything not allowed should be a constraint violation.

However, I don't think I'm going to get struct comparison so it doesn't
matter.
 
K

Keith Thompson

Golden California Girls said:
Interesting ...

union u {
char c;
short int si;
long int li;
volatile long long int lli;
};

So the above ...

Perhaps there was a reason K&R left the reference of a different
member of the union implementation defined. I'm not sure the
committee did us a favor by trying to define it.

I'm not sure how that follows from what I wrote. The case being
considered did not involve writing one member of a union and then
reading a different one.
 
E

Eric Sosman

Keith said:
[...]
If we're not going to allow structs containing arrays to be compared
meaningfully, it should be a constraint violation.

Everything not compulsory is forbidden?
 
K

Keith Thompson

Eric Sosman said:
Keith said:
[...]
If we're not going to allow structs containing arrays to be compared
meaningfully, it should be a constraint violation.

Everything not compulsory is forbidden?

Everything that's an obvious error, easily detectable at compile time,
should be detected at compile time. If, as you suggested, comparison
of structs containing arrays should compare the array members after
they decay to pointers, then ``s1 == s2'' is simply an elaborate way
to write ``0''.

But if struct comparison is to be allowed, then array members should
be compared element by element.
 

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,982
Messages
2,570,189
Members
46,735
Latest member
HikmatRamazanov

Latest Threads

Top