Допустим, у меня есть структура с именем «пример», в которой есть член с именем данные, который хранит данные, выделенные в куче:
typedef struct _EXAMPLE
{
signed char *data;
size_t size;
} example_t;
example_t *example_alloc(size_t size)
{
example_t *ret = malloc(sizeof *ret);
ret->size = size;
ret->data = calloc(size, sizeof *ret->data);
return ret;
}
example_t *a = example_alloc(10), b = *a;
Хранится ли значение в b.data
в стеке или в куче? Указывает ли это на данные в a
?
Указатели — это просто значения.
Точно так же, как b.size
— это просто копия a->size
, b.data
— это копия того же значения указателя, которое содержит a->data
.
Он указывает на одно и то же место, где бы оно ни находилось.
Необходимо соблюдать осторожность, так как free
ing a->data
делает b.data
недействительным, и наоборот, что может привести к Неопределенное поведение при доступе.
Структура b
является локальной переменной, и все ее члены хранятся локально в стеке (пожалуйста, никаких педантичных комментариев о том, что C не указывает, что стек существует — для этого требуется что-то, что действует как стек).
Поскольку b.data
является членом b
, он хранится в стеке. Однако данные, на которые он указывает, совпадают с теми, на которые указывает a->data
, и это память кучи, которая была выделена calloc()
в функции.
После b = *a
, a->data == b.data
. То есть они оба относятся к одной и той же выделенной памяти. Это известно как "мелкая копия". Для достижения "глубокая копия" в C потребуется дополнительный код. Например:
example_t* example_copy( example_t* b, const example_t* a )
{
*b = *a ; // shallow copy all members
// Allocate new block and copy content
size_t sizeof_block = b->size * sizeof(*b->data)
b->data = malloc( sizeof_block ) ;
memcpy( b->data, a->data, sizeof_block ) ;
return b ;
}
Затем
example_t *a = example_alloc(10) ;
example_t b ;
example_copy( &b, a ) ;
Обратите внимание, что такой код ужасно подвержен утечкам памяти. Здесь, например, если b
выходит за рамки до того, как b.data
будет free
'd, вы потеряете доступ к этой памяти. Любая функция, которая выделяет память и возвращается без free
'd, возлагает на нее ответственность вызывающей стороны, но вовсе не делает это обязательно видимым или очевидным (ваше соглашение об именах _alloc
, возможно, является некоторым уровнем смягчения). Вы должны по крайней мере также реализовать соответствующие функции для облегчения очистки объектов. Я понимаю, что это не по теме, но C++ предоставляет решение этой проблемы, когда деструктор вызывается, когда объект выходит за пределы области видимости. Кроме того, для автоматического вызова семантики глубокое копирование можно использовать конструкторы копирования и перегрузку оператора присваивания.
@Clifford Да, это моя ошибка. Слишком долго пялился в монитор.