Sie sind auf Seite 1von 100

Programar en C

ndice general
1

Programacin en C/Primer programa en C

1.1

Pre-requisitos para la compilacin de programas . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.2

Compilacin de programas segn la plataforma

. . . . . . . . . . . . . . . . . . . . . . . . . . .

1.2.1

Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.2.2

GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.3

Diseccionando el Hola Mundo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.4

Comentarios

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Programacin en C/Tipos de datos

2.1

Historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.2

Enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.3

Flotantes

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.4

Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Programacin en C

3.1

Prlogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1.1

Para quin es este libro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1.2

Por qu otro manual de C?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Anexos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.3

Enlaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.4

Licencia y autores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

3.5

Cmo contribuir a este WikiLibro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

3.2

ndice de contenidos
3.2.1

Programacin en C/Expresiones

11

Programacin en C/Interaccin con el usuario

13

5.1

Imprimir por pantalla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

5.2

Lectura de datos del teclado

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

La funcin scanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

5.2.1
6

Programacin en C/Instrucciones de control

15

6.1

La estructura condicional if ... else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

6.1.1

16

Operadores de comparacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i

ii

NDICE GENERAL
6.1.2

Operadores lgicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16

6.1.3

Evaluacin de cortocircuito

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16

6.2

La estructura condicional abierta y cerrada switch ... case . . . . . . . . . . . . . . . . . . . . . . .

17

6.3

El bucle while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

6.4

El bucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

6.5

El bucle do...while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

6.6

La sentencia goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

Programacin en C/Uso de funciones

19

7.1

Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

7.1.1

La sentencia return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

7.1.2

Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

7.1.3

Declaracin y denicin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

7.1.4

Paso de Parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

7.1.5

Variables Locales y Globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

Funciones Recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

7.2.1

Recursividad indirecta o recursin mutua . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

7.2.2

Recursin versus Iteracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

7.2

Programacin en C/Vectores

24

Programacin en C/Cadenas de caracteres

26

10 Programacin en C/Manejo de archivos

28

10.1 Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

28

10.1.1 fopen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

28

10.1.2 fclose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

10.1.3 feof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

10.1.4 rewind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

10.2 Lectura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

10.2.1 fgetc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

10.2.2 fgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

10.2.3 fread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

10.2.4 fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

10.3 Escritura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

10.3.1 fputc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

10.3.2 fputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

10.3.3 fwrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

10.3.4 fprintf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

11 Programacin en C/Estructuras y Uniones

33

11.1 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

11.1.1 Estructuras Anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

NDICE GENERAL

iii

11.2 Uniones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

11.3 Enumeraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

12 Programacin en C/Punteros

37

13 Concepto

38

14 Declarando punteros

39

15 Explicacin

40

16 Operadores

41

17 Operaciones Aritmticas

42

18 Punteros Constantes

43

19 Punteros Genericos

44

20 Punteros y Matrices

45

21 Punteros a cadenas de caracteres

46

22 Matrices de Punteros

47

23 Punteros a Punteros

48

24 Matrices de punteros a cadenas de caracteres

49

25 Ejemplos

50

25.0.1 Asignando valores a punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

26 Programacin en C/Matrices Dinamicas

52

27 Concepto

53

28 Matrices Dinamicas de Dos Dimensiones

54

29 Programacin en C/Algoritmos y Estructuras de Datos

55

30 Concepto

56

31 Metodo de Burbuja (Buble Sort)

57

31.1 Explicacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

31.2 Ejemplos

58

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32 Mtodo de Insercin

59

32.1 Concepto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

32.2 Explicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

32.3 Ejemplos

59

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iv

NDICE GENERAL

33 Mtodo QuickSort

60

33.1 Concepto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

33.2 Explicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

33.3 Ejemplos

60

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34 Programacin en C/El proceso de compilacin

61

35 Los pasos del proceso

62

35.1 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36 Programacin en C/Los errores

62
64

36.1 Chequeos del C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


37 Programacin en C/Herramientas externas tiles
37.1 Sistemas de construccin de cdigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38 make

64
65
65
66

38.1 Sistemas de control de versiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


39 Programacin en C/Ejemplos

67
68

39.1 El Hola Mundo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68

39.2 El Hola Mundo comentado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68

39.3 El Hola Mundo estructurado en funciones

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68

39.4 Ejemplo de clculo con enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69

39.5 Control de acceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69

40 Programacin en C/Glib

70

40.1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

40.2 Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

40.3 Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

41 Programacin en C/Referencia

72

41.1 Tipos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

41.2 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

41.3 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

41.4 Bibliotecas y funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

74

41.5 Preprocesador de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

42 Programacin en C/Cmo compilar un programa


42.1 Un poco ms sobre compilacin

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76
76

43 Programacin en C/Cdigo embebido

77

44 Programacin en C/Recursos en la red

78

44.1 Manuales, tutoriales y otros documentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

44.1.1 Espaol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

NDICE GENERAL

44.1.2 Ingls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

44.2 Compiladores e IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

45 Programacin en C/Bibliografa

79

45.1 Bibliografa en espaol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

79

45.2 Bibliografa en ingls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

79

46 Fundamentos de programacin

80

47 Programacin en C/Fundamentos de programacin

81

47.1 Deniciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

47.2 Tipos de lenguajes

81

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47.2.1 Esquemas de programacin

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

47.2.2 Alto o bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82

47.2.3 Compilados o interpretados

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82

47.3 Estructura de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82

48 Programacin en C/Historia de C

83

48.1 Evolucin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

83

48.2 Ms informacin

84

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49 Programacin en C/Introduccin

85

49.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

49.2 Nota sobre la exactitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

49.3 Estndar utilizado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

49.4 Para los principiantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

49.5 Para los ms avanzados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

49.6 Requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

49.7 Herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

49.8 Windows

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

49.9 GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87

50 Programacin en C/Manejo dinmico de memoria


50.1 Memoria dinmica

88

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

50.2 Memoria esttica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

50.3 Diferencias, ventajas y desventajas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

50.4 El lenguaje C y el manejo de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

50.4.1 malloc

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

50.4.2 calloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

50.4.3 realloc

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

50.4.4 free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

50.5 Buenas prcticas

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

50.6 Text and image sources, contributors, and licenses . . . . . . . . . . . . . . . . . . . . . . . . . .

92

50.6.1 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

92

vi

NDICE GENERAL
50.6.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93

50.6.3 Content license . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93

Captulo 1

Programacin en C/Primer programa en C


En el libro El Lenguaje de Programacin C, Kernighan y Ritchie introdujeron al lenguaje C utilizando un sencillo
programa que mostraba un saludo por la pantalla. Desde entonces se hizo tradicin empezar con cualquier lenguaje
de programacin con el ejemplo del Hola mundo.
En particular en C se involucran muchas partes y sintaxis del lenguaje, por lo cual es especialmente til verlo como
el primer ejemplo de programacin en C.
Ejemplo: Hola mundo
/* Inclusin de archivos */ #include <stdio.h> /* Funcin principal */ int main (int argc,char **argv) { /* Impresin
por pantalla y salida del programa*/ printf(Hola mundo\n); return 0; }
Para poder editar y ejecutar este programa ser necesario utilizar algn editor y luego un compilador, como se explic
en la seccin Herramientas necesarias.
Si se tiene el compilador gcc en un entorno UNIX o GNU/Linux, la forma sencilla de compilar y ejecutar ser:
$ gcc holamundo.c $ ./a.out Hola Mundo $
Es decir que el compilador genera un archivo, en este caso llamado a.out, y la salida generada por ese archivo es
Hola mundo. A continuacin una explicacin detallada sobre el proceso de compilacin del programa, y luego un
anlisis lnea por lnea del contenido de este ejemplo.

1.1 Pre-requisitos para la compilacin de programas


Como ya se mencion, ser necesario tener instalado el compilador y un editor o entorno de desarrollo que permitan
escribir el cdigo a compilar. Para ms informacin ver la seccin Herramientas necesarias.
El cdigo a compilar debe guardarse con un nombre que represente al programa en cuestin y la extensin .c. En el
caso del ejemplo del Hola mundo, el archivo puede llamarse hola.c.
En las explicaciones a continuacin, se asume que se cuenta con un compilador instalado y se ha editado un archivo
hola.c que se quiere compilar. Si tu sistema operativo no aparece en esta lista busca en internet, ya que seguro que
existe algn compilador para ese sistema.

1.2 Compilacin de programas segn la plataforma


1.2.1

Windows

Para compilar un programa C en entornos Windows, debemos seguir una serie de pasos que varan segn el compilador de C que queramos utilizar. Antes que nada, sera bueno que se revises la documentacin del compilador elegido
para conocer los comandos exactos.
1

CAPTULO 1. PROGRAMACIN EN C/PRIMER PROGRAMA EN C

Compilacin del cdigo fuente


Si se utiliza un entorno de desarrollo, ser posible compilar directamente desde el entorno, mediante un botn o una
combinacin de teclas.
Si se ejecuta el compilador desde la lnea de comandos, la lnea ser distinta segn el compilador utilizado. A continuacin algunos ejemplos de ciertos comandos segn el compilador:
En Turbo C de Borland es: tcc hola.c
En C++ de Borland: bcc hola.c
En Visual C de Microsoft: cl hola.c
En GNU gcc: gcc hola.c o cc hola.c
El C de Zortech: ztc hola.c
Una vez compilado el cdigo fuente se genera un archivo llamado archivo objeto o programa objeto que es luego
enlazado mediante el enlazador, para generar el archivo ejecutable.
Los compiladores actuales suelen hacer dos funciones de una vez, compilando y enlazando todo en una sola funcin,
aunque es posible pedirles que no lo hagan mediante parmetros adicionales.
Segn el compilador y la conguracin utilizada, se obtendrn dos o tres archivos:
El archivo fuente
hola.c
El archivo objeto
hola.obj
El archivo ejecutable
hola.exe
Este ltimo es el que nos interesa, puesto a que es el cdigo ejecutable, el programa en s. Al ejecutarlo se producir
la salida deseada en una ventana de consola.
Salida por pantalla
Si ejecutamos en entorno Windows el programa directamente desde el navegador de archivos, o tambin desde algunos
entornos de desarrollo, lo que suceder ser que apenas abierta la ventana de la consola, se mostrar la cadena esperada
y luego de terminada la funcin, la consola se cerrar sin tener el tiempo suciente de ver nuestro mensaje en pantalla.
Para poder ver la salida por pantalla ser necesario ejecutar el programa desde la lnea de comandos, o modicar la
conguracin del entorno de desarrollo para que muestre la salida por pantalla al ejecutar el programa.
Una posible solucin es agregar una funcin adicional a nuestro hola.c":
/* Inclusin de archivos */ #include <stdio.h> #include <stdlib.h> /* Funcin principal */ int main (int argc,char
**argv) { /* Impresin por pantalla y salida del programa*/ printf(Hola mundo\n); system (pause); return 0; }
Las dos lneas agregadas permiten que utilicemos la biblioteca stdlib, que incluye la funcin system y que mediante
esta funcin se ejecute el comando pause del sistema, que evita que el programa siga hasta que se presione una tecla.
As es posible visualizar que la salida de hola.c se complet perfectamente.

1.2.2

GNU

Si bien existen otros compiladores, lo ms usual y ms sencillo para compilar un programa en GNU/Linux es el
compilador gcc, ya que es el que se incluye en todas las distribuciones.
De cualquier forma, es posible realizar la compilacin desde lnea de comandos o desde el entorno grco.

1.3. DISECCIONANDO EL HOLA MUNDO

Para realizarla desde lnea de comandos, ser necesario contar con una terminal (xterm, konsole, gnome-terminal,
etc). No es necesario contar con permisos de root para crear o compilar programas. En esa terminal ser necesario
escribir
gcc hola.c
Si no existen errores en el cdigo, este comando nos crear un archivo ejecutable, que por omisin se llama a.out,
y que podemos ejecutar desde la lnea de comandos de la siguiente forma:
./a.out Hola mundo
Es una buena idea especicar el nombre que el archivo ejecutable tendr, pasando como parmetro al compilador la
opcin -o, de la siguiente forma:
gcc hola.c -o hola
Con lo cual, el nombre del archivo creado ser hola. Este archivo no tiene extensin ya que es la forma usual de
llamar a los archivos ejecutables en los entornos UNIX y GNU/Linux, sin embargo funcionara de la misma forma si
se llamara hola.exe.
Para ejecutarlo, haremos los mismo que en el caso anterior:
./hola Hola mundo
Existen otros parmetros que podemos especicar al compilador en la lnea de comandos, dependiendo del tipo de
programa, y en funcin de la complejidad del mismo. Por ejemplo, podemos agregar las siguientes opciones:
gcc hola.c -o hola -Wall -pedantic
La opcin -Wall nos mostrar todos los avisos que produzca el compilador, no solamente los errores. Los avisos nos
indican dnde y/o porqu podra surgir algn error en nuestro programa.
La opcin -pedantic nos aporta ms informacin sobre los errores y los avisos mostrados por GCC.

1.3 Diseccionando el Hola Mundo


A continuacin veremos cul es la estructura bsica de un programa en C, para poder entender qu hace cada una de
las lneas de nuestro sencillo programa.
Es probable que lo primero que salte a la vista sea la lnea:
printf(Hola mundo\n);
Esta es la lnea que hace aparecer la cadena Hola Mundo en nuestra pantalla. Notamos que en C la sentencia para
imprimir algo por pantalla es printf() y, adems, hay que colocar parntesis alrededor de lo que queremos imprimir
para utilizarla.
Esto se debe a que en C, printf es una funcin, que imprime su argumento (la cadena Hola Mundo\n) en la pantalla.
Se denomina invocar una funcin a la accin de utilizarla para que realice una accin.
Podemos observar tambin que la cadena a imprimir termina con una extraa combinacin: \n. La combinacin \n
no representa a dos caracteres independientes, sino que representa un nico carcter no imprimible: el salto de lnea.
Sin el salto de lnea, el resultado al ejecutar el programa sera:
$ ./a.out Hola Mundo$
Es decir que no hay salto de lnea entre la cadena impresa, y la siguiente entrada de la lnea de rdenes, que no es lo
que esperbamos.
Lo ltimo a notar en la lnea es que termina con un punto y coma. En C, todas las sentencias terminan con un punto
y coma. Al principio puede parecer obvio dnde termina una sentencia, pero ya veremos ms adelante que no lo es
tanto.
Observemos ahora la siguiente sentencia del programa:
return 0;
Luego de esta sentencia, termina el programa. En el caso de la instruccin return dentro de la funcin main, el resultado

CAPTULO 1. PROGRAMACIN EN C/PRIMER PROGRAMA EN C

es que se naliza el programa, comunicndole al sistema operativo que el valor de retorno (un cdigo numrico
que el sistema utiliza para saber si el programa ha funcionado bien o ha dado fallos) es 0, es decir, correcto.
Las dos ltimas sentencias se encuentran encerradas entre llaves. De esta manera, forman un bloque, es decir, un
grupo de sentencias que se ejecutarn siempre de forma correlativa.
Y qu es esa lnea que precede (en realidad, que da nombre) al bloque?
int main (int argc, char **argv)
Pues es la denicin de una funcin, en este caso llamada main. En C (y en general en todos los lenguajes de
programacin estructurada) todo se hace a base de funciones, como main y printf.
La funcin main es especial, porque es el la que se invoca cuando se ejecuta el programa. Todos los programas en C
comienzan su ejecucin al principio de la funcin main, y cuando sta acaba, el programa tambin.
Veamos con ms detalle la denicin de la funcin:
int main (int argc, char **argv) { ... }

El nombre de la funcin que viene a continuacin, entre llaves, es main.


Recibe dos argumentos: int argc y char **argv (que representan a la cantidad de argumentos ingresados al
ejecutar el programa y a los valores de estos argumentos respectivamente).[1]
La funcin devuelve como resultado un nmero entero, int (que es el 0 de la instruccin return).[2]
Finalmente, y un tanto aparte (est separada del resto por una lnea en blanco), tenemos la lnea:
#include <stdio.h>
Que parece bastante distinta al resto del programa, y que, adems, parece no tener sentido, puesto que ya hemos
denido la funcin main que hace todo el trabajo.
Efectivamente, esa lnea no es parte del programa, aunque sea imprescindible. La lnea es una instruccin del preprocesador
de C, como nos lo indica el smbolo #, y lo que hace es incluir en ese punto el contenido de otro chero, antes (de ah
el nombre de preprocesador) de que comience la compilacin. El chero stdio.h es el que contiene la denicin de
la funcin printf(), que antes utilizamos pero que no escribimos, ya que forma parte de la biblioteca estndar de C.

1.4 Comentarios
Una vez escrito un cdigo, tratar de entenderlo un ao ms tarde solo con leerlo puede ser frustrante: no hay manera
de saber (si el programa es medianamente complicado) qu es cada variable, o qu hace cada bloque de cdigo. Por
esto, en cualquier lenguaje de programacin son importantes los comentarios.
Un comentario en C es todo lo que se encuentre entre los smbolos /* y */. Hay que tener en cuenta que los comentarios
no se pueden anidar: si dentro de un comentario hay un /*, seguir siendo el primer */ el que nalice el comentario,
no se esperar al segundo.
Hay otro tipo de comentarios en C, procedentes del lenguaje C++, e incorporadas al estndar de C a partir de C99:
//. Todo lo que est despus de estos signos, hasta el nal de la lnea, se considerar un comentario y el compilador
no lo tomar en cuenta.
En el ejemplo presentado pueden verse tres lneas con comentarios, que documentan someramente las distintas funcionalidades del cdigo. En los prximos captulos podrn verse mejores usos de los comentarios dentro del cdigo.
Tambin podra decirse que es una herramienta bsica basada en compilador
[1] En un captulo posterior podr ver un ejemplo del uso de los parmetros que recibe main.
[2] Es importante sealar que el estndar dice que main deber denirse como funcin que retorna un entero, o de lo contrario
el resultado queda indenido.

Captulo 2

Programacin en C/Tipos de datos


2.1 Historia
En el lenguaje C estandarizado como C89, existan cuatro tipos de datos bsicos que son: los nmeros enteros, los
nmeros reales, los caracteres, y los punteros. A partir del estndar C99 se agregan: los valores lgicos (verdadero o
falso) y los nmeros complejos.
Estos tipos de datos son parte del lenguaje, y por ello se los considera primitivos. Ms adelante veremos que con el
uso de estructuras y uniones es posible crear tipos compuestos de datos a partir de estos tipos primitivos.
En este captulo veremos los enteros, los reales y los caracteres. Ms adelante se vern otros tipos de datos ms
complejos, como son los vectores, las cadenas de caracteres, y los punteros en general.

2.2 Enteros
Los enteros son el tipo de dato ms primitivo en C. Se usan para representar nmeros enteros. Pero siempre se pueden
encontrar otras aplicaciones para los nmeros enteros. En general se pueden usar para representar cualquier variable
discreta.
Los tipos de datos enteros son: short, int, long y long long, cada uno representando un nmero entero de un tamao
o capacidad determinado. Segn el compilador y la plataforma de hardware, cada uno de estos tipos de dato puede
ocupar desde 1 byte hasta 8 bytes en memoria (para ms detalles busca en la referencia).
Adems, el lenguaje C hace la distincin de si el entero es con signo (signed) o sin signo (unsigned). En caso de que
no se declare si es con signo o sin signo, se toma con signo.
Algunos ejemplos de declaraciones de enteros:
int a; unsigned int a; signed long a; signed long long a = 10000000;
Todos los nmeros son representados en memoria mediante una cadena de bits. En el caso de los nmeros con signo,
el bit ms signicativo es el que se usa para representar el signo. La representacin de los nmeros negativos se
realiza mediante el complemento a dos, que es una tcnica que permite operar con los nmeros negativos de forma
lgica.
A modo de ejemplo, la representacin en memoria del nmero 8 en una variable de 2 bytes, entera, con signo, sera
la siguiente:
1111111111111000

2.3 Flotantes
Se denomina otantes a los tipos de datos que representan a los nmeros reales, ya que utilizan un sistema de representacin basado en la tcnica de coma otante, que permite operar con nmeros reales de diversas magnitudes,
5

CAPTULO 2. PROGRAMACIN EN C/TIPOS DE DATOS

mediante un nmero decimal llamado mantisa y un exponente que indica el orden de magnitud.
El tipo de dato otante en lenguaje C slo tiene dos tamaos: el oat y el double, que son 4 bytes y 8 bytes respectivamente. Se los puede utilizar tanto para representar nmeros decimales, como para representar nmeros enteros
con un orden de magnitud muy grande.
La forma de declarar una variable otante es escribiendo en una lnea uno de los tipos de datos otantes y a continuacin el nombre de la variable y tal vez algn valor que se les quiera dar.
Algunos ejemplos:
oat a; double a = 1e23; double a = 3.1416; oat a = 4e-9; double a = 78;
Hay que tener en cuenta que aunque los valores otantes son ms convenientes para algunas aplicaciones, hay casos
en los que se preeren los enteros. Esto se debe a que los nmeros otantes no necesariamente tienen soporte de
hardware, en particular en las plataformas integradas. Una alternativa que se utiliza en estas situaciones es interpretar
los enteros como decimales de forma que 150 se interprete como 1.5 y 2345 como 23.45.
Para el caso de los otantes de 4 bytes, se utiliza 1 bit para el signo, 8 bits para el exponente y 23 bits para el valor
del nmero. El procedimiento para almacenar un nmero en una variable otante es el siguiente:
1. Se convierte a binario la parte entera.
2. Se coloca el signo en el bit ms signicativo de la misma manera que en los enteros (1 para el - y 0 para el +).
3. Se mueve la coma (en la representacin binaria de la parte entera) hasta que est a la derecha del primer uno
y ste se descarta (el uno ms signicativo). El valor del exponente ser el nmero de posiciones que se movi
la coma. El exponente usa la representacin de un entero con complemento a dos.
4. Se convierte en binario la parte decimal del nmero. Esto usando el peso de los bits. el bit decimal ms signicativo vale 1/2, el siguiente vale 1/4, el otro 1/8, el otro 1/16 y as hasta completar lo que falta para los 23bits
del valor.
5. Se concatena todo y ese es el valor otante representado en memoria.

2.4 Caracteres
Los caracteres se representan utilizando el tipo char, que tiene slo 1 byte de tamao. Este tipo se utiliza para representar los 256 caracteres de la tabla de caracteres del sistema. El tipo char es tambin un tipo entero, ya que puede
tomar valores de 0 a 255. Por lo tanto tambin puede ser signed o unsigned.
En cuanto a la forma de declarar variables de tipo char es la misma forma que con los otros tipos.
char a; char a = 's; unsigned char a = 48;
Como puedes ver, se le puede asignar un nmero a una variable char, ya que se trata de un tipo entero. En muchas
situaciones se utiliza el tipo char para almacenar nmeros pequeos, ya que ocupa en memoria slamente un byte.
Es importante notar que con la llegada de la codicacin UTF-8, los caracteres de los diversos idiomas pueden ocupar
1, 2, 3 o 4 bytes, de modo que el tipo char ya no alcanza para la representacin de todos los caracteres posibles. Por
ello, el estndar C99 introduce el tipo wchar que puede ocupar ms de 1 byte, segn sea necesario para la codicacin
utilizada por el sistema.

Captulo 3

Programacin en C
3.1 Prlogo
El avance de la tecnologa y la concurrente entrega de informacin, nos permite generar una edicin, variable y
alternativa en la enseanza del lenguaje de programacin que ms impacto social ha tenido en la historia de la informtica. Este libro ha sido forjado con la incansable ayuda de informticos de habla hispana, quienes byte a byte han
colaborado por hacer de la informacin una fuente de conocimiento global. De la misma forma, contina hoy siendo
modicado en una constante bsqueda de superacin de la calidad.
Esta obra est llamada a ser la piedra angular en la enseanza de la programacin, logrando abarcar todos los aspectos
del lenguaje en diversos niveles y de esta forma ser tanto una referencia tcnica para quienes ya dominan el lenguaje
como una introduccin sencilla para quienes estn empezando a conocerlo.
Viajaremos por la historia del lenguaje, veremos su propsito e indagaremos en la ciencia de la programacin. El
n es otorgar al lector una doctrina clara de la programacin y el lenguaje C; lo induciremos a conseguir un manejo
importante del lenguaje.

3.1.1

Para quin es este libro?

Este libro est dirigido a todos los que deseen obtener conocimientos de programacin, pues el objetivo explcito que
nos ha motivado a crearlo es difundir la importancia del lenguaje C en el mundo de la informtica. Si nos enfocamos
a un grupo social especco, podremos indicar que este libro contribuir con los estudiantes de carreras del rea
informtica, debido a que los temas convenidos, son parte de su plan de estudios.

3.1.2

Por qu otro manual de C?

Porque el lenguaje C es la base fundamental de la programacin. Para quienes estn en el ambiente de la informtica
es crucial tener por lo menos nociones de este lenguaje. Varios sistemas operativos, cientos de bibliotecas, y miles de
programas estn construidos utilizando C, al conocerlo es posible entender, colaborar y desarrollar en este lenguaje.
Los sistemas, programas, juegos y herramientas que nosotros disfrutamos hoy fueron construidos por personas como nosotros, que empezaron con nuestro mismo potencial y fueron aprendiendo a hacer cosas fantsticas con las
herramientas que tenan a mano.
Una razn importante para otro libro de lenguaje C es tambin que los libros existentes muestran muy poca documentacin de calidad. En Internet existe una cantidad inmensa de informacin publicada pero est dispersa, y mal
manejada en algunos sitios. Es la intencin de este libro crear un buen compendio de informacin, que permita a los
interesados aprender a programar en C.

3.2 ndice de contenidos


1. Introduccin
7

CAPTULO 3. PROGRAMACIN EN C
(a) Herramientas necesarias
2. Historia de C
3. Fundamentos de programacin
4. Primer programa en C
(a) Compilacin de programas en C
5. Tipos de datos
(a) Enteros
(b) Reales
(c) Caracteres
6. Expresiones
7. Interaccin con el usuario
8. Instrucciones de control
(a) La estructura condicional if...else
(b) La estructura condicional switch...case
(c) El bucle for
(d) El bucle while
(e) El bucle do...while
(f) La sentencia goto
9. Uso de Funciones
10. Vectores
11. Cadenas de caracteres
12. Manejo de archivos
(a) Lectura
(b) Escritura
(c) fread y fwrite
13. Estructuras y Uniones
(a) Estructuras
(b) Uniones
14. Punteros
15. Manejo dinmico de memoria
16. Matrices Dinamicas
17. Algoritmos y Estructuras de Datos
(a) Punteros a Estructuras
(b) Ordenamiento de Datos
i. Burbuja
ii. Induccion
iii. QuickSort
18. Detalles sobre el proceso de compilacin
19. Los errores

3.3. ENLACES
(a) Chequeos del C
(b) El depurador
20. Herramientas externas tiles
21. Resumen de ejemplos
(a) El Hola Mundo
(b) El Hola Mundo comentado
(c) El Hola Mundo estructurado en funciones
(d) Ejemplo de clculo con enteros
(e) Ejemplo comentado de clculo con enteros
(f) Calendario en C
(g) Clculo del logaritmo binario
22. La librera glib
23. Programacin paralela en memoria compartida

3.2.1

Anexos

1. Referencia
(a) Tipos de datos
(b) Operadores
(c) Estructuras
(d) Bibliotecas y funciones
2. Cmo compilar un programa
(a) Un poco ms sobre compilacin
3. Cdigo embebido
4. Recursos en la red
(a) Manuales, tutoriales y otros documentos
(b) Compiladores e IDEs
5. Bibliografa

3.3 Enlaces
Nociones bsicas de programacin (generales, independientes del lenguaje):
Fundamentos de programacin
Wikilibro similar a ste en ingls
C Programming
esquema de operadores y expresiones

10

CAPTULO 3. PROGRAMACIN EN C

3.4 Licencia y autores


Copyright 2004 Envite
Copyright 2005 Alejandro Moreno Calvo
Copyright 2006 Andreu Correa Casablanca
Copyright 2009 zerohours
Ver el historial de cada pgina para el resto de autores.

3.5 Cmo contribuir a este WikiLibro?


Contribuir con este libro es muy simple primero deberas registrarte un usuario y/o entrar (esto no es necesario pero
si muy conveniente) a Wikilibros, para que podamos identicar tus ediciones. Luego, si nunca has contribuido en un
proyecto de wikipedia o en otro wiki deberas leer el manual de uso de wikilibros.
Una vez hecho esto todo lo que tienes que hacer es agregar el contenido que consideres necesario para el libro. Para
editar cualquier seccin basta con hacer click al link que dice editar en la pestaa en la parte superior de la pagina,
seria bueno revisar (antes de editar cualquier cosa) la pestaa de discusin que est ah para ser usada. Ten en cuenta
que el material que ya est fue escrito por personas que deseaban contribuir igual que t, as que trata de respetarlo.
Aunque con esto no me reero a que si se necesita alguna correccin, reorganizacin, quitar partes que sean ambiguas,
no dejes de hacerlo. Lo mejor sera que todos contribuyramos de cualquier manera al libro.
Adems, es recomendable consultar la pgina de discusin del libro y la del articulo en particular que quieras modicar, ya que de esta manera se pueden coordinar esfuerzos.
Recuerda que todo el contenido que aadas al libro es publicado bajo la licencia GFDL, por lo que no uses material
que no haya sido escrito por ti o que no est ya publicado bajo GFDL. Recientemente wikimedia decidi adoptar la
Licencia Creative Commons Compartir-Igual 3.0 para todos los aportes, por lo que actualmente el libro tiene una
licencia dual.

Captulo 4

Programacin en C/Expresiones
Vamos a tratar ahora de que el ordenador haga un poco de matemticas para nosotros. Por ejemplo, que realice unas
pocas sumas, restas multiplicaciones y divisiones.
#include <stdio.h> int main(void) { int resultado; resultado=5+2; printf(Resultado de la suma: %i\n,resultado);
resultado=5-2; printf(Resultado de la resta: %i\n,resultado); resultado=5*2; printf(Resultado de la multiplicacin:
%i\n,resultado); resultado=5/2; printf(Resultado de la divisin: %i\n,resultado); return(0); }
Despus de grabarlo (por ejemplo, con el nombre ejemplo.c), lo compilamos y ejecutamos, con (respectivamente):
$ gcc ejemplo.c $ ./a.out Resultado de la suma: 7 Resultado de la resta: 3 Resultado de la multiplicacin: 10 Resultado
de la divisin: 2 $
Fijmonos en la lnea del principio de la funcin main:
int resultado;
Esta lnea lo que hace es reservar un trozo de memoria, del tamao de un int (normalmente 4 bytes), y asignarle el
nombre resultado, para poder despus referirnos a l. A partir de este momento, podemos considerar que en nuestro
programa existe una variable, que no tiene valor denido, pero a la que le podremos dar valor posteriormente.
Las lneas con printf() ya las conocemos, pero hay algo en ellas que no habamos visto antes. Esos %i y la parte de
resultado son nuevas para nosotros.
La funcin printf() no slo sabe imprimir cadenas simples, como Hola Mundo\n, sino tambin imprimir variables.
Para ello, en el lugar de la cadena donde queremos que aparezca el valor de la variable, introducimos lo que se llama
una cadena de conversin de printf(). Estas cadenas siempre empiezan por %, siendo %i la cadena para imprimir un
entero, como es en nuestro caso int resultado. Finalmente, printf() debe saber qu valor escribir, por eso le damos
otro argumento (u otros), usando , como separador, que contienen las variables cuyos valores queremos mostrar.
En el resto del programa hemos visto cmo decirle al ordenador que ejecute una suma, una resta, una multiplicacin
y una divisin entera, con los operadores +, -, * y /. Es de notar que el resultado de una operacin como estas
entre nmeros enteros ser siempre otro entero, como se puede observar en la divisin, en la que no obtenemos un
bonito decimal, sino un resultado entero. Adems, hemos visto que el resultado de esas operaciones, que llamamos
expresiones, puede ser asignado a una variable:
resultado = 7;
Esa asignacin se hace mediante el operador de asignacin: =. Con l, ya conocemos cinco operadores.
Pero, como = tambin es un operador, cmo sabe el ordenador qu operador debe ejecutar primero? Y si es un
operador, por qu no da un resultado? No crea una expresin?
Empezando por las ltimas preguntas, el operador de asignacin s crea una expresin, como los operadores de suma,
resta, multiplicacin y divisin, y esa expresin tiene un resultado, que es el valor que obtiene el lado izquierdo al
realizar la operacin. En cuanto a saber qu se debe ejecutar primero, el ordenador tiene una lista de precedencia,
segn la cual siempre ejecuta primero las multiplicaciones y divisiones, de izquierda a derecha, a continuacin las
sumas y restas, de izquierda a derecha, y a continuacin las asignaciones, de derecha a izquierda. Para ms detalles
11

12

CAPTULO 4. PROGRAMACIN EN C/EXPRESIONES

acerca de la precedencia de los operadores ver el anexo de los operadores.


En cuanto a los caracteres de punto y coma, notamos aqu que una expresin tambin puede ser una sentencia por s
misma, sin necesidad de que haya ninguna funcin. De hecho, una sentencia puede no tener siquiera una expresin.
La lnea: ; es una sentencia perfectamente vlida, la sentencia vaca, que sera til en puntos donde el lenguaje requiera
una sentencia pero no sea necesaria para nuestro programa.

Captulo 5

Programacin en C/Interaccin con el


usuario
En este captulo veremos un poco ms sobre como interactuar con el usuario de nuestros programas desde la consola,
utilizando printf() como vimos en el primer ejemplo Hola mundo, as como scanf() para la lectura del teclado.

5.1 Imprimir por pantalla


Como hemos visto hasta ahora en los ejemplos, hay una funcin que utilizamos para sacar por pantalla textos arbitrarios o el resultado de alguna operacin: la funcin printf().
Si miramos (en la documentacin) su denicin, no nos aclarar demasiado:
int printf (const char *TEMPLATE, ...)
...claro que por algo tiene una seccin completa de la documentacin para ella sola.
Vemosla poco a poco. Se trata de una funcin de la biblioteca estndar, lo que quiere decir que para utilizarla
tenemos que incluir previamente su denicin. La encontraremos en <stdio.h>.
Lo primero que vemos en la denicin es que es una funcin de tipo int, lo que quiere decir que devuelve un entero.
Ese entero es el nmero de caracteres impresos en la pantalla, o un nmero negativo en caso de que se produzca algn
error.
Lo siguiente a notar es su primer argumento: const char *TEMPLATE. Se trata de una cadena de caracteres (char
*) que no ser modicada por la funcin (const), con lo que puede ser una constante de cadena o una variable que
contenga una cadena, pero siempre debe acabar con el carcter nulo \0.
Y luego vienen esos extraos puntos suspensivos. Esa elipsis nos indica que como argumentos adicionales de printf()
podemos poner una serie ilimitada de otros argumentos, que se supone que la funcin sabr qu hacer con ellos. Y
eso es justamente lo que hace tan fabulosa y til a printf().
Como hemos visto, el uso ms simple de printf() es imprimir una cadena de texto simple y corriente. Como ya vimos:
printf(Hola Mundo\n); /*imprime la cadena*/
Y tambin hemos visto printf() tambin puede, con un argumento extra y una sintaxis especial, imprimir un nmero
entero que hayamos almacenado en una variable:
char resultado;
resultado=5+2;
printf(Resultado de la suma: %i\n,resultado);
Aqu el punto de insercin es la secuencia %i. printf() siempre trata las secuencias que comiencen por % como
secuencias de control que le dicen que debe imprimir algo que le proporcionamos en los otros argumentos. As, podemos imprimir varios enteros distintos en los sitios que queramos de la cadena, insertando varias de estas secuencias
%i:
13

14

CAPTULO 5. PROGRAMACIN EN C/INTERACCIN CON EL USUARIO

int numero;
numero=3;
printf(El doble de %i es %i y su cuadrado es %i\n,numero,numero*2,numero*numero);

5.2 Lectura de datos del teclado


La entrada de datos se puede hacer de muchas maneras y entre ellas estn desde el uso de dispositivos especiales
hasta nuestro simple teclado. La entrada de datos se reere a cualquier forma de inuencia del usuario sobre los datos
que posee el sistema.
Con el n de mostrar una forma de entrada simple para el aprendizaje vamos a hablar de la funcin scanf() que se
encuentra denida en <stdio.h> y que se usa para capturar diferentes tipos de datos.

5.2.1

La funcin scanf()

scanf() es una de las funciones ms usadas por los principiantes para hacer entrada de datos en el lenguaje C. Tiene
una sintaxis muy parecida a printf: recibe una cadena con el formato de los datos y luego se ponen las variables en
orden que correspondan a ese tipo de datos. Es decir, as como en printf se pueden mostrar por pantalla los datos de
varias variables en una misma sentencia, en scanf se pueden capturar varios datos en una sola sentencia.
#include <stdio.h> int main() { int a; printf (diga un valor para a:"); scanf("%i,&a); printf (el valor es: %i\n,a);
return 0; }
Por ahora no nos interesan las dems sentencias, slo la que contiene scanf. En el cdigo se ve lo siguiente:
scanf("%i,&a);
Se observa que la funcion printf dej en pantalla una peticin para que el usuario introdujera un valor. Entonces,
scanf recibe como argumento una cadena del formato en que se van a capturar los datos y la lista de variables que
van a recibir valores y que deben coincidir con los del formato.
En este caso la cadena de formato, "%i, especica que el usuario ingresar un nmero entero. Luego se designa a la
variable a para contener a ese nmero. El smbolo (&) que precede a a es para especicar que lo que se est enviando
como argumento no es el valor que posee la variable a sino la direccin de memoria en que se encuentra. En este
momento eso no tiene mucha relevancia, slo hay que recordar que se debe usar el smbolo & dentro del scanf. En el
momento en que hablemos de punteros veremos ms detalles de esto.
Otro ejemplo del uso de scanf:
#include <stdio.h> int main() { int a,b; printf (introduzca dos valores con el formato \"a,b\" :"); scanf("%i,%i,&a,&b);
printf (el primer valor : %i\n,a); printf (el segundo valor : %i\n,b); return 0; }
Aqu hemos introducido una nueva variable en el cdigo. La cadena de formato, "%i,%i especica que el usuario
ingresar un nmero, seguido de una coma, y luego otro nmero. El primer %i ser capturado por la variable a y el
segundo por b.

Captulo 6

Programacin en C/Instrucciones de
control
Como ya se ha mencionado, C es un ejemplo de programacin estructurada. En este tipo de programacin, es necesario contar con ciertas estructuras que permitan controlar el ujo del programa, es decir, tomar decisiones y repetir
acciones.

6.1 La estructura condicional if ... else


En la gran mayora de los programas ser necesario tomar decisiones sobre qu acciones realizar. Esas decisiones
pueden depender de los datos que introduzca el usuario, de si se ha producido algn error o de cualquier otra cosa.
La estructura condicional if ... else es la que nos permite tomar ese tipo de decisiones. Traducida literalmente del
ingls, se la podra llamar la estructura si...si no, es decir, si se cumple la condicin, haz esto, y si no, haz esto
otro.
Un ejemplo sencillo sera el siguiente (no se trata de un programa completo, sino tan slo una porcin de cdigo):
if (edad < 18) printf(No puedes acceder.\n); else printf(Bienvenido.\n);
Este cdigo de ejemplo dice que si el valor de la variable edad es menor que 18 se imprimir No puedes acceder.\n,
mientras que en caso contrario se imprimir Bienvenido.\n.
Como se ve en el ejemplo, la estructura de un condicional es bastante simple:
if (condicin) { sentencias_si_verdadero; } else { sentencias_si_falso; }
La condicin, encerrada entre parntesis, es una expresin que puede dar como resultado 0 (interpretado como falso)
o cualquier valor distinto de 0 (interpretado como verdadero). Cuando la condicin sea verdadera, se ejecutarn
las sentencias dentro del primer bloque de cdigo, cuando la condicin sea falsa, se ejecutarn las sentencias del
segundo bloque de cdigo. Las expresiones y valores de tipo verdadero/falso son tambin llamados valores lgicos o
booleanos.
La indentacin o sangra (los espacios al comienzo de las lneas) no es necesaria, pero ayuda a la claridad del cdigo.
La utilizacin de las llaves {...} es obligatoria cuando se quiere utilizar ms de una instruccin por bloque, y optativa
cuando slo se quiere escribir una instruccin. Por claridad, sin embargo, es recomendable utilizarlas an cuando slo
vaya a haber una instruccin.
El bloque del else es opcional. Si no se lo encuentra, slo se realizar la accin correspondiente al bloque if.
A continuacin, un ejemplo con una funcin, que devuelve el mayor de dos nmeros:
int mayor(int a, int b) { if (b > a) { return b; }// No posee especicacin de la parte else, ya que no es necesaria.
return a; // Finaliza la funcin retornando el valor de a. }

15

16

6.1.1

CAPTULO 6. PROGRAMACIN EN C/INSTRUCCIONES DE CONTROL

Operadores de comparacin

El smbolo > visto en el ltimo ejemplo es un operador, que en este caso compara dos nmeros enteros y devuelve
verdadero si el primero es mayor, falso en caso contrario.
A continuacin un listado de los posibles operadores de comparacin en C y su signicado.
Teniendo en cuenta que en C se toma como falso el valor 0, y como verdadero cualquier otro valor, una prctica
comn es expresar condiciones sin utilizar ningn operador:
oat division(int dividendo, int divisor) { if (divisor) { return dividendo / divisor; } else { printf (No se puede dividir
por cero\n); return 0; } }
En este caso, la expresin (divisor) es equivalente a (divisor != 0).

6.1.2

Operadores lgicos

Los operadores && (y), || (o) y ! (no) son operadores lgicos. Permiten operar con expresiones lgicas para
generar expresiones ms complejas.
Por ejemplo: determinar si un ao es bisiesto o no. Los aos son bisiestos si son divisibles por 4, pero no si son
divisibles por 100, a menos que tambin sean divisibles por 400.
if ( (!(a % 4) && (a % 100)) || !(a % 400) ) { printf(es un ao bisiesto.\n); } else { printf(no es un ao bisiesto.\n); }
En realidad, teniendo en cuenta la prioridad de los operadores utilizados, podemos simplicar la expresin anterior
del siguiente modo:
if ( !(a % 4) && (a % 100) || !(a % 400) ) { printf(es un ao bisiesto.\n); } else { printf(no es un ao bisiesto.\n); }
Adems, como a cada rama del if le sigue una nica instruccin, podemos expresar la expresin anterior del siguiente
modo:
if ( !(a % 4) && (a % 100) || !(a % 400) ) printf(es un ao bisiesto.\n); else printf(no es un ao bisiesto.\n);
En este caso, se utiliza el operador mdulo (%), que obtiene el resto de la divisin entera de un nmero por otro.
Cuando un nmero es divisible por otro, el resto de su divisin entera ser cero. Siendo que cero es equivalente a
falso, y cualquier valor distinto de cero es equivalente a verdadero, podemos usar el operador % para vericar si el
nmero es mltiplo de 4, de 100 o de 400.

6.1.3

Evaluacin de cortocircuito

La evaluacin en corto circuito es una caracterstica del lenguaje C que se utiliza para optimizar la ejecucin de
programas. Consiste en que el programa puede vericar si una expresin es verdadera o falsa antes de haber evaluado
toda condicin.
Por ejemplo, si se tiene una condicin como la siguiente:
if ((a > 2) || (b < 4)) { ... }
Al ejecutarse el programa, se evaluar primero si a > 2. En el caso en que sea verdadero, no continuar con la siguiente
condicin, ya que el resultado ser de cualquier modo verdadero.
De la misma forma, si la condicin fuera:
if ((a > 2) && (b < 4)) { ... }
En este caso, si no se cumple que a > 2, no se evaluar la siguiente condicin, ya que el resultado ser falso de todos
modos.
Esta caracterstica no tiene demasiada importancia al comenzar a programar, pero facilitar ciertas operaciones y

6.2. LA ESTRUCTURA CONDICIONAL ABIERTA Y CERRADA SWITCH ... CASE

17

optimizaciones en programas avanzados.

6.2 La estructura condicional abierta y cerrada switch ... case


La estructura condicional switch ... case se utiliza cuando queremos evitarnos las llamadas escaleras de decisiones. La
estructura if nos puede proporcionar, nicamente, dos resultados, uno para verdadero y otro para falso. Una estructura
switch ... case, por su parte, nos permite elegir entre muchas opciones. Ejemplo:
#include <stdio.h> #include <stdlib.h> int main(void) { int dia; printf(que nmero de da de la semana es?");
scanf("%d,&dia); switch(dia) { case 1 : printf(Lun, Lunes); break; case 2 : printf(Mar, Martes); break; case
3 : printf(Mier, Miercoles); break; case 4 : printf(Jue, Jueves); break; case 5 : printf(Vie, Viernes); break; case 6 : printf(Sab, Sabado); break; case 7 : printf(Dom, Domingo); break; default : printf(No existe); } return 0; }
La estructura anterior, de realizarse con sentencias if, necesitara cuatro de ellas, resultando un enorme bloque muy
difcil de leer. En la mayora de los casos, adems, la sentencia switch proporciona una ganancia en velocidad del
cdigo, pues permite al compilador trabajar en base a que se trata de una decisin mltiple para una nica variable,
cosa que con sentencias if el compilador no tiene por qu detectar.
Como vemos, para cada valor de la variable se ejecuta un bloque de sentencias distinto, en el que no necesitamos
llaves. Hay un caso especial, default, que se ejecuta si ningn otro corresponde, y que no es necesario poner. Es, en
todo, equivalente al bloque else de una sentencia if.
Las sentencias break son muy importantes, ya que el comportamiento normal de un bloque switch es ejecutarlo todo
desde la etiqueta case que corresponda hasta el nal. Por ello, si no queremos que se nos ejecute ms de un bloque,
pondremos sentencias break al nal de cada bloque excepto el ltimo.
Es decir, las etiquetas case son puntos de entrada de la ejecucin, y no implican que al acabarse el bloque case la
ejecucin salte al nal del bloque switch. Las etiquetas case siguientes a la que hemos utilizado para entrar son,
sencillamente, ignoradas.
A la ausencia de sentencias break se le llama, en ocasiones, dejar caer la cascada switch.

6.3 El bucle while


El bucle while sirve para ejecutar cdigo reiteradas veces.
while (/*condicion*/) { /* Cdigo */ }
La condicin debe de ser una expresin lgica, similar a la de la sentencia if. Primero se evala la condicin. Si
el resultado es verdadero, se ejecuta el bloque de cdigo. Luego se vuelve a evaluar la condicin, y en caso de dar
verdadero se vuelve a ejecutar el bloque. El bucle se corta cuando la condicin da falso.
Ejemplo: imprimir los nmeros de 0 a 99:
int i = 0; while (i < 100) { printf("%d\n, i); i = i + 1; }

6.4 El bucle for


El bucle for es un bucle muy exible y a la vez muy potente ya que tiene varias formas interesantes de implementarlo,
su forma ms tradicional es la siguiente:
for (/* inicializacin */; /* condicin */; /* incremento */) { /* cdigo a ejecutar */ }
Inicializacin: en esta parte se inicia la variable que controla el bucle y es la primera sentencia que ejecuta el bucle.
Slo se ejecuta una vez ya que solo se necesita al principio del bucle.
Expresin condicional: al igual que en el bucle while, esta expresin determina si el bucle continuar ejecutndose o

18

CAPTULO 6. PROGRAMACIN EN C/INSTRUCCIONES DE CONTROL

no.
Incremento: es una sentencia que ejecuta al nal de cada iteracin del bucle. Por lo general, se utiliza para incrementar
la variable con que se inicio el ciclo. Luego de ejecutar el incremento, el bucle revisa nuevamente la condicin, si es
verdadera tiene lugar una ejecucin ms del cuerpo del ciclo, si es falsa se termina el ciclo y as.
Aqu se muestra el mismo ejemplo visto para el bucle while, pero implementado con un bucle for:
int i; for (i=0; i < 100; i = i + 1) { printf("%d\n, i); }
Nota: En C, la sentencia i = i + 1 puede escribirse en forma ms reducida como i++. Esta forma se utiliza ms
comnmente en el bucle for:
int i; for (i=0; i < 100; i++) { printf("%d\n, i); }

6.5 El bucle do...while


El bucle do...while es un bucle que, por lo menos, se ejecuta una vez. Do signica literalmente hacer, y while
signica mientras
Su forma es esta:
do { /* CODIGO */ } while (/* Condicin de ejecucin del bucle */)
Os muestro un ejemplo sencillo de uso:
int aleatorio; do { aleatorio = rand(); } while (aleatorio != 25);
La verdad es que este ejemplo puede resultar un poco absurdo, pero es bastante intuitivo. El cdigo del bucle asigna
un valor aleatorio a la variable denida anteriormente, y mientras esa variable no tenga el valor 25, el bucle sigue
ejecutndose.

6.6 La sentencia goto


La sentencia goto sirve para indicar al programa que continue ejecutndose desde la lnea de cdigo indicada. Su
sintaxis es ms o menos as:
/* Cdigo */ ETIQUETA: /* Cdigo */ goto ETIQUETA; /* Cdigo */
As, cuando se ejecute la sentencia goto, el programa saltar" y continuar su ejecucin a partir de la etiqueta
marcada.
Como se puede observar se puede usar para crear un bucle, o para ir a una parte del cdigo u otra si se combina
con una sentencia if...else. Pero por lo general puede obtenerse el mismo efecto utilizando los bucles anteriormente
vistos.
Por eso, la sentencia goto es poco aceptada por la comunidad de programadores, pues puede provocar que se hagan
programas un poco sucios y confusos. Slo en ocasiones muy excepcionales ser recomendado el uso del goto al
crear iteraciones muy complejas. Sin embargo, con el pasar de los aos este comando ya ha quedado prcticamente
descartado del lenguaje de los programadores.

Captulo 7

Programacin en C/Uso de funciones


7.1 Funciones
Como vimos anteriormente C tiene como bloque bsico la funcin main() , tambin hemos visto la sentencia printf()
que es otra funcin, y de igual forma hay muchas ms funciones predenidas, pero nosotros mismos tambin podemos
denir nuestras propias funciones. De hecho, es fundamental hacerlo.
Podemos denir una funcin cualquiera de la misma manera en que denimos la funcin main(). Basta con poner su
tipo, su nombre, sus argumentos entre parntesis y luego, entre llaves, su cdigo:
/* Inclusin de archivos */ #include <stdio.h> void holamundo(void) /* Funcin donde se ejecuta la lgica del programa */ { printf(Hola Mundo\n); /* imprime la cadena */ return; /* sale de la funcin */ } int main(void) /* Funcin
principal del programa */ { holamundo(); /* llamada a la funcin holamundo */ return 0; /* sale del programa con
cdigo 0 (correcto) */ }
Este cdigo es en todo equivalente al Hola Mundo original, slo que nos muestra cmo escribir y cmo utilizar
una funcin. Y adems nos muestra un principio de buena programacin: meter las sentencias que hacen el trabajo
en otras funciones especcas para sacarlas de main(), dejando en sta tan slo un guin general de lo que hace el
programa, no las rdenes especcas. De esta manera se facilita la comprensin del programa, y por tanto el futuro
trabajo de modicarlo.

7.1.1

La sentencia return

La sentencia return puede utilizarse dentro de una funcin para terminar su ejecucin.
En el ejemplo anterior, la funcin holamundo fue declarada con valor de retorno de tipo void (es decir, valor de retorno
nulo). En ese caso, la sentencia return no lleva ningn parmetro adicional, ya que la funcin no debe devolver ningn
valor a la funcin que la llama.
En cambio, la funcin main tiene un valor de retorno de tipo int, por lo que return debe ir seguido de un valor entero
(0 en el ejemplo). El valor 0 se utiliza para indicar que el programa ha llegado a un punto en el que todo se ha
desarrollado correctamente y se utiliza cualquier otro valor para indicar que ha habido algn tipo de error.
La instruccin return no es una funcin, se trata de una sentencia que lo que hace es retornar como valor de la funcin
el valor que se le proporciona como argumento.

7.1.2

Argumentos

Las funciones tambin pueden recibir argumentos o parmetros, para modicar su comportamiento. Por ejemplo, la
denicin de una funcin para sumar dos nmeros sera de la siguiente manera:
#include <stdio.h> int sumar(int numero1, int numero2) { return numero1 + numero2; } int main(void) { int suma
= sumar(5, 3); printf(La suma es: %d ", suma); return 0; }

19

20

CAPTULO 7. PROGRAMACIN EN C/USO DE FUNCIONES

En este ejemplo, la funcin sumar recibe dos argumentos de tipo int y su valor de retorno tambin es de tipo int. Dentro
de la funcin main, se llama a la funcin sumar poniendo entre parntesis los valores deseados para sus argumentos,
en orden, separados por una coma. As, dentro de sumar el nmero 5 ser asignado a la variable numero1 y el nmero
3 a numero2.

7.1.3

Declaracin y denicin

En el ejemplo anterior podemos notar que la funcin sumar gura en el cdigo antes que main. Qu pasara si las
escribiramos en distinto orden?
#include <stdio.h> int main(void) { int suma = sumar(5, 3); /* ERROR, sumar no ha sido declarada an */ printf(La
suma es: %d ", suma); return 0; } int sumar(int numero1, int numero2) { return numero1 + numero2; }
En este caso el programa es errneo y no compila, ya que en la lnea donde se llama a la funcin sumar, el compilador
an no conoce ninguna funcin con ese nombre, y cules son sus argumentos y valor de retorno.
Una posible solucin es declarar el prototipo de la funcin al principio, para informar al compilador que existe, y
luego denir el cuerpo de la misma en cualquier lugar del programa:
#include <stdio.h> /* Declaracin */ int sumar(int numero1, int numero2); int main(void) { int suma = sumar(5, 3);
printf(La suma es: %d ", suma); return 0; } /* Denicin */ int sumar(int numero1, int numero2) { return numero1
+ numero2; }

7.1.4

Paso de Parmetros

Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas de enviar los datos hacia una
funcin por valor ypor referencia, las cuales modican en diferente forma el comportamiento de el programa.
Por Valor
El paso por valor enva una copia de los parmetros a la funcin por lo tanto los cambios que se hagan en ella no son
tomados en cuenta dentro de la funcin main(). Ejemplo:
/* * por_valor.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void sumar_valor(int numero); /* prototipo de la funcin */ int main(void) { int numero = 57; /* denimos numero con valor de 57*/
sumar_valor(numero); /* enviamos numero a la funcin */ printf(Valor de numero dentro de main() es: %d\n,
numero); /* podemos notar que el valor de numero se modica * slo dentro de la funcin sumar_valor pero en la
principal * nmero sigue valiendo 57 */ return 0; } void sumar_valor(int numero) { numero++; /* le sumamos 1 al
numero */ /* el valor de nmero recibido se aumenta en 1 * y se modica dentro de la funcin sumar_valor() */
printf(Valor de numero dentro sumar_valor() es: %d\n, numero); return; }

Por Referencia
El paso por referencia se hace utilizando apuntadores. Se enva la direccin de memoria de la variable, por lo tanto
los cambios que haga la funcin si afectan el valor de la variable. Ejemplo:
/* * por_referencia.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void sumar_referencia(int
*numero); /* prototipo de la funcin */ int main(void) { int numero = 57; /* denimos numero con valor de 57*/
sumar_referencia(&numero); /* enviamos numero a la funcin */ printf("\nValor de numero dentro de main() es:
%d ", numero); /* podemos notar que el valor de numero se modica * y que ahora dentro de main() tambin se ha
modicado * aunque la funcin no haya retornado ningn valor. */ return 0; } void sumar_referencia(int *numero) {
*numero += 1; /* le sumamos 1 al numero */ /* el valor de numero recibido se aumenta en 1 * y se modica dentro
de la funcin */ printf("\nValor de numero dentro sumar_referencia() es: %d, *numero); return; }

7.2. FUNCIONES RECURSIVAS

7.1.5

21

Variables Locales y Globales

Adems de pasar valores a una funcin, tambin se pueden declarar tipos de datos dentro de las funciones, estos tipos
de datos declarados dentro de una funcin solo son accesibles dentro de esta misma funcin y se les conocen como
variables locales, as pues podemos denir los mismos nombres de variables en diferentes funciones, ya que estas
variables solo son accesibles dentro de esas funciones. Ejemplo:
/* * locales.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void funcion1() { int dato = 53; /* denimos dato en 53*/ char num1 = 'a'; /* num1 vale a */ /* imprimimos */ printf(Funcion1, dato=%d, num1=%c\n,
dato, num1); return; } void funcion2() { int dato = 25; /* denimos dato en 25*/ char num2 = 'z'; /* num2 vale z*/
/* imprimimos */ printf(Funcion2, dato=%d, num2=%c\n, dato, num2); return; } int main(void) { funcion1(); /*
llamamos a funcion1() */ funcion2(); /* llamamos a funcion2() */ return 0; }
En este caso la variable dato, esta denida dentro de cada una de las funciones y son totalmente distinta una de otra
y no se puede utilizar fuera de esta, as pues num2 no puede ser utilizada por la funcion1() y num1 tampoco puede
ser utilizada por funcion2().
Existen pues variables que se denen fuera de la funcin principal main() y fuera de cualquier otra funcin creada
por nosotros, estas variables se les conoce con el nombre de Variables Globales ya que se pueden utilizar dentro de
main() y dentro de cualquier funcin creada por nosotros. Ejemplo:
/* * global.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin
en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> int variable_global = 99; /* inicializamos la variable global */ void funcion(); int main(void) { /* imprimimos el valor*/ printf(main(), acceso a
variable_global %d\n, variable_global); /* llamamos a la funcin */ funcion(); return 0; } void funcion() { /* imprimimos el valor*/ printf(funcion(), acceso a variable_global %d\n, variable_global); return; }

7.2 Funciones Recursivas


La recursividad (recursin) es la propiedad por la cual una funcin se llama a s misma directa o indirectamente.
La recursin indirecta implica utilizar ms de una funcin.
Se puede considerar la recursividad como una alternativa a la iteracin. La recursin permite especicar soluciones
naturales, sencillas, que seran, en caso contrario, difciles de resolver. Toda funcin recursiva debe contemplar un
caso base o condicin de salida, para terminar, o la recursividad no podr terminar nunca.
Una funcin recursiva podra denirse as:
funcion_recursiva( /* parmetros recibidos por la funcin */ ) { /* Cdigo */ funcion_recursiva( ); /* llamada a la
funcin misma */ /* Cdigo */ }
Uno de los ejemplos ms representativos en la recursividad es el factorial de un numero ( n! ):

n! =

n N

k=1

la denicin de recursividad del factorial es:


{
n! =

1
n(n 1)!

Si n = 0
Si n > 0

n N.

En esta denicin, n = 0, es nuestro caso base, que le da n a la recursividad.


Entonces nuestro programa que calcula el factorial es:
/* *factorial.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int n) { if (n == 0) /* caso

22

CAPTULO 7. PROGRAMACIN EN C/USO DE FUNCIONES

base */ return 1; /* como 0! = 1, se retorna 1*/ else return n * factorial (n - 1); /* llamada a esta misma funcin */ } int
main(void) { /* en este caso se llama a la funcin y se imprime directamente*/ printf("%ld ", factorial(5)); return 0; }
Tambin existen otros tipos de funciones recursivas como lo es el producto de dos nmeros. El producto de a b,
donde a y b son nmeros enteros positivos seria:
Solucin iterativa:
a + a + + a b
{z
}=
ab=|
i=1 a
b veces
Solucin recursiva:
{
0
ab=
a + a (b 1)

Si b = 0
Si b > 0

As pues 7 3 es:

7 3 = 7 + 7 2 = 7 + 7 + 7 1 = 7 + 7 + 7 + 0 = 21
Podemos ver que la multiplicacin de dos nmeros a, b se puede transformar en otro problema ms pequeo multiplicar a por (b-1), el caso base se produce cuando b = 0 y el producto es 0. Ejemplo:
/* * producto.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin
en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> int producto(int a, int b) { if (b
== 0) /* caso base */ return 0; /* como b = 0, se retorna 0*/ else return a + producto (a, b - 1); /* llamada a esta
misma funcin */ } int main(void) { /* en este caso se llama a la funcin y se imprime directamente*/ printf("%i ",
producto( 7, 3)); return 0; }

7.2.1

Recursividad indirecta o recursin mutua

Esta se produce cuando una funcin llama a otra, que esta a su vez terminar llamando de nuevo a la primera funcin.
El siguiente programa visualiza el alfabeto utilizando recursin indirecta o mutua:
/* * elalfabeto.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin
en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void funcionA(char c); /* se declara
el prototipo de la funcin para que el llamado */ void funcionB(char c); /* a la misma en la funcin no sea implcita
*/ int main(void) { funcionA('z'); /* llamado a funcionA */ return 0; } void funcionA(char c) { if (c > 'a') /* caso
base mientras c no sea menor que A */ funcionB(c); /* llamado a la funcionB */ printf("%c ", c); /* imprimimos el
valor de c */ *la variable es un parametro no utilizado para este proceso } void funcionB(char c) { funcionA(--c); /*
llamado a la funcionA decrementando el valor de 'z' */ }

7.2.2

Recursin versus Iteracin

Tanto la iteracin como la recursin se basan en estructura de control: la iteracin utiliza una estructura repetitiva y
la recursin una estructura de seleccin. La iteracin utiliza explcitamente una estructura repetitiva mientras que la
recursin consigue la repeticin mediante llamadas repetitivas a funciones.
La iteracin termina si la condicin del bucle no se cumple, mientras que la recursin termina cuando se reconoce
un caso base.
La recursin puede presentar desventajas ante la iteracin ya que se invoca repetidas veces al mecanismo de llamada
de funciones y se necesita un tiempo mayor para realizar cada llamada.
La razn por la cual se puede elegir u optar por usar recursividad es que existen muchos problemas complejos que
poseen naturaleza recursiva y, en consecuencia, son mas fciles de implementar.

7.2. FUNCIONES RECURSIVAS

23

Ejemplo Iterativo
/* * iterativo.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int numero); int main(int
argc, char** argv) { int contador = 0; /* calcula el factorial de 0 a 10 */ for ( contador = 0; contador <= 10; contador++
) printf("%d! = %ld\n, contador, factorial( contador )); return 0; } /* funcion factorial iterativa */ long factorial( int
numero ) { long resultado = 1; int i = 0; /* declaracion de la funcin factorial iterativa */ for ( i = numero; i >= 1; i-) resultado *= i; return resultado; }

Ejemplo Recursivo
/* * recursivo.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int numero); int
main(int argc, char** argv) { int contador = 0; /* calcula el factorial de 0 a 10 */ for ( contador = 0; contador <= 10;
contador++ ) printf("%d! = %ld\n, contador, factorial( contador )); return 0; } /* funcin factorial recursiva */ long
factorial( int numero ) { if ( numero <= 0 ) /* caso base */ return 1; /* casos bases: 0! = 1 y 1! = 1 */ else /* llamada
recursiva */ return numero * factorial( numero - 1 ); /* llamada a la funcin factorial */ }

Captulo 8

Programacin en C/Vectores
Los vectores son una forma de almacenar datos que permiten contener una serie de valores del mismo tipo, cada
uno de los valores contenidos tiene una posicin asociada que se usar para accederlos. Est posicin o ndice ser
siempre un nmero entero positivo.
En C la cantidad de elementos que podr contener un vector es jo, y en principio se dene cuando se declara el
vector. Los vectores se pueden declarar de la siguiente forma:
tipo_elemento nombre[largo];
Esto declara la variable nombre como un vector de tipo_elementos que podr contener largo cantidad de elementos,
y cada uno de estos elemento podr contener un valor de tipo tipo_elemento.
Por ejemplo:
double valores[128];
En este ejemplo declaramos un vector de 128 elementos del tipo double, los ndices de los elementos iran entre 0
(para el primer elemento y 127 para el ltimo).
De la misma forma que con las otras declaraciones de variables que hemos visto se le puede asignar un valor iniciar
a los elementos.
O tambin se pueden declarar:
tipo_elemento nombre[largo]={valor_0, valor_1, valor_2};
En caso estamos asignadole valores a los primeros 3 elementos del vector nombre. Notar que largo debe ser mayor
o igual a la cantidad de valores que le estamos asignando al vector, en el caso de ser la misma cantidad no aporta
informacin, por lo que el lenguaje nos permite escribir:
tipo_elemento nombre[]={valor_0, valor_1, valor_2};
Que declarar nombre como el vector de largo 3.
Para acceder a un elemento accederemos a travs de su posicin. Es decir:
tipo_elemento elemento; ... elemento = nombre[2];
Asumiendo que tenemos el vector anterior denido estaramos guardando valor_2 en elemento.
Veamos algunos ejemplos:
/* * Ejemplo : El producto escalar de dos vectores */ #include <stdio.h> double producto_escalar(double v1[], double v2[], int d); int main() { const int largo = 3; double vector_1[] = {5,1,0}; double vector_2[] = {1,5,3}; double
resultado = producto_escalar(vector_1, vector_2, largo); // imprime el resultado printf("(%f, %f, %f) . (%f, %f,
%f) = %f\n, vector_1[0], vector_1[1], vector_1[2], vector_2[0], vector_2[1], vector_2[2], resultado); return 0; } /*
producto escalar entre dos vectores */ double producto_escalar(double v1[], double v2[], int d) { double resultado =

24

25
0; int i; for (i=0; i < d; i++) { resultado += v1[i] * v2[i]; } return resultado; }
En el ejemplo anterior usamos los vectores de C para representar vectores matemticos y calcular el producto escalar
entre ellos. Una peculiaridad que se puede notar es que al recibir un arreglo en una funcin no se especica el largo,
volveremos a esto en un captulo posterior.
Otra funcin clsica es la bsqueda de un mximo o mnimo, que podemos escribirla de la siguiente manera:
int buscar_maximo(double valores[], int num_valores) { int maximo_pos = 0; for (int i = 1; i < num_valores; i++) {
if (valores[i] > valores[maximo_pos]) { maximo_pos = i; } } return maximo_pos; }
Otro ejemplo sencillo, calcular el promedio de los valores.
double promedio(double valores[], int largo) { double suma=0; for (int i=0;i<largo;i++) { suma+=valores[i]; } return
suma/largo; }
Cuando una funcin recibe un vector por parmetro y cambia su contenido y el cambio es permanente (se ve an
fuera de la funcin). Esto puede parecer extrao despus del nfasis que pusimos en resaltar que todos los parmetros
de una funcin se reciben por valor, pero se aclarar en el siguiente capitulo.
Mientras tanto usemos esto para denir una funcin que le aplique otra funcin que recibe por parmetro a cada
elemento del vector, guardando el resultado en el mismo vector y una llamada de ejemplo a esta.
void cuadrados(double vector[], int largo) { for (int i=0;i<largo;i++) { vector[i]=cuadrado(vector[i]); } } ... double
cuadrado(double valor) { return valor*valor; } ... cuadrados(elementos,num_elem); ...
De la misma forma que venimos usando vectores de tipos bsicos, podemos tener vectores de vectores, estos se
declaran de la siguiente forma:
int matriz[3][7]; int tabla[3][4]={ { 1, 2, 3, 4}, { 5, 6, 7, 8}, /* los espacios y saltos de lneas no son tomados en cuenta */ { 9,10,11,12} }; double v[2][2][2]; ... printf(tabla[0][1]: %i\n, tabla[0][3]); // Imprime 4 printf(tabla[2][0]:
%i\n, tabla[2][0]); // Imprime 9 ...
En este ejemplo tabla es un vector de longitud 3, cuyos elementos son vectores de longitud 4 de elementos de tipo int.
En resumen, suponiendo que v[n] es un vector de cualquier tipo de dato con n cantidad de posiciones, al vector v se
le aplican las siguientes reglas:
1. La primera posicin siempre ser v[0]
2. La ltima posicin es v[n-1]
3. En versiones previas a C99 n es una constante denida antes de la declaracin de v[n]

Captulo 9

Programacin en C/Cadenas de caracteres


Las cadenas de caracteres (tambin llamadas cadenas o strings) son un tipo particular de vectores, son de hecho
vectores de char, con la particularidad que tienen una marca de n (el caracter '\0'), adems el lenguaje nos permite
escribirlas como texto dentro de comillas dobles. Veamos unos ejemplos de su declaracin:
char cadena_hola[]="Hola"; char otro_hola[]={'H','o','l','a','\0'}; // Igual al anterior char vector[]={'H','o','l','a'}; /*
Un vector de 4 elementos, con los elementos 'H','o','l' y 'a' */ char espacio_cadena[1024]="Una cadena en C"; char
cadena_vacia[]="";
Cmo vimos anteriormente al declarar un vector se dene la cantidad de elementos que puede contener, en el caso de
las cadenas se debe tener en cuenta el espacio adicional necesario para el \0. Viendo el ejemplo, tanto cadena_hola
y otro_hola tienen un largo 5 y cadena_vacia tiene un largo de 1.
Tambin vimos anteriormente que al usar vectores debemos tener en cuenta su largo, y as es que el largo o cantidad
de elemento lo necesitamos en todas las funciones que denimos usando vectores y lo recibimos como un parmetro
ms en estas, en el caso de las cadenas al tener una marca de n podemos prescindir del largo y procesar una cadenas
hasta llegar a la marca de n.
Por ejemplo, la siguiente funcin calcula el largo de una cadena:
/* devuelve la cantidad de caracteres en cadena sin contar el '\0' */ int largo_cadena(char cadena[]) { int largo=0
while (cadena[largo]!='\0') largo++; return largo; }
Se debe tener en cuenta que el largo de una cadena y el largo del vector con la que se representa son distintos, tanto
por como largo_cadena() cuenta el largo de la cadena, como por espacio_cadena del ejemplo anterior.
Algo bastante usual es necesitar unir dos cadenas, veamos un ejemplo:
bool unir_cadenas(char destino[], char origen[], int largo) { int largo_origen = largo_cadena(origen); int largo_destino
= largo_cadena(destino); if ( largo_origen+largo_destino+1 > largo ) { return false; } for (int i=0; i<largo_origen;i++)
{ destino[largo_destino+i] = origen[i]; } destino[largo_destino+largo_origen]='\0'; return true; } ... if ( unir_cadenas(espacio_cadena,
que puede crecer hasta 1023 caracteres,1024) ) { ...
Estos dos ejemplos son versiones simplicadas de funciones provistas por la biblioteca estndar de C a travs del
encabezado string.h. Nuestro largo_cadena() es similar al strlen() de la biblioteca estndar, y unir_cadenas() se asemeja al strncat(). Si bien ver estas versiones nos sirven para entender las cadenas en C, en general ser preferible usar
las funciones provistas por la biblioteca estndar, ya que podemos estar seguros que van a estar programadas de la
mejor manera posible.
Entre las funcione que provee la biblioteca estndar de C, las ms importantes son:
largo = strlen(cadena) // Para obtener el largo de una cadena strcpy(destino, origen) // Copia el contenido de origen
en destino // destino debe ser lo sucientemente grande strcat(destino, origen) // Agrega el contenido de origen al
nal de destino // destino debe ser lo sucientemente grande resultado = strcmp(cadena1, cadena2) // Compara dos
cadenas // devuelve un valor menor, igual o mayor que 0 segn si cadena1 es menor, // igual o mayor que cadena2,
respectivamente. posicion = strchr(cadena, caracter) // Devuelve la posicin en memoria de la primer // aparicin

26

27
de caracter dentro de cadena posicion = strstr(cadena,subcadena) // Devuelve la posicin en memoria de la primer //
aparicin de subcadena dentro de cadena
Veamos algunos ejemplos usando <string.h>:
#include <stdio.h> #include <string.h> ... char color[] = rojo"; char grosor[] = grueso"; ... char descripcion[1024];
strcpy(descripcion, Lapiz color "); strncat(descripcion, color, 1024); strncat(descripcion, " de trazo ", 1024); strncat(descripcion, grosor, 1024); // descripcion contiene Lapiz color rojo de trazo grueso ...
void intercambiar(char vector[], int pos1, int pos2); void invierte_cadena(char cadena[]) { int largo = strlen(cadena);
for (int i=0; i < (largo/2); i++) { intercambiar(cadena, i, (largo-1)-i); } } void intercambiar(char vector[], int pos1,
int pos2) { char aux=vector[pos1]; vector[pos1]=vector[pos2]; vector[pos2]=aux; }

Captulo 10

Programacin en C/Manejo de archivos


As como hemos revisado la salida y entrada por pantalla y teclado respectivamente, veremos ahora la entrada y/o
salida de datos utilizando cheros, lo cual ser imprescindible para un gran nmero de aplicaciones que deseemos
desarrollar.

10.1 Ficheros
El estndar de C contiene varias funciones para la edicin de cheros, estas estn denidas en la cabecera stdio.h y
por lo general empiezan con la letra f, haciendo referencia a le. Adicionalmente se agrega un tipo FILE, el cual
se usar como apuntador a la informacin del chero. La secuencia que usaremos para realizar operaciones ser la
siguiente:
Crear un apuntador del tipo FILE *
Abrir el archivo utilizando la funcin fopen y asignndole el resultado de la llamada a nuestro apuntador.
Hacer las diversas operaciones (lectura, escritura, etc).
Cerrar el archivo utilizando la funcin fclose.

10.1.1

fopen

Esta funcin sirve para abrir y crear cheros en disco.


El prototipo correspondiente de fopen es:
FILE * fopen (const char *lename, const char *opentype);
Los parmetros de entrada de fopen son:
lename: una cadena que contiene un nombre de chero vlido. opentype: especica el tipo de chero que se abrir
o se crear.
Una lista de parmetros opentype para la funcin fopen son:
r : abrir un archivo para lectura, el chero debe existir.
w : abrir un archivo para escritura, se crea si no existe o se sobreescribe si existe.
a : abrir un archivo para escritura al nal del contenido, si no existe se crea.
rt : abrir un archivo para lectura y escritura, el chero debe existir.
wt : crear un archivo para lectura y escritura, se crea si no existe o se sobreescribe si existe.
r+b rb+" : Abre un archivo en modo binario para actualizacin (lectura y escritura).
28

10.2. LECTURA

29

rb : Abre un archivo en modo binario para lectura.


Adicionalmente hay tipos utilizando b (binary) los cuales no sern mostrados por ahora y que solo se usan en los
sistemas operativos que no pertenecen a la familia de unix.

10.1.2

fclose

Esta funcin sirve para poder cerrar un chero que se ha abierto.


El prototipo correspondiente de fclose es:
int fclose (FILE *stream);
Un valor de retorno cero indica que el chero ha sido correctamente cerrado, si ha habido algn error, el valor de
retorno es la constante EOF.
Un ejemplo pequeo para abrir y cerrar el archivo llamado chero.in en modo lectura:
#include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { FILE *fp; fp = fopen ( chero.in, r ); if
(fp==NULL) {fputs (File error,stderr); exit (1);} fclose ( fp ); return 0; }
Como vemos, en el ejemplo se utiliz el opentype r, que es para la lectura.
Otra cosa importante es que el lenguaje C no tiene dentro de si una estructura para el manejo de excepciones o de
errores, por eso es necesario comprobar que el archivo fue abierto con xito if (fp == NULL)". Si fopen pudo abrir
el archivo con xito devuelve la referencia al archivo (FILE *), de lo contrario devuelve NULL y en este caso se
debera revisar la direccion del archivo o los permisos del mismo. En estos ejemplos solo vamos a dar una salida con
un retorno de 1 que sirve para sealar que el programa termino por un error.

10.1.3

feof

Esta funcin sirve para determinar si el cursor dentro del archivo encontr el nal (end of file). Existe otra forma
de vericar el nal del archivo que es comparar el caracter que trae fgetc del archivo con el macro EOF declarado
dentro de stdio.h, pero este mtodo no ofrece la misma seguridad (en especial al tratar con los archivos binarios).
La funcin feof siempre devolver cero (Falso) si no es encontrado EOF en el archivo, de lo contrario regresar un
valor distinto de cero (Verdadero).
El prototipo correspondiente de feof es:
int feof(FILE *chero);

10.1.4

rewind

Literalmente signica rebobinar, sita el cursor de lectura/escritura al principio del archivo.


El prototipo correspondiente de rewind es:
void rewind(FILE *chero);

10.2 Lectura
Un archivo generalmente debe verse como un string (una cadena de caracteres) que esta guardado en el disco duro.
Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podramos usar
para leer un archivo son:
char fgetc(FILE *archivo)

30

CAPTULO 10. PROGRAMACIN EN C/MANEJO DE ARCHIVOS


char *fgets(char *buer, int tamano, FILE *archivo)
size_t fread(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
int fscanf(FILE *chero, const char *formato, argumento, ...);

Las primeras dos de estas funciones son muy parecidas entre si. Pero la tercera, por el numero y el tipo de parmetros,
nos podemos dar cuenta de que es muy diferente, por eso la trataremos aparte junto al fwrite que es su contraparte
para escritura.

10.2.1

fgetc

Esta funcin lee un caracter a la vez del archivo que esta siendo sealado con el puntero *archivo. En caso de que la
lectura sea exitosa devuelve el caracter ledo y en caso de que no lo sea o de encontrar el nal del archivo devuelve
EOF.
El prototipo correspondiente de fgetc es:
char fgetc(FILE *archivo);
Esta funcin se usa generalmente para recorrer archivos de texto. A manera de ejemplo vamos a suponer que tenemos
un archivo de texto llamado prueba.txt en el mismo directorio en que se encuentra el fuente de nuestro programa.
Un pequeo programa que lea ese archivo ser:
#include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracter; archivo = fopen(prueba.txt,"r);
if (archivo == NULL){ printf("\nError de apertura del archivo. \n\n); }else{ printf("\nEl contenido del archivo de
prueba es \n\n); while (feof(archivo) == 0) { caracter = fgetc(archivo); printf("%c,caracter); } } fclose(archivo);
return 0; }

10.2.2

fgets

Esta funcin est diseada para leer cadenas de caracteres. Leer hasta n-1 caracteres o hasta que lea un cambio de
lnea '\n' o un nal de archivo EOF. En este ltimo caso, el carcter de cambio de lnea '\n' tambin es ledo.
El prototipo correspondiente de fgets es:
char *fgets(char *buer, int tamao, FILE *archivo);
El primer parmetro buer lo hemos llamado as porque es un puntero a un espacio de memoria del tipo char (podramos usar un arreglo de char). El segundo parmetro es tamao que es el limite en cantidad de caracteres a leer
para la funcion fgets. Y por ultimo el puntero del archivo por supuesto que es la forma en que fgets sabra a que
archivo debe leer.
#include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracteres[100]; archivo = fopen(prueba.txt,"r);
if (archivo == NULL) exit(1); printf("\nEl contenido del archivo de prueba es \n\n); while (feof(archivo) == 0) {
fgets(caracteres,100,archivo); printf("%s,caracteres); } system(PAUSE); fclose(archivo); return 0; }
Este es el mismo ejemplo de antes con la diferencia de que este hace uso de fgets en lugar de fgetc. La funcin fgets
se comporta de la siguiente manera, leer del archivo apuntado por archivo los caracteres que encuentre y a ponerlos
en buer hasta que lea un caracter menos que la cantidad de caracteres especicada en tamao o hasta que encuentre
el nal de una linea (\n) o hasta que encuentre el nal del archivo (EOF). En este ejemplo no vamos a profundizar
mas que para decir que caracteres es un buer, los pormenores seran explicados en la seccin de manejo dinmico
de memoria.
El benecio de esta funcin es que se puede obtener una linea completa a la vez. Y resulta muy til para algunos nes
como la construccin de un parser de algn tipo de archivo de texto.

10.3. ESCRITURA

10.2.3

31

fread

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
Esta funcin lee un bloque de una stream de datos. Efecta la lectura de un arreglo de elementos count, cada
uno de los cuales tiene un tamao denido por size. Luego los guarda en el bloque de memoria especicado por
ptr. El indicador de posicin de la cadena de caracteres avanza hasta leer la totalidad de bytes. Si esto es exitoso la
cantidad de bytes ledos es (size*count).
PARAMETROS:
ptr : Puntero a un bloque de memoria con un tamao mnimo de (size*count) bytes.
size : Tamao en bytes de cada elemento (de los que voy a leer).
count : Nmero de elementos, los cuales tienen un tamao size.
stream: Puntero a objetos FILE, que especica la cadena de entrada.

10.2.4

fscanf

La funcin fscanf funciona igual que scanf en cuanto a parmetros, pero la entrada se toma de un chero en lugar del
teclado.
El prototipo correspondiente de fscanf es:
int fscanf(FILE *chero, const char *formato, argumento, ...);
Podemos ver un ejemplo de su uso, abrimos el documento chero.txt en modo lectura y leyendo dentro de el.
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buer[100]; fp = fopen ( chero.txt, r );
fscanf(fp, "%s ,buer); printf("%s,buer); fclose ( fp ); return 0; }

10.3 Escritura
As como podemos leer datos desde un chero, tambin se pueden crear y escribir cheros con la informacin que
deseamos almacenar, Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones
que podramos usar para escribir dentro de un archivo son:
int fputc(int caracter, FILE *archivo)
int fputs(const char *buer, FILE *archivo)
size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
int fprintf(FILE *archivo, const char *formato, argumento, ...);

10.3.1

fputc

Esta funcin escribe un carcter a la vez del archivo que esta siendo sealado con el puntero *archivo. El valor de
retorno es el carcter escrito, si la operacin fue completada con xito, en caso contrario ser EOF.
El prototipo correspondiente de fputc es:
int fputc(int carcter, FILE *archivo);
Mostramos un ejemplo del uso de fputc en un chero.txt, se escribira dentro del chero hasta que presionemos la
tecla enter.
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char caracter; fp = fopen ( chero.txt, r+" );
printf("\nIntrouce un texto al chero: "); while((caracter = getchar()) != '\n') { fprintf("%c, fputc(caracter, fp)); }

32

CAPTULO 10. PROGRAMACIN EN C/MANEJO DE ARCHIVOS

fclose ( fp ); return 0; }

10.3.2

fputs

La funcin fputs escribe una cadena en un chero. No se aade el carcter de retorno de lnea ni el carcter nulo
nal. El valor de retorno es un nmero no negativo o EOF en caso de error. Los parmetros de entrada son la cadena
a escribir y un puntero a la estructura FILE del chero donde se realizar la escritura.
El prototipo correspondiente de fputs es:
int fputs(const char *buer, FILE *archivo)
para ver su funcionamiento mostramos el siguiente ejemplo:
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char cadena[] = Mostrando el uso de fputs en un
chero.\n"; fp = fopen ( chero.txt, r+" ); fputs( cadena, fp ); fclose ( fp ); return 0; }

10.3.3

fwrite

Esta funcin est pensada para trabajar con registros de longitud constante y forma pareja con fread. Es capaz de
escribir hacia un chero uno o varios registros de la misma longitud almacenados a partir de una direccin de memoria
determinada. El valor de retorno es el nmero de registros escritos, no el nmero de bytes. Los parmetros son: un
puntero a la zona de memoria de donde se obtendrn los datos a escribir, el tamao de cada registro, el nmero de
registros a escribir y un puntero a la estructura FILE del chero al que se har la escritura.
El prototipo correspondiente de fwrite es:
size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
Un ejemplo concreto del uso de fwrite con su contraparte fread y usando funciones es:

10.3.4

fprintf

La funcin fprintf funciona igual que printf en cuanto a parmetros, pero la salida se dirige a un archivo en lugar de
a la pantalla.
El prototipo correspondiente de fprintf es:
int fprintf(FILE *archivo, const char *formato, argumento, ...);
Podemos ver un ejemplo de su uso, abrimos el documento chero.txt en modo lectura/escritura y escribimos dentro
de el.
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buer[100] = Esto es un texto dentro del
chero."; fp = fopen ( chero.txt, r+" ); fprintf(fp, buer); fprintf(fp, "%s, "\nEsto es otro texto dentro del chero.); fclose ( fp ); return 0; }

Captulo 11

Programacin en C/Estructuras y Uniones


En la creacion de soluciones para algunos problemas surge la necesidad de agrupar datos de diferente tipo o de
manejar datos que serian muy dicil de describir en los tipos de datos primitivos, esta es la situacion en la que
debemos aprovecharnos de las caracteristicas que hacen al lenguaje C especial, o sea el uso de estructuras, uniones y
punteros.

11.1 Estructuras
Una estructura contiene varios datos. La forma de denir una estructura es haciendo uso de la palabra clave struct.
Aqui hay ejemplo de la declaracion de una estructura:
struct mystruct { int int_member; double double_member; char string_member[25]; } variable;
variable es una instancia de mystruct y no es necesario ponerla aqu. Se podria omitir de la declaracion de mystruct y ms tarde declararla usando:
struct mystruct variable;
Tambin es una prctica muy comn asignarle un alias o sinnimo al nombre de la estructura, para evitar el tener que
poner struct mystruct cada vez. C nos permite la posibilidad de hacer esto usando la palabra clave typedef, lo que
crea un alias a un tipo:
typedef struct { ... } Mystruct;
La estructura misma no tiene nombre (por la ausencia de nombre en la primera linea), pero tiene de alias Mystruct.
Entonces se puede usar as:
Mystruct variable;
Note que es una convencion, y una buena costumbre usar mayscula en la primera letra de un sinnimo de tipo. De
todos modos lo importante es darle algn identicador para poder hacer referencia a la estructura: podriamos tener
una estructura de datos recursiva de algn tipo.
Ejemplo de una estructura :
/* * estructura.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico * * Nombre Miembro Tipo * Titulo
char[30] * Artista char[25] * Precio oat * Total Canciones int */ #include <stdio.h> #include <string.h> /* denimos una estructura para cds */ struct cd { char titulo[30]; char artista[25]; oat precio; int canciones; } Cd1 = { /*
inicializamos la estructura Cd1 creaa con sus valores * usando las deniciones iniciales*/ Canciones Bebe, /* titulo
*/ Pinocho, /* artista */ 12.50, /* precio */ 16 /* total canciones */ }; int main(void) { struct cd Cd2; /* denimos
una nueva estructura llamado cd2 */ /* asignamos valores a los tipos de datos del cd2 */ strcpy(Cd2.titulo, New
Age); /* la forma de insertar valores a un * tipo char en una estructura es usando strcpy * de la libreria string.h
33

34

CAPTULO 11. PROGRAMACIN EN C/ESTRUCTURAS Y UNIONES

*/ strcpy(Cd2.artista, Old Man); Cd2.precio = 15.00; Cd2.canciones = 12; /* la forma de acceder a los valores de
una estructura */ /* es usando el ". despues de la denicion del dato*/ printf("\n Cd 1); printf("\n Titulo: %s ",
Cd1.titulo); printf("\n Artista: %s ", Cd1.artista); printf("\n Total Canciones: %d ", Cd1.canciones); printf("\n Precio
Cd: %f ", Cd1.precio); printf("\n); printf("\n Cd 2); printf("\n Titulo: %s ", Cd2.titulo); printf("\n Artista: %s ",
Cd2.artista); printf("\n Total Canciones: %d ", Cd2.canciones); printf("\n Precio Cd: %.2f ", Cd2.precio); /* el .2
que esta entre %f * sirve para mostrar unicamente * 2 decimales despues del punto*/ return 0; }

11.1.1

Estructuras Anidadas

Una estructura puede estar dentro de otra estructura a esto se le conoce como anidamiento o estructuras anidadas.
Ya que se trabajan con datos en estructuras si denimos un tipo de dato en una estructura y necesitamos denir ese
dato dentro de otra estructura solamente se llama el dato de la estructura anterior.
Denamos una estructura en nuestro programa:
struct empleado /* creamos una estructura llamado empleado*/ { char nombre_empleado[25]; char direccion[25];
char ciudad[20]; char provincia[20]; long int codigo_postal; double salario; }; /* las estructuras necesitan punto y
coma (;) al nal */
Y luego necesitamos una nueva estructura en nuestro programa:
struct cliente /* creamos una estructura llamada cliente */ { char nombre_cliente[25]; char direccion[25]; char ciudad[20]; char provincia[20]; long int codigo_postal; double saldo; }; /* las estructuras necesitan punto y coma (;) al
nal */
Podemos ver que tenemos datos muy similares en nuestras estructuras, asi que podemos crear una sola estructura
llamada infopersona con estos datos idnticos:
struct infopersona /* creamos la estructura que contiene datos parecidos */ { char direccion[25]; char ciudad[20];
char provincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al nal */
Y crear las nuevas estructuras anteriores, anidando la estructura necesaria:
struct empleado /* se crea nuevamente la estructura */ { char nombre_empleado[25]; /* creamos direcc_empleado
con struct del tipo estructura infopersona */ struct infopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al nal */ <source lang=c> struct cliente /* se crea nuevamente la estructura */ { char
nombre_cliente[25]; /* creamos direcc_cliente con struct del tipo estructura infopersona */ struct infopersona
direcc_cliente; double saldo; }; /* las estructuras necesitan punto y coma (;) al nal */
Y ac el ejemplo completo con estructuras anidadas:
/* * estructura2.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico * * Nombre Miembro Tipo * *
Titulo char[30] * Artista char[25] * Precio oat * Total Canciones int */ #include <stdio.h> #include <string.h>
/* creamos nuestra estructura con datos similares */ struct infopersona { char direccion[25]; char ciudad[20]; char
provincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al nal */ /* creamos nuestra estructura empleado */ struct empleado { char nombre_empleado[25]; /* agregamos la estructura infopersona
* con nombre direcc_empleado */ struct infopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al nal */ /* creamos nuestra estructura cliente */ struct cliente { char nombre_cliente[25]; /*
agregamos la estructura infopersona * con nombre direcc_cliente */ struct infopersona direcc_cliente; double saldo;
}; /* las estructuras necesitan punto y coma (;) al nal */ int main(void) { /* creamos un nuevo cliente */ struct
cliente MiCliente; /*inicializamos un par de datos de Micliente */ strcpy(MiCliente.nombre_cliente,"Jose Antonio); strcpy(MiCliente.direcc_cliente.direccion, Altos del Cielo); /* notese que se agrega direcc_cliente haciendo
referencia * a la estructura infopersona por el dato direccion */ /* imprimimos los datos */ printf("\n Cliente: ");
printf("\n Nombre: %s, MiCliente.nombre_cliente); /* notese la forma de hacer referencia al dato */ printf("\n
Direccion: %s, MiCliente.direcc_cliente.direccion); /* creamos un nuevo empleado */ struct empleado MiEmpleado; /*inicializamos un par de datos de MiEmplado */ strcpy(MiEmpleado.nombre_empleado,"Miguel Angel);
strcpy(MiEmpleado.direcc_empleado.ciudad,"Madrid); /* para hacer referencia a ciudad de la estructura infoperso-

11.2. UNIONES

35

na * utilizamos direcc_empleado que es una estructura anidada */ /* imprimimos los datos */ printf("\n); printf("\n
Empleado: "); printf("\n Nombre: %s, MiEmpleado.nombre_empleado); /* notese la forma de hacer referencia al
dato */ printf("\n Ciudad: %s, MiEmpleado.direcc_empleado.ciudad); return 0; }

11.2 Uniones
La denicion de union es similar a la de estructura, La diferencia entre las dos es que en una estructura, los
miembros ocupan diferentes areas de la memoria, pero en una union, los miembros ocupan la misma area de memoria.
Entonces como ejemplo:
union { int i; double d; } u;
El programador puede acceder a travs de u.i o de u.d, pero no de ambos al mismo tiempo. Como u.i y u.d
ocupan la misma rea de memoria, modicar uno modica el valor del otro, algunas veces de maneras impredecibles.
El tamao de una union es el de su miembro de mayor tamao.
Ejemplo de una unin:
/* * uniones.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> #include <string.h>
/*Creamos una union*/ union frases { char mensajes[50]; char ayudas[50]; char lineas[50]; } palabra; /*Creamos una estructura*/ struct comparte { char mensajes[50]; char ayudas[50]; char lineas[50]; }Sistema; /*Ntese
que la estructura y la union tienen los mismos tipos de datos*/ int main(int argc, char** argv) { /*Inicializamos*/
strcpy(palabra.mensajes, Primer Mensaje); /*Inicializamos*/ strcpy(palabra.ayudas, Una Ayuda); printf("\nFrases
en Union: "); /*Imprimimos mensajes de union*/ printf("\n1- %s, palabra.mensajes); /*Imprimimos ayudas de
union*/ printf("\n2- %s, palabra.ayudas); /*Inicializamos*/ strcpy(Sistema.mensajes, Primer Mensaje); /*Inicializamos*/ strcpy(Sistema.ayudas, Una Ayuda); /* Podemos notar que aunque inicializamos los valores * al imprimir
se tiene el mismo valor para cada miembro * de la estructura, esto se debe a que las uniones usan el * mismo espacio
de memoria para todos los elementos * de la union, siendo del tamao de su miembro de * mayor tamao, en este
caso 50 bytes. * Entonces los tres miembros creados dentro de la * union comparten esos 50 bytes. * Entonces el
ultimo valor agregado a la union es * el que se tiene. */ printf("\n\nFrases en Struct: "); /*Imprimimos mensajes de
struct*/ printf("\n1- %s, Sistema.mensajes); /*Imprimimos ayudas de union*/ printf("\n2- %s, Sistema.ayudas);
/* En la estructura comparte, se reservan 150 bytes * de memoria para los tres miembros, en este caso * cada uno
es independiente en memoria, asi pues se * puede inicializar cada uno o usar como un campo * independiente. */
return 0; }

11.3 Enumeraciones
Una enumeracion (enum) es un tipo denido con constante de tipo entero. En la declaracion de un tipo enum creamos
una lista de tipo de datos que se asocian con las constantes enteras 0, 1, 2, 3, 4, 5...
su forma de denirlas es la siguiente:
enum { enumerador1, enumerador2, enumeradorn }; enum Nombre { enumerador1, enumerador2, enumeradorn };
En este caso al ser declaradas enumerador1 toma el valor entero de 0, enumerador2 el valor de 1 y asi sucesivamente
para cada una de las expresiones siguientes.
Al declarar la enum se puede asociar a los tipos de datos a valores constantes en vez de la asociacion que por defecto
se realiza (0, 1, 2, ), se utiliza entonces este formato:
enum Nombre { enumerador1 = valor_constante1, enumerador2 = valor_constante2, ... enumeradorn = valor_constanten,
};
Un ejemplo de una enum:

36

CAPTULO 11. PROGRAMACIN EN C/ESTRUCTURAS Y UNIONES

enum Boolean { FALSE, TRUE };


Se denen dos constantes para las constantes true y false con valores iguales a 0 para False y 1 para True.
Ejemplo:
/* * Enum.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C
(fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> enum Boolean { FALSE,
TRUE }; /* Se dene un enum para emular las constantes * True y False con valores de 0 y 1. * Notese que las enum
no necesitan ; al nal * de cada tipo de dato. */ /* Denimos una funcion del tipo enum llamada numero*/ enum
Boolean numero(char c); int main(int argc, char** argv) { char caracter; int Numeros = 0; printf("\nIntroduce un
texto. Para terminar: Enter. \n\t); /* Tenemos un while que mientras no se presione Enter * seguira leyendo un tipo
de dato caracter */ while((caracter = getchar()) != '\n') { if (numero(caracter)) { Numeros++; } } printf("\nTotal
de Numeros leidos: %d, Numeros); return 0; } enum Boolean numero(char c) { switch(c) { case '0': case '1': case
'2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return TRUE; /* Mientras el caracter valga de 0 a 9
retornara TRUE (1) */ default: return FALSE; /* Por default retornara FALSE (0) */ } }
En la siguiente enum se declaran las variables inicializando la primera y las demas con los siguientes valores enteros:
/* * Enum2.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> enum DiasSemanas {
Domingo = 1, Lunes, Marte, Miercoles, Jueves, Viernes, Sabado }; /* Podemos inicializar nuestra primer constante
Domingo * en 2, asi pues las demas los siguientes valores enteros. */ int main(int argc, char** argv) { enum DiasSemanas dia; for (dia = Domingo; dia <= Sabado; dia++) { printf("%d ", dia); /* Salida: 1 2 3 4 5 6 7 */ } return 0; }
A los enumeradores se pueden asignar valores o expresiones constantes durante la declaracion:
enum Hexaedro { VERTICE = 8, LADOS = 12, CARAS = 6 };

Captulo 12

Programacin en C/Punteros

37

Captulo 13

Concepto
Un puntero es una variable que contiene la direccin de memoria de un dato o de otra variable que contiene al dato.
Quiere esto decir, que el puntero apunta al espacio fsico donde est el dato o la variable. Un puntero puede apuntar a
un objeto de cualquier tipo, como por ejemplo, a una estructura o una funcin. Los punteros se pueden utilizar para
referencia y manipular estructuras de datos, para referenciar bloques de memoria asignados dinamicamente y para
proveer el paso de argumentos por referencias en las llamadas a funciones.
Muchas de las funciones estandares de C, trabajan con punteros, como es el caso del scanf o strcpy. Estas reciben o
devuelve un valor que es un puntero. Por Ej. A scanf se le pasa la direccin de memoria del dato a leer (esto es un
puntero)...
char a; scanf ("%c,&a);

38

Captulo 14

Declarando punteros
Ya se dijo que un puntero es una variable que guarda la direccin de memoria de otra variable, haciendo logica a
esto, decimos que un puntero se declara igual que cualquier otra variable, pero anteponiendo un * (asterisco) antes
del nombre de la variable.
Su sintaxis seria:
tipo *NombrePuntero;
Donde tipo es el tipo de dato al que referenciar este puntero, es decir, que si se necesita guardar la direccin de
memoria de un dato int, se necesita un puntero de tipo int.

39

Captulo 15

Explicacin
Veamos el siguiente codigo:
#include <stdio.h> int main() { int a=0; //Declaracin de variable entera de tipo entero int *puntero; //Declaracin
de variable puntero de tipo entero puntero = &a; //Asignacin de la direccin memoria de a printf(El valor de a es:
%d. \nEl valor de *puntero es: %d. \n,a,*puntero); printf(La direccion de memoria de *puntero es: %p,puntero);
return 0; }
Igual que cuando usamos un &, en la lectura de datos con scanf, igual de esta forma lo usamos aqu, tal vez te
acordaras que decamos que las cadenas de caracteres (%s) no usaban este operador, esto es por que en una cadena
de caracteres es un arreglo de caracteres, por lo que el primer carcter es la direccin de inicio de la cadena.
El operador *, nos permite acceder al valor de la direccion del puntero, en este caso nos permite acceder al valor que
contiene a la variable a. De esta forma a y "*puntero muestran el mismo dato, pero esto no quiere decir que sea lo
mismo, uno es un entero el otro un puntero.
La impresin con %p, es para poder imprimir la direccin de memoria en valor hexadecimal (0x...), tambin podemos
imprimir: ...%p,&a) y funciona de la misma forma, y es lgico que tanto a, como puntero tienen la misma direccin
de memoria.
Diferentes direcciones?
Tal vez notaste que en cada ejecucin la direccin de memoria cambia, esto es por que es el sistema operativo es
quien esta encargado de administrar la memoria y es este quien dice que espacios podra tomar el programa.
Esto quiere decir que uno no puede asignarle una direccin de memoria a un puntero directamente, es decir yo no
puedo hacer lo siguiente.
int *puntero=0xbfc5b1c8;
Esto no puedo ni debo hacerlo ya que yo no se que esta haciendo esta direccin de memoria, si el sistema la tiene o
no disponible, etc... Pero si puedo hacer esto:
int *puntero=NULL;
NULL, es el espacio en memoria con direccin 0, esto quiere decir que existe, lo que signica que le asignamos una
direccion valida al puntero, pero el valor que tiene NULL no se nos permite modicarlo, ya que pertenece al sistema.

40

Captulo 16

Operadores
Ya anteriormente te poniamos algunos ejemplos de como asignar la direccin de memoria a un puntero y de como
acceder al valor de este.
Operador de Direccin (&): Este nos permite acceder a la direccion de memoria de una variable.
Operador de Indireccion (*): Ademas de que nos permite declarar un tipo de dato puntero, tambin nos permite
ver el VALOR que esta en la direccin asignada.
Incrementos (++) y Decrementos (--): Te daras cuenta que puedes usar un puntero como si de un array se tratase,
es por esto que permite estos operadores.
De lo anterior podemos decir que:
int a; Es Igual int *puntero=&a; printf("%d,a); Es Igual printf("%d,*puntero);

41

Captulo 17

Operaciones Aritmticas
Un puntero nos permite sumar o restar nmeros enteros, pero su funcionamiento ahora es de posiciones, es decir nos
permitir movernos a la siguiente direccin de memoria.
A un puntero no se le puede realizar multiplicaciones, divisiones, sumas o restas con otro puntero o con un valor de
tipo coma otante (oat, double...). Esto es por que un puntero no es un valor es una direccin.
En un puntero se pueden realizar varias operaciones de tipo enteras, pero en dependencia de como se usen sus
resultados pueden ser muy diferentes, a continuacin les muestro algunas de estas operaciones:
//Denamos estas variables: int x[100],b,*pa,*pb; //... x[50]=10; //Le asignamos el valor de 10, al array #50 pa=&x[50];
//Le asignamos al puntero pa, la direccion de memoria que tiene x[50] //Ahora mostramos algunas posibles operaciones: b = *pa+1; //Esto es como decir el valor que tiene el array de x[50] sumarle 1. //Esto es igual a: b=x[50]+1; =>
Su valor seria igual a 11. b = *(pa+1); //Esto primero pasa a la siguiente direccion de memoria y luego lo referencia
//El resultado es: b = x[51]; pb = &x[10]; //al puntero pb se le asigna la direccion de x[10] *pb = 0; //Al valor que
tiene el puntero se le asigna 0 //Esto es igual que decir: x[10] = 0 *pb += 2; //El valor del puntero se incrementa
en dos unidades, es decir x[10] = 2 (*pb)--; //El valor del puntero se decrementa en una unidad. x[0] = *pb--; //A
x[0] se le pasa el valor de x[10] y el puntero pb, pasa a apuntar a x[9] //recorda, que -- es post-incremento, primero
asignara y luego restara.

42

Captulo 18

Punteros Constantes
Es posible que hayas pensado como declarar un puntero como una constante, tal vez pensaste en un #dene, o en un
atributo const. Bueno es posible usar el atributo const, pero para un puntero hay que hacerlo de otra forma.
FORMA ERRADA:
int a=10,b=20; const int *p = &a; //objeto constante y puntero variable *p = 15; // ERROR: el valor apuntado por p
es constante. p=&b; //Correcto: p pasa a apuntar a un nuevo objeto.
Pero de esta forma no es muy til declararlo, pues el que hicimos constante fue el valor al que apunte p, es decir,
mejor hubisemos hecho que el puntero fuese una constante.
FORMA CORRECTA:
int a=10,b=20; int * const p = &a; //objeto variable y puntero constante *p = 15; // Correcto: El valor apuntado es
variable. p=&b; //ERROR: p es constante.

43

Captulo 19

Punteros Genericos
Un puntero a cualquier tipo de dato puede convertirse a un puntero del tipo void *. Por esto un puntero a void *,
recibe el nombre de puntero generico.
En C, se permite la conversin implcita de punteros, pero en C++ esto no es posible, asi que por compatibilidad y
buena practica recomendamos usar la conversion explicita (cast).
Supongamos:
int *puntero; funcion (*puntero); .... void funcion (void *p) int *q; q=(int *)p; //En C se podria hacer q = p;
Es decir que un puntero a void se puede usar sin importar el tipo de dato, recuerden que uno no puede trabajar con
punteros que referencia a un tipo de dato diferente, como lo es un puntero a char, con un puntero a int.

44

Captulo 20

Punteros y Matrices
Anteriormente decamos que una matriz es una secuencia de espacios en memoria, que nos permitan alojar datos
en cada uno y un puntero es una variable que guarda la direccin de memoria, tambin decamos como recorre las
direcciones de memoria con los operadores ++ y -.
Aqu veremos como puede usarse un puntero como si de una matriz se tratase, luego de que veas que es tcnicamente
lo mismo, te preguntaras por que usar punteros, pero estos son muy necesarios y nicos que nos permiten realizar
cosas que con un array normal, no se puede, como asignarle memoria dinmica, etc...
#include <stdio.h> int main() { int array[10]={0,2,3,5,5,6,7,8,9,0}; //Declarar e inicializar un array. int *puntero =
&array[0]; //Le damos la direccin de inicio del array int i; //variable contadora... for (i=0;i<10;i++) printf("%d\n,*(puntero+i));
//imprimimos los valores del puntero. return 0; }
Habrs notado que he usado *(puntero+i), as como explica la seccin de operaciones aritmticas, pero tambin
podemos acceder de otras maneras como lo son:
array[i] => Accede como un array normal
(array+i) => Tambin accede como un array normal.
puntero[i] => Accedemos al valor de puntero sub i
(puntero+i) => Accedemos al valor de puntero + i.

45

Captulo 21

Punteros a cadenas de caracteres


Ya hemos visto el uso que se le puede dar a un puntero como si de un array se tratase, entonces usando esta misma
logica podemos hacer un array de caracteres usando punteros.
char *nombre="Gustavo A. Chavarria";//Es como un array de 20 caracteres printf("%s,nombre);
Sin embargo al tratarse de una constante de caracteres no podemos modicarla luego de denir sus valores. Como
por ejemplo no podemos remplazar un carcter, o leer un nuevo valor.
gets(nombre); //ERROR en ejecucin
Para poder modicar el valor de este puntero, este tendra que apuntar a una direccin que no sea una constante,
como un array.
char nombre[]="Gustavo A. Chavarria"; //declaramos un array de caracteres char *puntero=nombre;//Asignamos
al puntero el comienzo del array printf("%s \nIngrese otro nombre: ",puntero);//Escribimos en pantalla nombre...
gets(puntero); //leemos otro nombre printf("%s,puntero); //escribimos el nuevo nombre...
Esta vez pudiste notar que si se pudo remplazar el valor del nombre, pero aun la cantidad de caracteres esta limitada
por el array original, mas adelante veremos como solucionar esto con memoria dinmica.

46

Captulo 22

Matrices de Punteros
Es muy probable que ya te hayas dicho: Si un puntero es una variable, Puedo tener un array de punteros? La respuesta
seria NO Exactamente.
Como ya habiamos explicado antes, un puntero trabaja de la misma forma que un array, tanto que podemos decir que
un puntero es un array, entonces si denimos un array de un array, nos dara como resultados un array bidimensional.
En la practica es mucho mas comn utilizar un array de punteros que un array bidimensional.
Hay que recordar que siguen siendo punteros, es decir solo apuntan a una direccin de memoria, por ende hay que
asignarles la direccin de memoria a cada uno de los elementos del puntero. Sin embargo podemos asignar en un solo
ciclo todas las las.
Ejemplo:
#include <stdio.h> int main() { int *puntero[5]; //array de puntero int a[5][5]; //Array bidimensional. int i; for
(i=0;i<5;i++) puntero[i]=a[i]; //Asignamos las las al puntero. //Pueden imprimir tambien en un ciclo //Tambien
pueden acceder mediante un ciclo anidado a la variables del puntero[i][j] }

47

Captulo 23

Punteros a Punteros
Es una variable que contiene la direccin de memoria de un puntero, el cual a su vez contiene la direccin de memoria
de un tipo de dato. Recuerden que un puntero sigue siendo un espacio en memoria, pero en vez de almacenar un valor
almacena una direccin.
Si decimos que:
int a=0; //Supongamos que es una variable cuya direccion es 0x1601 int *puntero1=&a; //El puntero tiene guardada
la direccion de a, //pero este tiene como direccion 0x1890 int **puntero2=&puntero1; //**puntero 2 guarda la direccion 0x1890
Ahora se entiende mejor. Al uso de punteros se le llama variables con niveles de indireccion, ya que no apuntan
directamente a un valor, sino que apuntan a alguien que lo tiene. Basndonos en esto podemos decir que *puntero1
es un puntero con un nivel de indireccion y *puntero2 es un puntero con dos niveles de indireccion.
En general estos tipos de datos son usados con matrices multidimensionales, es por esto que decid no dejar ejemplos,
pues su uso es bastante fcil de implementar.

48

Captulo 24

Matrices de punteros a cadenas de


caracteres
No hablaremos sobre este tema, debido a que es prcticamente una matriz de caracteres que se usa mediante punteros,
y esto es tcnicamente lo mismo que hemos estado viendo anteriormente.
Tambien podemos usar punteros a punteros y guardar multiples cadenas.

49

Captulo 25

Ejemplos
Tenga en cuenta el siguiente bloque de cdigo que declara 2 punteros
/*1*/ struct MyStruct { /*2*/ int m_aNumber; /*3*/ oat num2; /*4*/ }; /*5*/ /*6*/ int * pJ2; /*7*/ struct MyStruct
* pAnItem;
Las primeras 4 lneas denen la estructura. La linea 6 declara una variable que apuntar a un entero, y la lnea 7
declara una variable que apunta a algo de la estructura MyStruct. Entonces declarar un puntero es algo que apunta a
algo de algn tipo, ms que contener el tipo. El asterisco (*) se coloca antes del nombre de la variable.
En las siguientes lneas de cdigo, var1 es un puntero a un entero largo (long) mientras var2 es un entero largo (long)
y no un puntero a un entero largo. En la segunda lnea se declara p3 como un puntero a un puntero de un entero.
long * var1, var2; int ** p3;
Los punteros se usan habitualmente como parmetros de funciones. El siguiente cdigo muestra como declarar una
funcin que usa un puntero como argumento. Teniendo en cuenta que C pasa todos los argumentos por valor, para
poder modicar un valor desde el cdigo de la funcin, se debe usar un puntero al valor a modicar. Tambin se
usan punteros a estructuras como argumentos de una funcin an cuando la estructura no sea modicada dentro de
la funcin. Esto es realizado para evitar copiar todo el contenido de la estructura dentro de la pila.
int MyFunction( struct MyStruct *pStruct );

25.0.1

Asignando valores a punteros

Continuamos con el proceso de asignar valores a los punteros, para esto utilizamos el operador & o 'la direccin de'.
int myInt; int *pPointer; struct MyStruct dvorak; struct MyStruct *pKeyboard; pPointer = &myInt; pKeyboard =
&dvorak;
Aqu, pPointer apuntara a myInt y pKeyboard apuntara a dvorak.
Los punteros tambin pueden ser asignados a referencias de memorias dinamicas creadas comnmente por las funciones malloc() y calloc().
#include <stdlib.h> ... struct MyStruct *pKeyboard; ... pKeyboard = malloc(sizeof(struct MyStruct)); ...
La funcin malloc retorna un puntero de memoria asignada de manera dinmica (o NULL si falla). El tamao de esta
memoria es denido de modo que pueda contener la estructura MyStruct
El siguiente cdigo es un ejemplo mostrando un puntero siendo asignado a una referencia y se retorna el valor del
puntero en la funcin.
static struct MyStruct val1, val2, val3, val4; ... struct MyStruct *ASillyFunction( int b ) { struct MyStruct *myReturn;
if (b == 1) myReturn = &val1; else if (b==2) myReturn = &val2; else if (b==3) myReturn = &val3; else myReturn
50

51
= &val4; return myReturn; } ... struct MyStruct *strPointer; int *c, *d; int j; ... c = &j; /* puntero asignado usando
el operador & */ d = c; /* asignando un puntero a otro */ strPointer = ASillyFunction( 3 ); /* puntero retornado de
la funcion */
Cuando se retorna un puntero de una funcion, se tiene que tener cuidado de que el valor apuntado no sea de una
referencia de una variable local a la funcion, porque estos valores desaparecen despues de salir de la funcion y el
puntero estaria mal referenciado, causando errores en tiempo de ejecucion en el programa. La memoria creada dinamicamente dentro de la funcion puede devolverse como puntero, el valor de retorno aunque es creado en una variable
local la direccion como tal se devuelve por valor dejando esta informacion valida.

Captulo 26

Programacin en C/Matrices Dinamicas


Esta seccin es mas como un tema mas de asignacin dinmica de memoria, pero por motivos pedaggicos he decidido
dividirlo.

52

Captulo 27

Concepto
Dejando afuera el uso de matrices en C99, en donde uno puede declarar los valores luego de una lectura. Podemos
decir que solo hemos usado matrices estticas, ya que no permitan el cambio de dimensiones una vez que el programa
fuese compilado.
Con la asignacin dinmica de memoria un puede denir en tipo de ejecucin la cantidad de memoria, con las ventajas
y desventajas que esta posee.
Solo para demostrar rapidamente el potencial y el por que usarlo, vean lo siguiente:
Anteriormente si uno deseaba leer una cadena de caracteres, declaraba un array, supongamos que deseamos leer un
nombre, pero, no todos se llaman igual es por esto que debiamos darle suciente espacio al arreglo.
char nombre[100];
Pero si escribimos: Gustavo Chavarria. Solo necesitamos unos 20 caracteres (20 bytes), es decir desperdiciamos
80 bytes en memoria. Imaginate sucediendo esto en un programa muy grande...
Ejemplo:
//Hecho por: Gustavo Chavarria. //UNAN-LEON - Nicaragua. #include <stdio.h> #include <stdlib.h> int main() {
int *puntero=NULL; puntero = (int *)malloc(100*sizeof(int)); //Asignacion dinamica de memoria if (puntero ==
NULL) //evaluacion de condicion. { printf(NO hay suciente espacio en memoria); //Mensaje return 1; //Cierra
el programa con un error. }else printf(Se asigno memoria "); free(puntero);//liberacion de memoria return 0; } //El
programa solo asigna memoria, imprime un mensaje en dependecia de la condicion y libera //Revisado por: Gustavo
Chavarria. //UNAN-LEON - Nicaragua.

53

Captulo 28

Matrices Dinamicas de Dos Dimensiones


En esta parte aprenderemos como usar la asignacion dinamica en matrices bidimensionales. Este proceso se divide
en dos partes:
Asignar memoria a una matriz de punteros, cuyos elementos referenciaran cada una de las las de la matriz de
dos dimensiones que se desea crear.
Asignar memoria para cada una de las las. El numero de elementos de cada la puede ser variable.
Una vista rapida seria:
#dene FILAS 5 #dene COLS 6 ... int **matriz; matriz = (int **)malloc (FILAS*sizeof(int *)); for (i=0;i<FILAS;i++)
matriz[i] = (int *) malloc (COLS*sizeof(int));
Segun el codigo anterior podemos decir que cada la puede tener una cantidad de columnas de diferentes tamaos.
El uso del for, es para poder acceder a cada la (matriz[i]) asignandole memoria.

54

Captulo 29

Programacin en C/Algoritmos y
Estructuras de Datos

55

Captulo 30

Concepto
Uno de los procedimientos ms comunes y tiles en el procesamiento de datos, es la ordenacin de los mismos. Se
considera ordenar al proceso de reorganizar un conjunto dado de objetos en una secuencia determinada (patrn de
arreglo). El objetivo de este proceso generalmente es facilitar la bsqueda de uno o ms elementos pertenecientes a
un conjunto.
Como ejemplos de conjunto de datos ordenados tenemos:
Meses del ao (ordenados de 1 al 12).
Listado de estudiantes (ordenados alfabeticamente).
Guias Telefonicas (ordenadas por Pais/por region/por sector/por orden alfabetico)
La ordenacion, tanto numerica como alfanumerica, sigue las mismas reglas que empleamos nosotros en la vida normal.
Esto es, un dato numerico es mayor que otro cuando su valor es mas grande, y una cadena de caracteres es mayor que
otra cuando esta despues por orden alfabetico.
Los metodos de ordenacion, pueden agruparse en dos grandes grupos:
Los internos: Es cuando los datos estan disponibles de una area de la memoria principal, como cuando se leen
un conjunto de datos desde el teclado.
Los externos: Los datos estan guardados en un medio externo, como puede ser un chero, una base de datos,
etc. En donde los datos estan alojados en el disco duro u otro medio sico.
En este seccion explicaremos solo tres modos de ordenamiento, los mas usados como son:
Algoritmo de Ordenamiento Burbuja (Buble Sort)
Algoritmo de Ordenamiento Insercion
Algoritmo de Ordenamiento Quick Sort

56

Captulo 31

Metodo de Burbuja (Buble Sort)


El metodo de ordenamiento de burbuja, es un algoritmo que se aplica para poder ordenar una cantidad de datos ya
sea de forma ascendente o descendente.
Es el algoritmo ms facil de implementar, pero a cambio pagamos un alto precio en procesamiento, ya que este
metodo evalua una cantidad los datos muchas veces y en ocasiones innecesariamente (como por ejemplo cuando son
iguales).
A estas alturas posiblemente ya tengas conocimiento de sencillos pasos para ordenar datos, como por ejemplo, Determinar cual es el mayor o menor de dos numeros, pues aplicando este metodo podremos ordenar array, estructuras
y cualquier tipo de dato NO atomico (es decir que se pueda dividir)
Este libro trata de programacin en C, entonces a continuacin nos aplicamos a esto:

31.1 Explicacion
Este metodo necesita de lo siguiente para implementarse:
Un array o estructura que ordenar (>1 elemento).
Dos variables contadoras de ciclos (i,j por ejemplo).
Una variable temporal (para almacenar un dato momentaneamente).
Dos ciclos y un Condicional...
.... //DE MENOR A MAYOR (Ascendente) #dene Nelementos 4 .... int i,j; //Variables contadoras del ciclo. int
lista[Nelementos]={6,9,3,1}; //Declaracion e inicializacion de un arreglo de 4 elementos. int temp=0; //Variable
temporal. for (i=1;i<Nelementos;i++) { for (j=0; j <= Nelementos-1 ;j++) { if (lista[j] > lista[j+1])//Condicion
mayor-menor { temp=lista[j]; lista[j]=lista[j+1]; lista[j+1]=temp; } } } //Para cambiar el modo de ordenamiento
solo debemos cambiar la condicion < > '''<big>Explicando un poco lo que dice el codigo tenemos:</big>''' # Iniciamos i a 1, de esta forma correremos el ciclo solamente 3 veces. Asi evitamos correr ciclos innecesariamente. # El
segundo for, se ejecutara 3 veces por cada primer ciclo. # La condicion nos dice: * Si, el valor de lista 0 es mayor
al valor de lista 1, es decir * '''Si, 6 > 9''', pero como la condicion no se cumple, pasamos del ciclo y '''J=1'''. * Si,
el valor de lista 1 es mayor al valor de lista 2, es decir * '''Si, 9 > 3''', como es '''verdadera''' hacemos: # Guardamos
momentaneamente en la variable temporal el valor de lista 1, es decir 9. # En la posicion de lista 1, guardamos el valor
de lista 2, es decir 3. # En la posicion de lista 2, guardamos el valor de temp, es decir 9 '''Volvemos'' nuevamente
'''al ciclo''', ahora '''J=2'''... * Si, el valor de lista 2 es mayor al valor de lista 3, es decir * Si, '''9 > 1''', (recuerda que
anteriormente '''movimos'' al 9 a la posicion de 3), es verdadera => # Guardamos momentaneamente en la variable
temporal el valor de lista 2, es decir 9. # En la posicion de lista 2, guardamos el valor de lista 3, es decir 1. # En la
posicion de lista 3, guardamos el valor de temp, es decir 9. De esta forma el ciclo se repite hasta que todos los datos
se hallan movido. Como veras hasta ahora solo hemos estado moviendo el 9. Tu lista se veria asi: * ''Original:'' 6
'''9''' 3 1 :* ''1er Mov:'' 6 3 '''9''' 1 :* ''2do Mov:'' 6 3 1 '''9''' Si bien ya esta mas ordenada que la original, aun falta
bastante, pero recuerda que estbamos en el primer ciclo del primer for (i). '''Aca te dejo como seran los demas
57

58

CAPTULO 31. METODO DE BURBUJA (BUBLE SORT)

movimientos:''' * ''Segundo Ciclo i:'' :* 3 6 1 9 :* 3 1 6 9 * ''Tercer Ciclo i:'' :* 1 3 6 9 :* YA! En un principio,


pensaba no presentarles las variaciones que hay de este algoritmo, pero revisando en varios libros me percate de que
aunque es el mismo algoritmo y funciona de la misma forma (con sus ventajas y desventajas), Creo que es preferible
mostrarselos para evitar confusiones y enredos innecesarios. == Variantes == Buscando en varios libros e Internet
me he encontrado numerosas variantes, y por motivos pedaggicos voy a mostrarlos, ya que considero que aunque
sean el mismo algoritmo, su diferente implementacion puede llevar a confusiones. En lo personal considero que el
algoritmo planteado al principio es el que mejor se expresa (entiende), y es el que recomiendo implementar, pero hay
libros que lo implementan de otra forma, por lo que los que se guan con el, pueden tener confusiones a la hora de
apoyarse con este libro. ==== Variante: 1 Ciclo. ==== Aunque en realidad usa dos ciclos un while y un for, el while
hace la funcin de nuestro primer for. Sin embargo la dems implementaciones son tcnicamente las mismas, solo
que en vez de usar una variable ya preparada (j en nuestro caso), este evala con la variable i. <source lang=c>

Variante: Restar
Esta es una de las mas usuales que he visto en los libros, folletos y otros. Todo es igual, pero cambian las condiciones,
de esta forma se trabaja a la inversa de nuestro algoritmo.

31.2 Ejemplos
El algoritmo burbuja esta preparado para correr con todo tipo de datos NO atomicos, es por esto que no importa si
el arreglo es de tipo char, int, oat, etc. Funcionara con sus respectivas modicaciones.
El siguiente ejemplo ordenara una lista de tamao 6, que esta ordenada. Recuerden como buena practica:
Denir las variables en un solo punto del codigo.
Denir el tamao del array como constante.
Usar la indentacion correspondiente (Tabulaciones).
//Realizado por Master crack cocaino //UNAN-LEON Nicaragua. #include <stdio.h> #dene TAM 6 int main() { int
lista[TAM]={12,10,5,6,1,3}; //Declaracion e Inicializacion de un array int temp=0; //Variable temporal int i,j; //variables corredoras del ciclo printf(La lista DESORDENADA es: \n); for (i=0;i<TAM;i++) printf("%3d,lista[i]);
//impresion de la lista con espacio de 3 lineas (%3d) for (i=1;i<TAM;i++) { for (j=0;j<TAM-1;j++) { if (lista[j]
> lista[j+1]) //condicion { temp = lista[j]; //temp guarda momentaneamente el valor de lista[j] lista[j]=lista[j+1];
//Asigno al la posicion lista[j], lo que hay en lista[j+1] lista[j+1]=temp; //obtendra un nuevo valor por parte de temp.
} } } printf("\nLos valores ORDENADOS de lista son: \n); for(i=0;i<TAM;i++) printf("%3d,lista[i]); return 0; }
//Revisado por: Gustavo A. Chavarria. //UNAN-LEON Nicaragua

Captulo 32

Mtodo de Insercin
Este mtodo se podra decir que es algo superior al mtodo de la burbuja, ya que logra evaluar menos veces la
condicin. Sin embargo aun es un algoritmo muy pobre y que utiliza recursos tcnicamente igual que el algoritmo
burbuja.

32.1 Concepto
El algoritmo consiste en ordenar los dos primeros elementos de la matriz, luego se inserta el tercer elemento en la
posicin correcta con respecto a los dos primeros, a continuacin se inserta el cuarto elemento en la posicin correcta
con respecto a los tres primeros elementos ya ordenados y as sucesivamente hasta llegar al ultimo elemento de la
matriz.

32.2 Explicacin
32.3 Ejemplos
brbtgn

59

Captulo 33

Mtodo QuickSort
Es el ultimo mtodo del que hablaremos en este libro, es el algoritmo que mas calidad tiene, pudiendo llegar a lo
mismo que el mtodo burbuja e insercin, mas rpido y con uso de menos recursos.

33.1 Concepto
El ordenamiento rpido (quicksort en ingls) es un algoritmo basado en la tcnica de divide y vencers. Esta es
probablemente la tcnica ms rpida conocida. Fue desarrollada por Tony Hoare en 1960.
La idea del algoritmo es simple, se basa en la divisin en particiones de la lista a ordenar, por lo que se puede
considerar que aplica la tcnica divide y vencers. El mtodo es, posiblemente, el ms pequeo de cdigo, ms
rpido, ms elegante, ms interesante y eciente de los algoritmos de ordenacin conocidos.

33.2 Explicacin
El mtodo se basa en dividir los n elementos de la lista a ordenar en dos partes o particiones separadas por un elemento:
una particin izquierda, un elemento central denominado pivote o elemento de particin, y una particin derecha. La
particin o divisin se hace de tal forma que todos los elementos de la primera sublista (particin izquierda) son
menores que todos los elementos de la segunda sublista (particin derecha). Las dos sublistas se ordenan entonces
independientemente. Para dividir la lista en particiones (sublistas) se elige uno de los elementos de la lista y se utiliza
como pivote o elemento de particin. Si se elige una lista cualquiera con los elementos en orden aleatorio, se puede
seleccionar cualquier elemento de la lista como pivote, por ejemplo, el primer elemento de la lista. Si la lista tiene
algn orden parcial conocido, se puede tomar otra decisin para el pivote. Idealmente, el pivote se debe elegir de
modo que se divida la lista exactamente por la mitad, de acuerdo al tamao relativo de las claves.
Una vez que el pivote ha sido elegido, se utiliza para ordenar el resto de la lista en dos sublistas: una tiene todas las
claves menores que el pivote y la otra, todos los elementos (claves) mayores que o iguales que el pivote (o al revs).
Estas dos listas parciales se ordenan recursivamente utilizando el mismo algoritmo; es decir, se llama sucesivamente al
propio algoritmo quicksort. La lista nal ordenada se consigue concatenando la primera sublista, el pivote y la segunda
lista, en ese orden, en una nica lista. La primera etapa de quicksort es la divisin o particionado recursivo de la lista
hasta que todas las sublistas constan de slo un elemento. El algoritmo es ste: Recorres la lista simultneamente
con i y j: por la izquierda con i (desde el primer elemento), y por la derecha con j (desde el ltimo elemento).
Cuando lista[i] sea mayor que el elemento de divisin y lista[j] sea menor los intercambias. Repites esto hasta
que se crucen los ndices. El punto en que se cruzan los ndices es la posicin adecuada para colocar el elemento
de divisin, porque sabemos que a un lado los elementos son todos menores y al otro son todos mayores (o habran
sido intercambiados). Al nalizar este procedimiento el elemento de divisin queda en una posicin en que todos los
elementos a su izquierda son menores que l, y los que estn a su derecha son mayores.

33.3 Ejemplos

60

Captulo 34

Programacin en C/El proceso de


compilacin
Normalmente, a la hora de programar no creamos un nico archivo C .c, sino varios de ellos conteniendo diferentes funciones del programa. Esto nos proporciona varias ventajas: una mejor organizacin del cdigo, una mejor
modularidad y, sobre todo, ms facilidad (y velocidad) a la hora de compilar.
El proceso de compilacin que hemos tratado hasta ahora se divide en realidad en dos etapas, que el compilador nos
esconde en una: compilacin propiamente dicha y enlazado. En la primera etapa, la de compilacin, nuestro cdigo
en C se transforma en cdigo objeto, es decir, cdigo mquina (instrucciones que el ordenador puede ejecutar) en
cheros .o, mientras que en la segunda etapa (enlazado) estos cheros objeto son unidos entre s para formar el
chero ejecutable (normalmente sin extensin en el mundo Unix, o con extensin .com o .exe en el mundo MSDOS/Windows).
De esta manera, si no hemos modicado el chero .c que se compila a un determinado chero .o podemos ahorrarnos
esa parte de la compilacin cuando hagamos un cambio en otra parte del programa.
Adems tenemos los archivos de cabecera .h, que utilizamos para denir parmetros que se utilizan en el cdigo
(caso del fuente1.h del ejemplo) o para denir todas las funciones que vamos a utilizar en todos los archivos .c (caso
del cabeceras.h), ya que si recordamos, las funciones (como todo en C) deben denirse antes de usarse, y es posible
que estn en otro chero, lo cual nunca es considerado por el compilador como antes. Hay que tener en cuenta que
el chero en el que se encuentra la funcin main() llama, necesariamente, a todos los dems cheros .c, directa o
indirectamente, ya que de lo contrario lo que tendramos en esos cheros sera perfectamente intil.
Si tpicamente compilaramos el ejemplo con
$ gcc -o programa fuente1.c fuente2.c fuente3.c
para realizar esta compilacin por pasos, stos seran:
$ gcc -c fuente1.c #crea fuente1.o $ gcc -c fuente2.c #crea fuente2.o $ gcc -c fuente3.c #crea fuente3.o $ gcc -o
programa fuente1.o fuente2.o fuente3.o #crea programa
ya que la opcin -c del compilador lo que le dice es que detenga el proceso antes de enlazar, creando los cheros .o
necesarios.

61

Captulo 35

Los pasos del proceso


Lo primero que le ocurre a un chero .c de cdigo C es el preprocesado. En este paso se sustituyen todas las macros y
se eliminan los comentarios. El resultado, si lo vemos independientemente, es un chero de cdigo C preprocesado,
o .i.
El segundo paso es la compilacin propiamente dicha, en el que el cdigo C preprocesado se convierte en cdigo
ensamblador, que si lo vemos independientemente es un chero .s.
El tercer paso es el ensamblado del cdigo ensamblador, lo que lo convierte en cdigo mquina. Un chero de cdigo
mquina tambin es llamado chero objeto, y su extensin tpica es .o.
Dado que el camino anterior normalmente convierte un chero en un chero, se suele hacer en un slo paso, de cdigo
C a chero objeto.
El ltimo paso es el enlazado de los cheros objeto en el ejecutable nal.

35.1 Ejemplos
Tomemos el cdigo C ms simple posible:
int main(void) /*Ejemplo*/ { return(0); }
Al preprocesarlo tendremos:
# 1 prueba.c # 1 "<built-in>" # 1 "<command line>" # 1 prueba.c int main(void) { return(0); }
Vemos que el comentario ha desaparecido. En su lugar aparecen comentarios especcos del preprocesador. Al compilarlo tenemos:
.le prueba.c .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl
$16, %esp movl $0, %eax addl $15, %eax addl $15, %eax shrl $4, %eax sall $4, %eax subl %eax, %esp movl
$0, %eax leave ret .size main, .-main .ident GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)" .section
.note.GNU-stack,"",@progbits
Un precioso cdigo ensamblador que enseguida convertimos en un ilegible cdigo mquina:

ELF4( U)GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3) .symtab.strtab.shstrtab.text.data.bss.comme


stack4#!XX,X95E@ #prueba.cmain
Ese cdigo mquina es el chero .o que normalmente obtenemos directamente del cdigo C. Finalmente, ese cdigo
objeto es enlazado con las libreras necesarias para formar un ejecutable:

uUttPTRhhQVhhUSQ[t X[U=t+n_used__libc_start_mainGLIBC_2.0$
hU)UWVS [ )Eu [^_]&1G;}r [^_]'UWVS
[ )EHt41G9}uD [^_]USRtCuX[]USP[VX[$
Hp \ Y o$ooGCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU)
4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC:
(GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)",\ $$q!y_IO_stdin_used../sysdeps/i386/elf/start.S/ space/debian/glibc/build-area/glibc-2.3.5/ build62

35.1. EJEMPLOS

63

tree/glibc-2.3.5/csuGNU AS 2.16.1XF}xgMint\}nOV|/space/debian/glibc/build-area/glibc-2.3.5/ build-tree/i386libc/csu/crti.S/space/debian/glibc/build-area/glibc-2.3.5/build-tree/glibc-2.3.5/ csuGNU AS 2.16.1f(/space/debian/glibc/buildarea/glibc-2.3.5/ build-tree/i386-libc/csu/crtn.S/space/debian/glibc/build-area/ glibc-2.3.5/build-tree/glibc-2.3.5/csuGNU


AS 2.16.1% $ > $ > 4: ; I? T/ ../sysdeps/i386/elfstart.S01:"VWYX init.c^ /space/debian/glibc/build-area/glibc2.3.5/build-tree/i386-libc/csucrti.S3,W\#,: ,Wdd,,W^ /space/debian/glibc/build-area/glibc-2.3.5/build-tree/i386libc/csucrtn.Sq /space/debian/glibc /build-area/glibc-2.3.5/build-tree/glibc-2.3.5/csuinit.cshort intlong long intunsigned charlong long unsigned intshort unsigned int_IO_stdin_usedGNU C 4.0.2 (Debian 4.0.2-2) .symtab.strtab.shstrtab.interp.note.AB
tag.hash.dynsym.dynstr.gnu.version. gnu.version_r.rel.dyn.rel.plt.init.text.ni.rodata.eh_ frame.ctors.dtors.jcr.dynamic.got.got.plt.data.b
debug_aranges.debug_pubnames.debug_ info.debug_abbrev.debug_line.debug_str#(( 1HH(7 ppP?YGo To$$
c lD LL u\\ptt0{ 7p%} v 0: '|`!5 (Hp$L \ t -: f@
r` #*7X G\ M Tc dph# call_gmon_start__CTOR_LIST____DTOR_LIS
completed.4463p.4462__do_global_dtors_auxframe_dummy__CTOR_ END____DTOR_END____FRAME_END____JCR_END__
ni_array_end__ni_array_start__init_array_ end_GLOBAL_OFFSET_TABLE___init_array_start_fp_hw__dso_handle__libc_csu_
__libc_start_main@@GLIBC_2.0data_start_ni_edata_end_IO_stdin_used__data_start_Jv_RegisterClasses__gmon_start__

Captulo 36

Programacin en C/Los errores


Esta seccin no est lista si tienes algn conocimiento sobre este tema por favor, regstrate y contribuye con nuestro
libro.
Esta seccin est aqui para los sermones y para dar un par de ideas de como resolver los errores.
Qu tipo de errores se podran presentar ?
error de sintaxis: cuando el programador escribe algo mal. Usualmente el compilador no va a poder hacer su
trabajo por lo que nos va a advertir que hay algo equivocado.
error en runtime: cuando esta todo bien escrito pero el programa se comporta de forma incorrecta (viola los
espacios de memoria, trata de abrir un archivo que no existe, hace divisiones entre 0, ...). Normalmente es el
sistema operativo que nos advierte de estos errores.
error de logica: cuando el programa a pesar de estar bien escrito y comportarse bien no cumple con el objetivo
para el que fue hecho. (deba hacer divisiones y esta haciendo multiplicaciones). Ni el compilador, Ni el sistema
operativo nos van a advertir de estos errores, nos vamos a dar cuenta del error por resultados equivocados.
... etc
Cmo se resuelve cada tipo ?
Cuales son las practicas que se deben seguir para evitar los errores (tambien podria ser por tipo)?

36.1 Chequeos del C


Cual es la dinamica de c con los errores (por ejemplo me falta un ";" y el compilador muestra 7 errores diferentes)?
Que errores puede ver el compilador de C y cuales no?
El compilador de C, solamente puede ver los errores sintcticos del propio lenguaje y aquellos en los que se referencia
mal una posicion de memoria, no se inicializan variables, etc. Los errores que tenemos que tener cuidado de no cometer
los programadores son aquellos en los que aunque el compilador no de errores, hayamos creado un programa que en
tiempo de ejecucion s tenga errores o que simplemente no haga lo que queramos que hiciera.

64

Captulo 37

Programacin en C/Herramientas externas


tiles
37.1 Sistemas de construccin de cdigo

65

Captulo 38

make
make es un programa para la construccin de cdigo. Su funcin principal es evitarnos recompilar archivos innecesariamente. Se basa en reglas de dependencia, que especican qu cheros dependen de cuales, y rdenes de
construccin.
Para ver un ejemplo sencillo. En la seccin anterior vimos un programa que dependa de tres cheros objeto, los cuales
dependan a su vez de cheros fuente y cheros de cabecera. As, slo ser necesario enlazar de nuevo el programa
cuando cambie alguno de los cheros objeto. Esto lo podemos expresar, con dependencias, de esta manera:
programa: fuente1.o fuente2.o fuente3.o
Igualmente, los cheros objeto dependen de los cheros fuente y los cheros de cabecera de una manera que podemos
expresar como:
fuente1.o: fuente1.c fuente1.h cabecera.h
fuente2.o: fuente2.c cabecera.h
fuente3.o: fuente3.c cabecera.h
De esta manera, si hacemos un cambio en fuente2.c, la regla para fuente2.o lo detectar y recompilar fuente2.o, y
a continuacin la regla para programa detectar el cambio en fuente2.o y reenlazar programa.
Para ello, make necesita saber tambin cmo reconstruir sus objetivos. Estas rdenes (que, ojo, van siempre precedidas
de un tabulador, no de espacios) son las mismas que usaramos a mano y que vimos en el captulo anterior:
programa: fuente1.o fuente2.o fuente3.o
gcc -o programa fuente1.o fuente2.o fuente3.o
fuente1.o: fuente1.c fuente1.h cabecera.h
gcc -c -o fuente1.o fuente1.c
fuente2.o: fuente2.c cabecera.h
gcc -c -o fuente2.o fuente2.c
fuente3.o: fuente3.c cabecera.h
gcc -c -o fuente3.o fuente3.c
Todas estas rdenes van en chero, el chero de make, cuyo nombre suele ser makele. Utilizando la versin de
GNU de make es ms normal llamarlo Makele, ya que aunque el programa buscar primero si existe el nombre con
minscula, el capitalizado aparecer ms arriba al listar un directorio.
Una vez escrito el Makele, para construir nuestro programa basta con ejecutar make:
$ make gcc -c -o fuente1.o fuente1.c gcc -c -o fuente2.o fuente2.c gcc -c -o fuente3.o fuente3.c gcc -o programa
fuente1.o fuente2.o fuente3.o
Esto se debe a que, si no especicamos un objetivo para make, el programa tomar como objetivo por defecto el de
la primera regla que hayamos escrito en el Makele.
Si tenindolo todo ya compilado ejecutamos make de nuevo veremos:
$ make make: `programa' est actualizado.
66

38.1. SISTEMAS DE CONTROL DE VERSIONES

67

Y si ahora hacemos un cambio en fuente2.c, al ejecutar make una vez ms tendremos:


$ make gcc -c -o fuente2.o fuente2.c gcc -o programa fuente1.o fuente2.o fuente3.o
De esta manera, el programa nos ahorra recompilar o reenlazar los cheros cuyas dependencias no hayan cambiado.
Para ello, make se ja en la fecha y hora de la ltima actualizacin de cada chero. Si sta es posterior a la de todas sus
dependencias, make no reconstruir el objetivo. Si, en cambio, alguna de las dependencias de un chero es posterior
a l, el programa ejecutar las instrucciones que le hemos dado para reconstruir el objetivo.
Y como en todo lo que hemos visto hasta ahora, los comentarios son muy importantes. En un Makele (o makele)
stos se hacen con el carcter #:
######
# Makele
#
# (c) Envite, 2004
# para el wikilibro Programacin en C (fundamentos)"
# bajo licencia FDL
#####
#Regla para construir el programa
programa: fuente1.o fuente2.o fuente3.o
gcc -o programa fuente1.o fuente2.o fuente3.o
#Reglas para construir los cheros objeto
fuente1.o: fuente1.c fuente1.h cabecera.h
gcc -c -o fuente1.o fuente1.c
fuente2.o: fuente2.c cabecera.h
gcc -c -o fuente2.o fuente2.c
fuente3.o: fuente3.c cabecera.h
gcc -c -o fuente3.o fuente3.c

38.1 Sistemas de control de versiones


CVS
Subversion

Captulo 39

Programacin en C/Ejemplos
39.1 El "Hola Mundo"
#include <stdio.h> int main (int argc,char **argv) { printf(Hola mundo\n); return 0; }
Nota: este programa est tomado del Dominio Pblico
$ ./holamundo Hola Mundo $

39.2 El Hola Mundo comentado


[Contexto]
/* Inclusin de archivos */ #include <stdio.h> /* Funcin principal */ int main (int argc,char **argv) { /* Impresin
por pantalla y salida del programa*/ printf(Hola mundo\n); return 0; }
$ ./holamundoc Hola Mundo $

39.3 El Hola Mundo estructurado en funciones


[Contexto]
/*
holamundo.c
(c) Envite, 2004
para el wikilibro Programacin en C (fundamentos)"
bajo licencia FDL, adaptado del Dominio Pblico
*/
#include <stdio.h> /*Necesario para la funcin printf()*/
void holamundo(void) /*Funcin donde se ejecuta la lgica del programa*/
{
printf(Hola Mundo\n); /*imprime la cadena*/
return; /*sale de la funcin*/
}
int main(void) /*Funcin principal del programa*/
{
holamundo(); /*llamada a la funcin que lleva el peso*/
return(0); /*sale del programa: correcto*/
}
$ ./holamundof Hola mundo $
68

39.4. EJEMPLO DE CLCULO CON ENTEROS

69

39.4 Ejemplo de clculo con enteros


[Contexto]
/*
ejemplo.c
(c) Envite, 2004
para el wikilibro Programacin en C (fundamentos)"
bajo licencia FDL
*/
#include <stdio.h> /*Necesario para la funcin printf()*/
int main(void) /*Funcin principal del programa*/
{
char resultado; /*Variable de tipo carcter donde se almacenar el resultado de
las operaciones.*/
resultado=5+2; /*Realizamos una suma.*/
printf(Resultado de la suma: %i\n,resultado);
resultado=5-2; /*Realizamos una resta.*/
printf(Resultado de la resta:%i\char"005C\relax{}n,resultado);
resultado=5*2; /*Realizamos una multiplicacin.*/
printf(Resultado de la multiplicacin: %i\n,resultado);
resultado=5/2; /*Realizamos una divisin entera.*/
printf(Resultado de la divisin:%i\n,resultado);
return(0); /*Salimos del programa con el cdigo 0 porque no ha habido errores.*/
}
$ ./ejemploc Resultado de la suma: 7 Resultado de la resta: 3 Resultado de la multiplicacin: 10 Resultado de la
divisin: 2 $

39.5 Control de acceso


[Volver al ndice general] [Arriba]
[Contexto]
#include <stdio.h>
void acceso(unsigned char edad)
{
if (edad < 18)
printf(No puedes acceder.\n);
else
printf(Bienvenido.\n);
return;
}
int main(void)
{
acceso(18);
acceso(17);
return(0);
}
$ ./acceso Bienvenido. No puedes acceder. $
[Volver al ndice general] [Anterior: Salida por pantalla: printf()] [Arriba]
--Envite 01:31 11 dic, 2004 (UTC)

Captulo 40

Programacin en C/Glib
40.1 Introduccin
El lenguaje C fue creado como un lenguaje multiplataforma capaz de sustituir al ensamblador y generar cdigo
portable. Sin embargo a lo largo de los aos se empiezan a hacer visibles algunas lagunas del diseo original. Algunos
ejemplos que pueden citarse:
No existe estandarizacin en torno a la longitud real (en bits) de un entero. Un tipo int puede variar entre 16,
32 y 64 bits de longitud en funcin del sistema operativo y compilador utilizado.
No existe un soporte estndar en el lenguaje para programacin orientada a objetos.
No existe un soporte estndar para tipos de estructuras de datos frecuentemente utilizadas como listas enlazadas, tablas hash (tambin llamadas diccionarios).
No existe un soporte estndar para textos Unicode (el estndar Unicode es posterior a la creacin del C).
No existe un soporte estndar para patrones de diseo frecuentemente utilizados como el bucle principal de
eventos.
No existe un API comn para manejar aplicaciones multihilo como puede existir en Java y otros lenguajes/plataformas.
La librera glib se cre con el fn de solucionar estas y otras lagunas originales del lenguaje proporcionando una capa
de compatibilidad real multiplataforma en sistemas tipo UNIX (Linux, Solaris, AIX, BSD, ...), Windows, OS/2 y
BeOS.
La librera glib es gratuita y licenciada bajo LGPL (Lesser GPL), lo cual signica en la prctica que puede ser utilizada
tanto en programas de cdigo abierto como cerrado.
glib provee soporte para expresiones regulares tipo PERL, un conjunto de datos tipados ms seguros que el C estndar,
soporte multihilo multiplataforma, un sistema de bucle principal de eventos, colas asncronas, carga dinmica de
mdulos, soporte portable para uso de cheros, tuberas y sockets, programacin orientada a objetos, utilidades de
todo tipo para manipular textos, escaneado sintctico, Timers y multitud de estructuras de datos frecuentemente
utilizadas slices de memoria, listas doblemente enlazadas, colas, secuencias (listas escalables), tablas hash para
bsqueda mediante llaves (donde la llave puede ser un texto o un objeto complejo), arrays dinmicos, rboles binarios
balanceados, rboles N-rios (rboles con N ramas por nodo), quarks, listas de datos indexadas (accesibles mediante
un identicador GQuark, relaciones y tuplas que pueden ser indexadas mediante un nmero arbitrario de campos,
caches para comparticin de estructuras complejas de datos.
glib provee adems un marco base de utilidades para realizar tests de funcionamiento en tiempo de desarrollo.
glib no provee soporte grco. Frecuentemente suele emparejarse con la librera grca GTK+ ya que la misma utiliza
glib internamente como soporte base. Sin embargo glib puede ser utilizado para aplicaciones embebidas o servidor
sin dependencia alguna del sistema grco.
70

40.2. EJEMPLOS DE USO

71

40.2 Ejemplos de uso


glib se encuentra ampliamente documentado por los propios desarrolladores de la librera y existen tutoriales disponibles en Internet. Aqu se expondr un ejemplo de cdigo real dnde se utiliza glib para facilitar el desarrollo en
C:
Este cdigo se utiliz para monitorizar un sistema Linux donde el disco duro haba sido sustituido por un sistema de
cheros en red. El cdigo se encarga de crear un perro vigia encargado de resetear el sistema en caso de detectar un
fallo en el sistema de cheros NFS (Net File System) del cual depende la mquina para su correcto funcionamiento. El
mismo sirve para comprobar la facilidad con que glib permite crear timers, callbacks invocadas por el bucle principal
de eventos o gestionar acceso de entrada/salida de forma ms ordenada que las libreras estndar de C:
#include <glib.h> /* glib */ GError* err = NULL; gboolean isNFSUpAndRunning() { /* Salvo que se indique
lo contrario devuelve KO"(0) * Superados todos los test devolver'a OK (1) */ gboolean result = 0; GIOChannel *gioChan = g_io_channel_new_le("/NFSFlag, r, &err); /*|*/ /*|*/startBlock1: { /*|*/ if(NULL != err &&
g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { goto endBlock1; } g_io_channel_set_encoding(gioChan,
NULL, NULL); gsize *len = 0; char* ret; if(G_IO_STATUS_ERROR == g_io_channel_read_to_end(gioChan,
&ret, len, &err)) { goto endBlock1; } printf("%s, ret); result = 1; // Todo es correcto. Devolvemos OK /*|*/ /*|*/}
endBlock1: { /*|*/ if(NULL != err) { g_error_free(err); err = NULL; } if(NULL != gioChan) g_io_channel_unref(gioChan);
/*|*/ /*|*/} /*|*/ return result; } void hardReset() { // http://en.wikipedia.org/wiki/Magic_SysRq_key GIOChannel
*gioChan = g_io_channel_new_le("/proc/sysrq-trigger, w, &err); gsize bytes_written; g_io_channel_write_chars(gioChan,
b, 1, &bytes_written, &err); if (NULL != err) { printf("debug:hardReset Se detect'o un error\n); ush(stdout); }
g_io_channel_shutdown (gioChan, /*bFlush*/ TRUE, &err); } static gboolean callBackCheckNFS(gpointer user_data){
if (! isNFSUpAndRunning() ){ hardReset(); } return TRUE; } static gboolean setTimeOut(GMainContext* ctx) {
g_return_if_fail (ctx != NULL); // Creamos un objeto timer. source indica que es la fuente de eventos GSource*
source = g_timeout_source_new (60*1000 /*milisecs*/); // Asociamos un callBack a la fuente de eventos (timer).
g_source_set_callback (source, callBackCheckNFS, (gpointer) ctx, NULL); // Finalmente asociamos la fuente al
contexto (bucle principal de eventos) g_source_attach (source, ctx /*ctx->g_main_ctx*/); g_source_unref (source);
return TRUE; } int main(int argc, char *argv[]) { // Creamos el objeto bucle principal de eventos. GMainLoop *loop
= g_main_loop_new (NULL, FALSE); /* * Asociamos un Timer al bucle principal. Si falla algo abortamos el programa * pues no tiene sentido continuar en tal caso. */ if (! setTimeOut(g_main_loop_get_context(loop)) ) { return
1; } // Ejecutamos el bucle principal de eventos. g_main_run (loop); return 0; }

40.3 Referencias
El Interfaz de Programacin de Aplicaciones (API) puede consultarse (en ingls) en la siguiente URL:
http://library.gnome.org/devel/glib/stable/
El tutorial de la librera grca GTK+ (http://library.gnome.org/devel/gtk-tutorial/stable/) incluye tambin
ejemplos de glib y herramientas para compilar y linkar contra estas dos libreras.
El exitoso escritorio para UNIX de cdigo abierto GNOME (GNU Network Model Environment) as como la
plataforma Moblin de Intel, Nokia Maemo, Google Android y Google Chrome, la versin UNIX de Firefox y
otros programas de cdigo abierto como The Gimp, Inkscape o DIA, etc ... hacen un uso extenso de glib. El
cdigo est disponible gratuitamente en la web y puede servir como una gua y referencia para utilizar glib en
proyectos de software complejos con millones de lneas de cdigo y que son utilizados diariamente por millones
de personas.

Captulo 41

Programacin en C/Referencia
Esta parte pretende ser una referencia completa de los elementos del lenguaje C. Se pretende que aqu se presenten
todos los datos disponibles de los tipos de datos del lenguaje C, toda la informacin relevante de los operadores del
lenguaje C, las formas en la que se deben usar la principales estructuras e informacin acerca de algunas bibliotecas
y funciones que son estndares. Todo esto con la mayor calidad posible. O sea que en un momento aqu va a existir
algo as como una super sntesis del manual de programacin en C con los datos que son importantes pero que son
fciles de olvidar.

41.1 Tipos de datos


char
bytes = 1
Mximo = 127 (255 cuando es unsigned)
Mnimo = 128 (0 cuando es unsigned)
int
bytes = 4
Mximo = 2,147,483,647 (4294967295 cuando es unsigned)
Mnimo = 2,147,483,648 (0 cuando es unsigned)
oat
bytes = 4
Mximo Exponente = 1037
Mnimo Exponente = 1037
long
bytes = 4
Mximo = 2,147,483,647 (4294967295 cuando es unsigned)
Mnimo = 2,147,483,648 (0 cuando es unsigned)
long long
72

41.2. OPERADORES

73

bytes = 8
Mximo = 9,223,372,036,854,775,807 (18,446,744,073,709,551,616 cuando es unsigned)
Mnimo = 9,223,372,036,854,775,808 (0 cuando es unsigned)
short
bytes = 2
Mximo = 32767 (65,535 cuando es unsigned)
Mnimo = 32768 (0 cuando es unsigned)
double
bytes = 8
Mximo Exponente = 10308
Mnimo Exponente = 10308
void -sin tipo-

41.2 Operadores
Todos los operadores estn puestos en el orden en que estn en la jerarqua del lenguaje C. En caso de que estn
en una expresin todos los operadores juntos y de que no se use el operador (), que rompe la precedencia, entonces
van a ser evaluados desde el primero de la tabla hasta el ltimo en ese orden. En caso de que se encuentren varios
operadores iguales o de la misma precedencia en una expresin sin parntesis se van a resolver en el orden en que
diga la columna de Orden de agrupacin. La precedencia es una cosa importante porque nos dice en que orden va
a ser resuelta una expresin y el orden en que se resuelve una expresin inuye en su resultado.
un ejemplito: 1 + 2 / 3 + 4 no es igual a (1 + 2) / (3 + 4) eso porque segn la precedencia: 1 + 2 / 3 + 4 es igual a 1 +
(2 / 3) + 4) (2/3 = 0 por ser una operacin entre enteros, 1 + 4 = 5) (en el otro caso 1 + 2 = 3 y 3 + 4 = 7 y 3 / 7 = 0
por ser una operacin entera)
Como la divisin tiene mayor precedencia que la suma se resuelve primero la divisin.
otro ejemplito: 72 / 3 / 2 / 6 no es igual a (72 / 3) / (2 / 6) eso porque segn la precedencia: 72 / 3 / 2 / 6 es igual
a ((72 / 3) / 2) / 6 (La divisin no es una operacin asociativa y por eso cambia el resultado si resolvemos en orden
diferente, no es lo mismo cuando usamos la precedencia del lenguaje C como en la tabla de precedencias, que cuando
obligamos a que se resuelva la operacin en otro orden usando parntesis).
Para los operadores de Incremento (++) y decremento (--) hay que tener en cuenta el sitio en el que se encuentran con
respecto a la variable que estn inuenciando. Con esto me reero a que siendo a una variable ++a no es igual que
a++, aunque en algunas ocasiones parezca lo contrario. Para el caso de ++a el incremento ser realizado antes
de evaluar la expresin. Y para a++ se evaluara primero la expresin completa y luego se realizara el incremento.
En la tabla hay tres operadores que estn identicados con un calicativo para bits. Con esto me reero a que
inuenciaran los bits de una variable. Si tengo dos valores a y b el resultado de un a && b ser el valor del AND
lgico entre los valores respectivos pero el a & b tendr como resultado varios ANDs lgicos individuales entre cada
uno de los bits de las dos variables.

41.3 Estructuras
Estructura general de un programa de lenguaje C:
inclusin de bibliotecas ... denicin de macros y constantes ... deniciones de funciones o/y prototipos ... deniciones
de variables globales ... int main(argumentos){ sentencias ... return 0; } deniciones de cuerpos de funciones (cuyos
prototipos ya fueron declarados) ...

74

CAPTULO 41. PROGRAMACIN EN C/REFERENCIA

Estructura general de un header le de lenguaje C:


inclusin de bibliotecas ... denicin de macros y constantes ... deniciones de funciones o/y prototipos ... deniciones
de variables globales ... deniciones de cuerpos de funciones (cuyos prototipos ya fueron declarados) ...
Estructura de un bloque/sentencia:
{ sentencia/s }
En cualquier momento un bloque de sentencias puede sustituir a una sentencia.
Estructura general de una funcin de lenguaje C:
tipo_de_retorno nombre_de_la_funcion (tipo argumento1, tipo argumento2) { variables ... sentencia/s ... return valor_de_retorno; }
Estructura del if:
if (condicion) sentencia/s else if (condicion) sentencia/s else sentencia/s;
Estructura del switch:
switch (valor) { case valor: sentencias ... break; case valor: sentencias ... break; ... default: sentencias ... }
Estructura del while:
while(condicion) { sentencias ... }
Estructura del do - while:
do { sentencias ... }while(condicion);
Estructura del for:
for (iniciacion; condicion; incremento) { sentencias ... }
Estructura de un struct:
struct { tipo variable; tipo variable; ... }
Estructura de un unin:
union { tipo variable; tipo variable; ... }

41.4 Bibliotecas y funciones


Cuando no se especica un tipo de dato es porque la funcin permite mas de uno ya sea a travs de casting o de otra
maa. Esta parte como ya se explico antes es solo para mostrar el formato de las funciones.
Funciones de <stdio.h>:
int printf(cadena de formato, variable1, variable2); //Muestra por pantalla int scanf(cadena de formato, variable1, variable2); //Lee de pantalla
Funciones de <stdlib.h>:
int system(llamada);
Funciones de <math.h>:
double sin(valor); //Calcula el sinus double cos(valor); //Calcula el cosinus double tan(valor); //Calcula la tangente double asin(valor); double atan(valor); double acos(valor); double sinh(valor); double cosh(valor); double tanh(valor); double log10(valor); double log(valor); double ldexp(valor1,valor2); double pow(valor); double
sqrt(valor); //Calcula races cuadradas
Funciones de <time.h>:
struct tm time();
Funciones de <string.h>:
int strcmp(cadena1, cadena2); //Compara dos cadenas de carcteres int strcat(cadena1, cadena2); int strcpy(cadena1,
cadena2); //Copia la primera cadena en la segunda int strlen(cadena); //Da la longitud de una cadena
Funciones de <ctype.h>:

41.5. PREPROCESADOR DE C

75

Funcin toupper ANSI C Convierte un carcter, en un parmetro entero ch, a mayscula.


Valor de retorno: ch debe estar en el rango 0 a 255, y si est entre a y z lo convierte a su equivalente en el rango A
a Z, el resto de los valores no son modicados. El valor de retorno es el valor convertido si ch era una minscula, o
el valor original en caso contrario. Nota: los caracteres en acentuados, o con diresis, en minscula y la no sufren
modicaciones.
Ejemplo:
#include <stdio.h> #include <ctype.h> int main() { char cadena[] = esto es una cadena de prueba"; int i; for(i = 0;
cadena[i]; i++) cadena[i] = toupper(cadena[i]); printf("%s\n, cadena); return 0; }

41.5 Preprocesador de C

Captulo 42

Programacin en C/Cmo compilar un


programa
La palabra compilar signica traducir un cdigo de programacin a codigo ejecutable por la mquina. Para compilar
un codigo fuente y convertirlo en ejecutable hay distintas formas dependiendo del compilador que se utilice. Entre
estos compiladores, uno de los ms famosos es GCC (GNU Compiler Collection) el cual trae soporte para diversos
lenguajes (C, C++, Ada, Java, etc). Su fama y buen desempeo van ligados con la gratuidad con la cual se distribuye.
Un ejemplo simple para compilar un programa con gcc:
gcc -o ouput input.c
Lo cual dara como resultado un binario con el nombre ouput. Gcc adems trae varias ags de optimizacin las
cuales permiten sacarle mayor desempeo a los binarios en un tipo de mquina predeterminada.

42.1 Un poco ms sobre compilacin


[Volver al ndice general]
Hasta ahora hemos compilado los programas mediante la orden:
$ gcc [nombre].c $
y los ejecutbamos mediante la orden
$ ./a.out <resultado> $
Ello se debe a que los compiladores de C, en general, y gcc, en particular, crean un ejecutable de nombre a.out en
el directorio actual si no se les especica otra cosa. ./a.out es la manera de ejecutar ese archivo. Pero es en general
ms cmodo nombrar los archivos ejecutables a partir de los archivos fuente de los que vienen. Para ello con gcc
utilizamos el modicador -o [nombre], con lo que las dos compilaciones que hemos visto hasta ahora podran quedar
as:
$ gcc -o holamundo holamundo.c $ gcc -o ejemplo ejemplo.c $
y los ejecutaramos correspondientemente:
$ ./holamundo Hola Mundo $ ./ejemplo Resultado de la suma: 7 Resultado de la resta: 3 Resultado de la multiplicacin:
10 Resultado de la divisin: 2 $
[Volver al ndice general] [Anterior: El Hola Mundo"] [Siguiente: Instrucciones de control] [Arriba]
--Envite 01:54 10 dic, 2004 (UTC)

76

Captulo 43

Programacin en C/Cdigo embebido


Esta seccin no est lista si tienes algn conocimiento sobre este tema por favor, regstrate y contribuye con nuestro
libro.
Que es cdigo embebido ?
El cdigo embebido es parte de cdigo fuente de otro lenguaje de programacin diferente al lenguaje C, pero que
se incluye en nuestros programas. Por ejemplo: se permite la inclusin de cdigo en lenguaje ensamblador en un
programa escrito en lenguaje C.
Ventajas de utilizar cdigo embebido assembler o python u otros?
Algunas de las ventajas que ofrece es mayor control de rutinas e instrucciones de bajo nivel -en el caso del lenguaje
ensamblador- que de otra manera seria muy difcil de lograr.
Cdigo embebido de assembler en Gcc, otros? Cmo usar ?
Este es un ejemplo de cdigo embedido del lenguaje ensamblador dentro de un programa escrito en lenguaje C:
#include<stdio.h> int main ( int argc, char **argv ) { int modo = 1 ; asm { mov cx, contador mov al, 10 int 10h }
printf ( Este texto se presenta en modo de video VGA ) ; return 0 ; }
El ejemplo anterior permite cambiar el modo de video de la pantalla a un modo VGA estandar con codigo ensamblador, es mucho mas sencillo

77

Captulo 44

Programacin en C/Recursos en la red


44.1 Manuales, tutoriales y otros documentos
44.1.1

Espaol

Aprenda lenguaje ANSI C como si estuviera en Primero


Apuntes de la Escuela Superior de Ingenieros Industriales de la Universidad de Navarra. Muy buenos
para aprender a programar.
Apuntes de Fundamentos de programacin en C
Apuntes y ejercicios resueltos de Fundamentos de programacin en C para Formacin Profesional.
El Rincn del C
Curso de C, foros e informacin relacionada con el C/C++

44.1.2

Ingls

The C Book
Es la versin online del libro The C Book de Mike Banahan, Declan Brady y Mark Doran.

44.2 Compiladores e IDE


Code::Blocks: IDE libre para Windows y Linux que permite el uso de plugin para aadir funcionalidades.
Dev-c++: IDE para C y C++ libre para Windows que usa GCC de fcil manejo.
Eclipse CDT: Framework de Eclipse para programar en C/C++ con soporte para refactorizacin de cdigo.
Anjuta: Entorno avanzado de desarrollo para desarrollo de aplicaciones Glib/GTK en UNIX.
Qt Creator: Realmente se trata de un entorno centrado en la librera QT de C++. Actualmente este entorno es
desarrollado y soportado por Nokia y al igual que Eclipse CDT incluye funciones avanzadas como refactorizacin automtica de cdigo.
GCC: Compilador ms famoso de GNU/Linux, sumamente potente. Soporta cross-compiling.
NetBeans: IDE desarrollado en Java, entre sus plugins tiene el soporte para c/c++

78

Captulo 45

Programacin en C/Bibliografa
45.1 Bibliografa en espaol
45.2 Bibliografa en ingls
Kernighan, Brian W. y Ritchie, Dennis M.: The C Programming Language (2nd Edition), Prentice Hall,
1988.
Es conocido como Kernighan y Ritchie o como K&R. Este libro es la biblia del lenguaje C.

79

Captulo 46

Fundamentos de programacin

80

Captulo 47

Programacin en C/Fundamentos de
programacin
En este captulo veremos un resumido listado de conceptos bsicos, esta informacin puede encontrarse en forma
ms elaborada en el WikiLibro Fundamentos de programacin.

47.1 Deniciones
Se denomina algoritmo a una secuencia de instrucciones que permiten obtener un resultado en particular.
No necesariamente son programas de computadora, una receta de cocina, o las instrucciones para cambiar un
neumtico son ejemplos de algoritmos de la vida real.
Las computadoras, son maquinas sin inteligencia propia, cuya nica nalidad es interpretar el cdigo que se
les provee.
El lenguaje de mquina es el nico lenguaje que la computadora entiende y es capaz de ejecutar.
Los lenguajes de programacin son el medio de comunicacin entre el programador y una computadora. El
programador escribe en algn lenguaje de programacin y utiliza las herramientas provistas por ese lenguaje
para transformarlo en lenguaje de mquina.
Finalmente, denominamos programa a una secuencia de rdenes a ser ejecutadas por una computadora. Un
programa debe estar escrito en algn lenguaje de programacin, y puede incluir uno o ms algoritmos.

47.2 Tipos de lenguajes


Existe una gran cantidad de lenguajes de programacin, que estn pensados para distintas nalidades, siguen distintos
paradigmas, y de una u otra forma se diferencian de los dems.

47.2.1

Esquemas de programacin

El esquema de programacin llamado Programacin Imperativa, consiste en escribir una secuencia de instrucciones
una detrs de la otra, que se ejecutarn en orden. Algunas de esas instrucciones pueden hacer que la mquina pase a
una instruccin que no sea la siguiente, tal vez porque se cumpla una condicin que hayamos establecido.
En los ltimos aos ha tomado fuerza otro paradigma de computacin, llamado Programacin Orientada a Objetos
, en el cual se intentan modelar los sistemas creados como extensiones de la realidad mediante la denicin de objetos
que modelan entidades de la vida real y que interactan entre s mediante mensajes llamadas mtodos.
El lenguaje C es un lenguaje imperativo, no orientado a objetos.
81

82

47.2.2

CAPTULO 47. PROGRAMACIN EN C/FUNDAMENTOS DE PROGRAMACIN

Alto o bajo nivel

Por otro lado, los lenguajes de programacin se clasican en niveles. Un lenguaje es de ms bajo nivel cuanto ms
cercano est al cdigo de mquina, y un lenguaje que es de ms alto nivel cuanto ms lejano est de la mquina y
ms cercano al lenguaje humano.
C es un lenguaje de alto nivel aunque tiene muchas caractersticas de lenguaje de bajo nivel (como el uso que permite
hacer de la memoria). Estas caractersticas hacen que C sea un lenguaje muy potente, ya que permite optimizar al
mximo los recursos de la mquina. Por ende, esto tambin hace que la dicultad y que los errores que se puedan
cometer programando aumenten. As que a C se le considera de nivel medio.
Lenguajes de ms alto nivel que C son aquellos en los que el programador no necesita encargarse de manipular la
memoria, como Java, C#, Python, Ruby, entre otros.

47.2.3

Compilados o interpretados

Otra forma de clasicar a los lenguajes de programacin que es segn la forma en que se ejecutan sus rdenes. Existen
los lenguajes que son interpretados, cuyas rdenes pasan a travs de un intrprete que se encarga de ejecutarlas (a
partir del cdigo fuente) en el mismo momento en que estn siendo ledas. Algunos de los lenguajes interpretados
son Python, Perl o Tcl, entre muchos otros.
La contraparte de los lenguajes interpretados son los lenguajes compilados (como el mismo C) que se diferencian
en que las rdenes son transformadas a lenguaje de mquina que se almacena en un archivo ejecutable. Ese archivo
puede ejecutarse luego, sin recurrir al compilador.
Los lenguajes compilados tienen la ventaja de la velocidad y la eciencia, pero los interpretados tienen la ventaja de
que, generalmente, son muy portables y de ms alto nivel.

47.3 Estructura de la memoria


Parte de esta potencia de C viene de que permite acceder con mucha libertad a la memoria de la mquina. Para
entender un poco cmo es posible, debemos entender cmo se guardan los datos en la memoria.
Imaginemos que la memoria tiene un montn de casillas, una enorme la de casillas, cada una de las cuales contiene
un dgito binario (bit):
0101001010100001010101001010000100111010110010010101001011010110001101010110101010110111...
Es exactamente as, pero es ms cmodo recordar que esos bits se encuentran agrupados de ocho en ocho, formando
octetos (bytes):
Cada octeto puede contener 28 = 256 combinaciones distintas de ceros y unos, es decir, cualquier nmero entre 0 y
255:
Tambin podemos representar estos nmeros en base hexadecimal:
O considerarlos caracteres, mediante alguna codicacin:
Este es el tipo de dato ms elemental que nos podemos encontrar en C: el caracter. Un caracter ocupa exactamente un
byte (8 bits) de memoria, y puede contener un nmero entre 0 y 255, o entre 128 y 127, dependiendo si queremos
considerarlo como sin signo o con l.

Captulo 48

Programacin en C/Historia de C
El lenguaje de programacin C fue creado por Dennis Ritchie entre 1969 y 1973 cuando trabajaba en Bell Laboratories
de AT&T junto con Ken Thompson en el diseo del sistema operativo UNIX. C fue creado para poder escribir dicho
sistema operativo en un lenguaje de alto nivel, independiente del hardware donde se ejecutara.
Contar con un lenguaje de alto nivel permiti el avance de los sistemas operativos, ya que el mismo cdigo poda ser
utilizado en las distintas plataformas, propiciando la reutilizacin de cdigo y reduciendo los tiempos de desarrollo.
As es que los sistemas operativos basados en UNIX, el sistema BSD, el sistema GNU/Linux y muchos otros fueron
desarrollados en C.
Adems, con el paso del tiempo se han desarrollado cientos de bibliotecas que permiten a los programadores de
C utilizar el cdigo desarrollado por otros para la realizacin de tareas comunes. Esto, a su vez, ha propiciado el
desarrollo de aplicaciones en lenguaje C.
Actualmente es imposible contar la cantidad de aplicaciones y herramientas desarrolladas en C.

48.1 Evolucin
A mediados de los aos 60s, Martin Richards dise el lenguaje BCPL con la nalidad de usarlo para escribir software
de sistemas operativos y compiladores.
En 1969, Ken Thompson escribi el Lenguaje B, en Bell Laboratories, con el objetivo de recodicar UNIX (escrito
hasta ese momento en lenguaje ensamblador) usando un lenguaje de alto nivel ms portable y exible.
Durante los siguientes aos, Dennis Ritchie modic el lenguaje B, llegando a crear el lenguaje C y reescribiendo
el sistema UNIX en dicho lenguaje; aadi caractersticas nuevas, como son el diseo de tipos y las estructuras de
datos.
En 1978, Dennis Ritchie y Brian Kernighan publicaron la primera edicin del libro El lenguaje de programacin C.
Este libro fue durante aos la especicacin informal del lenguaje. El lenguaje descrito en la primera edicin de este
libro, fue conocido como el C de Kernighan y Ritchie o simplemente K&R C. En este libro se introdujeron nuevas
caractersticas al lenguaje: los tipo de datos struct, long int y unsigned int; los operadores =+ y =- fueron sustituidos
por += y -=.
A mediados de los aos 80, Bjarne Stroustrup (tambin de los laboratorios Bell), crea el lenguaje C++, un lenguaje
basado en C, con numerosas caractersticas adicionales, siendo la principal que est orientado a objetos. Si bien se
han creado muchos lenguajes basados en C, C++ es el que ha permanecido ms asociado a C.
En los aos siguientes a la publicacin del C de Kernighan y Ritchie, se aadieron al lenguaje muchas caractersticas
no ociales, que estaban presentes en algunos compiladores y no en otros. Fue por ello que en 1989 ANSI (American
National Standards Institute) public el primer estndar ocial de C, que es conocido como ANSI C.
En este estndar se tomaron muchas de las funcionalidades no ociales y se agregaron funcionalidades nuevas como los
prototipos de funcin, y un preprocesador mejorado. Tambin se cambi la sintaxis de la declaracin de parmetros
de funciones, para que incluyeran el tipo junto con el nombre.
Al ao siguiente, en 1990 se public la estandarizacin ISO del lenguaje. Este estndar es bsicamente el estndar
83

84

CAPTULO 48. PROGRAMACIN EN C/HISTORIA DE C

ANSI, con unas pocas modicaciones de formato. A este estndar se lo conoce, entonces, como C89, o C90, y se
trata del mismo lenguaje.
Basndose en el estndar ANSI que estaba en preparacin, en 1988 Kernighan y Ritchie publicaron la segunda edicin
de su libro, que es an hoy utilizada como una de las referencias principales del lenguaje.
Durante los siguientes aos, el lenguaje C permaneci sin demasiados cambios. Sin embargo, como haba sucedido
antes, los distintos compiladores fueron incorporando caractersticas adicionales, que otros compiladores no tenan,
siendo C++ la principal inuencia.
Fue por ello que a nales de los noventa se decidi revisar el estndar de C, lo que llev a la publicacin del estndar
C99. Este estndar incluye varias nuevas caractersticas como son: las funciones inline; la posibilidad de declarar
variables en cualquier parte del cdigo; los comentarios de una sola lnea utilizando //; los tipos de datos long long
int, bool y complex, entre otras.
An hoy el proceso de evolucin del lenguaje sigue avanzando, y desde 2007 se est trabajando en el armado de un
nuevo estndar.

48.2 Ms informacin
Lenguaje de programacin BCPL
Historia del lenguaje del programacin C
The Development of the C Language

Captulo 49

Programacin en C/Introduccin
49.1 Objetivos
El objetivo principal de este Wikilibro es que cualquier persona sin conocimientos previos de programacin pueda
ser capaz de programar en el lenguaje C.
Una vez logrado el dominio del lenguaje, es probable que los lectores se interesen por otros temas ms complejos que
superen a los temas bsicos. Tambin les ser ms o menos sencillo aprender cualquier otro lenguaje de programacin
estructurada.
Sin embargo, este no es un libro que apunte nicamente a programadores principiantes. Tambin puede resultar de
inters para quienes ya tengan experiencia en el rea de programacin. En esta introduccin hay dos secciones en
las que se explica para los dos grupos principales de lectores qu camino seguir para comenzar a programar en el
lenguaje C o bien perfeccionar conocimientos.
El lenguaje C es tan usado porque es un lenguaje de programacin que emplea pocas instrucciones en lenguaje
mquina para traducir elementos del cdigo. Esto reduce los tiempos de ejecucin de los programas.

49.2 Nota sobre la exactitud


Muchas de las cosas expresadas en este wikilibro, especialmente en los primeros captulos, no son completamente
exactas, aunque son buenas aproximaciones. Los detalles ms exactos irn apareciendo posteriormente, una vez que
los materiales anteriores hayan sido correctamente asimilados por el lector. En general, dadas dos deniciones o
datos contradictorios en este wikilibro, debe considerarse siempre como ms exacto al segundo, habiendo aparecido
el primero como una introduccin ms general al tema.

49.3 Estndar utilizado


El lenguaje C fue creado en los aos setenta, y a lo largo de su historia ha pasado por muchas modicaciones, tanto
con respecto a la sintaxis como con respecto al cdigo incluido dentro de la biblioteca estndar. Es por ello que se
fueron desarrollando estndares, para que todos sepan con qu versin del lenguaje se est trabajando.
Los distintos estndares del lenguaje C han sido: el C de Kernighan y Ritchie, un estndar no-ocial que surgi luego
de la publicacin de su libro en 1978; el C89 o C90, el primer estndar ocial, posterior a la publicacin de los
estndares ANSI en 1989 e ISO en 1990; y el C99, publicado en 1999.
En este libro se utilizar el estndar C99, si bien por cuestiones de estilo y compatibilidad muchas veces se utilizar
cdigo compatible con el estndar C89.
85

86

CAPTULO 49. PROGRAMACIN EN C/INTRODUCCIN

49.4 Para los principiantes


Para quien no haya programado antes, es recomendable seguir el orden del libro. Los temas estn especialmente
organizados de manera incremental o acumulativa. Tal vez, lo que se te va a hacer ms til en el camino del aprendizaje
es la constancia; s terco, no trastabilles, no te rindas, tal vez tu pregunta sea cuntas veces tengo que intentar?, las
veces necesarias para lograr tu objetivo, sera la respuesta.
Claro que el principal enemigo de nosotros los humanos es el tiempo y por eso en caso de que de verdad ests trancado
en algo busca ayuda de alguien que sepa ms que t". Que no tienes a nadie a tu alrededor con esa caracterstica?
Tal vez no buscaste bien y tal vez quieras usar la red de redes. Utiliza los buscadores, pregunta en IRC, en foros de
programacin, en listas de correo.

49.5 Para los ms avanzados


El lanzamiento queda libre por supuesto, solo t sabes lo que necesitas. Las reglas del juego son las mismas de siempre:
primero saber lo que se quiere o necesita y atacar por ah.
En este caso, te ser til acceder a los contenidos a partir del ndice, eligiendo slo aquellos que te sean necesarios.

49.6 Requisitos
Se presupone que los lectores tienen conocimientos elementales de informtica a nivel de usuario, y son capaces
de instalar un compilador del lenguaje C en sus sistema. Los detalles sobre la instalacin se vern en la seccin
Herramientas.
Con respecto al Hardware, slo ser necesario contar con una PC con sistema operativo, donde sea posible instalar un
compilador, y en lo posible un entorno de desarrollo. Cuanto mejor sea la computadora, ms rpido ser el proceso
de compilacin y ejecucin de los programas. Sin embargo, cualquier PC sirve para aprender con los ejemplos de
este libro.
Para quienes no tengan conocimientos bsicos de programacin, puede ser una buena idea comenzar leyendo los
primeros captulos del Wikilibro Fundamentos de programacin, ya que algunos temas explicados en ese libro se
asumen ya conocidos.
Finalmente, un requisito imprescindible en todo programador o programadora es tener sentido comn. Muchas veces
se pueden adoptar mejores o peores soluciones ante los diversos problemas, y la decisin de cul elegir pasa por la
aplicacin del sentido comn. yapa

49.7 Herramientas
Para programar tanto en C, como en C++, Java o cualquier otro lenguaje de programacin, necesitamos contar con
aplicaciones o herramientas que nos permitan poner en funcionamiento nuestro programa.
El lenguaje de programacin C es compilado, as que en este caso necesitaremos un compilador, que ser el encargado
de transformar nuestro cdigo fuente en cdigo que la computadora pueda ejecutar.
Adems, para facilitar la tarea de los programadores existen los denominados Entorno de desarrollo integrados
(IDE). En muchos casos, estos entornos incluyen un compilador, un depurador, y otras herramientas.
Las herramientas a instalar dependern del sistema operativo utilizado. A continuacin se listan algunas posibilidades
para el sistema operativo Windows o GNU/Linux, no es imprescindible utilizar estas herramientas en particular,
cualquier compilador puede servir.

49.8 Windows
Uno de los entornos de desarrollo ms conocidos entre los programadores de C sobre Windows, tanto novatos como
expertos, es el Bloodshed Dev-C++, que es un entorno libre multiplataforma. Tal entorno de desarrollo fue abando-

49.9. GNU/LINUX

87

nado y retomado mejorndolo pasando a llamarse WxDev-C++. Otro entorno libre y gratuito es el Code::Blocks.
Ambos entornos pueden utilizarse tanto para C como para C++.
Tambin hay otras alternativas privativas como los compiladores de Borland o de Microsoft (Microsoft Visual C++).

49.9 GNU/Linux
En los sistemas GNU/Linux, ser necesario tener instaladas las herramientas gcc y make y la versin 6 de la glibc
con su documentacin, que son las que permitirn compilar los programas.
Para escribir y modicar el cdigo, es posible utilizar cualquier editor de texto plano (en lo posible que cuente con
resaltado de sintaxis), como son emacs, vim, kate, gedit o geany.
Sin embargo, para quienes son novatos en la programacin, es recomendable utilizar un entorno de desarrollo como
son el Anjuta DevStudio (para el entorno GNOME) o KDevelop (para el entorno KDE), ya que incluyen facilidades
adicionales para la ejecucin y solucin de problemas.
Los programas mencionados se incluyen dentro de la instalacin estndar de la mayora de las distribuciones actuales
de GNU/Linux, de modo que para instalarlos slo ser necesario seguir el procedimiento usual de instalacin de
aplicaciones para la distribucin deseada.

Captulo 50

Programacin en C/Manejo dinmico de


memoria
Seguramente durante todo tu recorrido con este libro, especialmente en la seccin de punteros, te hablbamos sobre
la asignacin dinmica de memoria. Como su nombre lo dice, este es una forma de conseguir espacio en memoria,
dndote mas eciencia y tcnicamente hacer lo que requieras hacer con este.
En esta seccin haremos uso bastante (inevitablemente) de los punteros, por lo que te recomendamos que tengas muy
bien establecidos estos conocimientos.

50.1 Memoria dinmica


Es memoria que se reserva en tiempo de ejecucin. Su principal ventaja frente a la esttica, es que su tamao puede
variar durante la ejecucin del programa. (En C, el programador es encargado de liberar esta memoria cuando no la
utilice ms). El uso de memoria dinmica es necesario cuando a priori no conocemos el nmero de datos/elementos
a tratar.

50.2 Memoria esttica


Es el espacio en memoria que se crea al declarar variables de cualquier tipo de dato (primitivas [int,char...] o derivados
[struct,matrices,punteros...]). La memoria que estas variables ocupan no puede cambiarse durante la ejecucin y
tampoco puede ser liberada manualmente.

50.3 Diferencias, ventajas y desventajas


La memoria reservada de forma dinmica suele estar alojada en el heap o almacenamiento libre, y la memoria esttica
en el stack o pila (con excepcin de los objetos de duracin esttica, que se vern ms adelante, los cuales normalmente
se colocan en una zona esttica de datos). La pila generalmente es una zona muy limitada. El heap, en cambio, en
principio podra estar limitado por la cantidad de memoria disponible durante la ejecucin del programa y el mximo
de memoria que el sistema operativo permita direccionar a un proceso. La pila puede crecer de forma dinmica,
pero esto depende del sistema operativo. En cualquier caso, lo nico que se puede asumir es que muy probablemente
dispondremos de menor espacio en la pila que en el heap.
Otra ventaja de la memoria dinmica es que se puede ir incrementando durante la ejecucin del programa. Esto
permite, por ejemplo, trabajar con arreglos dinmicos. Aunque en C, a partir del estndar C99 se permite la creacin
de arreglos cuyo tamao se determina en tiempo de ejecucin, no todos los compiladores implementan este estndar.
Adems, se sigue teniendo la limitante de que su tamao no puede cambiar una vez que se especica, cosa que s se
puede lograr asignando memoria de forma dinmica.
Una desventaja de la memoria dinmica es que es ms difcil de manejar. La memoria esttica tiene una duracin
88

50.4. EL LENGUAJE C Y EL MANEJO DE LA MEMORIA

89

ja, que se reserva y libera de forma automtica. En contraste, la memoria dinmica se reserva de forma explcita y
contina existiendo hasta que sea liberada, generalmente por parte del programador.
La memoria dinmica puede afectar el rendimiento. Puesto que con la memoria esttica el tamao de las variables se
conoce en tiempo de compilacin, esta informacin est incluida en el cdigo objeto generado, por lo cual el proceso
es muy eciente. Cuando se reserva memoria de manera dinmica, se tienen que llevar a cabo varias tareas, como
buscar un bloque de memoria libre y almacenar la posicin y tamao de la memoria asignada, de manera que pueda
ser liberada ms adelante. Todo esto representa una carga adicional, aunque esto depende de la implementacin y hay
tcnicas para reducir su impacto.

50.4 El lenguaje C y el manejo de la memoria


Todos los objetos tienen un tiempo de vida, es decir, el tiempo durante el cual se garantiza que el objeto exista. En C,
existen 3 tipos de duracin: esttica, automtica y asignada. Las variables globales y las variables locales declaradas
con el especicador static tienen duracin esttica. Se crean antes de que el programa inicie su ejecucin y se destruyen
cuando el programa termina. Las variables locales no static tienen duracin automtica. Se crean al entrar al bloque
en el que fueron declaradas y se destruyen al salir de ese bloque. Duracin asignada se reere a los objetos cuya
memoria se reserva de forma dinmica. Como se explic anteriormente, esta memoria se crea y se debe liberar de
forma explcita. Los arreglos de longitud variable de C99 son un caso especial. Tienen duracin automtica, con la
particularidad de que son creados a partir de su declaracin.
La biblioteca estndar de C proporciona las funciones malloc, calloc, realloc y free para el manejo de memoria
dinmica. Estas funciones estn denidas en el archivo de cabecera stdlib.h.

50.4.1

malloc

La funcin malloc reserva un bloque de memoria y devuelve un puntero void al inicio de la misma. Tiene la siguiente
denicin:
void *malloc(size_t size);
donde el parmetro size especica el nmero de bytes a reservar. En caso de que no se pueda realizar la asignacin,
devuelve el valor nulo (denido en la macro NULL), lo que permite saber si hubo errores en la asignacin de memoria.
Ej:
int *puntero; char *puntcarc; puntero=(int *)malloc(4); puntcarc=(char *)malloc(200);
A continuacin se muestra un ejemplo de su uso:
int *i; /* Reservamos la memoria suciente para almacenar un int y asignamos su direccin a i */ i = malloc(sizeof(int)); /* Vericamos que la asignacin se haya realizado correctamente */ if (i == NULL) { /* Error
al intentar reservar memoria */ }
Uno de los usos ms comunes de la memoria dinmica es la creacin de vectores cuyo nmero de elementos se dene
en tiempo de ejecucin:
int *vect1, n; printf(Nmero de elementos del vector: "); scanf("%d, &n); /* reservar memoria para almacenar n
enteros */ vect1 = malloc(n * sizeof(int)); /* Vericamos que la asignacin se haya realizado correctamente */ if
(vect1 == NULL) { /* Error al intentar reservar memoria */ }

50.4.2

calloc

La funcin calloc funciona de modo similar a malloc, pero adems de reservar memoria, inicializa a 0 la memoria
reservada. Se usa comnmente para arreglos y matrices. Est denida de esta forma:
void *calloc(size_t nmemb, size_t size);

90

CAPTULO 50. PROGRAMACIN EN C/MANEJO DINMICO DE MEMORIA

El parmetro nmemb indica el nmero de elementos a reservar, y size el tamao de cada elemento. El ejemplo anterior
se podra reescribir con calloc de esta forma:
int *vect1, n; printf(Nmero de elementos del vector: "); scanf("%d, &n); /* Reservar memoria para almacenar n
enteros */ vect1 = calloc(n, sizeof(int)); /* Vericamos que la asignacin se haya realizado correctamente */ if (vect1
== NULL) { /* Error al intentar reservar memoria */ }

50.4.3

realloc

La funcin realloc redimensiona el espacio asignado de forma dinmica anteriormente a un puntero. Tiene la siguiente
denicin:
void *realloc(void *ptr, size_t size);
Donde ptr es el puntero a redimensionar, y size el nuevo tamao, en bytes, que tendr. Si el puntero que se le pasa
tiene el valor nulo, esta funcin acta como malloc. Si la reasignacin no se pudo hacer con xito, devuelve un puntero
nulo, dejando intacto el puntero que se pasa por parmetro. Al usar realloc, se debera usar un puntero temporal. De
lo contrario, podramos tener una fuga de memoria, si es que ocurriera un error en realloc.
Ejemplo de realloc usando puntero temporal:
/* Reservamos 5 bytes */ void *ptr = malloc(5); /* Redimensionamos el puntero (a 10 bytes) y lo asignamos a un
puntero temporal */ void *tmp_ptr = realloc(ptr, 10); if (tmp_ptr == NULL) { /* Error: tomar medidas necesarias
*/ } else { /* Reasignacin exitosa. Asignar memoria a ptr */ ptr = tmp_ptr; }
Cuando se redimensiona la memoria con realloc, si el nuevo tamao (parmetro size) es mayor que el anterior, se
conservan todos los valores originales, quedando los bytes restantes sin inicializar. Si el nuevo tamao es menor, se
conservan los valores de los primeros size bytes. Los restantes tambin se dejan intactos, pero no son parte del bloque
regresado por la funcin.

50.4.4

free

La funcin free sirve para liberar memoria que se asign dinmicamente. Si el puntero es nulo, free no hace nada.
Tiene la siguiente denicin:
void free(void *ptr);
El parmetro ptr es el puntero a la memoria que se desea liberar:
int *i; i = malloc(sizeof(int)); free(i);
Una vez liberada la memoria, si se quiere volver a utilizar el puntero, primero se debe reservar nueva memoria con
malloc o calloc:
int *i = malloc(sizeof(int)); free(i); /* Reutilizamos i, ahora para reservar memoria para dos enteros */ i = malloc(2 * sizeof(int)); /* Volvemos a liberar la memoria cuando ya no la necesitamos */ free(i);

50.5 Buenas prcticas


Como se vio en las secciones anteriores, siempre que se reserve memoria de forma dinmica con malloc, realloc o
calloc, se debe vericar que no haya habido errores (vericando que el puntero no sea NULL). Cuando se trata de
vericar el valor de un puntero (y slo en ese caso), se puede usar de forma indistinta 0 NULL. Usar uno u otro es
cuestin de estilo. Como ya se vio, las funciones de asignacin dinmica de memoria devuelven un puntero void. Las
reglas de C establecen que un puntero void se puede convertir automticamente a un puntero de cualquier otro tipo,
por lo que no es necesario hacer una conversin (cast), como en el siguiente ejemplo:
/* El puntero void devuelto por malloc es convertido explcitamente a puntero int */ int *i = (int *)malloc(sizeof(int));

50.5. BUENAS PRCTICAS

91

Aunque no hay un consenso, muchos programadores preeren omitir la conversin anterior porque la consideran
menos segura. Si accidentalmente se olvida incluir el archivo stdlib.h (donde estn denidas malloc, calloc, realloc y
free) en un programa que use dichas funciones, el comportamiento puede quedar indenido. Si omitimos la conversin
explcita, el compilador lanzar una advertencia. Si, en cambio, realizamos la conversin, el compilador generar el
cdigo objeto de forma normal, ocultado el bug.
Una posible razn para usar la conversin explcita es si se escribe cdigo en C que se vaya a compilar junto con
cdigo C++, ya que en C++ s es necesario realizar esa conversin.
En cualquier caso, dado que el manejo de memoria es un tema complejo, y ste es un error muy comn, se debe hacer
nfasis en que cuando se trabaja con memoria dinmica, siempre se debe vericar que se incluya el archivo stdlib.h.
Tratar de utilizar un puntero cuyo bloque de memoria ha sido liberado con free puede ser sumamente peligroso.
El comportamiento del programa queda indenido: puede terminar de forma inesperada, sobrescribir otros datos y
provocar problemas de seguridad. Liberar un puntero que ya ha sido liberado tambin es fuente de errores.
Para evitar estos problemas, se recomienda que despus de liberar un puntero siempre se establezca su valor a NULL.
int *i; i = malloc(sizeof(int)); free(i); i = NULL;

92

CAPTULO 50. PROGRAMACIN EN C/MANEJO DINMICO DE MEMORIA

50.6 Text and image sources, contributors, and licenses


50.6.1

Text

Programacin en C/Primer programa en C Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Primer%20programa%


20en%20C?oldid=225736 Colaboradores: ManuelGR, Envite, Almorca, Rutrus, X.Cyclop, Ikks, M.estarellas, Ksaver, Morza, MarcoAurelio, Luckas Blade, Maxy, Margamanterola, Url999, Dessaya y Annimos: 13
Programacin en C/Tipos de datos Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Tipos%20de%20datos?
oldid=225259 Colaboradores: Javier Carro, ManuelGR, Gargo, Alhen, Rafael.galvez.vizcaino, M.estarellas, MarcoAurelio, Ezarate, Don
Quijote, Wutsje, Juliancolton, Maxy, Margamanterola, Savh, Igna, Dessaya, Hahc21, LlamaAl y Annimos: 46
Programacin en C Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C?oldid=236462 Colaboradores: Pablo.cl,
Javier Carro, ManuelGR, Envite, Almorca, CaStarCo, Marsian, Gargo, H, Morfeomtx, LadyInGrey, Josemanuelmv, Valenluis, Necastro, Rafael.galvez.vizcaino, Magister Mathematicae, Ksaver, Oleinad, AlexGPL, Raulshc, Cvmontuy, MABot, Zerohours, Maxy, Margamanterola, C1245j7414, CarsracBot, Earizon, Asele, Savh, Jcaraballo, Narutoeshacker15, Igna, Polyglottos, Dessaya, Alan, LlamaAl y
Annimos: 56
Programacin en C/Expresiones Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Expresiones?oldid=233315
Colaboradores: Gargo, Baldur, Morza, Maxy, Savh, Dessaya y Annimos: 3
Programacin en C/Interaccin con el usuario Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Interacci%
C3%B3n%20con%20el%20usuario?oldid=199999 Colaboradores: Gargo, M.estarellas, Maxy, Dessaya y Annimos: 8
Programacin en C/Instrucciones de control Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Instrucciones%
20de%20control?oldid=232369 Colaboradores: ManuelGR, Envite, CaStarCo, Gargo, Magister Mathematicae, Luis449bp, Juanias, Zerohours, Maxy, Margamanterola, Arpia49, Fgrvpro, Savh, TheOrlSan, Dessaya, LlamaAl y Annimos: 58
Programacin en C/Uso de funciones Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Uso%20de%20funciones?
oldid=237861 Colaboradores: Gargo, Muro de Aguas, Morza, MarcoAurelio, Rrmsjp, Luckas Blade, Zerohours, Maxy, Fgrvpro, Dessaya,
LlamaAl, Matiia y Annimos: 19
Programacin en C/Vectores Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Vectores?oldid=226053 Colaboradores: Gargo, Josemanuelmv, Magister Mathematicae, Gallaecio, Kved, Maxy, Nahuel9000 y Annimos: 10
Programacin en C/Cadenas de caracteres Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Cadenas%20de%
20caracteres?oldid=234616 Colaboradores: Maxy y Annimos: 1
Programacin en C/Manejo de archivos Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Manejo%20de%
20archivos?oldid=234069 Colaboradores: ManuelGR, Gargo, Exe, Jpag87a, Oleinad, Morza, Ezarate, Malonph, Unopresente, Zerohours,
Irtusb, Mchingotto, Savh, Ruy Pugliesi, Juanhauara, LlamaAl, Godric y Annimos: 59
Programacin en C/Estructuras y Uniones Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Estructuras%
20y%20Uniones?oldid=232785 Colaboradores: Gargo, Taichi, Zerohours, Savh, LlamaAl y Annimos: 16
Programacin en C/Punteros Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Punteros?oldid=238033 Colaboradores: Jcongote, Morza, Luckas Blade, Ezarate, Jcaraballo, Frigotoni, Narutoeshacker15, Igna, Ralgisbot, Alan y Annimos: 35
Programacin en C/Matrices Dinamicas Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Matrices%20Dinamicas?
oldid=191878 Colaboradores: Narutoeshacker15
Programacin en C/Algoritmos y Estructuras de Datos Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/
Algoritmos%20y%20Estructuras%20de%20Datos?oldid=237932 Colaboradores: Morza, Rrmsjp, Savh, Narutoeshacker15, Igna y Annimos: 11
Programacin en C/El proceso de compilacin Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/El%20proceso%
20de%20compilaci%C3%B3n?oldid=142589 Colaboradores: Envite y Annimos: 2
Programacin en C/Los errores Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Los%20errores?oldid=236764
Colaboradores: Gargo y Annimos: 4
Programacin en C/Herramientas externas tiles Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Herramientas%
20externas%20%C3%BAtiles?oldid=164446 Colaboradores: ManuelGR, Envite, Jespa y Annimos: 5
Programacin en C/Ejemplos Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Ejemplos?oldid=235095 Colaboradores: ManuelGR, Envite, Drinibot, Margamanterola, Savh, Ajraddatz, Igna, Ixfd64 y Annimos: 11
Programacin en C/Glib Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Glib?oldid=183036 Colaboradores:
Earizon, Invadibot y Annimos: 1
Programacin en C/Referencia Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Referencia?oldid=175770 Colaboradores: ManuelGR, Almorca, Gargo, Fires, Wutsje, Drinibot y Annimos: 14
Programacin en C/Cmo compilar un programa Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/C%C3%
B3mo%20compilar%20un%20programa?oldid=171307 Colaboradores: ManuelGR, Gargo, Magister Mathematicae, Drinibot y Annimos: 2
Programacin en C/Cdigo embebido Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/C%C3%B3digo%20embebido?
oldid=146562 Colaboradores: Gargo, Vinikike, ONDIUX y Annimos: 2
Programacin en C/Recursos en la red Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Recursos%20en%
20la%20red?oldid=148592 Colaboradores: Almorca, Earizon, Lyonn y Annimos: 4
Programacin en C/Bibliografa Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Bibliograf%C3%ADa?oldid=
35363 Colaboradores: Almorca

50.6. TEXT AND IMAGE SOURCES, CONTRIBUTORS, AND LICENSES

93

Fundamentos de programacin Fuente: http://es.wikibooks.org/wiki/Fundamentos%20de%20programaci%C3%B3n?oldid=192637


Colaboradores: Javier Carro, ManuelGR, Sergiores, Envite, Danyur, Joserri, Almorca, Mpagano, Rutrus, Rocastelo, Oskarcah, Ciencia
Al Poder, Chlewbot, Jespa, Swazmo, Magister Mathematicae, Oleinad, LTSmash, HiTe, Especiales, MarcoAurelio, MABot, Drinibot,
Wikimi-dhiann, Mjbmrbot, Edgardo C, Polyglottos y Annimos: 24
Programacin en C/Fundamentos de programacin Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Fundamentos%
20de%20programaci%C3%B3n?oldid=205436 Colaboradores: ManuelGR, Envite, Almorca, Anakayama, Gargo, X.Cyclop, Rodrigobgcr,
Rafael.galvez.vizcaino, M.estarellas, Maxy, Margamanterola, Polyglottos, Dessaya, LlamaAl y Annimos: 10
Programacin en C/Historia de C Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Historia%20de%20C?oldid=
232029 Colaboradores: Javier Carro, ManuelGR, Envite, Almorca, X.Cyclop, Morza, Maxy, Margamanterola, JackPotte, Ortisa, Savh,
Anndresorozco, Jcaraballo y Annimos: 15
Programacin en C/Introduccin Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Introducci%C3%B3n?oldid=
215504 Colaboradores: Almorca, Gargo, Tigerfenix, Rafael.galvez.vizcaino, Magister Mathematicae, Maxy, Margamanterola, C1245j7414,
Rocafort8 y Annimos: 8
Programacin en C/Manejo dinmico de memoria Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Manejo%
20din%C3%A1mico%20de%20memoria?oldid=237400 Colaboradores: CaStarCo, Gargo, Morza, Url999, Narutoeshacker15, Igna, Simeondahl, Alan, LlamaAl y Annimos: 16

50.6.2

Images

Archivo:00%.svg Fuente: http://upload.wikimedia.org/wikipedia/commons/d/d6/00%25.svg Licencia: Public domain Colaboradores:


Based on the XML code of Image:25%.svg Artista original: Siebrand
Archivo:100%.svg Fuente: http://upload.wikimedia.org/wikipedia/commons/c/c7/100_percents.svg Licencia: CC0 Colaboradores: File:
100%.svg Artista original: Siebrand
Archivo:25%.svg Fuente: http://upload.wikimedia.org/wikipedia/commons/3/34/25%25.svg Licencia: Public domain Colaboradores:
Image:25%.png redone in svg. Artista original: Karl Wick
Archivo:50%.svg Fuente: http://upload.wikimedia.org/wikipedia/commons/c/c2/50%25.svg Licencia: Public domain Colaboradores:
Based on the XML code of Image:25%.svg Artista original: Siebrand
Archivo:75%.svg Fuente: http://upload.wikimedia.org/wikipedia/commons/4/49/75%25.svg Licencia: Public domain Colaboradores:
Based on the XML code of Image:25%.svg Artista original: Siebrand

50.6.3

Content license

Creative Commons Attribution-Share Alike 3.0

Das könnte Ihnen auch gefallen