12

What is the standard way to copy two structs that contain char arrays?

Here is some code:

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

typedef struct {
    char* name;
    char* surname;
} person;

int main(void){
    person p1;
    person p2;

    p1.name     = (char*)malloc(5);
    p1.surname  = (char*)malloc(5);

    strcpy(p1.name, "AAAA");
    strcpy(p1.surname, "BBBB");

    memcpy(&p2, &p1, sizeof(person));
    free(p1.name);
    printf("%s\n", p2.name);
    return 0;
}

The line printf("%s\n", p2.name); does not print something, because I freed the buffer.

The problem with my structs is that they are bigger than struct person. They contain hundreds of char pointers, and I have to copy every member one by one.

Is there another way to copy two structs that contain char arrays without using malloc and strcpy for every member?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cateof
  • 789
  • 3
  • 11
  • 24
  • How does memcpy is working, if memory is not allocated for p2, Can anybody explain? Shouldn't it throw some exception at run time? – Jagdish Mar 29 '15 at 16:30
  • 1
    Your structures contain *pointers*, not *arrays*. Your pointers may contain the address of an array of char, but is you want that array to be copied, you must manage it explicitely. – Michel Billaud May 10 '15 at 10:17

5 Answers5

13

You have no choice but provide a copy function yourself:

void copy_person(person *dst, const person *src)
{
    dst->name = malloc(strlen(src->name) + 1);
    dst->surname = malloc(strlen(src->surname) + 1);
    strcpy(dst->name, src->name);
    strcpy(dst->surname, src->surname);
}

which may be more elaborated than that: checking for errors, factoring the strlen + strcpy in an auxilliary function, etc.

That's what copy constructors in C++ are for.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
7

Yes, copying struct that contain char arrays will work without any problem, but struct with char pointers (or any type of pointer for that matter) you will have to do manually.

Also note that the cast of malloc's return type is not needed in C (it is in C++) and can hide a missing prototype for malloc.

schot
  • 10,958
  • 2
  • 46
  • 71
1

To elaborate on the answer of Alexandre C. you might want to do the malloc() as a single operation so that a free() is also simple.

This approach provides a degree of protection in that the single malloc() will either succeed or fail so that you would not have a problem of malloc() failing midway through constructing a copy. With this approach you would mix person with pointers to person that have been malloced so you might want to have two different data types something along the lines of the following in order to better mark which is which.

I have provided two alternatives for the copying with one using C Standard library functions strcpy() and strlen() and the other using a simple function that does a straight copy and returns a pointer to where it left off in the destination buffer.

I have not tried to compile this example so there may be problems with it.

There is one possible concern with this approach. Since the individual strings are not malloced you may run into a problem if you are moving the individual strings around using their pointers with the idea that each of the individual strings is its own malloced area of memory. This approach assumes the entire object is wanted or none of it is wanted.

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

    typedef struct {
        char* name;
        char* surname;
        char* address1;
    } person, *personptr;

    // copy a string to destination string return pointer after end of destination string
    char * StrCpyRetEnd (char *pDest, char *pSrc)
    {
        while (*pDest++ = *pSrc++);
        return pDest;
    }
    personptr DeepCopyPerson (person *pSrc)
    {
        personptr     pDest = 0;
        unsigned int  iTotalSize = sizeof(person);

        iTotalSize += (strlen(pSrc->name) + 1) * sizeof(char);
        iTotalSize += (strlen(pSrc->surname) + 1) * sizeof(char);
        iTotalSize += (strlen(pSrc->address1) + 1) * sizeof(char);
        pDest = malloc(iTotalSize);
        if (pDest) {
#if 1
            // alternative one without a helper function
            pDest->name = (char *)(pDest + 1);  strcpy (pDest->name, pSrc->name);
            pDest->surname = pDest->name + strlen(pDest->name) + 1; strcpy (pDest->surname, pSrc->surname);
            pDest->address1 = pDest->surname + strlen(pDest->surname) + 1; strcpy (pDest->address1, pSrc->address1);
#else
            // alternative two using StrCpyRetEnd () function
            pDest->name = (char *)(pDest + 1);
            pDest->surname = StrCpyRetEnd (pDest->name, pSrc->name);
            pDest->address1 = StrCpyRetEnd (pDest->surname, pSrc->surname);
            strcpy (pDest->address1, pSrc->address1);
#endif
        }
        return pDest;
    }

    int main(void){
        person    p1;  // programmer managed person with separate mallocs
        personptr p2;  // created using ClonePerson()

        p1.name     = malloc(5);
        p1.surname  = malloc(5);
        p1.address1 = malloc(10);

        strcpy(p1.name,"AAAA");
        strcpy(p1.surname,"BBBB");
        strcpy(p1.address1,"address1");

        p2 = DeepCopyPerson (&p1);

        free(p1.name);
        printf("%s\n", p2->name);

        free (p2);   // frees p2 and all of the memory used by p2
        return 0;
    }
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
1

You have to allocate memory to any pointer if you want to do a copy. However you can always make a pointer point to already allocated memory. For example, you can do the following:

p2.name = p1.name (p1.name is already allocated memory)

This is dangerous as there are more than one reference to the same memory location. If you free either p1.name or p2.name, it results in a dangerous situation.

In order to copy the entire content you have to allocate memory to the pointers of the struct p2.

p2.name = <allocate memory>
Copy individual struct members instead of a memcpy of the entire struct

This is because memory is not allocated in a contiguous manner. Also sizeof(struct) will give you size of the members of the struct and not the memory allocated to it.

For example sizeof(p2) = 8 = sizeof(p1)= sizeof(person) even after allocating memory to members of p1.

It would be a different case had the members been char arrays.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Praveen S
  • 10,355
  • 2
  • 43
  • 69
1

A bit out-of-the-box thinking:

Since the structure of your struct is static, you could write a small utility program or script to generate the copy code for you.

Take the source-code of your struct definition as input, and then devise a set of rules to generate the copying code.

This is quickshot, and I don't know if it were faster to just write the copy-code manually - but at least it is a more interesting problem.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sum1stolemyname
  • 4,506
  • 3
  • 26
  • 44