INF 01126 Estruturas de Dados I Estruturas de

  • Slides: 42
Download presentation
INF 01126 - Estruturas de Dados I Estruturas de Dados Genéricas (em C) Gabriel

INF 01126 - Estruturas de Dados I Estruturas de Dados Genéricas (em C) Gabriel Mattos Langeloh gmlangeloh@inf. ufrgs. br *material do prof. Anderson Rocha Tavares

Estruturas de Dados: generalidade ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no

Estruturas de Dados: generalidade ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no início) em uma lista de inteiros 8 42 prox ini prox Antes: 8 42 prox 15 prox ini prox Depois:

Estruturas de Dados: generalidade ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no

Estruturas de Dados: generalidade ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no início) em uma lista de floats 8. 0 4. 2 prox ini prox Antes: 8. 0 4. 2 prox 0. 15 prox ini prox Depois:

Estruturas de Dados: generalidade ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no

Estruturas de Dados: generalidade ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no início) em uma lista ● Códigos diferentes : ( typedef struct st_nodo_int { int dado; struct st_nodo_int *prox; } t_nodo_int; int inserir_ini(t_lista_int *l, int dado); typedef struct st_nodo_float { float dado; struct st_nodo_float *prox; } t_nodo_int; int inserir_ini(t_lista_float *l, float dado);

Estruturas de Dados Genéricas: ideia ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir

Estruturas de Dados Genéricas: ideia ● Dados diferentes ● Mesmas operações ○ Exemplo: inserir (no início) em uma lista ● Mesmo código : ) typedef struct st_nodo<TIPO> { TIPO dado; struct st_nodo<TIPO> *prox; } t_nodo; int inserir_ini(t_lista *l, <TIPO> dado); ● C++ permite construções desse tipo, mas C não : (

Tipos parametrizados ● Exemplo em C++ (https: //repl. it/@andertavares/Cpp-Generic-List-Example ) #include <iostream> #include <list>

Tipos parametrizados ● Exemplo em C++ (https: //repl. it/@andertavares/Cpp-Generic-List-Example ) #include <iostream> #include <list> using namespace std; int main () { list<int> ints; list<float> floats; ints. push_front(7); ints. push_front(9); floats. push_front(4. 55); floats. push_front(2 e 20); . . . return 0; } Saída:

Estruturas genéricas em C ● C não permite tipos parametrizados : ( ● Mas

Estruturas genéricas em C ● C não permite tipos parametrizados : ( ● Mas possui: ○ Tipos de dados genéricos ■ Vamos armazenar o espaço necessário para os dados e copiar os bytes quando o usuário mandar inserir ○ Recursos para customizar as funções ■ O usuário irá fornecer a funções para coisas específicas de cada tipo

Exemplo: Listas genéricas em C typedef struct st_nodo { void* dado; struct st_nodo *prox;

Exemplo: Listas genéricas em C typedef struct st_nodo { void* dado; struct st_nodo *prox; } t_nodo; void*: ponteiro sem tipo (referencia qualquer tipo de endereço) typedef st_lista { int tam_dado; //numero de bytes que um elemento ocupa t_nodo *ini; } t_lista;

Listas genéricas em C - Ilustração typedef struct st_nodo { void* dado; struct st_nodo

Listas genéricas em C - Ilustração typedef struct st_nodo { void* dado; struct st_nodo *prox; } t_nodo; typedef st_lista { int tam_dado; //numero de bytes que um elemento ocupa t_nodo *ini; } t_lista; ini dado prox

Listas genéricas em C - Exemplo (ints) typedef struct st_nodo { void* dado; struct

Listas genéricas em C - Exemplo (ints) typedef struct st_nodo { void* dado; struct st_nodo *prox; } t_nodo; typedef st_lista { int tam_dado; //numero de bytes que um elemento ocupa t_nodo *ini; } t_lista; 8 15 -1 ini dado prox

Listas genéricas em C - Exemplo (strings) typedef struct st_nodo { void* dado; struct

Listas genéricas em C - Exemplo (strings) typedef struct st_nodo { void* dado; struct st_nodo *prox; } t_nodo; typedef st_lista { int tam_dado; //numero de bytes que um elemento ocupa t_nodo *ini; } t_lista; “Joao” “roupa” “teatro” ini dado prox

Listas genéricas em C - Interface, parte 1 typedef struct st_nodo {. . .

Listas genéricas em C - Interface, parte 1 typedef struct st_nodo {. . . } t_nodo; typedef struct st_lista {. . . } t_lista; void inicializar(t_lista *l, int tam_dado); void inserir_ini(t_lista *l, void *dado); void* consultar(t_lista *l, int pos); int tamanho(t_lista *l); /* Exemplo de uso de uma lista generica */ t_lista minha_lista; inicializar(&minha_lista, sizeof(int)); //lista de inteiros int numero = 2, indice = 3; inserir(&minha_lista, &numero); int* res = (int *) consultar(&minha_lista, indice); if (res == NULL) { printf(“Nao encontrado!n”); }. . .

Listas Genéricas: inicialização Procedimentos necessários: 1. Estabelece as condições iniciais necessárias void inicializar(t_lista *l,

Listas Genéricas: inicialização Procedimentos necessários: 1. Estabelece as condições iniciais necessárias void inicializar(t_lista *l, int tam_dado) { l->tam_dado = tam_dado; l->ini = NULL; }

Listas Genéricas: inserção no início Procedimentos necessários: 1. Aloca espaço para armazenar nodo 2.

Listas Genéricas: inserção no início Procedimentos necessários: 1. Aloca espaço para armazenar nodo 2. Aloca espaço para armazenar dado e o copia 3. Encadeia elemento int inserir(t_lista_enc *l, void* dado, int pos){ t_nodo *novo = NULL; // Ponteiro para novo elemento (1) novo = (t_nodo *) malloc(sizeof(t_nodo)); // Aloca um novo nodo if (novo == NULL) return 0; // falta de memoria (2) novo->dado = malloc(l->tam_dado); // aloca espaco para o dado (3) if(novo->dado == NULL) return 0; // testa falta de memoria memcpy(novo->dado, l->tam_dado); // copia o dado novo->prox = l->ini; l->ini = novo;

Listas Genéricas: consulta (implementação) Procedimentos necessários: 1. Percorre a lista até a posição desejada

Listas Genéricas: consulta (implementação) Procedimentos necessários: 1. Percorre a lista até a posição desejada 2. Retorna o endereço do dado apropriadamente void *consulta(t_lista_enc *l, int pos) { int i = 0; t_nodo *atual = l-> ini; // Aux. para percorrer a lista (1) (2) } while (atual != NULL && i<pos) { atual = atual->prox; i++; } if (atual != NULL) return &(atual->dado); else return NULL;

Listas Genéricas: tamanho (implementação) Procedimentos necessários: 1. Conta o número de elementos int tamanho(t_lista_enc

Listas Genéricas: tamanho (implementação) Procedimentos necessários: 1. Conta o número de elementos int tamanho(t_lista_enc *l){ int cont = 0; t_nodo *atual = l>ini; while (atual != NULL) { cont++; atual = atual ->prox; } return cont; } LEMBRETE: versão não otimizada!

Listas genéricas em C - Interface, parte 2 typedef struct st_nodo {. . .

Listas genéricas em C - Interface, parte 2 typedef struct st_nodo {. . . } t_nodo; typedef struct st_lista {. . . } t_lista; void inicializar(t_lista *l, int tam_dado); void inserir_ini(t_lista *l, void *dado); void* consultar(t_lista *l, int pos); int tamanho(t_lista *l); void print_lista(t_lista *l); int buscar(t_lista *l); int remover(t_lista *l); void destruir(t_lista *l); Essas funções não vão funcionar assim, vamos ver o porquê.

Listas Genéricas: imprimir Se a lista fosse de inteiros. . . void print_lista(t_lista *l){

Listas Genéricas: imprimir Se a lista fosse de inteiros. . . void print_lista(t_lista *l){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ printf("%d", atual->dado); if(atual->prox != NULL) printf(", "); } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir Se a lista fosse de floats… void print_lista(t_lista *l){ t_nodo *atual;

Listas Genéricas: imprimir Se a lista fosse de floats… void print_lista(t_lista *l){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ printf("%f", atual->dado); if(atual->prox != NULL) printf(", "); } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir Se a lista fosse de structs. . . void print_lista(t_lista *l){

Listas Genéricas: imprimir Se a lista fosse de structs. . . void print_lista(t_lista *l){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ printf("%d, ", atual->dado. algo_int); printf("%s", atual->dado. algo_string); É possível customizar o if(atual->prox != NULL) printf("n"); separador de itens? } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir Se a lista fosse de structs. . . void print_lista(t_lista *l,

Listas Genéricas: imprimir Se a lista fosse de structs. . . void print_lista(t_lista *l, char* sep){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ printf("%d, ", atual->dado. algo_int); É possível customizar printf("%s", atual->dado. algo_string); o(s) comando(s) de if(atual->prox != NULL) printf("%s", sep); imprimir? } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir Se a lista fosse de structs. . . A ideia está

Listas Genéricas: imprimir Se a lista fosse de structs. . . A ideia está correta, mas a sintaxe, não. void print_lista(t_lista *l, comando_imprime, char* sep){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ comando_imprime(atual->dado); if(atual->prox != NULL) printf("%s", sep); } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir Em C, é possível passar funções como parâmetro Necessário identificar a

Listas Genéricas: imprimir Em C, é possível passar funções como parâmetro Necessário identificar a função (retorno e parâmetros) void print_lista(t_lista *l, comando_imprime, char* sep){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ comando_imprime(atual->dado); if(atual->prox != NULL) printf("%s", sep); } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir Em C, é possível passar funções como parâmetro Necessário identificar a

Listas Genéricas: imprimir Em C, é possível passar funções como parâmetro Necessário identificar a função (retorno e parâmetros) void print_lista(t_lista *l, void func_imprime(void* dado), char* sep){ t_nodo *atual; printf("["); //1. marcador de 'inicio da lista' //2. exibir cada elemento for(atual = l->ini; atual != NULL; atual = atual->prox){ func_imprime(atual->dado); if(atual->prox != NULL) printf("%s", sep); } printf("]n"); //3. marcador de 'fim da lista' }

Listas Genéricas: imprimir O usuário fornece a função que imprime o dado Exemplos: void

Listas Genéricas: imprimir O usuário fornece a função que imprime o dado Exemplos: void print_int(void* dado){ int* endereco = (int*) dado; printf("%d", *endereco); } void print_float(void* dado){ float* endereco = (float*) dado; printf("%f", *endereco); } void print_string(void* dado){ char** endereco = (char**) dado; printf("%s", *endereco); }

Listas genéricas em C - Interface, parte 2 typedef struct st_nodo {. . .

Listas genéricas em C - Interface, parte 2 typedef struct st_nodo {. . . } t_nodo; typedef struct st_lista {. . . } t_lista; void inicializar(t_lista *l, int tam_dado); void inserir_ini(t_lista *l, void *dado); void* consultar(t_lista *l, int pos); int tamanho(t_lista *l); void print_lista(t_lista *l, void func_imprime(void *), char *sep); int buscar(t_lista *l, void *dado, int func_compara(void *, void *)); int remover(t_lista *l, int pos, void func_libera(void *)); void destruir(t_lista *l, void func_libera(void *)); Usuário fornece as funções para imprimir, comparar e liberar dados

Listas Genéricas: remoção Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula ponteiros

Listas Genéricas: remoção Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula ponteiros 3. Desaloca memória para o elemento a ser removido int remover(t_lista_enc *l, int pos, void func_libera(void *)) { int i = 0; t_nodo *ant = NULL; // Aux. p/ anterior t_nodo *atual = l->ini; // Aux. p/ percorrer lista while(atual != NULL && i < pos) { // percorre ant = atual; atual = atual->prox; i++; }. . . }

Listas Encadeadas: remoção (implementação) Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula

Listas Encadeadas: remoção (implementação) Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula ponteiros 3. Desaloca memória para o elemento a ser removido int remover(t_lista_enc *l, int pos, void func_libera(void *)) {. . . if(atual == NULL) return 0; //nao achou if(ant == NULL) { // Vai remover o 1 o l->ini = atual->prox; // ou l->ini = null; } else { // Vai remover do meio ou final ant->prox = atual->prox; }. . . }

Listas Encadeadas: remoção (implementação) Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula

Listas Encadeadas: remoção (implementação) Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula ponteiros 3. Desaloca memória para o elemento a ser removido int remover(t_lista_enc *l, int pos, void func_libera(void *)) {. . . if(func_libera != NULL) func_libera(atual->dado); //apaga dado free(atual); //apaga nodo return 1; }

Listas Encadeadas: remoção (implementação) Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula

Listas Encadeadas: remoção (implementação) Procedimentos necessários: 1. Procura elemento a ser removido 2. Manipula ponteiros 3. Desaloca memória para o elemento a ser removido int remover(t_lista_enc *l, int pos, void func_libera(void *)) {. . . //exemplo: if(func_libera != NULL) int libera_string(void* d 1){ func_libera(atual->dado); //apaga dado free(atual); //apaga d 1; nodo char** e 1 = (char**) free(*e 1); return 1; } }

Listas Encadeadas: destruição (implementação) Procedimentos necessários: 1. Remove todos os elementos, liberando memória 2.

Listas Encadeadas: destruição (implementação) Procedimentos necessários: 1. Remove todos os elementos, liberando memória 2. (reaproveitando código) void destruir(t_lista_enc *l, void func_libera(void *)) { while(remover(l, 0, func_libera)); }

Listas Encadeadas: buscar (implementação) Procedimentos necessários: 1. Procura pelo elemento na lista 2. Retorna

Listas Encadeadas: buscar (implementação) Procedimentos necessários: 1. Procura pelo elemento na lista 2. Retorna a posição do elemento int buscar(t_lista *l, void* dado, int func_compara(void *, void *)){ t_nodo *atual = l->ini; // Aux. para percorrer a lista int i = 0; while (atual != NULL && func_compara(atual->dado, dado) != 0){ atual = atual->prox; i++; } if (atual == NULL) return -1; //chegou ao fim e nao achou return i; }

Listas Encadeadas: buscar (implementação) Procedimentos necessários: 1. Procura pelo elemento na lista 2. Retorna

Listas Encadeadas: buscar (implementação) Procedimentos necessários: 1. Procura pelo elemento na lista 2. Retorna a posição do elemento //exemplo: int buscar(t_lista *l, void* dado, int func_compara(void *, void *)){ int compara_string(void* d 1, void* d 2){ t_nodo *atual = l->ini; // Aux. para percorrer a lista char** e 1 = (char**) d 1; int i = 0; char** e 2 = (char**) d 2; return strcmp(*e 1, *e 2 ); while (atual != NULL && func_compara(atual->dado, dado) != 0){ } atual = atual->prox; //exemplo i++; (o retorno é no estilo de strcmp): } compara_int(void* d 1, void* d 2){ int if int* (atual NULL) return -1; //chegou ao fim e nao achou e 1 == = (int*) d 1; } int* e 2 = (int*) d 2; return i; ( *e 1 - *e 2 ); }

Para saber mais: ponteiros para funções

Para saber mais: ponteiros para funções

Ponteiros para funções ● Na verdade, estamos passando o endereço das funções como parâmetro

Ponteiros para funções ● Na verdade, estamos passando o endereço das funções como parâmetro

Ponteiros para funções ● Função: sequência de instruções para o processador ● Instruções: palavras

Ponteiros para funções ● Função: sequência de instruções para o processador ● Instruções: palavras binárias que residem na memória ● Programa: somatório de todas as suas funções ○ main() é uma função ; )

Ponteiros para funções ● Função: sequência de instruções para o processador ● Instruções: palavras

Ponteiros para funções ● Função: sequência de instruções para o processador ● Instruções: palavras binárias que residem na memória ● Programa: somatório de todas as suas funções ○ main() é uma função ; )

Ponteiros para funções ● Ponteiros: armazenam endereços de memória ○ Inclusive o de instruções

Ponteiros para funções ● Ponteiros: armazenam endereços de memória ○ Inclusive o de instruções ● Um ponteiro para uma função armazena o endereço da sua primeira instrução

Ponteiros para funções - Sintaxe ● Declarar um ponteiro para função: tipo_retorno (*nome)(tipo_param 1,

Ponteiros para funções - Sintaxe ● Declarar um ponteiro para função: tipo_retorno (*nome)(tipo_param 1, tipo_param 2, . . . ); tipo_retorno nome(tipo_param 1, tipo_param 2, . . . ); ● Exemplo: // tambem vale

Ponteiros para funções - Sintaxe ● Declarar um ponteiro para função: tipo_retorno nome(tipo_param 1,

Ponteiros para funções - Sintaxe ● Declarar um ponteiro para função: tipo_retorno nome(tipo_param 1, tipo_param 2, . . . ); ● Chamar a função através do ponteiro: var = nome(lista, de, parametros); ● Exemplo: Antes de usar, o ponteiro precisa receber o endereço da função.

Ponteiros para funções - Exemplo: https: //ide. geeksforgeeks. org/8 Ja. J 6 k. St

Ponteiros para funções - Exemplo: https: //ide. geeksforgeeks. org/8 Ja. J 6 k. St Declaração Atribuição Uso

Ponteiros para funções: outro exemplo https: //repl. it/@andertavares/Function. Parameter. Example

Ponteiros para funções: outro exemplo https: //repl. it/@andertavares/Function. Parameter. Example