Asignar una estructura a otra en C

146

¿Se puede asignar una instancia de una estructura a otra, así:

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

Lo he visto funcionar para estructuras simples, pero ¿funciona para estructuras complejas?
¿Cómo sabe el compilador cómo copiar elementos de datos dependiendo de su tipo, es decir, diferenciando entre una inty una cadena?

shreyasva
fuente

Respuestas:

151

Sí, si la estructura es del mismo tipo. Piense como una copia de memoria.

fabrizioM
fuente
72
Tenga en cuenta que no hay copia profunda, apuntado a la memoria no se copia.
Georg Schölly
3
La concurrencia también es un problema aquí.
Tim Post
16
@Tim Concurrency no es más un problema que la asignación de los tipos integrados, como enteros y dobles: la asignación tampoco es una operación atómica para estos.
2
OK, si hay una copia creada, ¿puedo liberar la memoria más tarde con free ()?
Betlista
55
@Betlista No puede liberar la memoria con free () porque son variables automáticas: en.wikipedia.org/wiki/Automatic_variable
joshdoe
138

Sí, la asignación es compatible con estructuras. Sin embargo, hay problemas:

struct S {
   char * p;
};

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

Ahora los punteros de ambas estructuras apuntan al mismo bloque de memoria: el compilador no copia los datos apuntados. Ahora es difícil saber qué instancia de estructura posee los datos. Esta es la razón por la cual C ++ inventó el concepto de operadores de asignación definibles por el usuario: puede escribir código específico para manejar este caso.


fuente
1
Lo subí porque leerlo me hizo darme cuenta del error / omisión en mi propia respuesta.
Clifford
1
+1 por notar que en realidad no hay ninguna copia en curso.
Tom Duckering
14
¿Por qué se marcó esto como spam? ¿Alguien ha perdido el control sobre su mouse?
Georg Fritzsche
@gf ¡Y aparentemente tan ofensivo también!
2
@rahmanisback La respuesta de anon es bastante clara sobre este tema: "el compilador no copia los datos apuntados ". Los datos del structmismo se copian claramente.
Tobias
24

Primero mira este ejemplo:

El código C para un programa C simple se proporciona a continuación.

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

El código ASM equivalente para foo_assign () es

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    

Como puede ver que una asignación simplemente se reemplaza por una instrucción "mov" en el ensamblaje, el operador de asignación simplemente significa mover datos de una ubicación de memoria a otra ubicación de memoria. La asignación solo lo hará para miembros inmediatos de una estructura y no se copiará cuando tenga tipos de datos complejos en una estructura. Aquí COMPLEJO significa que no puede tener una variedad de punteros, apuntando a listas.

Una matriz de caracteres dentro de una estructura no funcionará en la mayoría de los compiladores, esto se debe a que la asignación simplemente intentará copiar sin siquiera considerar que el tipo de datos es de tipo complejo.

Arun Kaushal
fuente
2
¿Puedes explicar en qué condiciones fallaría porque siempre parece funcionar para mí
AlphaGoku
15

Esta es una copia simple, tal como lo haría con memcpy()(de hecho, algunos compiladores en realidad producen una llamada memcpy()para ese código). No hay "cadena" en C, solo apunta a un grupo de caracteres. Si su estructura de origen contiene dicho puntero, entonces el puntero se copia, no los caracteres en sí.

Thomas Pornin
fuente
OK, entonces el compilador traduce esto a memcpy, vea aquí: godbolt.org/z/nPxqWc - Pero ahora si paso punteros idénticos ay b, y *a = *bse traduce a un memcpycomportamiento indefinido, porque memcpy"Las áreas de memoria no deben superponerse". (citando la página del manual). Entonces, ¿el compilador está equivocado al usar memcpyo estoy equivocado al escribir tal asignación?
no es un usuario
6

¿Quiso decir "complejo" como en número complejo con partes reales e imaginarias? Esto parece poco probable, por lo que si no, tendría que dar un ejemplo ya que "complejo" no significa nada específico en términos del lenguaje C.

Obtendrá una copia de memoria directa de la estructura; si eso es lo que quieres depende de la estructura. Por ejemplo, si la estructura contiene un puntero, ambas copias apuntarán a los mismos datos. Esto puede o no ser lo que quieres; eso depende del diseño de su programa.

Para realizar una copia 'inteligente' (o una copia 'profunda'), deberá implementar una función para realizar la copia. Esto puede ser muy difícil de lograr si la estructura en sí contiene punteros y estructuras que también contienen punteros, y quizás punteros a tales estructuras (quizás eso es lo que quiere decir con "complejo"), y es difícil de mantener. La solución simple es usar C ++ e implementar constructores de copia y operadores de asignación para cada estructura o clase, luego cada uno se hace responsable de su propia semántica de copia, puede usar la sintaxis de asignación y se mantiene más fácilmente.

Clifford
fuente