Funções em C
e breve revisão
Aula 15
Prof. Fábio Miranda Site das aulas: https://sites.google.com/site/fabionmiranda Email/orkut: fabionmiranda@gmail.com
•Gestor, Consultor e Educador em TI há mais de 15 anos • Bacharel em Ciência da Computação •Pós-graduado em Gestão em Tecnologias da Informação
Pós-Graduado em Gestão Educacional
•Mestrando em Ciência da Computação pela UFMG •Professor aprovado PUC, COTEMIG, FAMINAS, PITÁGORAS, POLIMIG,
• Nos programas anteriores, os valores das
variáveis eram estabelecidos em operações de atribuição. Mas agora, qual é o valor de C?
• Uma outra forma de atribuir valores a variáveis é a
leitura de dados. Em C, usa-se a função scanf. • Assim como printf, a função scanf pode ter vários
parâmetros, sendo o primeiro uma string.
• No caso da função scanf, esta string deve conter apenas tags, separadas por espaços em branco. • Os demais parâmetros da função scanf devem ser
endereços de variáveis.
• O que acontece quando o computador executa uma instrução de leitura de dados? Exemplo:
• A execução do programa é interrompida. O
computador espera que o usuário digite algum valor e pressione a tecla Enter.
• Após pressionar Enter, o computador retoma a
execução do programa e armazena o(s) valor(es) digitado(s) no(s) endereço(s) fornecido(s) na
scanf(“%f”,&C);
• O que difere a leitura de dados da operação de atribuição?
– Na operação de atribuição, o valor a ser atribuído é definido antes da execução do programa, enquanto
numa operação de leitura de dados, o valor atribuído é definido durante a execução.
• Em programação, diz-se que coisas são estáticas
quando ocorrem antes do programa executar e
dinâmicas, quando ocorrem durante a execução.
C = 32;
scanf(“%f”,&C)
Valor de C é estabelecido estaticamente. Valor de C é estabelecido dinamicamente.
Leitura de dados
• Na leitura de dados, o valor digitado pelo usuário deve ser do mesmo tipo que a variável.
• Com a leitura de dados, a execução de um programa pode ser realizada para valores diferentes das variáveis.
• Porém, se o valor da variável é estabelecido de forma estática, para cada valor da variável, é necessário compilar o programa novamente.
• Dadas as idades (tipo int) e os pesos (tipo float) de duas pessoas, exibir quem é a pessoa mais velha e a sua idade e quem é a pessoa mais leve e o seu peso.
• Todo comando if requer uma condição que pode ser verdadeira ou falsa.
• Caso a condição seja verdadeira, o comando if
executa um conjunto de instruções, podendo
deixar de executar um outro conjunto alternativo. • Quando existe um conjunto de instruções a ser
executado, caso o valor da condição seja falso, utiliza-se o comando if-else.
• Exemplo:
• Um conjunto de instruções começa com o símbolo
{ e termina com o símbolo }. Caso, o conjunto contenha apenas uma instrução, as chaves são opcionais. if (delta >=0) { x1 = (-b + sqrt(delta))/(2*a); x2 = (-b – sqrt(delta))/(2*a); } else {
printf(“Sem raízes reais.”); }
• Qualquer instrução pode fazer parte de um
conjunto de instruções, inclusive um comando if
ou um comando if-else. if (delta >=0) { x1 = (-b + sqrt(delta))/(2*a); if (delta == 0) x2 = x1; else x2 = (-b – sqrt(delta))/(2*a); } else {
printf(“Sem raízes reais.”); }
Comando
if-else
Por que não foram usadas as chaves { } neste comando?• Programas mais complexos são mais difíceis de ler e compreender.
• Uma forma de melhorar a legibilidade do programa é usar recuos.
• Os recuos devem ser usados sempre após o
símbolo {, sendo as instruções recuadas à direita. • O símbolo } deve estar alinhado ao abre-chaves
correspondente.
• Exemplo:
• De quem é o else acima?
– O compilador sempre associa um else ao “if anterior mais próximo que ainda não possui um else.”
• Como associar o else à instrução if (nota >= 9)?
if (nota >= 9)
if (nota_anterior < nota)
printf(“Você está melhorando.”); else
printf(“Sem estudo é difícil ser aprovado.”);
• Exemplo:
• Neste caso, as chaves, em vez de opcionais, serão obrigatórias, pois apenas os recuos não resolvem.
if (nota >= 9) {
if (nota_anterior < nota)
printf(“Você está melhorando.”); }
else
printf(“Sem estudo é difícil ser aprovado.”);
Funções?
• Funções também são chamados de:
– Subrotinas
– Subprogramas
– Leitura obrigatória: Capítulo 15 – Livro
“Algoritmos e Fundamentos e Praticas” do Everton Coimbra
– Fazer exercicios teóricos da pág. 375 – Trabalhem em equipe!!!!!
• Dadas as idades (tipo int) e os
pesos (tipo float) de duas pessoas, exibir quem é a pessoa mais
velha e a sua
idade e quem é a pessoa mais leve e o seu peso,
usando funções.
Problema 8
Lembrar que, quando se executa um programa, executa-se a função main.
• Observe como o programa principal tornou-se mais conciso e modularizado devido ao uso de
funções.
• Observe a função maiorValor:
int maiorValor(int idade1, int idade2) { if (idade1 > idade2) return idade1; else return idade2; }
Esta função funciona apenas para idades?
Esta função poderá ser usada sempre que for necessário
determinar o maior de dois valores inteiros.
Utilização de funções
• Assim, podemos reescrever a função maiorValor
como:
onde x e y representam valores inteiros genéricos.
• Atenção!
Os nomes dos parâmetros na definição da função não
precisam ser iguais aos nomes dos parâmetros durante o int maiorValor(int x, int y)
{ if (x > y) return x; else return y; }
Utilização de funções
• Como assim? Veja o exemplo abaixo:
• x e y: parâmetros de definição (ou formais)
• a
int a, b, maior;
maior = maiorValor(a,b);
int maiorValor(int x, int y) { if (x > y) return x; else return y; } 4 12 a b 4 12 x y No momento da chamada de maiorValor, cópias de a e b são feitas em x e y.
Utilização de funções
• Dado o valor da variável N, determine a soma dos números inteiros de 1 a N.
• Deseja-se calcular o valor de: 1 + 2 + 3 + ... + N.
• Observação: não sabemos, a priori, quantos termos serão somados, pois o valor de N é estabelecido dinamicamente.
• Para se calcular esta soma, o programa p09.c
utiliza o comando while.
– O comando while permite que um conjunto de
instruções seja executado tantas vezes quantas forem necessárias, enquanto uma condição for verdadeira.
• Quando um programa executa um conjunto de
instruções repetidas vezes, diz-se que o programa está realizando um processamento iterativo.
• Cada execução do conjunto de instruções
denomina-se uma iteração. Exemplo de uso do comando while: s = 0; i = 1; while (i <= 3) { s = s + i; i++; Instante s i (i <= 3) inicial 0 1 V 1ª Iteração 0 + 1 = 1 1 + 1 = 2 V 2ª Iteração 1 + 2 = 3 2 + 1 = 3 V
Comando
while
• A execução do programa p09.c sempre irá
terminar, pois i será maior do que N em algum momento.
• Porém, pode a execução de um programa com processamento iterativo não terminar? Observe:
s = 0; i = 0; while (i < 3) { i--; s = s + i; }
Este laço ou loop
nunca irá terminar!
(Erro de lógica) s = 0; i = 0; while (i > -3) { i--; s = s + i; } Maneira correta
Comando
while
• Atenção!
Em alguns casos, o loop infinito pode ser
desejável. Exemplo: um programa que monitora um reator nuclear deve estar sempre em
execução.
• Neste caso, pode-se escrever:
while (1) {
... }
• Outra forma de repetir um conjunto de instruções é com o comando do-while.
• Veja que no comando while, a condição é testada antes da execução das instruções, ao contrário do comando do-while. O que acontece para N=0?
s = 0; i = 1; do { s = s + i; i++; } while (i <= N); Comando do-while s = 0; i = 1; while (i <= N) { s = s + i; i++; } Comando while
Comando
do-while
• Suponha que soma (+) e subtração (-) são as únicas operações disponíveis em C. Dados dois números inteiros positivos A e B, determine o quociente e o resto da divisão de A por B.
Problema 10
A B
Algoritmos estruturados
• Para resolver o problema 10, precisamos de um
algoritmo:
Seqüência finita de instruções que, ao ser executada, chega a uma solução de um problema.
• Para escrever este algoritmo, podemos usar a seguinte idéia:
1. Representar os números A e B por retângulos de larguras proporcionais aos seus valores;
• Pode-se escrever este algoritmo como:
1. Sejam A e B os valores dados; 2. Atribuir o valor 0 ao quociente (q); 3. Enquanto B couber em A:
{
4. Somar 1 ao valor de q; 5. Subtrair B do valor de A; }
6. Atribuir o valor final de A ao resto (r).
• É conveniente representar algoritmos por meio de
fluxogramas (diagrama de blocos).
• Em um fluxograma, as operações possíveis são representadas por meio de figuras:
Figura Usada para representar
Início ou fim. Atribuição. Condição.
Leitura de dados.
Apresentação de resultados.
Conexão entre partes do algoritmo.
Fluxograma
• Exemplo: o algoritmo para o Problema 10 pode ser representado pelo seguinte fluxograma.
Atenção: observe que um algoritmo não inclui detalhes, tais como declaração de
variáveis, mensagens a serem exibidas, etc. Início A, B q = 0 B <= A r = A q, r q = q + 1 A = A - B Sim Não
Fluxograma
• Um algoritmo tem sempre um único bloco início e deve conter, pelo menos, um bloco fim. A
execução de um algoritmo deve ser feita seguindo-se as setas.
• Em geral, a construção de um algoritmo é uma tarefa mais difícil do que codificar o algoritmo em uma linguagem de programação.
• Porém, a construção de algoritmos pode se beneficiar de técnicas, como a Programação Estruturada.
• Na Programação Estruturada, os programas são construídos usando-se apenas três estruturas:
seqüência, seleção e repetição.
• Cada estrutura tem apenas um único ponto de entrada e um único ponto de saída (círculos).
...
Seqüência Seleção (duas formas)
V F
V F
Repetição (duas formas)
V F
F
V
Programação Estruturada
• Atenção!
– Nestas estruturas, os retângulos são utilizados para indicar qualquer ação, incluindo leitura de dados ou exibição de resultados.
• Construir programas estruturados corresponde a combinar estas estruturas de duas maneiras:
– Regra do empilhamento: o ponto de saída de uma estrutura pode ser conectado ao ponto de entrada de outra estrutura.
– Regra do aninhamento: um retângulo de uma estrutura pode ser substituído por uma outra estrutura qualquer.
• Estas regras podem ser aplicadas quantas vezes forem necessárias e em qualquer ordem.
• Na construção de fluxogramas, pode-se substituir o primeiro ponto de entrada e os últimos pontos de saída por ovais (início e fim).
• Os demais pontos de entrada e saída podem ser removidos.
• Exemplo: aplicação da regra de empilhamento.
• Exemplo: algoritmo do Problema 10.
Empilhamento Simplificação Fluxograma
Início Fim Aninhamento e Simplificação V F V F Início
Programação Estruturada
• Escreva um programa que permita ao usuário escolher dentre as seguintes opções:
– Exibir o conteúdo da pasta; – Modificar a hora do sistema; – Modificar a data do sistema;
– Terminar a execução do programa.
• O programa p12.c mostra diversas possibilidades de uso da função system:
Parâmetro Usado para
CLS Limpar a tela de execução.
DIR Exibir o conteúdo da pasta em uso.
TIME Exibir e permitir modificar a hora atual do sistema. DATE Exibir e permitir modificar a data atual do sistema. PAUSE Interromper a execução do programa.
• Os parâmetros possíveis para a função system
dependem do sistema operacional sob o qual os programas serão executados.
• Para o ambiente Dev-C++, os programas são executados sob o sistema operacional DOS. • Assim, os parâmetros “CLS”, “DIR”, “TIME”,
“DATE”, “PAUSE”, correspodem a comandos do
sistema DOS.
• O programa p12.c apresenta diversos comandos
if-else interrelacionados.
• Exemplo: imagine uma função que recebe como parâmetro um inteiro representando o número de um mês e retorna o número de dias deste mês (considere que fevereiro tem sempre 28 dias).
• Uma outra forma de escrever esta função, mas ainda com comandos if-else interrelacionados é:
• A demanda por comandos if-else interrelacionados
é muito comum em programação.
• Assim, a linguagem C disponibiliza um comando especial para tais situações: switch. A sintaxe
deste comando é a seguinte:
switch (expressão) { case constante-1: comandos-1; case constante-2: comandos-2; ... default: comandos-n;
Comando
switch
• Com o comando switch, a função dias_do_mes
pode ser reescrita como:
• Este comando permite que, de acordo com o valor de uma expressão, seja executado um ou mais
comandos dentre uma série de alternativas. • O caso cuja constante for igual ao valor da
expressão será selecionado para execução.
• Atenção!
– Os comandos associados a este caso e todos os
comandos seguintes serão executados em sequência até o final do comando switch.
– Para evitar a execução de todos os comandos seguintes, usa-se o comando break.
• Como assim?
Para mes = 2:
• Com o uso de break: dias = 28;
Comando
switch
Para mes = 2:
• Sem o uso de break: dias = 28;
dias = 30; dias = 0;
• Em alguns programas, durante um processamento iterativo, pode ser necessário:
– Encerrar o processamento iterativo independentemente do valor da condição do laço;
– Executar apenas parcialmente uma iteração, ou seja, executar somente algumas das instruções do laço da repetição.
• Para encerrar um processamento iterativo,
independentemente do valor da condição do laço, deve-se usar o comando break.
• Exemplo: dados os valores N (int) e A (float), determine a partir de qual termo o valor de:
é maior do que A. Suponha N = 10 e A = 2. N s ... 1 3 1 2 1 1 Instante Valor de s 10 termo 1.000000 20 termo 1.500000 30 termo 1.833333
40 termo 2.083333 A partir do quarto termo s > A.
• Um programa para resolver este problema pode ser escrito como:
• Neste caso, a condição do laço controla apenas o número de termos do somatório.
• O laço pode ser encerrado quando s > A, usando-se o comando break.
• Para executar somente algumas das instruções do
laço, mas sem encerrar a repetição: comando continue. • Exemplo: ler a idade e o peso de N pessoas e
determinar a soma dos pesos das pessoas com mais de 30 anos.
Comandos
break
e
continue
O comando continue faz com que a instrução
s = s + peso não seja executada quando idade <= 30.
Ou seja, ele volta a execução para o início do laço.
• Resultado da execução:
• Peso total = 35 + 40 + 48 + 60 + 85 = 193
• Um lojista atribui o preço de venda dos itens de sua loja com um número racional (uma fração de inteiros).
• Este comerciante precisa de uma calculadora capaz de realizar as operações de soma, subtração,
multiplicação e divisão de frações, a qual exibe o
resultado como uma fração na forma mais simplificada possível. Desenvolva um programa que implemente esta calculadora.
Análise do programa
Declaração de variáveis aqui?
• Nos programas anteriores, a função principal (main) aparecia sempre como a última função
declarada. Por quê?
• A Linguagem C exige que todos os identificadores
(nomes de variáveis e de funções) sejam
declarados antes de serem usados.
• Assim, sendo a última função declarada, a função
main pode chamar (ou usar) qualquer outra função do programa, pois as outras funções já teriam sido declaradas antes.
• Quando uma função é chamada, o compilador C verifica na declaração da função se o número e os tipos dos parâmetros estão sendo respeitados.
• Mas, e se a função principal (main) for declarada em primeiro lugar? Neste caso, o compilador C ainda
não “conhece” as declarações das demais funções. • Assim, como fazer para que main possa chamar
outras funções do programa? Como o compilador verificará a compatibilidade de tipos?
• A solução é usar protótipos de funções. Abaixo, são listados os protótipos das funções usadas pela
função main, no programa p20.c:
• O protótipo de uma função corresponde apenas ao
cabeçalho da função, seguido de ponto-e-vírgula.
...
• Observe que o programa p20.c inclui também a
função mdc e que não existe um protótipo para esta função. Por quê?
• Porque mdc é chamada apenas pela função
simplificar_fracao e a definição da função mdc
...
• Observe a declaração das variáveis a, b, c e d.
• Diferentemente do que vínhamos aprendendo, elas aparecem fora da definição de qualquer função
usada pelo programa. Qual é a repercussão disso?
...
• Na Linguagem C, as variáveis podem ser
declaradas em diversas partes de um programa. • A região dentro do programa onde o nome de uma
variável é visível (ou tem significado) é conhecida como escopo da variável.
• A Linguagem C define três categorias de escopo: – Bloco;
– Parâmetro de função; – Arquivo.
• Bloco é uma região do programa delimitada por chaves { e }.
• O exemplo comum de bloco é o corpo de uma função. Por exemplo:
Escopo de bloco
Normalmente, as variáveis são
declaradas no início do bloco.
• Uma variável com escopo de bloco é denominada
variável local ao bloco.
• Uma variável local tem visibilidade apenas dentro do bloco em que foi declarada.
• Mas, mesmo dentro do bloco, a variável local pode não ser visível, caso exista um bloco mais interno que declara uma outra variável de mesmo nome.
• Declarar variáveis no início do bloco não é
obrigatório, desde que a regra “definir antes e usar depois” seja atendida.
r pode ser usada dentro
r só pode ser usada dentro do while.
• Atenção! Para identificar uma variável, o compilador C leva em conta não apenas o nome da variável,
mas também seu escopo. Veja:
O que esta função irá exibir? O valor interno e 1 O valor externo e 100 Variáveis diferentes!
• O escopo de parâmetro de função existe na declaração de protótipos ou no cabeçalho de definição de funções.
• Variáveis declaradas em um protótipo de função têm significado apenas dentro do próprio protótipo. • Assim, no protótipo de uma função pode-se
declarar apenas os tipos dos parâmetros:
• Variáveis declaradas como parâmetros no cabeçalho de uma função são consideradas
variáveis locais à função.
Variáveis locais à função simplifica
• Variáveis declaradas fora de qualquer função do programa têm escopo de arquivo.
• Uma variável com escopo de arquivo tem
significado no restante do programa, a partir da linha em que foi declarada.
• Por isso, uma variável com escopo de arquivo é denominada variável global.
• Por exemplo, no programa p20.c, a declaração das variáveis a, b, c e d aparece no início do programa e, portanto, tais variáveis podem ser usadas em todas as funções.
...
• Atenção! A declaração de variáveis globais pode ser feita em qualquer parte do programa.
Não! A variável i só tem
• Para resolvermos este problema, basta declararmos a variável i antes da função func:
Em func: a[0] = 1
• Uma variável global também pode perder visibilidade em algumas funções, caso haja declaração de variável local de mesmo nome.
Variáveis distintas
Em func: a[2] = 3
• O uso de variáveis globais torna mais difícil a análise e compreensão do programa.
• Por quê?
Para se corrigir um erro de lógica relacionado com uma determinada variável global, é necessário
analisar todo o programa ou todo o escopo da variável global (que pode ser muito grande). • Conclusão: deve-se evitar o uso de variáveis
globais! As funções devem modificar apenas suas variáveis locais e as variáveis passadas como
parâmetros.
• Toda função define um processamento a ser realizado.
• Este processamento depende dos valores dos parâmetros da função.
• Assim, para usar uma função, um programa precisa fornecer a ela os parâmetros adequados. Exemplo:
– Para calcular o seno de 30º, escrevemos: sin(pi/6);
– Para calcular o valor absoluto de a-b, escrevemos: abs(a-b);
– Para calcular o mdc de 12 e 8, escrevemos: mdc(12,8);
• O mecanismo de informar os valores a serem
processados pela função chama-se passagem de parâmetros.
• A Linguagem C define duas categorias de passagem de parâmetros: passagem por valor e passagem por endereço (ou passagem por referência).
• Normalmente, a passagem de parâmetros a uma função é por valor.
• Mas, como os parâmetros de uma função são variáveis locais, alguns aspectos devem ser
• Considere o exemplo abaixo:
• O que este programa irá exibir?
Valores recebidos ... 1, 2 e 3 Valores alterados ... 2, 3 e 4 Valores finais ... 1, 2 e 3
Passagem por valor
• Observe que os valores das variáveis a, b e c não foram modificados na função alterar. Por quê?
• O tipo de passagem de parâmetros utilizado é por valor. Ou seja, são feitas apenas cópias dos valores das variáveis a, b, e c nas variáveis x, y e z.
1 2 a b
Escopo: função main
1 2 x
y z
Escopo: função alterar
2 3 4 x++ y++ z++ Apenas os conteúdos de x, y e z são alterados.