C: Создание глубокой копии структуры... создание мелкой копии структуры

Прежде, чем я получу высокую оценку, я упомянул, что есть вопросы, КАК ЭТО Я уже видел, но все они недостаточно похожи на мой конкретный вопрос ЗА МЕНЯ, чтобы забрать, по крайней мере (извините мое невежество в этом case), или они не обязательно C-специфичны.

Мой вопрос о том, как сделать глубокую копию структуры с указателями в качестве членов и как сделать SHALLOW копию структуры с элементами, которые являются указателями. И затем, только для справки, как сделать глубокую копию структуры WITHOUT членов-указателей и как сделать мелкую копию структуры WITHOUT членов-указателей (не уверен, что последний имеет смысл).

Скажем, мы имеем это:

typedef struct Student
{
    char* first_name; 
    char* last_name; 
    int grade;
    long id;
} Student;

Вот общая функция, которую я создал для создания ученика (заголовок трудно форматировать, пожалуйста, несите меня):

Student* create_student(const char* first_name, const char* last_name, int grade,long id)

{

   Student *newStudentp = (malloc(sizeof(Student)));

   newStudentp -> last_name = (malloc((strlen(last_name) + 1)  * sizeof(char)));
   newStudentp -> first_name = (malloc((strlen(first_name) + 1)  * sizeof(char)));

   strncpy(newStudentp -> first_name, first_name, strlen(first_name) + 1);
   strncpy(newStudentp -> last_name, last_name, strlen(last_name) + 1);

   newStudentp -> grade = grade;
   newStudentp -> id = id;


   return newStudentp;
}

Теперь я попытаюсь сделать глубокую и мелкую копию; скажи мне, если я сделаю что-то глупое

int main()
{
    Student *s1 = create_Student("Bo","Diddly", 100, 221);
    Student *s2 = create_Student("Leeroy","Jenkins",50,1337);
    memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2?
    return 0;
}

Теперь, для глубоких копий структур с элементами указателя, я знаю, что мы должны сделать НАШУ СОБСТВЕННУЮ функцию копирования, которая делает что-то разумное с указателями. Какая эта разумная вещь... Я не уверен... так вот моя (вероятно, жалкая) попытка этой копии DEEP.

void copy_Student(Student *s1, Student *s2)
{
   s2 -> grade = s1 -> grade;
   s2 -> id = s1 -> id;
   s2 -> first_name = s1 -> *first_name;
   s2 -> last_name = s1 -> *last_name;

}

Другая часть моего вопроса (структуры БЕЗ указателей как членов), вероятно, может быть просто объяснена в устной форме.

ОК, ИЗМЕНИТЬ ПОСЛЕ ЧТЕНИЯ ПОЛЕЗНЫХ КОММЕНТАРИЙ:

Мелкая копия:       тетср (s2, s1, SizeOf (студент));

Глубокая копия:

void free_student(Student* stu)
{
    free(stu -> first_name);
    free(stu -> last_name);
}

void copy_Student(Student *s1, Student *s2)
{
    s2 -> grade = s1 -> grade;
    s2 -> id = s1 -> id;
    s2 -> first_name = strdup(s1 -> first_name);
    s2 -> last_name = strdup(s1 -> last_name);
}

Спасибо, ребята (обязательно укажите, есть ли еще ошибки)!

Большое спасибо, Фил

Ответ 1

Код, который вы указали в качестве мелкой копии, не указан; он фактически разбивает стек и, вероятно, сбой программы.

Student *s1 = create_Student("Bo","Diddly", 100, 221);
Student *s2 = create_Student("Leeroy","Jenkins",50,1337);
memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2?

Если у вас был правильный размер, это будет то же самое, что и s2 = s1;. Но поскольку у вас неправильный размер, он слишком много копирует и перезаписывает все, что есть в памяти после s2. Чтобы сделать реальную мелкую копию, оставьте значение &:

memcpy(s2,s1,sizeof(Student)); //shallow copy of s1 INTO s2

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

void copy_Student(Student *s1, Student *s2)
{
    s2 -> grade = s1 -> grade;
    s2 -> id = s2 -> id;
    s2 -> first_name = strdup(s1 -> first_name);
    s2 -> last_name = strdup(s1 -> last_name);
}

Обратите внимание, что во избежание утечек памяти вам также необходимо будет освободить старые имена из s2 до назначения новых копий, сделать функцию free_Student, которая освободит эти имена, а также убедиться, что create_Student копии имена в первую очередь (или включают флаги "должны освобождать", поэтому вам не нужно копировать литеральные строки).

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

Ответ 2

Разница между мелкой копией и глубокой копией может быть объяснена в одном предложении: указатели на мелкие копии копий; глубокая копия копирует то, на что указывает.

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

Ваша попытка сделать мелкую копию технически правильной. Однако это логично неправильно. Ваша функция delete_student() (та, которая освобождает mallocs) не может обрабатывать мелкие копии. Он не знал бы, сколько еще экземпляров учеников все еще существует, и нужно было отложить free() до удаления копии alst.

У глубокой копии есть очень сложная проблема. это технически некорректно. Как ни странно, ваша функция create_student показывает, что вы знаете, как скопировать char * в другую, с глубокой копией как first_name, так и last_name. Ваш copy_Student должен сделать то же самое.

Ответ 3

Вместо того, чтобы думать об этом как о копии, почему бы вам не создать новую структуру, но с теми же параметрами, что и тот, который вы хотите дублировать? Это тонкая разница, но у вас уже есть код:

Student *s2 = create_Student("Leeroy","Jenkins",50,1337);
Student *wiper = create_Student(s2->first_name, s2->last_name, 
                                               s2->grade, s2->id);

Структура wiper имеет клон s2.

Чтобы сделать мелкую копию, сделайте так, как вы делаете с s1 и s2 (memcpy), или просто:

s2 = malloc(sizeof(Student));
*s2 = *s1

Ответ 4

memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2?

Здесь вы перезаписали указатель s2 и указатели внутри s2 соответствующими значениями указателя в s1, поэтому у вас просочилась память.

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

void copy_Student(Student *s1, Student *s2)
{
   assert( ( s1 != NULL ) && ( s2 != NULL ) );

   if( s2->first_name != NULL ) free( s2->first_name );
   if( s2->last_name != NULL ) free( s2->last_name );

   s2->grade = s1->grade;
   s2->id = s1->id;

   s2->last_name = (malloc((strlen(s1->last_name) + 1)  * sizeof(char)));
   s2->first_name = (malloc((strlen(s1->first_name) + 1)  * sizeof(char)));

   strncpy(s2-> first_name, s1->first_name, strlen(s1->first_name) + 1);
   strncpy(s2-> last_name, s1->last_name, strlen(s1->last_name) + 1); 
}

Ответ 5

Вместо этого:

newStudentp -> last_name = (malloc((strlen(last_name) + 1)  * sizeof(char)));

делать:

newStudentp -> last_name = strdup (last_name);

Ваша глубокая копия хочет сделать что-то подобное (не совсем то, что предложил cnicutar):

s2->first_name = strdup (s1->first_name);

Проблема с предложением cnicutar заключается в том, что ему необходимо вручную выделить буфер до strcpy.

И если я правильно помню:

* s2 = * s1;

сделает мелкую копию.

Конечно, как в глубоких, так и в мелких копиях вы должны убедиться, что указатели адресата free, иначе вы получите утечку памяти. Но даже free указатели могут привести к проблемам, если вы глубоко скопируете структуру, которая ранее была мелко скопирована.