lpbug lpbug - 1 month ago 17
C Question

C Shallow Copy Confusion

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;
}
Type2 * newType2Ptr = &newType2;
return newType2Ptr;
}

int main (void) {
Type2 type2Object = *Type2_newType2();
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!

Answer

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.

Comments