Linguagem C – funções e registros IF61A/IF71A - Computação 1 Prof. Leonelo Almeida Universidade Tecnológica Federal do Paraná Até agora ... • Introdução à linguagem C ▫ ... ▫ Operadores aritméticos, de comparação e lógicos ▫ Entrada e saída de dados ▫ Estruturas condicionais ▫ Estruturas de repetição ▫ Vetores ▫ Matrizes ▫ Funções Aula de hoje Crie uma função que recebe 100 variáveis inteiras e retorna a média de seus valores Tentativa: float media (int val1, int val2, int val3, ..., int valn) Funciona, mas cansativo, não? Ainda hoje • Declare uma (coleção de) variável(is) que contenha: ▫ Nome da pessoa ▫ Altura ▫ 3 cores preferidas Ainda hoje • Declare uma (coleção de) variável(is) que contenha: ▫ Nome da pessoa Posso usar um ▫ Altura vetor ou uma matriz? ▫ 3 cores preferidas Aula de hoje Vetores e matrizes como parâmetros em funções Recursão Coleções de variáveis heterogêneas Passagem de vetores como parâmetros de funções • Vetores unidimensionais ▫ Não precisa passar o tamanho do vetor na declaração da função • É diferente da passagem de parâmetros de variáveis normais ▫ Quando uma variável simples é passada como parâmetro, seu valor é atribuído para uma nova variável local da função ▫ No caso de vetores, não é criado um novo vetor! ▫ Os valores de um vetor são alterados dentro de uma função! Declaração tipo identificador (tipo vetor[], tipo vetor2[TAM], ...) { ... } • Exemplo: float funcVets (int veti[], float vetf[10]) { ... } Exemplo - Passagem de vetores como parâmetros de funções #include <stdio.h> void fun1(int vet[], int tam){ int i; for(i=0;i<tam;i++) vet[i]=i+1; } int main(void){ int x[10]; int i; for(i=0;i<10;i++) x[i]=8; fun1(x,10); for(i=0;i<10;i++) printf(“%d\n”, x[i]); return; } O que será impresso? Passagem de vetores multidimensionais como parâmetros • Também são alterados pelas funções • Somente a primeira dimensão pode ser omitida • Declaração tipo identificador (tipo vetor[][TAM1][TAM2], ...) • Exemplos int getDateCountry (int vet[][31][12][3000]) int getPotMat (int pot, float mat[2][3]) Retorno de funções • Vetores não podem ser retornados por funções • No entanto, como eles são alterados pelas funções o efeito é parecido • Contraexemplo: Esse código está incorreto! #include <stdio.h> int[] leVet(void) { int i, vet[100]; for (i = 0; i < 100; i++) { printf("Digite um numero:"); scanf("%d", &vet[i]); } } ... Exemplo #include <stdio.h> void leVet(int vet[], int tam){ int i; printf("Digite %d numeros: ",tam); for(i=0; i<tam; i++) scanf("%d",&vet[i]); } void escreveVet(int vet[], int tam){ int i; for(i=0; i<tam; i++) printf("vet[%d] = %d\n",i,vet[i]); } Exemplo - continuação int main(){ int vet1[10], vet2[20]; leVet(vet1,10); leVet(vet2,20); escreveVet(vet1,10); escreveVet(vet2,20); return 0; } Exemplo – Operações em matrizes • Faça um programa com operações básicas em matrizes quadradas. Onde o tamanho n é informado pelo usuário: ▫ Soma de 2 matrizes com dimensões nxn. ▫ Subtração de 2 matrizes com dimensões nxn. ▫ Calculo da transposta de uma matriz de dimensão nxn. ▫ Multiplicação de 2 matrizes com dimensões nxn. Exemplo – Operações em matrizes • Leitura da matriz void leMatriz(double mat[][MAX], int n){ int i, j; printf(“Lendo dados da matriz (linha por linha)\n”); for(i=0; i<n; i++){ for(j=0; j<n; j++){ scanf(“%lf”, &mat[i][j]); } } } Exemplo – Operações em matrizes • Escrita da matriz void imprimeMatriz(double mat[][MAX], int n){ int i, j; printf(“dados da matriz (linha por linha)\n”); for(i=0; i<n; i++){ for(j=0; j<n; j++){ printf(“%.2lf”, mat[i][j]); } printf("\n"); } } Exemplo – Operações em matrizes • Multiplicação de matrizes void multiplica(double mat1[][MAX], double mat2[][MAX], double resp[][MAX], int n){ int i, j, k; for(i=0; i<n; i++){ for(j=0; j<n; j++){ resp[i][j] = 0; for(k=0; k<n; k++){ resp[i][j] = resp[i][j] + (mat1[i][k]*mat2[k][j]); } } } } Exemplo – Operações em matrizes #include <stdio.h> #define MAX 100 void leMatriz(double mat[][MAX], int n); void imprimeMatriz(double mat[][MAX], int n); void multiplica(double mat1[][MAX], double mat2[][MAX], double resp[][MAX], int n); int main() { double mat1[MAX][MAX], mat2[MAX][MAX], mat3[MAX][MAX]; int n; printf("Qual dimensão das matrizes: "); scanf("%d", &n); leMatriz(mat1, n); leMatriz(mat2, n); multiplica(mat1, mat2, mat3, n); imprimeMatriz(mat1, n); imprimeMatriz(mat2, n); imprimeMatriz(mat3, n); return 0; } Atividades • Agora complete o programa de operações básicas sobre matrizes quadradas: ▫ Pergunte ao usuário que operação deseja realizar ▫ Crie funções para as demais operações Atividades • Crie uma função double media(int vet[], int tam) que recebe como parâmetros um vetor e seu tamanho e devolve a media dos valores armazenados no vetor. • Crie uma função char ordenado(int vet[], int tam) que recebe como parâmetros um vetor e seu tamanho e devolve ‘C’ se os valores no vetor estão em ordem crescente, ‘D’ se decrescente, e ‘N’ caso esteja desordenado. Atividades • Crie uma função int verifica(int vet[], int tam, int C) que recebe como parâmetros um vetor, seu tamanho e um inteiro C. A função deve retornar 1 caso existam dois elementos distintos do vetor tal que a multiplicação destes e C. Recursão Baseado no material: http://www.lis.ic.unicamp.br/~mc102/files/mc102jk-a18-4pp.pdf Recursão Para compreender o que é recursão ... ... é preciso compreender o que recursão. Recursão • Uma função é recursiva se ela chama a si mesma direta ou indiretamente • Organização de uma recursão ▫ Processo de indução Definir a solução para os caso(s) básico(s) Definir as chamadas recursivas. O objeto define em termos de si mesmo. Exemplo de recursão • Soma dos n primeiros inteiros ▫ Se n = 1 então a soma é 1 ▫ Se n > 1 então a soma é: O próprio n + soma dos n-1 primeiros Em outras palavras ... • Já terminou o cálculo? ▫ Se sim então retorne o resultado ▫ Senão simplifique o problema. Para tanto, calcule o problema em termos mais simples e encaixe os resultados para compor a solução final. • Sem a condição de parada, a recursão executaria infinitamente Voltando ao exemplo • Caso base: somar 1 elemento (resultado = 1) • Chamada recursiva: ▫ Soma de n elementos: n + soma de n-1 elementos ▫ Soma de n-1 elementos: n -1 + soma de n-2 elementos ▫ Soma de n-2 elementos: n-2 + soma de n-3 elementos ▫ ... ▫ Soma de 2 elementos: 2 + soma de 1 elemento ▫ Soma de 1 elemento: 1 (caso base) Exemplo • Como seria o código dessa função? int soma(int n) { if (n <= 1) return n; else return (n + soma(n-1)); } Exemplo • Como seria o código dessa função? int soma(int n) { if (n <= 1) return n; Caso base else return (n + soma(n-1)); } Chamada recursiva Como fica a execução? • Chamada para soma (4) Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) ▫ n = 3 ▫ resultado = 3 + soma(2) Chamada para soma(2) Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) ▫ n = 3 ▫ resultado = 3 + soma(2) Chamada para soma(2) n = 2 resultado = 2 + soma(1) Chamada para soma (1) Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) ▫ n = 3 ▫ resultado = 3 + soma(2) Chamada para soma(2) n = 2 resultado = 2 + soma(1) Chamada para soma (1) n = 1 resultado = 1 Chegamos ao caso base! Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) ▫ n = 3 ▫ resultado = 3 + soma(2) Chamada para soma(2) n = 2 resultado = 2 + soma(1) //2 + 1 Chamada para soma (1) n = 1 resultado = 1 Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) ▫ n = 3 ▫ resultado = 3 + soma(2) Chamada para soma(2) n = 2 resultado = 2 + soma(1) Chamada para soma (1) n = 1 resultado = 1 //3 + 3 Como fica a execução? • Chamada para soma (4) • n = 4 • resultado = 4 + soma (3) ▫ Chamada para soma(3) ▫ n = 3 ▫ resultado = 3 + soma(2) Chamada para soma(2) n = 2 resultado = 2 + soma(1) Chamada para soma (1) n = 1 resultado = 1 // 4 + 6 = 10 Exemplo 2 • Somar todos os inteiros no intervalo entre [m, n] • Caso base: se n=m retorna m (ou n) • Chamada recursiva: ▫ Se n > m, n + soma do intervalo [n-1, m] ▫ Senão n + soma do intervalo [n+1, m] Exemplo 2 • Somar todos os inteiros no intervalo entre [m, n] int soma_mn (int m, int n) { if (n == m) Caso base return m; else if (m < n) return n + soma_mn(m, n-1); else return n + soma_mn(m, n+1); } Chamada recursiva Exemplo 3 #include <stdio.h> void imprime(int v[], int i, int n); int main(){ int vet[] = {1,2,3,4,5,6,7,8,9,10}; imprime(vet, 0, 9); printf("\n"); } void imprime(int v[], int i, int n){ if(i==n) printf("%d, ", v[i]); else { O que será imprime(v,i+1,n); impresso após printf("%d, ", v[i]); executar esse } } programa? Atividades • Crie uma função recursiva que calcula o fatorial de um número n (n!) • Crie uma função recursiva que calcula a potência p de um número n. • A série de fibonacci é a seguinte: ▫ 1, 1, 2, 3, 5, 8, 13, 21, ... • Qual é o n-ésimo (fibo(n)) número da série? Coleções de variáveis heterogêneas (Registros) Coleções de variáveis heterogêneas • Mais conhecidos como registros • Agrupam várias variáveis que podem ser de tipos de dados diferentes, mas que compartilham um contexto comum • Exemplo: ▫ Registro de alunos: nome, RA, notas das provas, etc ▫ Registro de veículos: modelo, ano, tamanho do tanque, cor, etc Declaração de registros struct identificador_do_tipo { tipo_1 nome_1; tipo_2 nome_2; tipo_3 nome_3; ... tipo_n nome_n; }; • Exemplo: struct Aluno { char nome[45]; int idade; char genero; } Declaração de registros #include <stdio.h> struct Aluno { char nome[45]; int idade; char genero; } São variáveis do tipo “struct aluno”. int main() { struct Aluno a, b; } Usando uma struct • Para acessar uma das variáveis de um registro basta: ▫ nome_registro.nome_variavel • A partir daí o funcionamento de operações sobre as variáveis são os mesmos que de variáveis comuns do mesmo tipo. Exemplo #include <stdio.h> #include <string.h> struct Aluno{ char nome[45]; int idade; char genero; }; int main(){ struct Aluno a, b; strcpy(a.nome, “Helen”); a.idade = 18; a.genero = ‘F’; strcpy(b.nome, “Dilbert”); b.idade = 34; b.genero = ‘M’; printf(“A nome = %s, idade = %d, gênero = %c\n”, a.nome, a.idade, a.genero); printf(“B nome = %s, idade = %d, gênero = %c\n”, b.nome, b.idade, b.genero); } Atribuição de registros • É possível atribuir um registro ao outro. • Exemplo: int main(){ struct Aluno a, b; printf(“Digite o nome:”); scanf(“%[^\n]”, a.nome); printf(“Digite a idade:”); scanf(“%d”, &a.idade); printf(“Digite o gênero:”); scanf(“%c”, &a.genero); Cada campo de “a” é copiado para “b”. Mas os dois continuam independentes. b = a; printf(“B nome = %s, idade = %d, genero = %c\n”, b.nome, b.idade, b.genero); } Vetores de registros • Para quando precisamos de várias instâncias do mesmo tipo de registro • Exemplo: ▫ Declaração: struct Aluno turma[49]; ▫ Para usar: turma[posicao].campo; Atividade • Encontre no projeto do curso e defina todos os registros que deverão ser criados. Extras Redefinição de tipos de dados Tipos enumerados Redefinição de tipo de dados • Permite criar um tipo de dados, que se comporta da mesma maneira que um dos tipos de dados já existentes • Na linguagem isso é feito com o comando typedef • Declaração: typedef tipo_existente novo_tipo; • Exemplo: typedef float nota; nota n1, n2, n3, n4; Typedef para registros • Uso comum • Simplifica a declaração • Exemplo: struct RegAluno { int ra; double nota; }; typedef struct RegAluno Aluno; Aluno turma[10]; turma[0].ra = 23456; turma[0].nota = 5.4; ... Tipos enumerados • Quando queremos armazenar, por exemplo, o mês em uma variável, geralmente utilizamos um valor inteiro • Exemplo: ▫ mes = 1; • E se pudéssemos usar algo como: ▫ mes = janeiro; • Não tornaria o código mais claro? Tipos enumerados include <stdio.h> //aqui criamos um novo tipo enumerado //que pode ser usado por qualquer função enum meses {jan = 1, fev, mar, abr, mai, jun, jul, ago, set, out, nov, dec}; int main(){ //aqui criamos 2 variáveis do tipo "enum meses" enum meses a, b; a = jan; b = jun; if(a != b){ printf(“%d é um mês diferente de %d”, a, b); } }