Gabarito de Lista de Exercícios
#define max(A,B) ((A>B) ? (A):(B))
#define min(A,B) ((A<B) ? (A):(B))
#define sqr(x) x*x
void main()
{
int a = 10 ,b = 50, minimo, maximo, quad;
int z = 5;
minimo = min(a,b);
maximo = max(a++,b++);
quad = sqr(z+1);
}
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)) é tranquilo, 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 variaveis 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 maximo, 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 !!! */
Nota-se claramente que o erro é discrepante. Para resolver este
problema e preciso redefinir a macro, usando parênteses, como segue:
#define quad(x) (x)*(x)
- Ao se trocar os tipo 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 redrobado cuidado. 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.
Exercício 2:
O que faz a macro a seguir?
# ifdef PRINT
# define WRITE(X) printf X
# else
# define WRITE(X)
# endif
Como ela poderia ser utilizada?
Solução:
- Tudo depende de a expressão PRINT ter sido ou não
definida anteriormente. Se PRINT foi definido, a expressão
WRITE(X) vai ser definida como sendo
printf X. Se PRINT não foi definida, a expressão
WRITE(X) vai ser definida como nada (definição vazia),
e todas as vezes que aparecer no programa, será substituída
por nada.
Isto pode ser útil se você quer rodar um programa, numa
primeira instância, mostrando os valores das variáveis em
vários pontos, com printf's (uma espécie de
debugger, na unha..). Você escreve os WRITE(x) e define
o PRINT. Compila e roda, vendo todos os resultados. Depois, para
rodar o programa sem o debugger, você simplesmente comenta a linha
onde PRINT é definido. Daí, o seu programa não
apresenta mais resultados na tela.