Lew Pitcher <
lew.pitcher@digitalfreehold.ca> writes:
On Fri, 02 May 2025 20:44:45 +0000, Kenny McCormack wrote:
>
In article <51ba1k5h5lkj75qvfuj0ferlddpb6bi0n8@4ax.com>,
Barry Schwarz <schwarzb@delq.com> wrote:
...
>
Wouldn't it be quicker and easier to write a simple program to test
this rather than wait for someone to compose a response? You already
have 90% of the code written.
>
That depends on what the actual personal goal is.
>
Somehow, I don' think Lew's goal(s) (and reason for posting) are what you
think they are.
>
My goal is to add a feature to one of my older programs. The feature will
require passing a number of objects down and up the function call chain, and
I'm looking at various alternatives to accomplish this.
>
ATM, it looks like I either go with "global" objects, or I change the
argument list of several functions to include about 5 more objects each.
Because these additional objects all relate to one another, I'm thinking
of grouping them in a struct, and passing a pointer to the struct back
and forth.
>
But... I remembered this (apparently) seldom-used feature of being able
to pass a struct by value up and down the chain (down, as a function
argument, up as a function return value). As I've not done this before,
and I've not /seen/ it done in other peoples code, I wonder how viable
a solution it might be.
It works. As far as correctness goes, there is no reason not
to use it.
More to say about whether and how to use it, see below.
If it is legal, then why isn't it used more often? Is it a readability/
maintainability issue, or is it something else?
I expect there are at least three reasons.
One: old habits die hard. People who learned C during the K&R
era (and I am one) discovered that struct types are second-class
citizens: they couldn't be initialized when local to a function,
or assigned to at all, to say nothing of being function arguments
or return values. It became an ingrained habit: Don't Do That.
Two: old habits die hard part two. The rules for struct types
improved in C89/C90, but they still weren't first class, because
member initializers had to be constant expressions. And if
initialization didn't work, who knows what else didn't work?
What became ingrained was not fact but superstition: struct
types don't work except in simple ways.
Three: performance concerns. Having a parameter or a return
value be of struct type could be expensive in either space or
time or both. In some cases the performance hit is subtle;
for example, a compiler optimization that works with scalar types
sometimes fails with struct types, even when the struct is no
bigger than the scalar type used where the optimization succeeds.
All that said, let me give some personal reactions.
I don't mind using struct types for parameters or return values,
when the need arises, but I find it almost never does. I admit
to having a small bias against it, for no particular reason, so
there is an energy barrier against using struct types in such
cases, but the barrier is not insurmountable.
Assuming one is using C99 or later (and these days I basically
never use C90), there is a technique worth knowing that gets most
of the benefit of using struct type parameters without actually
giving structs as arguments. The idea is to use pointers rather
than actual structs as arguments, with the struct "arguments"
being locals in the calling functions. What really makes this
approach work is compound literals. For example:
#include <stdlib.h>
typedef struct { char *p; unsigned u; } PU;
void use_PU( PU * );
void
call_use_PU( unsigned n ){
PU it[1] = {{ 0, 0 }};
use_PU( it );
use_PU( (PU[1]){{ malloc(n), n }} );
}
In effect the function call_use_PU() is giving struct arguments,
but the actual struct objects are held locally in the caller; in
the first case the argument value is the address of a ordinary
local object, and in the second case the argument value is the
address of a compound literal object. As far as memory footprint
goes this approach is the same as giving an actual struct for the
argument, but without the complications of putting a struct
object in the physical argument list.
Having struct locals be contained in arrays of length 1 has another
benefit, in that the -> operator can be used everywhere, without
regard to whether the struct is a local or a pointer parameter. I
find the uniformity helps streamline program development.
For what it's worth, those are my thoughts on the question.