Edit page

Tipos Abstratos de Dados

Nos nossos projetos, vamos notar que vamos tentar usar as mesmas estruturas com tipos de dados diferentes: pensando em listas ligadas, por exemplo, queremos que a sua implementação seja idealmente independente do tipo de dados com que estamos a trabalhar. Para isso, implementamos abstrações no nosso código, com vista a que os detalhes de implementação não sejam dependentes de um só tipo de dados. Cada operação deve ser uma espécie de "módulo", reutilizável por qualquer tipo.

TAD - Introdução

São acima de tudo soluções elegantes que nos permitem separar o alto do baixo nível: as interfaces (vulgo cabeçalhos de funções) com que interagimos para efetuar operações está separada da manutenção da implementação.

Por norma, e para manter camadas de abstração (estruturas de dados diferentes não têm de conhecer a implementação de outras estruturas), costumamos implementar cada um destes tipos abstratos de dados em ficheiros diferentes, que funcionam como módulos. Cada módulo deve incluir a especificação e documentação do tipo de dados que o acompanha, bem como um conjunto de funcionalidades bem definido e coerente.

Geralmente, os módulos andam "aos pares": um módulo costuma ser composto por um ficheiro .h, header file, onde consta a documentação e especificação das operações do módulo, bem como possíveis definições de constantes que sejam necessárias para a implementação da estrutura em questão e a própria definição da estrutura. O .c irá conter o source code, a implementação em si. Um módulo pode, claro, ser dividido em vários ficheiros (pode dar jeito para uma maior separação de "propósitos", por exemplo).

Nos slides costumam constar implementações de filas e pilhas recorrendo a modelos abstratos.

Regras de Scope

Em C, todas as variáveis têm um scope, modo de acessibilidade e visibilidade. Podem ter 3 estados:

  • Bloco - variáveis locais;

  • Ficheiro - variáveis globais, apenas visíveis dentro do ficheiro onde são declaradas;

  • Programa - variáveis globais visíveis em múltiplos ficheiros.

Block Scope

Variáveis apenas visíveis dentro do scope (delimitada por { }) em que foram declaradas. Existem ainda variáveis static, que apesar de continuarem a ser apenas visíveis dentro do respetivo scope, são inicalizadas apenas uma vez no decorrer do programa. Isto é, se a função passar duas vezes por um dado bloco, a variável na segunda passagem inicia com o valor que terminou a primeira passagem, não com o valor de inicialização.

int soma(int v[], int n) {
    int i;
    static int soma = 0;
    for (i = 0; i < n; i++) {
        soma += v[i];
    }
    return soma;
}

Variáveis Globais

Variáveis visíveis em qualquer função, já que não têm um scope associado por definição. Podem ser utilizadas por qualquer função.

int acumulador;

void soma(int valor) {
    acumulador += valor;
}

Podemos definir variáveis globais como estáticas, por forma a limitar o seu scope ao ficheiro em que estão definidas. Mais ainda, também podemos definir funções estáticas: funções que só podem ser chamadas por outras funções que sejam definidas até ao fim do ficheiro em que ela foi.

static int acumulador;

static void soma(int valor) {
    acumulador += valor;
}

// mais funções, que podem ou não chamar soma