PROGRAMACIÓ EN LLENGUATGE C · llenguatge C i que corresponen a un curs introductori a la...

25
E. Valveny, R. Benavente, A. Lapedriza, M. Ferrer, J. Garcia-Barnés PROGRAMACIÓ EN LLENGUATGE C AMB 56 PROBLEMES RESOLTS I COMENTATS Universitat Autònoma de Barcelona Servei de Publicacions Bellaterra, 2009

Transcript of PROGRAMACIÓ EN LLENGUATGE C · llenguatge C i que corresponen a un curs introductori a la...

E. Valveny, R. Benavente, A. Lapedriza, M. Ferrer, J. Garcia-Barnés

PROGRAMACIÓ EN LLENGUATGE C

AMB 56 PROBLEMEs REsOLTs I COMENTATs

Universitat Autònoma de Barcelonaservei de Publicacions

Bellaterra, 2009

001-322 Computacio.indd 5 16/09/2009 13:44:23

DADEs CATALOGRÀFIQUEs RECOMANADEs PEL sERVEI DE BIBLIOTEQUEs DE LA UNIVERsITAT AUTÒNOMA DE BARCELONA

Programació en llenguatge C: amb 56 problemes resolts i comentats / E. Valveny [et al.]. — Bellaterra : Universitat Autònoma de Barcelona, servei de Publicacions, 2009. — (Manuals de la Universitat Autònoma de Barcelona ; 55)

IsBN 978-84-490-2603-4

I. C (llenguatge de programació)II. Programació (ordinadors)681.3.06

© del text: E. Valveny, R. Benavente, A. Lapedriza, M. Ferrer, J. Garcia-Barnés, 2009

© d’aquesta edició: servei de Publicacions, 2009

Imatge de la coberta:Josep Huguet Alzius i Marc Navarro Hernández

Composició:Fotocomposición gama, sl

Travessera de les Corts, 55, 2n 1a08028 Barcelona

Edició i impressió:Universitat Autònoma de Barcelona

servei de PublicacionsEdifici A

08193 Bellaterra (Cerdanyola del Vallès)Tel.: 93 581 10 22. Fax: 93 581 32 39

[email protected]://publicacions.uab.cat

IsBN 978-84-490-2603-4Dipòsit legal: B. 36.921-2009

Printed in spain

Aquest llibre s’ha publicat amb la col·laboració de la Generalitat de Catalunya

001-322 Computacio.indd 6 16/09/2009 13:44:23

7

Índex

Pròleg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

1. IntroduccIó. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.1. Història del llenguatge C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2. Característiques del llenguatge C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.3. Estructura d’un programa en C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.4. Elements sintàctic d’un programa en C. . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.1. Estil d’escriptura d’un programa en C . . . . . . . . . . . . . . . . . . . . 20 1.5 Compilació d’un programa en C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1.5.1. Detecció i depuració d’errors . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

2. tIpus de dades sImples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.1. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.1.1. Concepte de variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.1.2. Tipus de dades bàsics en C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.1.3. Declaració de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.1.4. Inicialització de variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2. Constants. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.2.1. Concepte de constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.2.2. Declaració de constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.3. Identificadors de variables i constants . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.4. Entrada / sortida bàsica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.4.1. sortida bàsica: funció printf . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.4.2. Entrada bàsica: funció scanf . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.4.3. Entrada de caràcters. Funcions: getchar, getche i getch . . . 42 2.5. Operadors i expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.5.1. Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.5.2. Operadors aritmètics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.5.3. Operadors d’assignació compostos . . . . . . . . . . . . . . . . . . . . . . 44 2.5.4. Operadors d’increment i decrement . . . . . . . . . . . . . . . . . . . . . . 45 2.5.5. Operadors relacionals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.5.6. Operadors lògics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 2.5.7. Operadors de bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 2.5.8. Prioritat i associativitat dels operadors. . . . . . . . . . . . . . . . . . . . 48 2.6. Conversions de tipus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.6.1. Conversions implícites. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

001-322 Computacio.indd 7 16/09/2009 13:44:23

8 PROGRAMACIÓ EN LLENGUATGE C

2.6.2. Conversions explícites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.7. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

3. estructures de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.1. Estructura seqüencial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.2. Estructures condicionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 3.2.1. Alternativa simple: if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 3.2.2. Alternativa doble: if/else . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 3.2.3. Estructures if/else encadenades . . . . . . . . . . . . . . . . . . . . . . . 62 3.2.4. Alternativa múltiple: switch/case. . . . . . . . . . . . . . . . . . . . . . 64 3.3. Composició iterativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.3.1. Estructura while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 3.3.2. Estructura do/while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 3.3.3. Estructura for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 3.3.4. Comparació de les estructures iteratives . . . . . . . . . . . . . . . . . . 74 3.4. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

4. estructures de dades compostes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 4.1. Taules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 4.1.1. Definició . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 4.1.2. Declaració . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 4.1.3. Accés als elements de la taula . . . . . . . . . . . . . . . . . . . . . . . . . . 83 4.1.4. Inicialització de les taules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.1.5. Representació en memòria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 4.2. Cadenes de caràcters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.2.1. Definició i declaració . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.2.2. Inicialització. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 4.2.3. Accés als elements de la cadena. . . . . . . . . . . . . . . . . . . . . . . . . 91 4.2.4. Entrada / sortida. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.2.5. Funcions de tractament de cadenes . . . . . . . . . . . . . . . . . . . . . . 93 4.3. Taules bidimensionals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 4.3.1. Definició i declaració . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 4.3.2. Inicialització. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.3.3. Accés als elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.3.4. Representació en memòria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.4. Registres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.4.1. Definició . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.4.2. Declaració . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 4.4.3. Accés als elements d’un registre . . . . . . . . . . . . . . . . . . . . . . . . 101 4.4.4. Inicialització. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 4.4.5. Taules de registres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 4.5. Unions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 4.5.1. Definició i declaració . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 4.5.2. Utilització. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 4.6. Enumeracions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 4.6.1. Definició i declaració . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

001-322 Computacio.indd 8 16/09/2009 13:44:23

ÍNDEx 9

4.6.2. Utilització. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 4.7. Definició de tipus (typedef) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 4.8. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

5. FuncIons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 5.1. Funcions i procediments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 5.1.1. Declaració i crida de funcions . . . . . . . . . . . . . . . . . . . . . . . . . . 121 5.1.2. Declaració i crida de procediments . . . . . . . . . . . . . . . . . . . . . . 123 5.2. Pas de paràmetres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 5.3. Àmbit de les variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 5.3.1. Variables estàtiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 5.4. Prototipus de les funcions. fitxers de capçalera. . . . . . . . . . . . . . . . . . . 129 5.4.1. Declaració i definició de funcions . . . . . . . . . . . . . . . . . . . . . . . 129 5.4.2. Divisió del programa en fitxers. Fitxers de capçalera . . . . . . . . 130 5.5. Funcions de llibreria estàndard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 5.6. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

6. apuntadors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 6.1. Conceptes bàsics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 6.1.1. Declaració d’apuntadors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 6.1.2. Operadors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 6.1.3. Precaucions amb la utilització d’apuntadors . . . . . . . . . . . . . . . 141 6.1.4. Valor NULL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 6.2. Pas de paràmetres per referència utilitzant apuntadors . . . . . . . . . . . . . 144 6.3. Apuntadors i taules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 6.3.1. Precaucions en la utilització de taules i apuntadors . . . . . . . . . . 150 6.3.2. Pas per referència de taules unidimensionals . . . . . . . . . . . . . . . 152 6.3.3. Pas per referència de taules multidimensionals . . . . . . . . . . . . . 156 6.4. Apuntadors a registres. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 6.4.1. Pas de registres per referència . . . . . . . . . . . . . . . . . . . . . . . . . . 158 6.5. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

7. memòrIa dInàmIca. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 7.1. Objectes dinàmics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 7.1.1. Creació d’objectes dinàmics: funció malloc . . . . . . . . . . . . . . 166 7.1.2. Destrucció d’objectes dinàmics: funció free . . . . . . . . . . . . . . 169 7.2. Taules dinàmiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 7.2.1. Retorn de taules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 7.2.2. Taules d’apuntadors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 7.3. Registres dinàmics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 7.3.1. Retorn de registres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 7.3.2. Taules dinàmiques de registres . . . . . . . . . . . . . . . . . . . . . . . . . 182 7.4. Estructures dinàmiques: llistes simplement enllaçades. . . . . . . . . . . . . 185 7.5. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

001-322 Computacio.indd 9 16/09/2009 13:44:23

10 PROGRAMACIÓ EN LLENGUATGE C

8. FItxers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 8.1. Conceptes bàsics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 8.2. El tipus fitxer (file) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 8.2.1. Obertura de fitxers: fopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 8.2.2. Tancament de fitxers: fclose . . . . . . . . . . . . . . . . . . . . . . . . . . 197 8.2.3. Lectura i escriptura de fitxers: aspectes generals . . . . . . . . . . . . 197 8.2.4. Lectura i escriptura de caràcters: fgetc i fputc . . . . . . . . . . . 198 8.2.5. Lectura i escriptura de línies de text: fgets i fputs . . . . . . . . 199 8.2.6. Lectura i escriptura amb format: fscanf i fprintf . . . . . . . . 202 8.2.7. Lectura i escriptura en fitxers binaris: fread i fwrite . . . . . . 205 8.2.8. Comprovació de final de fitxer: feof . . . . . . . . . . . . . . . . . . . . 208 8.3. Exercicis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209

annex a: estIl de codIFIcacIó . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

annex B: entorns de desenvolupament Integrat . . . . . . . . . . . . . . . . . . . . . . . 219 B.1. Elements dels IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 B.2. Creació de projectes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 B.2.1. Creació d’un projecte en Visual studio 2005 . . . . . . . . . . . . . . 221 B.2.2. Creació d’un projecte en Dev C++ . . . . . . . . . . . . . . . . . . . . . . 224 B.3. Compilació i execució de projectes . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 B.3.1. Compilació i execució de projectes en Visual studio 2005. . . . 226 B.3.2. Compilació i execució de projectes en DevC++ . . . . . . . . . . . . 227 B.4. Detecció i depuració d’errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 B.4.1. Depuració d’errors en Visual studio 2005 . . . . . . . . . . . . . . . . 229 B.4.2. Depuració d’errors en DevC++ . . . . . . . . . . . . . . . . . . . . . . . . . 230

annex c: proBlemes resolts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 C.1. Capítol 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 C.2. Capítol 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 C.3. Capítol 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 C.4. Capítol 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 C.5. Capítol 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 C.6. Capítol 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 C.7. Capítol 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306

001-322 Computacio.indd 10 16/09/2009 13:44:23

11

Pròleg

Aquest llibre sorgeix de la iniciativa i l’experiència docent dels professors de les assig-natures d’Algorismes i Programació i Llenguatges de Programació, de la titulació d’Enginyeria Informàtica de la Universitat Autònoma de Barcelona. Totes dues assig-natures s’imparteixen en el primer semestre dels estudis i, de manera conjunta, consti-tueixen la porta d’entrada de l’estudiant al món de la programació d’ordinadors.

Així, tot i que el contingut del llibre està bàsicament orientat a descriure el funciona-ment del llenguatge de programació C, s’aprofita també per introduir conceptes bàsics de la programació estructurada, amb la qual cosa es fusionen els continguts i objectius de les dues assignatures. Al llarg de les pàgines del llibre, el lector podrà trobar no sols una descripció rigorosa de l’ús del llenguatge C, sinó també l’explicació de conceptes fonamentals de programació que ajudaran a entendre millor tant la utilització del llen-guatge C com les estructures genèriques que s’utilitzen en la programació d’ordinadors.

El llibre repassa de manera exhaustiva les eines bàsiques que ens proporciona el llenguatge C i que corresponen a un curs introductori a la programació. Així, el primer capítol es dedica a fer una introducció a la història de llenguatge C per tal de posar en context el lector i ajudar a entendre el perquè del disseny del llenguatge C. A continu-ació, la resta de capítols del llibre van introduint els diferents tipus d’estructures de programació en C en dificultat progressiva: així, al capítol 2 s’introdueixen les instruc-cions i els tipus de dades més bàsics per construir programes molt senzills; al capítol 3, s’expliquen els tres principals tipus d’instruccions que s’utilitzen en la programació estructurada (seqüencial, condicional i iterativa) i que poden servir per construir qual-sevol programa; al capítol 4 es continua ampliant les possibilitats de representació d’informació, descrivint els tipus de dades compostes (taula, registre i enumerat); pos-teriorment, al capítol 5, s’explica com es pot dividir un programa en mòduls més petits (funcions i procediments) que es poden combinar i reaprofitar per construir un progra-ma més complex; el capítol 6 i el capítol 7 es dediquen al concepte d’apuntador i com es pot aprofitar per introduir noves maneres de representar la informació amb la me-mòria dinàmica; per acabar, el capítol 8 tracta de com podem guardar de manera per-manent la informació al disc utilitzant el concepte de fitxer. Finalment, el llibre es completa amb tres annexos. El primer resumeix els criteris i l’estil de programació que s’han seguit per escriure el codi dels exemples i exercicis que es presenten en el llibre; el segon es dedica a explicar dos entorns de desenvolupament concrets que ens poden ajudar a escriure, provar i corregir els programes en C i, finalment, el tercer annex conté les solucions de tots els exercicis que es proposen en cada capítol.

Al llarg de tot el llibre, la teoria es barreja amb exemples senzills que ajuden a en-tendre millor els conceptes del llenguatge. Així, en cada capítol el lector trobarà petits

001-322 Computacio.indd 11 16/09/2009 13:44:23

12 PROGRAMACIÓ EN LLENGUATGE C

exercicis que complementen l’explicació del llenguatge i que ajuden a entendre’n el funcionament. A més a més, cada capítol conté, també, una extensa col·lecció d’exer-cicis per complementar i portar a la pràctica la teoria exposada prèviament. Tots aquests exercicis estan resolts en un annex al final del llibre, de manera que el lector pot provar primer d’escriure el seu propi programa i comparar-lo després amb la solu-ció proposada. Tots els exemples i els exercicis proposats són fruit de l’experiència docent. s’han buscat problemes força didàctics que facin èmfasi en els punts del llen-guatge que, segons la nostra experiència, presenten més dificultats d’aprenentatge als estudiants. A més a més, tant els exemples com les solucions als exercicis inclouen comentaris detallats al codi explicant aquells aspectes del programa més rellevants o que poden ser més difícils de comprendre. El codi dels programes s’ha escrit seguint uns criteris comuns, explicats a l’annex A, que serveixen per introduir un estil i uns hàbits de programació adequats per construir programes clars, entenedors i fàcils de corregir i modificar.

Aquest llibre, doncs, no ha estat pensat simplement com una transcripció del llen-guatge C, sinó que s’ha seguit un enfocament didàctic, de manera que el lector pugui trobar fàcilment les analogies entre els conceptes basics de programació i la seva im-plementació en llenguatge C, així com exemples pràctics de com utilitzar-los correcta-ment. Pensem que, d’aquesta manera, l’ús del llenguatge no queda com un fet aïllat, sinó que cada instrucció té associat un sentit teòric i pràctic, de manera que se’n fan més fàcils la comprensió i la utilització.

001-322 Computacio.indd 12 16/09/2009 13:44:23

13

1Introducció

En aquest primer capítol comencem fent una mica d’història, explicant l’origen i de-senvolupament del llenguatge C. No és una ressenya simplement anecdòtica, sinó que ens pot ajudar a entendre alguna de les virtuts que l’han fet popular entre els programa-dors. Expliquem, també, les característiques principals de disseny del llenguatge, in-cloent-hi algunes de les seves limitacions. Per acabar, introduïm tot el que s’ha de tenir en compte per poder escriure i executar un programa en C: l’estructura bàsica i els principals elements sintàctics dels programes, així com els passos que cal seguir per escriure, compilar i executar qualsevol programa.

1.1 Història del llenguatge C

La història del llenguatge C és certament curiosa, ja que, a diferència d’altres llenguat-ges de programació, el C no va sorgir d’un procés de disseny deliberat, sinó que la seva aparició està lligada al disseny del sistema operatiu UNIx. Moltes de les característi-ques del llenguatge C que veurem més endavant en aquest capítol responen a necessi-tats de disseny de sistemes operatius. Per tant, considerem que és útil contextualitzar el llenguatge C dins d’aquest marc.

A finals de la dècada dels seixanta, el Massachusetts Institute of Technology (MIT), els laboratoris Bell d’AT&T i l’empresa General Electric estaven treballant en un sistema operatiu experimental anomenat MULTICs (Multiplexed Information and Computing service), dissenyat per ser executat en grans mainframes.1 Malauradament, ni la tecnologia ni les tècniques de programació no estaven prou madures en aquell moment per poder desenvolupar amb garanties un projecte de tal magnitud. Així que el projecte va ser un fracàs absolut, si només tenim en compte el seu objectiu original. Però si ens fixem en tot el que se’n va derivar, potser no ho va ser tant. Continuem, doncs, la història.

Tot i el fracàs del projecte MULTICs, alguns dels programadors de Bell (entre els quals hi havia Ken Thompson i Dennis Ritchie), van creure que encara hi havia alguna esperança de crear un sistema operatiu. Així, durant el 1969, van començar a pensar en alguna alternativa a MULTICs. En aquell moment l’equip dirigit per Thompson enca-ra treballava amb el mainframe on havien estat desenvolupant MULTICs. Veient que el desenvolupament del software sobre el mainframe es tornava impracticable donat

1. Mainframe: Ordinador de grans dimensions, potència i cost, utilitzat normalment per grans empreses o administracions per processar grans quantitats de dades.

001-322 Computacio.indd 13 16/09/2009 13:44:23

14 PROGRAMACIÓ EN LLENGUATGE C

que aquest també era usat per a tasques de l’empresa, Thompson va decidir continuar desenvolupant el nou sistema operatiu en una màquina més «petita», un PDP-7 de Di-gital Equipment. Així que, a finals del 1969, l’equip de Thompson va aconseguir exe-cutar una primera versió del nou sistema operatiu (embrió de UNIx) en el PDP-7. La programació havia estat feta íntegrament en llenguatge assemblador.2 Veient que el nou sistema operatiu començava a funcionar, Thompson va creure convenient crear un nou llenguatge de programació que fos de més alt nivell que l’assemblador. El nou llenguatge que va crear Thompson es va dir B, en referència al llenguatge en el qual estava inspirat, el BCPL. Durant el 1970, veient que el sistema operatiu avançava, l’equip de Thompson va decidir adquirir una màquina més potent, un PDP-11 (exem-ple 1). En aquelles mateixes dates, un altre programador de l’equip de Thompson, anomenat Brian Kernighan, proposava anomenar UNICs (en contraposició a MUL-TICs) el nou sistema operatiu. Probablement, la sonoritat del nou nom del sistema operatiu va fer que amb el temps aquest derivés a UNIx. Tanmateix, el llenguatge B que havia desenvolupat Thompson era poc adequat per ser portat a altres màquines. Així que, el 1971, Ritchie va decidir crear una extensió del llenguatge B, que va ano-menar C. A partir d’aquest moment, Dennis Ritchie i Brian Kernighan van anar desen-volupant el llenguatge C fins que, a principis del 1973, l’essència del llenguatge C que coneixem estava acabada. L’estiu del mateix any, utilitzant el llenguatge i el compila-dor de C que havien desenvolupat Kernighan i Ritchie, l’equip de Thompson va crear el primer UNIx escrit en C. A partir d’aquí els creadors del C van continuar treballant, fins que, el 1978, van publicar el que seria la primera guia sobre el llenguatge C, The C Programming Language.3

La popularitat del nou llenguatge es va estendre ràpidament i, ben aviat, molts fa-bricants d’ordinadors i universitats van desenvolupar els seus propis compiladors i traductors de llenguatge C. La seva gran popularitat va venir donada gràcies a què, tot i tenir una sintaxi senzilla (pels llenguatges del moment) i ser un llenguatge pensat per programar sistemes (recordem que originalment va ser creat per programar el sistema operatiu UNIx), s’adaptava molt bé a qualsevol tipus d’aplicació. La ràpida profusió de compiladors, en els quals cada fabricant incloïa les seves pròpies instruccions, va conduir a una certa falta de portabilitat dels programes que s’escrivien en C. Així que a mitjan dècada dels vuitanta, l’ANsI (American National standards Institute) va crear el comitè x3J11, amb l’objectiu d’estandarditzar el C. Del treball d’aquest comitè, n’han sortit dues versions de l’estàndard ANsI C, el C89, l’any 1989, i el C99, el 1999. Aquestes normes són les que segueixen gairebé tots els compiladors actuals i que tam-bé seguirem en aquest llibre.

Independentment de l’evolució pròpia del llenguatge, el C ha donat lloc, també, a variants que al final han derivat en nous llenguatges de programació. El més destacat és el llenguatge C++, dissenyat per Bjarne stroustrup amb l’objectiu d’afegir al C ca-racterístiques de la programació orientada a objectes.

2. Llenguatge assemblador: llenguatge de molt baix nivell, molt proper al llenguatge màquina i específic de cada processador.

3. B.W. KernIghan, D.M. rItchIe: El lenguaje de programación C. 2a edició, Prentice Hall, 1986.

001-322 Computacio.indd 14 16/09/2009 13:44:23

INTRODUCCIÓ 15

1.2 CaraCterístiques del llenguatge C

De cara al programador, les característiques principals del llenguatge C són les se-güents:

EficiènciaEl C és un llenguatge eficient. Gràcies al seu origen com a llenguatge per desenvolupar sistemes operatius, presenta característiques de baix nivell. Això significa que el seu disseny aprofita al màxim les possibilitats de la màquina sobre la qual s’executaran els programes. De fet, el llenguatge C incorpora alguns mecanismes que s’associen tradi-cionalment al llenguatge assemblador, cosa que permet, fins i tot, incrustar parts d’aquest llenguatge dins del mateix codi C. Així, els programes fets amb C tendeixen a ser compactes i a executar-se amb rapidesa.

PortabilitatUna de les característiques que fan més interessant el llenguatge C és el seu elevat grau de portabilitat. Això significa que un programa escrit en C en un sistema determinat pot executar-se en un altre sistema sense haver de fer canvis en el codi o introduint-hi molt poques modificacions, sovint només en les capçaleres dels fitxers.

Exemple 1. Dennis Ritchie (dret) i Ken Thompson treballant en una PDP-11

001-322 Computacio.indd 15 16/09/2009 13:44:24

16 PROGRAMACIÓ EN LLENGUATGE C

Potència i flexibilitatD’una banda el C és considerat un llenguatge potent. De fet, com ja s’ha comentat, la major part del sistema operatiu UNIx està escrit en llenguatge C. Fins i tot, compila-dors per altres llenguatges com FORTRAN, BAsIC, LIsP o Pascal també han estat escrits en llenguatge C. D’altra banda, permet escriure qualsevol altre tipus d’aplicació. És a dir, no està únicament restringit a aplicacions de sistemes.

Orientat al programadorEl C és un llenguatge que està pensat per adaptar-se a les necessitats del programador. Facilita l’accés al hardware i dóna al programador un control enorme sobre els recursos de la màquina. En general el C és un llenguatge petit. Compta amb sentències de con-trol senzilles i dóna suport a funcions. També posseeix una enorme selecció d’opera-dors, cosa que facilita la implementació d’una infinitat de tasques de manera senzilla. Totes aquestes eines donen molt de control al programador, però també molta respon-sabilitat, ja que pot induir a errors que en altres llenguatges no es podrien donar.

LimitacionsLes principals limitacions del llenguatge vénen derivades, bàsicament, de la seva sim-plicitat i de la responsabilitat que, tal com s’ha comentat al punt anterior, es dóna al programador per al control d’errors. se’n poden destacar les següents:

• ElCnoésunllenguatgefortamenttipat.Aixòvoldirqueelcompiladorfapo-ques comprovacions sobre la utilització correcta de les dades i, per tant, es poden produir confusions a l’hora d’interpretar i manipular les dades que donin lloc a errors en el resultat final.

• Senseunaprogramaciómetòdica,elsprogramestendeixenafer-sedifícilsdellegir. De fet, hi ha un concurs de programació obtusa en C,4 on es premia el codi més difícil d’interpretar (vegeu l’exemple 2). És obvi que aquest estil de progra-mació no és el més adequat per entendre el funcionament del programa i, evi-dentment, cal evitar-lo sempre.

• Comaconseqüènciadelspuntsanteriors,espodendonarerrorscomplicatsdetrobar i resoldre i, per això, al C sovint se li atribueix una depuració d’errors di-fícil.

Exemple 2. Exemple de codi obtús

/* Programa que permet anar introduint línies de comandes, les va desant i permet repetir la seva execució, modificar-les o seleccionar-ne una d'elles, utilitzant les tecles de funció F1-F9 */

#include <stdio.h>#define _ main(

4. www.ioccc.org

001-322 Computacio.indd 16 16/09/2009 13:44:24

INTRODUCCIÓ 17

#define _l ___l ___l ___l ___l ___)#define __l int#define ___l ___)*(#define ____l (_l],#define ____ 1#define ___ __+_____#define __ ____+____#define _____ __+____#define ______1 *(l__#define _____1 *__1%(__#define ____1 )?(#define ___1 _1&(____l __I[____l _I[____l*l__=_I,*l_=__I;__l _ __l _1,__l*__1){__l _l_;return ___1+ __ ____1 ___1 ____1*__1 = getchar()):__ ____1*__1<____?____:_____1+___ ____1 _____1+_____ ____1 _____1+____)____1 ___1+___+____ ____1 _____1 +__)____1 _____1 )____1 *l__)++:_____1+____)-____?______1 ++) :_____1+_____) -____?__:printf("%d\n",*l__):_____1+____+___ ____1 *l__) =*(l_++ ):__:___l ____- ____):_____1 +_____)-3?__-2:____:(___1+____+___ ____1 _____1)____1*l__)-- :_____1+__)?__:______1--):___l 0))?__:_ _1,__1+____)+____:(___1+____+___ )) &&* l__?_ ___1+___+___+__),__1+____)?_ ___1+11 )|(___1)?____:__- 2),__1):____-1 :(_l_=_ ___1 -____+___l ____),__1+____ )) ?_l_+_ ___1 )?_1 :___1+10)|(___1-____ ____1 __):0),__1+_l_):0):__:_1%(__ ____1 _1/(__)) ?_ scanf("%i",__1 ____1 _____):(___l _____)-____,__1 +____):_ _____,l_):__;}

1.3 estruCtura d’un programa en C

A l’exemple 3 podem veure un programa senzill escrit en C, on es poden observar els principals elements que trobarem en qualsevol programa en C:

Exemple 3. Codi amb els principals elements que es poden trobar en un programa escrit en C

/* Màxim.c: Programa d'exemple: Calcula el màxim de dos números introduïts per pantalla.*/

#include <stdio.h>

/* maxim: funció que retorna el més gran dels dos nºs donats */int maxim(int x, int y){ int max;

if (x>y) max=x; else max=y; return max;}

Directives de preprocessador

Definició de funcions

001-322 Computacio.indd 17 16/09/2009 13:44:24

18 PROGRAMACIÓ EN LLENGUATGE C

int main(){int n, m, max;

printf("Introdueix dos nombres:\n"); scanf("%d", &n); scanf("%d", &m); max=maxim(n,m); printf("El mes gran dels dos nombres es: %d", max); return 0;}

1. Programa principal: marca el punt on comença a executar-se el programa. L’exe-cució del programa sempre començarà amb la primera instrucció executable del bloc del programa principal (en l’exemple, la instrucció printf ("Introdueix dos nºs:\n")). El programa principal ve identificat per la capçalera int main(), i sempre ha d’estar present en qualsevol programa en C.Dins del programa principal podem trobar declaracions de variables (que serveixen per indicar amb quines dades treballarà el programa) i instruccions executables. En el programa de l’exemple declarem tres variables, n, m i max, per poder desar tres nombres enters. A les instruccions, demanem que s’introdueixin dos nombres i els desem a les variables n i m; calculem el màxim dels dos nombres, el desem a la variable max i el mostrem per pantalla.

2. Funcions: són blocs de programa independents que serveixen per fer una tasca concreta i específica (en l’exemple, calcular el màxim de dos nombres). Poden ser utilitzats des de qualsevol altre punt del programa tants cops com sigui necessari (en l’exemple, la funció maxim és utilitzada dins del programa principal, mitjançant la instrucció max=maxim(n,m)). Les funcions acostumen a retornar un valor que pot ser utilitzat després pel programa principal. En l’exemple, la funció retorna el màxim dels dos nombres (instrucció return max;) que, dins del programa princi-pal, es desa a la variable max i es mostra després per pantallaEn el tema 5 s’estudiaran tots els aspectes relacionats amb funcions.

3. Directives del preprocessador: les directives del preprocessador són totes les ins-truccions que comencen amb el símbol #. Acostumen a estar al principi del fitxer. No són instruccions pròpies del llenguatge C, sinó instruccions que són processades pel compilador quan fa la traducció del programa (vegeu la secció 1.5). Les més habituals són les definicions de constants i macros i la inclusió de fitxers de capça-lera. En l’exemple, la instrucció #include <stdio.h> permet incloure totes les defini-cions que hi ha en el fitxer de capçalera stdio.h. Els fitxers de capçalera acostumen a ser fitxers que contenen declaracions i definicions externes que són necessàries per executar el nostre programa. Normalment són fitxers que tenen extensió .h, i

Programa principal

001-322 Computacio.indd 18 16/09/2009 13:44:24

INTRODUCCIÓ 19

per poder-los utilitzar dins del programa els hem d’incloure amb la instrucció #in-clude. El fitxer stdio.h està present en qualsevol entorn de compilació en C i conté les definicions necessàries per poder utilitzar les funcions que permeten llegir i escriure valors per pantalla (scanf i printf). És a dir, per poder utilitzar les funcions scanf i printf, hem d’incloure el fitxer de capçalera stdio.h. Al llarg d’aquest manual, anirem introduint, a mesura que es vagin necessitant, di-verses funcions que estan definides en fitxers de capçalera. En cada cas indicarem quin fitxer de capçalera s’ha d’incloure per poder-les utilitzar. Al tema 5 es pot trobar un resum de les principals funcions que estan definides als fitxers de capça-lera.

4. Comentaris: són parts del programa que no s’executen i que serveixen per afegir explicacions i aclariments sobre el funcionament del programa, com per exemple, per a què serveix una funció o una variable, o explicar trossos de codi complicats. Els comentaris queden definits com qualsevol text inclòs entre els delimitadors /* i */, o qualsevol text a continuació del delimitador // si el comentari ocupa només una línia. És important acostumar-se a posar comentaris als programes per facilitar-ne la revisió i correcció.

A més d’aquests 4 elements que apareixeran pràcticament en qualsevol programa en C, n’hi ha d’altres que també podem trobar en els programes en C (bàsicament, de-claracions globals de tipus, variables, constants, o prototipus de funcions) i que anirem explicant en els capítols següents. A mesura que els anem introduint explicarem també on s’han de posar dins de l’estructura general de programa que acabem de definir.

1.4 elements sintàCtiCs d’un programa en C

Com es pot veure a l’exemple 3, en C hi ha alguns símbols amb un significat especial que s’utilitzen per delimitar alguna part del programa o marcar la presència d’algun element específic del llenguatge. La taula 1 resumeix els principals caràcters i símbols que tenen algun significat particular dins d’un programa en C.

Taula 1. Símbols especials de C

Símbol Significat

# Indicador de directives de precompilació (#define, #include, ...). Vegeu la secció 1.3.

/* ... */ Delimitadors dels comentaris.

; Final de sentència. Totes les sentències han d’acabar en punt i coma.

{ ... } Delimitadors d’un bloc d’instruccions que formen una unitat, com, per exemple, el programa principal o les funcions.

001-322 Computacio.indd 19 16/09/2009 13:44:24

20 PROGRAMACIÓ EN LLENGUATGE C

Paraules reservadesLes paraules reservades són totes aquelles paraules que tenen algun significat particu-lar dins d’un programa en C, ja sigui com a instruccions o com a part de declaracions. A l’exemple 3, totes les paraules reservades apareixen en negreta. A la taula 2 es mos-tra la llista de totes les paraules reservades de C.5

Taula 2. Paraules reservades de C

auto double inline sizeof volatile

break else int static While

case enum long struct _Bool

char extern register switch _Complex

const float restrict typedef _Imaginary

continue for return union

default goto short unsigned

do if signed void

Al llarg del manual s’anirà explicant el significat de la majoria d’aquestes paraules reservades.

IdentificadorsEls identificadors són seqüències de caràcters que donen nom a tot el que s’ha de de-clarar dins d’un programa en C, com, per exemple, variables, constants o funcions. Els identificadors els pot definir el programador com vulgui utilitzant caràcters alfabètics en majúscules i minúscules, els deu dígits decimals i el caràcter subratllat «_», seguint les normes que s’expliquen a la secció 2.3.

Sentències (instruccions)Les sentències en C estan formades per un conjunt d’identificadors i paraules reserva-des separades acabades en punt i coma.

OperadorsEls operadors son tots els símbols que representen qualsevol operació que es pot fer amb constants i variables. Com veurem al capítol 2, hi ha tres grans tipus d’operadors en C: aritmètics, lògics i relacionals.

1.4.1 Estil d’escriptura d’un programa en C

A l’hora d’escriure un programa en C, és important l’estil de codificació que utilitzem. Els programes han de ser clars i entenedors per facilitar-ne la modificació i correcció.

5. Llista de paraules reservades definides a l’estàndard C99.

001-322 Computacio.indd 20 16/09/2009 13:44:24

INTRODUCCIÓ 21

Hem de tenir en compte que un programador es passa moltes més hores modificant i corregint programes ja fets que no pas escrivint programes nous.

Com a contraexemple agafem l’exemple 4 (com a cas extrem podríem agafar els exemples de la pàgina 16):

Exemple 4. Codi sense un bon estil de programació

#include <stdio.h>int funcioA (int x, int y){int m; if (x>y) m=x;else m=y;return m;}int main(){int n, m, a;printf ("Introdueix dos numeros:\n");scanf ("%d", &n);scanf ("%d", &m);a=funcioA(n,m); printf ("El mes gran dels dos numeros es: %d", a); return 0;}

Aquest programa és sintàcticament correcte, es pot compilar i executar i fa exac-tament el mateix que l’exemple de la pàgina 17. Evidentment, a primer cop d’ull i sense necessitar cap noció de programació, podem veure que és molt més difícil d’en-tendre i saber què fa el programa, tal com està escrit en aquesta segona versió que en la primera.

Algunes regles que cal seguir a l’hora d’escriure un programa són les següents:

• Separardemaneraclaraelsdiferentsblocsdelprograma(funcions,programaprincipal, declaracions de variables, etc.).

• Utilitzarcomentarisperexplicarlesfuncions,variablesopartsdelprogramaquepuguin ser confuses. D’altra banda, hem d’evitar omplir el programa de comen-taris superflus com podria ser el següent exemple:

/* si x és més gran que y assignem a max el valor de x. Si no, assignem a max el valor de y. */if (x>y) max=x;else max=y;

• Utilitzarlestabulacionsialineacionsperfermésclarelprograma.Toteslesinstruccions d’un mateix bloc haurien d’estar alineades, i els símbols «{» i «}» que corresponguin a l’inici i al final del bloc també haurien d’estar alineats.

• Utilitzarnomsdevariablesifuncionsquetinguinaveureamblautilitzacióquese’n fa. Per exemple, si una funció calcula el màxim de dos nombres, és millor anomenar-la maxim que funcioA.

001-322 Computacio.indd 21 16/09/2009 13:44:24

22 PROGRAMACIÓ EN LLENGUATGE C

Els exemples que hi ha en aquest manual poden servir com a referència de les re-gles d’estil que s’han de seguir en escriure programes en C. Totes aquestes regles s’han resumit com a manual d’estil a l’annex A.

1.5 CompilaCió d’un programa en C

El llenguatge C és un llenguatge compilat. Això vol dir que per poder executar un pro-grama escrit en C, primer l’hem de traduir a llenguatge màquina, que és l’únic llen-guatge que l’ordinador pot entendre directament. El procés de traduir un programa escrit en llenguatge C a codi màquina executable per l’ordinador és el que s’anomena compilació.

A l’exemple 5 s’il·lustra el procés de compilació d’un programa en C. Els fitxers amb el codi del programa s’anomenen fitxers de codi font i acostumen a tenir extensió .c. Els fitxers amb extensió .h acostumen a ser fitxers de capçalera que inclouen les declaracions de funcions necessàries per executar el programa. És habitual que un mateix programa estigui dividit en diversos fitxers de codi font i que utilitzi diferents fitxers de capçalera, tal com es mostra a la figura. En el cas de programes senzills, tot el programa estarà en un sol fitxer de codi font, i el procés serà exactament el mateix, però només amb un fitxer involucrat en tot el procés.

L’exemple 5 mostra que el procés de compilació consta, en realitat, de tres passos diferents: precompilació, compilació i enllaçat:

• Precompilació: el precompilador s’encarrega de processar tot el que són ins-truccions (directives) de precompilació. Com s’ha explicat a la secció 1.3, les directives de precompilació són totes aquelles instruccions precedides pel sím-bol # (#define, #include). El resultat de la precompilació és el mateix fitxer de codi font (amb extensió .c), però modificat per incloure-hi l’efecte d’aques-tes directives.La directiva de precompilació més utilitzada és la instrucció #include, que serveix per incorporar al programa declaracions de funcions que estan en fitxers de capçalera amb extensió .h (com per exemple, les funcions scanf i printf, que estan declarades al fitxer stdio.h). En aquest cas, es modifica el fitxer font afegint-hi totes les definicions que hi ha en el fitxer de capçalera.

• Compilació: aquest és el pas de traducció pròpiament. El compilador analitza el codi font escrit en C i el tradueix al codi màquina que pot entendre l’ordinador. En el procés de traducció comprova que el programa en C sigui sintàcticament correcte, és a dir, que segueixi totes les regles del llenguatge C. En el cas que trobi errors, el procés de traducció s’atura i es mostren els errors que s’han trobat i en quina línia de codi estan, perquè es puguin corregir. Més endavant, a la sec-ció 1.5.1, es comenten amb detall els possibles tipus d’errors que es poden pro-duir en un programa en C. El resultat d’aquest pas de compilació és un fitxer de codi objecte (extensió .o ó .obj) per cada fitxer de codi font que es processa.

001-322 Computacio.indd 22 16/09/2009 13:44:24

INTRODUCCIÓ 23

Exemple 5. Esquema de precompilació, compilació i enllaçat de varis fitxers .c i .h

.C

Precompilador

Compilador

Enllaçador (linker)

.h .C .h .C

.C .C .C

.O .O .O

.lib

.exe.lib

• Enllaçat: aquest últim pas s’encarrega d’ajuntar tots els fitxers de codi objecte que s’han generat al pas de compilació, en un únic fitxer executable amb exten-sió .exe. A més a més, el procés d’enllaçat també incorpora tot el codi necessa-ri de les llibreries del sistema. Les llibreries del sistema són fitxers que existeixen en tot entorn C, que contenen el codi objecte ja compilat de les funcions que es-tan predefinides en els fitxers de capçalera (com, per exemple, les funcions printf i scanf). Durant el procés d’enllaçat també es poden produir errors, que s’expliquen també a la secció 1.5.1.

Com que aquestes tres operacions es repeteixen diverses vegades durant el desen-volupament d’un programa, molts fabricants de software proporcionen entorns de de-

001-322 Computacio.indd 23 16/09/2009 13:44:24

24 PROGRAMACIÓ EN LLENGUATGE C

senvolupament en els quals es pot realitzar tot el cicle complet, des de l’edició del codi font fins al seu enllaçat, passant per la compilació. Aquests sistemes es denominen entorns de desenvolupament integrat (IDE, Integrated Development Environment). A l’annex B s’explica el procés de creació, edició, compilació i enllaçat dels programes en C, utilitzant dos d’aquests IDE, el Microsoft Visual studio 2005 i el Dev-C++ 4.9.8.0.

1.5.1 Detecció i depuració d’errors

Normalment, un programa mai no funcionarà correctament a la primera. Quan s’escriu el codi d’un programa és fàcil que s’hi introdueixin errors, que podem dividir en tres classes: errors de compilació, errors d’enllaçat i errors d’execució. A continuació veu-rem en què consisteix cada tipus d’error i com cal tractar-los.

1.5.1.1 Errors de compilacióEls errors de compilació es produeixen a causa d’errors sintàctics. És a dir, el programa no s’ajusta estrictament a les regles del llenguatge C. Alguns exemples d’aquests tipus d’error podrien ser: escriure una instrucció de manera incorrecta, deixar-nos de posar un punt i coma al final d’una instrucció, no tancar un parèntesi, no declarar una varia-ble, escriure malament el nom d’una variable o funció, etc. Aquest tipus d’errors es detecta a la fase de compilació. si el compilador detecta errors de compilació, ja no passa a la fase d’enllaçat i, per tant, no genera cap fitxer executable.

Hi ha dos tipus de missatges d’errors de compilació: les advertències (o warnings) i els errors:

• Les advertències (warnings) són errors que no violen les regles sintàctiques del llenguatge i, per tant, no impedeixen la compilació completa del programa. En aquest tipus d’error, el compilador mostra un missatge, però continua el procés de compilació i d’enllaçat, i pot generar el fitxer executable. simplement ens avisa que hi ha quelcom estrany que potencialment pot ser font d’algun error, encara que no necessàriament ho ha de ser. Aquests errors s’han d’analitzar i decidir si s’han de corregir o no. Per exemple, si en el codi del programa intentem sumar un nombre enter amb un nombre real, el compilador donarà un missatge d’advertència. L’operació és cor-recta segons les regles de sintaxi del C, però el compilador ens adverteix de la diferència de tipus i que, per tant, podem perdre informació fent aquesta opera-ció. És responsabilitat del programador decidir si vol deixar el codi tal com està o modificar-lo per fer que el tipus dels dos nombres a sumar coincideixi.

• Elserrors de sintaxi es detecten quan hi ha alguna part del codi del programa que no s’ajusta a les regles sintàctiques del llenguatge. En aquests casos, el com-pilador no sap com ha de traduir el programa. Per tant, són errors que impedei-xen passar a la fase d’enllaçat i generar el fitxer executable. Aquest tipus d’errors s’han de corregir sempre.

001-322 Computacio.indd 24 16/09/2009 13:44:25

INTRODUCCIÓ 25

1.5.1.2 Errors d’enllaçatsón errors que es detecten a la fase d’enllaçat quan no es pot trobar el codi d’alguna de les funcions que s’utilitzen en el programa. són bastant menys habituals que els errors de compilació. Normalment, es produeixen o bé quan ens hem oblidat d’escriure el codi d’una funció, o bé quan el compilador no pot trobar el fitxer de llibreria (extensió .lib) amb el codi objecte associat a algun dels fitxers de capçalera (extensió .h) que incloem al programa (sovint per algun problema de directoris o configuració del com-pilador).

1.5.1.3 Errors d’execuciósón els errors que es produeixen mentre s’està executant el programa. El codi del pro-grama s’ha compilat i enllaçat sense errors, però no funciona correctament o no fa el que volem que faci. Aquests són els errors més difícils de detectar i corregir, ja que no apareix cap missatge d’error ni cap indici d’on pot estar localitzat. simplement, el programa no dóna els resultats esperats.

L’única manera d’assegurar-nos que el programa no té errors és provant totes les seves opcions amb tots els casos possibles d’entrada de dades i comprovar si el resultat obtingut és l’esperat. No n’hi ha prou amb executar el programa un sol cop per assegu-rar-ne el bon funcionament. s’ha de portar a terme un testeig exhaustiu que consti d’un bon joc de proves que permeti comprovar el funcionament del programa en totes les situacions possibles, mirant de preveure aquells casos concrets que poden fàcilment provocar errors d’execució.

Una vegada s’ha detectat la presència d’errors, el pas següent és corregir-los. La correcció d’aquests errors no és fàcil, perquè tampoc no tenim cap indicació directa de la seva localització. Per trobar-los i corregir-los s’ha de fer un seguiment detallat del programa tot executant les instruccions pas a pas i observant el valor que van prenent les diverses variables per tal d’arribar a deduir què és el que està malament. Aquest procés és el què es coneix com a depuració (debugging) del programa.

Tot compilador de C té una utilitat, el depurador (Debugger), que ajuda a detectar i corregir els errors d’execució que hi pugui haver en un programa. Amb el depurador podem fer que el programa s’aturi en una línia determinada de codi o que es vagi exe-cutant instrucció a instrucció o que ens ensenyi el valor de les variables en un punt determinat del programa. D’aquesta manera podem anar inspeccionant què va fent el programa en cada moment, i podem detectar quan fa alguna acció incorrecta, i tenir alguna pista de quin pot ser l’error que hi ha. Tot i l’ajuda del depurador, el procés de correcció d’errors d’execució és, moltes vegades, un procés lent i que requereix paci-ència i anar adquirint experiència a mesura que es van fent programes. A l’annex B, hi trobareu una descripció de les eines de depuració d’errors que inclouen els IDE expli-cats en aquest manual.

001-322 Computacio.indd 25 16/09/2009 13:44:25

27

2Tipus de dades simples

En aquest capítol s’introdueixen les eines que ofereix el llenguatge C per desar i mani-pular informació simple: caràcters, nombres enters i nombres reals. Aquests són els tipus de dades bàsiques amb què es pot treballar en C i les que utilitzarem per escriure els primers programes. Més endavant, a partir del capítol 4, ampliarem les possibilitats de representar informació als programes amb la utilització de taules, registres i memò-ria dinàmica.

Començarem explicant els conceptes de variable, tipus de dades i constant, i veu-rem com es poden declarar en C. A continuació, mostrarem com es pot utilitzar la in-formació que desem a les variables i constants: primer, veurem les operacions d’entrada i sortida, que ens permetran, respectivament, obtenir el valor d’una variable i mostrar aquest valor per pantalla. Després, explicarem tot el que està relacionat amb la manipulació del valor de les variables mitjançant operacions aritmètiques, lògiques o de comparació.

2.1 Variables

Les variables són l’eina principal que utilitzen la majoria dels llenguatges de progra-mació per desar i manipular informació. Començarem definint el concepte genèric de variable i descrivint els tipus de dades que es poden utilitzar per definir variables en C. A continuació veurem com declarar i inicialitzar les variables.

2.1.1 Concepte de variable

Una variable es pot definir com una posició de memòria on es pot desar un valor d’un tipus determinat, que pot ser modificat des del codi del programa. Una variable queda identificada per un nom, que ens permet referenciar-la i utilitzar-la dins del programa, i per un tipus de dades, que ens indica quins valors podem desar a la variable i quines operacions hi podem fer.

2.1.2 Tipus de dades bàsiques en C

El tipus de dades determina dues propietats fonamentats de qualsevol variable. D’una banda, defineix el domini de la variable, és a dir, el conjunt de valors que podem desar

001-322 Computacio.indd 27 16/09/2009 13:44:25

28 PROGRAMACIÓ EN LLENGUATGE C

a la variable i per tant, també la mida de memòria que ocupa. D’altra banda, especifica el conjunt d’operadors que es poden aplicar a la variable. D’aquesta manera, el tipus de dades determina completament tot el que pot fer una variable.

En C hi ha quatre tipus de dades bàsiques: enter, real, caràcter i lògic.6

El tipus enter: intserveix per desar valors numèrics de tipus enter. Hi ha, però, diferents variants del ti-pus enter. Cada variant del tipus enter permet desar un rang de valors diferent, ocupant també una mida de memòria diferent.

D’una banda, podem indicar si volem desar nombres enters amb signe (positius i negatius) o sense signe (només positius). En el primer cas el tipus s’especifica com a signed int, mentre que en el segon cas, com a unsigned int. Per defecte, el tipus int representa nombres enters amb signe.

D’altra banda, podem també especificar la mida de memòria que volem reservar per desar el nombre enter (i per tant, el rang de valors de la variable). si volem treballar amb nombre enters més petits utilitzarem el tipus short int i si necessitem nombres enters més grans, utilitzarem long int o long long int, en funció del rang de va-lors. La mida exacta de memòria que s’utilitza en cada cas depèn del sistema operatiu i del compilador.

Taula 3. Resum del tipus de dades enter

Identificador de tipus Mida Rang de valors

short / short int 2 bytes MinMax

-32.76832.767

int 4 bytes MinMax

-2.147.483.6482.147.483.647

long / long int 4 bytes MinMax

-2.147.483.6482.147.483.647

long long / long long int 8 bytes MinMax

-9.223.372.036.854.775.8089.223.372.036.854.775.807

unsigned short / unsigned short int

2 bytes MinMax

065535

unsigned / unsigned int 4 bytes MinMax

04.294.967.295

unsigned long / unsigned long int

4 bytes MinMax

04.294.967.295

unsigned long long / unsigned long long int

8 bytes MinMax

018.446.744.073.709.551.615

6. El tipus de dades lògic no existia a les primeres versions del llenguatge C. Va ser introduït a l’estàndard C99 i no tots els compiladors de C el suporten.

001-322 Computacio.indd 28 16/09/2009 13:44:25

TIPUs DE DADEs sIMPLEs 29

Totes aquestes variants es poden utilitzar conjuntament per formar qualsevol com-binació possible de nombres amb signe o sense signe, petits o grans. La taula 3 resu-meix totes les variants de tipus enter amb la mida de memòria que ocupen i el rang de valors possibles.7

El tipus real: float / doubles’utilitza per desar valors numèrics de tipus real. Igual que passa amb el tipus enter hi ha diferents variants del tipus real (float, double, long double) que es diferencien en la mida de memòria que ocupen (i per tant, en el rang de valors possibles que poden representar), en la precisió i en el nombre de dígits decimals que s’utilitzen per repre-sentar el nombre real.

El tipus real es pot utilitzar també per desar nombres enters que siguin més grans que el valor màxim que permet representar el tipus long long int.

El format en què normalment s’expressen els nombres reals és una forma especial de la notació científica habitual. Així, per exemple, 3.5·108 s’expressa com 3.5E+08.

La taula 4 resumeix la precisió (diferència mínima entre dos nombres reals conse-cutius), el nombre de dígits decimals, la mida de memòria ocupada i el rang (valor mínim i màxim que es pot representar) dels diferents tipus de dades reals8.

Taula 4. Resum del tipus de dades real

Tipus Mida Precisió Nre. decimals Rang de valors

Float 4 bytes 1.19209E-007 6 MinMax

1.175494E-0383.402823E+038

Double 8 bytes 2.22045E-016 15 MinMax

2.225074E-3081.797693E+308

long double 8 bytes 2.22045E-016 15 MinMax

2.225074E-3081.797693E+308

El tipus caràcter: chars’utilitza per desar caràcters alfanumèrics, és a dir, lletres, dígits, signes de puntuació, etc. Ocupa sempre 1 byte de memòria. Els caràcters es codifiquen amb un valor numè-ric segons la codificació AsCII9 que mostra la taula 5. Aquest valor numèric és el que es desa a la posició de memòria de la variable.

7. Les constants que defineixen els valors mínim i màxim per cada variant del tipus enter estan declarades al fitxer de capçalera limits.h. Els valors de mida i rang de valors de la taula s’han obtingut amb el compilador de Microsoft Visual studio 2005 en un entorn Windows xP.

8. Les constants que defineixen els valors de precisió i rang dels nombres reals estan declarades al fitxer float.h. Els valors de mida, precisió i rang de valors que es mostren a la taula s’han obtingut amb el compilador de Microsoft Visual studio 2005 en un entorn Windows xP.

9 La codificació AsCII (American Standard Code for Information Interchange) és la codificació estàn-dard més utilitzada per convertir caràcters a un codi numèric. Els primers 32 codis són codis de control (com per exemple, el salt de línia) que no es corresponen a cap caràcter imprimible.

001-322 Computacio.indd 29 16/09/2009 13:44:25

30 PROGRAMACIÓ EN LLENGUATGE C

El C aprofita aquesta codificació numèrica i permet tractar les variables de tipus char també com si fossin nombres enters d’un byte de mida. D’aquesta manera una variable de tipus char també es pot utilitzar per representar un nombre enter en el rang de -128 a 127 en el cas de nombres amb signe i de 0 a 255 en el cas de nombres sen-se signe (unsigned char). D’aquesta manera a una variable de tipus char se li po-den aplicar també tots els operadors que permeten manipular nombres enters.

Taula 5. Taula de caràcters ASCII amb els seus codis corresponents

codi char codi char codi char codi char codi char codi char codi char32 64 @ 96 ` 128 € 160 192 À 224 à

33 ! 65 A 97 a 129 □ 161 ¡ 193 Á 225 á

34 " 66 B 98 b 130 ‚ 162 ¢ 194 Â 226 â

35 # 67 C 99 c 131 ƒ 163 £ 195 Ã 227 ã

36 $ 68 D 100 d 132 „ 164 ¤ 196 Ä 228 ä

37 % 69 E 101 e 133 … 165 ¥ 197 Å 229 å

38 & 70 F 102 f 134 † 166 ¦ 198 Æ 230 æ

39 ' 71 G 103 g 135 ‡ 167 § 199 Ç 231 ç

40 ( 72 H 104 h 136 ˆ 168 ¨ 200 È 232 è

41 ) 73 I 105 i 137 ‰ 169 © 201 É 233 é

42 * 74 J 106 j 138 Š 170 ª 202 Ê 234 ê

43 + 75 K 107 k 139 ‹ 171 « 203 Ë 235 ë

44 , 76 L 108 l 140 Œ 172 ¬ 204 Ì 236 ì

45 - 77 M 109 m 141 □ 173 205 Í 237 í

46 . 78 N 110 n 142 Ž 174 ® 206 Î 238 î

47 / 79 O 111 o 143 □ 175 ¯ 207 Ï 239 ï

48 0 80 P 112 p 144 □ 176 ° 208 Ð 240 ð

49 1 81 Q 113 q 145 ‘ 177 ± 209 Ñ 241 ñ

50 2 82 R 114 r 146 ’ 178 ² 210 Ò 242 ò

51 3 83 S 115 s 147 “ 179 ³ 211 Ó 243 ó

52 4 84 T 116 t 148 ” 180 ´ 212 Ô 244 ô

53 5 85 U 117 u 149 • 181 µ 213 Õ 245 õ

54 6 86 V 118 v 150 – 182 ¶ 214 Ö 246 ö

55 7 87 W 119 w 151 — 183 · 215 × 247 ÷

56 8 88 X 120 x 152 ˜ 184 ¸ 216 Ø 248 ø

57 9 89 Y 121 y 153 Ö 185 ¹ 217 Ù 249 ù

58 : 90 Z 122 z 154 š 186 º 218 Ú 250 ú

59 ; 91 [ 123 { 155 › 187 » 219 Û 251 û

60 < 92 \ 124 | 156 œ 188 ¼ 220 Ü 252 ü

61 = 93 ] 125 } 157 □ 189 ½ 221 Ý 253 ý

62 > 94 ^ 126 ~ 158 ž 190 ¾ 222 Þ 254 þ

63 ? 95 _ 127 □ 159 Ÿ 191 ¿ 223 ß 255 ÿ

001-322 Computacio.indd 30 16/09/2009 13:44:25