[comp.lang.c] Portability of passing/operating on structures...

ark@alice.UUCP (Andrew Koenig) (10/15/88)

In article <8810111934.AA21941@ucbarpa.Berkeley.EDU>, U23405@UICVM.Berkeley.EDU (Michael J. Steiner) writes:
> Is it considered portable to do the following things with
> structures or unions?
>     -pass them (by value) to functions
Yes.

>     -have functions which return them
Yes.

>     -assign them (=)
Yes.

>     -test them (structures only) for equality with ==
No.

-- 
				--Andrew Koenig
				  ark@europa.att.com

rkl1@hound.UUCP (K.LAUX) (10/17/88)

In article <8810111934.AA21941@ucbarpa.Berkeley.EDU>, U23405@UICVM.Berkeley.EDU (Michael J. Steiner) writes:
> What I would like to know is (someone asked this before, I think):
>  
> Is it considered portable to do the following things with
> structures or unions?
>     -pass them (by value) to functions
>     -have functions which return them
>     -assign them (=)
>     -test them (structures only) for equality with ==
> 
>                                                  Thanks in advance,
> 
>                                                  Michael Steiner
>                                                  Email: U23405@UICVM.BITNET


	*Why* would you want to do this anyway?  It is far easier to use
pointers (and much more efficient).  You should pass a *pointer* to the
struct/union, declare functions as *returning a pointer* to the struct/union,
as far as assignment goes, you want a copy of the contents of the struct and
as for testing for equality, how can you assume that different compilers
will evaluate them properly and exactly the same?

	If you are trying to get the compiler to automatically generate code
to copy the fields of the structure, *don't*.  What you can do is declare
a pair of *unsigned char* pointers and cast the addresses of the source and
destination structures to 'unsigned char', then do a short loop to copy byte
by byte based on 'sizeof (struct)'.

	As for testing equality of the contents of two unions/structures,go
through the effort of testing field by field -  you can't go wrong this way.

--rkl

mh@wlbr.EATON.COM (Mike Hoegeman) (10/17/88)

 In article <8308@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
 >In article <8810111934.AA21941@ucbarpa.Berkeley.EDU>, U23405@UICVM.Berkeley.EDU (Michael J. Steiner) writes:
 >> Is it considered portable to do the following things with
 >> structures or unions?
 >>     -pass them (by value) to functions
 >Yes.

No

 >>     -have functions which return them
 >Yes.

No

 >>     -assign them (=)
 >Yes.

No

I've worked on alot of machines where you cannot do ANY of the above.
Maybe that's not "right". Maybe one should boycott such compilers. Any
way you look at it though it's still not portable. I would avoid
passing structures by value until more compilers allow this. Right now
there are too many that don't.

wcs@skep2.ATT.COM (Bill.Stewart.[ho95c]) (10/19/88)

In article <8810111934.AA21941@ucbarpa.Berkeley.EDU> U23405@UICVM.Berkeley.EDU (Michael J. Steiner) writes:
> Is it considered portable to do the following things with
> structures or unions?

There were a number of additions to the C language in about 1980, after
the original K&R book, which almost everyone has adopted, even most
compilers that call themselves "K&R".  The main features were void,
enums, and structure handling.  Not everybody has them, but the
following things are portable to any useful environment.

>     -pass them (by value) to functions
	Yes.
>     -have functions which return them
	Yes.  Note that the mechanism for returning the value is
	implementation-defined.  Some people put them on the stack;
	others may do arbitrarily strange things.
>     -assign them (=)
	Yes.
>     -test them (structures only) for equality with ==
	No!  The problem is that structures typically have holes in
	them caused by alignment requirements, and these holes have
	arbitrary junk in them.  When you assign one structure to
	another, this junk is generally copied, but if you assign
	values member by member you aren't changing the junk in the
	holes, so equality comparisons don't work.
-- 
#Bill Stewart, AT&T Bell Labs 2G218 Holmdel NJ 201-949-0705 ho95c.att.com!wcs
# New Jersey Division of Motor Vehicles - 
#	rising to a new level of incompetence

guy@auspex.UUCP (Guy Harris) (10/19/88)

>	If you are trying to get the compiler to automatically generate code
>to copy the fields of the structure, *don't*.  What you can do is declare
>a pair of *unsigned char* pointers and cast the addresses of the source and
>destination structures to 'unsigned char', then do a short loop to copy byte
>by byte based on 'sizeof (struct)'.

Which may be slower than the code generated by the compiler to copy the
structure....

Passing structures as arguments, returning them as function values, and
assigning structure values to other structures is part of the C language
specified by current ANSI C drafts (if it disappears in the final ANSI C
standard, I'd be damn surprised).  Some older compilers may not handle
it, but most modern compilers should.

(Note that there are compilers that support structure assignment but
don't support "unsigned char"....)

Even if you insist on not doing structure assignments, you may be better
off using your C library's byte-copy routine, if it has one ("memcpy" or
maybe "bcopy") than copying it yourself, byte by byte.  "memcpy" and
"bcopy" are usually optimized to run faster than a naive byte-by-byte copy.

>	As for testing equality of the contents of two unions/structures,go
>through the effort of testing field by field - you can't go wrong this
>way.

Especially since few, if any, compilers implement testing equality of
structures.

chip@ateng.ateng.com (Chip Salzenberg) (10/20/88)

According to mh@wlbr.EATON.COM (Mike Hoegeman):
>I've worked on alot of machines where you cannot do ANY [structures in
>assignment, as parameters and as function return values].
>Maybe that's not "right". Maybe one should boycott such compilers. Any
>way you look at it though it's still not portable.

This is a personal decision.  Do you pander to every broken compiler?  Or
do you concentrate on making the best code that will be accepted by any
correct compiler?

To each his own.

As for my opinion:  If your compiler's broken, take it up with your vendor.
-- 
Chip Salzenberg             <chip@ateng.com> or <uunet!ateng!chip>
A T Engineering             Me?  Speak for my company?  Surely you jest!
	   Beware of programmers carrying screwdrivers.

karl@haddock.ima.isc.com (Karl Heuer) (10/20/88)

In article <2685@hound.UUCP> rkl1@hound.UUCP (K.LAUX) writes:
>[re struct copy]
>	*Why* would you want to do this anyway?  It is far easier to use
>pointers (and much more efficient).

Whether it's easier depends on what you're trying to do.  Whether it's more
efficient depends on what you're doing and on how good the compiler is.
(Quality implementations will return a small struct in the registers, as is
already customary for builtin types.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

g-rh@XAIT.Xerox.COM (Richard Harter) (10/20/88)

In article <1988Oct19.192548.28438@ateng.ateng.com> chip@ateng.ateng.com (Chip Salzenberg) writes:
>According to mh@wlbr.EATON.COM (Mike Hoegeman):
>>I've worked on alot of machines where you cannot do ANY [structures in
>>assignment, as parameters and as function return values].
>>Maybe that's not "right". Maybe one should boycott such compilers. Any
>>way you look at it though it's still not portable.

>This is a personal decision.  Do you pander to every broken compiler?  Or
>do you concentrate on making the best code that will be accepted by any
>correct compiler?

>As for my opinion:  If your compiler's broken, take it up with your vendor.

	Mild flame on.  [The tone of Chip's comments irritate me.]  If
you have the option of only running on machines with compilers that you
approve of and you can tell all relevant vendors that you want their 
compilers "fixed" and they will do it at your request, then your "personal
decision" makes sense.  Bully for you.

	Yes, I pander to broken compilers.  I have to.  I write software
that runs on customers machines.  It has to work on their machines.  That
is what portability in the real world is all about -- if you can take your
code from machine A to machine B and compile and link it and have it work
correctly then it's portable; if you can't then it is not. 

	And the truth of the matter is that there is no penalty for
writing portable code -- you can still write well structured, well
documented code that is efficient.  The things that make programs non
portable are almost always frills and slop.  Things like passing structures
are frills.

	There are decision points in portability.  When we decide not to
run on system X because its compiler and operating system are too broken
that is not a personal decision -- it is a simple economic decision that
the market in system X is not worth pursuing.
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

dawson@fpssun.fps.com (Bryan Dawson ext 1184) (10/20/88)

In article <2685@hound.UUCP>, rkl1@hound.UUCP (K.LAUX) writes:
> In article <8810111934.AA21941@ucbarpa.Berkeley.EDU>, U23405@UICVM.Berkeley.EDU (Michael J. Steiner) writes:
> > What I would like to know is (someone asked this before, I think):
> >  
> > Is it considered portable to do the following things with
> > structures or unions?
> >     -pass them (by value) to functions
> >     -have functions which return them
> >     -assign them (=)
> >     -test them (structures only) for equality with ==
> > 
> >                                                  Thanks in advance,
> > 
> >                                                  Michael Steiner
> >                                                  Email: U23405@UICVM.BITNET
> 
> 
> 	*Why* would you want to do this anyway?  It is far easier to use
> pointers (and much more efficient).  You should pass a *pointer* to the
> struct/union, declare functions as *returning a pointer* to the struct/union,

[ lots of good arguments for using pointers to structs deleted... ]

	Well, since you *did* ask, there are many very useful algorithms
    for sorting, searching, building, manipulating and accessing data
    structures which are *much* easier to use if the atomic 'data
    item' is passed into a functions *by value* and if functions
    can also generate *and then return* items of this type.  For
    practical applications, this atomic 'data type' will usually
    be a structure with several fields.  Some of these algorithms
    are recursive and require a 'new' instance of the structure
    on every call.  This is done much more easily and naturally
    by passing the prior structure in *by value* modifying the
    new version and continuing.

	It is probably for good reasons like this that most good
    C compilers and the ANSI draft standard allow structures to
    be passed by value, returned from functions, and assigned as
    entities.  Test for equality is prohibited by ANSI and seldom
    supported because it is troublesome to implement when the
    structures in question are padded internally and thus have
    undefined 'holes' within them which preclude a byte-for-byte
    test for equality.

	Please note that I do not disagree that pointers to 
    structures are very natural and useful and can often be a
    more efficient method for manipulating structures with
    functions.  I am merely pointing out that the use of
    structures as full-fledged entities sometimes has value
    which outwieghs the efficiency penalty for such use.
    It is sometimes important to note (especially in C) that
    efficiency is only ONE of MANY constraints on good program
    design.  In situations where efficiency is not critical,
    I'll take readable, maintainable, simple and natural
    code which clearly follows the chosen algorithm any day...
-- 
============================================================================
= Bryan Dawson   tektronix!fpssun!dawson      ...The story you have just   =
=  read is true, reality was subsequently changed to protect the innocent. =
============================================================================ 

chip@ateng.ateng.com (Chip Salzenberg) (10/25/88)

According to g-rh@XAIT.Xerox.COM (Richard Harter):
>       Yes, I pander to broken compilers.  I have to.

If I must work with a specific broken compiler, then I do the same: pay for
play has its own rules.  I was, of course, referring to situations where
there *is* a choice, such as when writing free software, in which case I
don't worry about it.

>	And the truth of the matter is that there is no penalty for
>writing portable code [...]

But there *is* a penalty in pandering to broken compilers.  If, for example,
a compiler breaks on:

	foo(s) short s;
	{
	    short *sp = &s;
	    int i = *sp;

	    printf("%d\n", i);
	}

Then you have to invest time and effort into avoiding a language construct
-- taking the address of a function parameter -- that should have worked.
(I know that some compilers do, in fact, break this contruct.) I'm sure all
will agree that spent time and effort are just as much a "penalty" as
execution time.
-- 
Chip Salzenberg             <chip@ateng.com> or <uunet!ateng!chip>
A T Engineering             Me?  Speak for my company?  Surely you jest!
	   Beware of programmers carrying screwdrivers.

jas@ernie.Berkeley.EDU (Jim Shankland) (10/25/88)

In article <7356@ihlpl.ATT.COM> knudsen@ihlpl.ATT.COM (Knudsen) writes:
>This is my main objection to structure and array assignment and
>passing.  Aside from being hideously wasteful of time and stack space,
>they permit common typo errors (omission of &) to go undetected.

Nonsense.  Lint your code.

-----
Jim Shankland
jas@ernie.berkeley.edu

"I've been walking in a river all my life, and now my feet are wet."

g-rh@XAIT.Xerox.COM (Richard Harter) (10/25/88)

In article <1988Oct24.172209.27031@ateng.ateng.com> chip@ateng.ateng.com (Chip Salzenberg) writes:
>According to g-rh@XAIT.Xerox.COM (Richard Harter):
>>       Yes, I pander to broken compilers.  I have to.

>If I must work with a specific broken compiler, then I do the same: pay for
>play has its own rules.  I was, of course, referring to situations where
>there *is* a choice, such as when writing free software, in which case I
>don't worry about it.

	But how often do you really have a choice of compilers?  If you have
a small machine and you buy your compiler, yes, then you have a choice of
compilers.  If you are in an environment where you have a choice of machines,
each with its own compiler, again you have a choice. 

	More to the point, if your coding practices use constructs and
techniques that break compilers, you are laying problems up for yourself
and others in the future.  

	Different people have different situations.  I have high portability
requirements; others do not.  But I have been at this trade for a long time
and I have noticed that life is a great deal simpler if you, as a matter
of routine, assume that code may have to be ported.  

>>	And the truth of the matter is that there is no penalty for
>>writing portable code [...]

>But there *is* a penalty in pandering to broken compilers.  If, for example,
>a compiler breaks on:
>
>	foo(s) short s;
>	{
>	    short *sp = &s;
>	    int i = *sp;
>
>	    printf("%d\n", i);
>	}

>Then you have to invest time and effort into avoiding a language construct
>-- taking the address of a function parameter -- that should have worked.
>(I know that some compilers do, in fact, break this contruct.) I'm sure all
>will agree that spent time and effort are just as much a "penalty" as
>execution time.

	This is a good example.  It contains two coding techniques that I
do not use and, indeed, would not think of using.  The first is taking
the address of a function parameter.  The second is initialization to a
dynamic quantity.  Why do these things?  The latter is just asking for
trouble.  The only reason for taking the address of a function parameter
that I can think of is that you are calling a routine that requires a
pointer rather than a value.  I don't know whether they are "supposed"
to work or not and I don't care; they are dubious constructs.

	More to the point, I do not agree and I am quite sure that many
experience programmers do not agree with your assessment.  There is a cost
associated with learning a language -- the cost for learning to write it
in a clean style is not much different than that of learning to write it
any old way.  Once you've learned what you can and cannot do, coding is 
simply a matter of writing within the language.  Writing in language X
is no harder or easier than writing in language Y, unless a language lacks
a fundamental feature.  Writing clean C is no harder than writing dirty C.
But the price of writing dirty code is high -- debugging dirty code is a
real pain, and it is a gratuitously assumed cost.  

Patient: "Doc, it hurts when I hit myself on the head with a hammer."
Doctor:  "Don't hit yourself on the head with a hammer."
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

mouse@mcgill-vision.UUCP (der Mouse) (10/28/88)

From <8810111934.AA21941@ucbarpa.Berkeley.EDU>,
 by U23405@UICVM.Berkeley.EDU (Michael J. Steiner)
>> Is it considered portable to do the following things with
>> structures or unions?
>>     -pass them (by value) to functions
Yes.
>>     -have functions which return them
Yes.
>>     -assign them (=)
Yes.
>>     -test them (structures only) for equality with ==
No.

From <2685@hound.UUCP>, by rkl1@hound.UUCP (K.LAUX)
> *Why* would you want to do this anyway?  It is far easier to use
> pointers (and much more efficient).

It is often easier to use by-value, and in some cases it isn't much, if
any, less efficient.  If you use pointers all the time, you have to
manage the storage for the pointers to point to.  If you use structures
by value, you can let the usual variable allocation mechanisms handle
this for you.  An example might be complex numbers.

> If you are trying to get the compiler to automatically generate code
> to copy the fields of the structure, *don't*.

Why not?  The compiler knows better than you do whether it can use
clever machine-dependent tricks to get a fast copy....

> What you can do is declare a pair of *unsigned char* pointers and
> cast the addresses of the source and destination structures to
> 'unsigned char', then do a short loop to copy byte by byte based on
> 'sizeof (struct)'.

This will probably.  But on a machine with an addressing unit larger
than a char, you will pay a heavy efficiency penalty for this.  On
machines with a fast block move instruction, this almost certainly
won't use it, and you take a performance hit again.  That's what I
meant above: the compiler knows all these tricks, or ought to, and can
use the fastest one that applies.

> As for testing equality of the contents of two unions/structures, go
> through the effort of testing field by field -  you can't go wrong
> this way.

More to the point, you can go wrong if you do a byte-compare, because
of holes.  It seems reasonable to me for equality testing of structures
to generate code to compare all the members, but there are difficulties
like what to do when with arrays of char; presumably this is why this
isn't done.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

jgp@moscom.UUCP (Jim Prescott) (11/03/88)

In article <2685@hound.UUCP> rkl1@hound.UUCP (K.LAUX) writes:
>	If you are trying to get the compiler to automatically generate code
>to copy the fields of the structure, *don't*.  What you can do is ...
>a short loop to copy byte by byte based on 'sizeof (struct)'.

Does ANSI guarantee that this would even work?  I thought that attempting to
read or write holes in structures was undefined (because they could actually
be mapped out of your address space on some implementations).  Thus anything
that tried to look at all sizeof() bytes could fail.  Is this correct?
-- 
Jim Prescott	moscom!jgp@cs.rochester.edu
		{rutgers,ames,harvard}!rochester!moscom!jgp