9 Comandos condicionais Um comando condicional é uma instrução empregada quando se deseja criar um desvio, isto é, a opção de executar-se ou não um determinado trecho de código, segundo uma condição. Em C, há três tipos de comandos condicionais: o if, o switch e o operador?. 9.1 Comando condicional IF O comando condicional if apresenta uma estrutura bem simples, onde uma condição é apresentada e, se ela for verdadeira, uma instrução (ou bloco de código) associada ao if será executada. Além disso, é possível incluir a palavra-reservada else e uma instrução (ou bloco de código) associada a esta tal que seja executada caso a condição seja falsa. Caso não haja um else associado ao if, então nenhuma instrução será executada e o programa continuará a partir da próxima instrução após o comando if. Observemos com mais calma, então, a estrutura do if simples e do if composto. 9.1.1 IF Simples (sem else ) Um if simples não possui else, logo somente possui a instrução a ser executada caso a condição seja verdadeira. Abaixo, a implementação desse comando em Portugol e em C: Em Portugol SE <condição> ENTÃO <comando> Em C if (<condição>) <comando>;
9.1.2 If Composto Um if composto possui else, logo caso a condição expressa seja verdadeira, executará a instrução associada ao if, caso contrário, executará aquela associada ao else. Abaixo, a implementação desse comando em Portugol e em C: Em Portugol SE <condição> ENTÃO SENÃO <comando1> <comando2> Em C if (<condição>) else <comando1>; <comando2>; 9.1.3 Comandos condicionais aninhados Podemos ter comandos condicionais aninhados, isto é, um comando condicional dentro de outro comando condicional. Abaixo segue um exemplo em que se condicao1 for verdadeira então o programa deverá executar a instrução associada a esse primeiro if, que se trata de um comando condicional e portanto somente se a condicao2 também for verdadeira ter-se-á a execução de comando1: if (condicao1) if (condicao2) comando1; Especificamente neste caso, podemos obter os mesmos resultados empregando um único if porém utilizando a conjunção de ambas as condições como sendo condição necessária para a execução do comando1, ou seja: if ( (condicao1) && (condicao2) ) comando1; Vale lembrar que tal equivalência é possível quando o único comando associado a um if é um outro if! Caso se tratasse de um bloco de código contendo um if e outra instrução, já não seria possível fazer tal simplificação do código. 9.1.4 Problema dos Ifs compostos aninhados: Dado o comando: if (b1) if (b2) s1; else s2; Pergunto: O else pertence a qual if? Por quê?
Na maioria das linguagens, a associação é feita sempre entre os pares mais próximos, logo poderíamos entender a instrução acima como sendo: if (b1) if (b2) else s1; s2; Mas, o que acontece se o que nós queremos realmente é que o else pertença ao primeiro if e não ao segundo? Nós podemos usar blocos de código ({ e }) a fim de tornar mais claro qual else pertence a qual if: if (b1) { } else if (b2) s2; s1; Perceba que usando { e } eu alterei facilmente a posse do else. Ficou também claro agora a quem o else pertence (neste caso, ao primeiro if). Lembrando que blocos de código podem ser usados em qualquer lugar de um código que aceite um comando, a fim de melhorar a legibilidade ou para incluir mais comandos onde só poderíamos escrever um. 9.2 Comando condicional SWITCH É importante salientar que o comando condicional if não é nossa única opção quando necessitamos criar um desvio em nosso código (isto é, selecionar se um trecho de código deve ou não ser executado). A segunda opção que estudaremos em C é o switch, que se trata de um comando que se utiliza de uma expressão (seletor) e de uma lista de comandos associados a rótulos (vamos chamar de rótulos case) para determinar quais comandos devem ser executados. Entretanto, para que isso tudo funcione, há uma condição necessária: tanto o seletor quanto os rótulos case devem assumir o mesmo tipo escalar ordinal. Em outras palavras, podemos ter como expressões seletoras e rótulos valores dos tipos char, int e suas modificações, mas não podemos ter void, float ou arrays (como uma string, por exemplo). Vamos ver agora como seria um comando de switch na linguagem C: switch (<expressao>) {
} case <rotulo1>: <comando1>; case <rotulo2>: <comando2>; case <rotulo3>: <comando3>;... default: <comando> [; <comando>]... Esse comando será executado da seguinte forma: primeiro, o programa identifica um switch e ele sabe, então, que deverá executar somente os comandos associados a um rótulo case que tenha o mesmo valor de <expressao>. Ele então avalia o valor de <expressao> (essa expressão pode ser somente uma variável ou uma expressão mais complexa, mas deve obrigatoriamente retornar um valor do tipo escalar ordinal!). Agora, ele verificará cada um dos valores presentes em <rotulon> até encontrar aquele que seja igual ao valor avaliado. Se ele encontrar algum rótulo case em comum, ele executará o comando associado ao mesmo (e todos os comandos seguintes, até encontrar um break!), caso contrário, se houver uma cláusula default, os comandos associados a esta cláusula serão executados. Perceba que a cláusula default em um switch possui papel similar ao da cláusula else em um if, indicando comandos a serem executados quanto todas as condições anteriores falharem. Algo muito importante a ser considerado (principalmente por aqueles que já conhecem outras linguagens de programação, como Pascal, e migram para a linguagem C) é como o programa se comporta uma vez que encontre um rótulo case que satisfaz sua condição: ele não executará somente os comandos associados a este rótulo, serão executados os comandos associados a este E todos os seguintes até que encontre o fim do switch OU que um comando break seja encontrado. O comando break possui vários papeis, sempre atuando como um comando de interrupção. Em um switch, então, ele interrompe a execução de instruções, ordenando ao programa continuar a partir da próxima instrução após o switch. Vejamos um exemplo para que possamos entender melhor o funcionamento de um switch: int a = 2; switch ( a ) { } case 0: printf( Zero\n ); case 1: printf( Um\n ); case 2: printf( Dois\n ); case 3: printf( Três\n ); break; case 4: printf( Quatro\n ); default: printf( Desconheco tal numero!\n );
Bem, nossa expressão seletora é a variável a que foi inicializada com valor 2, logo seu valor é 2. Procuraremos então um rótulo que possua o valor 2. O primeiro rótulo possui valor 1, logo não pode ser este. O segundo possui valor 1, também não satisfaz. Entretanto, o terceiro rótulo possui valor 2, sendo portanto igual ao valor da expressão seletora (perceba que a expressão seletora é avaliada somente uma vez, no início do comando switch). O comando associado ao case 2 é o comando printf( Dois\n ); então executaremos tal comando, imprimindo na tela a palavra Dois e pulando para a próxima linha. Entretanto, ainda não foi encontrado o fim do switch nem um comando break, então a próxima instrução, no caso, o printf( Tres\n ); também será executado, imprimindo assim também a palavra Três. Logo após este, é encontrado um comando break, interrompendo assim a execução dos comandos dentro do switch e ordenando ao programa continuar a execução com o próximo comando após o switch. Vamos agora pensar um pouco: quando devemos e quando não devemos usar um comando switch? 9.2.1 Quando usar o switch? Em primeiro lugar, vale lembrar que um switch só funcionará corretamente se estamos a trabalhar com uma expressão do tipo escalar ordinal, seja esta uma única variável de um tipo que satisfaz tal condição, seja uma expressão mais complexa que também satisfaça. Além disso, seu uso é muito interessante (e até incentivado) quando há mais do que dois possíveis desvios (um if permite somente dois desvios: um comando a ser executado caso seja verdadeiro, outro caso seja falso). Entretanto, deve-se lembrar que as condições para cada desvio deveriam ser valores ou um intervalo de valores bem definidos! 9.2.2 Quando não usar o switch? Se a nossa condição de teste precisa ser expressa por meio de uma comparação com um tipo não-escalar (um array, por exemplo) ou um escalar não-ordinal (um float), então não será possível utilizar-se de um switch. Além disso, se em vez de valores ou intervalos de valores bem definidos eu preciso trabalhar com condições de desigualdade (exemplo: x < 0 ), então também não será possível trabalhar com um switch, já que este precisa de rótulos bem definidos quanto aos seus valores.
9.3 Operador? Em C, temos um operador especial, o operador?, que nos permite retornar um determinado valor baseado em uma condição. A ideia desse comando é simples: se a condição é verdadeira, o valor da primeira expressão deve ser retornado como resultado, entretanto se a condição é falsa, retorna-se o valor de uma segunda expressão. O tipo de retorno desse operador então será igual ao tipo das possíveis expressões retornadas. Bem, vamos a um exemplo para ajudar a compreender como isso funciona: solucao = ( (a < b)? a : b); Neste exemplo podemos ver claramente a estrutura de um operador? : à esquerda encontra-se a condição a ser testada e à direita as duas expressões, separadas por dois-pontos ( : ). Podemos ler tal instrução da seguinte forma: se a for menor que b, então solucao recebe a, caso contrário, solucao recebe b. E quem prestar bastante atenção perceberá que este exemplo é uma das muitas formas de identificar o menor dentre dois valores! Pode-se utilizar este operador em conjunto com outros operadores, formando expressões mais complexas. Exemplo: resultado = x % ( (device_type = = 1)? 16 : 32 ); Neste exemplo, o que será armazenado em resultado não é o valor 16 ou o valor 32, e sim, o resto da divisão inteira do valor de x por um daqueles dois valores, a depender da condição do operador? ser verdadeira ou falsa. Vale lembrar também que em C um valor de retorno não precisa obrigatoriamente ser armazenado ou usado em uma expressão e que, nesses casos, podemos nos utilizar de funções que geralmente possuem void como retorno. Exemplo: (a = = 0)? printf( A ) : printf( B ); Como você já deve imaginar, essa expressão fará o seguinte: caso o valor da variável a seja igual a zero, imprimirá na tela a letra A, caso contrário, imprimirá B. Perceba que, neste caso, estamos utilizando o operador? de forma muito similar ao comando condicional if, não é mesmo? Entretanto, esta não é uma forma muito utilizada deste operador, sendo geralmente empregado nas formas anteriores, para retornar um valor para uma expressão ou atribuição.