Programació en C/Control de flux

Molt pocs programes C segueixen exactament un camí de control i fan que es manifesti explícitament cada instrucció. Per programar eficaçment, és necessari d'entendre com poden canviar els passos presos per un programa a causa d'entrada de l'usuari o unes altres condicions, com es poden executar alguns passos gaires vegades amb poques línies de codi, i com poden semblar els programes demostrar una rudimentaria lògica. Les estructures C conegudes com a condicionals i bucles concedeixen aquestes possibilitats.

D'aquí en endavant, és necessari entendre què es coneix per block. Un bloc és un grup de sentències de codi que estan associades i estan pensades per ser executades com a unitat. A C, el començament d'un bloc de codi es denota amb { (clau d'ator esquerra), i el final d'un bloc es denota amb }. No és necessari posar uns dos punts al final d'un bloc. Els blocs poden ser buits, com en {}. També es poden niar blocs; i.e. hi pot haver blocs de codi dins de blocs més grans.

Condicionals[modifica]

És probable que no hi hagi cap programa significatiu a on no es demostrin les habilitats bàsiques de presa de decisions d'un ordinador. Es pot de fet sostenir que no hi ha cap activitat humana significativa en que alguna classe de presa de decisió, instinctual o d'altra tipus, no tingui lloc. Per exemple, quan es condueix un cotxe i s'està enfocant un semàfor, un no pensa, "Continuaré conduint a través de la intersecció." Més aviat, un pensa, "m'aturaré si el llum està vermell, anar-se'n si el llum és verd, i si groc anar-se'n només si estic viatjant a una certa velocitat una certa distància des de la intersecció." Aquestes classes de processos es poden simular en conditionals utilitzant C.

Una condicional és una declaració que instrueix l'ordinador d'executar un cert bloc de codi o canviar certes dades només si s'ha satisfet una condició específica. La condicional més comuna és la declaració d'si-Else, dins les expressions condicionals i sentències Switch-Case.

Abans que un pugui entendre declaracions condicionals, és necessari d'entendre primer com expressa C relacions lògiques. C tracta lògica com si fos aritmètica que. El valor 0 (zero) representa fals, i tots els altres valors representen veritable. Si s'escull un valor particular per representar veritable i llavors comparar valors contra aquest, tard o d'hora el seu codi fallarà quan el seu valor assumit (sovint 1) resulti ser incorrecte. Codi escrit per gent incòmode amb el C la llengua pot sovint ser identificada prop de l'ús de #define per fer un valor "VERITABLE".

Perquè la lògica és aritmètica a C, els operadors aritmètics i els operadors lògics són un i el mateix. No obstant això, hi ha un cert nombre d'operadors que estan associats típicament amb la lògica:

Expressions relacionals i d'equivalència[modifica]

a < b
1 si a es més petit que b, 0 d'altra manera.
a > b
1 si a es més gran que b, 0 d'altra manera.
a <= b
1 si a es més petit que o igual a b, 0 d'altra manera.
a >= b
1 si a es més gran que o igual a b, 0 d'altra manera.
a == b
1 si a es igual a b, 0 d'altra manera.
a != b
1 si a no és igual a b, 0 d'altra manera

Els programadors nous haurien de prendre nota especial del fet que l'"igual a" operador és ==, no =. Això és la causa d'equivocacions de codificació nombroses i és sovint un error difícil a descobrir, mentre que la declaració (un = b) posa un igual a b i posteriorment avalua a b; però la declaració (un == b), que es el que es vol posar sovint, comprova si un és igual a b. Necessita que se li assenyali que, si confon = amb ==, la seva equivocació no serà sovint portada a la seva atenció pel compilador. Una declaració com ( c = 20) {} és considerada perfectament vàlid per la llengua, però n'assignarà 20 a c i serà avaluat com veritable. Una tècnica simple per evitar aquesta classe d'errors (en molts, no tots els casos) és posar la constant primer. Això farà que el compilador emeti un error, si == s'ha confós amb =.

Fixi's que C no té un tipus booleà com tenen moltes altres llengües. 0 significa fals i qualsevol altra cosa veritable. Així el que ve a continuació és equivalent:

 if (foo()) {
   //fer qualque cosa
 }

i


 if (foo() != 0) {
   //do something
 }


Sovint #define VERITABLE 1 i #define FALS 0 són utilitzats evitar el problema de la manca d'un tipus booleà. Això és mala pràctica, ja que fa suposicions que no s'aguanten. És una millor idea indicar què s'està esperant com a resultat d'una crida a una funció, com hi ha moltes maneres diferents de indicar condicions d'error, depenent de la situació.


 if (strstr("foo", bar) >= 0) {
   //bar conté "foo"
 }

Aquí, strstr torna l'índex on es troba la subcadena "foo" i -1 si no es trobava. Fixi's que això fallaria amb la definició VERITABLE esmentada en el paràgraf previ. Tampoc no produiria els resultats esperats si ometíem el >= 0.

Una altra cosa a considerar és que les expressions relacionals no avaluen com ho farien en texts matemàtics. És a dir, un valor de myMin < valor < myMax no s'avalua com probablement es podria pensar. Matemàticament, això provaria si el valor està entre myMin i myMax. Però a C, què passa és que el valor es compara primer amb myMin. Això en produeix o un 0 o un 1. És aquest valor que es compara amb myMax. Exemple:

 int valor = 20;
 /* ... */
 if ( 0 < valor < 10) { //no faci això! Sempre s'evalua com "vertader"
    /* fer qualque cosa */
 }

Perquè valor és més gran que 0, la primera comparació produeix un valor d'1. Ara 1 es compara per ser menys de 10, que és veritable, així les declaracions en el if són executades. Això probablement no és què s'esperava el programador. El codi apropiat seria:


 int valor = 20;
 /* ... */
 if ( 0 < valor && valor < 20) {   // el && significa "i"
  /* fer qualque cosa */
 }

Expressions lògiques[modifica]

a || b
when UN DELS DOS a o b és vertader (or both), the result is 1, otherwise the result is 0.
a && b
when ELS DOS a i b son vertader, el resultat és 1, d'altra manera el resultat és 0.
!a
quan a és vertader, el resultat és 0, quan a és 0, el resultat és 1.

Aquí hi ha un exemple d'una expressió lògica més gran. En la declaració:

  e = ((a && b) || (c > d));

a e se li posa igual a 1 si a i b són diferents a zero, o si c és més gran que d. En tots els altres casos, e es posa a 0.

C usa curtcircuits en l'avaluació d'expressions lògiques. És a dir, una vegada que és capaç determinar la veritat d'una expressió lògica, no fa avaluació més llunyana. Això és sovint útil com a continuació:

int mevaArray[12];
....
if ( i < 12 && mevaArray[i] > 3) { 
....

Al retall de codi, la comparació d'i amb 12 es fa primer. Si avalua a 0 (fals), i estaria fora de fites com a índex a mevaArray. En aquest cas, el programa mai no intenta accedir a mevaArray[i] ja que la veritat de l'expressió se sap que és fals. Per això no ens necessitem preocupar aquí d'intentar accedir a un element de l'array fora de rang si ja se sap que i és més gran que o igual a zero. Una cosa similar passa amb expressions que impliquen el l'operador || (o).

while( ferAixò() || ferAllò()) ...

ferAllò mai no es crida si ferAixò torna un valor diferent a zero (vertader).