#include <stdio.h>
int main () {
int n = 42;
int h = 0, v = 0;
printf ("Adivina en que número estoy pensando ? ");
scanf ("%d", &v);
if (v < 0) {
fprintf (stderr, "Sólo números positivos\n");
return -1;
}
if (v == n) printf ("Bien Hecho!\n");
else {
printf ("Sigue Probando!");
printf ("El número es %s\n", v < n ? "MAYOR" : "MENOR");
/* Si hemos fallado genera una pista */
printf ("Pista : ");
h = (v > n) ? v - n : n - v;
if (h < 5) printf (" TE QUEMAS!!!\n");
else if (h < 10) printf (" CALIENTE\n");
else if (h < 15) printf (" TEMPLADO\n");
else printf (" FRIO\n");
}
return 0;
}
En C y C++, la construcción if-then-else no necesita que
escribamos el then, lo cual es bastante común (Bash y Lua
son excepciones). Como podemos ver en el ejemplo, cuando a cada parte
del bloque if-else sigue una sola instrucción no es
necesario utilizar las llaves {}. De hecho, en C y C++
varias instrucciones agrupadas con {} son sintácticamente
un única instrucción.
La condición de la instrucción if debe ir siempre entre
paréntesis y, como en la mayoría de otros lenguajes, el bloque
else es opcional. Es posible, como se puede ver en el
programa, añadir un nuevo bloque if tras la clausura
else para refinar todavía más el flujo del programa.
C permite definir bloques if-then-else de una forma
alternativa, si bien, más allá de la curiosidad su interés es limitado.
Esta construcción la veremos en otros lenguajes como Python o Bash, y
nos permite re-escribir el bloque de código que genera la pista de la
siguiente forma:
(h < 5) && puts ("TER QUEMAS") ||
(h < 10) && puts ("CALIENTE") ||
(h < 15) && puts ("TEMPLADO") ||
puts ("FRIO");Esta construcción se aprovecha de la forma en la que se evaluan los
operadores && y ||. Puesto que un AND
lógico, requiere que ambas operadores sean ciertos, la segunda expresión
(en este caso la instrucción puts) debe ser evaluada si la
primera es cierta. Si la primera es falsa, ya no tiene sentido evaluar
la segunda ya que el resultado del AND será 0, independientemente del
valor de la segunda expresión.
Estas expresiones se pueden encadenar con el operador ||
que realiza un OR lógico y sigue una lógica similar, pero teniendo en
cuenta que un OR es solo falso solo si los dos operadores son falsos..
En el caso de que el primer operador sea verdadero, ya no es necesario
evaluar el segundo (lo que equivaldría a comprobar la siguiente
condición), pero en caso de que sea falso, hay que comprobar el segundo
valor para obtener el resultado.
El operador ternario, es ?: y se utiliza de la siguiente
forma:
(condición) ? entonces-haz-esto : sino-haz-esto
SABÍAS QUE
La secuencia de
ifs de nuestro ejemplo la podríamos haber escrito usando el operador ternario. Sería algo como esto:(h < 5) ? printf (" TE QUEMAS!!!\n") : (h < 10) ? printf (" CALIENTE\n") : (h < 15) ? printf (" TEMPLADO\n") : printf (" FRIO\n");Como podéis ver la estructura es muy similar a el uso de
cond && inst1 || inst2, la diferencia es que el operador ternario es un operador y por tanto nos permite construir expresiones, mientras que el uso de los operadores lógicos no lo permiten. En otras palabras, el operador ternario nos permite devolver un valor arbitrario, mientras que la operación lógica solo devolverá cero o 1.
Ya os adelanto que los lenguajes que proporcionan este operador lo
hacen usando esta misma sintaxis (Python es la excepción). La última
línea en al que calculamos el valor de h para generar la
pista, se podría re-escribir de la siguiente forma:
h = abs (v - n);Donde abs es la función que calcula el valor absoluto de
un entero y que está definida en stdlib.h.
NOTA
La función
scanf(Escanea con Formato), es la complementaria aprintfy nos permite leer valores desde la entrada estándar. La cadena que pasamos como primer parámetro se llama cadena de formato (format string) y nos permite especificar los tipos de los datos que queremos leer en las variables que pasamos como siguientes parámetros.En este caso
%dsignifica Decimal y se utiliza para leer un número en formato decimal.Otros lenguajes proporcionan soluciones similares y en muchas ocasiones utilizan el mismo (o muy similar) conjunto de caracteres para las cadenas de formato.
Para terminar, las condiciones, tanto la instrucción if
como el operador ternario ?: no tienen porque ser
operaciones booleanas en C, pero es recomendable que lo sean. En el caso
de que no lo sean, un valor 0 o NULL es
equivalente al valor lógico FALSO y cualquier otra cosa es
equivalente a VERDADERO.
SABÍAS QUE
El lenguaje C introdujo el tipo
boolpara representar valores booleanos en el estándar C99. En C++ el tipoboolestá soportado desde siempre y en objective-C, si bien podemos utilizarbooligual que en C99, gran cantidad de código usa la redefinición de tipoBOOLque no es más que ununsigned char. Objective-CBOOLtoma valoresYESandNOen lugar de lostrueofalsedel tipo estándarbool.
C (al igual que C++ y Objective C) también ofrece el comando
switch, el cual se puede ver como una secuencia de
ifs. Su sintaxis es la siguiente:
switch (variable) {
case CONSTANTE1: //código
break;
case CONSTANTE2: //código
break;
default: //Código
}Este código evaluará la variable y saltará al
case correspondiente o a la etiqueta default
en caso de que el valor de la variable no se encuentre listado en
ninguno de los casos. switch funciona literalmente como
hemos descrito. El flujo del programa se desvía al case
correspondiente y por lo tanto deberemos terminar el bloque de cada caso
con una instrucción break o de lo contrario, el programa
seguirá ejecutándose secuencialmente pasando por todos los
case restantes.
SOY CURIOSO
Busca el dispositivo de Duff (Duff’s Device) para ver un uso curioso de
switch
C++ define el atributo [[fallthrough]] para indicar la
situación en la que el break no se incluye a propósito y se
espera que el código que sigue se ejecute. A efectos de código generado
el atributo no tiene ningún efecto y solo es usado por compiladores o
utilidades que realicen análisis estático del código reportando cosas
como olvidarnos el break en un switch.
Este programa utiliza tres variables de tipo entero: v,
n y h. En C es necesario declarar las
variables (en realidad esto aplica a todo) antes de poder usarlas, esto
es, darles un nombre ( v,n o h en
este caso) y un tipo que en el ejemplo anterior es el tipo entero
int.
SOY NOVATO
Las variables nos permiten almacenar valores y modificarlos según necesitemos en nuestros programas. En este caso la variable
vla utilizamos para leer el valor que introduce el usuario. La variablehse utiliza para almacenar un resultado intermedio. Podríamos reutilizar la variablevya que no la volvemos a utilizar en el resto del programa, pero en general eso no es muy buena idea a no ser que queramos parecer guays (o tengamos graves restricciones de memoria), puesto que hace que el programa sea más difícil de seguir.
Se considera buena práctica dar a las variables nombres con significado, especialmente hoy en día que no tenemos restricciones de memoria y los entornos de desarrollo van a escribir la mayoría de los nombres por nosotros. Sin embargo, en programas o funciones sencillas a veces es práctico usar nombres cortos, sobre todo si hay operaciones o condiciones muy largas que se hacen difíciles de leer. Al final, utiliza el sentido común. En este ejemplo, el programa es tan sencillo que realmente no hay diferencia entre uno u otro enfoque. Pero quizás el código sería un poco más sencillo de leer declarando las variables como:
int numero;
int pista, valor;La variable n… bueno, no cambia de valor, así que podría
ser una constante… En C/C++ esto lo podemos hacer de varias formas:
#define N 42
const int n = 42;Tradicionalmente C utiliza la directiva del precompilador
#define. En este caso, lo que el pre-procesador hace es
literalmente sustituir el valor de H por 42 en
todo el código antes de compilarlo… De la misma forma que lo haría un
procesador de textos. En general esto funciona perfectamente, pero es
importante saber que hacemos para evitar efectos indeseados.
#define es realmente un macro de sustitución más que una
constante formalmente hablando.
En el segundo caso, la palabra clave const le indica al
compilador que esta variable solo puede ser inicializada una vez, tras
lo cual su valor no puede ser modificado. Si bien esto no es del todo
cierto, en programas que no hagan filigranas con punteros la declaración
funcionará como una constante a efectos de compilación.
La principal ventaja del segundo método es que tenemos información de tipo. Si nuestra cadena es un entero, nuestra constante también lo es. Una vez más, esto no es un grave problema si sabes lo que haces, pero sino, las promociones automáticas de tipos de C/C++ te pueden jugar una mala pasada.
Resumen
- Soporta
if ... else [if] ...y(condicion) && ... || ... - Soporta operador terciario
(cond) ? valor_Verdad : Valor_Falso - Versiones anteriores a C99 No soporta booleanos:
==0 -> FALSO y !=0 VERDADERO - Soporta
switch (variable) { case VALORES: break; default:} - Constantes con preprocesador:
#define H 42(solo una secuencia de caracteres) - Constantes con modificador:
const int h = 42(tipo asociado)
■
