Programação — MEEC - MEAer 2o Exame — 27 de Janeiro de 2010 Duração: 3h00 1. (4.0 valores) 1.a) (1.5 valores) O código seguinte lê um número entre 0 e 200 e imprime o valor da função fibonacci para esse valor, mas tem erros. Identifique cada um dos erros sintácticos e lógicos indicando a linha onde ocorre. Sugira uma correcção possível para cada erro. Justifique todas as correcções. 0 ,n = 0 1 ,n =1 Função de fibonacci: F (n) = F (n − 1) + F (n − 2) ,n >1 1 #define MIN 0 2 #define MAX 200 3 #include <stdlib.h> 4 #include <stdio.h> 5 6 int fibonacci(int n){ 7 int resultado; 8 if (n == 0) 9 resultado = 0; 10 else 11 if (n == 1) 12 resultado = 1; 13 else 14 resultado = fibonacci(n-2) + fibonacci(n-1); 15 return resultado; 16 } 17 18 int main(){ 19 int n = -1; 20 while( (MIN > n) || (n > MAX)){ 21 printf("introduza um numero: "); 22 scanf("%d", &n); 23 } 24 printf("resultado: %d", fibonacci(n)); 25 exit (1); 26 } 1.b) (1.5 valores) O programa seguinte está no ficheiro fonte letras.c. Após se compilar este código é gerado o executável correspondente chamado LETRAS.EXE. Indique o que é escrito no écran durante a execução do programa LETRAS.EXE, supondo que o utilizador o invocou com o seguinte comando e argumentos, escrevendo o seguinte na linha de comandos: LETRAS.EXE G E Se não sabe o que faz a linha 4 assuma que a variável letra fica com o valor 'F'. Indique este facto explicitamente na sua resolução. Represente cada espaço impresso no écran pelo símbolo: 1 2 int main(int argc, char *argv[]){ char letra, ch, cha; 3 4 letra=argv[2][0]; 5 6 for (ch='A'; ch<=letra; ch++){ — 1/8 — 2. Exame de Programação MEEC-MEAer 2009/2010 7 8 9 for(cha=ch; cha<letra; cha++) printf(" "); 10 11 12 for(cha='A'; cha<ch; cha++) printf("%c",cha); 13 14 15 for(cha=ch; cha>='A'; cha--) printf("%c",cha); 16 17 18 printf("\n"); } 19 } A ABA ABCBA ABCDCBA ABCDEDCBA 1.c) (1.0 valores) O que acontece se o utilizador só escrever o seguinte na linha de comando (sem mais nenhum argumento): LETRAS.EXE ? Descreva sucintamente o problema, não usando mais do que 4 linhas. Corrija a aplicação para solucionar o problema. Se o utilizadro só der o comando LETRAS.EXE o vector argv só terá uma posição (a zero). Quando, durante a execução do programa, na linha 4 se tenta aceder à posição 2, ocorrerá um erro, visto essa posição não ter sido inicializada e apontar para uma string inválida. O programa poderá rebentar. Para resolver o problema dever-se iar usar a variável argc, que nos indica quantos elementos estão no vector argv: if (arc>=3){ /* inserir aqui o código entre a linha 4 e 19 */ } 2. (4.0 valores) 2.a) (1.5 valores) Escreva uma função que receba como argumento um número real e devolve verdadeiro se esse número corresponder a um inteiro (por exemplo 3.0) e falso caso contrário ( por exemplo 3.14). int eInteiro(float num){ int aux = num; if(num == aux) return 1; else return 0; } 2.b) (2.5 valores) Desenvolva um programa que lê um ficheiro chamado numeros.txt com várias linhas de texto e em que cada linha só existe um número. Se o ficheiro não seguir este formato (só um número por linha) deverá ser impressa uma mensagem de erro e tomada uma acção adequada. — 2/8 — 2. Exame de Programação MEEC-MEAer 2009/2010 Todas as linhas lidas do ficheiro deverão ser impressas no ecrã. Sempre que forem escritas 20 linhas a aplicação deve parar e esperar que o utilizador carregue na tecla Enter. Todos os valores lidos que sejam reais (com parte decimal) são escritos num ficheiro chamado reais.txt. No fim da execução deverá ser impresso no écran quantos números foram lidos. Deverá ser usada a função implementada na alínea anterior. int main(int argc, char *argv[]){ FILE *fp; FILE *reals; char linha[64], dummy[64]; float num; int cont_l = 0; int cont_n = 0; reals = fopen("reais.txt", "w"); if(reals == NULL){ printf("Nao e' possivel escrever os reais num ficheiro. Problemas ao criar ficheiro.\n"); exit(1); } fp = fopen("numeros.txt", "r"); if(fp == NULL){ printf("Nao e' possivel abrid ficheiro de dados\n"); exit(1); } while(fgets(linha, 64, fp) != NULL){ cont_l++; if(cont_l%20 == 0){ printf("Pressione uma tecla para continuar"); getchar(); } if(sscanf(linha, "%f%s", &num, dummy)==1){ printf("%f\n", num); cont_n++; if(eInteiro(num)==0 && reals != NULL) fprintf(reals, "%f\n", num); }else{ printf("Linha com formato inadequado\n"); } } fclose(fp); fclose(reals); printf("foram lidos %d numeros\n", cont_n); } — 3/8 — 2. Exame de Programação MEEC-MEAer 2009/2010 3. (2.0 valores) 3.a) (1.0 valor) Observe a função seguinte e indique o que deve aparecer nos espaços indicados por A, B, C, D e E. A função não lê nenhuma informação do teclado. A seguinte função executa uma cifra muito simples e só trabalha com caracteres minúsculos. O mecanismo de cifra consiste em somar ao caracter um determinado inteiro (n), obtendo-se dessa soma um outro caracter também minúsculo. Se o resultado dessa soma inicial passas o limite do abecedário, a soma recomeça no início do abecedário. Exemplos para n igual a 3: Entrada: 'a' saída 'd' Entrada: 'c' saída 'f' Entrada: 'x' saída 'a' Entrada: 'z' saída 'c' Entrada: 'A' saída 'A' void cifra( char * c, int n){ if( *c >= 'a' && *c <= 'z' ) { /* testa se minuscula */ *c = *c + n ; /* adiciona n */ if( *c >'z') *c = *c –'z' -1 + 'a' ; /*se exceder 'z',recomeça em 'a' */ } } 3.b) (1.0 valor) Observe a função seguinte e indique o que deve aparecer nos espaços indicados por A, B, C, D e E. A função le_invertido lê uma string (com no máximo 100 caracteres) a partir do teclado e devolve-a invertida (o primeiro caracter lido fica na última posição, o segundo na penúltima e assim sucessivamente). void le_invertido( char str[100] ){ int i; char linha[100]; fgets(linha, 100, stdin); for( i=0; i<strlen(linha); i++ ) str[i] = linha[ strlen(linha) -1 –i ]; str[i] = '\0'; } 4. (2.5 valores) Responda às seguintes questões: a) (0.2 valores) Declare uma variável api do tipo apontador para inteiro e outra apc do tipo apontador para caracter. int* api; char * apc; b) (0.2 valores) Declare v do tipo vector de 10 inteiros e s do tipo vector de 10 caracteres, inicializando-os com os 10 dígitos (entre 0 e 9) e as 10 primeiras maiúsculas respectivamente. int v[10]; char s[10]; int i; — 4/8 — 2. Exame de Programação MEEC-MEAer 2009/2010 for(i = 0; i < 10 ; i++){ v[i] = i; S[i] = 'A'+i; } c) (0.2 valores) Ponha api a apontar para o primeiro elemento de v e apc a apontar para o primeiro elemento de s. api = v; ou api = &v[0]; acp = s; ou apc = &s[0]; d) (0.2 valores) Incremente 2 vezes api e 2 vezes apc. Api -= 2; Apc -= 2; e) (0.2 valores) Qual o valor armazenado na posição de memória apontada por api? Qual o valor armazenado na posição de memória apontada por apc? * api 2 * apc 'A' f) (0.2 valores) Qual o valor de *(apc + *api) ? *(apc + *api) *(apc + 2) 'E' g) (0.2 valores) Qual o valor de *s *s 'A' ? h) (0.2 valores) Qual o valor de apc[5] ? apc[5] 'F' i) (0.2 valores) Coloque no último elemento de v o valor apc[6]-'A' . Que valor aí ficou armazenado? V[9] = apc[6] – 'A' 6 h) (0.7 valores) O que acontece se o programador fizer *api = 12 antes de executar a instrução da alínea c) (imediatamente após a declaração)? Como a variável api ainda não foi inicializada, não sabemos para onde aponta. O programa pode rebentar (se api apontar para uma posição de memória inválida) ou armazenar o valor 12 numa posição de memória aleatória. 5. (7.5 valores) Pretende-se com este programa simular parte do funcionamento de uma base de dados de biblioteca. Esta base de dados é composta por uma lista de fichas. Cada ficha de um livro inclui, entre outras informações, o código de registo do livro (inteiro), o nome do autor (string), o título (string), o identificador do requisitante (inteiro) e a data limite de devolução. Os livros são armazenados na lista por ordem crescente de código de registo. A base da lista aponta para um estrutura vazia que não representa nenhum livro e com código de registo 0. 5.a) (0.5 valores) Declare o tipo data com campos dia, mês e ano (inteiros). — 5/8 — 2. Exame de Programação MEEC-MEAer 2009/2010 typedef struct s_data{ int dia, mes ano; } data; 5.b) (1.0 valores) Escreva uma função compara_datas que compara duas datas (data1 e data2) e devolve o seguinte: • 1 se data1 for anterior a data2 • 0 se forem iguais • -1 se data1 for posterior a data2. int compara_datas(data data1, data data2){ if( data1.ano < data2.ano) return 1; if (data1.ano > data2.ano) return -1; if( data1.mes < data2.mes) return 1; if (data1.mes > data2.mes) return -1; if( data1.dia < data2.dia) return 1; if (data1.dia > data2.dia) return -1; return 0; } 5.c) (0.5 valores) Declare o tipo chamado livro que implementa a lista de livros, fazendo uso do tipo data anteriormente declarado. typedef struct s_livro{ int codigo; char nome_autor[100]; char titulo[100]; data data_devol; int id_requisitante; /* int requisitado; atributo opcional */ struct s_livro * seguinte; } livro; 5.d) (1.0 valores) Escreva uma função que marca um livro como não requisitado (aquando da sua devolução). Esta função recebe um único parâmetro representando o livro que está a ser devolvido, não retornando nenhum valor. void requisita_livro(livro * l){ l -> id_requisitante = -1; /* ou L -> requisitado = 0; } — 6/8 — */ 2. Exame de Programação MEEC-MEAer 2009/2010 5.e) (1.5 valores) Escreva uma função que insira um novo livro na base de dados. O código deste novo livro deverá ser numericamente seguinte ao do último livro inserido: código do último livro mais um. A função tem como argumentos um apontador para a base da lista e a informação do novo livro: título e autor. A função deve marcar o livro como não requisitado. void insere_livro(livro * bd, char * titulo, char * autor){ livro * aux, *novo; novo = malloc (sizeof(livro)); if (novo == NULL){ printf(“Erro: falta de memoria\n”); exit(-1); } strcpy( novo->titulo, titulo); strcpy( novo->autor , autor); novo->id_requisitante = -1; /* ou novo->requisitado = 0; */ aux = bd; while (aux->seg != NULL){ aux = aux -> seg; } novo->codigo = aux->codigo +1; aux -> seg = novo; novo-> seg = NULL; } 5.f) (1.5 valores) Escreva uma função que permite requisitar um livro. Esta função recebe como argumento a base da lista, o código do livro, o identificador do requisitante e a data limite de devolução. Se o livro já estiver requisitado deverá escrever uma mensagem de erro e retornar 0, caso contrário armazena o identificador do requisitante e a data de devolução. void requisita_livro(livro * bd, int codigo, int id_utilizador, data data_devolucao){ livro * aux; aux = bd->seg while (aux != NULL && aux-> codigo != codigo){ aux = aux -> seg; } if(aux == NULL){ Printf(“livro inexistente”); return; } if(aux -> id_requisitante == -1){ /* ou if(aux -> requisitado == 0){ */ printf(“Livro ja requisitado”); return; } aux -> id_requisitante = id_utilizador; aux -> data_devol = data_devolucao; /* pode ser necessário aux -> requisitado = 1; */ } — 7/8 — 2. Exame de Programação MEEC-MEAer 2009/2010 5.g) (1.5 valores) Escreva uma função que imprima no ecrã todos os livros requisitados que já deveriam ter sido devolvidos. Esta função recebe como argumentos a base da lista e uma data (a actual). Para cada livro requisitado cuja data limite de devolução seja anterior à actual é impresso o seguinte: título, autor e número do requisitante. É obrigatório usar a função compara_datas. void lista_livros(livro * bd, data data_actual) { livro * aux; aux = bd->seg while (aux != NULL){ if(aux -> id_requisitante != -1){ /* ou if(aux -> requisitado == 1){ */ if(compara_datas(data_actual, aux->data_ devol) > 0{ printf(“%s %s %s\n”, aux ->titulo, aux-> autor, aux-> id_requisitante); } } aux = aux -> seg; } } — 8/8 —