Atribuir uma estrutura para outra em C

146

Você pode atribuir uma instância de uma estrutura a outra, assim:

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

Eu já vi isso funcionar para estruturas simples, mas funciona para estruturas complexas?
Como o compilador sabe como copiar itens de dados dependendo do seu tipo, ou seja, diferenciando entre uma inte uma string?

shreyasva
fonte

Respostas:

151

Sim se a estrutura for do mesmo tipo. Pense nisso como uma cópia de memória.

fabrizioM
fonte
72
Lembre-se de que não há cópia em profundidade, apontada para a memória não é copiada.
Georg Schölly
3
A simultaneidade também é um problema aqui.
Tim Post
16
A simultaneidade do @Tim não é mais um problema do que a atribuição de tipos incorporados, como números inteiros e duplos - a atribuição também não é uma operação atômica para eles.
2
OK, se houver cópia criada, posso liberar a memória posteriormente com free ()?
Betlista
5
@Betlista Você não pode liberar a memória com free (), porque eles são variáveis automáticas: en.wikipedia.org/wiki/Automatic_variable
joshdoe
138

Sim, a atribuição é suportada para estruturas. No entanto, existem problemas:

struct S {
   char * p;
};

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

Agora, os ponteiros de ambas as estruturas apontam para o mesmo bloco de memória - o compilador não copia os dados apontados. Agora é difícil saber qual instância struct possui os dados. É por isso que o C ++ inventou o conceito de operadores de atribuição definíveis pelo usuário - você pode escrever um código específico para lidar com esse caso.


fonte
1
Eu aumentei porque a leitura me fez perceber o erro / omissão em minha própria resposta.
Clifford
1
+1 por observar que não há realmente nenhuma cópia em andamento.
Tom Duckering
14
Por que isso foi sinalizado como spam? Alguém perdeu o controle sobre o mouse?
Georg Fritzsche
@ gf E aparentemente tão ofensivo também!
2
@rahmanisback A resposta de anon é bastante clara sobre este tópico: "o compilador não copia os dados apontados ". Os dados em structsi são claramente copiados.
Tobias
24

Primeiro, veja este exemplo:

O código C para um programa C simples é fornecido abaixo

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

O código ASM equivalente para 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    

Como você pode ver que uma atribuição é simplesmente substituída por uma instrução "mov" na montagem, o operador de atribuição significa simplesmente mover dados de um local de memória para outro local de memória. A atribuição fará isso apenas para membros imediatos de uma estrutura e falhará ao copiar quando você tiver tipos de dados complexos em uma estrutura. Aqui, COMPLEXO significa que você não pode ter uma matriz de ponteiros, apontando para listas.

Uma matriz de caracteres dentro de uma estrutura não funcionará na maioria dos compiladores, porque a atribuição simplesmente tentará copiar sem sequer olhar para o tipo de dados de tipo complexo.

Arun Kaushal
fonte
2
U pode elaborar sobre que condições ele seria um fracasso porque ele parece funcionar para mim sempre
AlphaGoku
15

Esta é uma cópia simples, como você faria com memcpy()(de fato, alguns compiladores realmente produzem uma chamada memcpy()para esse código). Não há "string" em C, apenas ponteiros para um monte de caracteres. Se sua estrutura de origem contiver esse ponteiro, ele será copiado, não os caracteres.

Thomas Pornin
fonte
OK, então o compilador traduz isso memcpy, veja aqui: godbolt.org/z/nPxqWc - Mas agora se eu passar ponteiros idênticos ae b, e *a = *bé traduzido para um memcpyque é um comportamento indefinido, porque para memcpy"As áreas de memória não devem se sobrepor." (citando na página do manual). Então, o compilador está errado ao usar memcpyou estou errado ao escrever essa tarefa?
não usuário
6

Você quis dizer "complexo" como em número complexo com partes reais e imaginárias? Isso parece improvável; portanto, caso contrário, você teria que dar um exemplo, já que "complexo" não significa nada específico em termos da linguagem C.

Você receberá uma cópia de memória direta da estrutura; se é isso que você deseja depende da estrutura. Por exemplo, se a estrutura contiver um ponteiro, as duas cópias apontarão para os mesmos dados. Isso pode ou não ser o que você deseja; isso depende do design do seu programa.

Para executar uma cópia 'inteligente' (ou uma cópia 'profunda'), você precisará implementar uma função para executar a cópia. Isso pode ser muito difícil de conseguir se a própria estrutura contiver ponteiros e estruturas que também contenham ponteiros e talvez ponteiros para essas estruturas (talvez seja o que você quer dizer com "complexo"), e é difícil de manter. A solução simples é usar C ++ e implementar construtores de cópia e operadores de atribuição para cada estrutura ou classe, e cada um se torna responsável por sua própria semântica de cópia, você pode usar a sintaxe da atribuição e é mais fácil de manter.

Clifford
fonte