views:

346

answers:

2

I have a structure containing character arrays with no any other member functions. I am doing assignment operation between two instances of these structures. If I'm not mistaken, it is doing shallow copy. Is shallow copy safe in this case?

I've tried this in C++ and it worked but I would just like to confirm if this behavior is safe.

+4  A: 

If by "shallow copy", you mean that after assignment of a struct containing an array, the array would point to the original struct's data, then: it can't. Each element of the array has to be copied over to the new struct. "Shallow copy" comes into the picture if your struct has pointers. If it doesn't, you can't do a shallow copy.

When you assign a struct containing an array to some value, it cannot do a shallow copy, since that would mean assigning to an array, which is illegal. So the only copy you get is a deep copy.

Consider:

#include <stdio.h>

struct data {
    char message[6];
};

int main(void)
{
    struct data d1 = { "Hello" };
    struct data d2 = d1; /* struct assignment, (almost) equivalent to
                            memcpy(&d2, &d1, sizeof d2) */

    /* Note that it's illegal to say d2.message = d1.message */

    d2.message[0] = 'h';
    printf("%s\n", d1.message);
    printf("%s\n", d2.message);
    return 0;
}

The above will print:

Hello
hello

If, on the other hand, your struct had a pointer, struct assignment will only copy pointers, which is "shallow copy":

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct data {
    char *message;
};

int main(void)
{
    struct data d1, d2;
    char *str = malloc(6);
    if (str == NULL) {
        return 1;
    }
    strcpy(str, "Hello");
    d1.message = str;
    d2 = d1;

    d2.message[0] = 'h';
    printf("%s\n", d1.message);
    printf("%s\n", d2.message);
    free(str);
    return 0;
}

The above will print:

hello
hello

In general, given struct T d1, d2;, d2 = d1; is equivalent to memcpy(&d2, &d1, sizeof d2);, but if the struct has padding, that may or may not be copied.

Edit: In C, you can't assign to arrays. Given:

int data[10] = { 0 };
int data_copy[10];

data_copy = data;

is illegal. So, as I said above, if you have an array in a struct, assigning to the struct has to copy the data element-wise in the array. You don't get shallow copy in this case: it doesn't make any sense to apply the term "shallow copy" to a case like this.

Alok
Yes, I understand the case for pointers but I'm just not sure for arrays.
jasonline
@jasonline: see my edit. You can't assign to arrays in C, so you can't get a shallow copy if your struct has array(s) (and no pointers).
Alok
Whoever gave a negative vote: can you please tell me why?
Alok
@Alok: Was typing up my answer. You are, of course, right that you can't assign to arrays, but that's beside the point. A shallow copy is done and not a deep copy, even on arrays.
Roger Pate
@Roger: OK, thanks for the explanation. This is a question of terminology here I think. I agree with you: if an array member of a struct contains pointers, the pointer values are going to be copied of course. What I *think* the OP meant was if the array member of the assigned-to struct "point to" the original "assignee" array data, and the answer to that is "No, it can't."
Alok
@Roger: given that the OP is using a `char` array in the `struct`, I think we can be certain that by "shallow copy", he meant that the new `struct`'s data would point to the old `struct`'s data.
Alok
@Alok: I see what you mean there, in that case we should help him understand what "shallow copy" means.
Roger Pate
@Roger: I thought my original answer did explain the difference, where my second code snippet contains a pointer member. But anyway, I have made my answer clearer by adding a preface.
Alok
@Alok: Thanks for correcting my terms. Therefore, can I conclude that assigning between two structures with character arrays do deep copy and therefore this is just fine? After the assignment, each instance will have their own separate copy of equal values.
jasonline
@jasonline: The value of the array is the value of each item in it; when you copy the array (in this case done by the assignment), each array is separate.
Roger Pate
@jasonline: yes, assignment will do what you are expecting it to do. If you want "shallow copy", you will need pointers.
Alok
+2  A: 

Assigning structs does a member-wise assignment, and for arrays this means assigning each item. (And this is done recursively for "multiple dimension" arrays, which are really just arrays of arrays.)

You are correct that it does a shallow copy, even on arrays. (I'm assuming that you have not overloaded op= with respect to C++; if you overload it you can do anything you want.)

Remember that a shallow copy means copying the value of something, while a deep copy means copying the value to which something points or refers. The value of an array is each item in it.

The difference between shallow and deep is most meaningful when you have a type that does indirection, such as a pointer. I find my answer the most helpful way to look at this issue, but you could also say "shallow" vs "deep" doesn't even apply to other types, and they are just "copied".

struct S {
  int n;
  int* p;
  int a[2];
  int* ap[2];
  int xy[2][2];
};

void f() {
  S c, d;

  c = d;
  // equivalent to:
  c.n = d.n;
  c.p = d.p;

  c.a[0] = d.a[0];  // S::a is similar to your situation, only using
  c.a[1] = d.a[1];  // int instead of char.

  c.ap[0] = d.ap[0];
  c.ap[1] = d.ap[1];
  c.xy[0][0] = d.xy[0][0];
  c.xy[0][1] = d.xy[0][1];
  c.xy[1][0] = d.xy[1][0];
  c.xy[1][1] = d.xy[1][1];
}

That I used int above doesn't change anything of the semantics, it works identically for char arrays, copying each char. This is the S::a situation in my code.

Note that p and ap are copied shallowly (as is every other member). If those pointers "own" the memory to which they point, then it might not be safe. ("Safe" in your question is vague, and really depends on what you expect and how you handle things.)

For an interesting twist, consider boost::shared_ptr and other smart pointers in C++. They can be copied shallowly, even though a deep copy is possible, and this can still be safe.

Roger Pate