// control de flux

Condicionals en C

if, else, switch i l'operador ternari: com fa un programa per prendre decisions segons una condició.

if executar només si es compleix una condició

Sintaxi

if (condició) {
    // s'executa només si condició és certa
}

Exemple

int nota = 7;
if (nota >= 5) {
    printf("Aprovat\n");
}

Si nota >= 5 és certa, s'executa el cos. Si és falsa, simplement es continua després del bloc, sense fer res.

else l'alternativa

Exemple

int nota = 3;
if (nota >= 5) {
    printf("Aprovat\n");
} else {
    printf("Suspès\n");
}

El bloc else s'executa exactament quan la condició de l'if és falsa. Sempre s'executa un dels dos blocs, mai els dos.

else if encadenar diverses condicions

Exemple

int nota = 6;
if (nota >= 9) {
    printf("Excel·lent\n");
} else if (nota >= 7) {
    printf("Notable\n");
} else if (nota >= 5) {
    printf("Aprovat\n");
} else {
    printf("Suspès\n");
}

Es comproven les condicions en ordre, d'una en una. En el moment que una és certa, s'executa el seu bloc i ja no es comprova cap més condició de la cadena.

Operadors lògics: && || !

Per combinar diverses condicions en una de sola. En C no hi ha cap tipus booleà real: 0 es tracta com a fals, i qualsevol altre valor (també els negatius) com a cert.

Taula

OperadorVol dir
a && bcert si a i b són certes totes dues
a || bcert si com a mínim una de les dues és certa
!ael contrari d'a: si a és certa, !a és falsa

Exemple

int edat = 16;
if (edat >= 0 && edat <= 17) {
    printf("Ets menor d'edat\n");
}

Com que en C el 0 és fals i qualsevol altre valor és cert, !0 val 1 i !5 val 0 — fixa't que no cal que l'operand sigui 1 perquè es consideri cert.

Avaluació en curtcircuit

En a && b, si a ja és falsa, b ni s'arriba a avaluar: el resultat ja no pot canviar. El mateix amb a || b quan a ja és certa. Això es pot aprofitar per evitar errors:

if (b != 0 && a / b > 1) {
    printf("Es compleix\n");
}

Si b val 0, b != 0 ja és falsa i a / b mai s'avalua: no hi ha divisió per zero. Si escrivissis les dues condicions a l'inrevés (a / b > 1 && b != 0), el programa petaria quan b fos 0.

Compte: = no és ==

Un error molt típic és escriure una assignació on toca una comparació. Com que en C tota expressió té un valor, x = 5 també en té un (5, que és cert), i el compilador no s'hi queixa:

if (x = 5) { ... }   // assigna 5 a x i SEMPRE és cert: bug!
if (x == 5) { ... }  // compara x amb 5: això és el que volíem

Compila igualment i, com que 5 és un valor diferent de zero, el bloc s'executa sempre, canviant per sorpresa el valor de x. Val la pena mirar-s'hi dues vegades quan s'escriu un if.

Quan calen parèntesis

&& es comprova abans que ||, així que a || b && c es llegeix com a || (b && c). És vàlid tal qual, però els parèntesis explícits (a || (b && c)) fan que ningú hagi de recordar la regla per entendre-ho.

switch triar entre valors concrets

Quan cal comparar una mateixa variable amb molts valors possibles, un switch és més clar que una cadena llarga d'else if.

Sintaxi

switch (variable) {
    case valor1:
        // ...
        break;
    case valor2:
        // ...
        break;
    default:
        // si no coincideix amb cap cas
}

Exemple

int dia = 3;
switch (dia) {
    case 1:
        printf("Dilluns\n");
        break;
    case 2:
        printf("Dimarts\n");
        break;
    case 3:
        printf("Dimecres\n");
        break;
    default:
        printf("Dia no vàlid\n");
}

El default és opcional i s'executa quan cap case coincideix.

Compte amb oblidar el break!

Si un case no acaba amb break, l'execució continua cap al case següent encara que no hi torni a haver cap comprovació — es diu fall-through, i sol ser un error involuntari.

int x = 1;
switch (x) {
    case 1:
        printf("u\n");
        // falta el break!
    case 2:
        printf("dos\n");
        break;
}
// imprimeix "u" I "dos", encara que x només val 1

?: operador ternari

Una forma compacta d'escriure un if-else que retorna un valor, útil quan només vols triar entre dues expressions.

Sintaxi

condició ? valor_si_certa : valor_si_falsa

Exemple

int a = 5;
int b = 8;
int max = (a > b) ? a : b;
printf("%d\n", max);

Equival a: si a > b, max val a; si no, val b. Aquí a > b és fals, així que max val 8.

Mira quin camí pren el programa

Tria una variant i avança pas a pas per veure quina condició es compleix i quin bloc s'acaba executant.

Codi

Variables

NomValor

Terminal

Pas 1 / 1

Problemes per resoldre

Sense solució ni comprovació automàtica: escriu el programa, compila'l (gcc programa.c -o programa) i prova'l amb els exemples i amb valors límit. Amb variables, condicionals i l'entrada i sortida que ja coneixes en tens prou (no calen bucles). Les pistes són plegades perquè les obris només si les necessites.

1. Signe i paritat if / else if

Demana un enter. Escriu en una línia si és positiu, negatiu o zero, i a la línia següent si és parell o senar (el zero és parell).

Nombre: -7
negatiu
senar

Nombre: 0
zero
parell
Pistes

El signe té tres casos que s'exclouen entre ells: una cadena if / else if / else. La paritat és una segona decisió, independent de la primera.

Compte amb els negatius en C: -7 % 2 val -1, no 1. Per això comprova si el residu és 0 (n % 2 == 0 vol dir parell) i deixa la resta com a senar, en lloc de comprovar n % 2 == 1.

2. Entre 1 i 5 operadors lògics

Demana un enter i digues si és un valor entre 1 i 5, els dos extrems inclosos. Prova-ho amb 0, 1, 5 i 6.

Nombre: 5
correcte

Nombre: 6
no és correcte
Pistes

Han de complir-se dues condicions alhora: n >= 1 && n <= 5.

Els extrems són la trampa: amb > i < l'1 i el 5 quedarien fora. Comprova sempre els valors límit.

3. Vocal o consonant operadors lògics

Demana una lletra. Escriu «vocal» si ho és, «consonant» si és una altra lletra, i «no és una lletra» si és qualsevol altre caràcter. Ha de funcionar tant amb majúscules com amb minúscules (sense accents).

Lletra: E
vocal

Lletra: x
consonant

Lletra: 7
no és una lletra
Pistes

Llegeix el caràcter amb scanf(" %c", &c): l'espai davant de %c salta qualsevol blanc pendent.

Passa-la a minúscula amb tolower(c) (cal #include <ctype.h>) i només hauràs de comparar amb minúscules. Sense tolower, hauràs de comparar amb les deu vocals.

Decideix primer si és una lletra (entre 'a' i 'z'; els caràcters es comparen pel seu codi) i només després si és vocal, amb una cadena de ||.

4. Calculadora switch

Demana dos nombres decimals i un operador (+, -, * o /) i escriu l'operació amb el resultat, amb dos decimals. Si l'operador no és cap dels quatre, escriu «Operació desconeguda»; si es demana dividir entre zero, escriu «No es pot dividir per zero».

Primer nombre: 7
Operador (+ - * /): /
Segon nombre: 2
7.00 / 2.00 = 3.50

Primer nombre: 5
Operador (+ - * /): /
Segon nombre: 0
No es pot dividir per zero

Primer nombre: 5
Operador (+ - * /): %
Segon nombre: 2
Operació desconeguda
Pistes

L'operador és un char: llegeix-lo amb scanf(" %c", &op) i tria l'operació amb un switch (op) amb case '+': (cometes simples: són caràcters). No t'oblidis dels break; el default recull els operadors desconeguts.

Dins del case '/' comprova b == 0 abans de dividir. A C, dividir un double entre zero no atura el programa: dona inf, així que ho has de detectar tu.

5. Dies d'un mes switch

Demana un mes (1-12) i un any, i escriu quants dies té aquell mes. El febrer en té 29 als anys de traspàs i 28 als altres; si el mes no és entre 1 i 12, escriu «mes incorrecte». Un any és de traspàs si és divisible entre 4 però no entre 100, o bé si és divisible entre 400.

Mes: 2
Any: 2024
29 dies

Mes: 2
Any: 1900
28 dies

Mes: 11
Any: 2023
30 dies

Mes: 13
Any: 2023
mes incorrecte
Pistes

Els mesos de 31 dies (1, 3, 5, 7, 8, 10 i 12) donen la mateixa resposta: quan diversos case van seguits, sense cap instrucció entre ells, comparteixen el mateix codi (case 4: case 6: case 9: case 11:). Aquí és una manera deliberada d'agrupar, no el parany del fall-through.

El febrer és l'únic cas amb una decisió a dins. L'any de traspàs es comprova amb (any % 4 == 0 && any % 100 != 0) || any % 400 == 0. Prova'l amb 1900 (no), 2000 (sí) i 2024 (sí). El default serveix per als mesos incorrectes.

6. Tipus de triangle if / else if

Demana els tres costats d'un triangle (nombres enters). Si no formen un triangle, escriu «no és un triangle». Si en formen un, escriu si és equilàter (tres costats iguals), isòsceles (exactament dos d'iguals) o escalè (tots diferents).

Costats: 3 3 5
isòsceles

Costats: 3 4 5
escalè

Costats: 1 2 3
no és un triangle
Pistes

Tres costats formen un triangle quan cada costat és més petit que la suma dels altres dos (estrictament: 1, 2 i 3 no en fan cap), i tots han de ser positius. Comprova-ho primer i només classifica si és cert.

L'ordre de les comprovacions importa: un triangle equilàter també té dos costats iguals, així que comprova primer l'equilàter (a == b && b == c), després l'isòsceles (a == b || b == c || a == c), i el que sobri és escalè.

Pots llegir els tres costats d'una sola vegada amb scanf("%d %d %d", &a, &b, &c).

7. Màxim i mínim de tres nombres operador ternari

Demana tres enters i escriu el màxim i el mínim, sense fer servir cap if: només l'operador ternari.

Tres nombres: 7 12 5
Màxim: 12
Mínim: 5
Pistes

El màxim de dos nombres és (a > b) ? a : b. Per a tres, calcula primer el màxim de dos i compara aquest resultat amb el tercer. El mínim funciona igual.

Guarda els resultats intermedis en una variable en lloc d'escriure una expressió enorme amb ternaris dins de ternaris: costa molt de llegir.

8. Equació de segon grau if imbricats

Demana els coeficients a, b i c de l'equació ax² + bx + c = 0 (nombres decimals) i resol-la. Si a és 0 no és de segon grau: resol bx + c = 0 (i tracta el cas b = 0: si c també és 0 hi ha infinites solucions, i si no, no en té cap). Si a no és 0, mira el discriminant Δ = b² − 4ac: positiu, dues solucions; zero, una de sola; negatiu, cap de real.

a, b i c: 1 -3 2
Dues solucions: x1 = 2.00, x2 = 1.00

a, b i c: 1 1 1
No té solucions reals

a, b i c: 0 2 -4
Equació de primer grau: x = 2.00
Pistes

Pensa-ho com decisions en dos nivells: primer a == 0 o no, i dins de cada cas una altra decisió (sobre b a la branca d'a = 0, sobre el discriminant a l'altra). Un if dins d'un altre if és perfectament vàlid.

Calcula el discriminant una sola vegada, en una variable (b*b - 4*a*c). Les solucions són (-b + sqrt(d)) / (2*a) i (-b - sqrt(d)) / (2*a); sqrt és de <math.h>, i a Linux cal compilar amb -lm al final.

Prova també els casos límit: 1 2 1 (discriminant 0), 0 0 5 i 0 0 0.

Exercicis d'autoavaluació

Cada exercici es genera amb valors nous a l'atzar. Llegeix el codi, pensa quina serà la sortida per terminal i comprova-ho.

—

Quina serà la sortida per terminal d'aquest programa?