Progamar en C

105
Progamar en C wikilibros

description

Descripción Básica para desarrollar software en el lenguaje C

Transcript of Progamar en C

Progamar en Cwikilibros

Índice general

1 Programación en C 11.1 Prólogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.1.1 ¿Para quién es este libro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1.2 ¿Por qué otro manual de C? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Índice de contenidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2.1 Anexos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.3 Enlaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.4 Licencia y autores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.5 ¿Cómo contribuir a este WikiLibro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Programación en C/Introducción 52.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.2 Nota sobre la exactitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3 Estándar utilizado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.4 Para los principiantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.5 Para los más avanzados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.6 Requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.7 Herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.8 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.9 GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

3 Programación en C/Historia de C 83.1 Evolución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2 Más información . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

4 Programación en C/Fundamentos de programación 104.1 Definiciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104.2 Tipos de lenguajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

4.2.1 Esquemas de programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104.2.2 Alto o bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

i

ii ÍNDICE GENERAL

4.2.3 Compilados o interpretados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114.3 Estructura de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

5 Programación en C/Primer programa en C 125.1 Pre-requisitos para la compilación de programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125.2 Compilación de programas según la plataforma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

5.2.1 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135.2.2 GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

5.3 Diseccionando el “Hola Mundo” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145.4 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

6 Programación en C/Tipos de datos 176.1 Historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176.2 Enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176.3 Flotantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186.4 Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

7 Programación en C/Expresiones 19

8 Programación en C/Interacción con el usuario 218.1 Imprimir por pantalla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218.2 Lectura de datos del teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

8.2.1 La función scanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

9 Programación en C/Instrucciones de control 239.1 La estructura condicional if ... else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

9.1.1 Operadores de comparación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249.1.2 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249.1.3 Evaluación de cortocircuito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

9.2 La estructura condicional abierta y cerrada switch ... case . . . . . . . . . . . . . . . . . . . . . . . . . 259.3 El bucle while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259.4 El bucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269.5 El bucle do...while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269.6 La sentencia goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

10 Programación en C/Uso de funciones 2810.1 Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

10.1.1 La sentencia return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2810.1.2 Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2810.1.3 Declaración y definición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2910.1.4 Paso de Parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

ÍNDICE GENERAL iii

10.1.5 Variables Locales y Globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3010.2 Funciones Recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

10.2.1 Recursividad indirecta o recursión mutua . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3110.2.2 Recursión versus Iteración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

11 Programación en C/Vectores 33

12 Programación en C/Cadenas de caracteres 35

13 Programación en C/Manejo de archivos 3713.1 Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

13.1.1 fopen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3713.1.2 fclose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3813.1.3 feof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3813.1.4 rewind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

13.2 Lectura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3913.2.1 fgetc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3913.2.2 fgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3913.2.3 fread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4013.2.4 fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

13.3 Escritura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4013.3.1 fputc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113.3.2 fputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113.3.3 fwrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113.3.4 fprintf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

14 Programación en C/Estructuras y Uniones 4314.1 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

14.1.1 Estructuras Anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4414.2 Uniones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4514.3 Enumeraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

15 Programación en C/Punteros 47

16 Concepto 48

17 Declarando punteros 49

18 Explicación 50

19 Operadores 51

iv ÍNDICE GENERAL

20 Operaciones Aritméticas 52

21 Punteros Constantes 53

22 Punteros Genericos 54

23 Punteros y Matrices 55

24 Punteros a cadenas de caracteres 56

25 Matrices de Punteros 57

26 Punteros a Punteros 58

27 Matrices de punteros a cadenas de caracteres 59

28 Ejemplos 6028.0.1 Asignando valores a punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

29 Programación en C/Manejo dinámico de memoria 6229.1 Memoria dinámica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6229.2 Memoria estática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6229.3 Diferencias, ventajas y desventajas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6229.4 El lenguaje C y el manejo de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

29.4.1 malloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6329.4.2 calloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6429.4.3 realloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6429.4.4 free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

29.5 Buenas prácticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

30 Programación en C/Matrices Dinamicas 66

31 Concepto 67

32 Matrices Dinamicas de Dos Dimensiones 68

33 Programación en C/Algoritmos y Estructuras de Datos 69

34 Concepto 70

35 Metodo de Burbuja (Buble Sort) 7135.1 Explicacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7135.2 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

ÍNDICE GENERAL v

36 Método de Inserción 7336.1 Concepto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7336.2 Explicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7336.3 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

37 Método QuickSort 7437.1 Concepto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7437.2 Explicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7437.3 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

38 Programación en C/El proceso de compilación 76

39 Los pasos del proceso 7739.1 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

40 Programación en C/Los errores 7940.1 Chequeos del C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

41 Programación en C/Herramientas externas útiles 8041.1 Sistemas de construcción de código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

42 make 8142.1 Sistemas de control de versiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

43 Programación en C/Ejemplos 8343.1 El “Hola Mundo” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8343.2 El “Hola Mundo” comentado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8343.3 El “Hola Mundo” estructurado en funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8343.4 Ejemplo de cálculo con enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8443.5 Control de acceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

44 Programación en C/Glib 8644.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8644.2 Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8744.3 Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

45 Programación en C/Referencia 8845.1 Tipos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8845.2 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8945.3 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9045.4 Bibliotecas y funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9045.5 Preprocesador de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

vi ÍNDICE GENERAL

46 Programación en C/Cómo compilar un programa 9246.1 Un poco más sobre compilación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

47 Programación en C/Código embebido 93

48 Programación en C/Recursos en la red 9448.1 Manuales, tutoriales y otros documentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

48.1.1 Español . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9448.1.2 Inglés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

48.2 Compiladores e IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

49 Programación en C/Bibliografía 9649.1 Bibliografía en español . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9649.2 Bibliografía en inglés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9649.3 Text and image sources, contributors, and licenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

49.3.1 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9749.3.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9849.3.3 Content license . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

Capítulo 1

Programación en C

1.1 Prólogo

El avance de la tecnología y la concurrente entrega de información, nos permite generar una edición, variable y alternativaen la enseñanza del lenguaje de programación que más impacto social ha tenido en la historia de la informática. Este libroha sido forjado con la incansable ayuda de informáticos de habla hispana, quienes byte a byte han colaborado por hacer dela información una fuente de conocimiento global. De la misma forma, continúa hoy siendo modificado en una constantebúsqueda de superación de la calidad.Esta obra está llamada a ser la piedra angular en la enseñanza de la programación, logrando abarcar todos los aspectos dellenguaje en diversos niveles y de esta forma ser tanto una referencia técnica para quienes ya dominan el lenguaje comouna introducción sencilla para quienes están empezando a conocerlo.Viajaremos por la historia del lenguaje, veremos su propósito e indagaremos en la ciencia de la programación. El fin esotorgar al lector una doctrina clara de la programación y el lenguaje C; lo induciremos a conseguir un manejo importantedel lenguaje.

1.1.1 ¿Para quién es este libro?

Este libro está dirigido a todos los que deseen obtener conocimientos de programación, pues el objetivo explícito que nosha motivado a crearlo es difundir la importancia del lenguaje C en el mundo de la informática. Si nos enfocamos a ungrupo social específico, podremos indicar que este libro contribuirá con los estudiantes de carreras del área informática,debido a que los temas convenidos, son parte de su plan de estudios.

1.1.2 ¿Por qué otro manual de C?

Porque el lenguaje C es la base fundamental de la programación. Para quienes están en el ambiente de la informáticaes crucial tener por lo menos nociones de este lenguaje. Varios sistemas operativos, cientos de bibliotecas, y miles deprogramas están 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 comonosotros, que empezaron con nuestro mismo potencial y fueron aprendiendo a hacer cosas fantásticas con las herramientasque tenían a mano.Una razón importante para otro libro de lenguaje C es también que los libros existentes muestran muy poca documen-tación de calidad. En Internet existe una cantidad inmensa de información publicada pero está dispersa, y mal manejadaen algunos sitios. Es la intención de este libro crear un buen compendio de información, que permita a los interesadosaprender a programar en C.

1

2 CAPÍTULO 1. PROGRAMACIÓN EN C

1.2 Índice de contenidos1. Introducción

(a) Herramientas necesarias

2. Historia de C

3. Fundamentos de programación

4. Primer programa en C

(a) Compilación de programas en C

5. Tipos de datos

(a) Enteros(b) Reales(c) Caracteres

6. Expresiones

7. Interacción 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 dinámico de memoria

16. Matrices Dinamicas

17. Algoritmos y Estructuras de Datos

(a) Punteros a Estructuras

1.2. ÍNDICE DE CONTENIDOS 3

(b) Ordenamiento de Datosi. Burbuja

ii. Induccioniii. QuickSort

18. Detalles sobre el proceso de compilación

19. Los errores

(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 cálculo con enteros(e) Ejemplo comentado de cálculo con enteros(f) Calendario en C(g) Cálculo del logaritmo binario

22. La librería glib

23. Programación paralela en memoria compartida

1.2.1 Anexos

1. Referencia

(a) Tipos de datos(b) Operadores(c) Estructuras(d) Bibliotecas y funciones

2. Cómo compilar un programa

(a) Un poco más sobre compilación

3. Código embebido

4. Recursos en la red

(a) Manuales, tutoriales y otros documentos(b) Compiladores e IDEs

5. Bibliografía

4 CAPÍTULO 1. PROGRAMACIÓN EN C

1.3 Enlaces

Nociones básicas de programación (generales, independientes del lenguaje):

• Fundamentos de programación

Wikilibro similar a éste en inglés

• C Programming

esquema de operadores y expresiones

1.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 página para el resto de autores.

1.5 ¿Cómo contribuir a este WikiLibro?

Contribuir con este libro es muy simple primero deberías registrarte un usuario y/o entrar (esto no es necesario pero si muyconveniente) a Wikilibros, para que podamos identificar tus ediciones. Luego, si nunca has contribuido en un proyecto dewikipedia o en otro wiki deberías 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 editarcualquier sección basta con hacer click al link que dice editar en la pestaña en la parte superior de la pagina, seria buenorevisar (antes de editar cualquier cosa) la pestaña de discusión que está ahí para ser usada. Ten en cuenta que el materialque ya está fue escrito por personas que deseaban contribuir igual que tú, así que trata de respetarlo. Aunque con esto nome refiero a que si se necesita alguna corrección, reorganización, quitar partes que sean ambiguas, no dejes de hacerlo.Lo mejor sería que todos contribuyéramos de cualquier manera al libro.Además, es recomendable consultar la página de discusión del libro y la del articulo en particular que quieras modificar,ya que de esta manera se pueden coordinar esfuerzos.Recuerda que todo el contenido que añadas al libro es publicado bajo la licencia GFDL, por lo que no uses material queno haya sido escrito por ti o que no esté ya publicado bajo GFDL. Recientemente wikimedia decidió adoptar la LicenciaCreative Commons Compartir-Igual 3.0 para todos los aportes, por lo que actualmente el libro tiene una licencia dual.

Capítulo 2

Programación en C/Introducción

2.1 Objetivos

El objetivo principal de este Wikilibro es que cualquier persona sin conocimientos previos de programación pueda sercapaz de programar en el lenguaje C.Una vez logrado el dominio del lenguaje, es probable que los lectores se interesen por otros temas más complejos quesuperen a los temas básicos. También les será más o menos sencillo aprender cualquier otro lenguaje de programaciónestructurada.Sin embargo, este no es un libro que apunte únicamente a programadores principiantes. También puede resultar de interéspara quienes ya tengan experiencia en el área de programación. En esta introducción hay dos secciones en las que seexplica para los dos grupos principales de lectores qué camino seguir para comenzar a programar en el lenguaje C o bienperfeccionar conocimientos.El lenguaje C es tan usado porque es un lenguaje de programación que emplea pocas instrucciones en lenguaje máquinapara traducir elementos del código. Esto reduce los tiempos de ejecución de los programas.

2.2 Nota sobre la exactitud

Muchas de las cosas expresadas en este wikilibro, especialmente en los primeros capítulos, no son completamente exactas,aunque son buenas aproximaciones. Los detalles más exactos irán apareciendo posteriormente, una vez que los materialesanteriores hayan sido correctamente asimilados por el lector. En general, dadas dos definiciones o datos contradictoriosen este wikilibro, debe considerarse siempre como más exacto al segundo, habiendo aparecido el primero como unaintroducción más general al tema.

2.3 Estándar utilizado

El lenguaje C fue creado en los años setenta, y a lo largo de su historia ha pasado por muchas modificaciones, tanto conrespecto a la sintaxis como con respecto al código incluido dentro de la biblioteca estándar. Es por ello que se fuerondesarrollando estándares, para que todos sepan con qué versión del lenguaje se está trabajando.Los distintos estándares del lenguaje C han sido: el C de Kernighan y Ritchie, un estándar no-oficial que surgió luego dela publicación de su libro en 1978; el C89 o C90, el primer estándar oficial, posterior a la publicación de los estándaresANSI en 1989 e ISO en 1990; y el C99, publicado en 1999.En este libro se utilizará el estándar C99, si bien por cuestiones de estilo y compatibilidad muchas veces se utilizará códigocompatible con el estándar C89.

5

6 CAPÍTULO 2. PROGRAMACIÓN EN C/INTRODUCCIÓN

2.4 Para los principiantes

Para quien no haya programado antes, es recomendable seguir el orden del libro. Los temas están especialmente orga-nizados de manera incremental o acumulativa. Tal vez, lo que se te va a hacer más útil en el camino del aprendizaje esla constancia; sé terco, no trastabilles, no te rindas, tal vez tu pregunta sea ¿cuántas veces tengo que intentar?, las vecesnecesarias para lograr tu objetivo, sería la respuesta.Claro que el principal enemigo de nosotros los humanos es el tiempo y por eso en caso de que de verdad estés trancado enalgo busca ayuda de alguien que sepa “más que tú". ¿Que no tienes a nadie a tu alrededor con esa característica? Tal vezno buscaste bien y tal vez quieras usar la red de redes. Utiliza los buscadores, pregunta en IRC, en foros de programación,en listas de correo.

2.5 Para los más 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 sólo aquellos que te sean necesarios.

2.6 Requisitos

Se presupone que los lectores tienen conocimientos elementales de informática a nivel de usuario, y son capaces de instalarun compilador del lenguaje C en sus sistema. Los detalles sobre la instalación se verán en la sección Herramientas.Con respecto al Hardware, sólo será necesario contar con una PC con sistema operativo, donde sea posible instalar uncompilador, y en lo posible un entorno de desarrollo. Cuanto mejor sea la computadora, más rápido será el proceso decompilación y ejecución de los programas. Sin embargo, cualquier PC sirve para aprender con los ejemplos de este libro.Para quienes no tengan conocimientos básicos de programación, puede ser una buena idea comenzar leyendo los prime-ros capítulos del Wikilibro Fundamentos de programación, ya que algunos temas explicados en ese libro se asumen yaconocidos.Finalmente, un requisito imprescindible en todo programador es tener sentido común. Muchas veces se pueden adoptarmejores o peores soluciones ante los diversos problemas, y la decisión de cuál elegir pasa por la aplicación del sentidocomún.

2.7 Herramientas

Para programar tanto en C, como en C++, Java o cualquier otro lenguaje de programación, necesitamos contar conaplicaciones o herramientas que nos permitan poner en funcionamiento nuestro programa.El lenguaje de programación C es compilado, así que en este caso necesitaremos un compilador, que será el encargadode transformar nuestro código fuente en código que la computadora pueda ejecutar.Además, 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 dependerán del sistema operativo utilizado. A continuación se listan algunas posibilidadespara el sistema operativo Windows o GNU/Linux, no es imprescindible utilizar estas herramientas en particular, cualquiercompilador puede servir.

2.8. WINDOWS 7

2.8 Windows

Uno de los entornos de desarrollo más conocidos entre los programadores de C sobre Windows, tanto novatos comoexpertos, es el Bloodshed Dev-C++, que es un entorno libre multiplataforma. Tal entorno de desarrollo fue abandonado yretomado mejorándolo pasando a llamarse WxDev-C++. Otro entorno libre y gratuito es el Code::Blocks. Ambos entornospueden utilizarse tanto para C como para C++.También hay otras alternativas privativas como los compiladores de Borland o de Microsoft (Microsoft Visual C++).

2.9 GNU/Linux

En los sistemas GNU/Linux, será necesario tener instaladas las herramientas gcc y make y la versión 6 de la glibc consu documentación, que son las que permitirán compilar los programas.Para escribir y modificar el código, es posible utilizar cualquier editor de texto plano (en lo posible que cuente con resaltadode sintaxis), como son emacs, vim, kate, gedit o geany.Sin embargo, para quienes son novatos en la programación, es recomendable utilizar un entorno de desarrollo como son elAnjuta DevStudio (para el entorno GNOME) o KDevelop (para el entorno KDE), ya que incluyen facilidades adicionalespara la ejecución y solución de problemas.Los programas mencionados se incluyen dentro de la instalación estándar de la mayoría de las distribuciones actuales deGNU/Linux, de modo que para instalarlos sólo será necesario seguir el procedimiento usual de instalación de aplicacionespara la distribución deseada.

Capítulo 3

Programación en C/Historia de C

El lenguaje de programación C fue creado por Dennis Ritchie entre 1969 y 1973 cuando trabajaba en Bell Laboratoriesde AT&T junto con Ken Thompson en el diseño del sistema operativo UNIX. C fue creado para poder escribir dichosistema 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 código podía serutilizado en las distintas plataformas, propiciando la reutilización de código y reduciendo los tiempos de desarrollo. Así esque los sistemas operativos basados en UNIX, el sistema BSD, el sistema GNU/Linux y muchos otros fueron desarrolladosen C.Además, con el paso del tiempo se han desarrollado cientos de bibliotecas que permiten a los programadores de C utilizarel código desarrollado por otros para la realización de tareas comunes. Esto, a su vez, ha propiciado el desarrollo deaplicaciones en lenguaje C.Actualmente es imposible contar la cantidad de aplicaciones y herramientas desarrolladas en C.

3.1 Evolución

A mediados de los años 60s, Martin Richards diseñó el lenguaje BCPL con la finalidad de usarlo para escribir softwarede sistemas operativos y compiladores.En 1969, Ken Thompson escribió el Lenguaje B, en Bell Laboratories, con el objetivo de recodificar UNIX (escrito hastaese momento en lenguaje ensamblador) usando un lenguaje de alto nivel más portable y flexible.Durante los siguientes años, Dennis Ritchie modificó el lenguaje B, llegando a crear el lenguaje C y reescribiendo elsistema UNIX en dicho lenguaje; añadió características nuevas, como son el diseño de tipos y las estructuras de datos.En 1978, Dennis Ritchie y Brian Kernighan publicaron la primera edición del libro El lenguaje de programación C. Estelibro fue durante años la especificación informal del lenguaje. El lenguaje descrito en la primera edición de este libro, fueconocido como “el C de Kernighan y Ritchie” o simplemente “K&R C”. En este libro se introdujeron nuevas característicasal lenguaje: los tipo de datos struct, long int y unsigned int; los operadores =+ y =- fueron sustituidos por += y -=.A mediados de los años 80, Bjarne Stroustrup (también de los laboratorios Bell), crea el lenguaje C++, un lenguaje basadoen C, con numerosas características adicionales, siendo la principal que está orientado a objetos. Si bien se han creadomuchos lenguajes basados en C, C++ es el que ha permanecido más asociado a C.En los años siguientes a la publicación del C de Kernighan y Ritchie, se añadieron al lenguaje muchas característicasno oficiales, que estaban presentes en algunos compiladores y no en otros. Fue por ello que en 1989 ANSI (AmericanNational Standards Institute) publicó el primer estándar oficial de C, que es conocido como ANSI C.En este estándar se tomaron muchas de las funcionalidades no oficiales y se agregaron funcionalidades nuevas como losprototipos de función, y un preprocesador mejorado. También se cambió la sintaxis de la declaración de parámetros defunciones, para que incluyeran el tipo junto con el nombre.

8

3.2. MÁS INFORMACIÓN 9

Al año siguiente, en 1990 se publicó la estandarización ISO del lenguaje. Este estándar es básicamente el estándar ANSI,con unas pocas modificaciones de formato. A este estándar se lo conoce, entonces, como C89, o C90, y se trata del mismolenguaje.Basándose en el estándar ANSI que estaba en preparación, en 1988 Kernighan y Ritchie publicaron la segunda ediciónde su libro, que es aún hoy utilizada como una de las referencias principales del lenguaje.Durante los siguientes años, el lenguaje C permaneció sin demasiados cambios. Sin embargo, como había sucedido antes,los distintos compiladores fueron incorporando características adicionales, que otros compiladores no tenían, siendo C++la principal influencia.Fue por ello que a finales de los noventa se decidió revisar el estándar de C, lo que llevó a la publicación del estándar C99.Este estándar incluye varias nuevas características como son: las funciones inline; la posibilidad de declarar variables encualquier parte del código; los comentarios de una sola línea utilizando //; los tipos de datos long long int, bool y complex,entre otras.Aún hoy el proceso de evolución del lenguaje sigue avanzando, y desde 2007 se está trabajando en el armado de un nuevoestándar.

3.2 Más información• Lenguaje de programación BCPL

• Historia del lenguaje del programación C

• The Development of the C Language

Capítulo 4

Programación en C/Fundamentos deprogramación

En este capítulo veremos un resumido listado de conceptos básicos, esta información puede encontrarse en forma máselaborada en el WikiLibro Fundamentos de programación.

4.1 Definiciones• 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 neumáticoson ejemplos de algoritmos de la vida real.

• Las computadoras, son maquinas sin inteligencia propia, cuya única finalidad es interpretar el código que se lesprovee.

• El lenguaje de máquina es el único lenguaje que la computadora “entiende” y es capaz de ejecutar.

• Los lenguajes de programación son el medio de comunicación entre el programador y una computadora. Elprogramador escribe en algún lenguaje de programación y utiliza las herramientas provistas por ese lenguaje paratransformarlo en lenguaje de máquina.

• Finalmente, denominamos programa a una secuencia de órdenes a ser ejecutadas por una computadora. Un pro-grama debe estar escrito en algún lenguaje de programación, y puede incluir uno o más algoritmos.

4.2 Tipos de lenguajes

Existe una gran cantidad de lenguajes de programación, que están pensados para distintas finalidades, siguen distintosparadigmas, y de una u otra forma se diferencian de los demás.

4.2.1 Esquemas de programación

El esquema de programación llamado Programación Imperativa, consiste en escribir una secuencia de instruccionesuna detrás de la otra, que se ejecutarán en orden. Algunas de esas instrucciones pueden hacer que la máquina pase a unainstrucción que no sea la siguiente, tal vez porque se cumpla una condición que hayamos establecido.

10

4.3. ESTRUCTURA DE LA MEMORIA 11

En los últimos años ha tomado fuerza otro paradigma de computación, llamado Programación Orientada a Objetos ,en el cual se intentan modelar los sistemas creados como extensiones de la realidad mediante la definición de “objetos”que modelan entidades de la vida real y que interactúan entre sí mediante “mensajes” llamadas métodos.El lenguaje C es un lenguaje imperativo, no orientado a objetos.

4.2.2 Alto o bajo nivel

Por otro lado, los lenguajes de programación se clasifican en niveles. Un lenguaje es de más bajo nivel cuanto más cercanoesté al código de máquina, y un lenguaje que es de más alto nivel cuanto más lejano esté de la máquina y más cercano allenguaje humano.C es un lenguaje de alto nivel aunque tiene muchas características de lenguaje de bajo nivel (como el uso que permite hacerde la memoria). Estas características hacen que C sea un lenguaje muy potente, ya que permite optimizar al máximo losrecursos de la máquina. Por ende, esto también hace que la dificultad y que los errores que se puedan cometer programandoaumenten. Así que a C se le considera de nivel medio.Lenguajes de más 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.

4.2.3 Compilados o interpretados

Otra forma de clasificar a los lenguajes de programación que es según la forma en que se ejecutan sus órdenes. Existenlos lenguajes que son interpretados, cuyas órdenes pasan a través de un intérprete que se encarga de ejecutarlas (a partirdel código fuente) en el mismo momento en que están siendo leídas. 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 enque las órdenes son transformadas a lenguaje de máquina que se almacena en un archivo ejecutable. Ese archivo puedeejecutarse luego, sin recurrir al compilador.Los lenguajes compilados tienen la ventaja de la velocidad y la eficiencia, pero los interpretados tienen la ventaja de que,generalmente, son muy portables y de más alto nivel.

4.3 Estructura de la memoria

Parte de esta potencia de C viene de que permite acceder con mucha libertad a la memoria de la máquina. Para entenderun poco cómo es posible, debemos entender cómo se guardan los datos en la memoria.Imaginemos que la memoria tiene un montón de casillas, una enorme fila de casillas, cada una de las cuales contiene undígito binario (bit):0101001010100001010101001010000100111010110010010101001011010110001101010110101010110111...Es exactamente así, pero es más cómodo recordar que esos bits se encuentran agrupados de ocho en ocho, formandooctetos (bytes):Cada octeto puede contener 28 = 256 combinaciones distintas de ceros y unos, es decir, cualquier número entre 0 y 255:También podemos representar estos números en base hexadecimal:O considerarlos caracteres, mediante alguna codificación:Este es el tipo de dato más elemental que nos podemos encontrar en C: el caracter. Un caracter ocupa exactamente unbyte (8 bits) de memoria, y puede contener un número entre 0 y 255, o entre −128 y 127, dependiendo si queremosconsiderarlo como sin signo o con él.

Capítulo 5

Programación en C/Primer programa en C

En el libro “El Lenguaje de Programación C”, Kernighan y Ritchie introdujeron al lenguaje C utilizando un sencilloprograma que mostraba un saludo por la pantalla. Desde entonces se hizo tradición empezar con cualquier lenguaje deprogramación 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 elprimer ejemplo de programación en C.Ejemplo: Hola mundo/* Inclusión de archivos */ #include <stdio.h> /* Función principal */ int main (int argc,char **argv) { /* Impresión porpantalla y salida del programa*/ printf(“Hola mundo\n”); return 0; }

Para poder editar y ejecutar este programa será necesario utilizar algún editor y luego un compilador, como se explicó enla sección 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 “Holamundo”. A continuación una explicación detallada sobre el proceso de compilación del programa, y luego un análisis líneapor línea del contenido de este ejemplo.

5.1 Pre-requisitos para la compilación de programas

Como ya se mencionó, será necesario tener instalado el compilador y un editor o entorno de desarrollo que permitanescribir el código a compilar. Para más información ver la sección Herramientas necesarias.El código a compilar debe guardarse con un nombre que represente al programa en cuestión y la extensión .c. En el casodel ejemplo del Hola mundo, el archivo puede llamarse hola.c.En las explicaciones a continuación, se asume que se cuenta con un compilador instalado y se ha editado un archivo hola.cque se quiere compilar. Si tu sistema operativo no aparece en esta lista busca en internet, ya que seguro que existe algúncompilador para ese sistema.

12

5.2. COMPILACIÓN DE PROGRAMAS SEGÚN LA PLATAFORMA 13

5.2 Compilación de programas según la plataforma

5.2.1 Windows

Para compilar un programa C en entornos Windows, debemos seguir una serie de pasos que varían según el compiladorde C que queramos utilizar. Antes que nada, sería bueno que se revises la documentación del compilador elegido paraconocer los comandos exactos.

Compilación del código fuente

Si se utiliza un entorno de desarrollo, será posible compilar directamente desde el entorno, mediante un botón o unacombinación de teclas.Si se ejecuta el compilador desde la línea de comandos, la línea será distinta según el compilador utilizado. A continuaciónalgunos ejemplos de ciertos comandos según 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 código fuente se genera un archivo llamado archivo objeto o programa objeto que es luegoenlazado 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 función,aunque es posible pedirles que no lo hagan mediante parámetros adicionales.Según el compilador y la configuración utilizada, se obtendrán dos o tres archivos:El archivo fuentehola.cEl archivo objetohola.objEl archivo ejecutablehola.exeEste último es el que nos interesa, puesto a que es el código ejecutable, el programa en sí. Al ejecutarlo se producirá lasalida deseada en una ventana de consola.

Salida por pantalla

Si ejecutamos en entorno Windows el programa directamente desde el navegador de archivos, o también desde algunosentornos de desarrollo, lo que sucederá será que apenas abierta la ventana de la consola, se mostrará la cadena esperaday luego de terminada la función, la consola se cerrará sin tener el tiempo suficiente de ver nuestro mensaje en pantalla.Para poder ver la salida por pantalla será necesario ejecutar el programa desde la línea de comandos, o modificar laconfiguración del entorno de desarrollo para que muestre la salida por pantalla al ejecutar el programa.Una posible solución es agregar una función adicional a nuestro “hola.c":

14 CAPÍTULO 5. PROGRAMACIÓN EN C/PRIMER PROGRAMA EN C

/* Inclusión de archivos */ #include <stdio.h> #include <stdlib.h> /* Función principal */ int main (int argc,char **argv){ /* Impresión por pantalla y salida del programa*/ printf(“Hola mundo\n”); system (“pause”); return 0; }

Las dos líneas agregadas permiten que utilicemos la biblioteca stdlib, que incluye la función system y que mediante estafunción 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.

5.2.2 GNU

Si bien existen otros compiladores, lo más usual y más sencillo para compilar un programa en GNU/Linux es el compiladorgcc, ya que es el que se incluye en todas las distribuciones.De cualquier forma, es posible realizar la compilación desde línea de comandos o desde el entorno gráfico.Para realizarla desde línea 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 escribirgcc hola.cSi no existen errores en el código, este comando nos creará un archivo ejecutable, que por omisión se llama “a.out”, y quepodemos ejecutar desde la línea de comandos de la siguiente forma:./a.out Hola mundoEs una buena idea especificar el nombre que el archivo ejecutable tendrá, pasando como parámetro al compilador la opción-o, de la siguiente forma:gcc hola.c -o holaCon lo cual, el nombre del archivo creado será hola. Este archivo no tiene extensión ya que es la forma usual de llamar alos archivos ejecutables en los entornos UNIX y GNU/Linux, sin embargo funcionaría de la misma forma si se llamarahola.exe.Para ejecutarlo, haremos los mismo que en el caso anterior:./hola Hola mundoExisten otros parámetros que podemos especificar al compilador en la línea de comandos, dependiendo del tipo de pro-grama, y en función de la complejidad del mismo. Por ejemplo, podemos agregar las siguientes opciones:gcc hola.c -o hola -Wall -pedanticLa opción -Wall nos mostrará todos los avisos que produzca el compilador, no solamente los errores. Los avisos nosindican dónde y/o porqué podría surgir algún error en nuestro programa.La opción -pedantic nos aporta más información sobre los errores y los avisos mostrados por GCC.

5.3 Diseccionando el “Hola Mundo”

A continuación veremos cuál es la estructura básica de un programa en C, para poder entender qué hace cada una de laslíneas de nuestro sencillo programa.Es probable que lo primero que salte a la vista sea la línea:printf(“Hola mundo\n”);

Esta es la línea que hace aparecer la cadena Hola Mundo en nuestra pantalla. Notamos que en C la sentencia para imprimiralgo por pantalla es printf() y, además, hay que colocar paréntesis alrededor de lo que queremos imprimir para utilizarla.Esto se debe a que en C, printf es una función, que imprime su argumento (la cadena Hola Mundo\n) en la pantalla. Se

5.3. DISECCIONANDO EL “HOLA MUNDO” 15

denomina invocar una función a la acción de utilizarla para que realice una acción.Podemos observar también que la cadena a imprimir termina con una extraña combinación: \n. La combinación \n norepresenta a dos caracteres independientes, sino que representa un único carácter no imprimible: el salto de línea. Sin elsalto de línea, el resultado al ejecutar el programa sería:$ ./a.out Hola Mundo$Es decir que no hay salto de línea entre la cadena impresa, y la siguiente entrada de la línea de órdenes, que no es lo queesperábamos.Lo último a notar en la línea es que termina con un punto y coma. En C, todas las sentencias terminan con un punto ycoma. Al principio puede parecer obvio dónde termina una sentencia, pero ya veremos más 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 instrucción return dentro de la función main, el resultadoes que se finaliza el programa, comunicándole al sistema operativo que el valor de retorno (un código numérico queel 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 grupode sentencias que se ejecutarán siempre de forma correlativa.¿Y qué es esa línea que precede (en realidad, que da nombre) al bloque?int main (int argc, char **argv)

Pues es la definición de una función, en este caso llamada main. En C (y en general en todos los lenguajes de programaciónestructurada) todo se hace a base de funciones, como main y printf.La función main es especial, porque es el la que se invoca cuando se ejecuta el programa. Todos los programas en Ccomienzan su ejecución al principio de la función main, y cuando ésta acaba, el programa también.Veamos con más detalle la definición de la función:int main (int argc, char **argv) { ... }

• El nombre de la función que viene a continuación, entre llaves, es main.

• Recibe dos argumentos: int argc y char **argv (que representan a la cantidad de argumentos ingresados al ejecutarel programa y a los valores de estos argumentos respectivamente).[1]

• La función devuelve como resultado un número entero, int (que es el 0 de la instrucción return).[2]

Finalmente, y un tanto aparte (está separada del resto por una línea en blanco), tenemos la línea:#include <stdio.h>

Que parece bastante distinta al resto del programa, y que, además, parece no tener sentido, puesto que ya hemos definidola función main que hace todo el trabajo.Efectivamente, esa línea no es parte del programa, aunque sea imprescindible. La línea es una instrucción del preprocesadorde C, como nos lo indica el símbolo #, y lo que hace es incluir en ese punto el contenido de otro fichero, antes (de ahíel nombre de preprocesador) de que comience la compilación. El fichero stdio.h es el que contiene la definición de lafunción printf(), que antes utilizamos pero que no escribimos, ya que forma parte de la biblioteca estándar de C.

16 CAPÍTULO 5. PROGRAMACIÓN EN C/PRIMER PROGRAMA EN C

5.4 Comentarios

Una vez escrito un código, tratar de entenderlo un año más tarde solo con leerlo puede ser frustrante: no hay manera desaber (si el programa es medianamente complicado) qué es cada variable, o qué hace cada bloque de código. Por esto, encualquier lenguaje de programación son importantes los comentarios.Un comentario en C es todo lo que se encuentre entre los símbolos /* y */. Hay que tener en cuenta que los comentariosno se pueden anidar: si dentro de un comentario hay un /*, seguirá siendo el primer */ el que finalice el comentario, no seesperará al segundo.Hay otro tipo de comentarios en C, procedentes del lenguaje C++, e incorporadas al estándar de C a partir de C99: //.Todo lo que esté después de estos signos, hasta el final de la línea, se considerará un comentario y el compilador no lotomará en cuenta.En el ejemplo presentado pueden verse tres líneas con comentarios, que documentan someramente las distintas funcio-nalidades del código. En los próximos capítulos podrán verse mejores usos de los comentarios dentro del código.También podría decirse que es una herramienta básica basada en compilador

[1] En un capítulo posterior podrá ver un ejemplo del uso de los parámetros que recibe main.

[2] Es importante señalar que el estándar dice que main deberá definirse como función que retorna un entero, o de lo contrario elresultado queda indefinido.

Capítulo 6

Programación en C/Tipos de datos

6.1 Historia

En el lenguaje C estandarizado como C89, existían cuatro tipos de datos básicos que son: los números enteros, los númerosreales, los caracteres, y los punteros. A partir del estándar C99 se agregan: los valores lógicos (verdadero o falso) y losnúmeros complejos.Estos tipos de datos son parte del lenguaje, y por ello se los considera primitivos. Más adelante veremos que con el usode estructuras y uniones es posible crear tipos compuestos de datos a partir de estos tipos primitivos.En este capítulo veremos los enteros, los reales y los caracteres. Más adelante se verán otros tipos de datos más complejos,como son los vectores, las cadenas de caracteres, y los punteros en general.

6.2 Enteros

Los enteros son el tipo de dato más primitivo en C. Se usan para representar números enteros. Pero siempre se puedenencontrar otras aplicaciones para los números enteros. En general se pueden usar para representar cualquier variablediscreta.Los tipos de datos enteros son: short, int, long y long long, cada uno representando un número entero de un tamaño ocapacidad determinado. Según el compilador y la plataforma de hardware, cada uno de estos tipos de dato puede ocupardesde 1 byte hasta 8 bytes en memoria (para más detalles busca en la referencia).Además, el lenguaje C hace la distinción de si el entero es con signo (signed) o sin signo (unsigned). En caso de que no sedeclare 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 números son representados en memoria mediante una cadena de bits. En el caso de los números con signo, elbit más significativo es el que se usa para representar el signo. La representación de los números negativos se realizamediante el complemento a dos, que es una técnica que permite operar con los números negativos de forma lógica.A modo de ejemplo, la representación en memoria del número −8 en una variable de 2 bytes, entera, con signo, sería lasiguiente:1111111111111000

17

18 CAPÍTULO 6. PROGRAMACIÓN EN C/TIPOS DE DATOS

6.3 Flotantes

Se denomina flotantes a los tipos de datos que representan a los números reales, ya que utilizan un sistema de representaciónbasado en la técnica de coma flotante, que permite operar con números reales de diversas magnitudes, mediante unnúmero decimal llamado mantisa y un exponente que indica el orden de magnitud.El tipo de dato flotante en lenguaje C sólo tiene dos tamaños: el float y el double, que son 4 bytes y 8 bytes respectivamente.Se los puede utilizar tanto para representar números decimales, como para representar números enteros con un orden demagnitud muy grande.La forma de declarar una variable flotante es escribiendo en una línea uno de los tipos de datos flotantes y a continuaciónel nombre de la variable y tal vez algún valor que se les quiera dar.Algunos ejemplos:float a; double a = 1e23; double a = 3.1416; float a = 4e-9; double a = −78;

Hay que tener en cuenta que aunque los valores flotantes son más convenientes para algunas aplicaciones, hay casos enlos que se prefieren los enteros. Esto se debe a que los números flotantes no necesariamente tienen soporte de hardware,en particular en las plataformas integradas. Una alternativa que se utiliza en estas situaciones es interpretar los enteroscomo decimales de forma que 150 se interprete como 1.5 y 2345 como 23.45.Para el caso de los flotantes de 4 bytes, se utiliza 1 bit para el signo, 8 bits para el exponente y 23 bits para el valor delnúmero. El procedimiento para almacenar un número en una variable flotante es el siguiente:

1. Se convierte a binario la parte entera.

2. Se coloca el signo en el bit más significativo de la misma manera que en los enteros (1 para el - y 0 para el +).

3. Se mueve la coma (en la representación binaria de la parte entera) hasta que esté a la derecha del primer uno y éstese descarta (el uno más significativo). El valor del exponente será el número de posiciones que se movió la coma.El exponente usa la representación de un entero con complemento a dos.

4. Se convierte en binario la parte decimal del número. Esto usando el peso de los bits. el bit decimal más significativovale 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 flotante representado en memoria.

6.4 Caracteres

Los caracteres se representan utilizando el tipo char, que tiene sólo 1 byte de tamaño. Este tipo se utiliza para representarlos 256 caracteres de la tabla de caracteres del sistema. El tipo char es también un tipo entero, ya que puede tomar valoresde 0 a 255. Por lo tanto también 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 número a una variable char, ya que se trata de un tipo entero. En muchassituaciones se utiliza el tipo char para almacenar números pequeños, ya que ocupa en memoria sólamente un byte.Es importante notar que con la llegada de la codificación 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 representación de todos los caracteres posibles. Por ello, elestándar C99 introduce el tipo wchar que puede ocupar más de 1 byte, según sea necesario para la codificación utilizadapor el sistema.

Capítulo 7

Programación en C/Expresiones

Vamos a tratar ahora de que el ordenador haga un poco de matemáticas para nosotros. Por ejemplo, que realice unas pocassumas, 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 multiplicación: %i\n”,resultado);resultado=5/2; printf(“Resultado de la división: %i\n”,resultado); return(0); }

Después 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 multiplicación: 10 Resultado dela división: 2 $Fijémonos en la línea del principio de la función main:int resultado;

Esta línea lo que hace es reservar un trozo de memoria, del tamaño de un int (normalmente 4 bytes), y asignarle el nombreresultado, para poder después referirnos a él. A partir de este momento, podemos considerar que en nuestro programaexiste una variable, que no tiene valor definido, pero a la que le podremos dar valor posteriormente.Las líneas con printf() ya las conocemos, pero hay algo en ellas que no habíamos visto antes. Esos %i y la parte deresultado son nuevas para nosotros.La función printf() no sólo sabe imprimir cadenas simples, como “Hola Mundo\n”, sino también imprimir variables. Paraello, en el lugar de la cadena donde queremos que aparezca el valor de la variable, introducimos lo que se llama una cadenade conversión de printf(). Estas cadenas siempre empiezan por %, siendo %i la cadena para imprimir un entero, comoes en nuestro caso int resultado. Finalmente, printf() debe saber qué valor escribir, por eso le damos otro argumento (uotros), usando , como separador, que contienen las variables cuyos valores queremos mostrar.En el resto del programa hemos visto cómo decirle al ordenador que ejecute una suma, una resta, una multiplicación yuna división entera, con los operadores +, -, * y /. Es de notar que el resultado de una operación como estas entre númerosenteros será siempre otro entero, como se puede observar en la división, en la que no obtenemos un bonito decimal, sinoun resultado entero. Además, hemos visto que el resultado de esas operaciones, que llamamos expresiones, puede serasignado a una variable:resultado = 7;

Esa asignación se hace mediante el operador de asignación: =. Con él, ya conocemos cinco operadores.Pero, como = también es un operador, ¿cómo sabe el ordenador qué operador debe ejecutar primero? Y si es un operador,¿por qué no da un resultado? ¿No crea una expresión?

19

20 CAPÍTULO 7. PROGRAMACIÓN EN C/EXPRESIONES

Empezando por las últimas preguntas, el operador de asignación sí crea una expresión, como los operadores de suma,resta, multiplicación y división, y esa expresión tiene un resultado, que es el valor que obtiene el lado izquierdo al realizarla operación. En cuanto a saber qué se debe ejecutar primero, el ordenador tiene una lista de precedencia, según la cualsiempre ejecuta primero las multiplicaciones y divisiones, de izquierda a derecha, a continuación las sumas y restas, deizquierda a derecha, y a continuación las asignaciones, de derecha a izquierda. Para más detalles acerca de la precedenciade los operadores ver el anexo de los operadores.En cuanto a los caracteres de punto y coma, notamos aquí que una expresión también puede ser una sentencia por símisma, sin necesidad de que haya ninguna función. De hecho, una sentencia puede no tener siquiera una expresión. Lalínea: ; es una sentencia perfectamente válida, la sentencia vacía, que sera útil en puntos donde el lenguaje requiera unasentencia pero no sea necesaria para nuestro programa.

Capítulo 8

Programación en C/Interacción con elusuario

En este capítulo veremos un poco más 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.

8.1 Imprimir por pantalla

Como hemos visto hasta ahora en los ejemplos, hay una función que utilizamos para sacar por pantalla textos arbitrarioso el resultado de alguna operación: la función printf().Si miramos (en la documentación) su definición, no nos aclarará demasiado:int printf (const char *TEMPLATE, ...)...claro que por algo tiene una sección completa de la documentación para ella sola.Veámosla poco a poco. Se trata de una función de la biblioteca estándar, lo que quiere decir que para utilizarla tenemosque incluir previamente su definición. La encontraremos en <stdio.h>.Lo primero que vemos en la definición es que es una función de tipo int, lo que quiere decir que devuelve un entero. Eseentero es el número de caracteres impresos en la pantalla, o un número negativo en caso de que se produzca algún error.Lo siguiente a notar es su primer argumento: const char *TEMPLATE. Se trata de una cadena de caracteres (char *) queno será modificada por la función (const), con lo que puede ser una constante de cadena o una variable que contenga unacadena, pero siempre debe acabar con el carácter nulo \0.Y luego vienen esos extraños 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 función sabrá qué hacer con ellos. Y eso esjustamente lo que hace tan fabulosa y útil a printf().Como hemos visto, el uso más simple de printf() es imprimir una cadena de texto simple y corriente. Como ya vimos:printf(“Hola Mundo\n”); /*imprime la cadena*/Y también hemos visto printf() también puede, con un argumento extra y una sintaxis especial, imprimir un número enteroque hayamos almacenado en una variable:char resultado;resultado=5+2;printf(“Resultado de la suma: %i\n”,resultado);

Aquí el punto de inserción es la secuencia %i. printf() siempre trata las secuencias que comiencen por % como secuencias

21

22 CAPÍTULO 8. PROGRAMACIÓN EN C/INTERACCIÓN CON EL USUARIO

de control que le dicen que debe imprimir algo que le proporcionamos en los otros argumentos. Así, podemos imprimirvarios enteros distintos en los sitios que queramos de la cadena, insertando varias de estas secuencias %i:int numero;numero=3;printf(“El doble de %i es %i y su cuadrado es %i\n”,numero,numero*2,numero*numero);

8.2 Lectura de datos del teclado

La entrada de datos se puede hacer de muchas maneras y entre ellas están desde el uso de dispositivos especiales hastanuestro simple teclado. La entrada de datos se refiere a cualquier forma de influencia del usuario sobre los datos que poseeel sistema.Con el fin de mostrar una forma de entrada simple para el aprendizaje vamos a hablar de la función scanf() que se encuentradefinida en <stdio.h> y que se usa para capturar diferentes tipos de datos.

8.2.1 La función scanf()

scanf() es una de las funciones más usadas por los principiantes para hacer entrada de datos en el lenguaje C. Tiene unasintaxis muy parecida a printf: recibe una cadena con el formato de los datos y luego se ponen las variables en orden quecorrespondan a ese tipo de datos. Es decir, así como en printf se pueden mostrar por pantalla los datos de varias variablesen 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); return0; }

Por ahora no nos interesan las demás sentencias, sólo la que contiene scanf. En el código se ve lo siguiente:scanf("%i”,&a);

Se observa que la funcion printf dejó en pantalla una petición para que el usuario introdujera un valor. Entonces, scanfrecibe como argumento una cadena del formato en que se van a capturar los datos y la lista de variables que van a recibirvalores y que deben coincidir con los del formato.En este caso la cadena de formato, "%i”, especifica que el usuario ingresará un número entero. Luego se designa a lavariable a para contener a ese número. El símbolo (&) que precede a a es para especificar que lo que se está enviandocomo argumento no es el valor que posee la variable a sino la dirección de memoria en que se encuentra. En este momentoeso no tiene mucha relevancia, sólo hay que recordar que se debe usar el símbolo & dentro del scanf. En el momento enque hablemos de punteros veremos más 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 código. La cadena de formato, "%i,%i” especifica que el usuario ingresaráun número, seguido de una coma, y luego otro número. El primer %i será capturado por la variable a y el segundo por b.

Capítulo 9

Programación en C/Instrucciones de control

Como ya se ha mencionado, C es un ejemplo de programación estructurada. En este tipo de programación, es necesariocontar con ciertas estructuras que permitan controlar el flujo del programa, es decir, tomar decisiones y repetir acciones.

9.1 La estructura condicional if ... else

En la gran mayoría de los programas será necesario tomar decisiones sobre qué acciones realizar. Esas decisiones puedendepender de los datos que introduzca el usuario, de si se ha producido algún error o de cualquier otra cosa.La estructura condicional if ... else es la que nos permite tomar ese tipo de decisiones. Traducida literalmente del inglés,se la podría llamar la estructura “si...si no”, es decir, “si se cumple la condición, haz esto, y si no, haz esto otro”.Un ejemplo sencillo sería el siguiente (no se trata de un programa completo, sino tan sólo una porción de código):if (edad < 18) printf(“No puedes acceder.\n”); else printf(“Bienvenido.\n”);

Este código 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 (condición) { sentencias_si_verdadero; } else { sentencias_si_falso; }

La condición, encerrada entre paréntesis, es una expresión que puede dar como resultado 0 (interpretado como falso)o cualquier valor distinto de 0 (interpretado como verdadero). Cuando la condición sea verdadera, se ejecutarán lassentencias dentro del primer bloque de código, cuando la condición sea falsa, se ejecutarán las sentencias del segundobloque de código. Las expresiones y valores de tipo verdadero/falso son también llamados valores lógicos o booleanos.La indentación o sangría (los espacios al comienzo de las líneas) no es necesaria, pero ayuda a la claridad del código. Lautilización de las llaves {...} es obligatoria cuando se quiere utilizar más de una instrucción por bloque, y optativa cuandosólo se quiere escribir una instrucción. Por claridad, sin embargo, es recomendable utilizarlas aún cuando sólo vaya ahaber una instrucción.El bloque del else es opcional. Si no se lo encuentra, sólo se realizará la acción correspondiente al bloque if.A continuación, un ejemplo con una función, que devuelve el mayor de dos números:int mayor(int a, int b) { if (b > a) { return b; }// No posee especificación de la parte “else”, ya que no es necesaria. returna; // Finaliza la función retornando el valor de “a”. }

23

24 CAPÍTULO 9. PROGRAMACIÓN EN C/INSTRUCCIONES DE CONTROL

9.1.1 Operadores de comparación

El símbolo > visto en el último ejemplo es un operador, que en este caso compara dos números enteros y devuelveverdadero si el primero es mayor, falso en caso contrario.A continuación un listado de los posibles operadores de comparación en C y su significado.Teniendo en cuenta que en C se toma como falso el valor 0, y como verdadero cualquier otro valor, una práctica comúnes expresar condiciones sin utilizar ningún operador:float division(int dividendo, int divisor) { if (divisor) { return dividendo / divisor; } else { printf (“No se puede dividirpor cero\n”); return 0; } }

En este caso, la expresión (divisor) es equivalente a (divisor != 0).

9.1.2 Operadores lógicos

Los operadores && (“y”), || (“o”) y ! (“no”) son operadores lógicos. Permiten operar con expresiones lógicas para generarexpresiones más complejas.Por ejemplo: determinar si un año es bisiesto o no. Los años son bisiestos si son divisibles por 4, pero no si son divisiblespor 100, a menos que también sean divisibles por 400.if ( (!(a % 4) && (a % 100)) || !(a % 400) ) { printf(“es un año bisiesto.\n”); } else { printf(“no es un año bisiesto.\n”); }

En realidad, teniendo en cuenta la prioridad de los operadores utilizados, podemos simplificar la expresión anterior delsiguiente modo:if ( !(a % 4) && (a % 100) || !(a % 400) ) { printf(“es un año bisiesto.\n”); } else { printf(“no es un año bisiesto.\n”); }

Además, como a cada rama del if le sigue una única instrucción, podemos expresar la expresión anterior del siguientemodo:if ( !(a % 4) && (a % 100) || !(a % 400) ) printf(“es un año bisiesto.\n”); else printf(“no es un año bisiesto.\n”);

En este caso, se utiliza el operador módulo (%), que obtiene el resto de la división entera de un número por otro. Cuandoun número es divisible por otro, el resto de su división entera será cero. Siendo que cero es equivalente a falso, y cualquiervalor distinto de cero es equivalente a verdadero, podemos usar el operador % para verificar si el número es múltiplo de4, de 100 o de 400.

9.1.3 Evaluación de cortocircuito

La evaluación en corto circuito es una característica del lenguaje C que se utiliza para optimizar la ejecución de programas.Consiste en que el programa puede verificar si una expresión es verdadera o falsa antes de haber evaluado toda condición.Por ejemplo, si se tiene una condición 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 siguientecondición, ya que el resultado será de cualquier modo verdadero.De la misma forma, si la condición fuera:if ((a > 2) && (b < 4)) { ... }

9.2. LA ESTRUCTURA CONDICIONAL ABIERTA Y CERRADA SWITCH ... CASE 25

En este caso, si no se cumple que a > 2, no se evaluará la siguiente condición, ya que el resultado será falso de todosmodos.Esta característica no tiene demasiada importancia al comenzar a programar, pero facilitará ciertas operaciones y opti-mizaciones en programas avanzados.

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

La estructura condicional switch ... case se utiliza cuando queremos evitarnos las llamadas escaleras de decisiones. Laestructura if nos puede proporcionar, únicamente, dos resultados, uno para verdadero y otro para falso. Una estructuraswitch ... case, por su parte, nos permite elegir entre muchas opciones. Ejemplo:#include <stdio.h> #include <stdlib.h> int main(void) { int dia; printf(“que número de día 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, Mierco-les”); 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, necesitaría cuatro de ellas, resultando un enorme bloque muy difícilde leer. En la mayoría de los casos, además, la sentencia switch proporciona una ganancia en velocidad del código, puespermite al compilador trabajar en base a que se trata de una decisión múltiple para una única variable, cosa que consentencias 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. Hayun caso especial, default, que se ejecuta si ningún otro corresponde, y que no es necesario poner. Es, en todo, equivalenteal bloque else de una sentencia if.Las sentencias break son muy importantes, ya que el comportamiento normal de un bloque switch es ejecutarlo todo desdela etiqueta case que corresponda hasta el final. Por ello, si no queremos que se nos ejecute más de un bloque, pondremossentencias break al final de cada bloque excepto el último.Es decir, las etiquetas case son puntos de entrada de la ejecución, y no implican que al acabarse el bloque case la ejecuciónsalte al final 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”.

9.3 El bucle while

El bucle while sirve para ejecutar código reiteradas veces.while (/*condicion*/) { /* Código */ }

La condición debe de ser una expresión lógica, similar a la de la sentencia if. Primero se evalúa la condición. Si el resultadoes verdadero, se ejecuta el bloque de código. Luego se vuelve a evaluar la condición, y en caso de dar verdadero se vuelvea ejecutar el bloque. El bucle se corta cuando la condición da falso.Ejemplo: imprimir los números de 0 a 99:int i = 0; while (i < 100) { printf("%d\n”, i); i = i + 1; }

26 CAPÍTULO 9. PROGRAMACIÓN EN C/INSTRUCCIONES DE CONTROL

9.4 El bucle for

El bucle for es un bucle muy flexible y a la vez muy potente ya que tiene varias formas interesantes de implementarlo, suforma más tradicional es la siguiente:for (/* inicialización */; /* condición */; /* incremento */) { /* código a ejecutar */ }

Inicialización: en esta parte se inicia la variable que controla el bucle y es la primera sentencia que ejecuta el bucle. Sólose ejecuta una vez ya que solo se necesita al principio del bucle.Expresión condicional: al igual que en el bucle while, esta expresión determina si el bucle continuará ejecutándose o no.Incremento: es una sentencia que ejecuta al final de cada iteración del bucle. Por lo general, se utiliza para incrementarla variable con que se inicio el ciclo. Luego de ejecutar el incremento, el bucle revisa nuevamente la condición, si esverdadera tiene lugar una ejecución más 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 más reducida como i++. Esta forma se utiliza más comúnmenteen el bucle for:int i; for (i=0; i < 100; i++) { printf("%d\n”, i); }

9.5 El bucle do...while

El bucle do...while es un bucle que, por lo menos, se ejecuta una vez. Do significa literalmente “hacer”, y while significa“mientras”Su forma es esta:do { /* CODIGO */ } while (/* Condición de ejecución 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 código del bucle asigna unvalor aleatorio a la variable definida anteriormente, y mientras esa variable no tenga el valor 25, el bucle sigue ejecutándose.

9.6 La sentencia goto

La sentencia goto sirve para indicar al programa que continue ejecutándose desde la línea de código indicada. Su sintaxises más o menos así:/* Código */ ETIQUETA: /* Código */ goto ETIQUETA; /* Código */

Así, cuando se ejecute la sentencia goto, el programa “saltará" y continuará su ejecución a partir de la etiqueta marcada.Como se puede observar se puede usar para crear un bucle, o para ir a una parte del código u otra si se combina con unasentencia 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 haganprogramas un poco “sucios” y confusos. Sólo en ocasiones muy excepcionales será recomendado el uso del goto al crear

9.6. LA SENTENCIA GOTO 27

iteraciones muy complejas. Sin embargo, con el pasar de los años este comando ya ha quedado prácticamente descartadodel lenguaje de los programadores.

Capítulo 10

Programación en C/Uso de funciones

10.1 Funciones

Como vimos anteriormente C tiene como bloque básico la función main() , también hemos visto la sentencia printf() quees otra función, y de igual forma hay muchas más funciones predefinidas, pero nosotros mismos también podemos definirnuestras propias funciones. De hecho, es fundamental hacerlo.Podemos definir una función cualquiera de la misma manera en que definimos la función main(). Basta con poner su tipo,su nombre, sus argumentos entre paréntesis y luego, entre llaves, su código:/* Inclusión de archivos */ #include <stdio.h> void holamundo(void) /* Función donde se ejecuta la lógica del programa*/ { printf(“Hola Mundo\n”); /* imprime la cadena */ return; /* sale de la función */ } int main(void) /* Función prin-cipal del programa */ { holamundo(); /* llamada a la función holamundo */ return 0; /* sale del programa con código 0(correcto) */ }

Este código es en todo equivalente al “Hola Mundo” original, sólo que nos muestra cómo escribir y cómo utilizar unafunción. Y además nos muestra un principio de buena programación: meter las sentencias que “hacen el trabajo” en otrasfunciones específicas para sacarlas de main(), dejando en ésta tan sólo un guión general de lo que hace el programa, no lasórdenes específicas. De esta manera se facilita la comprensión del programa, y por tanto el futuro trabajo de modificarlo.

10.1.1 La sentencia return

La sentencia return puede utilizarse dentro de una función para terminar su ejecución.En el ejemplo anterior, la función holamundo fue declarada con valor de retorno de tipo void (es decir, valor de retornonulo). En ese caso, la sentencia return no lleva ningún parámetro adicional, ya que la función no debe devolver ningúnvalor a la función que la llama.En cambio, la función main tiene un valor de retorno de tipo int, por lo que return debe ir seguido de un valor entero (0en el ejemplo). El valor 0 se utiliza para indicar que el programa ha llegado a un punto en el que todo se ha desarrolladocorrectamente y se utiliza cualquier otro valor para indicar que ha habido algún tipo de error.La instrucción return no es una función, se trata de una sentencia que lo que hace es retornar como valor de la función elvalor que se le proporciona como argumento.

10.1.2 Argumentos

Las funciones también pueden recibir argumentos o parámetros, para modificar su comportamiento. Por ejemplo, ladefinición de una función para sumar dos números sería de la siguiente manera:

28

10.1. FUNCIONES 29

#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; }

En este ejemplo, la función sumar recibe dos argumentos de tipo int y su valor de retorno también es de tipo int. Dentrode la función main, se llama a la función sumar poniendo entre paréntesis los valores deseados para sus argumentos, enorden, separados por una coma. Así, dentro de sumar el número 5 será asignado a la variable numero1 y el número 3 anumero2.

10.1.3 Declaración y definición

En el ejemplo anterior podemos notar que la función sumar figura en el código antes que main. ¿Qué pasaría si lasescribiéramos en distinto orden?#include <stdio.h> int main(void) { int suma = sumar(5, 3); /* ERROR, sumar no ha sido declarada aún */ printf(“Lasuma es: %d ", suma); return 0; } int sumar(int numero1, int numero2) { return numero1 + numero2; }

En este caso el programa es erróneo y no compila, ya que en la línea donde se llama a la función sumar, el compiladoraún no conoce ninguna función con ese nombre, y cuáles son sus argumentos y valor de retorno.Una posible solución es declarar el prototipo de la función al principio, para informar al compilador que existe, y luegodefinir el cuerpo de la misma en cualquier lugar del programa:#include <stdio.h> /* Declaración */ int sumar(int numero1, int numero2); int main(void) { int suma = sumar(5, 3);printf(“La suma es: %d ", suma); return 0; } /* Definición */ int sumar(int numero1, int numero2) { return numero1 +numero2; }

10.1.4 Paso de Parámetros

Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas de enviar los datos hacia unafunción por valor y por referencia, las cuales modifican en diferente forma el comportamiento de el programa.

Por Valor

El paso por valor envía una copia de los parámetros a la función por lo tanto los cambios que se hagan en ella no sontomados en cuenta dentro de la función main(). Ejemplo:/* * por_valor.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC” * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> void sumar_valor(int numero); /* prototi-po de la función */ int main(void) { int numero = 57; /* definimos numero con valor de 57*/ sumar_valor(numero); /*enviamos numero a la función */ printf(“Valor de numero dentro de main() es: %d\n”, numero); /* podemos notar queel valor de numero se modifica * sólo dentro de la función sumar_valor pero en la principal * número sigue valiendo 57*/ return 0; } void sumar_valor(int numero) { numero++; /* le sumamos 1 al numero */ /* el valor de número recibidose aumenta en 1 * y se modifica dentro de la función 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 envía la dirección de memoria de la variable, por lo tanto loscambios que haga la función si afectan el valor de la variable. Ejemplo:

30 CAPÍTULO 10. PROGRAMACIÓN EN C/USO DE FUNCIONES

/* * por_referencia.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programa-ción en C” * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> void sumar_referencia(int *nu-mero); /* prototipo de la función */ int main(void) { int numero = 57; /* definimos numero con valor de 57*/ su-mar_referencia(&numero); /* enviamos numero a la función */ printf("\nValor de numero dentro de main() es: %d ",numero); /* podemos notar que el valor de numero se modifica * y que ahora dentro de main() también se ha modificado* aunque la función no haya retornado ningún 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 modifica dentro de la función */printf("\nValor de numero dentro sumar_referencia() es: %d”, *numero); return; }

10.1.5 Variables Locales y Globales

Además de pasar valores a una función, también se pueden declarar tipos de datos dentro de las funciones, estos tipos dedatos declarados dentro de una función solo son accesibles dentro de esta misma función y se les conocen como variableslocales, así pues podemos definir los mismos nombres de variables en diferentes funciones, ya que estas variables solo sonaccesibles dentro de esas funciones. Ejemplo:/* * locales.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación en C” *bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> void funcion1() { int dato = 53; /* definimos datoen 53*/ char num1 = 'a'; /* num1 vale a */ /* imprimimos */ printf(“Funcion1, dato=%d, num1=%c\n”, dato, num1);return; } void funcion2() { int dato = 25; /* definimos 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 definida dentro de cada una de las funciones y son totalmente distinta una de otra y nose puede utilizar fuera de esta, así pues num2 no puede ser utilizada por la funcion1() y num1 tampoco puede ser utilizadapor funcion2().Existen pues variables que se definen fuera de la función principal main() y fuera de cualquier otra función creada pornosotros, estas variables se les conoce con el nombre de Variables Globales ya que se pueden utilizar dentro de main() ydentro de cualquier función creada por nosotros. Ejemplo:/* * global.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación en C”* bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> int variable_global = 99; /* inicializamosla variable global */ void funcion(); int main(void) { /* imprimimos el valor*/ printf(“main(), acceso a variable_global%d\n”, variable_global); /* llamamos a la función */ funcion(); return 0; } void funcion() { /* imprimimos el valor*/printf(“funcion(), acceso a variable_global %d\n”, variable_global); return; }

10.2 Funciones Recursivas

La recursividad (recursión) es la propiedad por la cual una función se llama a sí misma directa o indirectamente. Larecursión indirecta implica utilizar más de una función.Se puede considerar la recursividad como una alternativa a la iteración. La recursión permite especificar soluciones natu-rales, sencillas, que serían, en caso contrario, difíciles de resolver. Toda función recursiva debe contemplar un caso baseo condición de salida, para terminar, o la recursividad no podrá terminar nunca.Una función recursiva podría definirse así:funcion_recursiva( /* parámetros recibidos por la función */ ) { /* Código */ funcion_recursiva( ); /* llamada a la funciónmisma */ /* Código */ }

Uno de los ejemplos más representativos en la recursividad es el factorial de un numero ( n! ):

10.2. FUNCIONES RECURSIVAS 31

n! =

n∏k=1

k ∀n ∈ N

la definición de recursividad del factorial es:

n! =

{1 Si n = 0

n(n− 1)! Si n > 0∀n ∈ N.

En esta definición, n = 0, es nuestro caso base, que le da fin a la recursividad.Entonces nuestro programa que calcula el factorial es:/* *factorial.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC” * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> long factorial(int n) { if (n == 0) /* ca-so base */ return 1; /* como 0! = 1, se retorna 1*/ else return n * factorial (n - 1); /* llamada a esta misma función */} int main(void) { /* en este caso se llama a la función y se imprime directamente*/ printf("%ld ", factorial(5)); return 0; }

También existen otros tipos de funciones recursivas como lo es el producto de dos números. El producto de a × b, dondea y b son números enteros positivos seria:Solución iterativa:

a× b =a+ a+ · · ·+ a︸ ︷︷ ︸

b veces=

∑bi=1 a

Solución recursiva:

a× b =

{0 Si b = 0

a+ a× (b− 1) 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 multiplicación de dos números a, b se puede transformar en otro problema más pequeño multiplicara por (b-1), el caso base se produce cuando b = 0 y el producto es 0. Ejemplo:/* * producto.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación en C”* bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> int producto(int a, int b) { if (b == 0) /* casobase */ return 0; /* como b = 0, se retorna 0*/ else return a + producto (a, b - 1); /* llamada a esta misma función */ }int main(void) { /* en este caso se llama a la función y se imprime directamente*/ printf("%i ", producto( 7, 3)); return 0; }

10.2.1 Recursividad indirecta o recursión mutua

Esta se produce cuando una función llama a otra, que esta a su vez terminará llamando de nuevo a la primera función. Elsiguiente programa visualiza el alfabeto utilizando recursión indirecta o mutua:/* * elalfabeto.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC” * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> void funcionA(char c); /* se declara elprototipo de la función para que el llamado */ void funcionB(char c); /* a la misma en la función no sea implícita */ intmain(void) { funcionA('z'); /* llamado a funcionA */ return 0; } void funcionA(char c) { if (c > 'a') /* caso base mientrasc no sea menor que A */ funcionB(c); /* llamado a la funcionB */ printf("%c ", c); /* imprimimos el valor de c */ *lavariable es un parametro no utilizado para este proceso } void funcionB(char c) { funcionA(--c); /* llamado a la funcionAdecrementando el valor de 'z' */ }

32 CAPÍTULO 10. PROGRAMACIÓN EN C/USO DE FUNCIONES

10.2.2 Recursión versus Iteración

Tanto la iteración como la recursión se basan en estructura de control: la iteración utiliza una estructura repetitiva y larecursión una estructura de selección. La iteración utiliza explícitamente una estructura repetitiva mientras que la recursiónconsigue la repetición mediante llamadas repetitivas a funciones.La iteración termina si la condición del bucle no se cumple, mientras que la recursión termina cuando se reconoce uncaso base.La recursión puede presentar desventajas ante la iteración ya que se invoca repetidas veces al mecanismo de llamada defunciones y se necesita un tiempo mayor para realizar cada llamada.La razón por la cual se puede elegir u optar por usar recursividad es que existen muchos problemas complejos que poseennaturaleza recursiva y, en consecuencia, son mas fáciles de implementar.

Ejemplo Iterativo

/* * iterativo.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC” * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> long factorial(int numero); int main(intargc, 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( intnumero ) { long resultado = 1; int i = 0; /* declaracion de la función factorial iterativa */ for ( i = numero; i >= 1; i-- )resultado *= i; return resultado; }

Ejemplo Recursivo

/* * recursivo.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC” * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> long factorial(int numero); int main(intargc, 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; } /* función factorial recursiva */ long factorial( intnumero ) { 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 función factorial */ }

Capítulo 11

Programación en C/Vectores

Los vectores son una forma de almacenar datos que permiten contener una serie de valores del mismo tipo, cada uno delos valores contenidos tiene una posición asociada que se usará para accederlos. Está posición o índice será siempre unnúmero entero positivo.En C la cantidad de elementos que podrá contener un vector es fijo, y en principio se define 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 cadauno 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 irían entre 0 (parael 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 loselementos.O también 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 iguala la cantidad de valores que le estamos asignando al vector, en el caso de ser la misma cantidad no aporta información,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 través de su posición. Es decir:tipo_elemento elemento; ... elemento = nombre[2];

Asumiendo que tenemos el vector anterior definido estaríamos guardando valor_2 en elemento.Veamos algunos ejemplos:/* * Ejemplo : El producto escalar de dos vectores */ #include <stdio.h> double producto_escalar(double v1[], double

33

34 CAPÍTULO 11. PROGRAMACIÓN EN C/VECTORES

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 escalarentre dos vectores */ double producto_escalar(double v1[], double v2[], int d) { double resultado = 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 matemáticos y calcular el producto escalar entreellos. Una peculiaridad que se puede notar es que al recibir un arreglo en una función no se especifica el largo, volveremosa esto en un capítulo posterior.Otra función clásica es la búsqueda de un máximo o mínimo, 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]; } returnsuma/largo; }

Cuando una función recibe un vector por parámetro y cambia su contenido y el cambio es permanente (se ve aún fuerade la función). Esto puede parecer extraño después del énfasis que pusimos en resaltar que todos los parámetros de unafunción se reciben por valor, pero se aclarará en el siguiente capitulo.Mientras tanto usemos esto para definir una función que le aplique otra función que recibe por parámetro a cada elementodel 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 cua-drado(double valor) { return valor*valor; } ... cuadrados(elementos,num_elem); ...

De la misma forma que venimos usando vectores de tipos básicos, podemos tener vectores de vectores, estos se declarande la siguiente forma:int matriz[3][7]; int tabla[3][4]={ { 1, 2, 3, 4}, { 5, 6, 7, 8}, /* los espacios y saltos de líneas 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 leaplican las siguientes reglas:

1. La primera posición siempre será v[0]

2. La última posición es v[n-1]

3. En versiones previas a C99 n es una constante definida antes de la declaración de v[n]

Capítulo 12

Programación en C/Cadenas de caracteres

Las cadenas de caracteres (también llamadas cadenas o strings) son un tipo particular de vectores, son de hecho vectoresde char, con la particularidad que tienen una marca de fin (el caracter '\0'), además el lenguaje nos permite escribirlascomo texto dentro de comillas dobles. Veamos unos ejemplos de su declaración:char cadena_hola[]="Hola"; char otro_hola[]={'H','o','l','a','\0'}; // Igual al anterior char vector[]={'H','o','l','a'}; /* Unvector de 4 elementos, con los elementos 'H','o','l' y 'a' */ char espacio_cadena[1024]="Una cadena en C"; char cade-na_vacia[]="";

Cómo vimos anteriormente al declarar un vector se define la cantidad de elementos que puede contener, en el caso delas cadenas se debe tener en cuenta el espacio adicional necesario para el \0. Viendo el ejemplo, tanto cadena_hola yotro_hola tienen un largo 5 y cadena_vacia tiene un largo de 1.También vimos anteriormente que al usar vectores debemos tener en cuenta su largo, y así es que el largo o cantidad deelemento lo necesitamos en todas las funciones que definimos usando vectores y lo recibimos como un parámetro más enestas, en el caso de las cadenas al tener una marca de fin podemos prescindir del largo y procesar una cadenas hasta llegara la marca de fin.Por ejemplo, la siguiente función 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 porcomo 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++) { des-tino[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 simplificadas de funciones provistas por la biblioteca estándar de C a través del enca-bezado string.h. Nuestro largo_cadena() es similar al strlen() de la biblioteca estándar, y unir_cadenas() se asemeja alstrncat(). Si bien ver estas versiones nos sirven para entender las cadenas en C, en general será preferible usar las funcio-nes provistas por la biblioteca estándar, ya que podemos estar seguros que van a estar programadas de la mejor maneraposible.Entre las funcione que provee la biblioteca estándar de C, las más importantes son:largo = strlen(cadena) // Para obtener el largo de una cadena strcpy(destino, origen) // Copia el contenido de origen en

35

36 CAPÍTULO 12. PROGRAMACIÓN EN C/CADENAS DE CARACTERES

destino // destino debe ser lo suficientemente grande strcat(destino, origen) // Agrega el contenido de origen al final dedestino // destino debe ser lo suficientemente grande resultado = strcmp(cadena1, cadena2) // Compara dos cadenas //devuelve un valor menor, igual o mayor que 0 según si cadena1 es menor, // igual o mayor que cadena2, respectivamente.posicion = strchr(cadena, caracter) // Devuelve la posición en memoria de la primer // aparición de caracter dentro decadena posicion = strstr(cadena,subcadena) // Devuelve la posición en memoria de la primer // aparición de subcadenadentro 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; }

Capítulo 13

Programación en C/Manejo de archivos

Así como hemos revisado la salida y entrada por pantalla y teclado respectivamente, veremos ahora la entrada y/o salidade datos utilizando ficheros, lo cual será imprescindible para un gran número de aplicaciones que deseemos desarrollar.

13.1 Ficheros

El estándar de C contiene varias funciones para la edición de ficheros, estas están definidas en la cabecera stdio.h y por logeneral empiezan con la letra f, haciendo referencia a file. Adicionalmente se agrega un tipo FILE, el cual se usará comoapuntador a la información del fichero. La secuencia que usaremos para realizar operaciones será la siguiente:

• Crear un apuntador del tipo FILE *

• Abrir el archivo utilizando la función fopen y asignándole el resultado de la llamada a nuestro apuntador.

• Hacer las diversas operaciones (lectura, escritura, etc).

• Cerrar el archivo utilizando la función fclose.

13.1.1 fopen

Esta función sirve para abrir y crear ficheros en disco.El prototipo correspondiente de fopen es:FILE * fopen (const char *filename, const char *opentype);

Los parámetros de entrada de fopen son:filename: una cadena que contiene un nombre de fichero válido. opentype: especifica el tipo de fichero que se abrirá o secreará.Una lista de parámetros opentype para la función fopen son:

• “r” : abrir un archivo para lectura, el fichero 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 final del contenido, si no existe se crea.

• “rt” : abrir un archivo para lectura y escritura, el fichero debe existir.

37

38 CAPÍTULO 13. PROGRAMACIÓN EN C/MANEJO DE ARCHIVOS

• “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 actualización (lectura y escritura).

• “rb” : Abre un archivo en modo binario para lectura.

Adicionalmente hay tipos utilizando “b” (binary) los cuales no serán mostrados por ahora y que solo se usan en los sistemasoperativos que no pertenecen a la familia de unix.

13.1.2 fclose

Esta función sirve para poder cerrar un fichero que se ha abierto.El prototipo correspondiente de fclose es:int fclose (FILE *stream);

Un valor de retorno cero indica que el fichero ha sido correctamente cerrado, si ha habido algún error, el valor de retornoes la constante EOF.Un ejemplo pequeño para abrir y cerrar el archivo llamado fichero.in en modo lectura:#include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { FILE *fp; fp = fopen ( “fichero.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 archivocon éxito devuelve la referencia al archivo (FILE *), de lo contrario devuelve NULL y en este caso se debera revisar ladireccion del archivo o los permisos del mismo. En estos ejemplos solo vamos a dar una salida con un retorno de 1 quesirve para señalar que el programa termino por un error.

13.1.3 feof

Esta función sirve para determinar si el cursor dentro del archivo encontró el final (end of file). Existe otra forma deverificar el final del archivo que es comparar el caracter que trae fgetc del archivo con el macro EOF declarado dentrode stdio.h, pero este método no ofrece la misma seguridad (en especial al tratar con los archivos “binarios”). La funciónfeof siempre devolverá cero (Falso) si no es encontrado EOF en el archivo, de lo contrario regresará un valor distinto decero (Verdadero).El prototipo correspondiente de feof es:int feof(FILE *fichero);

13.1.4 rewind

Literalmente significa “rebobinar”, sitúa el cursor de lectura/escritura al principio del archivo.El prototipo correspondiente de rewind es:void rewind(FILE *fichero);

13.2. LECTURA 39

13.2 Lectura

Un archivo generalmente debe verse como un string (una cadena de caracteres) que esta guardado en el disco duro. Paratrabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podríamos usar para leer unarchivo son:

• char fgetc(FILE *archivo)

• char *fgets(char *buffer, int tamano, FILE *archivo)

• size_t fread(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);

• int fscanf(FILE *fichero, 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 parámetros,nos podemos dar cuenta de que es muy diferente, por eso la trataremos aparte junto al fwrite que es su contraparte paraescritura.

13.2.1 fgetc

Esta función lee un caracter a la vez del archivo que esta siendo señalado con el puntero *archivo. En caso de que lalectura sea exitosa devuelve el caracter leído y en caso de que no lo sea o de encontrar el final del archivo devuelve EOF.El prototipo correspondiente de fgetc es:char fgetc(FILE *archivo);

Esta función se usa generalmente para recorrer archivos de texto. A manera de ejemplo vamos a suponer que tenemosun archivo de texto llamado “prueba.txt” en el mismo directorio en que se encuentra el fuente de nuestro programa. Unpequeño 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 pruebaes \n\n”); while (feof(archivo) == 0) { caracter = fgetc(archivo); printf("%c”,caracter); } } fclose(archivo); return 0; }

13.2.2 fgets

Esta función está diseñada para leer cadenas de caracteres. Leerá hasta n-1 caracteres o hasta que lea un cambio de línea'\n' o un final de archivo EOF. En este último caso, el carácter de cambio de línea '\n' también es leído.El prototipo correspondiente de fgets es:char *fgets(char *buffer, int tamaño, FILE *archivo);

El primer parámetro buffer lo hemos llamado así porque es un puntero a un espacio de memoria del tipo char (podríamosusar un arreglo de char). El segundo parámetro es tamaño que es el limite en cantidad de caracteres a leer para la funcionfgets. 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 función fgetsse comporta de la siguiente manera, leerá del archivo apuntado por archivo los caracteres que encuentre y a ponerlos en

40 CAPÍTULO 13. PROGRAMACIÓN EN C/MANEJO DE ARCHIVOS

buffer hasta que lea un caracter menos que la cantidad de caracteres especificada en tamaño o hasta que encuentre el finalde una linea (\n) o hasta que encuentre el final del archivo (EOF). En este ejemplo no vamos a profundizar mas que paradecir que caracteres es un buffer, los pormenores seran explicados en la sección de manejo dinámico de memoria.El beneficio de esta función es que se puede obtener una linea completa a la vez. Y resulta muy útil para algunos finescomo la construcción de un parser de algún tipo de archivo de texto.

13.2.3 fread

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );Esta función lee un bloque de una “stream” de datos. Efectúa la lectura de un arreglo de elementos “count”, cada unode los cuales tiene un tamaño definido por “size”. Luego los guarda en el bloque de memoria especificado por “ptr”. Elindicador de posición de la cadena de caracteres avanza hasta leer la totalidad de bytes. Si esto es exitoso la cantidad debytes leídos es (size*count).PARAMETROS:ptr : Puntero a un bloque de memoria con un tamaño mínimo de (size*count) bytes.size : Tamaño en bytes de cada elemento (de los que voy a leer).count : Número de elementos, los cuales tienen un tamaño “size”.stream: Puntero a objetos FILE, que especifica la cadena de entrada.

13.2.4 fscanf

La función fscanf funciona igual que scanf en cuanto a parámetros, pero la entrada se toma de un fichero en lugar delteclado.El prototipo correspondiente de fscanf es:int fscanf(FILE *fichero, const char *formato, argumento, ...);

Podemos ver un ejemplo de su uso, abrimos el documento “fichero.txt” en modo lectura y leyendo dentro de el.#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buffer[100]; fp = fopen ( “fichero.txt”, “r” ); fs-canf(fp, "%s” ,buffer); printf("%s”,buffer); fclose ( fp ); return 0; }

13.3 Escritura

Así como podemos leer datos desde un fichero, también se pueden crear y escribir ficheros con la información quedeseamos almacenar, Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones quepodríamos usar para escribir dentro de un archivo son:

• int fputc(int caracter, FILE *archivo)

• int fputs(const char *buffer, FILE *archivo)

• size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);

• int fprintf(FILE *archivo, const char *formato, argumento, ...);

13.3. ESCRITURA 41

13.3.1 fputc

Esta función escribe un carácter a la vez del archivo que esta siendo señalado con el puntero *archivo. El valor de retornoes el carácter escrito, si la operación fue completada con éxito, en caso contrario será EOF.El prototipo correspondiente de fputc es:int fputc(int carácter, FILE *archivo);

Mostramos un ejemplo del uso de fputc en un “fichero.txt”, se escribira dentro del fichero hasta que presionemos la teclaenter.#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char caracter; fp = fopen ( “fichero.txt”, “r+" );printf("\nIntrouce un texto al fichero: "); while((caracter = getchar()) != '\n') { fprintf("%c”, fputc(caracter, fp)); } fclose( fp ); return 0; }

13.3.2 fputs

La función fputs escribe una cadena en un fichero. No se añade el carácter de retorno de línea ni el carácter nulo final.El valor de retorno es un número no negativo o EOF en caso de error. Los parámetros de entrada son la cadena a escribiry un puntero a la estructura FILE del fichero donde se realizará la escritura.El prototipo correspondiente de fputs es:int fputs(const char *buffer, 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 fiche-ro.\n"; fp = fopen ( “fichero.txt”, “r+" ); fputs( cadena, fp ); fclose ( fp ); return 0; }

13.3.3 fwrite

Esta función está pensada para trabajar con registros de longitud constante y forma pareja con fread. Es capaz de escribirhacia un fichero uno o varios registros de la misma longitud almacenados a partir de una dirección de memoria determi-nada. El valor de retorno es el número de registros escritos, no el número de bytes. Los parámetros son: un puntero a lazona de memoria de donde se obtendrán los datos a escribir, el tamaño de cada registro, el número de registros a escribiry un puntero a la estructura FILE del fichero 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:

13.3.4 fprintf

La función fprintf funciona igual que printf en cuanto a parámetros, pero la salida se dirige a un archivo en lugar de a lapantalla.El prototipo correspondiente de fprintf es:int fprintf(FILE *archivo, const char *formato, argumento, ...);

42 CAPÍTULO 13. PROGRAMACIÓN EN C/MANEJO DE ARCHIVOS

Podemos ver un ejemplo de su uso, abrimos el documento “fichero.txt” en modo lectura/escritura y escribimos dentro deel.#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buffer[100] = “Esto es un texto dentro del fichero.";fp = fopen ( “fichero.txt”, “r+" ); fprintf(fp, buffer); fprintf(fp, "%s”, "\nEsto es otro texto dentro del fichero.”); fclose (fp ); return 0; }

Capítulo 14

Programación en C/Estructuras y Uniones

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

14.1 Estructuras

Una estructura contiene varios datos. La forma de definir una estructura es haciendo uso de la palabra clave struct. Aquihay 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 más tarde declararla usando:struct mystruct variable;

También es una práctica muy común asignarle un alias o sinónimo al nombre de la estructura, para evitar el tener queponer “struct mystruct” cada vez. C nos permite la posibilidad de hacer esto usando la palabra clave typedef, lo que creaun 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 mayúscula en la primera letra de un sinónimo de tipo. Detodos modos lo importante es darle algún identificador para poder hacer referencia a la estructura: podriamos tener unaestructura de datos recursiva de algún tipo.Ejemplo de una estructura :/* * estructura.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC (fundamentos)" * bajo licencia FDL, adaptado del Dominio Público * * Nombre Miembro Tipo * Titulo char[30]* Artista char[25] * Precio float * Total Canciones int */ #include <stdio.h> #include <string.h> /* definimos una es-tructura para cds */ struct cd { char titulo[30]; char artista[25]; float precio; int canciones; } Cd1 = { /* inicializamos laestructura Cd1 creaa con sus valores * usando las definiciones iniciales*/ “Canciones Bebe”, /* titulo */ “Pinocho”, /*

43

44 CAPÍTULO 14. PROGRAMACIÓN EN C/ESTRUCTURAS Y UNIONES

artista */ 12.50, /* precio */ 16 /* total canciones */ }; int main(void) { struct cd Cd2; /* definimos una nueva estruc-tura llamado cd2 */ /* asignamos valores a los tipos de datos del cd2 */ strcpy(Cd2.titulo, “New Age”); /* la forma deinsertar valores a un * tipo char en una estructura es usando strcpy * de la libreria string.h */ strcpy(Cd2.artista, “OldMan”); Cd2.precio = 15.00; Cd2.canciones = 12; /* la forma de acceder a los valores de una estructura */ /* es usandoel ".” despues de la definicion 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 unica-mente * 2 decimales despues del punto*/ return 0; }

14.1.1 Estructuras Anidadas

Una estructura puede estar dentro de otra estructura a esto se le conoce como anidamiento o estructuras anidadas. Ya quese trabajan con datos en estructuras si definimos un tipo de dato en una estructura y necesitamos definir ese dato dentrode otra estructura solamente se llama el dato de la estructura anterior.Definamos una estructura en nuestro programa:struct empleado /* creamos una estructura llamado empleado*/ { char nombre_empleado[25]; char direccion[25]; charciudad[20]; char provincia[20]; long int codigo_postal; double salario; }; /* las estructuras necesitan punto y coma (;) alfinal */

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 final */

Podemos ver que tenemos datos muy similares en nuestras estructuras, asi que podemos crear una sola estructura llamadainfopersona con estos datos idénticos:struct infopersona /* creamos la estructura que contiene datos parecidos */ { char direccion[25]; char ciudad[20]; charprovincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al final */

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 estructurasnecesitan punto y coma (;) al final */ <source lang=c> struct cliente /* se crea nuevamente la estructura */ { charnombre_cliente[25]; /* creamos direcc_cliente con “struct” del tipo “estructura infopersona” */ struct infopersona di-recc_cliente; double saldo; }; /* las estructuras necesitan punto y coma (;) al final */

Y acá el ejemplo completo con estructuras anidadas:/* * estructura2.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación enC (fundamentos)" * bajo licencia FDL, adaptado del Dominio Público * * Nombre Miembro Tipo * * Titulo char[30]* Artista char[25] * Precio float * Total Canciones int */ #include <stdio.h> #include <string.h> /* creamos nuestraestructura con datos similares */ struct infopersona { char direccion[25]; char ciudad[20]; char provincia[20]; long intcodigo_postal; }; /* las estructuras necesitan punto y coma (;) al final */ /* creamos nuestra estructura empleado */ structempleado { char nombre_empleado[25]; /* agregamos la estructura infopersona * con nombre direcc_empleado */ structinfopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al final */ /* creamos nues-tra estructura cliente */ struct cliente { char nombre_cliente[25]; /* agregamos la estructura infopersona * con nombredirecc_cliente */ struct infopersona direcc_cliente; double saldo; }; /* las estructuras necesitan punto y coma (;) al final */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”); /* note-

14.2. UNIONES 45

se que se agrega direcc_cliente haciendo referencia * a la estructura infopersona por el dato direccion */ /* imprimimos losdatos */ printf("\n Cliente: "); printf("\n Nombre: %s”, MiCliente.nombre_cliente); /* notese la forma de hacer referenciaal dato */ printf("\n Direccion: %s”, MiCliente.direcc_cliente.direccion); /* creamos un nuevo empleado */ struct em-pleado MiEmpleado; /*inicializamos un par de datos de MiEmplado */ strcpy(MiEmpleado.nombre_empleado,"MiguelAngel”); strcpy(MiEmpleado.direcc_empleado.ciudad,"Madrid”); /* para hacer referencia a ciudad de la estructura info-persona * utilizamos direcc_empleado que es una estructura anidada */ /* imprimimos los datos */ printf("\n”); printf("\nEmpleado: "); 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; }

14.2 Uniones

La definicion de “union” es similar a la de “estructura”, La diferencia entre las dos es que en una estructura, los miembrosocupan diferentes areas de la memoria, pero en una union, los miembros ocupan la misma area de memoria. Entoncescomo ejemplo:union { int i; double d; } u;

El programador puede acceder a través de “u.i” o de “u.d”, pero no de ambos al mismo tiempo. Como “u.i” y “u.d” ocupanla misma área de memoria, modificar uno modifica el valor del otro, algunas veces de maneras impredecibles.El tamaño de una union es el de su miembro de mayor tamaño.Ejemplo de una unión:/* * uniones.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación en C(fundamentos)" * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> #include <string.h> /*Creamosuna 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; /*Nótese que la estructura y la uniontienen los mismos tipos de datos*/ int main(int argc, char** argv) { /*Inicializamos*/ strcpy(palabra.mensajes, “PrimerMensaje”); /*Inicializamos*/ strcpy(palabra.ayudas, “Una Ayuda”); printf("\nFrases en Union: "); /*Imprimimos mensa-jes 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 laestructura, esto se debe a que las uniones usan el * mismo espacio de memoria para todos los elementos * de la union,siendo del tamaño de su miembro de * mayor tamaño, en este caso 50 bytes. * Entonces los tres miembros creadosdentro 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); /*Imprimimosayudas de union*/ printf("\n2- %s”, Sistema.ayudas); /* En la estructura comparte, se reservan 150 bytes * de memoriapara los tres miembros, en este caso * cada uno es independiente en memoria, asi pues se * puede inicializar cada uno ousar como un campo * independiente. */ return 0; }

14.3 Enumeraciones

Una enumeracion (enum) es un tipo definido con constante de tipo entero. En la declaracion de un tipo enum creamosuna lista de tipo de datos que se asocian con las constantes enteras 0, 1, 2, 3, 4, 5...su forma de definirlas 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 paracada una de las expresiones siguientes.

46 CAPÍTULO 14. PROGRAMACIÓN EN C/ESTRUCTURAS Y UNIONES

Al declarar la enum se puede asociar a los tipos de datos a valores constantes en vez de la asociacion que por defecto serealiza (0, 1, 2, …), se utiliza entonces este formato:enum Nombre { enumerador1 = valor_constante1, enumerador2 = valor_constante2, ... enumeradorn = valor_constanten,};

Un ejemplo de una enum:enum Boolean { FALSE, TRUE };

Se definen dos constantes para las constantes true y false con valores iguales a 0 para False y 1 para True.Ejemplo:/* * Enum.c * * Julio César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación en C(fundamentos)" * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> enum Boolean { FALSE,TRUE }; /* Se define un enum para emular las constantes * True y False con valores de 0 y 1. * Notese que las enum nonecesitan ; al final * de cada tipo de dato. */ /* Definimos una funcion del tipo enum llamada numero*/ enum Booleannumero(char c); int main(int argc, char** argv) { char caracter; int Numeros = 0; printf("\nIntroduce un texto. Para ter-minar: 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 César Brizuela <[email protected]> 2009 * * para el wikilibro “Programación en C(fundamentos)" * bajo licencia FDL, adaptado del Dominio Público */ #include <stdio.h> enum DiasSemanas { Domin-go = 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 };

Capítulo 15

Programación en C/Punteros

47

Capítulo 16

Concepto

Un puntero es una variable que contiene la dirección de memoria de un dato o de otra variable que contiene al dato.Quiere esto decir, que el puntero apunta al espacio físico donde está el dato o la variable. Un puntero puede apuntar a unobjeto de cualquier tipo, como por ejemplo, a una estructura o una función. Los punteros se pueden utilizar para referenciay manipular estructuras de datos, para referenciar bloques de memoria asignados dinamicamente y para proveer el pasode 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 odevuelve un valor que es un puntero. Por Ej. A scanf se le pasa la dirección de memoria del dato a leer (esto es unpuntero)...char a; scanf ("%c”,&a);

48

Capítulo 17

Declarando punteros

Ya se dijo que un puntero es una variable que guarda la dirección 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 nombrede 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 dirección de memoriade un dato int, se necesita un puntero de tipo int.

49

Capítulo 18

Explicación

Veamos el siguiente codigo:#include <stdio.h> int main() { int a=0; //Declaración de variable entera de tipo entero int *puntero; //Declaración devariable puntero de tipo entero puntero = &a; //Asignación de la dirección 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 acordarasque decíamos que las cadenas de caracteres (%s) no usaban este operador, esto es por que en una cadena de caracteres esun arreglo de caracteres, por lo que el primer carácter es la dirección 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 quecontiene a la variable a. De esta forma “a” y "*puntero” muestran el mismo dato, pero esto no quiere decir que sea lomismo, uno es un entero el otro un puntero.La impresión con %p, es para poder imprimir la dirección de memoria en valor hexadecimal (0x...), también podemosimprimir: ...%p”,&a) y funciona de la misma forma, y es lógico que tanto a, como puntero tienen la misma dirección dememoria.Diferentes direcciones?Tal vez notaste que en cada ejecución la dirección de memoria cambia, esto es por que es el sistema operativo es quienesta 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 dirección de memoria a un puntero directamente, es decir yo no puedohacer lo siguiente.int *puntero=0xbfc5b1c8;

Esto no puedo ni debo hacerlo ya que yo no se que esta haciendo esta dirección de memoria, si el sistema la tiene o nodisponible, etc... Pero si puedo hacer esto:int *puntero=NULL;

NULL, es el espacio en memoria con dirección 0, esto quiere decir que existe, lo que significa que le asignamos unadireccion valida al puntero, pero el valor que tiene NULL no se nos permite modificarlo, ya que pertenece al sistema.

50

Capítulo 19

Operadores

Ya anteriormente te poníamos algunos ejemplos de como asignar la dirección de memoria a un puntero y de como accederal valor de este.Operador de Dirección (&): Este nos permite acceder a la dirección de memoria de una variable.Operador de Indirección (*): Además de que nos permite declarar un tipo de dato puntero, también nos permite ver elVALOR que está en la dirección asignada.Incrementos (++) y Decrementos (--): Te darás cuenta que puedes usar un puntero como si de un array se tratase, espor 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);

51

Capítulo 20

Operaciones Aritméticas

Un puntero nos permite sumar o restar números enteros, pero su funcionamiento ahora es de posiciones, es decir nospermitirá movernos a la siguiente dirección de memoria.A un puntero no se le puede realizar multiplicaciones, divisiones, sumas o restas con otro puntero o con un valor de tipocoma flotante (float, double...). Esto es por que un puntero no es un valor es una dirección.En un puntero se pueden realizar varias operaciones de tipo enteras, pero en dependencia de como se usen sus resultadospueden ser muy diferentes, a continuación les muestro algunas de estas operaciones://Definamos 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 valorseria igual a 11. b = *(pa+1); //Esto primero pasa a la siguiente direccion de memoria y luego lo referencia //El resultadoes: 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 sele asigna 0 //Esto es igual que decir: x[10] = 0 *pb += 2; //El valor del puntero se incrementa en dos unidades, es decirx[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] //recuerda, que -- es post-incremento, primero asignara y luego restara.

52

Capítulo 21

Punteros Constantes

Es posible que hayas pensado como declarar un puntero como una constante, tal vez pensaste en un #define, o en unatributo 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 esconstante. 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, mejorhubiésemos 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 varia-ble. p=&b; //ERROR: p es constante.

53

Capítulo 22

Punteros Genericos

Un puntero a cualquier tipo de dato puede convertirse a un puntero del tipo void *. Por esto un puntero a void *, recibeel nombre de puntero generico.En C, se permite la conversión implícita de punteros, pero en C++ esto no es posible, asi que por compatibilidad y buenapractica 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 conpunteros que referencia a un tipo de dato diferente, como lo es un puntero a char, con un puntero a int.

54

Capítulo 23

Punteros y Matrices

Anteriormente decíamos que una matriz es una secuencia de espacios en memoria, que nos permitían alojar datos en cadauno y un puntero es una variable que guarda la dirección de memoria, también decíamos como recorre las direcciones dememoria con los operadores ++ y -−.Aquí veremos como puede usarse un puntero como si de una matriz se tratase, luego de que veas que es técnicamente lomismo, te preguntaras por que usar punteros, pero estos son muy necesarios y únicos que nos permiten realizar cosas quecon un array normal, no se puede, como asignarle memoria dinámica, 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 dirección 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; }

Habrás notado que he usado *(puntero+i), así como explica la sección de operaciones aritméticas, pero también podemosacceder de otras maneras como lo son:array[i] => Accede como un array normal

• (array+i) => También accede como un array normal.

puntero[i] => Accedemos al valor de puntero sub i

• (puntero+i) => Accedemos al valor de puntero + i.

55

Capítulo 24

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 logicapodemos 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 modificarla luego de definir sus valores. Como porejemplo no podemos remplazar un carácter, o leer un nuevo valor.gets(nombre); //ERROR en ejecución

Para poder modificar el valor de este puntero, este tendría que apuntar a una dirección que no sea una constante, comoun array.char nombre[]="Gustavo A. Chavarria"; //declaramos un array de caracteres char *puntero=nombre;//Asignamos al pun-tero 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 porel array original, mas adelante veremos como solucionar esto con memoria dinámica.

56

Capítulo 25

Matrices de Punteros

Es muy probable que ya te hayas dicho: Si un puntero es una variable, ¿Puedo tener un array de punteros? La respuestaseria NO Exactamente.Como ya habiamos explicado antes, un puntero trabaja de la misma forma que un array, tanto que podemos decir que unpuntero es un array, entonces si definimos un array de un array, nos daría como resultados un array bidimensional.En la practica es mucho mas común utilizar un array de punteros que un array bidimensional.Hay que recordar que siguen siendo punteros, es decir solo apuntan a una dirección de memoria, por ende hay queasignarles la dirección de memoria a cada uno de los elementos del puntero. Sin embargo podemos asignar en un solociclo todas las filas.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 filas al puntero. //Pueden imprimir tambien en un ciclo //Tambien pueden acceder me-diante un ciclo anidado a la variables del puntero[i][j] }

57

Capítulo 26

Punteros a Punteros

Es una variable que contiene la dirección de memoria de un puntero, el cual a su vez contiene la dirección de memoriade un tipo de dato. Recuerden que un puntero sigue siendo un espacio en memoria, pero en vez de almacenar un valoralmacena una dirección.Si decimos que:int a=0; //Supongamos que es una variable cuya direccion es 0x1601 int *puntero1=&a; //El puntero tiene guardada ladireccion de a, //pero este tiene como direccion 0x1890 int **puntero2=&puntero1; //**puntero 2 guarda la direccion0x1890

Ahora se entiende mejor. Al uso de punteros se le llama variables con niveles de indireccion, ya que no apuntan directa-mente a un valor, sino que apuntan a alguien que lo tiene. Basándonos en esto podemos decir que *puntero1 es un punterocon 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 fácil de implementar.

58

Capítulo 27

Matrices de punteros a cadenas decaracteres

No hablaremos sobre este tema, debido a que es prácticamente una matriz de caracteres que se usa mediante punteros, yesto es técnicamente lo mismo que hemos estado viendo anteriormente.Tambien podemos usar punteros a punteros y guardar multiples cadenas.

59

Capítulo 28

Ejemplos

Tenga en cuenta el siguiente bloque de código que declara 2 punteros/*1*/ struct MyStruct { /*2*/ int m_aNumber; /*3*/ float num2; /*4*/ }; /*5*/ /*6*/ int * pJ2; /*7*/ struct MyStruct *pAnItem;

Las primeras 4 líneas definen la estructura. La linea 6 declara una variable que apuntará a un entero, y la línea 7 declarauna variable que apunta a algo de la estructura MyStruct. Entonces declarar un puntero es algo que apunta a algo de algúntipo, más que contener el tipo. El asterisco (*) se coloca antes del nombre de la variable.En las siguientes líneas de código, var1 es un puntero a un entero largo (long) mientras var2 es un entero largo (long) yno un puntero a un entero largo. En la segunda línea se declara p3 como un puntero a un puntero de un entero.long * var1, var2; int ** p3;

Los punteros se usan habitualmente como parámetros de funciones. El siguiente código muestra como declarar una funciónque usa un puntero como argumento. Teniendo en cuenta que C pasa todos los argumentos por valor, para poder modificarun valor desde el código de la función, se debe usar un puntero al valor a modificar. También se usan punteros a estructurascomo argumentos de una función aún cuando la estructura no sea modificada dentro de la función. Esto es realizado paraevitar copiar todo el contenido de la estructura dentro de la pila.int MyFunction( struct MyStruct *pStruct );

28.0.1 Asignando valores a punteros

Continuamos con el proceso de asignar valores a los punteros, para esto utilizamos el operador & o 'la dirección 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 también pueden ser asignados a referencias de memorias dinamicas creadas comúnmente por las funcionesmalloc() y calloc().#include <stdlib.h> ... struct MyStruct *pKeyboard; ... pKeyboard = malloc(sizeof(struct MyStruct)); ...

La función malloc retorna un puntero de memoria asignada de manera dinámica (o NULL si falla). El tamaño de estamemoria es definido de modo que pueda contener la estructura MyStructEl siguiente código es un ejemplo mostrando un puntero siendo asignado a una referencia y se retorna el valor del puntero

60

61

en la función.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 = &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 referenciade una variable local a la funcion, porque estos valores desaparecen despues de salir de la funcion y el puntero estaria malreferenciado, causando errores en tiempo de ejecucion en el programa. La memoria creada dinamicamente dentro de lafuncion puede devolverse como puntero, el valor de retorno aunque es creado en una variable local la direccion como talse devuelve por valor dejando esta informacion valida.

Capítulo 29

Programación en C/Manejo dinámico dememoria

Seguramente durante todo tu recorrido con este libro, especialmente en la sección de punteros, te hablábamos sobre laasignación dinámica de memoria. Como su nombre lo dice, este es una forma de conseguir espacio en memoria, dándotemas eficiencia y técnicamente hacer lo que requieras hacer con este.En esta sección haremos uso bastante (inevitablemente) de los punteros, por lo que te recomendamos que tengas muybien establecidos estos conocimientos.

29.1 Memoria dinámica

Es memoria que se reserva en tiempo de ejecución. Su principal ventaja frente a la estática, es que su tamaño puede variardurante la ejecución del programa. (En C, el programador es encargado de liberar esta memoria cuando no la utilice más).El uso de memoria dinámica es necesario cuando a priori no conocemos el número de datos/elementos a tratar.

29.2 Memoria estática

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 ejecución y tampocopuede ser liberada manualmente.

29.3 Diferencias, ventajas y desventajas

La memoria reservada de forma dinámica suele estar alojada en el heap o almacenamiento libre, y la memoria estática enel stack o pila (con excepción de los objetos de duración estática, que se verán más adelante, los cuales normalmente secolocan en una zona estática de datos). La pila generalmente es una zona muy limitada. El heap, en cambio, en principiopodría estar limitado por la cantidad de memoria disponible durante la ejecución del programa y el máximo de memoriaque el sistema operativo permita direccionar a un proceso. La pila puede crecer de forma dinámica, pero esto depende delsistema operativo. En cualquier caso, lo único que se puede asumir es que muy probablemente dispondremos de menorespacio en la pila que en el heap.Otra ventaja de la memoria dinámica es que se puede ir incrementando durante la ejecución del programa. Esto permite,por ejemplo, trabajar con arreglos dinámicos. Aunque en C, a partir del estándar C99 se permite la creación de arregloscuyo tamaño se determina en tiempo de ejecución, no todos los compiladores implementan este estándar. Además, se

62

29.4. EL LENGUAJE C Y EL MANEJO DE LA MEMORIA 63

sigue teniendo la limitante de que su tamaño no puede cambiar una vez que se especifica, cosa que sí se puede lograrasignando memoria de forma dinámica.Una desventaja de la memoria dinámica es que es más difícil de manejar. La memoria estática tiene una duración fija,que se reserva y libera de forma automática. En contraste, la memoria dinámica se reserva de forma explícita y continúaexistiendo hasta que sea liberada, generalmente por parte del programador.La memoria dinámica puede afectar el rendimiento. Puesto que con la memoria estática el tamaño de las variables seconoce en tiempo de compilación, esta información está incluida en el código objeto generado, por lo cual el proceso esmuy eficiente. Cuando se reserva memoria de manera dinámica, se tienen que llevar a cabo varias tareas, como buscarun bloque de memoria libre y almacenar la posición y tamaño de la memoria asignada, de manera que pueda ser liberadamás adelante. Todo esto representa una carga adicional, aunque esto depende de la implementación y hay técnicas parareducir su impacto.

29.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 duración: estática, automática y asignada. Las variables globales y las variables locales declaradas con elespecificador static tienen duración estática. Se crean antes de que el programa inicie su ejecución y se destruyen cuando elprograma termina. Las variables locales no static tienen duración automática. Se crean al entrar al bloque en el que fuerondeclaradas y se destruyen al salir de ese bloque. Duración asignada se refiere a los objetos cuya memoria se reserva deforma dinámica. Como se explicó anteriormente, esta memoria se crea y se debe liberar de forma explícita. Los arreglosde longitud variable de C99 son un caso especial. Tienen duración automática, con la particularidad de que son creadosa partir de su declaración.La biblioteca estándar de C proporciona las funciones malloc, calloc, realloc y free para el manejo de memoria dinámica.Estas funciones están definidas en el archivo de cabecera stdlib.h.

29.4.1 malloc

La función malloc reserva un bloque de memoria y devuelve un puntero void al inicio de la misma. Tiene la siguientedefinición:void *malloc(size_t size);donde el parámetro size especifica el número de bytes a reservar. En caso de que no se pueda realizar la asignación,devuelve el valor nulo (definido en la macro NULL), lo que permite saber si hubo errores en la asignación de memoria.Ej:int *puntero; char *puntcarc; puntero=(int *)malloc(4); puntcarc=(char *)malloc(200);

A continuación se muestra un ejemplo de su uso:int *i; … /* Reservamos la memoria suficiente para almacenar un int y asignamos su dirección a i */ i = malloc(sizeof(int));/* Verificamos que la asignación se haya realizado correctamente */ if (i == NULL) { /* Error al intentar reservar me-moria */ }

Uno de los usos más comunes de la memoria dinámica es la creación de vectores cuyo número de elementos se define entiempo de ejecución:int *vect1, n; printf(“N£mero de elementos del vector: "); scanf("%d”, &n); /* reservar memoria para almacenar n ente-ros */ vect1 = malloc(n * sizeof(int)); /* Verificamos que la asignación se haya realizado correctamente */ if (vect1 ==NULL) { /* Error al intentar reservar memoria */ }

64 CAPÍTULO 29. PROGRAMACIÓN EN C/MANEJO DINÁMICO DE MEMORIA

29.4.2 calloc

La función calloc funciona de modo similar a malloc, pero además de reservar memoria, inicializa a 0 la memoria reser-vada. Se usa comúnmente para arreglos y matrices. Está definida de esta forma:void *calloc(size_t nmemb, size_t size);El parámetro nmemb indica el número de elementos a reservar, y size el tamaño de cada elemento. El ejemplo anteriorse podría reescribir con calloc de esta forma:int *vect1, n; printf(“N£mero de elementos del vector: "); scanf("%d”, &n); /* Reservar memoria para almacenar n en-teros */ vect1 = calloc(n, sizeof(int)); /* Verificamos que la asignación se haya realizado correctamente */ if (vect1 ==NULL) { /* Error al intentar reservar memoria */ }

29.4.3 realloc

La función realloc redimensiona el espacio asignado de forma dinámica anteriormente a un puntero. Tiene la siguientedefinición:void *realloc(void *ptr, size_t size);Donde ptr es el puntero a redimensionar, y size el nuevo tamaño, en bytes, que tendrá. Si el puntero que se le pasa tieneel valor nulo, esta función actúa como malloc. Si la reasignación no se pudo hacer con éxito, devuelve un puntero nulo,dejando intacto el puntero que se pasa por parámetro. Al usar realloc, se debería usar un puntero temporal. De lo contrario,podríamos 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 unpuntero temporal */ void *tmp_ptr = realloc(ptr, 10); if (tmp_ptr == NULL) { /* Error: tomar medidas necesarias */ }else { /* Reasignación exitosa. Asignar memoria a ptr */ ptr = tmp_ptr; }

Cuando se redimensiona la memoria con realloc, si el nuevo tamaño (parámetro size) es mayor que el anterior, se conservantodos los valores originales, quedando los bytes restantes sin inicializar. Si el nuevo tamaño es menor, se conservan losvalores de los primeros size bytes. Los restantes también se dejan intactos, pero no son parte del bloque regresado por lafunción.

29.4.4 free

La función free sirve para liberar memoria que se asignó dinámicamente. Si el puntero es nulo, free no hace nada. Tienela siguiente definición:void free(void *ptr);El parámetro 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 malloco 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);

29.5. BUENAS PRÁCTICAS 65

29.5 Buenas prácticas

Como se vio en las secciones anteriores, siempre que se reserve memoria de forma dinámica con malloc, realloc o calloc,se debe verificar que no haya habido errores (verificando que el puntero no sea NULL). Cuando se trata de verificar elvalor de un puntero (y sólo en ese caso), se puede usar de forma indistinta 0 ó NULL. Usar uno u otro es cuestión deestilo. Como ya se vio, las funciones de asignación dinámica de memoria devuelven un puntero void. Las reglas de Cestablecen que un puntero void se puede convertir automáticamente a un puntero de cualquier otro tipo, por lo que no esnecesario hacer una conversión (cast), como en el siguiente ejemplo:/* El puntero void devuelto por malloc es convertido explícitamente a puntero int */ int *i = (int *)malloc(sizeof(int));

Aunque no hay un consenso, muchos programadores prefieren omitir la conversión anterior porque la consideran menossegura. Si accidentalmente se olvida incluir el archivo stdlib.h (donde están definidas malloc, calloc, realloc y free) en unprograma que use dichas funciones, el comportamiento puede quedar indefinido. Si omitimos la conversión explícita, elcompilador lanzará una advertencia. Si, en cambio, realizamos la conversión, el compilador generará el código objeto deforma normal, ocultado el bug.Una posible razón para usar la conversión explícita es si se escribe código en C que se vaya a compilar junto con códigoC++, ya que en C++ sí es necesario realizar esa conversión.En cualquier caso, dado que el manejo de memoria es un tema complejo, y éste es un error muy común, se debe hacerénfasis en que cuando se trabaja con memoria dinámica, siempre se debe verificar 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. Elcomportamiento del programa queda indefinido: puede terminar de forma inesperada, sobrescribir otros datos y provocarproblemas de seguridad. Liberar un puntero que ya ha sido liberado también es fuente de errores.Para evitar estos problemas, se recomienda que después de liberar un puntero siempre se establezca su valor a NULL.int *i; i = malloc(sizeof(int)); … free(i); i = NULL;

Capítulo 30

Programación en C/Matrices Dinamicas

Esta sección es mas como un tema mas de asignación dinámica de memoria, pero por motivos pedagógicos he decididodividirlo.

66

Capítulo 31

Concepto

Dejando afuera el uso de matrices en C99, en donde uno puede declarar los valores luego de una lectura. Podemos decirque solo hemos usado matrices estáticas, ya que no permitían el cambio de dimensiones una vez que el programa fuesecompilado.Con la asignación dinámica de memoria un puede definir en tipo de ejecución la cantidad de memoria, con las ventajas ydesventajas 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 unnombre, pero, no todos se llaman igual es por esto que debiamos darle suficiente espacio al arreglo.char nombre[100];

Pero si escribimos: Gustavo Chavarria. Solo necesitamos unos 20 caracteres (20 bytes), es decir desperdiciamos 80bytes 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 suficiente espacio en memoria”); //Mensaje return −1; //Cierra el progra-ma con un error. }else printf(“Se asigno memoria "); free(puntero);//liberacion de memoria return 0; } //El programasolo asigna memoria, imprime un mensaje en dependecia de la condicion y libera //Revisado por: Gustavo Chavarria.//UNAN-LEON - Nicaragua.

67

Capítulo 32

Matrices Dinamicas de Dos Dimensiones

En esta parte aprenderemos como usar la asignacion dinamica en matrices bidimensionales. Este proceso se divide en dospartes:

• Asignar memoria a una matriz de punteros, cuyos elementos referenciaran cada una de las filas de la matriz de dosdimensiones que se desea crear.

• Asignar memoria para cada una de las filas. El numero de elementos de cada fila puede ser variable.

Una vista rapida seria:#define FILAS 5 #define 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 fila puede tener una cantidad de columnas de diferentes tamaños. Eluso del for, es para poder acceder a cada fila (matriz[i]) asignandole memoria.

68

Capítulo 33

Programación en C/Algoritmos yEstructuras de Datos

69

Capítulo 34

Concepto

Uno de los procedimientos más comunes y útiles en el procesamiento de datos, es la ordenación de los mismos. Se consi-dera ordenar al proceso de reorganizar un conjunto dado de objetos en una secuencia determinada (patrón de arreglo).El objetivo de este proceso generalmente es facilitar la búsqueda de uno o más elementos pertenecientes a un conjunto.Como ejemplos de conjunto de datos ordenados tenemos:

• Meses del año (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 queotra 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 unconjunto de datos desde el teclado.

• Los externos: Los datos estan guardados en un medio externo, como puede ser un fichero, una base de datos, etc.En donde los datos estan alojados en el disco duro u otro medio fisico.

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

70

Capítulo 35

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 deforma ascendente o descendente.Es el algoritmo más facil de implementar, pero a cambio pagamos un alto precio en procesamiento, ya que este metodoevalua 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, Determinarcual es el mayor o menor de dos numeros, pues aplicando este metodo podremos ordenar array, estructuras y cualquiertipo de dato NO atomico (es decir que se pueda dividir)Este libro trata de programación en C, entonces a continuación nos aplicamos a esto:

35.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) #define Nelementos 4 .... int i,j; //Variables contadoras del ciclo. int lis-ta[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 cambiarla condicion < ó > '''<big>Explicando un poco lo que dice el codigo tenemos:</big>''' # Iniciamos i a 1, de esta formacorreremos el ciclo solamente 3 veces. Asi evitamos correr ciclos innecesariamente. # El segundo for, se ejecutara 3 vecespor 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 lista2, es decir * '''Si, 9 > 3''', como es '''verdadera''' hacemos: # Guardamos momentaneamente en la variable temporal elvalor 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 lista2, guardamos el valor de temp, es decir 9 '''Volvemos’'' nuevamente '''al ciclo''', ahora '''J=2'''... * Si, el valor de lista 2es 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 posicionde 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

71

72 CAPÍTULO 35. METODO DE BURBUJA (BUBLE SORT)

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 yaesta mas ordenada que la original, aun falta bastante, pero recuerda que estábamos en el primer ciclo del primer for (i).'''Aca te dejo como seran los demas movimientos:''' * ''Segundo Ciclo i:'' :* 3 6 1 9 :* 3 1 6 9 * ''Tercer Ciclo i:'' :* 1 36 9 :* YA! En un principio, pensaba no presentarles las variaciones que hay de este algoritmo, pero revisando en varioslibros 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 varioslibros e Internet me he encontrado numerosas variantes, y por motivos pedagógicos voy a mostrarlos, ya que consideroque aunque sean el mismo algoritmo, su diferente implementacion puede llevar a confusiones. En lo personal consideroque el algoritmo planteado al principio es el que mejor se expresa (entiende), y es el que recomiendo implementar, perohay libros que lo implementan de otra forma, por lo que los que se guían con el, pueden tener confusiones a la hora deapoyarse con este libro. ==== Variante: 1 Ciclo. ==== Aunque en realidad usa dos ciclos un while y un for, el while hacela función de nuestro primer for. Sin embargo la demás implementaciones son técnicamente las mismas, solo que en vezde usar una variable ya preparada (j en nuestro caso), este evalúa 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, deesta forma se trabaja a la inversa de nuestro algoritmo.

35.2 Ejemplos

El algoritmo burbuja esta preparado para correr con todo tipo de datos NO atomicos, es por esto que no importa si elarreglo es de tipo char, int, float, etc. Funcionara con sus respectivas modificaciones.El siguiente ejemplo ordenara una lista de tamaño 6, que esta ordenada. Recuerden como buena practica:

• Definir las variables en un solo punto del codigo.

• Definir el tamaño del array como constante.

• Usar la indentacion correspondiente (Tabulaciones).

//Realizado por Master crack cocaino //UNAN-LEON Nicaragua. #include <stdio.h> #define TAM 6 int main() { intlista[TAM]={12,10,5,6,1,3}; //Declaracion e Inicializacion de un array int temp=0; //Variable temporal int i,j; //variablescorredoras del ciclo printf(“La lista DESORDENADA es: \n”); for (i=0;i<TAM;i++) printf("%3d”,lista[i]); //impresionde 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]) //con-dicion { temp = lista[j]; //temp guarda momentaneamente el valor de lista[j] lista[j]=lista[j+1]; //Asigno al la posicionlista[j], lo que hay en lista[j+1] lista[j+1]=temp; //obtendra un nuevo valor por parte de temp. } } } printf("\nLos valo-res 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

Capítulo 36

Método de Inserción

Este método se podría decir que es algo superior al método de la burbuja, ya que logra evaluar menos veces la condición.Sin embargo aun es un algoritmo muy pobre y que utiliza recursos técnicamente igual que el algoritmo burbuja.

36.1 Concepto

El algoritmo consiste en ordenar los dos primeros elementos de la matriz, luego se inserta el tercer elemento en la posicióncorrecta con respecto a los dos primeros, a continuación se inserta el cuarto elemento en la posición correcta con respectoa los tres primeros elementos ya ordenados y así sucesivamente hasta llegar al ultimo elemento de la matriz.

36.2 Explicación

36.3 Ejemplos

brbtgn

73

Capítulo 37

Método QuickSort

Es el ultimo método del que hablaremos en este libro, es el algoritmo que mas calidad tiene, pudiendo llegar a lo mismoque el método burbuja e inserción, mas rápido y con uso de menos recursos.

37.1 Concepto

El ordenamiento rápido (quicksort en inglés) es un algoritmo basado en la técnica de divide y vencerás. Esta es proba-blemente la técnica más rápida conocida. Fue desarrollada por Tony Hoare en 1960.La idea del algoritmo es simple, se basa en la división en particiones de la lista a ordenar, por lo que se puede considerarque aplica la técnica divide y vencerás. El método es, posiblemente, el más pequeño de código, más rápido, más elegante,más interesante y eficiente de los algoritmos de ordenación conocidos.

37.2 Explicación

El método se basa en dividir los n elementos de la lista a ordenar en dos partes o particiones separadas por un elemento: unapartición izquierda, un elemento central denominado pivote o elemento de partición, y una partición derecha. La particióno división se hace de tal forma que todos los elementos de la primera sublista (partición izquierda) son menores que todoslos elementos de la segunda sublista (partición derecha). Las dos sublistas se ordenan entonces independientemente. Paradividir la lista en particiones (sublistas) se elige uno de los elementos de la lista y se utiliza como pivote o elemento departición. Si se elige una lista cualquiera con los elementos en orden aleatorio, se puede seleccionar cualquier elementode la lista como pivote, por ejemplo, el primer elemento de la lista. Si la lista tiene algún orden parcial conocido, se puedetomar otra decisión para el pivote. Idealmente, el pivote se debe elegir de modo que se divida la lista exactamente por lamitad, de acuerdo al tamaño 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 lasclaves menores que el pivote y la otra, todos los elementos (claves) mayores que o iguales que el pivote (o al revés). Estasdos listas parciales se ordenan recursivamente utilizando el mismo algoritmo; es decir, se llama sucesivamente al propioalgoritmo quicksort. La lista final ordenada se consigue concatenando la primera sublista, el pivote y la segunda lista, enese orden, en una única lista. La primera etapa de quicksort es la división o «particionado» recursivo de la lista hasta quetodas las sublistas constan de sólo un elemento. El algoritmo es éste: � Recorres la lista simultáneamente con i y j: porla izquierda con i (desde el primer elemento), y por la derecha con j (desde el último elemento). � Cuando lista[i] seamayor que el elemento de división 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 posición adecuada para colocar el elemento de división, porque sabemosque a un lado los elementos son todos menores y al otro son todos mayores (o habrían sido intercambiados). Al finalizareste procedimiento el elemento de división queda en una posición en que todos los elementos a su izquierda son menoresque él, y los que están a su derecha son mayores.

74

37.3. EJEMPLOS 75

37.3 Ejemplos

Capítulo 38

Programación en C/El proceso decompilación

Normalmente, a la hora de programar no creamos un único archivo C .c, sino varios de ellos conteniendo diferentesfunciones del programa. Esto nos proporciona varias ventajas: una mejor organización del código, una mejor modularidady, sobre todo, más facilidad (y velocidad) a la hora de compilar.El proceso de compilación que hemos tratado hasta ahora se divide en realidad en dos etapas, que el compilador nosesconde en una: compilación propiamente dicha y enlazado. En la primera etapa, la de compilación, nuestro código enC se transforma en código objeto, es decir, código máquina (instrucciones que el ordenador puede ejecutar) en ficheros.o, mientras que en la segunda etapa (enlazado) estos ficheros objeto son unidos entre sí para formar el fichero ejecutable(normalmente sin extensión en el mundo Unix, o con extensión .com o .exe en el mundo MS-DOS/Windows).De esta manera, si no hemos modificado el fichero .c que se compila a un determinado fichero .o podemos ahorrarnos esaparte de la compilación cuando hagamos un cambio en otra parte del programa.Además tenemos los archivos de cabecera .h, que utilizamos para definir parámetros que se utilizan en el código (casodel fuente1.h del ejemplo) o para definir todas las funciones que vamos a utilizar en todos los archivos .c (caso delcabeceras.h), ya que si recordamos, las funciones (como todo enC) deben definirse antes de usarse, y es posible que esténen otro fichero, lo cual nunca es considerado por el compilador como “antes”. Hay que tener en cuenta que el fichero enel que se encuentra la función main() llama, necesariamente, a todos los demás ficheros .c, directa o indirectamente, yaque de lo contrario lo que tendríamos en esos ficheros sería perfectamente inútil.Si típicamente compilaríamos el ejemplo con$ gcc -o programa fuente1.c fuente2.c fuente3.cpara realizar esta compilación por pasos, éstos serían:$ gcc -c fuente1.c #crea fuente1.o $ gcc -c fuente2.c #crea fuente2.o $ gcc -c fuente3.c #crea fuente3.o $ gcc -o programafuente1.o fuente2.o fuente3.o #crea programaya que la opción -c del compilador lo que le dice es que detenga el proceso antes de enlazar, creando los ficheros .onecesarios.

76

Capítulo 39

Los pasos del proceso

Lo primero que le ocurre a un fichero .c de código C es el preprocesado. En este paso se sustituyen todas las macros y seeliminan los comentarios. El resultado, si lo vemos independientemente, es un fichero de código C preprocesado, o .i.El segundo paso es la compilación propiamente dicha, en el que el código C preprocesado se convierte en código ensam-blador, que si lo vemos independientemente es un fichero .s.El tercer paso es el ensamblado del código ensamblador, lo que lo convierte en código máquina. Un fichero de códigomáquina también es llamado fichero objeto, y su extensión típica es .o.Dado que el camino anterior normalmente convierte un fichero en un fichero, se suele hacer en un sólo paso, de código Ca fichero objeto.El último paso es el enlazado de los ficheros objeto en el ejecutable final.

39.1 Ejemplos

Tomemos el código C más 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 específicos del preprocesador. Al compilarlotenemos:.file “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, %eaxleave ret .size main, .-main .ident “GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)" .section .note.GNU-stack,"",@progbitsUn precioso código ensamblador que enseguida convertimos en un ilegible código máquina:ELFØ4( UåäðžÀÀÁèÁà)ÄžÉÃGCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3) .symtab.strtab.shstrtab.text.data.bss.comment.note.GNU-stack4#!XX,X95E@Àñÿ #prueba.cmainEse código máquina es el fichero .o que normalmente obtenemos directamente del código C. Finalmente, ese códigoobjeto es enlazado con las librerías necesarias para formar un ejecutable:ìÀ£žÿÒ¡žÒuëÆŒÉÃöUå¡ÌÀtžÀtäðPTRhhQVhhè³ÿÿÿôUåSQè[ßüÿÿÿÒtè ÿÿÿX[ÉÃUå=Œt+ën_used__libc_start_mainGLIBC_2.0$iihÌÿÐÄÉÃUåäðžÀÀÁèÁà)ÄžÉÃUåWVSì è[Ãþè²þÿÿ ÿÿÿ ÿÿÿ)ÐÁøEðuÄ [^_]ÃŽ&1ÿÖ¶¿ÿGÆ;}ðrõÄ [^_]öŒ'UåWVSì[à ÿÿÿ ÿÿÿ)ÐÁøEðHøÿt41ÿ¶¿ÿGî9}ðuõèDÄ [^_]ÃUåSR¡Œøÿt»Œ¶¿ÿÐCüëøÿuóX[]ÃUåSPè[ÃþèVþÿÿX[ÉÃÿÿÿÿÿÿÿÿ$HÀp \ Y þÿÿo$ÿÿÿoðÿÿoÐÈGCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU)

77

78 CAPÍTULO 39. LOS PASOS DEL PROCESO

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/ build-tree/glibc-2.3.5/csuGNU AS 2.16.1XÔÔ¬F}xgMint\}nŽO V|/space/debian/glibc/build-area/glibc-2.3.5/ build-tree/i386-libc/csu/crti.S/space/debian/glibc/build-area/glibc-2.3.5/build-tree/glibc-2.3.5/ csuGNU AS 2.16.1 f(/space/debian/glibc/build-area/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.S°À01:"VWYX û init.cš^û /space/debian/glibc/build-area/glibc-2.3.5/build-tree/i386-libc/csucrti.S3,W\#,:Ô,Wdd,,W^û /space/debian/glibc/build-area/glibc-2.3.5/build-tree/i386-libc/csucrtn.Sªq /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_usedGNUC 4.0.2 (Debian 4.0.2-2) .symtab.strtab.shstrtab.interp.note.ABI-tag.hash.dynsym.dynstr.gnu.version. gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment. debug_aranges.debug_pubnames.debug_ info.debug_abbrev.debug_line.debug_str#((1HH(7 ppP?ÀÀYGÿÿÿo Tþÿÿo$$ c lD LL u\\ptt0{°°ä°žžŒ ħÌ̬ÐеºÃ°° ŒŒÎŒ7×øæp%ö} v ²0: '|`!5 Üî(HpÀ$L\ t ° °žŒÄÌаŒ ŒÄ-Ì: f@ rÀȞ̊` ŒÐÅŒñÿÖŒñÿéŒñÿúŒñÿ#°*Ž7X G\ M° Tc dŒñÿph# £ŒñÿªÀñÿ¯ŽŸ°Ë ßcall_gmon_start__CTOR_LIST____DTOR_LIST____JCR_LIST__ completed.4463p.4462__do_global_dtors_auxframe_dummy__CTOR_END____DTOR_END____FRAME_END____JCR_END____do_global_ctors_aux_DYNAMIC__ fini_array_end__fini_array_start__init_array_end_GLOBAL_OFFSET_TABLE___init_array_start_fp_hw__dso_handle__libc_csu_fini_init_start__libc_csu_init__bss_startmain__libc_start_main@@GLIBC_2.0data_start_fini_edata_end_IO_stdin_used__data_start_Jv_RegisterClasses__gmon_start__

Capítulo 40

Programación en C/Los errores

Esta sección no está lista si tienes algún conocimiento sobre este tema por favor, regístrate y contribuye con nuestro libro.Esta sección está aqui para los sermones y para dar un par de ideas de como resolver los errores.¿ Qué tipo de errores se podrían presentar ?

• error de sintaxis: cuando el programador escribe algo mal. Usualmente el compilador no va a poder hacer su trabajopor 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 espaciosde memoria, trata de abrir un archivo que no existe, hace divisiones entre 0, ...). Normalmente es el sistema operativoque nos advierte de estos errores.

• error de logica: cuando el programa a pesar de estar bien escrito y comportarse bien no cumple con el objetivopara el que fue hecho. (debía hacer divisiones y esta haciendo multiplicaciones). Ni el compilador, Ni el sistemaoperativo nos van a advertir de estos errores, nos vamos a dar cuenta del error por resultados equivocados.

... etc¿ Cómo se resuelve cada tipo ?¿ Cuales son las practicas que se deben seguir para evitar los errores (tambien podria ser por tipo)?

40.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 sintácticos del propio lenguaje y aquellos en los que se referencia maluna posicion de memoria, no se inicializan variables, etc. Los errores que tenemos que tener cuidado de no cometer losprogramadores son aquellos en los que aunque el compilador no de errores, hayamos creado un programa que en tiempode ejecucion sí tenga errores o que simplemente no haga lo que queríamos que hiciera.

79

Capítulo 41

Programación en C/Herramientas externasútiles

41.1 Sistemas de construcción de código

80

Capítulo 42

make

make es un programa para la construcción de código. Su función principal es evitarnos recompilar archivos innecesaria-mente. Se basa en reglas de dependencia, que especifican qué ficheros dependen de cuales, y órdenes de construcción.Para ver un ejemplo sencillo. En la sección anterior vimos un programa que dependía de tres ficheros objeto, los cualesdependían a su vez de ficheros fuente y ficheros de cabecera. Así, sólo será necesario enlazar de nuevo el programa cuandocambie alguno de los ficheros objeto. Esto lo podemos expresar, con dependencias, de esta manera:programa: fuente1.o fuente2.o fuente3.o

Igualmente, los ficheros objeto dependen de los ficheros fuente y los ficheros de cabecera de una manera que podemosexpresar como:fuente1.o: fuente1.c fuente1.h cabecera.hfuente2.o: fuente2.c cabecera.hfuente3.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 acontinuación la regla para programa detectará el cambio en fuente2.o y reenlazará programa.Para ello, make necesita saber también cómo reconstruir sus objetivos. Estas órdenes (que, ojo, van siempre precedidasde un tabulador, no de espacios) son las mismas que usaríamos a mano y que vimos en el capítulo anterior:programa: fuente1.o fuente2.o fuente3.ogcc -o programa fuente1.o fuente2.o fuente3.ofuente1.o: fuente1.c fuente1.h cabecera.hgcc -c -o fuente1.o fuente1.cfuente2.o: fuente2.c cabecera.hgcc -c -o fuente2.o fuente2.cfuente3.o: fuente3.c cabecera.hgcc -c -o fuente3.o fuente3.c

Todas estas órdenes van en fichero, el fichero de make, cuyo nombre suele ser makefile. Utilizando la versión de GNU demake es más normal llamarlo Makefile, ya que aunque el programa buscará primero si existe el nombre con minúscula,el capitalizado aparecerá más arriba al listar un directorio.Una vez escrito el Makefile, 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.ofuente2.o fuente3.oEsto se debe a que, si no especificamos un objetivo para make, el programa tomará como objetivo por defecto el de la

81

82 CAPÍTULO 42. MAKE

primera regla que hayamos escrito en el Makefile.Si teniéndolo todo ya compilado ejecutamos make de nuevo veremos:$ make make: `programa' está actualizado.Y si ahora hacemos un cambio en fuente2.c, al ejecutar make una vez más tendremos:$ make gcc -c -o fuente2.o fuente2.c gcc -o programa fuente1.o fuente2.o fuente3.oDe esta manera, el programa nos ahorra recompilar o reenlazar los ficheros cuyas dependencias no hayan cambiado. Paraello, make se fija en la fecha y hora de la última actualización de cada fichero. Si ésta es posterior a la de todas susdependencias, make no reconstruirá el objetivo. Si, en cambio, alguna de las dependencias de un fichero 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 Makefile (o makefile) éstosse hacen con el carácter #:####### Makefile## (c) Envite, 2004# para el wikilibro “Programación en C (fundamentos)"# bajo licencia FDL######Regla para construir el programaprograma: fuente1.o fuente2.o fuente3.ogcc -o programa fuente1.o fuente2.o fuente3.o#Reglas para construir los ficheros objetofuente1.o: fuente1.c fuente1.h cabecera.hgcc -c -o fuente1.o fuente1.cfuente2.o: fuente2.c cabecera.hgcc -c -o fuente2.o fuente2.cfuente3.o: fuente3.c cabecera.hgcc -c -o fuente3.o fuente3.c

42.1 Sistemas de control de versiones• CVS ▶

• Subversion

Capítulo 43

Programación en C/Ejemplos

43.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 Público$ ./holamundo Hola Mundo $

43.2 El “Hola Mundo” comentado

[Contexto]/* Inclusión de archivos */ #include <stdio.h> /* Función principal */ int main (int argc,char **argv) { /* Impresión porpantalla y salida del programa*/ printf(“Hola mundo\n”); return 0; }$ ./holamundoc Hola Mundo $

43.3 El “Hola Mundo” estructurado en funciones

[Contexto]/*holamundo.c(c) Envite, 2004para el wikilibro “Programación en C (fundamentos)"bajo licencia FDL, adaptado del Dominio Público*/#include <stdio.h> /*Necesario para la función printf()*/void holamundo(void) /*Función donde se ejecuta la lógica del programa*/{printf(“Hola Mundo\n”); /*imprime la cadena*/return; /*sale de la función*/}int main(void) /*Función principal del programa*/{holamundo(); /*llamada a la función que lleva el peso*/return(0); /*sale del programa: correcto*/

83

84 CAPÍTULO 43. PROGRAMACIÓN EN C/EJEMPLOS

}

$ ./holamundof Hola mundo $

43.4 Ejemplo de cálculo con enteros

[Contexto]/*ejemplo.c(c) Envite, 2004para el wikilibro “Programación en C (fundamentos)"bajo licencia FDL*/#include <stdio.h> /*Necesario para la función printf()*/int main(void) /*Función principal del programa*/{char resultado; /*Variable de tipo carácter donde se almacenará el resultado delas 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 multiplicación.*/printf(“Resultado de la multiplicación: %i\n”,resultado);resultado=5/2; /*Realizamos una división entera.*/printf(“Resultado de la división:%i\n”,resultado);return(0); /*Salimos del programa con el código 0 porque no ha habido errores.*/}$ ./ejemploc Resultado de la suma: 7 Resultado de la resta: 3 Resultado de la multiplicación: 10 Resultado de la división:2 $

43.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”);elseprintf(“Bienvenido.\n”);return;}int main(void){acceso(18);acceso(17);return(0);

43.5. CONTROL DE ACCESO 85

}

$ ./acceso Bienvenido. No puedes acceder. $[Volver al índice general] [Anterior: Salida por pantalla: printf()] [Arriba]--Envite 01:31 11 dic, 2004 (UTC)

Capítulo 44

Programación en C/Glib

44.1 Introducción

El lenguaje C fue creado como un lenguaje multiplataforma capaz de sustituir al ensamblador y generar código portable.Sin embargo a lo largo de los años se empiezan a hacer visibles algunas lagunas del diseño original. Algunos ejemplos quepueden citarse:

• No existe estandarización en torno a la longitud real (en bits) de un entero. Un tipo int puede variar entre 16, 32 y64 bits de longitud en función del sistema operativo y compilador utilizado.

• No existe un soporte estándar en el lenguaje para programación orientada a objetos.

• No existe un soporte estándar para tipos de estructuras de datos frecuentemente utilizadas como listas enlazadas,tablas hash (también llamadas diccionarios).

• No existe un soporte estándar para textos Unicode (el estándar Unicode es posterior a la creación del C).

• No existe un soporte estándar para patrones de diseño frecuentemente utilizados como el “bucle principal de even-tos”.

• No existe un API común para manejar aplicaciones multihilo como puede existir en Java y otros lenguajes/plataformas.

La librería glib se creó con el fín de solucionar estas y otras lagunas originales del lenguaje proporcionando una capa decompatibilidad real multiplataforma en sistemas tipo UNIX (Linux, Solaris, AIX, BSD, ...), Windows, OS/2 y BeOS.La librería glib es gratuita y licenciada bajo LGPL (Lesser GPL), lo cual significa en la práctica que puede ser utilizadatanto en programas de código abierto como cerrado.glib provee soporte para expresiones regulares tipo PERL, un conjunto de datos tipados más seguros que el C estándar, so-porte multihilo multiplataforma, un sistema de “bucle principal de eventos”, colas asíncronas, carga dinámica de módulos,soporte portable para uso de ficheros, tuberías y sockets, programación orientada a objetos, utilidades de todo tipo paramanipular textos, escaneado sintáctico, “Timers” y multitud de estructuras de datos frecuentemente utilizadas “slices”de memoria, listas doblemente enlazadas, colas, secuencias (listas escalables), tablas hash para búsqueda mediante llaves(donde la llave puede ser un texto o un objeto complejo), arrays dinámicos, árboles binarios balanceados, árboles “N-ários” (árboles con N ramas por nodo), quarks, listas de datos indexadas (accesibles mediante un identificador GQuark,relaciones y tuplas que pueden ser indexadas mediante un número arbitrario de campos, caches para compartición deestructuras complejas de datos.glib provee además un marco base de utilidades para realizar tests de funcionamiento en tiempo de desarrollo.glib no provee soporte gráfico. Frecuentemente suele emparejarse con la librería gráfica GTK+ ya que la misma utilizaglib internamente como soporte base. Sin embargo glib puede ser utilizado para aplicaciones embebidas o servidor sindependencia alguna del sistema gráfico.

86

44.2. EJEMPLOS DE USO 87

44.2 Ejemplos de uso

glib se encuentra ampliamente documentado por los propios desarrolladores de la librería y existen tutoriales disponiblesen Internet. Aquí se expondrá un ejemplo de código real dónde se utiliza glib para facilitar el desarrollo en C:Este código se utilizó para monitorizar un sistema Linux donde el disco duro había sido sustituido por un sistema deficheros en red. El código se encarga de crear un “perro vigia” encargado de resetear el sistema en caso de detectar unfallo en el sistema de ficheros NFS (Net File System) del cual depende la máquina para su correcto funcionamiento. Elmismo sirve para comprobar la facilidad con que glib permite crear timers, callbacks invocadas por el bucle principal deeventos o gestionar acceso de entrada/salida de forma más ordenada que las librerías estándar de C:#include <glib.h> /* glib */ GError* err = NULL; gboolean isNFSUpAndRunning() { /* Salvo que se indique lo con-trario devuelve “KO"(0) * Superados todos los test devolver'a “OK” (1) */ gboolean result = 0; GIOChannel *gioChan= g_io_channel_new_file("/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)) { go-to 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_file("/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”); fflush(stdout); } g_io_channel_shutdown(gioChan, /*bFlush*/ TRUE, &err); } static gboolean callBackCheckNFS(gpointer user_data){ if (! isNFSUpAndRun-ning() ){ 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, call-BackCheckNFS, (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 ental caso. */ if (! setTimeOut(g_main_loop_get_context(loop)) ) { return 1; } // Ejecutamos el bucle principal de eventos.g_main_run (loop); return 0; }

44.3 Referencias• El Interfaz de Programación de Aplicaciones (API) puede consultarse (en inglés) en la siguiente URL:

http://library.gnome.org/devel/glib/stable/

• El tutorial de la librería gráfica GTK+ (http://library.gnome.org/devel/gtk-tutorial/stable/) incluye también ejem-plos de glib y herramientas para compilar y linkar contra estas dos librerías.

• El exitoso escritorio para UNIX de código abierto GNOME (GNU Network Model Environment) así como laplataforma Moblin de Intel, Nokia Maemo, Google Android y Google Chrome, la versión UNIX de Firefox y otrosprogramas de código abierto como “The Gimp”, Inkscape o DIA, etc ... hacen un uso extenso de glib. El códigoestá disponible gratuitamente en la web y puede servir como una guía y referencia para utilizar glib en proyectosde software complejos con millones de líneas de código y que son utilizados diariamente por millones de personas.

Capítulo 45

Programación en C/Referencia

Esta parte pretende ser una referencia completa de los elementos del lenguaje C. Se pretende que aquí se presenten todoslos datos disponibles de los tipos de datos del lenguaje C, toda la información relevante de los operadores del lenguaje C,las formas en la que se deben usar la principales estructuras e información acerca de algunas bibliotecas y funciones queson estándares. Todo esto con la mayor calidad posible. O sea que en un momento aquí va a existir algo así como unasuper síntesis del manual de programación en C con los datos que son importantes pero que son fáciles de olvidar.

45.1 Tipos de datos

char

• bytes = 1

• Máximo = 127 (255 cuando es unsigned)

• Mínimo = −128 (0 cuando es unsigned)

int

• bytes = 4

• Máximo = 2,147,483,647 (4294967295 cuando es unsigned)

• Mínimo = −2,147,483,648 (0 cuando es unsigned)

float

• bytes = 4

• Máximo Exponente = 1037

• Mínimo Exponente = 10−37

long

• bytes = 4

• Máximo = 2,147,483,647 (4294967295 cuando es unsigned)

88

45.2. OPERADORES 89

• Mínimo = −2,147,483,648 (0 cuando es unsigned)

long long

• bytes = 8

• Máximo = 9,223,372,036,854,775,807 (18,446,744,073,709,551,616 cuando es unsigned)

• Mínimo = 9,223,372,036,854,775,808 (0 cuando es unsigned)

short

• bytes = 2

• Máximo = 32767 (65,535 cuando es unsigned)

• Mínimo = −32768 (0 cuando es unsigned)

double

• bytes = 8

• Máximo Exponente = 10308

• Mínimo Exponente = 10−308

void -sin tipo-

45.2 Operadores

Todos los operadores están puestos en el orden en que están en la jerarquía del lenguaje C. En caso de que estén en unaexpresión todos los operadores juntos y de que no se use el operador (), que rompe la precedencia, entonces van a serevaluados desde el primero de la tabla hasta el último en ese orden. En caso de que se encuentren varios operadores igualeso de la misma precedencia en una expresión sin paréntesis se van a resolver en el orden en que diga la columna de “Ordende agrupación”. La precedencia es una cosa importante porque nos dice en que orden va a ser resuelta una expresión y elorden en que se resuelve una expresión influye en su resultado.un ejemplito: 1 + 2 / 3 + 4 no es igual a (1 + 2) / (3 + 4) eso porque según la precedencia: 1 + 2 / 3 + 4 es igual a 1 + (2 /3) + 4) (2/3 = 0 por ser una operación entre enteros, 1 + 4 = 5) (en el otro caso 1 + 2 = 3 y 3 + 4 = 7 y 3 / 7 = 0 por seruna operación entera)Como la división tiene mayor precedencia que la suma se resuelve primero la división.otro ejemplito: 72 / 3 / 2 / 6 no es igual a (72 / 3) / (2 / 6) eso porque según la precedencia: 72 / 3 / 2 / 6 es igual a ((72 /3) / 2) / 6 (La división no es una operación asociativa y por eso cambia el resultado si resolvemos en orden diferente, noes lo mismo cuando usamos la precedencia del lenguaje C como en la tabla de precedencias, que cuando obligamos a quese resuelva la operación en otro orden usando paréntesis).Para los operadores de Incremento (++) y decremento (--) hay que tener en cuenta el sitio en el que se encuentran conrespecto a la variable que están influenciando. Con esto me refiero a que siendo a una variable ++a no es igual quea++, aunque en algunas ocasiones parezca lo contrario. Para el caso de ++a el incremento será realizado antes deevaluar la expresión. Y para a++ se evaluara primero la expresión completa y luego se realizara el incremento.En la tabla hay tres operadores que están identificados con un calificativo “para bits”. Con esto me refiero a que influen-ciaran los bits de una variable. Si tengo dos valores a y b el resultado de un a && b será el valor del AND lógico entre losvalores respectivos pero el a & b tendrá como resultado varios ANDs lógicos individuales entre cada uno de los bits delas dos variables.

90 CAPÍTULO 45. PROGRAMACIÓN EN C/REFERENCIA

45.3 Estructuras

Estructura general de un programa de lenguaje C:inclusión de bibliotecas ... definición de macros y constantes ... definiciones de funciones o/y prototipos ... definiciones devariables globales ... int main(argumentos){ sentencias ... return 0; } definiciones de cuerpos de funciones (cuyos prototiposya fueron declarados) ...Estructura general de un header file de lenguaje C:inclusión de bibliotecas ... definición de macros y constantes ... definiciones de funciones o/y prototipos ... definiciones devariables globales ... definiciones 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 función 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 unión:union { tipo variable; tipo variable; ... }

45.4 Bibliotecas y funciones

Cuando no se especifica un tipo de dato es porque la función permite mas de uno ya sea a través de casting o de otramaña. 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 pantallaFunciones de <stdlib.h>:int system(“llamada”);Funciones de <math.h>:

45.5. PREPROCESADOR DE C 91

double sin(valor); //Calcula el sinus double cos(valor); //Calcula el cosinus double tan(valor); //Calcula la tangente doubleasin(valor); double atan(valor); double acos(valor); double sinh(valor); double cosh(valor); double tanh(valor); doublelog10(valor); double log(valor); double ldexp(valor1,valor2); double pow(valor); double sqrt(valor); //Calcula raícescuadradasFunciones de <time.h>:struct tm time();Funciones de <string.h>:int strcmp(cadena1, cadena2); //Compara dos cadenas de carácteres int strcat(cadena1, cadena2); int strcpy(cadena1,cadena2); //Copia la primera cadena en la segunda int strlen(cadena); //Da la longitud de una cadenaFunciones de <ctype.h>:Función toupper ANSI C Convierte un carácter, en un parámetro entero ch, a mayúscula.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 modificados. El valor de retorno es el valor convertido si ch era una minúscula, o el valororiginal en caso contrario. Nota: los caracteres en acentuados, o con diéresis, en minúscula y la ñ no sufren modificaciones.Ejemplo:#include <stdio.h> #include <ctype.h> int main() { char cadena[] = “esto es una cadena de prueba"; int i; for(i = 0; ca-dena[i]; i++) cadena[i] = toupper(cadena[i]); printf("%s\n”, cadena); return 0; }

45.5 Preprocesador de C

Capítulo 46

Programación en C/Cómo compilar unprograma

La palabra compilar significa traducir un código de programación a codigo ejecutable por la máquina. Para compilar uncodigo fuente y convertirlo en ejecutable hay distintas formas dependiendo del compilador que se utilice. Entre estoscompiladores, uno de los más famosos es GCC (GNU Compiler Collection) el cual trae soporte para diversos lenguajes(C, C++, Ada, Java, etc). Su fama y buen desempeño van ligados con la gratuidad con la cual se distribuye.Un ejemplo simple para compilar un programa con gcc:gcc -o ouput input.cLo cual dara como resultado un binario con el nombre “ouput”. Gcc además trae varias “flags” de optimización las cualespermiten sacarle mayor desempeño a los binarios en un tipo de máquina predeterminada.

46.1 Un poco más sobre compilación

[Volver al índice general]Hasta ahora hemos compilado los programas mediante la orden:$ gcc [nombre].c $y los ejecutábamos 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 eldirectorio actual si no se les especifica otra cosa. ./a.out es la manera de ejecutar ese archivo. Pero es en general máscómodo nombrar los archivos ejecutables a partir de los archivos fuente de los que vienen. Para ello con gcc utilizamosel modificador -o [nombre], con lo que las dos compilaciones que hemos visto hasta ahora podrían quedar así:$ gcc -o holamundo holamundo.c $ gcc -o ejemplo ejemplo.c $y los ejecutaríamos correspondientemente:$ ./holamundo Hola Mundo $ ./ejemplo Resultado de la suma: 7 Resultado de la resta: 3 Resultado de la multiplicación:10 Resultado de la división: 2 $[Volver al índice general] [Anterior: El “Hola Mundo"] [Siguiente: Instrucciones de control] [Arriba]--Envite 01:54 10 dic, 2004 (UTC)

92

Capítulo 47

Programación en C/Código embebido

Esta sección no está lista si tienes algún conocimiento sobre este tema por favor, regístrate y contribuye con nuestro libro.¿Que es código embebido ?El código embebido es parte de código fuente de otro lenguaje de programación diferente al lenguaje C, pero que seincluye en nuestros programas. Por ejemplo: se permite la inclusión de código en lenguaje ensamblador en un programaescrito en lenguaje C.¿Ventajas de utilizar código 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 lenguajeensamblador- que de otra manera seria muy difícil de lograr.¿Código embebido de assembler en Gcc, otros? ¿Cómo usar ?Este es un ejemplo de código 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

93

Capítulo 48

Programación en C/Recursos en la red

48.1 Manuales, tutoriales y otros documentos

48.1.1 Español

• Aprenda lenguaje ANSI C como si estuviera en Primero

Apuntes de la Escuela Superior de Ingenieros Industriales de la Universidad de Navarra. Muy buenos paraaprender a programar.

• Apuntes de Fundamentos de programación en C

Apuntes y ejercicios resueltos de Fundamentos de programación en C para Formación Profesional.

• El Rincón del C

Curso de C, foros e información relacionada con el C/C++

48.1.2 Inglés

• The C Book

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

48.2 Compiladores e IDE• Code::Blocks: IDE libre para Windows y Linux que permite el uso de plugin para añadir funcionalidades.

• Dev-c++: IDE para C y C++ libre para Windows que usa GCC de fácil manejo.

• Eclipse CDT: Framework de Eclipse para programar en C/C++ con soporte para refactorización de código.

• Anjuta: Entorno avanzado de desarrollo para desarrollo de aplicaciones Glib/GTK en UNIX.

• Qt Creator: Realmente se trata de un entorno centrado en la librería QT de C++. Actualmente este entorno esdesarrollado y soportado por Nokia y al igual que Eclipse CDT incluye funciones avanzadas como refactorizaciónautomática de código.

94

48.2. COMPILADORES E IDE 95

• GCC: Compilador más famoso de GNU/Linux, sumamente potente. Soporta cross-compiling.

• NetBeans: IDE desarrollado en Java, entre sus plugins tiene el soporte para c/c++

Capítulo 49

Programación en C/Bibliografía

49.1 Bibliografía en español

49.2 Bibliografía en inglés• 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.

96

49.3. TEXT AND IMAGE SOURCES, CONTRIBUTORS, AND LICENSES 97

49.3 Text and image sources, contributors, and licenses

49.3.1 Text• Programación en C Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C?oldid=236462 Colaboradores: Pablo.cl, Ja-

vier Carro, ManuelGR, Envite, Almorca, CaStarCo, Marsian, Gargo, H, Morfeomtx, LadyInGrey, Josemanuelmv, Valenluis, Necastro, Ra-fael.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 Anónimos: 56

• Programación en C/Introducción Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Introducci%C3%B3n?oldid=243091 Colaboradores: Almorca, Gargo, Tigerfenix, Rafael.galvez.vizcaino, Magister Mathematicae, Maxy, Margamanterola, C1245j7414,Rocafort8 y Anónimos: 9

• Programación 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, Ann-dresorozco, Jcaraballo y Anónimos: 15

• Programación enC/Fundamentos de programaciónFuente: 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, Ra-fael.galvez.vizcaino, M.estarellas, Maxy, Margamanterola, Polyglottos, Dessaya, LlamaAl y Anónimos: 10

• Programación enC/Primer programa enCFuente: 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 Anónimos: 13

• Programación enC/Tipos de datosFuente: 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 Anónimos: 46

• Programación en C/Expresiones Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Expresiones?oldid=233315 Co-laboradores: Gargo, Baldur, Morza, Maxy, Savh, Dessaya y Anónimos: 3

• Programación en C/Interacción 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 Anónimos: 8

• Programación enC/Instrucciones de controlFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Instrucciones%20de%20control?oldid=243020 Colaboradores:ManuelGR, Envite, CaStarCo, Gargo, Magister Mathematicae, Luis449bp, Juanias, Zerohours, Maxy,Margamanterola, Arpia49, Fgrvpro, Savh, TheOrlSan, Dessaya, LlamaAl y Anónimos: 59

• Programación en C/Uso de funciones Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Uso%20de%20funciones?oldid=240471 Colaboradores: Gargo, Muro de Aguas, Morza, MarcoAurelio, Rrmsjp, Luckas Blade, Zerohours, Maxy, Fgrvpro, Dessaya,Alan, LlamaAl, Matiia y Anónimos: 21

• Programación en C/Vectores Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Vectores?oldid=226053 Colabora-dores: Gargo, Josemanuelmv, Magister Mathematicae, Gallaecio, Kved, Maxy, Nahuel9000 y Anónimos: 10

• Programación enC/Cadenas de caracteresFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Cadenas%20de%20caracteres?oldid=234616 Colaboradores: Maxy y Anónimos: 1

• Programación enC/Manejo de archivosFuente: 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, Mchin-gotto, Savh, Ruy Pugliesi, Juanhauara, LlamaAl, Godric y Anónimos: 59

• Programación 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 Anónimos: 16

• Programación en C/Punteros Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Punteros?oldid=239936 Colabora-dores: Jcongote, Morza, Luckas Blade, Ezarate, Jcaraballo, Frigotoni, Narutoeshacker15, Igna, Ralgisbot, Alan y Anónimos: 36

• Programación en C/Manejo dinámico 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, Simeon-dahl, Alan, LlamaAl y Anónimos: 16

• Programación enC/MatricesDinamicasFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Matrices%20Dinamicas?oldid=191878 Colaboradores: Narutoeshacker15

• Programación enC/Algoritmos yEstructuras deDatosFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Algoritmos%20y%20Estructuras%20de%20Datos?oldid=237932 Colaboradores: Morza, Rrmsjp, Savh, Narutoeshacker15, Igna y Anónimos: 11

• Programación en C/El proceso de compilación Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/El%20proceso%20de%20compilaci%C3%B3n?oldid=142589 Colaboradores: Envite y Anónimos: 2

• Programación en C/Los errores Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Los%20errores?oldid=236764Colaboradores: Gargo y Anónimos: 4

• Programación enC/Herramientas externas útilesFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Herramientas%20externas%20%C3%BAtiles?oldid=164446 Colaboradores: ManuelGR, Envite, Jespa y Anónimos: 5

• Programación enC/Ejemplos Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Ejemplos?oldid=235095 Colabora-dores: ManuelGR, Envite, Drinibot, Margamanterola, Savh, Ajraddatz, Igna, Ixfd64 y Anónimos: 11

98 CAPÍTULO 49. PROGRAMACIÓN EN C/BIBLIOGRAFÍA

• Programación en C/Glib Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Glib?oldid=183036 Colaboradores: Ea-rizon, Invadibot y Anónimos: 1

• Programación en C/Referencia Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Referencia?oldid=175770 Cola-boradores: ManuelGR, Almorca, Gargo, Fires, Wutsje, Drinibot y Anónimos: 14

• Programación enC/Cómo compilar un programaFuente: 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 Anónimos: 2

• Programación enC/Código embebidoFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/C%C3%B3digo%20embebido?oldid=146562 Colaboradores: Gargo, Vinikike, ONDIUX y Anónimos: 2

• Programación 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 Anónimos: 4

• Programación enC/BibliografíaFuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n%20en%20C/Bibliograf%C3%ADa?oldid=35363Colaboradores: Almorca

49.3.2 Images• Archivo:100%.svgFuente: http://upload.wikimedia.org/wikipedia/commons/c/c7/100_percents.svgLicencia:CC0Colaboradores: File:100%

.svg Artista original: Siebrand

49.3.3 Content license• Creative Commons Attribution-Share Alike 3.0