Подтвердить что ты не робот

Скопируйте две структуры в C, содержащие указатели char

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

Вот какой код:

#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); ничего не печатает, потому что я освободил буфер.

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

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

4b9b3361

Ответ 1

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

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 в вспомогательной функции и т.д.

То, что для конструкторов копирования в С++.

Ответ 2

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

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

Ответ 3

Немного готового мышления:

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

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

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

Ответ 4

Чтобы подробнее ответить на вопрос Alexandre C., вы можете сделать malloc() как одну операцию, так что a free() также прост.

Этот подход обеспечивает степень защиты в том, что одиночный malloc() будет либо успешным, либо неудачным, так что у вас не возникнет проблемы с malloc(), если он не будет на полпути через создание копии. При таком подходе вы смешали бы person с указателями на person, которые были разделены так, чтобы вы могли захотеть иметь два разных типа данных в соответствии со следующими строками, чтобы лучше определить, что именно.

Я предоставил две альтернативы для копирования с одним, использующим функции стандартной библиотеки C strcpy() и strlen(), а другой - с помощью простой функции, которая выполняет прямую копию и возвращает указатель туда, где она была остановлена ​​в целевом буфере.

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

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

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

Ответ 5

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

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.

Было бы другое дело, если бы членами были char массивы.