Aula 4 Algoritmo para determinar o máximo de uma sequência de n inteiros sendo n dado. Limite da capacidade de representação das variáveis do tipo int. Programa para tabelar potências de base 2 Análise de erros de overflow/underflow
Encontrar o máximo numa sequência de n inteiros Escrever um algoritmo para indicar o maior valor numa sequência de n inteiros lidos do standard input, sendo n 0 o primeiro valor lido. Implementá-lo. O que guarda cada variável Se (n 0) então ler(maximo); Enquanto (n 0) fazer ler(valor); Se (maximo < valor) então maximo valor; escrever(maximo); n: o número de valores que ainda serão dados maximo: o maior dos elementos já dados valor: o último dos já dados (a partir do 2 ō elemento) Por isso, maximo foi inicializado com o primeiro elemento.
Encontrar o máximo numa sequência de n inteiros Codificação em linguagem C: Se (n 0) então ler(maximo); Enquanto (n 0) fazer ler(valor); Se (maximo < valor) então maximo valor; escrever(maximo); int valor, maximo, n; if (n!= 0) { scanf("%d",&maximo); n = n-1; while (n!= 0) { scanf("%d",&valor); if (maximo < valor) maximo = valor; n = n-1; printf("%d\n",maximo); return 0;
Questões de robustez Assumiu-se que o valor de n seria um inteiro não negativo. Neste pressuposto, pode-se demonstrar a correcção e terminação dos métodos 1 e 2. São ambos algoritmos para resolução do problema. Método 1 Método 2 Se ( n 0 ) então ler(maximo); Enquanto (n 0) fazer ler(valor); Se (maximo < valor) então maximo valor; escrever(maximo); Se ( n > 0 ) então ler(maximo); Enquanto (n 0) fazer ler(valor); Se (maximo < valor) então maximo valor; escrever(maximo); Contudo, se, por descuido, fosse dado um valor negativo para n, o método 1 não terminaria mas o método 2 terminaria sempre. Assim, o método 2 seria mais robusto.
Questões de robustez // --- metodo1.c --- int valor, maximo, n; if (n!= 0) { scanf("%d",&maximo); n = n-1; while (n!= 0) { scanf("%d",&valor); if (maximo < valor) maximo = valor; n = n-1; printf("%d\n",maximo); return 0; // --- metodo2.c --- int valor, maximo, n; if (n > 0) { scanf("%d",&maximo);... $ gcc -o m1 metodo1.c $ m1 11-6 8-1 45 2 14 450 8-456 9 12 450 $ m1-6 8-1 45 2 14 450 8-456 9 12 ^C (usar CTRL-c para terminar a execução) $ gcc -o m2 metodo2.c $ m2 11-6 8-1 45 2 14 450 8-456 9 12 450 $ m2-6 8-1 45 2 14 450 8-456 9 12 $ NB: se n < 0, o programa m1 pode terminar após erro de underflow
Capacidade de representação das variáveis do tipo int int n; Declara n como variável do tipo int (signed int) A declaração pode incluir modificadores: short, long, unsigned O tamanho de int depende da arquitectura das máquinas e do sistema operativo, mas será sempre de pelo menos 16 bits. Nas máquinas disponíveis nas salas de aula, cada valor do tipo int ocupa 32 bits (4 bytes) Intervalo Ler/Escrever (base 10) int [ 2 31, 2 31 1] %d unsigned int [0, 2 32 1] %u unsigned long int [0, 2 32 1] %lu long int [ 2 31, 2 31 1] %ld short int [ 2 15, 2 15 1] %hd unsigned short int [0, 2 16 1] %u Para saber mais: na shell, executar more /usr/include/limits.h e, mais tarde, man 3 printf
Capacidade de representação das variáveis do tipo int $more /usr/include/limits.h /* * ISO C99 Standard: 7.10/5.2.4.2.1 Sizes of integer types <limits.h> */ (...) /* Minimum and maximum values a signed short int can hold. */ # define SHRT_MIN (-32768) # define SHRT_MAX 32767 /* Maximum value an unsigned short int can hold. (Minimum is 0.) */ # define USHRT_MAX 65535 /* Minimum and maximum values a signed int can hold. */ # define INT_MIN (-INT_MAX - 1) # define INT_MAX 2147483647 /* Maximum value an unsigned int can hold. (Minimum is 0.) */ # define UINT_MAX 4294967295U /* Minimum and maximum values a signed long int can hold. */ # if WORDSIZE == 64 # define LONG_MAX 9223372036854775807L # else # define LONG_MAX 2147483647L # endif 2 15 = 32768 2 16 = 65536 2 31 = 2147483648 2 32 = 4294967296
Erros de overflow Escrever um programa para tabelar potências de 2, desde 2 0 = 1 até 2 n, para n 0 dado. pot 1; i 0; Enquanto (i n) fazer escrever(i, pot); pot 2 pot; i i + 1; Quando testa a condição do ciclo, pot tem 2 i int pot=1, i=0, n; while (i <= n) { printf("2 elevado a %d = %d\n", i,pot); pot = 2*pot; i = i+1; return 0; O algoritmo descreve um método para calcular correctamente potências de 2. No programa, a variável pot é do tipo int e só permite guardar inteiros até 2 31 1. Para i 31, os resultados estarão errados devido a erros de overflow.
Overflow em implementações de int com 32 bits int pot=1, i=0, n; while (i <= n) { printf("2 elevado a %d = %d\n", i,pot); pot = 2*pot; i = i+1; return 0; Recordar que: a representação do valor 2 32 em binário requer 33 bits, o mais significativo é 1 e outros 0 s; numa representação em 32 bits e complemento para 2, o valor 2 31 representa o inteiro 2 31. cf., matéria leccionada em Introdução aos Computadores $ a.out 35 2 elevado a 0 = 1 2 elevado a 1 = 2 2 elevado a 2 = 4 2 elevado a 3 = 8 2 elevado a 4 = 16 2 elevado a 5 = 32 2 elevado a 6 = 64 2 elevado a 7 = 128 2 elevado a 8 = 256 2 elevado a 9 = 512... 2 elevado a 30 = 1073741824 2 elevado a 31 = -2147483648 2 elevado a 32 = 0 2 elevado a 33 = 0 2 elevado a 34 = 0 2 elevado a 35 = 0