Gabarito da Lista 5
Aula 8: Funções, Constantes e Macros
Exercício 1:
Enunciado:
Escreva a função EDivisivel(int a, int b). A função
deverá retornar 1 se o resto
da divisão de a por b for zero. Caso contrário, a função
deverá retornar zero.
Solução:
int EDivisivel(int a, int b)
{
if (a%b) /* Se houver resto, nao e' divisivel */
return 0;
else / * Nao havendo resto, e' divisivel */
return 1;
}
Exercício 2:
Enunciado:
Escreva um programa que faça uso da função EDivisivel(int
a, int b), criada na
página c720.html.
Organize o seu programa em três arquivos: o arquivo prog.c , conterá
o programa
principal; o arquivo
func.c conterá a função; o arquivo func.h conterá
o protótipo da função. Compile
os arquivos e gere o
executável a partir deles.
Solução:
a) conteúdo do arquivo func.c
int EDivisivel(int a, int b)
{
if (a%b) /* Se houver resto, nao e' divisivel */
return 0;
else / * Nao havendo resto, e' divisivel */
return 1;
}
b) conteúdo do arquivo func.h
int EDivisivel(int a, int b);
c) conteúdo do arquivo prog.c
#include <stdio.h>
#include "func.h"
void main()
{
int a, b;
printf (" Entre com dois números inteiros para testar se o primeiro
e'
divisivel pelo segundo: ");
scanf("%d %d", &a, &b);
if(Edivisivel(a,b))
printf("O primeiro numero e' divisivel pelo segundo");
else
printf("O primeiro numero nao e' divisivel pelo segundo");
}
Obviamente, para que este programa possa ser compilado e executado, você
deverá montar um projeto no seu ambiente de compilação.
No DevC++ isto é feito
da seguinte forma:
1 - File -> New Project;
2 - Escolha as opcoes Console Application e C Project -> OK;
3 - Escolha um nome para o seu projeto, algo como MeuProjeto;
4 - Escolha a pasta onde voce armazenara o seu projeto (pode ser a mesma onde
voce esta armazenando seus arquivos .c).
5 - Remova o arquivo fonte Untitled1 do seu projeto (o Dev acrescenta este
arquivo por default ao projeto). Para fazer isto, va na janela da esquerda,
clique sobre Untitled1 com o botao direito do mouse e escolha a opcao Remove
from Project;
6 - Adicione os seus arquivos .c ao projeto: clique sobre o simbolo do seu
projeto na janela da esquerda usando o botao direito do mouse e escolha o
arquivo com seu programa principal . Faca isto novamente, adicionando o arquivo
funcao.c . Nao e' necessario adicionar func.h ao projeto, pois ele ja e'
adicionado com o #include "func.h"
7 - Agora e' so compilar, linkar e executar: Compile Project e Run Project .
Exercício 3:
Enunciado:
Estude o seguinte programa e aponte o valor de cada variável sempre que
solicitado:
Solução:
#include <stdio.h>
int num;
int func(int a, int b)
{
/* a = 0 */
a = (a+b)/2;
/* a = 25 e num = 10 */
num -= a;
/* a = 25 e num = -15 */
return a;
}
int main()
{
int first = 0, sec = 50;
num = 10;
/* first = 0
sec = 50
num = 10 */
num += func(first, sec); /* LINHA ### num = -15 + 25 ok? */
/* first = 0
sec = 50
num = 10 */
printf("\n\nConfira!! num = %d\tfirst = %d\t sec = %d\n",num, first,
sec);
return 0;
}
Comentários: no início do programa first=0, sec=50,
num=10. Ao se chegar na
linha marcada com ###, a função func deve ser avaliada. Dentro
de func, num tem
seu valor alterado para -15 e esta alteração se reflete no programa
principal
pelo fato de num ser uma variável global. O retorno de func é
igual a 25 e,
assim, ao retornar de func, teremos o operador += avaliado e o resultado,
armazenado em num sera' 10. Este programa mostra claramente que o comportamento
de variáveis globais é difícial de ser acompanhado, pois
estas variáveis podem
estar sendo modificadas dentro de funções. Para saber se a variável
é
modificada ou não devemos seguir o código da função
chamada linha a linha!
Exercício 4:
Enunciado:
Escreva um programa que leia um vetor de inteiros pelo teclado e o apresente
na
tela. Crie uma função(void levetor(int *vet, int dimensao)) para
fazer a leitura do vetor.
Solução:
#include <stdio.h>
#define MAX 10 /* dimensão do vetor */
void leVetor(int vet[], int dimensao); /* protótipo de leVetor */
int main()
{
int i;
int vet[MAX];
leVetor(vet, MAX); /* Para passar um vetor como parametro e' necessario
apenas passar o endereco de seu primeiro elemento */
printf("Os numeros lidos foram \n");
for (i=0; i<MAX; i++) printf("%d ", vet[i]);
return 0;
}
void leVetor(int vet[], /* Endereco de inicio do vetor */
int dimensao ) /* tamanho do vetor */
{
int i;
printf("Digite os %d valores a serem armazenados no vetor ", dimensao);
for (i=0 ; i < dimensao ; i++) scanf("%d",&vet[i]);
}
Exercício 5:
Enunciado:
Escreva uma macro que retorne 1 se o seu argumento for um número ímpar
e 0 se
for um número par.
Solução:
#define EHPAR(x) ((x)%2) ? 1 : 0
Comentários: repare o uso dos parênteses, que permite que expressões
do tipo
EPAR(a+b) sejam possíveis.
Exercício 6
Enunciado:
Escreva uma macro que receba dois parâmetros inteiros como entrada, e que resulto no maior valor. Ex: MAIOR(10,3) == 10
Solução:
#define MAIOR(x,y) ((x)>(Y)) ? (x) : (y)
Comentários: repare o uso dos parênteses, que permite que expressões do tipo
EPAR(a+b) sejam possíveis.
Exercicios adicionais:
Exercício 1:
Faça um programa que utilize os argumentos argv e argc. O programa deverá
receber dois números e apresentar a soma dos dois. Veja que para isto
você
deverá ter também uma função que transforme uma
string em um inteiro, pois o
tipo de argv é char; logo você irá receber strings e deverá
transformá-las em
inteiros antes de somá-las.
Solução:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int numa, numb;
if (argc !=3)
printf(" Para usar, voce tem que passar dois parametros \n");
else
{
if ( ((numa = atoi(argv[1])) == 0) ||
((numb = atoi(argv[2])) == 0) )
printf("\n\nArgumentos invalidos!! \n");
else
printf("\n\nA soma de %d e %d eh %d\n\n",numa, numb, numa+numb);
}
return 0;
}
Comentários:
Para a transformação dos números que estavam em argv[]
para int, usou-se a
função atoi() da biblioteca stdlib.h, que recebe um argumento
do tipo char * e
retorna o int correspondente, caso a string represente um número. Caso
o valor
retornado por atoi seja nulo, indica que o argumento passado foi inválido.
Exercício 2: As séries de Fibonacci
Um problema tradicional é o de encontrar o enésimo termo da série
de Fibonacci.
As series de Fibonacci são de grande importância matemática,
e a lei básica é
que a partir do terceiro termo, todos os termos são a soma dos dois últimos.
Os
primeiros termos da seqüência são: 1, 1, 2, 3, 5, 8, 13, 21,
34... O primeiro
e o segundo termos são 1. O terceiro termo é 2 (1+1). O quarto
termo é 3 (1+2).
O quinto termo é 5 (2+3) ... Faça uma função que
encontre o enésimo termo da
seqüência de Fibonacci. Use recursividade.
Solução:
#include <stdio.h>
int fib(int n)
{
if (n>2) return fib(n-1)+fib(n-2);
else return 1;
}
int main()
{
int n, fibon;
printf("\n\nEntre com um numero: ");
scanf("%d", &n);
fibon = fib(n);
printf("\nO termo %d da serie de Fibonacci e: %d\n", n, fibon);
return 0;
}
Exercício 3:
Verifique o programa abaixo e tente identificar problemas que irão ocorrer
em
tempo de execução. (Você pode usar um debugger, ou colocar
printf's para tentar
'ver' os valores das variáveis).
#define max(A,B) ((A>B) ? (A):(B))
#define min(A,B) ((A<B) ? (A):(B))
#define sqr(x) x*x
int main()
{
int a = 10 ,b = 50, minimo, maximo, quad;
int z = 5;
minimo = min(a,b);
maximo = max(a++,b++);
quad = sqr(z+1);
return 0;
}
Verifique o que acontece também, se mudarmos a linha de declarações
do programa
int a = 10 ,b = 50, minimo, maximo, quad;
por
float a = 10 ,b = 50, minimo, maximo, quad;
Solução:
- a primeira linha, onde se calcula o mínimo de a e b (min(a,b)) está
OK, tudo
funciona normalmente. A linha
minimo = min(a,b);
é substituída por
minimo = ((a<b) ? (a):(b));
que irá retornar o mínimo entre a e b, corretamente.
- a segunda linha,
maximo = max(a++,b++);
será substituída por
maximo = ((a++>b++) ? (a++):(b++));
Em virtude disto, em tempo de execução, o programa vai incrementar
as duas
variáveis na hora do teste, o que pode ser indesejado. Mas o pior de
tudo é que
o maior valor será incrementado duas vezes. Veja bem: se a = 10 e b =
50, quando
o programa executa o teste (a++>b++)? ele incrementa a e b. Quando ele retorna
o
valor de b, ele atribui 51 a máximo, mas incrementa em seguida b mais
uma vez
(lembre da expressão: ((a++>b++) ? (a++):(b++)). No final da linha,
b=52. Isto é
indesejado.
- a terceira linha:
quad = sqr(z+1);
será substituída por
quad = z+1*z+1;
em tempo de compilação, segundo a definição da macro
(vide o define). Para z=5,
teremos
quad = 5+1*5+1; /* = 11 & != 36 !!! */
Para resolver este problema é preciso redefinir a macro, usando parênteses,
como
segue:
#define quad(x) (x)*(x)
- Ao se trocar os tipos da variável, o resultado é muito interessante:
As três
macros continuam funcionando perfeitamente! Se tivéssemos uma função
para
realizar cada uma das operações (min, max e sqr), seriam definidos
tipos de
retorno para cada função. Assim, se quiséssemos fazer as
mesmas operações para
diferentes tipos de dados (int, float, double, char), deveríamos escrever
uma
função para cada tipo, ou uma função com um tipo
mais geral (tipo float), e
pagar o preço de conversão de tipo a cada chamada. Com o uso de
macros, isto se
torna desnecessário.
Comentários:
É bem verdade que não se pode fazer tudo com macros. Em muitas
vezes, inclusive,
seu uso pode até ser meio perigoso, e deve-se tomar cuidado redobrado.
Mas em
casos simples como os apresentados aqui, as macros podem ser uma boa alternativa
para resolver problemas menos complexos, sem excessiva geração
de códigos.
|