ASPER
UNIBRATEC

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.