5

If I have the following: -

struct foo
{
  int a;
  int *b;
} bar;

void baz(struct foo qux)
{

}

Am I right in thinking that passing bar to baz() results in a local copy of bar being pushed onto the stack? If so, what kind of copy is this? in C++, I assume it would call the copy constructor, or the default copy constructor but I don't really know how this would work in C.

Does C have any notion of a default copy constructor and does this have a name? Could something be done to perform a deep copy? (hypothetically). The only way I could think of is to actually do a deep copy and then pass it to the function.

Typically, I would be passing a pointer to a foo but I'm just curious as to how it is working. Furthermore I am under the impression that passing a pointer is faster, saves memory and is the recommended course of action to take when doing this kind of operation. I would guess that it is a shallow copy; can this be changed?

ed_me
  • 3,338
  • 2
  • 24
  • 34

2 Answers2

4

Am I right in thinking that passing bar to baz() results in a local copy of bar being pushed onto the stack?

Yes.

I don't really know how this would work in C.

Substantially as it would with the default copy constructor in C++; every field of the copy is initialized with the corresponding field of the original. Of course, because of the "as if" rule the whole thing may boil down to a memcpy.

I am under the impression that passing a pointer is faster, saves memory and is the recommended course of action to take when doing this kind of operation.

For larger structs it's often the case, but it's not always like that; if you have a small struct of few small fields the overhead of copying will probably be smaller than that of indirection (also, using pointer parameters can be costly because the aliasing rules of C and C++ can prevent some optimizations).

I would guess that it is a shallow copy; can this be changed?

No, a shallow copy (blindly copy each field) is what happens with the default copy constructor (while with "deep copy" you usually mean also creating a copy of each object referenced in pointer/reference fields).

What you mean is "pass by reference", and it's not the default to allow the maximum flexibility (and for coherency with the passing of primitive types). If you want pass by reference you pass a pointer (or a reference in C++), often const if you are in just for the performance, otherwise you pass the object itself.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • p.s. there are no constructors per se in C; only in C++. In C, your data structure would simply be copied byte-for-byte. – Edward Falk Apr 18 '13 at 22:26
  • @EdwardFalk: of course, in facts I talked about *initialization*, not construction; also, the "byte-for-byte" thing is not specified by the standard, which actually says that only the named fields are guaranteed to be copied ("Unnamed members of structure objects have indeterminate value even after initialization.", C99 §6.7.8 ¶9) - i.e., the padding may or may not be copied. – Matteo Italia Apr 18 '13 at 22:31
  • Great explanation, I am guessing you cannot override the default behavior to perform a deep copy if that was required? – ed_me Apr 18 '13 at 22:37
  • 1
    @chrisw69: you can in C++ by providing your custom copy constructor; it is done routinely, e.g. in the standard library containers (`std::vector`, ...). In C, instead, there's no automatic mechanism, you have to provide your own functions (to be invoked manually) to perform a deep copy. By the way, by some this is seen as an advantage, since the "deep copy by default" policy of some objects, if not well understood, can give considerable slowdowns. Compare with Java or C#, where an object is passed by reference by default, but can optionally provide a `Clone` method to perform a deep copy. – Matteo Italia Apr 18 '13 at 22:52
  • I believe in C# and java, object references are passed by value; the ref keyword passes the object by reference, which, as far as I am aware, would be the same as passing a pointer to a pointer in a language like C or C++. As far as Clone goes, I think the ICloneable interface is not recommended as it doesn't specify whether or not the clone is deep or shallow. http://stackoverflow.com/questions/536349/why-no-icloneablet – ed_me Apr 18 '13 at 23:02
  • @chrisw69: for the references stuff: passing object references by value is the same as passing an object by reference, it's just a terminology difference (due to the fact that in C#/Java you only have object references and there are no "explicit" pointers); as for `ICloneable`, you are right, and that's the same problem you have with C++ copy constructors/assignment operators (the deep/shallow semantic can vary between classes). – Matteo Italia Apr 19 '13 at 00:38
1

Yes a local copy of bar is pushed on to the stack. and rest is commented on following working example.

    #include <stdio.h>
    struct foo
    {
        int a;
        int *b;
    } bar;
    void baz(struct foo qux)
    {
        bar.a = 2; // if its a different copy then printf on main should print 1 not 2.
        *bar.b = 5; // if its a different copy then printf on main should print 5 not 4. since the place pointer pointing to is same
    }
    int main(){
        bar.a=1;
        bar.b = (int*)malloc(sizeof(int));
        *bar.b = 4;
        baz(bar); // pass it to baz(). now the copy of bar in the stack which is what baz going to use
        printf("a:%d | b:%d\n",bar.a,*bar.b);
        //answer is  2 and 5
        /*So indeed it does a shallow copy thats why we lost the "4" stored in bar.b it did not created new space in heap to store "5" instead it used the same space that b was pointing to.
        */
    return 0;
    }
printfmyname
  • 983
  • 15
  • 30