Programação — MEEC — 2º Semestre 1o Exame — 11 de Junho de 2011 Duração: 3h00 1. (5.0 valores) 1.a) (4.0 valores) O código seguinte tem erros e como tal não faz o que aparece descrito no comentário. Identifique cada um dos erros sintácticos e lógicos indicando a linha onde ocorre. Identifique os erros e sugira uma correcção possível para cada um. Pressuponha que foram feitos todos os includes necessários. Identifique o erro indicando as suas consequências, e JUSTIFIQUE TODAS CORRECÇÕES 1) /* Programa que lê do teclado uma string e conta o 2) número de dígitos aí presentes*/ 3) int main(){ 4) int linha[100]; 5) int I; 6) int c; 7) 8) printf("Escreva uma frase\n"); 9) fgets(linha, 1024, stdin); 10) 11) while(i<100){ 12) if(linha(i) >0 && linha(i) < 9){ 13) c = c+1; 14) i=i+1; 15) } 16) printf("foram lidos o $d digitos",c); 17) exit(0); 18)} • • • • • • • • Linha 4 – a declaração da linha deve usar o tipo char. Uma string é um vector de caracteres e não de inteiros. Linha 4 – o comprimento da string é muito pequeno: o fgets pode ler até 1024 caracteres. Ou se aumenta o comprimento da string ou se reduz o valor no fgets Linha 5 – durante o programa usa-se a variável i minúsculo e não I maiúsculo. Ou se altera a declaracção da variável (na linha 5 ou se muda no código i para I) . Linha 10 - é necessário inicializar a variável c, caso contrário o contador não fica com o resutado certo (a variável b pode conter “ lixo”). Linha 10 - é necessário inicializar a variável i, caso não se acede às posições correctas do vector (a variável i pode conter “ lixo”) Linha 11 – Não se pode percorrer o vector até à última posição (100 ou 1024) é necessario terminar na último caracter lido (i< strlen(linha) ou linha[i] != '\0') Linha 12 – o acesso às posições de um vector não é representado por parenteses curvos mas sim rectos. Daria erro de compilação. Linha 12 – é necessário usar a plicas à volta do 0 e do 9. Assim estamos a comparar com os inteiros, mas quer-se comparar com os caracteres '0' e '9'. • • • Linha 12 – as comparações que se usam < e > devem ser substituídas por <= e >=. Se tal não acontecer os dígitos '0' e '9' não são contabilizados. Linha 13/14 – falta uma chaveta entre c = c+1; e i=i+1; . A ausência de chaveta provoca um erro de compilação, visto haver mais chavetas { do que } .Só queremos a instrução c = c+1; dentro do if. Linha 16 – dentro do printf usa-se %d ( e não $d) para indicar que se deseja escrever um inteiro. Se não se resolvesse esta questão, não seria impresso o valor de c, mas sim $d. /* Programa que lê do teclado uma string e conta o número de dígitos aí presentes*/ int main(){ char linha[1024]; int i; int c; 1) 2) 3) 4) 5) 6) 7) 8) 9) 10) 11) 12) 13) 14) 15) 16) 17) 18) 19) printf("Escreva uma frase\n"); fgets(linha, 1024, stdin); c = 0; i = 0; while(i< strlen(linha)){ if(linha[i] >= '0' && linha[i] <= '9'){ c = c+1; } i=i+1; } printf("foram lidos o %d digitos",c); exit(0); } 1.b)(1.0 valores) Observe o seguinte troço de código. Identifique e explique o seu problema. Sugira uma solução. int main(){ char linha[1024]; char palavra1[20]; ... sscanf(linha, "%s %s", palavra1); ... } • • O comprimento da linha é muito superior ao comprimento da palavra 1. Se a primeira palavra escrita tiver mais de 20 caracteres é possível que outras variáveis sejam corrompidas O sscanf tem dois %s mas apenas uma variável onde vão ser armazenados os valores. Ou se retira um %s ou se adiciona uma variável dentro do scanf. 2. (4.5 valores) 2.a) (2.0 valores) Desenvolva uma função que recebe como argumento um vector de 100 inteiros, calcula a sua média e desvio padrão. O cabeçalho dessa função deve ser void media_desvio(int vect[100], float * media, float * desvio); A fórmula genérica para o desvio padrão é: desvio= √ n 1 ∑ ( x −media )2 n−1 i=1 i #define MAX_VECT 10 float media_vect (int v[MAX_VECT]){ float sum= 0; int i = 0; for (i = 0; i <MAX_VECT; i++){ sum = sum+ v[i]; } return sum / MAX_VECT; } void media_desvio(int vect[MAX_VECT], float * media, float * desvio){ float desv, med; int i; med = media_vect(vect); desv = 0; for (i = 0; i < MAX_VECT; i++){ desv = desv + pow(med- vect[i], 2); } desv = desv/(MAX_VECT-1); *desvio = sqrt(desv); *media = med; } 2.b) (2.5 valores) Desenvolva um programa que lê 100 inteiros a partir do ficheiro chamado dados.dat e calcula, usando a função anterior, a média desses valores. Em cada linha do ficheiro deve aparecer um inteiro. Se a linha não tiver um inteiro, deverá ser ignorada e impressa uma mensagem de erro. Se a linha tiver mais caracteres para além do inteiro deverá unicamente ser impressa uma mensagem de aviso ( número será posteriormente utilizado). Se o ficheiro contiver mais de 100 inteiros, deverá ser impressa uma mensagem de aviso e calculada a média dos 100 primeiros. Se o ficheiro contiver menos de 100 inteiros deverá ser impressa uma mensagem de erro e sair (sem calcular a média). int main(){ FILE * fp; char linha [1024]; int cont_linhas = 0; int valor; char caracter_lixo; int vect[MAX_VECT]; float media, desvio; int n; fp = fopen("dados.txt", "r"); if(fp == NULL){ printf("ficheio inexistente\n"); exit(0); } while( fgets(linha, 1024, fp)!= NULL){ n = sscanf(linha, "%d %c", & valor, & caracter_lixo); if( n == 1){ if(cont_linhas < MAX_VECT) vect[cont_linhas] = valor; cont_linhas ++; } if( n > 1){ printf(" linha com lixo no fim!\n"); if(cont_linhas < MAX_VECT) vect[cont_linhas] = valor; cont_linhas ++; } if( n == 0){ printf(" linha sem inteiro!\n"); } } if(cont_linhas < MAX_VECT){ printf("ficheiro com menos que %d linhas correctas!(so %d)\n", MAX_VECT, cont_linhas); }else{ if(cont_linhas > MAX_VECT){ printf("ficheiro com mais que %d linhas correctas!(so %d)\n", MAX_VECT, cont_linhas); } media_desvio(vect, &media, &desvio); printf("media %f \ndsvio %f\n", media, desvio); } exit(0); } 3. (5.0 valores) 3.a) (0.5 valores) Defina o tipo quadrado, que permite guardar a informação de quadrados: coordenada do centro e comprimento do lado. typedef struct quadrado{ float x, y; float largura; } quadrado; ou typedef struct coord{ float x, y; } coord; typedef strcut quadrado{ coord centro; float largura; } ou struct quadrado{ float x, y; float largura; } 3.b) (0.5 valores) Defina uma função que recebe como argumento um quadrado e retorna a sua área. float area_quadrado(quadrado q){ return q.largura * q.largura; } 3.c) (1.0 valores) Defina uma função que recebe como argumento um quadrado e um ponto e retorna verdadeiro se esse ponto se encontrar dentro do quadrado e falso caso contrário. Pressuponha que os lados dos quadrados são paralelos aos eixos x e y. int dentro_quadrado(quadrado q, float p_x, float p_y){ if(p_x > q.x+q.largura/2 || p_x > q.x-q.largura/2) return 0; if(p_y > q.y+q.largura/2 || p_y > q.y-q.largura/2) return 0; return 1; } ou int dentro_quadrado(quadrado q, float p_x, float p_y){ if(p_x > q.x+largura/2 && p_x < q.x-largura/2 && p_y < q.y+largura/2 && p_y < q.y-largura/2) return 1; else return 0; } 3.d) (1.5 valores) Desenvolva uma função que recebe como argumento dois quadrados e retorna verdadeiro se esse quadrados se interceptarem e falso caso contrário. Pressuponha que os lados dos quadrados são paralelos aos eixos x e y. int intersecta(quadrado q1, quadrado q2){ if(abs(q1.x -q2.x) > (q1.largura + q2.largura)/2){ return 0; } if(abs(q1.y -q2.y) > (q1.largura + q2.largura)/2){ return 0; } return 1; } ou int intersecta(quadrado q1, if(q1.x - q1.largura/2 q1.x + q1.largura/2 return 0; if(q1.y - q1.largura/2 q1.y + q1.largura/2 return 0; return 1; } quadrado q2){ > q2.x + q2.largura/2 || < q2.x - q2.largura/2) > q2.y + q2.largura/2 || < q2.y - q2.largura/2) 3.e) (1.5 valores) Defina uma função que recebe como argumento um vector de 100 quadrados e calcula quantos desses quadrados se interceptam entre si. Pressuponha que os lados dos quadrados são paralelos aos eixos x e y. int cont_intercep(quadrado vect[100]){ int i, j; int contador = 0; for (i = 0 ; i< 100; i++){ for (j = i+1 ; i< 100; j++){ if( intersecta( vect[i],vect[j]) ){ contador ++; } } } return contador; } 4. (5.5 valores) Nas repartições de finanças usam-se sistemas de senhas em papel para ordenar os utentes. Pretende-se substituir estes sistemas por um mais avançado em que, quando chega à repartição, o utente apenas inseres o seu nome e selecciona os serviços pretendidos, sendo posteriormente chamado por um altifalante. Este sistema utiliza uma fila de utentes, ordenada por ordem de chegada, contendo, para além do nome, o serviço que pretende usar. Os serviços são numerados entre 1 e 6. A fila contém todos os utentes, independentemente do serviço pelo qual estão à espera. 4.a) (0.5 valores) Declare os tipos necessários ao armazenamento da fila de espera de utentes. typedef struct utente{ char nome [100]; int servico; struct utente *proximo; }utente; 4.b) (1.0 valores) Desenvolva a função que permite inserir na fila de espera um novo utente: nome e serviço pretendido. Esta função não lê nenhuma informação do teclado. utente * insere_utente(utente * fila, char * nome, int servico){ utente * aux; utente * novo; novo = malloc(sizeof(utente)); if(novo == NULL){ printf("ERRO\n"); exit(-1); } strcpy(novo->nome, nome); novo->servico = servico; novo->proximo = NULL; if(fila == NULL){ return novo; } aux = fila; while(aux->proximo != NULL){ aux = aux-> proximo; } aux->proximo = novo; return fila; } 4.c) (1.0 valores) Desenvolva a função que permite interagir com o utente na altura da sua entrada na repartição: leitura do nome, leitura dos vários serviços pretendidos e inserção na fila. Esta função recebe como argumento a fila. Esta pode ser passada por referência ou retornada no fim. Esta função lê do teclado o nome do utente e os vários serviços pretendidos. utente * entrada_utente(utente * fila){ char nome[100]; char linha[100]; int i; printf("Nome do utente: "); fgets(nome, 100, stdin); for(i= 1; i<= 6; i++){ printf("pretende servico %d? (sim/nao): ", i); fgets(linha, 100, stdin); if(strcmp(linha, "sim\n") == 0){ fila = insere_utente(fila, nome, i); } } return fila; } 4.d) (1.0 valores) Desenvolva a função que permite “chamar” o próximo utente a ser atendido. Esta função recebe como argumento a fila e o número do serviço. Depois de encontrar na fila o próximo utente a ser atendido, imprime o seu nome no écran e retira-o da fila. utente * chama_utente(utente * fila, int servico){ utente * prox; utente * ant; if(fila == NULL){ return fila; } if(fila->servico == servico){ prox = fila; fila = fila->proximo; printf("PROXIMO UTENTE: %s\nSERVICO: %d\n", prox->nome, prox->servico); free(prox); return fila; } ant = fila; prox = fila->proximo; while(prox != NULL && prox->servico != servico){ prox = prox->proximo; ant = ant->proximo; } if(prox!= NULL){ ant->proximo = prox->proximo; printf("PROXIMO UTENTE: %s\nSERVICO: %d\n", prox->nome, prox->servico); free(prox); } return fila; } 4.e) (1.0 valores) Desenvolva a função que imprime o número de utentes em espera para cada serviço. void estatistica (utente * fila){ int i, cont; utente * aux; for (i = 1; i <=6; i++){ cont = 0; aux = fila; while( aux != NULL){ if(aux->servico == i){ cont ++; } aux = aux-> proximo; } printf("SERVICO %d - UTENTES EM ESPERA %d\n", i, cont); } } 4.f) (1.0 valores) Quando um utente está a ser atendido e é chamado por outro serviço, perde a vez visto o sistema não o conseguir pôr em espera. Descreva textualmente quais as alterações que seria necessário realizar, para que o sistema conseguisse colocar um utilizador em espera, sendo posteriormente chamado quando deixasse de ser atendido. Indique: Alterações ao tipo de dados Funções adicionais que seriam necessárias Alterações às funções já implementadas • • • • As estruturas que armazenam os utentes em fila de espera necessitam de um atributo adicional (chamado estado) que indica se esse utente está a ser atendido ou não. Seria um booleano que seria inicializado a Falso. Quando é chamado um utente para um servicó (função da alínea 4.d seria necessário verificar se o próximo utente para o serviço pretendido) estava nesse momento a ser ratendido noutro serviço (estado a Verdadeiro). Em caso positivo, chamar-se-ia o utente seguinte na fila. Quando um utente é chamado para um serviço é necessário actualizar o estado desse utente na fila, mudando o campo estado para verdadeiro. Após ser atendido o estado do utente passa a falso, podendo ser chamado para outros serviços