structs passed by value

M

Mantorok Redgormor

When you pass a struct by value how is it
suppose to be handled internally?

Passing say an int by value is simple
since the contents of which can be held by a register
or some other means, allocating space on the stack etc

But for a struct? Since structs can describe more than
one type, it is confusing to think how it is handled
internally when passing by value to other functions.
 
J

Jack Klein

When you pass a struct by value how is it
suppose to be handled internally?

However the compiler wants to.
Passing say an int by value is simple
since the contents of which can be held by a register
or some other means, allocating space on the stack etc

The language standard does not specify how anything is passed to a
function, merely that the called function gets a copy. Furthermore,
each call to the function gets its own unique copy. If a function
that receives a structure by value calls itself recursively, the
second invocation must receive a fresh copy.
But for a struct? Since structs can describe more than
one type, it is confusing to think how it is handled
internally when passing by value to other functions.

It makes no difference at all what the number and types of the members
of the structure are, conceptually. An implementation must provide
memory somewhere (perhaps on the "stack", if the implementation uses
one, perhaps allocated with malloc()), and then it can copy the
original structure into the new block of memory with memcpy() or
something that works similarly.

It is highly unlikely that the compiler would actually generate the
copy of the structure by assigning member-by-member (although it
could, as nothing in the language standard prevents it). In most
cases, memcpy() or some intrinsic equivalent byte copying routine is
used. So the low level code that makes the copy does not need to know
anything at all about the members or their types, merely the size of
the structure.

--
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++ ftp://snurse-l.org/pub/acllc-c++/faq
 
C

Chris Torek

When you pass a struct by value how is it
suppose to be handled internally?
[/QUOTE]

However the compiler wants to.

Indeed.

There are only two commonly-used mechanisms, though: either the
caller makes a copy, or the callee makes a copy.

One of the two must do so unless the compiler can guarantee that
"not making a copy" is indistinguishable to the program from "making
a copy". That includes things like &param != &orig, e.g.:

#include <stdio.h>

struct S { int a[1000]; };

void f(void);
void g(struct S);

static struct S *some_s;

void f(void) {
struct S localvar;

some_s = &localvar;
g(localvar);
}

void g(struct S arg) {
if (some_s == &arg)
puts("aha, compiler failed to copy it!");
else
puts("compiler apparently made a copy");
}

int main(void) { f(); return 0; }

If neither f() nor g() copies localvar, g() will detect the
compiler's failure -- unless the compiler is REALLY clever and
changes the if to "if (0)" even though some_s == &arg. :)

Normally this sort of optimization -- avoiding the copy, then
rewriting comparisons to pretend the pointers are not equal even
though they are -- is not even attempted. Experienced C programmers
know that the copy happens and is expensive, so they avoid paying
the price by passing &localvar instead. This relieves some of the
pressure on compiler-writers, who can say: "Ah, well, no experienced
programmer would code it that way without good reason, so there is
no reason for me to bother trying to optimize it." This then feeds
back into programmers' experience: copies are expensive, so that
experienced programmers do not make them, so that compilers do not
optimize them, so that programmers do not make them, so....

(Implementations that have stacks often, but not always, have
"caller makes the copy" rules, even though "callee makes the copy"
tends to be more efficient. In particular, compilers that use a
callee-copies rule can remove the copy using nothing but simple
local information: if the copy's address is never taken and the
copy is never modified, the copy need not be made. In other words,
if g() secretly gets a "struct S *argp", all "arg.foo"s can become
"argp->foo"s if there is no &arg and no arg.foo=val. Otherwise,
g() can have a local "arg" variable and do "arg = *argp" and all
will be fine.)
 
C

CBFalconer

EventHelix.com said:
The compiler does a memory copy of the passed structure to the
stack before the function is called. In C the calling function
is responsible for pushing all the parameters on the stack.

The following article discusses the stack copying mechanism:

C does not have a stack. Some C implementations do. The actual
mechanism used is OT on c.l.c, although examples of differenct
systems may be of interest to some.

Compilers do not do copying. They may generate code to do so.

Please learn to differentiate example mechanisms from the actions
required. The latter are detailed in the C standard.

Jack Klein and Chris Torek gave accurate answers at least 5 hours
before you gave your flawed one. I guess we can attribute your
missing those to your use of google groups, with the attendant
delays.
 
E

EventHelix.com

OOPS! should have said compiler generated code does a memory copy...

Sandeep
 
J

Jack Klein

OOPS! should have said compiler generated code does a memory copy...

Sandeep

Still not necessarily correct. On many popular RISC architectures
today, a small structure might be passed entirely in registers. ARM
or PowerPC, for example, in fact most modern architectures other than
the register starved x86.

--
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++ ftp://snurse-l.org/pub/acllc-c++/faq
 
D

Dave Thompson

Indeed.

There are only two commonly-used mechanisms, though: either the
caller makes a copy, or the callee makes a copy.

One of the two must do so unless the compiler can guarantee that
"not making a copy" is indistinguishable to the program from "making
a copy". <snip example> In particular, compilers that use a
callee-copies rule can remove the copy using nothing but simple
local information: if the copy's address is never taken and the
copy is never modified, the copy need not be made. <snip>

Surely (don't call me <G>) "and the secretly-uncopied struct cannot
be, or at least is not, modified through another path". Sometimes
(often?) implemented as the rather stricter "and there is no store to
any lvalue of the same (struct) type (unless local hence known
distinct) nor to one of character type (that might alias the struct),
nor intervening call to another function that might do so."

- David.Thompson1 at worldnet.att.net
 
C

Chris Torek

Surely (don't call me <G>) "and the secretly-uncopied struct cannot
be, or at least is not, modified through another path". Sometimes
(often?) implemented as the rather stricter "and there is no store to
any lvalue of the same (struct) type (unless local hence known
distinct) nor to one of character type (that might alias the struct),
nor intervening call to another function that might do so."

Er, yes. This is the same "alias analysis" problem an optimizer
faces in dealing with arrays, for instance. You cannot quite use
"same struct type", because something like this:

void f1(struct byvalue val) {
extern int *p;

printf("before: val.someint = %d\n", val.someint);
*p = 42;
printf("after: val.someint = %d\n", val.someint);
}

must not show val.someint changing to 42. Unlike the (pseudo)
array parameter problem, however, an optimizer *can* simply cache
val.someint -- if this were instead:

void f2(int arr[]) {
... same code as before ...
}

and we were printing arr[3], the optimizer *would* have to
reload arr[3] in case the reference via *p changed it to 42.

In other words, it is OK if some or all of the "secretly-uncopied"
(as you put it) struct parameter is eventually modified, *provided*
the compiler caches (copies) the potentially-modified values first.
A write through an otherwise unconstrained lvalue -- such as *p
above -- whose type matches any element type, including (recursively)
any sub-structures or unions within the structure, or through one
of character type, forces at least some copying. Calls to
un-expandable functions that might do this likewise forces a copy.

(It is not all that uncommon to have a leaf function -- one that
makes no additional function calls -- that also uses nothing but
local variables that cannot alias the original structure that the
compiler is "secretly-not-copying". But it is not all that
common either, and -- as I noted earlier -- there is a sort of
chicken-and-egg problem that leads programmers not to pass large
structures by value because it is inefficient because compilers
do not optimize it because programmers do not do it.)
 

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
474,129
Messages
2,570,770
Members
47,329
Latest member
FidelRauch

Latest Threads

Top