Subsecções

27 Estruturas

A estrutura de dados array é usada para conter dados do mesmo tipo junto. Dados de tipos diferentes também podem ser agregados em tipos chamados de estruturas ou registros (tipo struct em linguagem C). Primeiro, o tipo estrutura é declarado (precisamos especificar que tipos de variáveis serão combinados na estrutura), e então variáveis deste novo tipo podem ser definidas (de maneira similar que usamos para definir variáveis do tipo int ou char).

27.1 Declaração de Estruturas

Uma declaração de estrutura declara um tipo struct. Cada tipo struct recebe um nome (ou tag). Refere-se àquele tipo pelo nome precedido pela palavra struct. Cada unidade de dados na estrutura é chamada membro e possui um nome de membro. Os membros de uma estrutura podem ser de qualquer tipo. Declarações de estrutura não são definições. Não é alocada memória, simplesmente é introduzida um novo tipo de estrutura.

Geralmente declarações de estruturas são globais. Elas são colocadas próximas ao topo do arquivo com o código fonte do programa, assim elas são visíveis por todas as funções (embora isto dependa de como a estrutura está sendo usada).

A forma padrão de declaração de uma estrutura é:



struct nome-estrutura {

declaração dos membros

} definição de variáveis (optional);



Abaixo se apresenta um exemplo de um tipo estrutura que contém um membro do tipo int e um outro membro do tipo char.

          struct facil {
              int num;
              char ch;
          };

Esta declaracao cria um novo tipo chamado struct facil que contém um inteiro chamado num e um caracter chamado ch.

27.2 Definição de variáveis de um tipo estrutura declarado

Como acontece com qualquer outro tipo de dados, variáveis de tipos de estruturas são definidas fornecendo o nome do tipo e o nome da variável. Considere a definição abaixo relativa a uma variável com o nome fac1 que é do tipo struct facil:

          struct facil fac1;

Tal definição está associada com a alocação de memória: memória suficiente será alocada para guardar um int e um char (nesta ordem). Como qualquer outra variável, fac1 tem um nome, um tipo, e um endereço associados.

Variáveis de estruturas possuem também valores, e como outras variáveis locais, se elas não tem atribuídas um valor específico, seu valor é indefinido.

É possível definir variáveis durente a declaração do tipo estrutura:

          struct facil {
              int num;
              char ch;
          } fac1;

Note-se que sem conflito, nomes de membros (tais como num e ch) podem ser usados como nomes de outras variáveis independentes (fora do tipo estrutura definido) or como nomes de membros em outros tipos estrutura. No entanto, deve-se evitar situações que criem confusão.

27.3 Acesso a membros de uma estrutura: ponto (.), o operador membro de estrutura

Dada uma variável de estrutura, um membro específico é referenciado usando o nome da variável seguida de . (ponto) e pelo nome do membro da estrutura. Assim, as seguintes referências a membros de uma estrutura são válidas:

fac1.num se refere ao membro com nome num na estrutura fac1;

fac1.ch se refere ao membro com nome ch na estrutura fac1.

Membros de estrutura (como fac1.num) são variáveis, e podem ser usadas como valores (no lado direito de uma atribuição, em expressões como argumentos para funções), ou como lvalues (no lado esquerdo de atribuições, com operadores de incremento/decremento ou com o operador de endereço (&)). O exemplo abaixo mostra alguns exemplos do uso de membros de estrutura:

          fac1.ch = 'G';
          fac1.num = 42;

          fac1.num++;

          if (fac1.ch == 'H') {
             printf("%d\n", fac1.num);
          }

Tentar acessar um nome de membro que não existe causa um erro de compilação.

27.4 Operadores usados com variáveis de estrutura: valores e lvalues

Uma variável de estrutura pode ser tratada como um objeto simples no todo, com um valor específico associado a ela (a estrutura fac1 tem um valor que agrega valores de todos os seus membros). Note a diferença com arrays: se arr[] é um array de tamanho 2 definedo como int arr[2] = {0,1};, o nome arr2 não se refere ao valor coletivo de todos os elementos do array. Na verdade, arr2 é um ponteiro constante e se refere ao endereço de memória onde o array se inicia. Além disso arr2 não é um lvalue e não pode ser mudado. Variáveis de estrutura são diferentes. Elas podem ser usadas como valores e lvalues, mas com certas limitações.

Os únicos usos válidos de uma variável de estrutura são dos dois lados de um operador de atribuição (=), como operando do operador de endereço & (obtendo o endereço da estrutura), e referenciando seus membros.

De todas as variações de atribuição (incluindo o incremento e decremento) atribuição de estruturas pode ser usada APENAS com =. O uso de outros operadores de atribuição ou de incremento causará um erro de compilação A atribuição de um valor de estrutura para outro copia todos os membros de uma estrutura para outra. Mesmo que um dos membros seja um array ou outra estrutura, ela é copiada integralmente. As duas estruturas envolvidas na atribuição devem ser do mesto tipo struct. Considere o seguinte exemplo:

          struct facil {
              int num;
              char ch;
          };

          main()
          {
              struct facil fac1, fac2;
              
              fac1.num = 3;
              fac1.ch = 'C';

              /* Atribuindo fac1 a fac2 */
              fac2 = fac1;
          }

Lembre-se que este tipo de atribuição é ilegal com arrays. Tentar fazer isto com dois arrays causa um erro de compilação (uma vez que nomes de arrays são ponteiros constantes).

           int a[5], b[5];

           /* Está errado -- Não irá compilar */
           a = b;

27.5 Inicialização de estruturas

Variáveis de estruturas não-inicializadas contém valores indefinidos em cada um de seus membros. Como em outras variáveis, variáveis de estruturas podem ser inicializadas ao serem declaradas. Esta inicialização é análoga ao que é feito no caso de arrays. O exemplo abaixo ilustra a inicialização de estruturas:

          struct facil {
              int num;
              char ch;
          };

          main()
          {
              struct facil fac1 = { 3, 'C' }, fac2;

              fac2 = fac1;
          }

Uma lista de valores separados por vírgula fica entre chaves ({ and }). Os valores de inicialização devem estar na mesma ordem dos membros na declaração da estrutura.

27.6 Estruturas como argumentos de função e valores de retorno

Como qualquer outro valor do tipo int ou float, valores de estruturas podem ser passados como argumentos para funções, e podem ser retornados de funções. O exemplo abaixo ilustra tal prorpiedade:

    #define LEN 50

    struct endereco {
        char rua[LEN];
        char cidade_estado_cep[LEN];
    };

    struct endereco obtem_endereco(void);
    void imprime_endereco(struct endereco);

    struct endereco obtem_endereco(void)
    {
         struct endereco ender;

         printf("\t Entre rua: ");
         gets(ender.rua);
         printf("\t Entre cidade/estado/cep: ");
         gets(ender.cidade_estado_cep);

         return ender;
    }

    void imprime_endereco(struct endereco ender)
    {
         printf("\t %s\n", ender.rua);
         printf("\t %s\n", ender.cidade_estado_cep);
    }

    main()
    {
         struct endereco residencia;

         printf("Entre seu endereco residencial:\n");
         residencia = obtem_endereco();

         printf("\nSeu endereco eh:\n");
         imprime_endereco(residencia);
    }

No exemplo acima, a estrutura struct endereco contém dois arrays de tamanho 50. Dentro da função obtem_endereco(), a variável ender é declarada como sendo do tipo struct endereco. Após usar gets() para o fornecimento da informação, o valor de ender é retornado para main(), de onde a função obtem_endereco() foi chamada. Este valor é então passado para a função imprime_endereco(), onde o valor de cada membro da estrutura é exibido na tela.

Este programa pode ser comparado ao programa abaixo, que usa valores do tipo int no lugar de valores do tipo struct endereco (claro que a informação lida e exibida é um simples valor numérico, e não um nome de rua, etc.):

    int obtem_int(void);
    void imprime_int(int);

    int obtem_int(void)
    {
         int i;

         printf("Entre valor: ");
         scanf("%d", &i);

         return i;
    }

    void imprime_int(int i)
    {
         printf("%d\n", i);
    }

    main()
    {
         int valor;

         valor = obtem_int();

         printf("\nSeu valor:\n");
         imprime_int(valor);
    }

27.7 Arrays de estruturas

Arrays de estruturas são como arrays de qualquer outro tipo. Eles são referenciados e definidos da mesma forma. O exemplo abaixo é análogo ao exemplo de endereço apresentado anteriormente, exceto que uma quantidade de NUM endereços é armazenada ao invés de apenas um.

    #define LEN 50
    #define NUM 10

    struct endereco {
        char rua[LEN];
        char cidade_estado_cep[LEN];
    };

    void obtem_endereco(struct endereco [], int);
    void imprime_endereco(struct endereco);

    void obtem_endereco(struct endereco aender [], int index)
    {
         printf("Entre rua: ");
         gets(aender[index].rua);
         printf("Entre cidade/estado/cep: ");
         gets(aender[index].cidade_estado_cep);
    }

    void imprime_endereco(struct endereco ender)
    {
         printf("%s\n", ender.rua);
         printf("%s\n", ender.cidade_estado_cep);
    }

    main()
    {
         struct endereco residencias[NUM];
         int i;

         for (i = 0; i < NUM; i++) {
             printf("Entre o endereco da pessoa %d:\n", i);
             obtem_endereco(residencias,i);
         }

         for (i = 0; i < NUM; i++) {
             printf("endereco da pessoa %d:\n", i);
             imprime_endereco(residencias[i]);
         }
    }

Neste programa, o array residencias é passado para obtem_endereco(), juntamente com o indice onde deve ser guardado o novo endereço. Depois, cada elemento do array é passado para imprime_endereco() um por vez.

Observe-se ainda na função obtem_endereco() como os membros de cada elemento do array podem ser acessados. elements da estrutura em can be accessed as well. Por exemplo, para acessar a rua do elemento residencias[0] usa-se:

          printf("%s\n", residencias[0].rua);
          printf("%s\n", residencias[0].cidade_estado_cep);

27.8 Estruturas aninhadas

Como definido anteriormente, membros de estruturas podem ser de qualquer tipo. Isto inclui outras estruturas. Abaixo define-se duas estruturas, a segunda tendo membros que são também estruturas:

    #define LEN 50

    struct endereco {
        char rua[LEN];
        char cidade_estado_cep[LEN];
    };

    struct student {
        char id[10];
        int idade;
        struct endereco casa;
        struct endereco escola;
    };

    struct student pessoa;

Dadas estas definições, pode-se potencialmente acessar os seguintes campos de pessoa, uma variável do tipo struct student:

           pessoa.id
           pessoa.casa.rua
           pessoa.casa.cidade_estado_cep
           pessoa.escola.rua
           pessoa.escola.cidade_estado_cep

Note o uso repetido de . quando se acessa membros dentro de membros.

Armando Luiz Nicolini Delgado
2013-10-21