7 Execução condicional com if
A maioria dos programas requer que alguns comandos apenas sejam executados em algumas condições e isso é chamado, naturalmente, de execução condicional.
A principal estrutura na linguagem C para determinar se determinados comandos são ou não executados é o if
. Esta estrutura é o assunto deste capítulo.
7.1 A estrutura condicional if
Para introduzir a estrutura condicional é apresentado um programa que implementa o Algoritmo 7.1.
Algoritmo 7.1: Apresentação de dois valores reais em ordem não decrescente.
A codificação em C deste algoritmo pode ser feita como se segue.
/*
Apresentação de dois valores em ordem não decrescente
Requer: dois valores reais v1 e v2
Assegura: v1 <= v2
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite dois valores reais: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble v1, v2;
(entrada, "%lf%lf", &v1, &v2);
sscanf
if(v2 < v1) {
double temporario = v1;
= v2;
v1 = temporario;
v2 }
("Valores em ordem não decrescente: %g e %g.\n", v1, v2);
printf
return 0;
}
4.5 1.3
Digite dois valores reais: Valores em ordem não decrescente: 1.3 e 4.5.
A estratégia da implementação é a mesma do algoritmo, que opta por fazer a troca dos valores (e isso requer o uso de uma variável temporária auxiliar). As três instruções que realizam a troca somente são executadas quando v2
for menor que v1
e ignoradas se isso não for verdade
É importante ressaltar que as chaves agrupam as três atribuições do if
, de forma que todos os comandos ficam condicionados. O uso das chaves para agrupar comandos cria um comando composto, o qual pode ser usado no lugar de qualquer comando simples.
Em C, a estrutura básica do if
se estabelece conforme destacado na sequência.
A condição é uma expressão que deve resultar em verdadeiro ou falso. Somente se a condição for verdadeira comando é executado. O comando, por sua vez, pode ser tanto um comando simples (terminado com ponto e vírgula) ou um comando composto (usando chaves).
Seguem dois exemplos válidos de estruturas if
: a primeira usa um comando simples e, na segunda, um comando composto contendo apenas um comando simples. É interessante observar que o ponto e vírgula ocorre apenas no final do comando simples, enquanto comandos compostos não usam esse terminador depois do fecha chaves.
if(valor < 0)
= -valor; // torna positivo valor
if(valor < 0) {
= -valor; // torna positivo
valor }
7.2 if
+ else
A cláusula else
pode ser adicionada ao if
para indicar uma ação a ser feita caso a condição de teste seja avaliada como falsa.
Como exemplo, considere o problema de determinar o peso (massa) ideal de uma pessoa baseada em seu sexo biológico. Para o sexo feminino, o peso em quilogramas é dado pela expressão 62,1\(h\) - 44,7, sendo \(h\) a altura da pessoa em metros; para o masculino, o cálculo é 72,7\(h\) - 58. Seguem a solução algorítmica e uma implementação em C para esse problema.
Algoritmo 7.2: Estimativa do peso ideal conforme sexo biológico e altura.
/*
Cálculo estimado da massa ideal de uma pessoa baseada em seu sexo biológico
e sua altura
Requer: o sexo biológico (masculino ou feminino) e a altura em metros
Assegura: a massa ideal da pessoa apresentada (kg)
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite o sexo (M ou F) e a altura em quilogramas: ");
printf(entrada, sizeof entrada, stdin);
fgetschar sexo;
double altura;
(entrada, "%c%lf", &sexo, &altura);
sscanf
double massa_ideal;
if(sexo == 'F')
= 62.1 * altura - 44.7; // feminino
massa_ideal else
= 72.7 * altura - 58; // masculino
massa_ideal
("Massa ideal: %.1fkg.", massa_ideal);
printf
return 0;
}
F 1.68
Digite o sexo (M ou F) e a altura em quilogramas: Massa ideal: 59.6kg.
Neste há dois caminhos possíveis: o cálculo como sexo biológico feminino ou masculino. Pela especificação do problema, apenas M
ou F
são entradas válidas, o que permitiu associar ao else
, inequivocamente, o valor M
. Em C, vale o destaque que =
é usado para a atribuição, sendo que a comparação de igualdade é feita com ==
.
Em relação ao if
, tanto no caso verdadeiro quanto no falso foram inseridos comandos simples.
Como exemplo adicional, um algoritmo para classificar um triângulo como equilátero, escaleno ou isósceles a partir do comprimento de seus lados é apresentado no Algoritmo 7.3.
Algoritmo 7.3: Classificação de um triângulo em relação aos comprimentos de seus lados
Em C, a codificação pode ser expressa na forma seguinte.
#include <stdio.h>
/*
Classificação de um triângulo em relação aos comprimentos de seus lados
Requer: Os comprimentos dos lados de um triângulo válido
Assegura: uma classificação em equilátero, escaleno ou isósceles
*/
int main(void) {
char entrada[160];
("Digites os comprimentos dos lados de um triângulo: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble lado1, lado2, lado3;
(entrada, "%lf%lf%lf", &lado1, &lado2, &lado3);
sscanf
char *classificacao;
if(lado1 == lado2 && lado2 == lado3)
= "equilátero";
classificacao else if(lado1 == lado2 || lado2 == lado3 || lado1 == lado3)
= "escaleno";
classificacao else
= "isósceles";
classificacao
("O triângulo é %s.\n", classificacao);
printf
return 0;
}
9.1 4.3 9.1
Digites os comprimentos dos lados de um triângulo: O triângulo é escaleno.
O programa usa comandos simples para as atribuições, o que dispensa os comandos compostos com as chaves. Na lógica da solução inicialmente verificada a hipótese dos três lados iguais (equilátero). Caso essa verificação dê falso, então há pelo menos um lado diferente e isso leva ao segundo if
, que verifica a hipótese de haver algum par de lados de mesmo comprimento.
Este programa, além do if
e algumas operações lógicas com e e ou, também usa uma variável classificacao
declarada como do tipo char *
. Quando são feitas atribuições como as usadas no exemplo a esse tipo de variável, é feita apenas uma referência à constante e não uma atribuição convencional, na qual uma cópia do texto é guardada na variável. A manipulação de cadeias de caracteres em C é, para se bem dizer, chata e difícil. Como ela não é tão natural, acaba exigindo conhecimento de outros elementos da linguagem. O Capítulo 16 aborda esse tema com mais detalhes.
Nas comparações com valores double
resultantes de expressões aritméticas, nunca se deve verificar pela igualdade. Os valores reais são armazenados com precisão limitada. É conveniente o uso uma tolerância nessas comparações.
/*
Ilustração do problema de precisão nas operações com double
*/
#include <stdio.h>
#include <math.h>
int main(void) {
double a = 40.96;
double b = 6.4 * 6.4; // 40.96
// Opção ruim
if(a == b)
("%g == %g.\n", a, b);
printfelse {
("%g != %g. :-(\n", a, b);
printf("Diferença: %g.\n\n", a - b);
printf}
// Opção melhor, contornando o problema
if(fabs(a - b) < 1e-5) // cinco casas decimais...
("%g == %g. (o suficiente!)\n", a, b);
printfelse {
("%g != %g. :-(\n", a, b);
printf("Diferença: %g.\n\n", a - b);
printf}
return 0;
}
40.96 != 40.96. :-(
Diferença: -7.10543e-15.
40.96 == 40.96. (o suficiente!)
Neste programa, se a diferença for menor que 0,00001 (segundo if
), então os valores são considerados iguais. A tolerância, é claro, depende do contexto do problema.
Como um último exemplo, segue o algoritmo para o cálculo das raízes reais de uma equação de segundo grau \({ax^2 + bx + c = 0}\) dados seus coeficientes (Algoritmo 1.1, apresentado no Capítulo 1).
Sua codificação em C pode ser apresentada conforme se segue.
/*
Cálculo e apresentação das raízes reais de uma equação de segundo grau na
forma ax^2 + bx + c = 0
Requer: Os coeficientes a, b e c da equação
Assegura: as raízes reais da equação; ou mensagem que a equação é
inválida; ou mensagem que não há raízes reais
*/
#include <stdio.h>
#include <math.h>
int main(void) {
char entrada[160];
("Digite os valores de a, b e c da equação: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble a, b, c;
(entrada, "%lf%lf%lf", &a, &b, &c);
sscanf
if(a == 0)
("Não é equação do segundo grau (a = 0).\n");
printfelse {
double discriminante = pow(b, 2) - 4 * a * c;
if(discriminante < 0)
("A equação não possui raízes reais.\n");
printfelse if(discriminante < 1.0e-10) { // tolerância: < 1.0e-10 é "zero"
// Uma raiz real
double x = -b / (2 * a);
("Uma raiz: %g.\n", x);
printf}
else {
// Duas raízes reais
double x1 = (-b - sqrt(discriminante)) / (2 * a);
double x2 = (-b + sqrt(discriminante)) / (2 * a);
("Raízes reais: %g e %g.\n", x1, x2);
printf}
}
return 0;
}
1 -5.9 7.14
Digite os valores de a, b e c da equação: Raízes reais: 1.7 e 4.2.
7.3 De quem é esse else
?
Um ponto relevante quando se usa if
dentro de if
é a quem pertence um determinado else
. Para considerar esse problema é apresentado um código em C.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 5;
if(k > 10)
if (k > 20)
= 10;
i else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
i = 0 e j = 0.
A intenção do código seria fazer j = 10
para valores de k
menores ou iguais a 10. Porém, apesar da organização visual sugerir que o else pertence ao if(k > 10)
, ele se liga ao if(k > 20)
. Assim, independentemente da disposição dos comandos no codigo fonte, o programa será sempre interpretado como se segue. E, neste caso, o primeiro if
não possui else
.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 5;
if(k > 10)
if (k > 20)
= 10;
i else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
O else
sempre se ligará ao if
mais próximo da sequência das instruções. Para resolver esse problema do exemplo, é preciso isolar o if
mais interno, colocando-o em um bloco, formando um comando composto.
O resultado é apresentado na sequência.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 5;
if(k > 10) {
if (k > 20)
= 10;
i }
else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
i = 0 e j = 10.
É ainda interessante notar que, caso o if(k > 20)
tivesse seu próprio else
, a ambiguidade desapareceria e o comando composto seria desnecessário.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 15;
if(k > 10)
if (k > 20)
= 10;
i else
= 20;
i else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
i = 20 e j = 0.
A manutenção da organização visual do código com as indentações corretas ajuda na identificação dos erros.