Присвойте одну структуру другой в C

146

Можете ли вы назначить один экземпляр структуры другому, например так:

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

Я видел, как это работает для простых структур, но это работает для сложных структур?
Как компилятор знает, как копировать элементы данных в зависимости от их типа, т.е. различать intстроку и?

shreyasva
источник

Ответы:

151

Да, если структура того же типа. Думайте это как копия памяти.

fabrizioM
источник
72
Имейте в виду, что нет глубокой копии, указывающей на то, что память не копируется.
Георг Шолли
3
Параллелизм также является проблемой здесь.
Тим Пост
16
@Tim Concurrency - это не проблема для присваивания встроенных типов, таких как целые и двойные числа - для них присваивание не является атомарной операцией.
2
Хорошо, если создана копия, могу ли я позже освободить память с помощью free ()?
Бетлиста
5
@Betlista Вы не можете освободить память с помощью free (), потому что они являются автоматическими переменными: 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 для простой программы на 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» в сборке, оператор присваивания просто означает перемещение данных из одной ячейки памяти в другую ячейку памяти. Назначение будет делать это только для непосредственных членов структур и не сможет скопировать, если в структуре есть сложные типы данных. Здесь COMPLEX означает, что вы не можете иметь массив указателей, указывающих на списки.

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

Арун Каушал
источник
2
Можете ли вы уточнить, при каких условиях это не удастся, потому что это, кажется, работает для меня всегда
AlphaGoku
15

Это простая копия, как и вы memcpy()(с некоторыми компиляторами, которые действительно вызывают memcpy()этот код). В Си нет «строки», только указатели на кучу символов. Если ваша исходная структура содержит такой указатель, то копируется указатель, а не сами символы.

Томас Порнин
источник
Итак, компилятор переводит это в memcpy, см. Здесь: godbolt.org/z/nPxqWc - Но теперь, если я передаю идентичные указатели aи b, и *a = *bпреобразуется в memcpyнеопределенное поведение, потому что для memcpy«Области памяти не должны перекрываться». (цитата из справочной страницы). Так что компилятор неверен в использовании memcpyили я ошибаюсь при написании такого назначения?
не пользователь
6

Вы имели в виду «Комплекс», как в комплексном числе с действительными и мнимыми частями? Это кажется маловероятным, поэтому, если нет, вам придется привести пример, поскольку «сложный» не означает ничего конкретного с точки зрения языка Си.

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

Чтобы выполнить «умную» копию (или «глубокую» копию), вам потребуется реализовать функцию для выполнения копирования. Этого может быть очень трудно достичь, если сама структура содержит указатели и структуры, которые также содержат указатели и, возможно, указатели на такие структуры (возможно, это то, что вы подразумеваете под «сложным»), и его трудно поддерживать. Простое решение заключается в использовании C ++ и реализации конструкторов копирования и операторов присваивания для каждой структуры или класса, тогда каждый из них становится ответственным за свою собственную семантику копирования, вы можете использовать синтаксис присваивания и его легче поддерживать.

Клиффорд
источник