Notas Aula

Embed Size (px)

Citation preview

Linguagem C - Notas de AulaProfa . Carmem Hara e Prof. Wagner Zola Reviso: Prof. Armando Luiz N. Delgado

Maio 2008

Parte I

Programao Bsica em CEstas notas de aula apresentam os conceitos bsicos da Linguagem C e se prope a abordar apenas o que importante para a compreenso bsica de programas de computadores.

1

1

Programas C

Um programa C consiste de uma ou mais partes chamadas funes. Um programa em C consiste de pelo menos uma funo chamada main. Esta funo marca o ponto de incio de execuo do programa. Programas C tem a seguinte estrutura geral: #include denio de constantes funes int main() { declarao de variveis .... sentenas .... }

1.1

Sentenas: simples e compostas

Cada instruo em C chamada de sentena. Sentenas simples so terminadas com um ponto e vrgula. Usando chaves, podemos agrupar sentenas em blocos, chamados de sentenas compostas. Exemplos de sentenas incluem: Simples: x = 3; Composta: { i = 3; printf("%d\n", i); i = i + 1; } O corpo da funo main() um exemplo de sentena composta.

1.2

Variveis em C

Uma varivel uma informao qe voc pode usar dentro de um programa C . Esta informao est associada com um lugar especco da memria (isso feito pelo compilador). O nome da varivel e o endereo da memria onde a informao est armazenada esto associados. O nome e o endereo no mudam. Mas, o valor da informao pode mudar (o valor do que est dentro da caixa pode mudar, embora o tipo seja sempre o mesmo). Cada varivel tem um tipo associado. Alguns tipos de variveis que discutiremos incluem int, char e oat. Cada varivel usa uma determinada quantidade de armazenamento em memria. A maneira como sabemos quantos bytes so utilizados pelo tipo da varivel. Variveis do mesmo tipo utilizam o mesmo nmero de bytes, no interessando qual o valor que a varivel armazena. 2

Um dos tipos utilizados para armazanar nmeros o int. Ele usado para armazenar nmeros inteiros. Outro tipo o char, usado para armazenar caracteres. Um caracter um smbolo (uma letra do alfabeto, um dgito, um smbolo de pontuao, etc). Um char armazenado em 1 byte de memria. Cada caracter associado com um valor entre 0 e 255. O compilador C faz a traduo para voc, portanto voc no precisa saber estes nmeros. Em C , um caracter representado entre apstrofes (). Por exemplo, C, a, 5, $. Note que 5 um caracter, e no o inteiro 5.

A gura acima mostra como um int e um char so armazenados na memria. Outro tipo existente o oat, usado para armazenar nmeros reais (nmeros com o ponto decimal). Este nmeros so armazenados em duas partes: a mantissa e o expoente. Eles so armazenados de uma maneira que se assemelha a notao exponencial. Por exemplo, o nmero 6.023 1023 escrito como 6.023e23. Neste caso, a mantissa 6.023 e o expoente 23. Estes nmeros so armazenados de uma forma padro, tal que a mantissa tem apenas um dgito para a esquerda do ponto decimal. Desta forma, 3634.1 escrito como 3.6341e3, e 0.0000341 escrito 3.41e5. Note tambm que a preciso limitada pela mantissa. Somente os 6 dgitos mais signicativos so armazenados. Em Dev-C++ um oat ocupa 4 bytes de memria. H muitos outros tipos (short, long, double), que sero descritos no futuro.

1.3

Denio de Varivel em C

Se voc usa variveis no programa, voc deve deni-las. Isto envolve especicar o tipo da varivel e o seu nome. As regras para formar nomes de variveis em C so: qualquer sequncia de letras, digitos, e _, MAS DEVE COMEAR com uma letra ou com _. Por exemplo, hora_inicio, tempo, var1 so nomes de variveis vlidos, enquanto 3horas, total$ e azul-claro no so nomes vlidos; Maisculas = Minsculas; No so permitidos nomes ou palavras reservadas da linguagem. sempre uma boa idia ter certas regras (para voc mesmo) para nomear variveis para tornar o programa mais legvel: D nomes signicativos as variveis (mas no muito longos); Use nomes de variveis do tipo i, j, k somente para variveis tipo contadores; Pode-se usar letras maisculas ou _ para juntar palavras. Por exemplo, horaInicio ou hora_inicio. Use o que voc preferir, mas SEJA CONSISTENTE em sua escolha. 3

auto default oat main static void

break do for register struct volatile

case double goto return switch while

char else if short typedef

const enum int signed union

continue extern long sizeof unsigned

Tabela 1: Palavras Reservadas da Linguagem C Os tipos bsicos de dados existentes em C so: Tipo de Dado char int oat double Bits 8 16 32 64 Faixa de Valores -128 a 127 -32768 a 32767 7 dgitos signicativos 15 dgitos signicativos

Abaixo est um exemplo de um programa com diversas denies de variveis: int main() { int pera; char qualidade; oat peso; pera = 3; qualidade = A; peso = 0.653; ... } Quando variveis so denidas, elas no possuem valores ainda. Ns damos valores s variveis usando o operador de atribuio (=). Variveis tambm podem ser inicializadas para conter valores quando so denidas. Usando esta forma, o program acima caria: int main() { int pera = 3; char qualidade = A; oat peso = 0.653; ... } Para resumir: quando um programa executado, uma varivel associada com: um tipo: diz quantos bytes a varivel ocupa, e como ela deve ser interpretada. um nome: um identicador. um endereo: o endereo do byte menos signicativo do local da memria associado a varivel. 4

um valor: o contedo real dos bytes associados com a varivel; o valor da varivel depende do tipo da varivel; a denio da varivel no d valor a varivel; o valor dado pelo operador de atribuio, ou usando a funo scanf(). Ns veremos mais tarde que a funo scanf() atribui a uma varivel um valor digitado no teclado. Em C , nomes de variveis devem ser declarados antes de serem usados. Se no for declarado, ocorrer um erro de compilao. Devem ser dados valores s variveis antes que sejam utilizadas. Se voc tentar utilizar a varivel antes de especicar o seu valor, voc obter lixo (o que quer que esteja armazenado no endereo da varivel na memria quando o programa comea sua execuo), culminando com falha na execuo do programa.

1.4

Constantes

Em C , alm de variveis, ns podemos usar tambm nmeros ou caracteres cujos valores no mudam. Eles so chamados de constantes. Constantes no so associados a lugares na memria. Assim como variveis, constantes tambm tm tipos. Uma constante pode ser do tipo int, char, etc. Voc nao tem que declarar constantes, e pode utiliz-las diretamente (o compilador reconhece o tipo pela maneira que so escritos). Por exemplo, 2 do tipo int, e 2.0 do tipo double. Por conveno, todas as constantes reais so do tipo double.

1.5

Caracteres Constantes

Um constante caracter escrita entre apstrofes, como em A. Todas as letras, nmeros e smbolos que podem ser impressos so escritos desta forma em C . s vezes precisamos de caracteres que no podem ser impressos, por exemplo, o caracter de nova linha, que no tem uma tecla especca no teclado. Neste caso, usa-se caracteres de escape. Tais caracteres so escritos no somente como um smbolo entre apstrofes, mas como um sequncia de caracteres entre apstrofes. Por exemplo, \n o caracter para nova linha (uma sequncia que inicia com a barra invertida chamada de sequncia de escape). Se quisermos representar o caracter de barra invertida, temos que escrever \\. Note que \n o caracter de nova linha - embora use-se dois smbolos para represent-lo. A barra invertida chamada de escape. Ele diz ao compilador que o n que segue no a letra n, mas que a sequncia completa de caracteres deve ser interpretada como o caracter de nova linha. Cada caracter constante tem um valor inteiro igual ao seu valor numrico do seu cdigo ASCII. Por exemplo, considere a constante A, que tem cdigo ASCII 65, e B que tem cdigo 66. Ns podemos usar a expresso A + 1. O resultado o valor 66. E se o tipo da expresso resultante for char, ento o resultado da expresso B.

1.6

Entrada e Sada

Se quisermos que um programa C mostre alguns resultados, ou se quisermos que o programa pea ao usurio que entre com alguma informao, ns podemos usar as funes existentes em C chamadas printf() e scanf(). Se voc quiser usar estas funes em seu programa, voce deve incluir a seguinte linha no incio do seu cdigo fonte: #include Isto faz com que o arquivo header chamado stdio.h seja includo no seu arquivo fonte. Este arquivo contm prottipos das funes print() e scanf(). Ele declara ao compilador o nome das funes e algumas informaes adicionais necessrias para que as instrues sejam executadas corretamente.

5

1.7

Exibindo informaes na tela: printf()

printf() pode ser utilizado para imprimir mensagens e valores em uma variedade de formatos. Por enquanto, printf() melhor descrito atravs de exemplos. printf(Al todo mundon); Imprimir Al todo mundo na tela do computador. Para dizer funo printf exatamente o que fazer, ns devemos especicar o que ser impresso. Ns devemos dar a funo o que chamamos de argumentos. No exemplo acima, Alo todo mundo um argumento para a funo printf(). O primeiro argumento do printf() sempre um string (uma srie de caracteres entre aspas (")). Ns tambm podemos colocar caracteres de escape no string para imprimir caracteres especiais. Por exemplo, colocando \n no string causa que o restante do string seja impresso na linha seguinte. Outros caracteres de escape sero apresentados no futuro. Se quisermos imprimir o valor de expresses variveis, argumentos adicionais so necessrios. Dizemos ao printf() como mostrar valores de expresses usando especicadores de formato. Podemos colocar %c, %d, %f (ou outros especicadores de formato listados no texto) dentro do primeiro argumento para especicar o que queremos dar display. Ns ento passamos argumentos adicionais que ser referem aos especicadores de formato (na ordem em que eles ocorrem). Este argumentos podem ser constantes ou variveis, ou alguma expresso mais complicada. O que quer que eles sejam, eles devem ser avaliados e os valores obtidos e impressos de acordo com os especicadores de formato. Considere o seguinte programa: #include #dene PRECO 1.99 int main() { int pera = 3; char qualidade = A; oat peso = 2.5; printf(Existem %d peras de qualidade %c , pera, qualidade); printf(pesando %f quilos.\n, peso); printf(O preco por quilo e %f, total e %f\n, PRECO, peso * PRECO); } A sada do programa ser: Existem 3 peras de qualidade A pesando 2.500000 quilos. O preco por quilo e 1.990000, total e 4.975000 A linha #dene PRECO 1.99 no incio do programa dene uma macro. Ou seja, denimos que PRECO um sinnimo para 1.99 e, portanto, toda ocorrncia de PRECO no programa substitudo por 1.99 antes que ele seja compilado. Ns tambm podemos especicar o tamanho utilizado para impresso da seguinte forma: %6d inteiro, com pelo tamanho pelo menos 6 %6f ponto utuante, com tamanho pelo menos 6 %.3f ponto utuante, com 3 digitos depois do ponto decimal %6.3fponto utuante, com tamanho pelo menos 6 e 3 digitos depois do ponto decimal %6.0fponto utuante, com pelo menos tamanho 6 e nenhum digito depois do ponto decimal. 6

Note que a especicao de tamanho simplesmente determina o tamanho mnimo. Se o nmero no couber no tamanho especicado, o nmero completo ser mostrado. Quando utilizar a funo printf() tenha cuidado para especicar o tipo correto dos argumentos. Se o tipo do argumento no for correto, o compilador Dev-C++ no acusar erro, e um valor incorreto ser mostrado. Por exemplo, no programa abaixo que est incorreto: #include int main() { printf(Exemplo errado: %d\n, 3.14159); } O resultado do programa ser alguma coisa como: Exemplo errado: -31147

1.8

Lendo informao: scanf()

scanf() pode ser usado para ler valores digitados no teclado. Estes valores so lidos de acordo com especicadores de converso, que so especicados pelo programador como argumentos do scanf(). Considere o seguinte programa: #include int main() { int idade; printf(Entre sua idade: ); scanf(%d, &idade); printf(Voce tem %d anos\n, idade); } Este programa mostrar no monitor: Entre sua idade: e aguardar que um nmero seja digitado e a tecla ENTER. Depois disso, a varivel idade conter o valor digitado pelo usurio. Assim como com o printf(), o primeiro argumento o especicador de formato. Os prximos argumentos correspondem a o que est sendo especicado pelo primeiro argumento. Note o & precedendo a varivel idade. Simplesmente lembre-se que voc geralmente precisar colocar um & precedendo nomes de variveis em um scanf(). Voc sempre precisar us-lo antes de variveis do tipo primrio como os discutidos at este momento (int, char, oat, e suas verses long e unsigned). Mais de um valor pode ser lido por um mesmo scanf(). Considere o seguinte exemplo: #include int main() { int dia, mes, ano; printf(Entre com a data do seu aniversario (dd mm aa): ); scanf(%d %d %d, &dia, &mes, &ano); 7

printf(Voce nasceu em %d/%d/%d\n, dia, mes, ano); } Este exemplo funciona exatamente como o exemplo anterior. Um nico scanf() l os 3 nmeros quando estes nmeros so separados por espaos (espaos em branco, tabulao, novas linhas). Ento voc pode teclar ENTER depois de cada nmero, ou colocar espaos ou tabulaes entre os nmeros. Os espaos so ignorados pelo scanf(). Os brancos na especicao de formato do scanf(), %d %d %d so simplesmente para facilitar a leitura do programa, e no tem nada a ver com os espaos ignorados pelo scanf(). Se tivssemos escrito %d%d%d, o scanf() funcionaria da mesma forma. Os espaos em branco simplesmente so necessrios para saber quando termina um nmero e comea o outro. Porm se o scanf() estiver lendo caracteres (%c), os espaos no so ignorados, j que espaos so caracteres vlidos na linguagem. Por exemplo, o cdigo ASCII para espao em branco e 32.

1.9

Algoritmo X ProgramaALGORITMO PERIMETRO_AREA /* Calcula o permetro e a area de uma circunferencia de raio R (fornecido pelo usuario) */ /* Definir variaveis */ int Raio; float Perim, Area, PI; PI = 3.14159; /* Obter Raio da circunferencia */ Escreva("Entre com o valor do raio:"); Leia(Raio); /* Calcular Perimetro do Circulo */ Perim = 2 * PI * Raio; /* Calcular Area da Circunferencia */ Area = PI * Raio ** 2; /* Exibir Resultados */ Escreva("O perimetro da circunferencia de raio", Raio, "eh", Perim); Escreva("e a area eh ",Area); /* Terminar Programa */ FIM_ALGORITMO PERIMETRO_AREA

Programa em C /* programa que calcula o permetro e a rea de uma circunferncia de raio R (fornecido pelo usurio) */ #include /* inclui diretivas de entrada-sada */ #include /* inclui diretivas das funes matemticas */ 8

#define

PI

3.14159

int main() { /* Definir variaveis */ int Raio; float Perim, Area; /* Obter Raio da circunferencia */ printf("Entre com o valor do raio: "); scanf("%d", &Raio); /* Calcular Perimetro do Circulo */ Perim = 2 * PI * Raio; /* Calcular Area da Circunferencia */ Area = PI * pow(Raio, 2); /* Exibir Resultados */ printf("O perimetro da circunferencia de raio %d eh %.2f printf("e a area eh %.2f", Area); }

\n", Raio, Peri

9

22.1

Operaes Aritmticas e Expresses. Operaes Relacionais.Operaes Aritmticas

Em C , ns podemos executar operaes aritmticas usando variveis e constantes. Algumas operaes mais comuns so: + adio - subtrao * multiplicao / diviso % resto (mdulo) Estas operaes podem ser usadas como mostram os exemplos abaixo, assumindo que as variveis necessrias j esto declaradas: celsius = (fahrenheit - 32) * 5.0 / 9.0; forca = massa * aceleracao;

i = i + 1; 2.1.1 Precedncia de Operadores

Em C , assim como em lgebra, h uma ordem de precedncia de operadores. Assim, em (2 + x)(3x2 + 1), expresses em parntesis so avaliadas primeiro, seguidos por exponenciao, multiplicao, diviso, adio e subtrao. Da mesma forma, em C , expresses entre parntesis so executadas primeiro, seguidas de *, / and % (que tem todos a mesma precedncia), seguido de + and - (ambos com a mesma precedncia). Quando operaes adjacentes tm a mesma precedncia, elas so associadas da esquerda para a direita. Assim, a * b / c * d % e o mesmo que ((((a * b) / c) * d) % e). 2.1.2 A Operao de Resto (%)

Esta operao usada quando queremos encontrar o resto da diviso de dois inteiros. Por exemplo, 22 dividido por 5 4, com resto 2 (4 5 + 2 = 22). Em C , a expresso 22 % 5 ter valor 2. Note que % s pode ser utilizados entre dois inteiros. Usando ele com um operando do tipo float causa um erro de compilao (como em 22.3 % 5). 2.1.3 Expresses e Variveis

Expresses aritmticas podem ser usadas na maior parte dos lugares em que uma varivel pode ser usada. O exemplo seguinte vlido: int raio = 3 * 5 + 1; printf("circunferencia = %f\n", 2 * 3.14 * raio); 10

Exemplos de lugares onde uma expresso aritmtica NO pode ser usada incluem: int yucky + 2 = 5; scanf("%d", &(oops * 5)) Este exemplo ilegal e causar erro de compilao.

2.2

Operadores RelacionaisEm C , h operadores que podem ser usados para comparar expresses: os operadores relacionais. H seis operadores relacionais em C :

< menor que > maior que = maior ou igual que () == igual a != no igual a (=) Os resultados deste operadores 0 (correspondendo a falso), ou 1 (correspondendo a verdadeiro). Valores como esses so chamados valores booleanos. Algumas linguagens de programao como Pascal tem um tipo de varivel distinto para valores booleanos. Este no o caso do C , onde valores booleanos so armazenados como variveis numricas tais como o int. Considere o seguinte programa: int main() { int idade; idade = 17; printf("Pode tirar carteira de motorista? %d\n", idade >= 18); idade = 35; printf("Pode tirar carteira de motorista? %d\n", idade >= 18); } A sada deste programa ser: Pode tirar carteira de motorista? 0 Pode tirar carteira de motorista? 1 Na primeira linha, idade 17. Logo, 17 >= 18 falso, que 0. Depois disso, idade 35. Logo, 35 >= 18 verdadeiro, que 1. Note tambm que o operador de igualdade escrito com sinais de igual duplo, ==, no =. Tenha cuidado com esta diferena, j que colocar = no lugar de == no um erro sinttico (no gera erro de compilao), e no signica o que voc espera.

11

2.2.1

Precedncia dos operadores relacionais

Operadores aritmticos tem precedncia maior que os operadores relacionais. Por exemplo, a expresso 3 + 5 < 6 * 2 o mesmo que (3 + 5) < (6 * 2). Se por alguma razo voc quer que o resultado do uma operao relacional em uma expresso aritmtica, necessrio usar parntesis. Por exemplo, a expresso score + (score == 0) ser sempre igual ao valor de score, exceto quando o valor de score seja 0. Neste caso, o valor da expresso 1 (porque (score == 0) igual a 1). Uma observao sobre valores booleanos embora voc possa assumir que o valor de uma operao relacional 0 ou 1 em C , qualquer valor diferente de zero considerado verdadeiro. Falaremos sobre isso mais tarde durante o curso.

2.3

Reviso de Expresses:

O que impresso pelos dois programas abaixo? #include int main() { int score = 5; printf(%d, printf(%d, printf(%f, printf(%c, printf(%d, } #include int main() { int n1, n2, n3; printf(Entre com um numero inteiro: ); scanf(%d, &n1); n1 += n1 * 10; n2 = n1 / 5; n3 = n2 % 5 * 7; n2 *= n3-- % 4; printf(%d %d %d, n2, n3, n2 != n3 + 21); } Como a seguinte expresso completamente parentizada ? a * b / c + 30 >= 45 + d * 3 ++e == 10 5 + 10 * 5 % 6); 10 / 4); 10.0 / 4.0); A + 1); score + (score == 0)); ==> ==> ==> ==> ==> 7 2 2.5 B 5

2.4

Exemplo de programas

Exemplo 1: escreva um programa que leia um nmero inteiro e imprima 0 se o nmero for par e 1 se o nmero for mpar. 12

#include int main() { int numero; printf(Entre com um numero inteiro: ); scanf(%d, &numero); printf(\nPar? %d\n, numero % 2 ); } Exemplo 2: escreva um programa que leia 3 nmeros inteiros e calcule a soma, mdia, e produto. #include int main() { int n1, n2, n3; int soma; printf( "Entre com 3 numeros inteiros: "); scanf( "%d %d %d",&n1, &n2, &n3); soma = n1 + n2 + n3; printf( "Soma = %d\n", soma ); printf( "Media = %8.2f\n", soma / 3.0 ); printf( "Produto = %d\n", n1 * n2 * n3 ); }

2.5

Precedncia e associatividade de operadores

Operador () ++ * + < == =

Associatividade esquerda para direita direita para esquerda esquerda para direita esquerda para direita esquerda para direita esquerda para direita direita para esquerda

-- & (unrios) / % >= != += -= *= /=

%=

13

3

Expresses como valores

Em C , todas as expresses so avaliadas. O resultado da avaliao um valor e pode ser usado em quaisquer lugares.

3.1

Expresses aritmticas, relacionais e lgicas

Como voc j sabe, expresses usando operadores aritmticos, relacionais e lgicos1 so avaliados. O valor resultante um nmero. Para os operadores relacionais e lgicos, este nmero pode ser 0 (que signica falso) ou 1 (que signica verdadeiro). Por exemplo: 3 + 5 * 4 % (2 + 8) tem valor 3; 3 < 5 tem valor 1; x + 1 tem valor igual ao valor da varivel x mais um; (x < 1) || (x > 4) tem valor 1 quando o valor da varivel x fora do intervalo [1,4], e 0 quando x est dentro do intervalo.

3.2

Expresses envolvendo o operador de atribuio (=)

O formato do operador de atribuio : lvalue = expressao (1)

Um lvalue (do ingls left-hand-side value - valor a esquerda) um valor que se refere a um endereo na memria do computador. At agora, o nico lvalue vlido visto no curso o nome de uma varivel. A maneira que a atribuio funciona a seguinte: a expresso do lado direito avaliada, e o valor copiado para o endereo da memria associada ao lvalue. O tipo do objeto do lvalue determina como o valor da expressao armazenada na memria. Expresses de atribuio, assim como expresses, tm valor. O valor de uma expresso de atribuio dado pelo valor da expresso do lado direito do =. Por exemplo: x = 3 tem valor 3; x = y+1 tem o valor da expresso y+1. Como consequncia do fato que atribuies serem expresses que so associadas da direita para esquerda, podemos escrever sentenas como: i = j = k = 0; Que, usando parnteses, equivalente a i = (j = (k = 0)). Ou seja, primeiro o valor 0 atribudo a k, o valor de k = 0 (que zero) atribudo a j e o valor de j = (k = 0) (que tambm zero) atribudo a i. Uma caracterstica muito peculiar de C que expresses de atribuio podem ser usados em qualquer lugar que um valor pode ser usado. Porm voc deve saber que us-lo dentro de outros comandos produz um efeito colateral que alterar o valor da varivel na memria. Portanto, a execuo de: int quadrado, n = 2; printf("Quadrado de %d eh menor que 50? %d \n", n, (quadrado = n * n) < 50);1

Operadores lgicos && e || sero vistos na prxima aula.

14

causa no apenas que o valor 4 seja impresso, como a avaliao da expresso relacional dentro do printf() faz com que o nmero 4 seja copiado para o endereo de memria associado com a varivel quadrado. Note que necessrio usar parnteses em quadrado = n * n j que = tem menor precedncia que o operador relacional = 0) { if (num % 2 == 0) printf("O numero e par e positivo\n"); else printf("O numero e impar e positivo\n"); } else { if (num % 2 == 0) printf("O numero e par e negativo\n"); else printf("O numero e impar e negativo\n"); } }

5.1

A ambigidade do else

O aninhamento de sentenas if-else sem usar chaves ({ e }) para delimitar o bloco de senteas a ser executado pode trazer efeitos indesejados. H uma regra simples para determinar qual if est associado a qual else. Regra de associao: Um else est associado com a ltima ocorrncia do if sem else. O exemplo seguinte est errado porque associa o else ao if "incorreto": #include main(void) { int num; /* Obtem um numero do usuario */ printf("Entre com o numero de peras: "); scanf("%d", &num); 20

/* Imprime uma mensagem dizendo se o numero de peras e 0 ou 1 (*** isto esta errado !! ***) */ if (num != 0) if (num == 1) printf("Voce tem uma pera.\n"); else printf("Voce nao tem nenhuma pera.\n"); } Neste exemplo, o if tem o seguinte signicado, segundo a regra de associao: #include main(void) { int num; /* Obtem um numero do usuario */ printf("Entre com o numero de peras: "); scanf("%d", &num); /* Como a sentenca if e vista pelo compilador */ if (num != 0) if (num == 1) printf("Voce tem uma pera.\n"); else printf("Voce nao tem nenhuma pera.\n"); } Para evitar este problema, chaves ({ e }) devem ser usadas para tirar a ambiguidade. O exemplo abaixo mostra como as chaves podem ser inseridas para corrigir o programa acima. #include main(void) { int num; /* Obtem um numero do usuario */ printf("Entre com o numero de peras: "); scanf("%d", &num); /* Como corrigir o problema (este programa funciona) */ if (num != 0) { if (num == 1) printf("Voce tem uma pera.\n"); } else printf("Voce nao tem nenhuma pera.\n"); } Exerccio 1: Faa um programa que leia 3 nmeros e imprima o maior. 21

#include main(void) { int a, b, c, maior; printf("Entre com os tres numeros: "); scanf("%d %d %d", &a, &b, &c); if (a > b) maior = a; else maior = b; if (maior < c) maior = c; printf("O Maior numero eh %d\n", maior); }

6

Operadores Lgicos

Todos os programas at agora consideraram if com condies de teste simples. Alguns exemplos de testes simples: b != 0, contador = 0) if (num % 2 == 0) printf("Numero par nao negativo.\n"); Com os operadores lgicos isso pode ser simplicado: if ((num>=0) && (num%2 == 0)) printf("Numero par nao negativo.\n"); A operao de negao, !, pode ser usado da seguinte forma: !expresso lgica: O valor a negao lgica da expresso dada. Por exemplo: 22

!0 !1

1 0

Ns podemos usar o operador de negao lgica e escrever o exemplo acima como: if (num>0 && !(num%2)) printf("Numero par nao negativo.\n"); Os dois operadores binrios operam sobre duas expresses lgicas e tem o valor 1 (verdadeiro) or 0 (falso). Os exemplos abaixo mostram o seu uso: a==0 && b==0 (verdadeiro se ambos a == 0 e b == 0, portanto se a e b so 0) a==0 || b==0 (verdadeiro se pelo menos uma das variveis a or b for 0) Uma expresso usando && verdadeira somente se ambos os operadores forem verdadeiros (no zero). Uma expresso usando || falsa somente se ambos os operadores forem falsos (zero). Verique na Tabela 2 o resultado do uso de operadores lgicos: expr1 expr2 expr1 && expr2 expr1 || expr2 verdadeiro verdadeiro verdadeiro verdadeiro verdadeiro f also f also verdadeiro f also verdadeiro f also verdadeiro f also f also f also f also Tabela 2: Resultado de uso de Operadores Lgicos A precedncia do operador de negao lgica a mais alta (no mesmo nvel que o - unrio). A precedncia dos operadores lgicos binrios menor que a dos operadores relacionais, e mais alta que a operao de atribuio. O && tem precedncia mais alta que o ||, e ambos associam da esquerda para a direita (como os operadores aritmticos). Como a precedncia dos operadores lgicos menor que a dos operadores relacionais, no necessrio usar parnteses em expresses como: x >= 3 && x =

+=

-=

*=

/=

%=

Tabela 3: Precedncia e associatividade de operadores #include main() { int lado1, lado2, lado3; int s1, s2, s3; printf(Entre com o tamanho dos lados do triangulo: ); scanf(%d %d %d, &lado1, &lado2, &lado3); /* s1 s2 s3 calcula o quadrado dos lados */ = lado1*lado1; = lado2*lado2; = lado3*lado3;

/* testa a condicao para um triangulo reto */ if ( lado1>0 && lado2>0 && lado3 > 0 ) { if (s1==s2+s3 || s2==s1+s2 || s2==s1+s3) ) { printf("Triangulo reto!\n"); } else { printf("Nao pode ser um triangulo!\n"); } } Na utilizao de expresses lgicas, as seguintes identidades so teis. Elas so chamadas de Lei de DeMorgan: !(x && y) equivalente a !x || !y e !(x || y) equivalente a !x && !y

77.1

ExemplosIF - ELSE24

Assuma as seguintes declaraoes de variveis:

int x = 4; int y = 8; O que impresso pelos seguintes programas ? 1. if (y = 8) if (x = 5) printf( "a " ); else printf( "b " ); printf( "c " ); printf( "d\n" ); ==> a c d 2. mude = para == ==> b c d 3. altere o programa acima para produzir a seguinte saida: Assuma x = 5 e y = 8 (a) a (b) a d Assuma x = 5 e y = 7 (a) b c d

7.2

Operadores lgicos

O que impresso pelas seguintes sentenas? 1. Assuma x = 5 e y = 8. if (x == 5 && y == 8) printf( "a\n" ); else printf( "b\n" ); 2. Assuma x = 4 e y = 8. if (x == 5 || y == 8) printf( "a\n" ); else printf( "b\n" );

==> a

==> a

if !(x == 5 || y == 8) printf( "a\n" ); else printf( "b\n" );

/* equiv. (x != 5 && y != 8) */

==> b 25

if !(x == 5 && y == 8) printf( "a\n" ); else printf( "b\n" ); 3. Precedncia: ! > && > ||

/* equiv. (x != 5 || y != 8) */

==> a

if (x == 5 || equiv.

y == 8 && z == 10)

if (x == 5 || (y == 8 && z == 10))

8

A construo else-if

Embora ela no seja um tipo diferente de sentena, a seguinte construo bastante comum para programar decises entre diversas alternativas: if (expressao1 ) sentenca1 else if (expressao2 ) sentenca2 else if (expressao3 ) sentenca3 . . . else if (expressaon1 ) sentencan1 else sentencan As expresses lgicas so avaliadas em ordem, comeando com a expressao1 . Se uma das expresses for verdadeira, a sentena associada ser executada. Se nenhuma for verdadeira, ento a sentena, sentencan , do ltimo else ser executada como opo default. Se a opo default no for necessria, ento a parte else sentencan pode ser removida.

26

Exemplo 9: O seguinte exemplo mostra um else-if de trs opes. O programa l dois nmeros e diz se eles so iguais ou se o primeiro nmero menor ou maior que o segundo. #include main(void) { int num1, num2; /* obtem 2 numeros do usuario */ printf("Entre um numero: "); scanf("%d", &num1); printf("Entre com um outro numero: "); scanf("%d", &num2); /* mostra a mensagem de comparacao */ if (num1 == num2) printf("Os numeros sao iguais\n"); else if (num1 < num2) printf("O primeiro numero e menor\n"); else printf("O primeiro numero e maior\n"); } No programa acima, se (num1 == num2) for verdadeiro, ento os nmeros so iguais. Seno, vericado se (num1 < num2). Se esta condio for verdadeira, ento o primeiro nmero menor. Se isso no for verdadeiro, ento a nica opo restante que o primeiro nmero maior. Exemplo 10: Este programa l um nmero, um operador e um segundo nmero e realiza a operao correspondente entre os operandos dados. #include main(void) { float num1, num2; char op; /* obtem uma expressao do usuario */ 27

printf("Entre com numero operador numero\n"); scanf("%f %c %f", &num1, &op, &num2); /* mostra o resultado da operacao */ if (op == +) printf(" = %.2f", num1 + num2); else if (op == -) printf(" = %.2f", num1 - num2); else if (op == /) printf(" = %.2f", num1 / num2); else if (op == *) printf(" = %.2f", num1 * num2); else printf(" Operador invalido."); printf("\n"); } Exemplos da execuo deste programa: Entre com numero operador numero: 5 * 3.5 = 17.50 Entre com numero operador numero: 10 + 0 = 10.00 Entre com numero operador numero: 10 x 5.0 Operador invalido.

28

9

A sentena switch

A sentena switch outra maneira de fazer decises mltiplas. Ele pode ser usado para testar se uma dada expresso igual a um valor constante e, dependendo do valor, tomar determinadas aes. O formato da sentena switch : switch (expressao) { case expressao-constante 1: sentencas 1 case expressao-constante 2: sentencas 2 . . . default: sentencas n }

A sentena switch primeiro avalia a expresso. Se o valor da expresso for igual a uma das expresses constantes, as sentenas que seguem o case so executados. Se o valor da expresso no for igual a nenhuma das constantes, as sentenas que seguem default so executadas. As sentenas que seguem o case so simplesmente uma lista de sentenas. Esta lista pode conter mais de uma sentena e no necessrio coloc-las entre chaves ({ e }). A lista de sentenas tambm pode ser vazia, isto , voc pode no colocar nenhuma sentena seguindo o case. Tambm no obrigatrio colocar o default. S o use quando for necessrio. Note no diagrama acima que TODAS as sentenas que seguem a constante com o valor igual ao da expresso sero executados. Para que se execute APENAS as sentenas que seguem o case que seja igual ao valor da expresso precisamos usar a sentena break, que veremos em seguida.

10

A sentena break

O break faz com que todas as sentenas que o seguem dentro da mesma sentena switch sejam ignorados. Ou seja, colocando a sentena break no nal de uma sentena case faz com que as sentenas que seguem os cases subsequentes no sejam executadas. Em geral, este o comportamento desejado quando se usa o switch, e cases sem o break no nal so de pouca utilidade. Portanto, o uso de sentenas case sem o break devem ser evitados e quando utilizados devem ser comentados ao lado com algo como /* continua proxima sentenca - sem break */. Com a sentena break o diagrama de uxo ca: 29

Note a similaridade com o diagrama da sentena else-if e a diferena com o diagrama da sentena switch acima. O prximo programa tem a mesma funo de calculadora do programa anterior, porm utilizando a sentena switch. Exemplo 11: #include main(void) { float num1, num2; char op; printf("Entre com numero operador numero:\n"); scanf("%f %c %f", &num1, &op, &num2); switch (op) { case +: printf(" = %.2f", break; case -: printf(" = %.2f", break; case *: printf(" = %.2f", break; case /: printf(" = %.2f", break; default: printf(" Operador break; } printf("\n");

num1 + num2);

num1 - num2);

num1 * num2);

num1 / num2);

invalido.");

30

} Como mencionado anteriormente, possvel no colocar nenhuma sentena seguindo um case. Isso til quando diversas sentenas case (diversas constantes) tm a mesma ao. Por exemplo, podemos modicar o programa acima para aceitar x e X para multiplicao e \ para diviso. O programa ca ento: #include main(void) { float num1, num2; char op; printf("Entre com numero operador numero:\n"); scanf("%f %c %f", &num1, &op, &num2); switch (op) { case +: printf(" = %.2f", break; case -: printf(" = %.2f", break; case *: case x: case X: printf(" = %.2f", break; case /: case \\: printf(" = %.2f", break; default: printf(" Operador break; } printf("\n"); } Exerccio 2: Ler mes e ano e imprimir o numero de dias do mes no ano digitado. #include main() { int mes, ano, numDias; printf("Entre com mes e ano (mm aa):"); scanf( "%d %d", &mes, &ano); if( mes < 1 || mes > 12 || ano < 0 || ano > 99 ) printf("mes ou ano invalido\n"); 31

num1 + num2);

num1 - num2);

num1 * num2);

num1 / num2);

invalido.");

else { switch( mes ){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: numDias = 31; break; case 2: if( ano % 4 == 0 ) numDias = 29; else numDias = 28; break; default: numDias = 30; } printf("%2d/%2d tem %d dias\n", mes, ano, numDias); } }

32

1111.1

FunesFunes: o que so e por que us-las

Quando queremos resolver um problema, em geral tentamos dividi-lo em subproblemas mais simples e relativamente independentes, e resolvemos os problemas mais simples um a um. A linguagem C dispe de construes (abstraes) que auxiliam o projeto de programas de maneira top-down. Uma funo cria uma maneira conveniente de encapsular alguns detalhes de processamento, ou seja, como algum resultado obtido. Quando esta computao necessria, a funo chamada, ou invocada. Desta forma, quando uma funo chamada o usurio no precisa se preocupar como a computao realizada. importante saber o que a funo faz (qual o resultado da execuo de uma funo) e tambm como se usa a funo. Criando funes, um programa C pode ser estruturado em partes relativamente independentes que correspondem as subdivises do problema. Voc j viu algumas funes: printf(), scanf(), getchar(), sqrt(). Elas so funes de uma biblioteca padro (do C ). Voc no sabe como elas foram escritas, mas j viu como utiliz-las. Ou seja, voc sabe o nome das funes e quais informaes especcas voc deve fornecer a elas (valores que devem ser passados para as funes) para que a funo produza os resultados esperados. Quando nos referirmos a uma funo neste texto usaremos a maneira frequentemente utilizada que o nome da funo seguido de (). Tomemos como exemplo o programa abaixo, que calcula o produto de 2 nmeros inteiros positivos apenas se ambos forem primos: /*---------------------------------------------------------------------Verifica se 2 Numeros sao primos e multiplica um pelo outro se o forem ---------------------------------------------------------------------*/ #include int main() { int n1, n2, j; int prod = 0; printf("\nEntre com 2 numeros inteiros: "); scanf ("%d %d", &n1, &n2);

/* Se for indicado 1, 0 ou negativo, nao sao primos */ if ( n1 >= 2 && n2 >= 2) { /* Testa se n1 primo */ j = n1 - 1; while ((j > 1) && (n1 % j != 0)) { j = j - 1; } if( j == 1) { /* n1 eh primo */ /* Testa se n2 primo */ j = n2 - 1; 33

while ((j > 1) && (n2 % j != 0)) { j = j - 1; } if( j == 1) prod = n1 * n2; } } if (prod) printf("PRODUTO = %d\n", prod); } /* n2 eh primo */

Observe que o cdigo que verica se um nmero primo teve que ser reproduzido dentro do programa por duas vezes (para testar se os nmeros fornecidos pelo usurio eram primos). Um dos benefcios mais bvios de usar funes que podemos evitar repetio de cdigo. Em outras palavras, se voc quiser executar uma operao mais de uma vez, voc pode simplesmente escrever a funo uma vez e utiliz-la diversas vezes ao invs de escrever o mesmo cdigo vrias vezes. Outro benefcio que se voc desejar alterar ou corrigir alguma coisa mais tarde, mais fcil alterar em um nico lugar. O exemplo acima poderia ser simplicado pela criao de uma funo chamada ehPrimo, que dado um nmero n, d como resultado 1 se este nmero primo, ou 0 (zero) se o nmero no primo: int ehPrimo(int n) { int j; j = n - 1; while ((j > 1) && (n % j != 0)) { j = j - 1; } if (j == 1) return 1; else return 0; }

O exemplo pode ser ento alterado e simplicado com o uso da funo ehPrimo(): /*---------------------------------------------------------------------Verifica se 2 Numeros sao primos e multiplica um pelo outro se o forem ---------------------------------------------------------------------*/ #include 34

int ehPrimo(int n) { int j; j = n - 1; while ((j > 1) && (n % j != 0)) { j = j - 1; } if (j == 1) return 1; else return 0; } main() { int n1, n2, j; int prod = 0, ep1, ep2; printf("\nEntre com 2 numeros inteiros: "); scanf ("%d %d", &n1, &n2);

/* Se for indicado 1, if ( n1 >= 2 && n2 >= ep1 = ehPrimo(n1); ep2 = ehPrimo(n2);

0 ou negativo, nao sao primos */ 2) { /* Verifica se n1 eh primo */ /* Verifica se n2 eh primo */

if (ep1 != 0 && ep2 != 0) prod = n1 * n2; } if (prod) printf("PRODUTO = %d\n", prod); } Como pode ser observado, sejam quais forem os 2 nmeros fornecidos, no precisa escrever um cdigo similar ao mostrado na funo ehPrimo acima para cada nmero.Basta chamar a funo ehPrimo(), passar os valores necessrios para vericar a primalidade de cada nmero, e utilizar os resultados. Evitar repetio de cdigo a razo histrica que funes foram inventadas (tambm chamado de procedimento ou subrotinas em outras linguagens de programao). A maior motivao para utilizar funes nas linguagens contemporneas a reduo da complexidade do programa e melhoria da modularidade do programa. Dividindo o programa em funes, muito mais fcil projetar, entender e modicar um programa. Por exemplo, obter a entrada do programa, realizar as computaes necessrias e apresentar o resultado ao usurio pode ser implementado como diferentes funes chamadas por main() nesta ordem. Funes podem ser escritas independentemente uma da outra. Isto signica que, em geral, variveis usadas dentro de funes no so compartilhadas pelas outras funes. Assim sendo, o comportamento da 35

funo previsvel. Se no for assim, duas funes completamente no relacionadas podem alterar os dados uma da outra. Se as variveis so locais a uma funo, programas grandes passam a ser mais fceis de serem escritos. A comunicao entre funes passa a ser controlada elas se comunicam somente atravs pelos valores passados as funes e os valores retornados.

11.2

Denindo funes

Um programa C consiste de uma ou mais denies de funes (e variveis). H sempre uma funo chamada main. Outras funes tambm podem ser denidas. Cada uma pode ser denida separadamente, mas nenhuma funo pode ser denida dentro de outra funo. Abaixo, mostramos um exemplo simples de um programa que consiste de duas funes: main() e alo(). Quando executado, este programa imprimir a mensage Alo! trs vezes. #include /* declaracao (prottipo) da funcao alo() */ void alo(void); /* definicao da funcao main() */ main() { int i; i = 1; while (i 0 && s2 > 0 && s3 > 0 && (quadrado(s1) + quadrado(s2) == quadrado(s3) || quadrado(s2) + quadrado(s3) == quadrado(s1) || quadrado(s3) + quadrado(s1) == quadrado(s2)) ) printf(" %d %d %d podem formar um triangulo reto\n", s1, s2, s3); else printf(" %d %d %d nao podem formar um triangulo reto\n",s1, s2, s3); } /* funcao que calcula o quadrado de um numero */ int quadrado(int n) { return n * n; } Note que quando chamamos a funo quadrado() passamos o valor no qual desejamos executar o clculo, e tambm usamos o valor retornado pela funo em expresses. O valor de quadrado(s1) o valor que a funo quadrado() retorna quando chamado com o valor do argumento sendo igual ao valor da varivel s1. Os valores retornados pelas chamadas de funes podem ser usados em todos os lugares valores podem ser usados. Por exemplo, y = quadrado(3); Aqui quadrado(3) tem o valor 9, portanto 9 pode ser atribudo a varivel y; x = quadrado(3) + quadrado(4); atribuir 25 a varivel x, e area = quadrado(tamanho); atribuir a varivel area o valor da varivel tamanho elevado ao quadrado. O prximo exemplo tem uma funo chamada cinco: int cinco(void); main() { printf("cinco = %d\n", cinco() ); } 39

int cinco(void) { return 5; } A sada do programa ser cinco = 5 porque o valor de cinco() dentro da sentena printf() 5. Olhando na sentena return, 5 a expresso retornada para o chamador. Outro exemplo: int obtem_valor(void); main() { int a, b; a = obtem_valor(); b = obtem_valor(); printf("soma = %d\n", a + b); } int obtem_valor(void) { int valor; printf("Entre um valor: "); scanf("%d", &valor); return valor; } Este programa obtm dois inteiros do usurio e mostra a sua soma. Ele usa a funo obtem valor() que mostra uma mensagem e obtm o valor do usurio. Um exemplo de sada deste programa : Entre um valor: 15 Entre um valor: 4 soma = 19

11.5

Mais sobre o return

Quando uma funo return executada, a funo imediatamente acaba mesmo que haja cdigo na funo aps a sentena return. A execuo do programa continua aps o ponto no qual a chamada de funo foi feita. Sentenas return podem ocorrer em qualquer lugar na funo no somente no nal. Tambm vlido ter mais de um return dentro de uma funo. A nica limitao que return retorna um nico valor. O seguinte exemplo mostra uma funo (uma verso para int da funo obtem valor) que pede para usurio um valor e se o usurio digitar um valor negativo, imprime uma mensagem e retorna um valor positivo. 40

int obtem_valor_positivo(void) { int valor; printf("Entre um valor: "); scanf("%d", &valor); if (valor >= 0) return valor; printf("Tornando o valor positivo...\n"); return -valor; } Em uma funo void, return; (s com ;) pode ser usado para sair de uma funo. O exemplo seguinte, pede instrues ao usurio. Se o usurio reponder nao, a funo termina. Do contrrio, ele imprime as instrues e depois termina. void instrucoes(void) { int ch; printf("Voce quer instrucos? (s/n): "); ch = getchar(); /* Termina se resposta for n */ if (ch == n || ch == N) return; /* Mostra instrucoes */ printf("As regras do jogo sao . . . "); . . . return; } O return nal (antes de fechar as chaves do corpo da funo) na funo opcional. Se omitido, a funo atingir o nal da funo e retornar automaticamente. Note que o return opcional somente para funes void.

11.6

Mais sobre Argumentos

A comunicao entre uma funo e o chamador pode ser nas duas direes. Argumentos podem ser usados pelo chamador para passar dados para a funo. A lista de argumentos denida pelo cabealho da funo entre parnteses.. Para cada argumento voc precisa especicar o tipo do argumento e o nome do argumento. Se houver mais de um argumento, eles so separados por vrgula. Funes que no possuem argumentos tem void como lista de argumento. No corpo da funo os argumentos (tambm chamados de argumentos formais ou parmetros formais) so tratados como variveis. erro deni-los dentro do corpo da funo porque eles j esto denidos no cabealho. Antes da execuo da funo os valores passados pelo chamador so atribudos aos argumentos da funo. 41

Considere o seguinte programa com a funo abs() que calcula o valor absoluto de um nmero. int abs(int); main() { int n; printf("Entre um numero: "); scanf("%d", &n); printf("Valor absoluto de } /* Definicao da funcao abs */ int abs(int x) { if (x < 0) x = -x; return x; } A funo abs() tem um argumento do tipo int, e seu nome x. Dentro da funo, x usado como uma varivel x. Uma vez que abs() tem um nico argumento, quando ela chamada, h sempre um valor dentro do parnteses, como em abs(n). O valor de n passado para a funo abs(), e antes da execuo da funo, o valor de n atribudo a x. Aqui est um exemplo de uma funo que converte uma temperatura de Farenheit para Celsius: float fahr_para_cels(float f) { return 5.0 / 9.0 * (f - 32.0); } Como voc pode ver, esta funo tem somente um argumento do tipo float. Um exemplo de chamada desta funo poderia ser: fervura = fahr_para_cels(212.0); O resultado da funo fahr para cels(212.0) atribudo a fervura. Portanto, depois da execuo desta sentena, o valor de fervura (que do tipo float) ser 100.0. O exemplo seguinte possui mais de um argumento: float area(float largura, float altura) { return largura * altura; } Esta funo possui dois argumentos do tipo float. Para chamar uma funo com mais de um argumento, os argumentos devem ser separados por vrgula. A ordem em que os argumentos so passados deve ser na mesma em que so denidos. Neste exemplo, o primeiro valor passado ser a largura e o segundo a altura. Um exemplo de chamada seria tamanho = area(14.0, 21.5); Depois desta sentena, o valor de tamanho (que do tipo float) ser 301.0. 42 %d e %d", n, abs(n));

Quando passar os argumentos, importante ter certeza de pass-los na ordem correta e que eles so do tipo correto. Se isso no for observado, pode ocorrer erro ou aviso de compilao, ou resultados incorretos podem ser gerados. Uma ltima observao. Os argumentos que so passados pelo chamador podem ser expresses em geral e no somente constantes e varivies. Quando a funo chamada durante a execuo do programa, estas expresses so avaliadas, e o valor resultante passado para a funo chamada.

11.7

Chamada por valor

Considere novamente a funo quadrado(). Se esta funo chamada de main() como

p = quadrado(x);

somente o valor (no o endereo) de x passado para quadrado. Por exemplo, se a varivel tem valor 5, para a funo quadrado(), quadrado(x) ou quadrado(5) so o mesmo. De qualquer forma, quadrado() receber somente o valor 5. quadrado() no sabe se na chamada da funo o 5 era uma constante inteira, o valor de uma varivel do tipon int, ou alguma expresso como 625/25 - 4 * 5. Quando quadrado() chamado, no interessa qual a expresso entre parnteses, ela ser avaliada e o valor passado para quadrado(). Esta maneira de passar argumentos chamada de chamada por valor. Argumentos em C so passados por valor. Portanto, a funo chamada no pode alterar o valor da varivel passada pelo chamador como argumento, porque ela no sabe em que endereo de memria o valor da varivel est armazenado.

11.8

Variveis locais

Como voc provavelmente j reparou em alguns exemplos, possvel denir variveis dentro de funes, da mesma forma que temos denido variveis dentro da funo main(). A declarao de variveis feita no incio da funo. Estas variveis so restritas a funo dentro da qual elas so denidas. S esta funo pode enxergar suas prprias variveis. Por exemplo: void obtem_int(void); main() { obtem_int(); /* **** Isto esta errado **** */ printf("Voce digitou %d\n", x); } void obtem_int(void) { int x; printf("Entre um valor: "); scanf("%d", &x); printf("Obrigado!\n"); 43

} A funo main() usou um nome x, mas x no denido dentro de main; ele uma varivel local a get int(), no a main(). Este programa gera erro de compilao. Note que possvel ter duas funes que usam variveis locais com o mesmo nome. Cada uma delas restrita a funo que a dene e no h conito. Analise o seguinte programa (ele est correto): int obtem_novo_int(void); main() { int x; x = obtem_novo_int(); /* ****Isto nao esta errado !! **** */ printf("Voce digitou %d\n", x); } int obtem_novo_int(void) { int x; printf("Entre um valor: "); scanf("%d", &x); printf("Obrigado!\n"); return x; } A funo obtem novo int() usa uma varivel local chamada x para armazenar o valor digitado e retorna como resultado o valor de x. main() usa outra varivel local, tambm chamada de x para receber o resultado retornado por obtem novo int(). Cada funo tem sua prpria varivel x.

11.9

Prottipos

Os prottipos servem para dar ao compilador informaes sobre as funes. Isso para que voc possa chamar funes antes que o compilador tenha a denio (completa) das funes. O prottipo de uma funo idntico ao cabealho da funo, mas o nome dos argumentos podem ser omitidos e ele terminado com uma vrgula. Prottipos declaram uma funo ao invs de deni-las. O formato dos prottipos : tipo-de-retorno nome-da-funo(lista-dos-tipos-dos-argumentos); Denindo prottipos, voc no precisa se preocupar com a ordem em que dene as funes dentro do programa. A principal vantagem de denir prottipos que erros de chamada de funes (como chamar uma funo com o nmero incorreto de argumentos, ou com argumentos de tipo errado) so detectados pelo compilador. Sem prottipos, o compilador s saberia que h erro depois de encontrar a denio da funo. Em verses antigas do compilador C , programas com tais erros compilariam sem erros, o que tornava a depurao de erros mais difcil. Abaixo, mostramos duas funes e seus prottipos: float volume(float, float, float); 44

float dinheiro(int, int, int, int);

float volume(float comprimento, float largura, float altura) { return comprimento * largura * altura; } float dinheiro(int c25, int c10, int c5, int c1) { return c25 * 0.25 + c10 * 0.10 + c5 * 0.05 + c1 * 0.01; }

11.10

Documentao de funes

Voc deve documentar as funes que escreve. Na documentao voc deve especicar as seguintes informaes: Ao o que a funo faz Entrada descrio dos argumentos passados para a funo Sada descrio do valor retornado pela funo Suposies o que voc assume ser verdade para que a funo funcione apropriadamente Algoritmo como o problema resolvido (mtodo) Estas informaes devem ser colocadas como comentrio antes da denio da funo.

11.11

Comentrios

Voc pode colocar comentrios no seu programa para documentar o que est fazendo. O compilador ignora completamente o que quer esteja dentro de um comentrio. Comentrios em C comeam com um /* e terminam com um */. Alguns exemplos: /* Este e um comentario sem graca */ /* Este e um comentario que usa diversas linhas */ /* Este e * um comentario * de diversas linhas * mais bonito */ 45

Note que no podemos aninhar comentrios dentro de comentrios. Um comentrio termina no primeiro / que encontrar. O comentrio abaixo ilegal: * /* Este e um comentario /* illegal */ ilegal */

Regras para comentrio sempre uma boa idia colocar comentrios em seu programa das coisas que no so claras. Isto vai ajudar quando mais tarde voc olhar o programa que escreveu j h algum tempo ou vai ajudar a entender programas escritos por outra pessoa. Um exemplo de comentrio til: /* converte temperatura de farenheit para celsius */ celsius = (fahrenheit - 32) * 5.0 / 9.0; O comentrio deve ser escrito em portugus e no em C . No exemplo abaixo /* usando scanf, obter valor de idade e multiplicar por 365 para * obter dias */ scanf("%d", &idade); dias = idade * 365; o comentrio basicamente uma transcrio do cdigo do programa. Em seu lugar, um comentrio como /* obtem idade e transforma em numero de dias */ seria mais informativo neste ponto. Ou seja, voc deve comentar o cdigo, e no codicar o comentrio. Voc tambm deve evitar comentrios inteis. Por exemplo: /* Incrementa i */ i++; No h necessidade de comentrios j que i++ j auto explicativo. E abaixo est um exemplo de como voc deve comentar uma funo. /* funcao instrucoes() mostra instrucoes do programa * acao: entrada: nenhuma * nenhuma * saida: suposicoes: nenhuma * imprime as instrucoes * algoritmo: */ void instrucoes(void) { /* mostra instrucoes */ printf("O processo de purificacao do Uranio-235 e . . . . . } 46

");

12

O pr-processador

O pr-processador um programa que faz alguns processamentos simples antes do compilador. Ele executado automaticamente todas as vezes que seu programa compilado, e os comandos a serem executados so dados atravs de diretivas do pr-processador. Estas diretivas so colocadas em linhas que contm somente a diretiva (elas no so cdigo da linguagem C , portanto as regras para elas so um pouco diferentes). As linhas que comeam com um # so comandos para o pr-processador. A linha inteira reservada para este comando (nenhum cdigo C pode aparecer nesta linha e comandos do pr-processador no podem estar separados em diversas linhas).

12.1

A diretiva #define

Uma diretiva que usada frequentemente o #define. Esta diretiva usada para fazer substituio de macros. Por enquanto, mostraremos uma utilizao simples do #define, que simplestemente uma substituio no texto. O uso mais frequente desta diretiva dar nomes simblicos a uma constante (voc j viu outra maneira de denir contantes que colocar a palvavra const antes da denio de uma varivel). Por exemplo, seria conveniente usar PI em seus programas ao invs de digitar 3.1415926535 toda hora. Como outro exemplo, se voc quiser escrever um programa sobre estudantes de uma turma de 81 alunos, voc poderia denir NUM_ALUNOS como 81. Assim, se o nmero de alunos mudar, voc no precisaria modicar todo o seu programa onde o nmero de alunos (81) utilizado, mas simplesmente alterar a diretiva #define. Estas duas diretivas so denidas da seguinte forma: #define PI 3.1415926535 #define NUM_ALUNOS 81 Por conveno, nomes introduzidos por um #define so geralmente em letra maiscula (e variveis so em letra minscula, ou uma mistura de letras minsculas e maisculas). Assim, quando voc v um nome em um programa, voc sabe se o nome refere-se a uma varivel ou um nome denido por um #define. Considere o seguinte programa exemplo que usa PI: #define PI int main() { double raio; printf("Entre com o raio: "); scanf("%f", &raio); printf("Circunferencia = %f\n", 2.0 * PI * raio); } Lembre-se que o nome PI no um nome de varivel. Ele um nome que o pr-processador substituir pelo texto especicado pelo #define (mais ou menos da mesma forma que o comando pesquisa-e-substitui do editor de texto). O compilador nunca v ou sabe sobre PI. O compilador v o seguinte printf() do programa acima depois do pr-processador ser executado: printf("Circunferencia = %f\n", 2.0 * 3.14159265 * raio); 3.14159265

12.2

A diretiva #include

Agora imagine que estamos escrevendo uma biblioteca geomtrica: um conjunto de funes para calcular a rea de cilindros, cones, esferas. Se diferentes pessoal esto escrevendo cada uma das funes, eles 47

provavelmente colocaro suas funes em diferentes arquivos. Mas todas as funes usam o numero , e algumas outras constantes podem ser necessrias tambm. Ao invs de colocar o #define no incio de cada arquivo, um nico arquivo geom.h pode ser criado. Este arquivo conter a linha #define PI 3.14159265 Assim, se todos os arquivos de funes geomtricas puderem enxergar geom.h, eles compartilharo as mesmas denies. para isso que usamos a diretiva #include, para incluir em seu programa, informaes que esto em outro arquivo. Estas diretivas geralmente esto no incio do programa fonte, antes da denio de funes e varveis. Por exemplo, a diretiva #include "geom.h" colocada nos arquivos fontes que contm as funes geomtricas far com que todos eles usem o nome simblico PI ao invs de 3.14159265. O fato do nome do arquivo estar em aspas signica que o arquivo geom.h est no mesmo diretrio que os arquivos fontes (ao invs do diretrio onde se encontram as bibliotecas padro de C ). A diretiva #include colocada no incio do programa fonte para incluir informaes (como prottipos de funes) que so necessrios quando printf() e scanf() so chamados dentro do programa. O arquivo entre < > est em algum diretrio padro conhecido pelo pr-processador. Este arquivo stdio.h comum a todas as implementaes da linguagem C e contm infomaes necessrias para executar operaes de entrada e sada da entrada e sada padro (teclado e monitor). A extenso .h vem do ingls header le. Apesar de no ser obrigatrio que arquivos includos tenham a extenso .h, geralmente esta a conveno utilizada.

12.3

Comentrios

De um modo geral, o pr-processador dos compiladores existentes remove todos os comentrios do arquivo fonte antes do programa ser compilado. Portanto, o compilador nunca v realmente os comentrios.

13

Mais sobre funes

A nfase aqui ser em como funes funcionam. O que acontece quando uma funo chamada ? A que varivel um nome est se referenciando? O tratamento em tempo de execuo de um nome de varivel em C simples: um nome de varivel ou uma varivel local (a funo) ou uma varivel global (denida fora de qualquer funo). Em C , todas as funes tem que ser denidas. Para cada funo deve ser denido um prottipo. O prottipo escrito fora de qualquer funo. Desta forma, nomes de funes so visveis para todas as outras funes que podem ento invoc-las. A funo main() especial: onde a execuo do programa comea, e o prottipo de main() pode ser omitido. Uma denio de funo consiste de quatro partes: 1. o nome da funo; 2. a lista de parmetros formais (argumentos) com seus nomes e tipos. Se no houver argumentos, a palavra void escrita entre os parnteses. 3. o tipo do resultado que a funo retorna atravs da sentena return ou void se a funo no retorna nenhum valor. Lembre-se que somente um valor pode ser retornado por uma sentena return. 4. o corpo da funo, que uma sentena composta (comea e termina com chaves ({ }) contendo denio de variveis e outras sentenas. Em C , no se pode denir uma funo dentro de outra.

48

Para funes com argumentos: uma funo chamada dando o seu nome e uma lista de argumentos (expresses que so avaliadas e cujos valores so atribudos para os correspondentes parmetros formais da funo). Por exemplo, suponha que triang area() e circ area() sejam funes que calculam a rea de tringulos e crculos, respectivamente. Seus prottipos so: float triang_area(float , float); float circ_area(float); Estas funes podem chamadas de dentro de outras funes. Os argumentos reais com os quais elas so chamadas podem ser expresses constantes, or variveis locais, ou qualquer expresso cuja avaliao resulte em valores do tipo float (inteiros so convertidos para float da mesma forma que ocorre com atribuio de inteiros para variveis do tipo float). Alguns exemplos de chamadas: float area2, area3, area4, area5, base, altura, raio;

printf("area do triangulo = ", triang_area(0.03, 1.25)); base = 0.03; altura = 1.25; area2 = triang_area(base, altura); area3 = triang_area(1.6, altura); area4 = triang_area( 0.03 + base, 2 * altura); raio = base + altura; area5 = triang_area(raio, circ_area(raio)); A ltima sentena do exemplo acima atribui a varivel area5 a rea de um tringulo cuja base igual ao valor da varivel raio e a altura igual a area de um crculo de raio igual ao valor da varivel raio. Quando um programa executado, somente uma nica funo tem o controle em determinado momento. Falaremos mais sobre o que acontece quando uma funo chamada mais tarde nestas notas de aula. Variveis Locais Variveis que so denidas dentro de uma funo so variveis locais desta funo. Parmetros formais de uma funo so variveis locais da funo. Variveis locais so privativas a funo na qual so denidas. Somente esta funo pode enxerg-las (ela conhece o endereo das variveis e pode usar e modicar o seu contedo). Nenhuma outra funo pode acessar variveis locais de outra funo sem permisso (uma funo pode acessar variveis locais de outra se esta passar o endereo da varivel local como argumento este assunto ser tratado em notas de aula futuras). O fato de cada funo manter variveis locais escondidas do resto do mundo torna mais fcil a tarefa de escrever programas estruturados e modulares. Quando voc est escrevendo uma funo, voc pode dar as suas variveis locais o nome que quiser. Voc tambm no precisa se preocupar se outra pessoa escrevendo outra funo ter acesso ou altera variveis locais a sua funo. Variveis locais que so denidas dentro da funo devem ser inicializadas com algum valor antes de serem usadas. Caso contrrio, o seu valor indenido. J que parmetros formais (argumentos) so variveis locais da funo, eles podem ser usados no corpo da funo. Eles no devem ser denidos dentro da funo (sua denio j est no cabealho da funo). Os parmetros formais no precisam ser inicializados. Seus valores so fornecidos pelo chamador da funo atravs dos argumentos reais. Considere o seguinte exemplo: /***************************************************************** * Um programa que calcula a area de triangulos e circulos. * A base, altura e raio sao fornecidos pelo usuario. 49

* A saida do programa e a area do triangulo e circulo. *****************************************************************/ #include #define PI 3.1415 /******************* prototipos *******************/ float triang_area(float, float); float circ_area(float); /******************* definicao de funcoes *******************/ main(void) { /* definicao das variaveis locais */ float base, altura, raio; /* dialogo de entrada printf("\nEntre com a scanf("%f %f", &base, printf("\nEntre com o scanf("%f", &raio); */ base e altura do triangulo: "); &altura); raio do circulo: ");

/* chama as funcoes e imprime o resultado */ printf("Area do triagulo com base e altura %f e %f = %f\n", base, altura, triang_area(base, altura)); printf("Area do circulo com raio %f = %f\n", raio, circ_area(raio)); } /***************************************************************** * funcao: triang_area * calcula a area de um triangulo dada a base e altura * Entrada: base e altura do triangulo * Saida: area do triangulo *****************************************************************/ float triang_area(float base, float alt) { return 0.5*base*alt; } /***************************************************************** * funcao: circ_area * calcula a area de um circulo dado o raio * Entrada: raio do circulo * Saida: area do circulo 50

*****************************************************************/ float circ_area(float r) { return PI*r*r; }

Este programa C consiste de trs funes, main(), triang_area(), e circ_area(). main() tem variveis locais chamadas base, altura e raio; triang_area() tem como variveis locai seus parmetros formais, base e alt; circ_area() tem como varivel local seu parmetro formal r. Em geral, uma varivel local s existe durante a execuo da funo na qual ela est denida. Portanto, variveis locais existem desde o momento que a funo chamada at o momento em que a funo completada. Tais variveis so chamadas de automatic. Em C , uma varivel pode ser denida como sendo static. Neste caso, uma varivel local no visvel de fora do corpo da funo, mas ela no destruda no nal da funo como variveis automticas so. Cada vez que a funo chamada, o valor das variveis static o valor nal da varivel da chamada anterior. Variveis Globais At este momento, todas as variveis que vimos so denidas dentro de funes (no corpo da funo ou como parmetros formais). possvel tambm denir variveis fora das funes. Tais variveis so chamadas de variveis globais ou externas. O formato da denio de variveis globais o mesmo da denio de variveis locais. A nica diferena onde a varivel denida: variveis globais so denidas fora de qualquer funo. Ao contrrio das variveis locais, variveis globais podem ser vistas por todas as funes denidas aps a denio das variveis globais. Ns temos usado declaraes globais este tempo todo por exemplo, as declaraes de prottipos de funes. Elas so declaradas fora de qualquer funo e podem ser vistas por qualquer funo que esto aps sua declarao. No exemplo seguinte, uma varivel saldo que atualizada por trs funes diferentes denida como uma varivel global. As trs funes que a atualizam no chamam uma a outra. /***************************************************************** * Caixa eletronico simples * o saldo e o valor a ser alterado e entrado pelo usuario * a saida do programa e o saldo atualizado, incluindo juros *****************************************************************/ #include #define JUROS 0.07 /******************* prototipos *******************/ void credito(float); void debito(float); void juros(void); /******************* globais *******************/ float saldo; /* saldo atual; 51

* Alterada em: credito(), debito(), juros(), main() * Lida em: */

/*********************** definicao de funcoes ***********************/ main(void) { float valor;

/* valor a ser

depositado/retirado

*/

printf("Entre com o saldo atual: "); scanf("%f",&saldo); printf("Deposito: "); scanf("%f", &valor); credito(valor); printf("Retirada: "); scanf("%f", &valor); debito(valor); juros(); printf("Juros 7%%.\n"); printf("Saldo = : %.2f\n ", saldo); } /***************************************************************** * Deposita um valor; atualiza a variavel global saldo * Entrada: valor a ser depositado * Saida: nenhum *****************************************************************/ void credito(float val) { saldo += val; } /***************************************************************** * Debita um valor; atualiza a variavel global saldo * Entrada: valor a ser debitado * Saida: nenhum *****************************************************************/ void debito(float val) { saldo -= val; } /***************************************************************** * Acumula juros; atualiza a variavel global saldo; juros: RATE * Entrada: nenhuma * Saida: nenhuma 52

*****************************************************************/ void juros(void) { saldo += (saldo * JUROS); }

Um exemplo de execuo do programa:

Entre com o saldo atual: 1000 Deposito: 200 Retirada: 80 Juros 7%. Saldo = 1198.40 Variveis globais devem ser usadas SOMENTE quando muitas funes usam muito as mesmas variveis. No entanto, o uso de variveis globais perigoso (e no recomendado) porque a modularidade do programa pode ser afetada. Uma varivel global pode ser alterada de dentro de uma funo, e esta alterao pode inuir no resultado de uma outra funo, tornando-a incorreta (em um exemplo dado posteriormente nestas notas, duas chamadas a funo soma_y() com o mesmo argumento (zero) produz resultados diferentes, 100 e 300). Quando variveis globais so utilizadas, deve ser dado a elas nomes descritivos e um breve comentrio qual a nalidade da varivel e quais funes a acessam. Neste curso, voc utilizar variveis globais SOMENTE QUANDO FOR DADO PERMISSO PARA FAZ-LO. Caso contrrio, no permitido utiliz-las (ou seja, sero descontados pontos). Escopo de Variveis Como j discutimos anteriormente, uma varivel uma abstrao de dados que ns usamos em um programa. A varivel representa um endereo de memria onde os valores so armazenados. Durante a execuo do programa, valores diferentes poder ser armazenados neste endereo. Quando uma varivel denida, o nome da varivel atrelada a um endereo especco na memria. At este momento, j discutimos o que o nome de uma varivel, seu endereo, tipo e valor. Outra caracterstica que apresentaresmo agora o escopo. O escopo de uma varivel refere-se a parte do programa onde podemos utilizar a varivel. Em outras, palavras, uma varivel visvel dentro do seu escopo. O escopo de uma varivel local a funo na qual ela denida. Os parmetros formais de uma funo tambm so tratados como variveis locais. O escopo de uma varivel global a poro do programa depois da denio da varivel global (a partir do ponto onde ela denida at o nal do programa). Se o nome de uma varivel global idntico a uma varivel local de uma funo, ento dentro desta funo em particular, o nome refere-se a varivel local. (Embora tais conitos devem ser evitados para evitar confuso). Por exemplo, considere o seguinte programa: int valor = 3; /* definicao da variavel global */

int main() { /* definicao local de valor */ int valor = 4; printf("%d\n", valor); } 53

A sada do programa acima ser 4 j que valor refere-se a denio local. Considere outro exemplo: #include int soma_y(int); int soma_yy(int); int y = 100; /* variavel global main(void) { int z = 0;

*/

/* variavel local */

printf("%d\n", soma_y(z)); printf("%d\n", soma_yy(z)); printf("%d\n", soma_y(z)); } int soma_y(int x) { return x + y; /* x e variavel local, y e global */ } int soma_yy(int x) { y = 300; /* y e variavel global */ return x + y; /* x e variavel local */ } Vamos seguir a execuo deste programa. Primeiro, a varivel global y criada e inicializada com 100. Ento, a execuo da funo main() comeca: alocado espao na memria para a varivel local z. Esta varivel inicializada com 0. Considere a primeira sentena printf(): printf("%d\n", soma_y(z)); Esta uma chamada para a funo da biblioteca padro printf(). Os parmetros reais desta chamada so o string "%d\n" e a expresso soma_y(z). A ltima expresso a chamada da funo soma_y(). O valor desta expresso o resultado retornado por soma_y(). Qual o resultado? A funo soma_y chamada com o parmetro real z. Como z = 0, este o valor que ser passado para a funo soma_y; o 0 copiado para o parmetro formal x da funo soma_y(). Portanto, durante a excuo da primeira chamada a funo soma_y(), o valor da expresso x + y ser 0 + 100, que 100. Portanto, o valor da primeira chamada soma_ y(z) 100, e este nmero ser impresso com o primeiro printf() em main(). Agora considere a segunda sentena: printf("%d\n", soma_yy(z)); Quando a funo soma_yy(z) chamada, o valor de z (a varivel local z) ainda 0, portanto novamente 0 copiado para o parmetro formal int x da funo soma_yy. Quando a execuo de soma_yy() comea, ela primeiro troca o valor da varivel global y para 300 e ento retorna o valor de x + y, que neste caso 0 + 300. Portanto, o valor desta chamada a soma_yy(z) 300, e este nmero ser impresso pelo segundo printf() em main(). Por ltimo, considere a terceira sentena: 54

printf("%d\n", soma_y(z)); Quando a funo soma_y(z) chamada, o valor de z ainda 0, portanto, 0 copiada para o parmetro formal int x da funo soma_y(). Quando soma_ y() executada pela segunda vez, a varivel global y foi modicada para 300, portanto o valor de x + y 0 + 300. Portanto, o valor da chamada soma_yy(z) 300, e este nmero ser impresso pelo terceiro printf() em main(). Portanto, a sada da execuo deste programa ser 100 300 300 Neste exemplo, o escopo da varivel global y o programa todo. O escopo da varivel local z, denida dentro de maio o corpo da funo main. O escopo do parmetro formal x da funo soma_y o corpo de soma_y. O escopo do parmetro formal x da funo soma_yy o corpo de soma_yy.

13.1

Outro exemplo

Aqui apresentamos um exemplo de uma funo mais complicada. Esta funo calcula a raiz quadrada inteira de um nmero (o maior inteiro menor ou igual a raiz quadrada do nmero). Este programa usa o algoritmo divide e calcula mdia (uma aplicao do mtodo de Newton). Ele executa o seguinte: Dado x, achar x computando sucessivamente an = 1x +an1 an1

se n = 0 caso contrrio

para todo n N

2

Os valores de an convergem para

x a medida que n cresce.

Para achar a raiz quadrada inteira, este algoritmo repetido at que a2 x < (an + 1)2 n Por exemplo, para achar a raiz quadrada inteira de 42 (usando diviso inteira que trunca a parte fracional do nmero) a0 = 1, a1 = (42/1 + 1)/2 = 21, a2 = (42/21 + 21)/2 = 11, a3 = (42/11 + 11)/2 = 7, a4 = (42/7 + 7)/2 = 6. Uma vez que a2 = 62 = 36 42 < (a4 + 1)2 = 72 = 49, o processo termina e a resposta 6. 4 (No necessrio voc entender por que este algoritmo funciona portanto no se preocupe se no conseguir entend-lo)

55

int raizInteira(int);

/* prototipo */

/************************************************************** * function: raizInteira(x) dado x, retorna a raiz quadrada inteira de x * acao: inteiro positivo x * in: out: raiz quadrada inteira de x * * suposicoes: x >= 0 metodo de dividr e calcular media: comecando com * algoritmo: um palpite de 1, o proximo palpite e calculado como * (x/palpite_ant + palpite_ant)/2. Isso e repetido * ate que palpite^2 = palpite*palpite && x < (palpite+1)*(palpite+1))) { /* Calcula proximo palpite */ palpite = (x/palpite + palpite) / 2; } return palpite; }

Note que usando a lei de DeMorgan, podemos re-escrever a expresso teste do while em uma forma equivalente: x < palpite * palpite || x >= (palpite + 1) * (palpite + 1) Deve estar claro neste ponto a diferenca entre ao e algoritmo. Uma pessoa que quer usar esta funo precisa saber somente a ao, no o algoritmo. tambm importante especicar os dados que so esperados pela funo e retornados por ela para outras pessoas poderem us-la. As suposies devem esclarecer as restries da funo sobre quando a funo pode falhar ou produzir resultados errados. Neste caso, um nmero negativo produziria um erro, j que nmeros negativos no possuem raiz quadrada. No h necessidade de ir em muitos detalhes em qualquer parte da documentao da funo. Embora ela deva conter informao suciente para que algum (que no possa ver o cdigo) saber utiliz-la. Detalhes sobre implementao e detalhes menores sobre o algoritmo devem ser colocados como comentrios no prprio cdigo.

56

14

Estruturas de Repetio

A linguagem C possui comandos para repetir uma sequncia de instrues. Estas estruturas de repetio, tambm conhecidas como laos (do ingls loops). A principal construo que veremos o while2

14.1

O comando de repetio while

O comando de repetio while tem duas partes: a expresso de teste e o corpo da repetio. O formato do while : while (expresso teste) corpo da repetio A expresso teste inicialmente avaliada para vericar se o lao deve terminar. Caso a expresso seja verdadeira (isto , diferente de 0 (zero)), o corpo da repetio executado. Depois desta execuo, o processo repetido a partir da expresso teste. O corpo do lao, por sua vez, pode ser uma sentena simples ou composta.

O exemplo abaixo mostra o uso do comando de repetio while: int contador = 0; while( contador < 5 ) { printf( "contador = %d\n", contador); contador += 1; } printf("ACABOU !!!!\n"); Sada: contador = 0 contador = 1 contador = 2 contador = 3 contador = 4 ACABOU !!!!2

Existem outras estruturas de repetio: for (seo 15.1) e do ... while (seo 15.3).

57

Neste exemplo, a expresso de teste contador < 5, e o corpo do lao a sentena printf(). Se examinarmos cuidadosamente este exemplo, veremos que a varivel contador inicializada com 0 (zero) quando denida. Depois disso, a expresso de teste vericada e, como 0 < 5 verdadeiro, o corpo da repetio executado. Assim, o programa imprime contador = 0, e incrementa contador de um (atravs do ps-decremento indicado no argumento de printf()). Em seguida, a expresso de teste vericada novamente e todo o processo se repete at que contador seja 4 e contador = 4 seja impresso. Depois disso, contador incrementado para 5 e o teste executado. Mas desta vez, 5 < 5 falso, ento o lao no continua. A execuo do programa continua na sentena que segue o lao (no caso, imprimir a frase ACABOU !!!). Aps a execuo do while, a varivel contador tem valor 5. No exemplo acima, h uma sentena simples no corpo da repetio. Quando este for denido por uma sentena composta (bloco), no se deve esquecer de usar as chaves ({ e }) para delimitar o bloco da sentena composta. O exemplo seguinte mostra um uso mais apropriado do comando while: Em situaes onde o nmero de repeties no conhecido antes do inico do comando while: Exemplo 1: Este programa pede nmeros ao usurio at que a soma de todos os nmeros digitados for pelo menos 20. #include main( ){ int total = 0, num; while( total < 20 ) { printf( "Total = %d\n", total ); printf( "Entre com um numero: " ); scanf( "%d", &num ); total += num; } printf( "Final total = %d\n", total ); } Exemplo de sada: Total Entre Total Entre Total Entre Final = 0 com um numero: 3 = 3 com um numero: 8 = 11 com um numero: 15 total = 26

Inicialmente, dado o valor 0 varivel total, e o teste verdadeiro (0 < 20). Em cada iterao, o total impresso e o usurio digita um nmero que somado a total. Quanto total for maior ou igual a 20, o teste do while torna-se falso, e a repetio termina. 58

14.2

Estilo de formatao para estruturas de repetio

A regra principal ser consistente. Assim, seu programa ser mais legvel. 14.2.1 Colocao das chaves

H trs estilos comuns de colocar as chaves: while (expressao) { sentenca; } while (expressao) { sentenca; } while (expressao) { sentenca; } APENAS UM DESTES ESTILOS deve ser consistentemente usado para as sentenas for, while e do ... while. Use o estilo com o qual voc se sentir mais confortvel.

14.2.2

Necessidade ou no das chaves

Foi mencionado anteriormente que as chaves ({ e }) podem ser omitidas quando o corpo da repetio contiver apenar uma sentena. Por exemplo: while( i < 5 ) i += 1; Embora as chaves possam ser omitidas, h uma nica razo para coloc-las sempre: while( i < 5 ) { i += 1; } Quando voc adicionar algo ao programa, voc poder adicionar uma sentena para um lao com apenas uma sentena. Se voc zer isso, vital que voc tambm adicione chaves. Se voc no zer isso, a segunda sentena do lao no ser considerada como parte do lao. Por exemplo: while( i < 5 ) i += 1; j += 1; na verdade o mesmo que: while( i < 5 ) i += 1; j += 1; 59

enquanto a inteno era na realidade: while( i < 5 ) { i += 1; j += 1; } 14.2.3 Uso de espao em branco

A outra questo de formato se deve ser colocado um espao em branco depois do while e antes do abre parnteses ((). Por exemplo: while (i 1 --> 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 --> conteudo do array depois da passagem ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 5 2 7 3 8 1 4 6 1 1 1 1 1 1 1 1 5 2 2 2 2 2 2 2 2 5 3 3 3 3 3 3 7 7 7 4 4 4 4 4 3 3 5 5 5 5 5 5 8 8 8 8 8 6 6 6 9 9 9 9 9 9 7 7 4 4 4 7 7 7 9 8 6 6 6 6 6 8 8 9

Note que mesmo que se comessemos com um array ordenado de 9 elementos, ainda assim o algoritmo dado faz 8 passagens sobre o array. 18.7.1 Prottipo da funo e denio

1. Prottipo void ordena(int [], int); 2. Denicao #include #include "imprimePassos.c" /* Uma funcao que encontra o menor valor em um * "a" e "b" (inclusive) */ int menor_indice(int v[], int a, int b) { int i; int menor; menor = a; i = a+1; 86 array entre os indices

while (i 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 --> conteudo do array depois da passagem ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1 9 8 7 6 5 4 3 2 1 1 1 1 1 1 1 2 2 2 2 2 2 2 9 3 3 3 3 3 3 8 9 4 4 4 4 4 7 8 9 5 5 5 5 6 7 8 9 6 6 6 5 6 7 8 9 7 7 4 5 6 7 8 9 8 3 4 5 6 7 8 9

Note que, tambm aqui, mesmo que se comessemos com um array ordenado de 9 elementos, ainda assim o algoritmo dado faz 8 passagens sobre o array. Isto pode ser melhorado da seguinte forma: Antes de comear cada passagem, inicializamos uma varivel ordenado com 1. Se durante a passagem uma troca de valores ocorrer, trocamos o valor da varivel para 0. Assim, se depois da passagem, o valor da varivel continuar sendo 1, isso signica que nenhuma troca ocorreu e que o array est ordenado. 18.8.1 Algoritmo Bubble Sort otimizado

Enquanto o array nao estiver ordenado 1. inicializar ordenado com 1 2. comparar pares adjacentes do array troque seus valores se estiver fora de ordem ordenado = 0.

88

18.8.2

Prottipo da funo e denio

1. Prottipo void ordena(int [], int); 2. Denicao #include #include "imprimePassos.c" /* Uma funcao que ordena um array de inteiros usando o algoritmo de * Bubble sort. * Entrada: array a ser ordenado -- lista[] tamanho do array -- tam * */ void ordena(int lista[], int tam) { int ordenado, /* se 1 depois da passagem o array esta ordenado */ elem_final = 1, /* em uma passagem, elementos do ultimo ate elem_final sao comparados com o elemento anterior */ i,j, temp; /* enquanto o array nao estiver ordenado, fazer uma passagem sobre ele */ ordenado = 0; while (ordenado == 0) { ordenado = 1; /* assume que array esta ordenado */ /* Examina o array do ultimo elemento ate elem_final. Compara cada elemento com o anteior e troca seus valores se estiver fora de ordem. */ imprimePassos(lista, tam, elem_final - 1); i = tam - 1; while (i >= elem_final) { if (lista[i] < lista[i - 1]) /* troca os elementos de i e i-1 */ { temp = lista[i]; lista[i] = lista[i - 1]; lista[i - 1] = temp; ordenado = 0; /* marca como nao ordenado */ } i -= 1; } elem_final++; 89

} }

18.9

Comentrios Finais

Neste curso, um dos nicos lugares que veremos o nome do array sem estar indexado quando passamos o array (como um todo) para uma funo. Para outras nalidades, veremos sempre o array indexado. Por exemplo, o seguinte trecho de programa est errado: int main(void){ int arr1[4] = {10, 20, 30, 40}; int arr2[4]; arr2 = arr1; /* ERRADO: copia arr1 em arr2 */ /* tem que copiar elemento por elemento */

if( arr1 == arr2 ) /* ERRADO: nao podemos comparar arrays inteiros */ printf(X); /* tem que comparar elemento por elemento */ }

90

19

Arrays Multidimensionais

Nas notas de aula anteriores, apresentamos arrays unidimensionais. Em C , possvel tambm denir arrays com 2 ou mais dimenses. Eles so arrays de arrays. Um array de duas dimenses podem ser imaginado como uma matriz (ou uma tabela). Como voc deve ter imaginado, para denir e acessar arrays de dimenses maiores, usamos colchetes adicionais ([ e ]). Por exemplo: int tabela[3][5]; Dene um array bidimensional chamado tabela que uma matriz 3 por 5 de valores do tipo int (15 valores no total). Os ndices da primeira dimenso vo de 0 a 2, e os ndices da segunda dimenso vo de 0 a 4. Abaixo apresentamos um programa que imprime os elementos de um array bidimensional. #include #define ALTURA 5 #define LARGURA 5 int main() { int x; /* numero da coluna */ int y; /* numero da linha */ int matriz [ALTURA] [LARGURA]; /* array 2-D [num_lins, num_cols] */

/* preenche a matriz com zeros y = 0; while(y < ALTURA) { x = 0; while(x < LARGURA) { matriz[y][x] = 0; x+=1; } y+=1; }

*/

/* Imprime a matriz com zeros e a coordenada escolhida com 1 */ printf("\nEntre coordenadas na forma y,x (2,4).\n"); printf("Use valores negativos para sair do programa.\n"); printf("Coordenadas: "); scanf("%d,%d", &y, &x); while (x >= 0 && y >= 0) { matriz[y][x] = 1; /* coloca 1 no elemento escolhido */ 91

y = 0; while (y < ALTURA) /* imprime o array todo */ { x = 0; while (x < LARGURA) { printf("%d ", matriz[y][x] ); x += 1; } printf("\n\n"); y += 1; } printf("\n"); printf("Coordenadas: "); scanf("%d,%d", &y, &x); } } Neste exemplo, matriz um array bidimensional. Ela tem nmero de elementos igual a ALTURAxLARGURA, sendo cada elemento do tipo int. O exemplo abaixo preenche os elementos de um array bidimensional com os valores que representam a taboada e imprime a matriz. ATENO: a partir daqui os exemplos usam a estrutura de controle for. Veja a explicao sobre esta estrutura (uma variao do while()) na Seo 15.1. /* Exemplo de array 2-D - taboada */ #include #define LIN 10 #define COL 10 int main() { int x; int y; int tabela[LIN] [COL]; /* preenche a tabela */ for(y=0; y < LIN; y+=1) for(x=0; x < COL; x+=1) tabela[y][x] = y*x; printf("\n Tabela de Multiplicacao\n");

/* numero da coluna */ /* numero da linha */ /* tabela de taboada */

/* Imprime o numero das colunas */ printf("%6d", 0); 92

for (x=1; x < COL; x+=1) printf("%3d", x); printf("\n"); /* Imprime uma linha horizontal */ printf(" "); for (x=0; x < 3*COL; x+=1) printf("-"); printf("\n"); /* Imprime as linhas da tablea. Cada linha a precedida pelo indice de linha e uma barra vertical */ for (y=0; y < LIN; y+=1) { printf("%2d|", y); for(x=0; x < COL; x+=1) printf("%3d", tabela[y][x]); printf("\n"); } } A sada do programa : Tabela de Multiplicacao 0 1 2 3 4 5 6 7 8 9 -----------------------------0| 0 0 0 0 0 0 0 0 0 0 1| 0 1 2 3 4 5 6 7 8 9 2| 0 2 4 6 8 10 12 14 16 18 3| 0 3 6 9 12 15 18 21 24 27 4| 0 4 8 12 16 20 24 28 32 36 5| 0 5 10 15 20 25 30 35 40 45 6| 0 6 12 18 24 30 36 42 48 54 7| 0 7 14 21 28 35 42 49 56 63 8| 0 8 16 24 32 40 48 56 64 72 9| 0 9 18 27 36 45 54 63 72 81

19.1

Inicializao

Arrays multidimensionais podem ser inicializados usando listas aninhadas de elementos entre chaves. Por exemplo, um array bidimensional tabela com trs linhas e duas colunas pode ser inicializado da seguinte forma: double tabela[3][2] = { {1.0, 0.0}, {-7.8, 1.3}, {6.5, 0.0} }; /* linha 0 */ /* linha 1 */ /* linha 2 */

Quando o array inicializado, o tamanho da primeira dimenso pode ser omitido. A denio de array abaixo equivalente a dada anteriormente. 93

double tabela[][2] = { {1.0, 0.0}, {-7.8, 1.3}, {6.5, 0.0} };

/* linha 0 */ /* linha 1 */ /* linha 2 */

19.2

Arrays Multidimensionais arrays de arrays

O formato da denio de um array de dimenso k, onde o nmero de elementos em cada dimenso n0 , n1 , . . . , nk1 , respectivamente, : nome_tipo nome_array[n0 ][n1 ]...[nk1 ]; Isto dene um array chamado nome_array consistindo de um total de n0 n1 . . . nk1 elementos, sendo cada elemento do tipo nome_tipo. Arrays multidimensionais so armazenados de forma que o ltimo subscrito varia mais rapidamente. Por exemplo, os elementos do array int tabela[2][3]; so armazenados (em endereos consecutivos de memria) como

tabela[0][0], tabela[0][1], tabela[0][2], tabela[1][0], tabela[1][1], tabela[1][2 Um array de dimenso k, onde o nmero de elementos em cada dimenso n0 , n1 , . . . , . . . , nk1 , respectivamente, pode ser imaginado como um array de dimenso n0 cujos elementos so arrays de dimenso k 1. Por exemplo, o array bidimensional tabela, com 20 elementos do tipo int int tabela[4][5] = { {13, {20, {31, {40, 15, 22, 33, 42, 17, 24, 35, 44, 19, 26, 37, 46, 21}, 28}, 39}, 48} };

pode ser imaginado como um array unidimensional de 4 elementos do tipo int[], ou seja, arrays de int; cada um dos 4 elementos um array de 5 elementos do tipo int: tabela[0] tabela[1] tabela[2] tabela[3] ---> ---> ---> ---> {13, {20, {31, {40, 15, 22, 33, 42, 17, 24, 35, 44, 19, 26, 37, 46, 21} 28} 39} 48}

19.3

Arrays Multidimensionais como argumento para funes

Quando o parmetro formal de uma funo um array multidimensional (um array com dimenso maior que um), todas as dimenses deste array, exceto a primeira, precisa ser explicitamente especicada no cabealho e prottipo da funo. tipo_do_resultado nome_da_f uncao ( nome_do_tipo nome_do_array[ ][n1 ]...[nk1 ], ...... ) Quando uma funo com um parmetro formal do tipo array chamada, na chamada da funo somente o nome do array passado como parmetro real. O tipo (e portanto a dimenso) do array passado como parmetro real deve ser consistente com o tipo (e portanto a dimenso) do array que o parmetro formal. O programa abaixo mostra o exemplo da tabela de multiplicao escrita usando funes. 94

/* Exemplo de array 2-D - tabela de multiplicacao */ #include #define LIN 10 #define COL 10 void inicializa_arr (int arr[][COL], int); void imprime_arr (int arr[][COL], int); int main() { int tabela[LIN] [COL]; inicializa_arr(tabela, LIN); printf("\n Tabela de Multiplicacao\n");

imprime_arr(tabela, LIN); } /* Inicializa o array com a tabela de multiplicacao */ void inicializa_arr (int arr[][COL], int nLIN) { int x; /* numero da coluna */ int y; /* numero da linha */ /* preenche o array */ for (y=0; y < nlin; y+=1) for(x=0; x < COL; x+=1) arr[y][x] = y*x; } /* imprime um array LIN x COL */ void imprime_arr(int arr[][COL], int nlin) { int x; /* numero da coluna */ int y; /* numero da linha */ /* imprime o numero das colunas */ printf("%6d", 0); for (x=1; x < COL; x+=1) printf("%3d", x); printf("\n");

/* imprime uma linha horizontal */ 95

printf(" "); for (x=0; x < 3*COL; x+=1) printf("_"); printf("\n"); /* imprime as linhas do array. cada linha e precedida pelo numero da linha e uma barra vertical */ for (y=0; y < nlin; y+=1) { printf("%2d|", y); for(x=0; x < COL; x+=1) printf("%3d", arr[y][x]); printf("\n"); } } Outro exemplo com funoes de manipulao de arrays bidimensionais: /* funcoes com argumentos tipo array 2-D */ #include #define ALTURA 5 #define LARGURA 5 void void void void void v