struct by value

K

Kenny McCormack

Keith Thompson <[email protected]> once again went non-linear over some newbie's
somewhat injudicious use of the 's-word':
....
Having said that, most C implementations *do* use a contiguous
stack in memory -- but not all do. There are implementations
where the storage for each function call is allocated on the heap.
The assumption of a contiguous stack is neither universally correct
nor particularly useful. And even in stack-based implementations,
it's very common to pass some arguments in registers.

http://redwing.hutman.net/~mreed/warriorshtm/android.htm

Really says it all about Kiki. I think we should all just post that URL
in response to any future posts of his...

--
"The anti-regulation business ethos is based on the charmingly naive notion
that people will not do unspeakable things for money." - Dana Carpender

Quoted by Paul Ciszek (pciszek at panix dot com). But what I want to know
is why is this diet/low-carb food author doing making pithy political/economic
statements?

Nevertheless, the above quote is dead-on, because, the thing is - business
in one breath tells us they don't need to be regulated (which is to say:
that they can morally self-regulate), then in the next breath tells us that
corporations are amoral entities which have no obligations to anyone except
their officers and shareholders, then in the next breath they tell us they
don't need to be regulated (that they can morally self-regulate) ...
 
D

David Resnick

Keith Thompson  <[email protected]> once again went non-linear over some newbie's
somewhat injudicious use of the 's-word':
...


http://redwing.hutman.net/~mreed/warriorshtm/android.htm

Really says it all about Kiki.  I think we should all just post that URL
in response to any future posts of his...

http://redwing.hutman.net/~mreed/warriorshtm/troller.htm

Really says it all about Kenny. I think we should all just post that
URL
in response to any future posts of his...

If it weren't that such attention feeds his needs and he should be
just ignored. Doh!
 
S

Seebs

It seems that you and the O.P. somehow regard null pointer values
as Bad Things, poison pills in your program. That's an attitude I can't
understand, as it seems important to be able to respond to "Get another
Thing" with "No Thing there, boss." The null-valued pointer is a very
convenient device, a way to pass either "Here's a Thing" or "No Thing"
through one channel, without the burden of inventing a separate channel
for an independent "Yes/No" answer (with "No" also having the meaning
of "Just pay no attention to that Thing in the other channel; it's not
really there").

At least for the "No Thing there, boss" case, this can also be done through
exceptions, which get you away from the channel entirely. That's actually
sort of nice in some cases, as it reduces the amount of testing you have
to do before other operations -- in some cases, anyway.

That said, you still really want a way to distinguish between "I have an
object" and "I don't have an object." Even the exception-oriented languages
I use (Ruby, Python) still have a way to set an object reference to nil/None.

(I don't know Python well enough yet to comment, but in Ruby, nil is actually
an object itself, the sole instance of NilClass.)

-s
 
K

Keith Thompson

David Resnick said:
On Oct 14, 12:23 pm, (e-mail address removed) (Kenny McCormack)
wrote: [more of the same]

http://redwing.hutman.net/~mreed/warriorshtm/troller.htm

Really says it all about Kenny. I think we should all just post that
URL
in response to any future posts of his...

If it weren't that such attention feeds his needs and he should be
just ignored. Doh!

Just ignore him. We all know he's a troll; please don't feed him.
 
N

Nobody

At least for the "No Thing there, boss" case, this can also be done through
exceptions, which get you away from the channel entirely. That's actually
sort of nice in some cases, as it reduces the amount of testing you have
to do before other operations -- in some cases, anyway.

That said, you still really want a way to distinguish between "I have an
object" and "I don't have an object." Even the exception-oriented languages
I use (Ruby, Python) still have a way to set an object reference to nil/None.

FWIW, Haskell has the Maybe type constructor for this purpose:

data Maybe a = Nothing | Just a

E.g.:
Prelude> :type lookup
lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
Prelude> lookup 1 [(1,"one"),(2,"two")]
Just "one"
Prelude> lookup 3 [(1,"one"),(2,"two")]
Nothing

Passing a "Maybe Foo" to a function expecting a "Foo" will result in a
type mismatch, forcing you to handle the Nothing case, e.g.:

case lookup var env of
Just x -> f x
Nothing -> ...
 
J

Jon

Keith Thompson said:
Jon said:
Keith Thompson wrote: [...]
I'd say that "small" structs can sensibly be passed by value (unless
of course the function needs to modify them), and "large" structs
should be passed by pointer for performance reasons.

I have no particular guidance to offer about the dividing line
between
"small" and "large". I'd say that anything no larger than a pointer
is certainly "small", but beyond that ...

I know that's not very helpful.

How about this "rule": if it's a primitive, pass by value, else don't.
There are not stack frame guarantees that allow further portable
("overall") rules of thumb, as far as I know.

What exactly do you mean by "primitive"?

If you mean that scalar types (i.e., numeric and pointer types)
should be passed by value and other types (arrays, structs, unions)
should be passed by pointer, well, that's a consistent rule,

That's what I meant.
but it
fails to take advantage of C's ability to pass and return structs
and unions by value.

I wouldn't pass a struct by value, simply to achieve greater style
consistency.
If you have a small struct type, I see no reason not to pass it
by value if that satisfies the semantics you need.

It's just one more step toward self-documenting code if using my rule.
 
J

Jon

Eric Sosman said:
It seems that you and the O.P. somehow regard null pointer values
as Bad Things, poison pills in your program.

Actually, I was making a case for pass-by-reference, I believe.
That's an attitude I can't
understand, as it seems important to be able to respond to "Get another
Thing" with "No Thing there, boss." The null-valued pointer is a very
convenient device, a way to pass either "Here's a Thing" or "No Thing"
through one channel, without the burden of inventing a separate channel
for an independent "Yes/No" answer (with "No" also having the meaning
of "Just pay no attention to that Thing in the other channel; it's not
really there").

I was saying that, given just pass-by-ptr, doesn't analyze the situation
and try to decide which is more often used (ptrs can be null or ptrs
can't be null) and therefor requires all those null ptr checks and that
is a PITA but doesn't have to be so.
A pointer type that cannot be null seems to me crippled,

In lieue of references, I suggested a not_null keyword or the converse
keyword, but I'd opt for references (that can't be "null").
 
J

Jon

Keith Thompson said:
(Why did you put the word "language" in quotation marks?)

I don't remember. Probably because it lacks a lot of decidedly required
elements to be considered a modern language.
You seem to be new here, so you may not be aware that we've
discussed this at great and tedious length before.

Which part? The subject or ...?
No, C is not a stack-based language.

Well then how can C be called from other languages if nothing about the
calling mechanism is defined? When I said "stack-based", I meant "it
behaves as if args are pushed right-to-left onto a stack..." etc.
The word "stack" doesn't even
appear in the C language standard. The semantics of C function calls
do imply some kind of stack-like structure, but only in the sense
that storage for function calls is allocated and deallocated in a
last-in first-out manner.

Well that's understood, but it behaves that way.
There is no implication of a stack laid
out in contiguous memory growing and shrinking in any consistent
direction.

I'll bet most every implementation does that though.
Having said that, most C implementations *do* use a contiguous
stack in memory -- but not all do.
See.

There are implementations
where the storage for each function call is allocated on the heap.

I don't need to be *that* portable. I focus on common practice, so my
assumptions seem well-founded.
The assumption of a contiguous stack is neither universally correct
nor particularly useful.

It is if you are just learning Assembly! =)
And even in stack-based implementations,
it's very common to pass some arguments in registers.

Seems like those implementations would be closed to interoperability with
most other languages and tools for they obliterate the easy-to-understand
concept of arguments passed on "the stack".
 
I

Ian Collins

That's what I meant.


I wouldn't pass a struct by value, simply to achieve greater style
consistency.

That's not the only reason to pass or return by value. Modern compilers
(especially those that share code generation with C++) are very good at
optimising away the copies.
It's just one more step toward self-documenting code if using my rule.

How?
 
J

Jon

Ian Collins said:
That's not the only reason to pass or return by value. Modern
compilers (especially those that share code generation with C++) are
very good at optimising away the copies.

Considering that seems like premature optimization and time spent
thinking about that is time not spent on domain-level thinking, which is
a bad thing.

It is known that things passed by value are going to be primitive types
and not structures. A minor thing, but every little bit helps.
 
I

Ian Collins

Considering that seems like premature optimization and time spent
thinking about that is time not spent on domain-level thinking, which is
a bad thing.

Going to the effort of passing by pointer is more like a premature
optimization (why else would you do it?).
It is known that things passed by value are going to be primitive types
and not structures. A minor thing, but every little bit helps.

Why does that matter?

If you were performing complex arithmetic, would you pass the two
complex number by value, or pointer?
 
J

Jon

Ian Collins said:
Going to the effort of passing by pointer is more like a premature
optimization (why else would you do it?).

Because if you analyze the "extreme" case, you would pass those kinds of
things by ptr (by reference if in C++) so there is a "correctness" to the
"madness".
Why does that matter?

If you were performing complex arithmetic, would you pass the two
complex number by value, or pointer?

Probably by value because it "feels" like a primitive type. The *general*
rule is to pass structs by reference, there are exceptions to the rule,
but performance would probably not be part of the analysis in considering
when to make an exception to the rule just because the struct was small.
 
S

Seebs

So then, unportably since a simple change of the compiler would muck-up
the assumptions.

Er, duh.

You're talking about trying to write assembly to interface with another
language's calling conventions. This is an EXEMPLAR of "unportable".

That said, in most cases, the ABI stays pretty stable for a fairly
long time, meaning that you should be able to predict the argument
passing behaviors for any specific function for any specific target
quite well.
I think ever-increasingly as I find things that I don't like about the
compiler implementations or the language specification.

Down this path lies madness. You're going to waste more time than you
save, by several orders of magnitude.

-s
 
S

Seebs

Well then how can C be called from other languages if nothing about the
calling mechanism is defined?

The same way any other languages can call each other -- by definitions
of calling conventions being provided *by the platform*. The ABI,
usually.
I'll bet most every implementation does that though.

The majority, yes, but... Not as consistently as you might think. For
instance, if you are assuming that the local variables for each function
are contiguous, you're just plain wrong. That's not true on several
ABIs that are in fairly widespread use.
It is if you are just learning Assembly! =)

Actually, then it's even worse. If you make up silly theories about
how things really work in C, it usually has no major effect. If you do
that in assembly, you're doomed.
Seems like those implementations would be closed to interoperability with
most other languages and tools for they obliterate the easy-to-understand
concept of arguments passed on "the stack".

Not at all.

ABI for the win. As long as you define WHICH registers are to be used,
and how, and in what order, and when, it's perfectly suitable. It doesn't
matter what the rules are, so long as they're documented and people can
follow them.

-s
 
E

Eric Sosman

Keith Thompson said:
Jon said:
[... different mechanisms for struct parameters and func values ...]
OK, then, here is something I don't understand: if those compilers do
that, how am I supposed to write an assembly language
prologue/epilogue
to interface such things if arguments are not passed on the stack?

The same way you would in any circumstances: by understanding the
calling convention used by the compiler, which is probably based
on the ABI for the platform.

So then, unportably since a simple change of the compiler would muck-up
the assumptions.

You began with the *goal* of writing non-portable code, and
now you raise the objection that it's non-portable? "Doctor, it
hurts when I stick my thumb in my eye."

Also, you've missed the meaning of the last phrase in Keith's
post: "... which is probably based on the ABI for the platform."
Unless you're writing to the bare metal with no O/S in sight, a
platform will have established conventions for communication between
a programming language and the O/S services, and often for at least
some of the communication between different languages. A compiler
for the platform will perforce follow those conventions, or it will
find itself unable to get along with other languages and/or the O/S.

It is true that a platform ABI may omit some items of interest
to some of its supported languages. An O/S whose services deal only
in integers and addresses and bags of bytes may not prescribe how
floating-point values should be passed around. Few platforms say
how a Lisp-style List is passed, or even encoded. And sometimes it
may happen that language X has an X-specific type and that two X
compilers manage the type differently, so components built by the
gxx and MSX compilers don't play nicely together. Well, that's life:
If you want to join the games, you have to learn the rules.
I think ever-increasingly as I find things that I don't like about the
compiler implementations or the language specification. (I'm learning
Assembly Language, so if the lights go out over there, it's probably just
me tweaking my prologue/epilogue code) =).

If C is not the right language for your purposes, use a different
language. That will be far more productive than whining -- and it will
also be more productive than trying to twist C to your liking, whether
with macro magic, assembly abracadabra, or library lunacy. Trust me:
I've been there, I bear the scars.
 
K

Keith Thompson

Jon said:
Probably by value because it "feels" like a primitive type. The *general*
rule is to pass structs by reference, there are exceptions to the rule,
but performance would probably not be part of the analysis in considering
when to make an exception to the rule just because the struct was small.

I suggest that "pass small types by value, large types by pointer" is a
more sensible rule. All scalar (what you call "primitive") types are
small; some struct and union types small.

I agree with Ian that passing small structures by pointer just because
they're pointers is premature optimization.

(Of course anything, scalar or not, can sensibly be passed by pointer
if the called function needs to modify its value. Or you can pass
it by value and return the new value.)
 
K

Keith Thompson

Jon said:
I don't need to be *that* portable. I focus on common practice, so my
assumptions seem well-founded.

If you write straight C code that doesn't make any assumptions beyond
what's guaranteed by the standard, your code can be 100% portable to
all conforming C implementations. You don't have to know *or care*
whether function activation records are allocated on a stack or a
heap, or whether arguments are passed on the stack, in registers,
or by carrier pigeon. You pass expressions as arguments, you refer
to the corresponding parameters by name inside the function, and
it all just works -- as long as you don't do something that goes
beyond the guarantees the language gives you.
It is if you are just learning Assembly! =)

comp.lang.c is not a good place to learn assembly language.

C is not a very high-level language, but it's substantially higher than
assembly language. Most of things you're worrying about (and that
I'm sure are very valid concerns if you're writing assembly language)
are irrelevant in C, because they're below the level of abstraction
defined by the C language.

If you want to program in assembly language and worry about registers
vs. stacks and all that, feel free. There can be perfectly valid
reasons for choosing assembly language over C. And if you want to
tinker with the machine language prologue and epilogue generated
by your C compiler, you can do that too -- but it has to do with
your compiler, not with the C language. This is just the wrong
place to get advice about it.
Seems like those implementations would be closed to interoperability with
most other languages and tools for they obliterate the easy-to-understand
concept of arguments passed on "the stack".

Nope. (Seebs already explained this.)
 
J

Jon

Keith Thompson said:
I suggest that "pass small types by value, large types by pointer" is a
more sensible rule.

That's fine. Some order to the chaos is better than none.
All scalar (what you call "primitive") types are
small; some struct and union types small.

I agree with Ian that passing small structures by pointer just because
they're pointers is premature optimization.

Would you care to restate that?
(Of course anything, scalar or not, can sensibly be passed by pointer
if the called function needs to modify its value.

That's an addendum to the other rule, as the complex number thing may be
(it's all a matter of personal style of course, so there is no right or
wrong answer).
 
J

Jon

Keith Thompson said:
If you write straight C code that doesn't make any assumptions beyond
what's guaranteed by the standard, your code can be 100% portable to
all conforming C implementations. You don't have to know *or care*
whether function activation records are allocated on a stack or a
heap, or whether arguments are passed on the stack, in registers,
or by carrier pigeon. You pass expressions as arguments, you refer
to the corresponding parameters by name inside the function, and
it all just works -- as long as you don't do something that goes
beyond the guarantees the language gives you.

But I am planning on working outside of C and in Assembly Language.
comp.lang.c is not a good place to learn assembly language.

Note that I am interested in runtime environments and code generation at
this time because I plan on building a compiler.
C is not a very high-level language, but it's substantially higher than
assembly language. Most of things you're worrying about (and that
I'm sure are very valid concerns if you're writing assembly language)
are irrelevant in C, because they're below the level of abstraction
defined by the C language.

Unless you're writing a C compiler. =)
If you want to program in assembly language and worry about registers
vs. stacks and all that, feel free.

It's a stepping-stone to generating the assembly from a compiler.
There can be perfectly valid
reasons for choosing assembly language over C. And if you want to
tinker with the machine language prologue and epilogue generated
by your C compiler, you can do that too -- but it has to do with
your compiler, not with the C language. This is just the wrong
place to get advice about it.

I wasn't doing that (I have the Dragon book and others), but
understanding of call stacks and the like make for better programmers
IMHO (and someone has to *implement* the language afterall) so it seems
on-topic to me to discuss stacks and such even if it's just one popular
implementation of the runtime environment of C. Or is this ng limited to
just *usage* of C?
Nope. (Seebs already explained this.)

Well I'll figure it "all" out sooner or later as I implement it.
 
J

Jon

Seebs said:
The same way any other languages can call each other -- by definitions
of calling conventions being provided *by the platform*. The ABI,
usually.

I guess what I am missing is the information of how much interoperability
there actually is or isn't across hardware, OSes, compilers for the
"simple" task of calling a C function.
The majority, yes, but... Not as consistently as you might think. For
instance, if you are assuming that the local variables for each
function
are contiguous, you're just plain wrong.

I would not assume that and I have been remotely thinking about that as I
develop a scope-management strategy.
That's not true on several
ABIs that are in fairly widespread use.



Actually, then it's even worse. If you make up silly theories about
how things really work in C, it usually has no major effect. If you do
that in assembly, you're doomed.

You are assuming too much given such miniscule information about what I'm
working on.
Not at all.

ABI for the win. As long as you define WHICH registers are to be used,
and how, and in what order, and when, it's perfectly suitable.

And I *have* seen that documentation on the MS site while I was reading
about naked function calls, but even so, I guess I was hoping for more
consistency than that. I know now though. Thx for reiterating that. It
helps to just gab about things sometimes and get verification of what
things mean outside of a specific platform.
It doesn't
matter what the rules are, so long as they're documented and people can
follow them.

Got the 10000 foot view now. Now I'll ponder how much interoperability
there actually is in practice (not much apparently) and surely that will
lead to object formats and linkers probably, but that's way out there as
I just need enought tools to play in my own little sandbox. =)
 

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,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top