0

Edit: Fixed bug that was mentioned in the comments and the problem still exists, so this question isn't really a duplicate - just that my C programming skill is not very good at this point, the solution given below answers the question and doesn't address the local variable bug.

Warning: fairly new to C

I understand that when we assign a struct to another struct, a shallow copy is performed. However, I was unable to understand the result of why this happened:

Suppose the following where I try to initialize a struct's, call it a Type2 struct's, Type1 member by using the assignment operator, then a shallow copy should have been performed. This means that the address of the Type2 member is copied:

typedef struct {
    uint8_t someVal;
} Type1

typedef struct {
    Type1 grid[3][3];
} Type2

//Constructor for Type2 "objects"
Type2 Type2_Constructor(void) {
    Type1 empty = {.value = o}
    Type2 newType2;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++) {
            //Shallow copy empty struct
            newType2.grid[i][j] = empty;
        }
    return newType2;
}

int main (void) {    
    Type2 type2Object = Type2_Constructor();   
    for (int i = 0; i < 3; i ++) 
        for (int j = 0; j < 3; j++){
            printf("%u",type2OBject.grid[i][j].value);
            printf("\n\r%p\n\r",&(type2Object.grid[i][j].value));
            printf("\n\r");
        }   
    return 0;
}

I would expect to see:

0
0xMemoryLocation

0
0xMemoryLocation

0
0xMemoryLocation

.
.
.

In fact, what I am seeing is something like this where the address is incremented by 2 bytes:

0
0x7fff57eaca18

0
0x7fff57eaca1a

0
0x7fff57eaca1c

0
0x7fff57eaca1e

0
0x7fff57eaca20

.
.
.

Since shallow copy is supposed to copy the address directly, why are &(type2Object.grid[i][j].value) different?

Thanks for all the help!

Alex
  • 3,441
  • 4
  • 18
  • 30
  • Can you please post a [Minimal, Complete, Verifiable Example](http://stackoverflow.com/help/mcve)? – Michael Foukarakis Oct 25 '16 at 11:37
  • 1
    read again what shallow copy is. Hint: the difference between shallow and deep copy is relevant only when you have pointer members, which you don't have. – bolov Oct 25 '16 at 11:37
  • 2
    UB - you can not return pointer to local data: `Type2 * newType2Ptr = &newType2; return newType2Ptr;` – Sergio Oct 25 '16 at 11:39
  • Returning that pointer is the cause of the bug(s). – Lundin Oct 25 '16 at 13:46
  • Fixed that bug in the code, still same problem so I am guessing that bug didn't affect anything before. I tested this by verifying that whatever default value I set for Type1.val, it would be printed out. – Alex Oct 25 '16 at 13:50

3 Answers3

5

Think of pointers. If you have two pointer variables of the same type, lets call them a and b. If you do a = b you do a shallow copy, you copy only the actual pointer b to a, not the memory that b points to. This means that both a and b points to the same memory.

A deep copy would copy the contents of what b points to, into some newly allocated memory. A deep copy would lead to a and b point to different memory.


Taking your structure:

typedef struct {
    Type1 grid[3][3];
} Type2

If you have

Type2 a;
Type2 b = { ... };  // Some initialization, not relevant exactly what

then an assignment

a = b;   // Copy structure b to a

This is a shallow copy. But because the data of the structure is an array the whole array is copied, and it looks like a deep copy.

If there was a pointer inside the structure only the pointer would be copied. Another example, with a new structure:

typedef struct {
    char *pointer;
} Type3;

Type3 a;
Type3 b = { "abcd" };

a = b;

printf("a.pointer = %s\n", a.pointer);
printf("b.pointer = %s\n", b.pointer);

The code above will print the same string for both a.pointer and b.pointer. But it is the same pointer. If we add a new printout of the pointers:

printf("a.pointer = %p\n", (void *) a.pointer);
printf("b.pointer = %p\n", (void *) b.pointer);

The above two lines would after the assignment print the same value. Both pointers would be pointing to the same memory. It is a shallow copy.

Also, a structure assignment like the ones shown above is no different thatn doing e.g.

memcpy(&a, &b, sizeof a);

Actually I think your confusion is that you think referencing another structure is like references in Java or C# or C++, when it's not.

Each element in the grid array in the Type2 structure is a unique instance of the Type1 structure. And as unique instances they all occupy different memory, leading to the addresses you print being all different.

When you do

newType2.grid[i][j] = empty;

you copy the contents of the empty structure instance, into the structure instance newType2.grid[i][j]. Using the memcpy call shown above, what the assignment does is really

memcpy(&newType2.grid[i][j], &empty, sizeof newType2.grid[i][j]);

It does a bitwise copy of the contents of empty.


About the difference between a pointer an an array, consider the following definitions:

int a[] = { 1, 2, 3, 4 };
int *p = a;

In memory it would look something like this:

+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
^
|
+---+     
| p |
+---+

That is, you have the array and its contents taking up space enough for four int values. Then you have the pointer p which points to the first element in the array (arrays decays to pointers to their first element, in the case of the array a above, using a as a pointer is equal to &a[0]).

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Sorry to bring this up so late but I was re-reading this answer and noticed the sentence "But because the data of the structure is an array the whole array is copied, and it looks like a deep copy." and "If there was a pointer inside the structure only the pointer would be copied." But I thought C arrays are just pointers, so if today I had Type1 * grid[3] then that would be a shallow copy? – Alex Jan 31 '17 at 05:06
  • @lpbug A pointer is something which *points* to some chunk of memory. An array *is* a chunk of memory. Added a section about it at the end of the answer. – Some programmer dude Jan 31 '17 at 06:51
3

You seem to be misunderstanding what shallow/deep copy is. A shallow copy would copy each member (or do a bitwise copy if you will). A deep copy would also copy the owned resources of the struct.

Let's start with a simple example:

struct X {
   int val;
};

X x1 = {24};
X x2 = {42};

x1 = x2;

Yes, the build-in assignment operator operator = does perform a shallow copy. That means the above is equivalent with:

x1.val = x2.val;

In this case as X has no external resources it owns so it is also considered a deep copy because everyting that x2 owns is copied to x1. This is a consequence of the fact that X does not own any external resources.

The importance of shallow/deep copy comes into play when X would own an external resource/ E.g.:

struct X {
   int *my_very_own_external_value;
};

X x1, x2;
x1.my_very_own_external_value = malloc(sizeof(int));
*x1.my_very_own_external_value = 24;

x2.my_very_own_external_value = malloc(sizeof(int));
*x2.my_very_own_external_value = 42;

A shallow copy would be this:

x1 = x2;

This would be equivalent to this:

x1.my_very_own_external_value  = x2.my_very_own_external_value;

Which if you understand pointers means that x1 lost the address of it's allocated memory (who holds 24) and now both x1 and x2 have pointers to the memory allocated for x2, i.e. the one that stores 42;

If you want a deep copy you would need to write:

free(x1.my_very_own_external_value);
x1.my_very_own_external_value  = malloc(sizeof(int));
*x1.my_very_own_external_value  = *x2.my_very_own_external_value;

Now you have copied the memory (external resource) that x2 owns externally.


disclaimer: I might have written in C++ style

bolov
  • 72,283
  • 15
  • 145
  • 224
0

You code has a bug:

//Constructor for Type2 "objects"
Type2 * Type2_Constructor(void) {
    Type1 empty = ...;
    Type2 newType2;
    ... do something ...
    Type2 * newType2Ptr = &newType2;
    return newType2Ptr;
}

newType2 is a local variable - a struct that is defined on stack. Then you are returning its address to the calling function. Once the you exit the Type2_Constructor function, its stack is no longer in your hands.

When you print values from this area you get all sorts of things (you'd hope to get a core dump, but sadly, that's not always the case).

kliteyn
  • 1,917
  • 11
  • 24
  • I see your point, could you offer a way to resolve this bug? I've been looking at other questions regarding constructor/factory functions and have not found any satisfactory answers. – Alex Oct 25 '16 at 12:57
  • I fixed that bug in the edit and the same problem still exists, so I think I just lucked out and the stack wasn't overwritten upon return. – Alex Oct 25 '16 at 13:51
  • Re. access to local variable of another function - as pointed out, you can find the answer here: http://stackoverflow.com/questions/4570366/pointer-to-local-variable – kliteyn Oct 25 '16 at 15:00