Скопируйте две структуры в C, которые содержат указатели на символы - PullRequest
12 голосов
/ 22 июля 2010

Каков стандартный способ копирования двух структур, содержащих массивы символов?

Вот код:

#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;
}

Строка printf("%s\n", p2.name); не печатает что-то, потому что я освободил буфер.

Проблема с моими структурами в том, что они больше чем структура person. Они содержат сотни указателей на символы, и я должен скопировать каждого члена по одному.

Есть ли другой способ скопировать две структуры, содержащие массивы символов, без использования malloc и strcpy для каждого члена?

Ответы [ 5 ]

13 голосов
/ 22 июля 2010

У вас нет выбора, кроме как предоставить функцию копирования самостоятельно:

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);
}

, которая может быть более сложной, чем эта: проверка на ошибки, факторизация strlen + strcpy во вспомогательной функции и т. Д.

Для этого и нужны конструкторы копирования в C ++.

7 голосов
/ 22 июля 2010

Да, копирование структуры, содержащей char массивы , будет работать без каких-либо проблем, но структуру с char указателями (или любым другим типом указателя в этом отношении) вам придется делать вручную .

Также обратите внимание, что приведение типа возвращаемого значения malloc не требуется в C (это в C ++) и может скрыть отсутствующий прототип для malloc.

1 голос
/ 07 мая 2015

Для уточнения ответа Александра С. вы можете выполнить malloc() как одну операцию, чтобы free() также был простым.

Этот подход обеспечивает определенную степень защиты в этомсингл malloc() будет либо успешным, либо неудачным, чтобы у вас не было проблемы с malloc() неудачей на полпути при создании копии.При таком подходе вы бы смешали person с указателями на person, которые были неправильно размещены, поэтому вы можете захотеть иметь два разных типа данных в соответствии с приведенным ниже описанием, чтобы лучше пометить, какой именно.* Я предоставил две альтернативы для копирования, одна из которых использует стандартные библиотечные функции C strcpy() и strlen(), а другая - простую функцию, которая выполняет прямое копирование и возвращает указатель на то место, где она остановилась в буфере назначения.1013 *

Я не пытался скомпилировать этот пример, поэтому могут возникнуть проблемы с ним.

Существует один возможный недостаток этого подхода.Поскольку отдельные строки не имеют неправильного расположения, вы можете столкнуться с проблемой, если перемещаете отдельные строки, используя их указатели, с мыслью, что каждая из отдельных строк - это собственная выделенная область памяти.Этот подход предполагает, что весь объект находится в розыске или ни один из них не нужен.

    #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;
    }
1 голос
/ 22 июля 2010

Немного нестандартного мышления:

Поскольку структура вашей структуры статическая, вы можете написать небольшую служебную программу или скрипт для генерации кода для вас.

Возьмите исходный код вашего определения структуры в качестве ввода и затем разработайте набор правил для генерации кода копирования.

Это быстрый выстрел, и я не знаю, было ли быстрее просто написать код копирования вручную - но по крайней мере это более интересная проблема.

1 голос
/ 22 июля 2010

Вы должны выделить память для любого указателя, если вы хотите сделать копию.Однако вы всегда можете указать указатель на уже выделенную память.Например, вы можете сделать следующее:

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

Это опасно , поскольку существует несколько ссылок на одну и ту же ячейку памяти.Если вы освобождаете p1.name или p2.name, это приводит к опасной ситуации.

Чтобы скопировать весь контент, вы должны выделить память указателям структуры p2.

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

Это потому, что память не распределяется непрерывно.Также sizeof(struct) даст вам размер членов структуры, а не выделенную для нее память.

Например, sizeof(p2) = 8 = sizeof(p1)= sizeof(person) даже после выделения памяти для членов p1.

Это был бы другой случай, если бы члены были массивами символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...