10

I have the following struct in my C program

struct person {
    char key[50];
    char color[20];
    int age;
};

I want to make a deep copy of this struct. I've got my deep copy function setup however I'm a bit confused about how to deep copy strings. I've heard of people using strcpy and others using strdup.

What I want in my program is for the deep copied person's key and color not to be affected if the original person is freed. Once set, the key and color cannot change. For my purpose, should I be using the strcpy or strdup function?

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Ardembly
  • 213
  • 1
  • 2
  • 9
  • 3
    since there are no 'char *' fields in the posted struct, there is zero need to do anything beyond memcpy( destStruct, srcStruct, sizeof destStruct ); – user3629249 May 01 '15 at 15:24
  • Because the arrays (not only pointers to them!) are members of your struct, a shallow copy is a deep copy. There is no indirection. The memory footprint of your struct is > 70 bytes, on the stack, on the heap, wherever you want them. – Peter - Reinstate Monica May 01 '15 at 16:03
  • possible duplicate of [Is shallow copy sufficient for structures with char\[\]?](http://stackoverflow.com/questions/2241699/is-shallow-copy-sufficient-for-structures-with-char) – ecatmur May 01 '15 at 16:08

4 Answers4

14

Post-K&R (i.e. in standard C)1 you can just assign them. The function below is just to make the example clear, you would always just assign in-place:

void deepCopyPerson(struct person *target, struct person *src)
{
    *target = *src;
}

To elaborate: The char arrays are part of your struct object (true arrays, not only pointers!), and as such are allocated and copied with the object.

In order to satisfy the disbeliefers ;-) I dug around in the standard draft 1570 :

6.5.16 Assignment operators

Semantics

An assignment operator stores a value in the object designated by the left operand. [Followed by type conversion and sequencing considerations which are not relevant here.]

[...]

6.5.16.1 Simple assignment

Constraints

One of the following shall hold:

  • [...]
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;

[...]

Semantics

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.


1 The first edition of The C Programming Language, written by Brian W. Kernighan and Dennis M. Ritchie at Bell Labs and published in 1978, is the informal language specification for the first C version, often called "K&R C" after the two authors of the book specifying it (and as a distinction to ANSI C which was specified 10 years later). This first edition of the book contains the following sentence on page 121:

The essential rules are that the only operations that you can perform on a structure are take its address with &, and access one of its members.

In particular, you cannot assign structures. (But they continue "these restrictions will be removed in forth- coming versions".)

Indeed, this was one of the few substantial (as opposed to syntactical) additions to the language in the ANSI version passed 10 years later. Kernighan and Ritchie prepared an accompanying "ANSI C edition" of their book. The sentence, this time on page 129, now reads:

The only legal operations on a structure are copying and assigning to it as a unit, taking its address with &, and accessing its members.

They elaborate:

Copy and assignment include passing arguments to functions and returning values from functions as well.

This equivalence is still true today in the latest C++ standard.

And since we are here in the root canal of C++: The next statement was an apodictic "Structures may not be compared." It is interesting that they added this sentence — obviously, built-in comparison suggested itself after introducing built-in assignment, but was rejected. In my opinion, there was never a good reason for that: If you can assign member-wise, why not compare that way as well? That would do just fine in many cases, and manual comparison is hard to maintain. It took another 30 years to make that possible in C++20 with the spaceship operator which generates all the other comparisons when it is (explicitly) defaulted; indeed, it defaults to member-wise comparison.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • this will not copy a struct. for copying a struct, use memcpy() rather than an assignment – user3629249 May 01 '15 at 15:25
  • 1
    @user3629249 What makes you think that? I think somewhere in the inet headers they use the trick to put *just an array* (and nothing else) in a struct, just in order to prevent array decay and be able to pass it around as arguments. – Peter - Reinstate Monica May 01 '15 at 15:26
  • 1
    Peter Schneider is correct in this case, user3629249, because the structs contain arrays not pointers. – Peter May 01 '15 at 15:34
6

To perform a deep copy of a struct that contains arrays (without any pointers), a deep copy is simple

struct person x = {"Key", "Color", 42};   /*  initialise to something */
struct person y = x;

This doesn't work if the "strings" are pointers though. It is necessary then to allocate new strings, and then use a function like strcpy() to copy members.

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

struct pointer_person
{
    char *key;
    char *color;
    int age;
};

struct pointer_person deep_copy(struct pointer_person p)
{
     struct pointer_person retval;
     retval.key = malloc(strlen(p.key) + 1);
     strcpy(retval.key, p.key);
     retval.color = malloc(strlen(p.color) + 1);
     strcpy(retval.color, p.color);
     retval.age = p->age;
     return retval;
}

int main()
{
   struct pointer_person p;
   struct pointer_person pc;

   p.key = malloc(50);
   strcpy(p.key, "A key");
   p.color = malloc(20);
   strcpy(p.color, "A colour");
   p.key = 42;

   pc = deep_copy(p);

   /* do stuff with pc and c */

   free(p.key);
   free(p.color);
   free(pc.key);
   free(pc.color);
   return 0;
}

Some error checking left out of the above (e.g. need to check that malloc() succeeds before copying).

Peter
  • 35,646
  • 4
  • 32
  • 74
  • I think `struct person y = x;` is an initialization, not an assignment (which would become relevant for `static struct person y = x;`). C++ is clearer about the distinction (copy constructor vs. assignment operator). – Peter - Reinstate Monica Mar 04 '19 at 20:51
  • @PeterA.Schneider - I'd agree that `struct person y = x` is an initialisation, not an assignment. However, I don't understand the reason for your comment, since I stated nothing in this answer on whether it is an initialisation or assignment. – Peter Mar 05 '19 at 09:31
  • This question is not asking about copying the struct itself, it's only about the `char []` members. – Iharob Al Asimi Mar 05 '19 at 15:36
  • @IharobAlAsimi - Well ..... The question title NOW says that, since you just modified it (three years after the question was asked) to say that. The point of my answer also isn't affected by your edits. Moot point anyway - it appears the OP has not been active on SO, since posting this question. I can't predict whether your latest edits will convince the three down-voters of your answer to change their minds. – Peter Mar 05 '19 at 19:17
-1

There is a difference between strcpy() and strdup().

  • strdup() allocates space and returns a pointer to the copy of the string, you also have to free() the returned pointer.

  • strcpy() takes the allocated space and copies the string into it.

It seems that in your case it's strcpy() because the fields of your structure are not pointers so you can't assign to them a pointer to allocated space which is what strdup() returns.

However, as explained in this answer you actually don't need to.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • That usage of memcpy() is actually a shallow copy. It won't do a deep copy if the struct contains pointer members. – Peter May 01 '15 at 15:38
  • 4
    To memcopy the given structs wouldn't make much sense. The language has a readymade, built-in facility for assignments, the aptly named assignment operator `=`. – Peter - Reinstate Monica May 01 '15 at 16:00
-1

If you use strcpy() you have to allocate the memory yourself. strdup() will do that for you. You can use either to create a new memory block which is separate from the original, but naturally strdup() is simpler since it doesn't require a separate malloc(strlen()) call.

Sami Kuhmonen
  • 30,146
  • 9
  • 61
  • 74