Przypisz jedną strukturę do innej w C

146

Czy możesz przypisać jedną instancję struktury do innej, na przykład:

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

Widziałem, jak działa w przypadku prostych struktur, ale czy działa w przypadku złożonych struktur?
Skąd kompilator wie, jak kopiować elementy danych w zależności od ich typu, tj. Rozróżniając intciąg i łańcuch?

shreyasva
źródło

Odpowiedzi:

151

Tak, jeśli struktura jest tego samego typu. Pomyśl o tym jak o kopii pamięci.

fabrizioM
źródło
72
Pamiętaj, że nie ma głębokiej kopii, wskazana pamięć nie jest kopiowana.
Georg Schölly,
3
Problemem jest również współbieżność.
Tim Post
16
Współbieżność @Tim nie jest większym problemem niż przypisywanie wbudowanych typów, takich jak liczby całkowite i podwójne - przypisanie również nie jest operacją niepodzielną.
2
OK, jeśli została utworzona kopia, czy mogę później zwolnić pamięć za pomocą funkcji free ()?
Betlista
5
@Betlista Nie możesz zwolnić pamięci za pomocą free (), ponieważ są to zmienne automatyczne: en.wikipedia.org/wiki/Automatic_variable
joshdoe
138

Tak, przypisanie jest obsługiwane w przypadku struktur. Istnieją jednak problemy:

struct S {
   char * p;
};

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

Teraz wskaźniki obu struktur wskazują na ten sam blok pamięci - kompilator nie kopiuje wskazanych danych. Obecnie trudno jest stwierdzić, która instancja struktury jest właścicielem danych. Właśnie dlatego C ++ wymyślił koncepcję definiowanych przez użytkownika operatorów przypisania - możesz napisać określony kod, aby obsłużyć ten przypadek.


źródło
1
Podkręciłem go, ponieważ przeczytanie go uświadomiło mi błąd / pominięcie w mojej własnej odpowiedzi.
Clifford,
1
+1 za zauważenie, że w rzeczywistości nie ma miejsca kopiowanie.
Tom Duckering
14
Dlaczego to oznaczono jako spam? Czy ktoś stracił kontrolę nad swoją myszą?
Georg Fritzsche
@gf I najwyraźniej równie obraźliwe!
2
@rahmanisback Odpowiedź anona jest dość jasna w tym temacie: „kompilator nie kopiuje wskazanych danych”. Dane structsame w sobie są wyraźnie skopiowane.
Tobias,
24

Najpierw spójrz na ten przykład:

Kod w C dla prostego programu w C jest podany poniżej

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;
}

Równoważny kod ASM dla foo_ assign () to

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    

Jak widać, przypisanie jest po prostu zastępowane przez instrukcję „mov” w asemblerze, operator przypisania oznacza po prostu przenoszenie danych z jednej lokalizacji pamięci do innej. Przypisanie zrobi to tylko dla bezpośrednich członków struktur i nie powiedzie się, jeśli masz w strukturze złożone typy danych. Tutaj LICZBA.ZESP oznacza, że ​​nie możesz mieć tablicy wskaźników wskazujących na listy.

Tablica znaków w strukturze sama nie będzie działać na większości kompilatorów, ponieważ przypisanie będzie po prostu próbowało skopiować, nawet nie patrząc, czy typ danych jest złożony.

Arun Kaushal
źródło
2
Czy możesz
wyjaśnić,
15

To jest prosta kopia, tak jak byś to zrobił memcpy()(w rzeczywistości niektóre kompilatory faktycznie tworzą wywołanie memcpy()dla tego kodu). W C nie ma „łańcucha”, tylko wskazuje na kilka znaków. Jeśli twoja struktura źródłowa zawiera taki wskaźnik, to wskaźnik zostanie skopiowany, a nie same znaki.

Thomas Pornin
źródło
OK, więc kompilator tłumaczy to na memcpy, zobacz tutaj: godbolt.org/z/nPxqWc - Ale teraz, jeśli przekażę identyczne wskaźniki ai b, i *a = *bzostanie przetłumaczone na memcpyniezdefiniowane zachowanie, ponieważ dla memcpy„Obszary pamięci nie mogą się pokrywać”. (cytat ze strony podręcznika). Czy więc kompilator źle używa, memcpyczy też piszę takie zadanie?
not-a-user
6

Czy chodziło Ci o „Złożone” w liczbie zespolonej z częściami rzeczywistymi i urojonymi? Wydaje się to mało prawdopodobne, więc jeśli nie, musisz podać przykład, ponieważ „złożone” nie oznacza nic konkretnego w języku C.

Otrzymasz bezpośrednią kopię pamięci struktury; czy tego chcesz, zależy od struktury. Na przykład, jeśli struktura zawiera wskaźnik, obie kopie będą wskazywać te same dane. To może być to, czego chcesz, ale nie musi; to zależy od projektu twojego programu.

Aby wykonać „inteligentną” kopię (lub „głęboką” kopię), będziesz musiał zaimplementować funkcję, aby wykonać kopię. Może to być bardzo trudne do osiągnięcia, jeśli sama struktura zawiera wskaźniki i struktury, które również zawierają wskaźniki, a być może wskaźniki do takich struktur (być może to masz na myśli przez „złożone”), i jest trudne do utrzymania. Prostym rozwiązaniem jest użycie C ++ i zaimplementowanie konstruktorów kopiujących i operatorów przypisania dla każdej struktury lub klasy, a następnie każda z nich staje się odpowiedzialna za własną semantykę kopiowania, można użyć składni przypisania i jest ona łatwiejsza w utrzymaniu.

Clifford
źródło