if (expressió booleana) { ( cos del LLAVORS ) } else /* optatiu */ { ( cos del SINÓ ) }
Si només hi ha una expressió, no són necessàries les claus. L'expressió booleana sempre ha d'anar entre parèntesi.
if (expressió booleana) (expressió del LLAVORS); else (expressió del SINÓ);
Exemples:
ex 1.1: Només hi ha una expressió pel Llavors i una pel Sinó -> no es posen claus:
if (a>=b) c='X'; else c='Y'; /* és equivalent a */ c=((a>=b)?'X':'Y');
ex 1.2: Una expressió booleana complexa, més vàries expressions dins la condició Llavors. No hi ha condició Sinó:
if ((a==b) && (!(b>c) || (a!=6))) { x*=a+b; y=x/(a-b) % 4; }
ex 1.3: Una expressió d'assignació dins d'una de comparació; s'executa l'expressió i es compara amb el resultat obtingut -> tot amb una sola línia! (cal anar amb compte, però, amb aquestes "virgueries" del C):
if (a==(b=c)) /* és equivalent a */ b=c; a>>=2; if (a==b) a>>=2;
ex 1.4: Tres variants on la col.locació de les claus canvia la situació de l'expressió b++:
sempre |
si (a!=b) i (a!=c) |
si (a!=b) |
if (a==b) a>>=1; else if (a==c) a>>=2; else a>>=3; b++; |
if (a==b) a>>=1; else if (a==c) a>>=2; else { a>>=3; b++; } |
if (a==b) a>>=1; else { if (a==c) a>>=2; else a>>=3; b++; } |
ContraExemples:
cex 1.1: (a=b) no és una expressió d'avaluació, sinó d'assignació. Per tant, no retorna un booleà sinó el valor que pren la variable a. Si aquest valor és 0, la sentència if ho interpretarà com a FALS. Si el valor és diferent de 0, ho interpretarà com a CERT. En tots dos casos es copiarà el valor de b sobre a:
if (a=b) a*=2;
cex 1.2: Aquí el problema està en el punt i coma de darrera de l'expressió d'avaluació. Com que no hi han claus, s'interpreta que l'acció a realitzar en cas que es compleixi la condició és l'acció buida. Per tant, tant si la condició és certa com si no ho és la sentència if no fa res, i l'expressió a*=2 s'executa sempre, ja que està fora del condicional:
if (a==b); a*=2;
switch (expressió) /* una expressió qualsevol (en particular, una variable) */ { case valor1 : ... /* cas (expressió==valor1) */ break; case valor2 : ... break; /* break salta al final de l'estructura switch */ ... case valorn : ... break; default : /* Altrament de tots els altres casos (opcional) */ ... }
- Si no hi ha break al final de les sentències de cada cas, s'executaran les sentències del cas següent, ja que el codi generat per a tots els casos està consecutiu en memòria, i les paraules reservades case fan de simples etiquetes a on es salta (quan s'executa el switch) si es compleix la igualtat de l'expressió amb el valor de cada cas.
- Només es pot estalviar el break al final de les sentències de l'últim cas (sigui un default o no).
Exemples:
ex 1.5:
switch (a) { case 0: b++; /* cas de que a==0, incrementar b */ c='A'; /* i assignar 'A' a la variable c */ break; /* sortir de l'estructura switch */ case 1: c='B'; break; /* cas a==1 -> una sola sentència */ case 3: if(b<10) /* cas a==3 -> sentències complexes */ { if(c!='Z') c='X'; b-=2; } else c='Y'; b+=5; break; default: c='D'; /* cas de que a no sigui 0 ni 1 ni 3 */ }
ex 1.6: Aprofitant aquest funcionament tan particular del switch de C (en combinació amb el break), es pot implementar condicions "semicomplexes", és a dir, que no s'executi un conjunt de sentències només quan es produeix una igualtat exacta:
switch (c) { case 'A': case 'B': a++; /* a++ si c=='A' o c=='B' */ case 'C': b++; /* b++ si c=='A' o c=='B' o c=='C' */ break; /* evita executar --b */ case 'D': --b; /* no cal el break perquè és l'últim cas */ }
for (inicialització; condició de mentre; increment) { ... }
- L'estructura for, en el llenguatge C, és equivalent a l'estructura while: consta d'unes inicialitzacions, una condició de fi, i unes sentències d'increment del comptador del bucle que s'executaran després d'executar el cos del bucle un pas. La condició de fi fa referència a una expressió booleana que determina la continuïtat del bucle mentre l'expressió sigui CERT. Per tant, és idèntica a la condició de fi d'un bucle while. Llavors, per què no es fa servir una sola estructura? La diferència és conceptual: si el bucle té un número d'iteracions conegut quan s'escriu el programa (recórrer una taula de 30 elements, per exemple), llavors es posarà un for; si el número d'iteracions variarà en funció del context quan s'executi el programa (llegir caràcters d'un fitxer fins que es trobi un Return), llavors es posarà un while.
Exemples:
ex 1.7: Bucle que va de 1 a max (encara que max pugui ser variable, conceptualment el número d'iteracions està ben delimitat). Cal fixar-se en la condició de mentre del bucle: i<=max (no és pas i==max):
s=0; for(i=1; i<=max; i++) { s += i; }
ex 1.8: El mateix algorisme que abans, però aquesta vegada s'ha introduït la inicialització de la variable s dins del lloc corresponent a les inicialitzacions del for (separats per comes). A més, com que només hi ha una sentència dins del cos del bucle, es poden ometre les claus:
for (s=0, i=1; i<=max; i++) s += i;
ex 1.9: Una vegada més, es pot incloure sentències dins les clàusules del bucle. En aquest exemple s'ha introduït l'única sentència del bucle dins del lloc d'increment (també separat per comes). Aquesta, però, no és una pràctica recomanable. També cal observar que ara el cos del bucle està buit i, per tant, cal posar el punt i coma darrera de la sentència (equival a obrir i tancar claus {}):
for (s=0, i=1; i<=max; s += i, i++);
ContraExemples:
cex 1.3: Si per error es posa un punt i coma darrera de la sentència for, el resultat és que el compilador suposa que no hi ha cos del bucle (no dóna cap missatge d'avís!). En l'exemple, la sentència s += i s'executaria un sol cop, després d'haver realitzat totes les iteracions del bucle (la sentència següent al bucle):
for (s=0, i=1; i<=max; i++); s += i;
cex 1.4: Si per error no es posa un punt i coma darrera de la sentència for, el resultat és que el compilador suposa que el cos de bucle és la sentència següent al for:
for (s=0, i=1; i<=max; i++, s += i)
cex 1.5: Si la condició de mentre només inclou l'últim cas, el bucle no s'executarà cap vegada, ja que inicialment la condició és falsa:
for (s=0, i=1; i==max; i++, s += i);
/* inicialitzacions */ while (condició de mentre) { ... /* cos del bucle */ /* increments */ }
- En aquesta estructura, les inicialitzacions i els increments (actualitzacions de les variables que determinen la condició de mentre) no tenen un lloc preassignat, com en cas del for. La condició de mentre és l'expressió booleana tal que, si és CERT, s'executa el cos del bucle un altre cop.
Exemples:
ex 1.10: Exemple d'estructura while equivalent als exemples de l'estructura for:
s=0; i=1; /* inicialitzacions */ while (i<=m) { s += i; /* cos del bucle */ i++; /* increment */ }
ex 1.11: Exemple simbòlic (funcions llegir i escriure) on la condició de mentre és complexa:
i=0; c=llegir(); while((c!='F') && (i<1000)) { escriure(c); i++; c=llegir(); }
ex 1.12: El mateix algorisme que abans, però incloent-hi les accions d'actualització de les variables c i i dins de la condició de comparació. En el cas de c, primer s'executa l'acció llegir() (perquè està dins de parèntesi) i el resultat (el que s'ha llegit) és emmagatzemat en c i comparat amb 'F'. En el cas d'i, primer es compara i amb 1000 i després s'incrementa (perquè l'operador d'autoincrement està darrera la variable). Com que ara només hi ha una sentència dins del cos del bucle, es poden ometre les claus. Cal repetir que aquesta tècnica d'incloure sentències dins de les expressions de comparació, si bé dóna codi font més curt (i no sempre un codi executable més curt), no és recomanable per a una programació clara, estructurada, i lliure d'efectes laterals (efectes imprevistos en l'execució):
i=0; while ((c=llegir())!='F') && ((i++)<1000)) escriure(c);
ex 1.13: Per veure l'equivalència entre while i for, el següent exemple mostra com es pot fer el mateix exemple de while, tot i que el més lògic és fer servir while ja que el número d'iteracions del bucle és indeterminat (depèn del moment en que es llegeixi un caràcter 'F' o, en el seu defecte, quan s'hagin llegit 1000 caràcters):
c=llegir() for(i=0; (c!='F') && (i<1000); i++) { escriure(c); c=llegir(); }
/* inicialitzacions */ do { ... /* cos del bucle */ /* increments */ } while (condició de repetir);
- En aquesta estructura, el bucle es continuarà executant mentre la condició booleana de repetir sigui CERT. És equivalent al while, però amb la diferència que l'avaluació de la condició es realitza després de l'execució del bucle (sempre executarà el bucle almenys una vegada). Observar que sempre hi ha un punt i coma després de while (per distingir el do-while de while).
Exemples:
ex 1.14: El mateix exemple que el de l'estructura while, però aquesta vegada sempre s'escriurà un caràcter com a mínim, és a dir, la F última:
i=0; do { c=llegir(); escriure(c); i++; } while ((c!='F') && (i<1000));
ex 1.15: L'equivalent de l'exemple anterior amb l'estructura algorísmica Repetir-Fins (la condició de fi és la contraria a la condició de repetir o condició de mentre):
i:=0; repeat c:=llegir(); escriure(c); i:=i+1; until ((c='F') OR (i>=1000));
- Per practicar les sentències de control de flux, completeu el següent programa COMPTAMM.C, que ha de comptar el número de lletres minúscules i el número de lletres majúscules introduïdes per teclat. Per llegir les lletres es disposa de la funció getchar(), que retorna el codi ASCII del caràcter llegit pel dispositiu estàndard d'entrada (en aquest cas, el teclat). Ara bé, cal prémer el <Return> per a que una línia de caràcters comenci a ser introduïda pel dispositiu d'entrada. És a dir, es pot escriure tota una línia i, quan s'hagi premut el <Return>, els caràcters de la línia seran llegits un a un cada vegada que s'executi una crida a la funció getchar():
#include <stdio.h> void main(void) { char c; /* declaració de variables */ short puls,maju,minu; /* inicialitzacions */ /* inici bucle */ c=getchar(); /* capturar lletra */ /* cos del bucle */ /* increments */ /* fi bucle */ /* imprimir resultats */ printf("nº minúscules = %d\n",minu); printf("nº majúscules = %d\n",maju); printf("nº pulsacions = %d\n",puls); }
- Es pot fer servir qualsevol l'estructura repetitiva que es cregui convenient.