Призначте одну структуру іншій у С


146

Чи можете ви призначити один екземпляр структури іншому, як-от так:

struct Test t1;
struct Test t2;
t2 = t1;

Я бачив, як це працює для простих структур, але це працює для складних структур?
Як компілятор знає, як скопіювати елементи даних залежно від їх типу, тобто розмежування між intі рядком?

Відповіді:


151

Так, якщо структура одного типу. Розгляньте це як копію пам'яті.


72
Майте на увазі, що немає глибокої копії, вказаної на пам'ять, не копіюється.
Георг Шоллі

3
Паралельність також тут є проблемою.
Тім Пост

16
@Tim Concurrency - це не більше питання, ніж це для присвоєння вбудованих типів, як цілі числа, так і подвійні - присвоєння не є атомною операцією для них.

2
Гаразд, якщо створена копія, чи можу я пізніше звільнити пам'ять безкоштовно ()?
Бетліста

5
@Betlista Ви не можете звільнити пам'ять безкоштовно (), оскільки вони є автоматичними змінними: en.wikipedia.org/wiki/Automatic_variable
joshdoe

138

Так, призначення підтримується для структур. Однак є проблеми:

struct S {
   char * p;
};

struct S s1, s2;
s1.p = malloc(100);
s2 = s1;

Тепер покажчики обох структур вказують на один блок пам'яті - компілятор не копіює вказані на дані. Зараз важко дізнатися, якому екземпляру структури належать дані. Ось чому C ++ винайшов концепцію визначених користувачем операторів призначення - ви можете написати конкретний код для обробки цього випадку.


1
Я збільшив це, тому що, прочитавши його, я зрозумів помилку / упущення власної відповіді.
Кліффорд

1
+1 за те, що відмічає, що копіювання насправді не відбувається.
Том Дакерінг

14
Чому це було позначено як спам? Хтось втратив контроль над своєю мишкою?
Георг Фріцше

@gf І, мабуть, також образливо!

2
@rahmanisback Відповідь anon досить чітка на цю тему: "компілятор не копіює вказані на дані". Самі дані structчітко скопійовані.
Тобіас

24

Спочатку подивіться на цей приклад:

Код С для простої програми C подано нижче

struct Foo {
    char a;
    int b;
    double c;
    } foo1,foo2;

void foo_assign(void)
{
    foo1 = foo2;
}
int main(/*char *argv[],int argc*/)
{
    foo_assign();
return 0;
}

Еквівалентний ASM-код для foo_assign () є

00401050 <_foo_assign>:
  401050:   55                      push   %ebp
  401051:   89 e5                   mov    %esp,%ebp
  401053:   a1 20 20 40 00          mov    0x402020,%eax
  401058:   a3 30 20 40 00          mov    %eax,0x402030
  40105d:   a1 24 20 40 00          mov    0x402024,%eax
  401062:   a3 34 20 40 00          mov    %eax,0x402034
  401067:   a1 28 20 40 00          mov    0x402028,%eax
  40106c:   a3 38 20 40 00          mov    %eax,0x402038
  401071:   a1 2c 20 40 00          mov    0x40202c,%eax
  401076:   a3 3c 20 40 00          mov    %eax,0x40203c
  40107b:   5d                      pop    %ebp
  40107c:   c3                      ret    

Як видно, що призначення просто замінюється інструкцією "mov" в зборі, оператор присвоєння просто означає переміщення даних з одного місця пам'яті в інше місце пам'яті. Призначення буде виконувати його лише для безпосередніх членів структури і не вдасться скопіювати, якщо у вас є складні типи даних у структурі. Тут КОМПЛЕКС означає, що ви не можете мати масив покажчиків, що вказують на списки.

Масив символів у структурі сам по собі не працюватиме на більшості компіляторів, це тому, що призначення просто намагатиметься копіювати, навіть не дивлячись на тип даних складного типу.


2
Чи можете ви уточнити, на яких умовах це не вдасться, тому що, здається, працює для мене завжди
AlphaGoku

15

Це проста копія, подібно до того, як ви робите це memcpy()(дійсно, деякі компілятори насправді викликають memcpy()цей код). У C немає жодної "струни", лише вказівники на купу символів. Якщо ваша джерельна структура містить такий вказівник, то вказівник копіюється, а не самі символи.


Гаразд, тому компілятор перекладає це на memcpy, дивіться тут: godbolt.org/z/nPxqWc - Але тепер, якщо я передаю ідентичні вказівники aі b, і *a = *bперекладається на memcpyне визначене поведінку, оскільки для memcpy"області пам'яті не повинні перетинатися". (цитування зі сторінки man). Тож чи не є компілятор неправильним у використанні memcpyчи я помиляюся в написанні такого завдання?
не-користувач

6

Ви мали на увазі "Комплекс" як у складному числі з реальними та уявними частинами? Це здається малоймовірним, тому, якщо не так, вам не доведеться наводити приклад, оскільки "складний" не означає нічого конкретного з точки зору мови С.

Ви отримаєте пряму копію пам'яті структури; чи це те, що ви хочете, залежить від структури. Наприклад, якщо структура містить покажчик, обидві копії будуть вказувати на однакові дані. Це може бути або не бути тим, що ви хочете; що залежить від вашої програми програми.

Щоб виконати "розумну" копію (або "глибоку" копію), вам потрібно буде реалізувати функцію для копіювання. Цього може бути дуже важко досягти, якщо сама структура містить вказівники та структури, які також містять вказівники, а можливо, і вказівники на такі структури (можливо, це ви маєте на увазі під "складними"), і це важко підтримувати. Просте рішення - використовувати C ++ та реалізовувати конструктори копій та оператори присвоєння для кожної структури чи класу, тоді кожен з них несе відповідальність за власну семантику копіювання, ви можете використовувати синтаксис присвоєння, і він легше підтримується.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.