• Nenhum resultado encontrado

Algoritmos e estruturas de dados: partes centrais de qualquer software. Escolher a solução mais adequada

N/A
N/A
Protected

Academic year: 2021

Share "Algoritmos e estruturas de dados: partes centrais de qualquer software. Escolher a solução mais adequada"

Copied!
76
0
0

Texto

(1)
(2)

 Algoritmos e estruturas de dados:

partes centrais de qualquer software

 Escolher a solução mais adequada

Entender os compromissos de diferentes

estruturas de dados

(3)
(4)

 Tipos abstratos de dados  Análise de algoritmos

O(n), O(log(n)), etc

 Estruturas de dados

Listas, filas, pilhas e árvores

 Métodos de ordenação

Quicksort, heapsort, etc

 Métodos de pesquisa

Hashing, árvores balanceadas

Parte 1 Prova 1 Trabalho prático 1 Parte 2 Prova 2 Trabalho prático 2 Parte 3 Prova 3 Trabalho prático 3

(5)

 Projeto de Algoritmos

 Nívio Ziviani

 Introduction to Algorithms

Cormen, Leiserson, Rivest, Stein

 Algorithms

Robert Sedgewick e Kevin Wayne

 The Art of Computer Programming

 Volumes 1 e 3

(6)

 3 provas (20+20+20 = 60 pontos)  1 prova suplementar  4 trabalhos práticos (4+10+10+16 = 40 pts)  Implementação  Documentação  Teste

(7)

 Página Web da disciplina

http://www.dcc.ufmg.br/~cunha/

 Linguagem de programação: C

 CodeBlocks

GCC

 Sistema operacional recomendado: Linux

Os trabalhos práticos precisam rodar no Linux

(8)
(9)

 Eficiência

Construções similares a instruções de máquina

 Acesso direto à memória  Portabilidade

De microcontroladores a supercomputadores

 Poucos requisitos para execução

(10)

 Software de sistema ou de base

Linux Gnome

Python, Perl, PHP, GCC Bibliotecas

▪ GNU Scientific Library

▪ Partes do Matlab

(11)

 Inclusão de cabeçalhos  Declarações globais

 Definições de funções

#include <stdio.h>

char *mensagem = “hello, world!\n”; int main(void) {

puts(mensagem); return 0;

(12)

Compilador

date.h

struct date {

int day; int month; int year;

}

struct date create(void);

int week_of_year(struct date d);

data.h

#include “data.h”

struct data create(void) {

... }

int semana_do_ano(struct data d) { ... } ... data.c data.o

(13)

Compilador

date.h

struct date {

int day; int month; int year;

}

struct date create(void);

int week_of_year(struct date d);

data.h

#include “data.h”

int main(int argc, char **argv) { ... } principal.c data.o principal.o

(14)

date.o main.o

(15)

 Economize tempo de depuração tratando

todos os avisos do compilador

Muitas vezes a mensagem de erro não reflete o

que está ocorrendo; observar as redondezas da linha em que o erro/warning foi indicado

[debian:~/prof/aeds2/src]% gcc –Wall –c data.c data.c: In function ‘main’:

data.c:12: warning: ‘return’ with no value, in function returning non-void data.c:11: warning: ‘hoje’ is used

(16)

 Identificadores de funções e variáveis

Letras, números, e underscores Não podem começar com número

 Palavras

reservadas

auto double int struct

break else long switch

case enum register typedef

char extern return union

const float short unsigned

continue for signed void

default goto sizeof volatile

do if static while

(17)

 Tipos inteiros

char, short, int, long, long long

 Tipos de ponto flutuante

float, double, long double

void

 Arranjos  Estruturas  Ponteiros

(18)

type_sizes

type_limits

float_precision

(19)

Aritméticos x + y, x - y, x * y, x / y, x % y, -x  Incremento e decremento  x++, ++x, y--, --y Comparação x > y, x >= y, x < y, x <= y, x == y, x!= y Lógicos !x, x && y, x || y  Binários  x & y, x | y, x ^ y, ~x, x << y, x >> y Atribuição x = y, x += y, x |= y, x <<= y, etc.  Endereçamento de memória

(20)

 Conversão  (int)x, (double)x  Condicional  x ? y : z  sizeof  sizeof(x), sizeof(double)  Precedência  1 << 2 * 3 % 4 ^ 5 – 6 && 7

(21)
(22)

 Sequência de elementos de um único tipo

Tamanho fixo

Sem checagem de limites

Opcional: inicialização durante a declaração

▪ Impossível atribuir a um arranjo depois da declaração

int primos[7];

int primos[7] = {2, 3, 5, 7, 11, 13, 17};

2 3 5 7 11 13 17

(23)

 Alocação linear

Compilador converte indíces para a posição do

elemento referenciado

double identidade[4][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};

1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1

identidade

identidade[x][y] = valor do elemento na posição x*4 + y

(24)

 Um string termina com o caractere nulo

 ’\0’

 Um string num arranjo de 80 caracteres

char string[80] = “hello world!”

 Strings inicializados na declaração

char string[] = “hello world!”

 O tamanho do arranjo é definido automaticamente pelo compilador

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 79

(25)

Copiar um string: strcpy, strncpy

Funções com n no nome recebem o tamanho do

arranjo como parâmetro

▪ Evita erros caso o string não caiba no arranjo

Concatenar strings: strcat, strncat

 String de destino tem que ter espaço

Comparar strings: strcmp, strncmp

Comparar strings com == só compara a posição dos

arranjos, não o conteúdo

Tamanho de um string: strlen

(26)

 Converter string para inteiros

int atoi(const char *string) long atol(const char *string)

long long atoll(const char *string)

 Converter de string para ponto flutuante

(27)

array_boundaries1

array_boundaries2

string_cmp

(28)
(29)

 Combinam conjunto de dados relacionados

 Campos armazenados em sequência

Possível inclusão de espaçamento pelo compilador

struct cliente { char nome[48]; long long cpf; long telefone; struct endereco residencial; ... }; struct cliente cl; cl.cpf = 65423423123; sprintf(cl.residencial.rua, “Rua dos Goitacases”);

cl.residencial.numero = 1325; struct endereco end = {

(30)
(31)
(32)

 Referência para um objeto na memória  Vários usos:

Manipulação de dados

▪ Exemplo: ordenar um arranjo de estruturas grandes

Passagem de parâmetro por referência

 Declarações:

int i = 10;

int * ponteiro = &i; int ** ppp = &ponteiro;

Variável Posição Valor

i 0x80 10

ponteiro 0x84 0x80

(33)

Operadores & e *

 Endereço de uma variável é um ponteiro para

aquela variável

 Acessar o valor da variável apontada

int x = 10; int y;

int * ponteiro = &x;

y = *ponteiro + 1; // y = x + 1 = 11 *ponteiro = 20; // x = 20

(34)

 Nenhum ponteiro válido tem o valor NULL  NULL não pode ser acessado

Falha de segmentação

 Útil para denotar um ponteiro não inicializado

ou condições de erro

int * ponteiro;

/* ponteiro tem um valor * aleatório, não sabemos * para onde ele aponta. */ ponteiro = NULL;

fatores(ponteiro);

int * fatores(int * p) {

if(!p) { return NULL; } // calcula fatores primos ...

(35)

Não existe objeto com tipo void

Um ponteiro para void é como um coringa,

pode apontar para qualquer tipo de objeto

int i; int * int_ptr; void * void_ptr; double * double_ptr; int_ptr = &i; void_ptr = int_ptr; // OK double_ptr = int_ptr; // !OK double_ptr = void_ptr; // OK /* Qual o problema com

* double_ptr? */

Variável Posição Valor

i 0x80 10

int_ptr 0x84 0x80

void_ptr 0x88 0x80

(36)

 Declaração e inicialização normal

 Acesso aos campos

struct data { int dia; int mes; int ano; }; struct data d1;

struct data *ptr = &d1; int i = 0; (*ptr).dia = 8; (*ptr).mes = 3; (*ptr).ano = 2012; ptr->dia = 8; ptr->mes = 3; ptr->ano = 2012;

Variável Posição Valor

d1.dia 0x80 8

d1.mes 0x84 3

d1.ano 0x88 2012

ptr 0x8c 0x80

(37)
(38)

 Declaração

 Tipo de retorno, nome, parâmetros

double pow(double x, double y)

Em cabeçalhos .h para funções externas

Em arquivos .c para funções auxiliares (internas)

 Definição

 Em arquivos .c

 Corpo da função

double pow(double x, double y) {

... }

(39)

 C passa parâmetros por valor

Modificações do valor de um parâmetro não afeta

a variável original (fora da função)

int incrementa(int x) { x = x+1;

return x; }

int main(int argc, char **argv) {

x = 1;

y = incrementa(x);

printf(“%d %d\n”, x, y); ...

(40)

 Variedade de utilidades  Declaração

int (*comparador)(void *e1, void *e2);

 Inicialização

 Nome da função é convertido em ponteiro

 Passando como parâmetro

int compara_dados(void *e1, void *e2) { ... }; int main(void) {

int (*compara)(void *e1, void *e2) = compara_dados;

void ordena(void *dados, int nelem,

(41)

func_params

struct_func_params

(42)
(43)

 Preferível sempre que não soubermos quanta

memória um programa utilizará

 Alocação estática é fixa

 Alocação dinâmica permite utilizar a quantidade necessária de memória sem desperdícios

 Alocar um novo bloco de memória

malloc(), calloc()

 Redimensionar um bloco já alocado

realloc()

 Liberar um bloco alocado

(44)

 malloc aloca um bloco de size bytes

 O conteúdo do bloco alocado é indeterminado  Retorna um ponteiro para void

 O programador decide como usar o bloco alocado

 Retorna NULL em caso de erro

int *primos = malloc(7 * sizeof(int)); if(!primos) { perror(NULL); exit(1); } char *buffer = malloc(64);

if(!buffer) { perror(NULL); exit(1); } double *fracoes = malloc(64);

if(!fracoes) { perror(NULL); exit(1); } memset(fracoes, 0, 8*sizeof(double)); int *primos = malloc(7 * sizeof(int)); if(!primos) { perror(NULL); exit(1); } char *buffer = malloc(64);

if(!buffer) { perror(NULL); exit(1); }

double *fracoes = malloc(8*sizeof(double)); if(!fracoes) { perror(NULL); exit(1); }

(45)

 calloc aloca um bloco com espaço para

count objetos de tamanho size bytes

 O bloco alocado é inicializado com zero

int *primos = calloc(7, sizeof(int)); if(!primos) { perror(NULL); exit(1); } char *buffer = calloc(64, sizeof(char)); if(!buffer) { perror(NULL); exit(1); }

double *fracoes = calloc(8, sizeof(double)); if(!fracoes) { perror(NULL); exit(1); }

(46)

 Redimensiona o bloco de memória apontado

por ptr para size bytes

Mantém o conteúdo do bloco apontado por ptr

(limitado pelo tamanho do novo bloco)

O local do novo bloco de memória pode mudar ▪ Mesmo se o novo bloco for menor que o anterior!

int *primos = calloc(7, sizeof(int)); if(!primos) { perror(NULL); exit(1); }

realloc(primos, 5*sizeof(int)); // BUG primos = realloc(primos, 5*sizeof(int)); // OK if(!primos) { perror(NULL); exit(1); }

(47)

 Libera o bloco de memória apontado por ptr

Chamar free mais de uma vez pra um mesmo

bloco de memória é um bug

free só pode ser chamada em ponteiros

retornados por malloc, calloc e realloc.

char * montar_string(struct endereco e) { char *string = malloc(128);

if(!string) { perror(NULL); exit(1); } // montar string ...

return string; }

(48)
(49)

char * exemplo(char parametro[]) {

int i; char estatico[80];

char *dinamico = malloc(80);

Variável Posição Valor

parametro 0x7c 0x480 i 0x80 ? estatico[0] 0x84 ? ... ... ? estatico[79] 0xd3 ? dinamico 0xd4 0x884

Variável Posição Valor

dinamico[0] 0x884 ? dinamico[1] 0x885 ?

... ... ?

(50)

dyn_alloc

(51)
(52)

 Rotinas de entrada e saída não fazem parte

da linguagem

 Disponíveis em bibliotecas que acompanham

os compiladores

 Padronizadas

(53)

 formato específica como os valores devem

ser impressos na saída.  printf(“X = %d”, x);

printf(“Area: %f\n”, PI*r*r); printf(“Nome: %s”, aluno.nome);

 Existem vários caracteres de controle

 Retorna o número de conversões impressas

(54)

conversão c char d int u unsigned int x int, hexadecimal f float e float, científico g float, e ou f p ponteiro s string % sinal percentual %[opções][largura mínima][.precisão][tamanho]conversão

printf(“valor do float na posição %p = %f\n”, ptr, *ptr);

tamanho hh char h short l long ll long long L long double z size_t t ptrdiff_t j intmax_t

printf(“valor do double na posição %p = %lf\n”, ptr, *ptr);

tamanho

0 zeros à esquerda # alternativa

- alinhar à esquerda + mostrar sinal positivo

espaço para sinal positivo

agrupar milhares I digitos alternativos

(55)

 Caracteres especiais e reposicionamento do

cursor

printf(“barra invertida \\\n”); printf(“aspas duplas \”\n”);

(56)

 scanf é o inverso do printf:

lê dados do terminal

Mesmos códigos de conversão  Mesmas sequências de escape

 Passar um ponteiro para a variável que você

quer inicializar

int nlados = 0; float lado = 0;

scanf(“%f %d\n”, &lado, &nlados); perimetro = lado * nlados;

(57)

 Observe que scanf interrompe a leitura de

um string (%s) quando encontra um branco

 Especificadores de tamanho e filtro

%[aeiou]s lê apenas vogais

Para na primeira consoante, número, espaço, pontuação, etc

%[0123456789]s lê apenas números %60s lê apenas 60 caracteres

%60[^0123456789]s lê até 60 caracteres parando

quando encontrar um número

char buffer[80];

(58)

 getchar lê um único caractere do terminal  putchar(int c) imprime o caractere

(59)

 Mesma coisa, só precisamos passar o

manipulador do arquivo como parâmetro  FILE *entrada; FILE *saida; ... fscanf(entrada, “%79s”, buffer); char c = fgetc(entrada); fputc(c, saida); fprintf(saida, “X = %d”, x);

(60)

FILE * fopen(char *nome, char *modo)

Abre o arquivo com o dado nome

modo pode ser:

▪ “r” para leitura, “w” para escrita, “rw” para leitura e escrita ▪ Se o arquivo já existir, podemos usar “a” para adicionar ao arquivo

 Sempre teste se o retorno é nulo, pois podem ocorrer erros

▪ Arquivo ou caminho não existente, permissões insuficientes, etc.

int fclose(FILE *arquivo)

Fecha o arquivo apontado por arquivo

FILE *arquivo = fopen(“C:\Users\Cunha\Desktop\teste.txt”, “w”); if(!arquivo) { perror(NULL); exit(EXIT_FAILURE); }

fprintf(arquivo, “hello arquivo!\n”); fclose(arquivo);

(61)

printf e fprintf são idênticas, só operam sobre

manipuladores de arquivos diferentes

printf sempre imprime na saída padrão (terminal)

fprintf recebe o arquivo onde imprimir como parâmetro O manipulador do arquivo correspondente à saída padrão é o

stdout, e o manipulador da entrada padrão é o stdin

(62)

 Cuidado ao terminar de ler um arquivo

Use int feof(FILE *arquivo)para testar

se já leu o arquivo até o fim

feof retorna falso se o arquivo ainda não tiver

terminado

feof só retorna verdadeiro depois que você tentar ler

(63)

 Saber a posição atual do arquivo

long ftell(FILE *arquivo)

 Mudar para uma dada posição no arquivo

int fseek(FILE *arquivo,

int base, int distancia)

Onde base pode ser:

SEEK_SET, o começo do arquivo

SEEK_CUR, a posição atual no arquivo

(64)

write_test_file

(65)

int main(int argc, char *argv[]) { ... }

argv é um arranjo de strings, um parâmetro em

cada índice do arranjo

argc é o número de parâmetros em argv argv[0] é sempre o nome do executável

▪ Logo, argc >= 1

 Para processamento avançado de

parâmetros, use getopt()

 Parâmetros em qualquer ordem, opcionais, etc.

(66)

cmdline_params (CodeBlocks)

(67)
(68)

 Pequeno esforço, grande impacto  Código mais legível

 Ajuda o entendimento das idéias

Útil quando você for ler o código 6 meses depois  Útil para outras pessoas

 Código com menos erros

Economizar tempo e ter menos dor de cabeça

 Em AEDS2: ajuda entendimento das idéias e

(69)

 Realça estrutura lógica do código

 Em geral, indenta-se com tabulação (tab)

 Em geral um tab corresponde a 8 espaços, mas é configurável na maioria dos editores

(70)

static char * concat (char *s1, char *s2) { while (x == y) { something (); somethingelse (); } finalthing (); }

static char * concat (char *s1, char *s2) { while (x == y) { something (); somethingelse (); } finalthing (); }

static char * concat(char *s1, char *s2) { while(x == y) { something(); something_else(); } final_thing(); } static char * concat(char *s1, char *s2) { while(x == y) { something(); something_else(); } final_thing(); } static char * concat(char *s1, char *s2) { while(x == y) { something(); something_else(); } final_thing(); } GNU K&R Allman

(71)

 Facilitam a compreensão do código, mais

importantes para código complexo

 Código bem escrito não depende muito de

comentários

Comentário errado é pior do que nenhum

comentário

(72)

 No início de um módulo

 Descrever variáveis globais ou importantes  Em funções para explicar os parâmetros, o

tipo de retorno, e o que a função faz

Não explicar como a função faz a tarefa, código

deve ser claro o bastante

 Indicar invariantes de loops

 Não comentar o óbvio DUH:i += 1; // incrementa i. OK:

(73)

 Escolher bons identificadores ajudam a

compreensão do código

void ordena(int *vetor); void processa(int *vetor); double media(int *vetor);

Mesma coisa para variáveis

▪ Variáveis auxiliares podem receber nomes simples, mas sem exagerar

Indices: i, j, k

Variáveis tipo ponto flutuante: x, y, z

(74)

 Se houver, preferir o estilo que já estiver em uso  Underscore:

int num_clientes;

struct list *lista_alunos;

 CamelCase:

int numClientes;

(75)

 Não usar “números mágicos” no código  Valores podem precisar ser modificados  “Números mágicos” não têm significado

Usar #define para dar nome a constantes

Nomes em maiúsculas

#define PI 3.14159

#define TAMANHO_MAX_LINHA 256 char * le_linha(FILE *entrada) {

char *linha = malloc(TAMANHO_MAX_LINHA); ...

return linha; }

(76)

 Particionamento de um programa

 Um módulo geralmente é um par de arquivos

modulo.c contém a implementação das funções modulo.h contém a declaração das funções e

tipos de dados; é importado por outros módulos

 Outros programadores só precisam saber o

que o módulo faz, não como ele funciona

 Procurar identificar módulos independentes

Referências

Documentos relacionados

Além das considerações sobre “a querela dos tempos” de Manoury (1998:78), foram basilares as idéias de McNutt (2003), que rejeita o uso dos sons em tempo diferido por se

As quinolonas já têm sido relatadas como seguras para o tratamento da otite, sem apresentar sinais de ototoxicidade nos animais (GOTTHELF, 2004), e não foram observados

Observações como esta ganham peso ainda maior quando se leva em conta que, num país desprovido de cultura científica, os veículos de comunicação representam o canal mais

É relevante, ainda, considerar que os termos “reciclagem/ formação” indiciam a vontade que o especialista, reconhecido como portador de um saber, tem de

Segundo a gravidade da doença, a causalidade da infecção hospitalar com o óbito foi mais importante no grupo com afecção não fatal, enquanto que como contribuinte ao óbito, foi

Se o agente não estiver perseguindo um goal e uma situação neutra for reconhecida como a vigente, então o ReasoningMode procura, no Story Frame daquele Scene Frame detectado, por

Orçamentos expeditos, “números mágicos dos orçamentistas”, adaptação aos sistemas Sicro e Sinapi, orçamentação da administração local e da manutenção do canteiro,

Muitos estudos encontram resultados adversos referentes aos efeitos climáticos e de poluentes na morbidade respiratória, dependendo da região do estudo, das fontes de emissões,