Скопируйте две структуры в 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
для каждого члена?
Ответы
Ответ 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 массивы.