Edit page

Estruturas

Definição

Porquê uma estrutura?

Imaginemos que somos um professor, estando atualmente no processo de inserir a nota de um aluno a uma dada UC no Fénix. Ora, para tal, precisamos:

  • do seu número de aluno;
  • da sigla da disciplina (máx: 6 caracteres);
  • da nota obtida.

Exemplo: 42069 IAED 20

Ora, representar algo deste género em C é possível de várias maneiras. Algumas delas são:

  • Recorrendo a um vector. Problema: Todos os dados precisam de ser do mesmo tipo!
  • Poderíamos, então, recorrer a 3 vectores do mesmo tamanho. Ora, mas isso acabava por ser relativamente desagradável, adicionando uma camada de complexidade adicional indesejável (para além de ter de haver nn vectores se quiséssemos nn propriedades - não ideal).

A solução "correta" em C passa por recorrer a estruturas, structs, para representar dados agregados de tipos diferentes.

Exemplos

Uma estrutura permite definir estruturas de dados sofisticadas, as quais possibilitam a agregação de diferentes tipos de declarações.

struct inscricao {
    double numero;
    char disciplina[7];
    int nota;
};

Declaração de Variáveis

A definição da estrutura introduz um novo tipo de dados: podemos agora passar a usar a estrutura como qualquer outro tipo de dados nativo a C, como um int ou um char.

struct ponto { /* Definição da estrutura */
    double x;
    double y;
};

struct ponto centro; /* Definição da variável com nome centro */

struct ponto centro = {12.3, 5.2}; /* Iniciar as variáveis */

Manipulação de Variáveis

A manipulação de propriedades de estruturas tem uma sintaxe bastante intuitiva: acedemos à propriedade via <variavel>.<membro>, e é também assim que a podemos alterar.

centro.x = 12.3;
centro.y = 5.2;

Nota

As estruturas permitem incluir outras estruturas.

struct rectangulo {
    struct ponto a, b;
};

struct rectangulo rect;

rect.a.x = 3.4;
rect.b.y = 6.1;

danger

Estruturas não podem ser comparadas usando o usual operador de comparação de igualdade.

Assim sendo, se queremos determinar se duas estruturas são iguais, é necessário comparar cada campo da estrutura.

Funções

A funções podem, claro, receber e retornar estruturas:

struct ponto adiciona_ponto(struct ponto p1, struct ponto p2) {
    struct ponto res;

    /*
     * Podíamos, alternativamente, fazer a soma sobre um dos argumentos
     * pois não alteraria o valor original de p1 e p2 fora da função
     */

    res.x = p1.x + p2.x;
    res.y = p1.y + p2.y;

    return res;
}

A função em questão retorna uma cópia da estrutura res. A passagem de argumentos é feita por valor, não por referência: chamar adiciona_ponto(pa, pb) não altera os valores de pa nem de pb.

Vetores de Estruturas

Permite, claro, criar tabelas que guardam estruturas:

struct ponto {
    double x;
    double y;
};

/* Cria uma tabela que guarda 100 pontos de nome figura */
struct ponto figura[100];

Podemos inicializar tabelas de estruturas com valores iniciais pré-definidos:

struct ponto {
    double x;
    double y;
};

/*
 * Estamos a declarar um vetor de "pontos"
 * de DIM 3, onde cada ponto tem as suas
 * coordenadas definidas
 */
struct ponto figura[] = {
    {1.2, 3.4},
    {4.5, 12.6},
    {1.2, 10.8}
};

Typedef

typedef permite associar um nome a um tipo de dados já existente. Para tal, basta adicionar a keyword typedef antes de struct, e escrever o nome a que queremos associar o novo tipo de dados após a definição da estrutura. Funcionam como uma espécie de alcunhas: podemos sempre usar struct <struct-name> para referenciar a estrutura, mas o typedef acaba por poder poupar key-strokes.

typedef struct complexo { /* nome do tipo de dados */
    float real;
    float imag;
} Complexo; /* "Alcunha" */

/* O nome e a alcunha devem ter nomes diferentes (Case Sensitive) */

Complexo k1;        /* exemplo de declaração de variável */
struct complexo k2; /* exemplo equivalente ao anterior */

Definir variáveis continua a ser bastante simples: basta escrever<alcunha> <nome da variavel> ou struct <nome> <nome da variavel>.