Tema 4: Introducción a la programación I - …biolab.uspceu.com/aotero/recursos/docencia/TEMA...

56
Tema 4: Introducción a la programación I Objetivos del tema: Una vez visto los fundamentos de un computador y conociendo cuáles son los pasos a seguir para solucionar un problema del mundo real mediante un programa, es el momento de comenzar a estudiar un lenguaje de programación que permita concretar una solución al problema en software. El lenguaje de programación que hemos elegido para esta asignatura es C++, si bien inicialmente empezaremos por C, que puede considerarse una versión limitada del anterior que no soporta la programación orientada a objetos.

Transcript of Tema 4: Introducción a la programación I - …biolab.uspceu.com/aotero/recursos/docencia/TEMA...

Tema 4:

Introducción a la programación I

Objetivos del tema: Una vez visto los fundamentos de un computador y

conociendo cuáles son los pasos a seguir para solucionar un problema del

mundo real mediante un programa, es el momento de comenzar a estudiar un

lenguaje de programación que permita concretar una solución al problema

en software. El lenguaje de programación que hemos elegido para esta

asignatura es C++, si bien inicialmente empezaremos por C, que puede

considerarse una versión limitada del anterior que no soporta la

programación orientada a objetos.

Programación 2/56

Índice

Índice ...........................................................................................................................................2

1 Breve historia de C ..............................................................................................................5

2 Variables y tipos de datos....................................................................................................6

3 Tipos de datos básicos .........................................................................................................7

3.1 Caracteres ....................................................................................................................7

3.2 Valores lógicos ............................................................................................................9

3.3 Números enteros ........................................................................................................10

3.4 Números reales ..........................................................................................................12

3.5 Rango y precisión de los tipos numéricos de C.........................................................13

3.6 Declaraciones de constantes ......................................................................................14

4 Datos estructurados ...........................................................................................................15

4.1 Tipos de datos estructurados .....................................................................................15

4.1.1 Tablas ................................................................................................................15

4.1.2 Listas..................................................................................................................16

4.1.2.1 Pilas ...........................................................................................................17

4.1.2.2 Colas ..............................................................................................................18

4.1.2.3 Diccionarios o mapas ....................................................................................19

4.1.2.4 Árboles ..........................................................................................................20

Programación 3/56

4.1.2.5 Grafos ............................................................................................................20

5 Nuestro primer programa en C ..........................................................................................21

6 Componentes de un programa en C...................................................................................21

6.1 Directivas del preprocesador .....................................................................................23

6.1.1 La directiva #include .........................................................................................23

6.1.2 La directiva #define...........................................................................................25

6.2 Comentarios...............................................................................................................25

6.3 Literales .....................................................................................................................26

6.4 Palabras reservadas....................................................................................................27

6.5 Identificadores ...........................................................................................................27

6.6 Símbolos ....................................................................................................................28

7 Sentencias ..........................................................................................................................32

8 Sentencias de control de flujo............................................................................................33

8.1 Bloque de sentencias .................................................................................................33

8.2 Sentencia condicional simple : if...............................................................................33

8.2.1 Expresiones condicionales.................................................................................35

8.2.2 Sentencias if anidadas........................................................................................36

8.3 Bucles en C................................................................................................................37

8.3.1 Bucle do-while...................................................................................................37

8.3.2 Bucle while ........................................................................................................39

Programación 4/56

8.3.3 Bucle for ............................................................................................................40

8.3.4 Bucles anidados .................................................................................................42

8.3.5 Sentencias break y continue ..............................................................................43

8.4 Bifurcación múltiple: sentencia switch .....................................................................45

8.5 El operador condicional.............................................................................................47

8.6 Terminación del programa: función exit().................................................................48

9 Ejercicios ...........................................................................................................................50

10 Apéndice I: sistemas binario, octal y hexadecimal........................................................52

10.1 Conversiones entre bases...........................................................................................53

10.2 Conversión Binario - Hexadecimal ..........................................................................53

10.3 Conversión Hexadecimal - Binario ...........................................................................54

10.4 Conversión Binario - Decimal...................................................................................54

10.5 Conversión Decimal - Binario...................................................................................55

10.6 Conversión Decimal - Octal ......................................................................................55

10.7 Conversión Decimal – Hexadecimal .........................................................................55

10.8 Conversión Binario – Octal .......................................................................................56

Programación 5/56

1 Breve historia de C

A principios de la década de los 70 un pequeño equipo de investigación de los laboratorios

Bell de ATT comenzó a desarrollar un nuevo sistema operativo que, con el tiempo,

acabaría llamándose UNIX. En esta tarea se encontraron con que programar en lenguaje

ensamblador un sistema operativo era excesivamente complicado, además de no permitir

portar el sistema operativo a otros ordenadores. Es por ello que decidieron desarrollar un

nuevo lenguaje de programación que permitiera construir el sistema operativo. Para ello se

inspiraron en otros dos lenguajes de programación en cuyo desarrollo habían participado

varios miembros del proyecto: los lenguajes B y BCPL. El lenguaje resultante se llamó C.

El nuevo lenguaje de programación resultó ser una herramienta muy útil para programar

software de sistemas, por lo que se hizo popular muy rápidamente los laboratorios Bell.

Inicialmente no existía ninguna versión definitiva y estable de lenguaje, hasta que en 1978

se escribió el primer libro de referencia sobre él: The C Programming Languge. El

lenguaje se hizo muy popular y muchas universidades y fabricantes de ordenadores

crearon sus propios compiladores para C. A mediados de la década de los 80 gran parte de

la programación de los computadores del mundo se efectuaba en C. La profusión de

compiladores, en los que cada fabricante añadía sus propias instrucciones y bibliotecas,

llevó a la falta de portabilidad entre los programas. Esto llevó a ANSI (American National

Standards Institute) a crear una versión estándar del lenguaje que se llamó ANSI C.

Desde entonces ha habido varias revisiones del estándar, la más reciente de ellas en 1999.

Esta versión de mantenimiento del estándar, C99, añade nuevas características a ANSI C y

resuelve ciertos problemas que tenía éste. En la actualidad C se encuentra en cierto

declive, por un lado por la aparición de nuevos lenguajes de programación más simples

que, a costa de disminuir la eficiencia del programa, abstraen más al programador de los

detalles de la maquina y le permiten ser más productivo, a la vez que disminuyen la curva

de aprendizaje del lenguaje. Por otro lado, la gran importancia las aplicaciones distribuidas

y orientadas a Internet, para las cuales C no fue diseñado ya que fue creado más de 20 años

antes de la aparición de Internet, hacen que en muchas ocasiones sean preferibles otros

lenguajes de programación.

Sin embargo, sigue habiendo ciertas aplicaciones para las cuales C (o su versión orientada

a objetos, C++) siguen siendo la mejor herramienta: los sistemas operativos, los vídeo

Programación 6/56

juegos y, en general, las aplicaciones de escritorio están mayormente desarrolladas en C.

Por otro lado, este lenguaje de programación ha influido notablemente en los lenguajes que

más apogeo tienen en la actualidad y que están llamados a ser los dominantes el día de

mañana: Java y C#.

C es un lenguaje de propósito general, esto es, desde él puede abordarse prácticamente

cualquier tipo de problema. Es un lenguaje de bajo-medio nivel, que permite el acceso al

hardware de la computadora de manera que se puede hacer uso eficiente de los recursos

con los que cuenta un equipo. Esto lo hace especialmente adecuado para aquellos

problemas que requieran cálculo intensivo y una gestión cuidadosa del espacio de

almacenamiento. Se ajusta muy bien a los esquemas de diseño jerárquico de arriba a abajo

y, por tanto, a la programación estructurada y a la modular.

2 Variables y tipos de datos

Las variables son identificadores utilizados para almacenar datos. Cada variable se asocia

con una determinada zona dentro de la memoria del ordenador cuyo tamaño dependerá del

tipo de dato que almacene la variable. Todos los datos a nivel de la máquina se representan

en una secuencia de bits; sin embargo C, en particular, y los lenguajes de alto nivel, en

general, permiten basarse en abstracciones que permiten ignorar los detalles de la

representación interna de los datos.

Las variables pueden contener diferentes valores durante los diferentes pasos de la

ejecución de un programa. Todas las variables en C deben definirse antes de su uso. Una

variable se define la siguiente forma:

tipo_de_dato nombre_variable;

esta sentencia define la variable nombre_variable que almacenará un dato del tipo

tipo_de_dato. Es posible definir varias variables del mismo tipo a la vez:

tipo_de_dato nombre_variable1, nombre_variable2,..., nombre_variableN;

Al definir una variable es posible inicializarla, esto es, darle un valor inicial.

tipo_de_dato nombre_variable = valor;

Programación 7/56

Si intentamos acceder al valor de la variable antes de inicializarla ésta puede contener un

valor cualquiera que podría diferir dependiendo de la plataforma en la que se ejecute el

programa, por lo que siempre debemos darle un valor inicial a las variables antes de

usarlas y no confiar en la inicialización por defecto del compilador. El nombre de una

variable es una forma simbólica de llamar a la dirección de memoria donde comienza el

dato que almacena. Los nombres de las variables deben guardar relación con el objeto que

representa.

En C existen una serie de tipos de datos básicos que es posible componer para crear tipos

de datos más complejos adaptados a las necesidades particulares un programa.

Empezaremos viendo cuáles son los tipos de datos básicos.

3 Tipos de datos básicos

En C hay tres categorías básicas de tipos de datos: caracteres, números enteros y números

reales. Además, muchos lenguajes de programación incluyen como tipo básico de datos

variables lógicas o boolean.

3.1 Caracteres

El tipo de datos char se emplea para almacenar caracteres, o enteros de 8 bits. Los literales

de tipo carácter se representan mediante un único carácter encerrado entre comillas

simples. Así por ejemplo:

char letra = ‘a’;

define una variable de tipo carácter que se llama letra y que contiene el carácter a. Hay

ciertos caracteres especiales (también se les denomina secuencias de escape) cuya

representación textual no coincide con su representación en pantalla (una tabulación, el

carácter de nueva línea, etc.), pudiendo incluso no tener ningún tipo de presentación en

pantalla sino realizando alguna acción (emitir un sonido, avanzar una página, etc.) o

poseyendo un significado especial dentro del lenguaje de programación (el carácter de fin

de cadena). Estos caracteres de escape son:

• '\0' carácter de fin de cadena

• '\a' carácter de alarma

Programación 8/56

• '\b' retroceso

• '\f' avance de hoja

• '\n' nueva línea

• '\r' retorno que carro

• '\t' tabulador horizontal

• '\v' tabulador vertical

• '\\' diagonal invertida

• '\?' interrogación

• '\'' apóstrofo

• '\"' comillas

• '\ooo' representa el carácter cuya posición de la tabla ASCII se corresponde

con el número octal "ooo".

• '\xhh' representa el carácter cuya posición de la tabla ASCII se corresponde

con el número hexadecimal "hh".

Los caracteres se representan en el ordenador mediante el código ASCII, American

Standard Code for Information Interchange (Código Estadounidense Estándar para el

Intercambio de Información), un código de caracteres basado en el alfabeto latino tal como

se usa en inglés moderno y otras lenguas occidentales. Creado en 1963 por el Comité

Estadounidense de Estándares (ASA) como una evolución de los conjuntos de códigos

utilizados entonces en telegrafía. A continuación mostramos la tabla de caracteres ASCII:

Programación 9/56

3.2 Valores lógicos

Se emplean para representar valores lógicos, esto es, datos que sólo pueden ser ciertos o

falsos. Su representación depende de la implementación de cada lenguaje; en ocasiones se

emplea TRUE para representar una condición que se satisface y FALSE para una que no se

satisface; otra opción es emplear un 0 para representar una condición que no se satisface y

un 1 (o un valor numérico cualquiera distinto cero) para representar una condición se

satisface.

C tradicionalmente no ha contado con un tipo de dato para representar los valores lógicos.

En C cualquier valor entero que tome un valor diferente de cero se interpreta como un dato

lógico que toma el valor cierto, y cualquier valor entero que tome el valor cero se

interpreta como un dato lógico que toma el valor falso. C99 introdujo un nuevo tipo de

dato, _Bool, para representar valores lógicos. Dado lo reciente de esta especificación,

todavía hay compiladores que no lo soportan. Por otro lado, las librerías estándar de C (y

Programación 10/56

la gran cantidad de código C que se escribió antes de esta especificación), definidas antes

de esta especificación, emplean enteros para representar valores lógicos. Finalmente, el

empleo de este nuevo tipo de dato rompe la compatibilidad entre C y C++, ya que impide

compilar con un compilador del segundo lenguaje código del primero: para C++ _Bool no

es un tipo de dato, ni una palabra reserva. Por todos estos motivos, en este texto no se

empleará el tipo de dato _Bool en los programas C, sino que se emplearán variables

enteras para representar valores lógicos.

3.3 Números enteros

Los números enteros, como su propio nombre indica, representan datos numéricos que no

tienen parte decimal. Existen varias formas de representar un número entero en código

binario.

• Números enteros sin signo:

o Binario puro: el número entero se representa por su binario correspondiente

25 → 00011001

o Decimal codificado en binario (BCD): se usa un grupo de 4 bits por cada

dígito decimal.

25 → 0010 0101

• Números enteros con signo o Representación en signo-magnitud (SM): el bit que va más a la izquierda

representa el signo (0 si es un número positivo,1 si es un número negativo)

y el resto representan la magnitud.

25 → 0 0011001

-25 → 1 0011001

El rango de valores que se puede presentar es: -2 n-1 +1 ≤ x ≤ 2 n-1 -1.

o Complemento a 1 (C-1): los números positivos se representa n igual que en

SM. En los negativos se cambian los 0s por 1s.

Programación 11/56

25 → 0 0011001

-25 → 1 11001 10

Rango de representación: -2 n-1 +1 ≤ x ≤ 2 n-1 -1.

o Complemento a (C-2): los números positivos se representan igual que en

SM. En los negativos se cambian los 0s por 1s y se suma 1.

25 → 0 0011001

-25 → 1 1100110 + 1 = 1 1100111

Rango de representación: -2 n-1 ≤ x ≤ 2 n-1-1.

o Exceso a 2 n-1: No hay bit para el signo. Se representa en binario el valor

correspondiente con número + exceso (2 n-1), donde exceso (2 n-1) es el

número entero una unidad superior al mayor número que se puede presentar

con n-1 bits.

25 → 128 + 25 = 153 → 10011001

-25 → 128 - 25 = 103 → 01100111

Rango de representación: -2 n-1 ≤ x ≤ 2 n-1-1.

En C, los tipos de datos enteros pueden ser char, short, int o long que almacenan,

habitualmente, enteros de 8, 16, 32 o 64 bits, respectivamente.

int numero, altura, profundidad; long balance, distancia;

A los tipos char e int se les pueden aplicar los modificadores signed y unsigned para que

sean con signo o sin signo. Los tipos con signo tienen un bit menos de longitud. El tipo

char puede corresponder a signed char o unsigned char dependiendo del compilador.

Programación 12/56

Los números enteros se pueden representar tanto el formato decimal como octal (para ello

se debe anteponer un 0 al número 0712) y en hexadecimal (se antepone un 0x al número

0xFA81). Para más información acerca de estos sistemas de numeración y las conversiones

entre distintos sistemas consultar el apéndice del capítulo.

Los tipos int y long admiten el modificador long (e int el short) para indicar el número de

bits (mayor o menor rango de valores).

3.4 Números reales

Los números reales se emplean, habitualmente, para presentar números que tienen una

parte decimal; en ocasiones también se le ampara representan números enteros cuyo

tamaño demasiado grande para ser representado por los datos de tipo enteros.

Los números reales se pueden representar como un conjunto dígitos del 0 al 9 más el punto

decimal:

25.32 -48759.4596 -0.41526 ....

O también en notación científica o exponencial: " mantisaEexponente".

Número = mantisa * Base de exponenciación exponente

• Mantisa: número real

• Base de exponenciación (E): base decimal.

• Exponente: exponente correspondiente a un número entero con su signo

2.5E3 → 2.5x103

0.25E-2 → 0.25x10-2

Internamente, los números reales se representan en notación científica; la base es 2, la

mantisa en binario puro (SM, C-1, C-2), y el exponente en exceso a 2 n-1 (ó SM).

En C los números reales de simple precisión (32 bits) se representan como sigue:

• Signo: 1 bit (posiciones 31)

• Exponente: 8 bits (posiciones 23 a 30)

Programación 13/56

• Mantisa: 23 bits (posiciones 0 a 22)

Y los de doble precisión precisión (64 bits):

• Signo: 1 bit (posiciones 63)

• Exponente: 11 bits (posiciones 52 a 62)

• Mantisa: 52 bits (posiciones 0 a 51)

Los tipos de datos reales son float y double; el segundo almacena números de mayor

tamaño y con una mayor precisión que el primero.

Ejemplos de uso de este tipo de de datos son:

float elevacion = 2.43, x = 4.0;

El tipo double admiten el modificador long para indicar es irse un número mayor de bits

(mayor o menor rango de valores).

3.5 Rango y precisión de los tipos numéricos de C

El número de bits, alcance y precisión de los tipos (tanto enteros como reales) de datos

dependen de cada compilador, aunque suelen ser similares para una misma CPU (I80x86,

SPARC, Alpha, etc.), ya que se adaptan a los que suministra ésta. Debido esta variación

entre distintos compiladores el mejor modo de conocer el rango de valores que pueden

representar cada uno de los tipos de datos es consultar los archivos de cabecera limits.h y

float.h, que, respectivamente, nos indican los valores mínimo y máximo para cada uno de

los tipos de datos enteros y reales. Para que el alumno tenga ciertas referencias del rango la

precisión de los distintos tipos de datos listamos a continuación los valores que típicamente

se emplean actualmente para los tipos de datos más comunes. En primer lugar indicamos el

tamaño en bits del dato y a continuación su rango.

• char: 8 -128 a 127

• unsigned char: 8 0 a 255

• signed char: 8 -128 a 127

• int: 32 -2147483648 a 2147483647

Programación 14/56

• unsigned int: 32 0 a 4294967295

• signed int: 32 -2147483648 a 2147483647

• short int: 16 -32768 a 32767

• unsigned short int: 16 0 a 65535

• signed short int: 16 -32768 a 32767

• long int: 32 -2147483648 a 2147483647

• signed long int: 32 -2147483648 a 2147483647

• unsigned long int: 32 0 a 4294967295

• long long: 64 –9,223,372,036,854,775,808 a 9,223,372,036,854,775,807

• float: 32 seis dígitos de precisión 3.4 x 10-38 a 3.4 x 1038

• double: 64 diez dígitos de precisión 1.7 x 10-308 a 1.7 x 10308

• long double: 80 17 dígitos de precisión 3.4 E -4932 a 1.1 E +4932

3.6 Declaraciones de constantes

Hemos visto una forma de representar datos cuyo valor no cambia (constantes) en un

programa C: mediante la declarativa define. Esta declarativa hace que el preprocesador

sustituya toda las ocurrencias de un identificador por un determinado valor, siendo por

tanto el valor de la constante un literal en el programa C que es compilado.

También es posible definir una constante empleando una variable cuyo contenido no puede

ser modificado durante la ejecución del programa. Para ello se emplea la palabra reservada

const:

const int i= 8;

tras esta definición cualquier intento de asignar un valor a la variable i provocará un error

en el compilador., así, el siguiente código sería ilegal:

const int i= 8; i=8;

Programación 15/56

4 Datos estructurados

La resolución de un problema puede complicarse o simplificarse notablemente

dependiendo de la representación elegida para los datos. Los tipos de datos básicos que

hemos visto hasta ahora a menudo resulta una representación demasiado simple para los

datos de los problemas del mundo real. Es por ello por lo que se definen estructuras de

datos, esto es, una colección de variables homogéneas o heterogéneas con algún tipo de

relación entre ellas.

Estas estructuras de datos pueden clasificarse, en función de su almacenamiento, en datos

internos y datos externos. Los datos internos son los que residen en la memoria principal

del ordenador, mientras que los externos residen en los dispositivos de almacenamiento

masivo.

Dependiendo si el tamaño los datos puede o no cambiar durante la ejecución del programa

los datos se clasifican estáticos (su tamaño no puede variar durante la ejecución del

programa) y dinámicos (su tamaño sí varía durante la ejecución del programa).

Dependiendo de las relaciones que guarda no datos entre si se pueden clasificar en datos

lineales, aquellos que están enlazados a un elemento anterior y a uno posterior, y datos no

lineales, que pueden estar enlazados múltiples elementos.

Por último, si cada dato individual está formado por un conjunto de datos simples o

primitivos (carácter, real, entero o lógico) se dice que los datos son compuestos.

4.1 Tipos de datos estructurados 4.1.1 Tablas

Son estructuras estáticas de datos homogéneos que están constituidas por un número fijo

de elementos situados en direcciones contiguas memoria. Dependiendo de su

dimensionalidad se clasifican en:

• Unidimensionales, son los llamados vectores o arrays.

• Bidimensionales, también llamados matrices.

• Multidimensionales, o poliedros.

Programación 16/56

Se suelen emplear para representar colecciones de datos homogéneas relacionadas

lógicamente ellas y de carácter estático, como puede ser los días de la semana (array) o un

listado de las notas que cada alumno de una clase ha obtenido en las diferentes asignaturas

(matriz).

Posición 0 1 2 3 4 5 6

Lunes Martes Miércoles Jueves Viernes Sábado Domingo

Alumno/

asignatura

Alumno 1 Alumno 2 Alumno 3 Alumno 4

Asignatura 1 8 6 9 3

Asignatura 2 6 4 9 5

Asignatura 3 4 7 6 5

4.1.2 Listas

Es una estructura dinámica de datos constituida por elementos (que habitualmente son del

mismo tipo) denominados nodos. Esta estructura se corresponde con el concepto de "lista"

de la vida cotidiana: una lista de la compra, la lista de invitados a una boda o la lista de las

películas más taquilleras se corresponden con esta estructura. En una lista es posible

acceder a cualquier elemento, sin importar en la posición en la que esté, siendo posible

eliminarlo o modificarlo y añadir elementos en cualquier posición.

Dependiendo de cómo se relacionan entre sí los distintos elementos de una lista, éstas se

pueden clasificar en:

• Simplemente enlazadas: cada

elemento de la lista está enlazado

con el siguiente elemento.

• Doblemente enlazadas: cada

elemento de la lista está enlazado

Programación 17/56

con los elementos siguiente y anterior.

• Listas circulares: son

aquellas listas en las

cuales el último

elemento está

enlazado con el

primero.

Las operaciones típicas que se pueden realizar sobre una lista son:

• Insertar, eliminar o buscar un nodo.

• Vaciar la lista de elementos.

• Extraer un su conjunto de elementos de la lista.

• Concatenar dos listas.

• Comprobar si la lista está vacía o no.

Existen dos tipos de listas muy empleadas en computación que son casos particulares de

las listas generales: las colas y las pilas.

4.1.2.1 Pilas

Una pila es una lista basada en el criterio LIFO (last in first out, el último que entra es el

primero que

sale). Los

elementos se

añaden

apilándose unos

sobre otros y

sólo se tiene

acceso al que

está en la parte

superior.

Programación 18/56

Las pilas también son estructuras de uso común en la vida real: una pila de libros que está

sobre una mesa, una pila de latas de coca cola en un supermercado o la pila de papeles que

usa la impresora para imprimir. En computación, son empleadas tanto por los

compiladores como por los intérpretes para ejecutar programas.

Las operaciones típicas que se pueden realizar sobre una pila son:

• Apilar un elemento en la parte superior (push).

• Extraer elemento que se encuentra en la parte superior (pop).

• Comprobar si la pila está vacía.

• Vaciar la pila.

• Obtener elemento que está encima de la pila sin extraerlo (top).

4.1.2.2 Colas

Una cola es una lista basada en el criterio FIFO (first in first out, el primero que entra es el

primero que sale). Los

nuevos elementos se

añaden en la parte de atrás

y la extracción de

elementos se realiza por la

de delante.

Nuevamente, esta

estructura informática se

inspira en soluciones de la

vida real: la cola del supermercado, la cola de trabajos en una impresora, o la cola del

comedor universitario funcionando modo similar a esta estructura.

Las operaciones típicas que se puede realizar sobre una cola son:

• Añadir un elemento la cola.

• Extraer el primer elemento de la cola.

• Comprobar si la cola está vacía.

• Vaciar todos los elementos la cola.

• Obtener el primer elemento de la cola sin extraerlo.

Programación 19/56

4.1.2.3 Diccionarios o mapas

Es una estructura dinámica de datos

formada por una serie de parejas (clave,

valor). Conociendo la clave es posible

recuperar el valor correspondiente. Una

guía telefónica o un diccionario son

ejemplos del mundo real que funcionan de

un modo similar a estructura. En ellos la

clave sería el nombre de la persona cuyo

teléfono queremos averiguar o la palabra

cuya definición estamos buscando, y el

valor sería el número de teléfono y la

definición, respectivamente.

En un mapa, los distintos elementos

pueden encontrarse ordenados o no según

su clave, y dependiendo del tipo de mapa es posible que se permitan claves repetidas o que

no se permita la repetición claves.

Las operaciones típicas que se realizan sobre un diccionario o mapa son:

• Insertar una pareja (clave, valor).

• Eliminar una pareja (clave, valor).

• Recuperar el valor asociado a una clave.

• Comprobar si una clave pertenece o no a la estructura.

• Listar todas las claves.

• Listar todos los valores.

• Borrar todos los elementos.

Programación 20/56

4.1.2.4 Árboles

Representan estructuras jerárquicas en

las que cada nodo tiene un único

antecesor pero puede tener varios

sucesores. Existe un elemento

denominado raíz sin antecesor del cual

derivan todos los demás. Un tipo muy

común de árboles son los árboles

binarios, donde cada nodo tiene como máximo dos descendientes.

Los árboles gramaticales para analizar las oraciones del lenguaje o un árbol genealógico

son ejemplos del mundo real de función de un modo similar a esta estructura.

4.1.2.5 Grafos

Un grafo es un conjunto de nodos conectados mediante arcos sin ningún tipo de restricción

entre las conexiones que se pueden establecer entre un nodo y los demás. Si, por ejemplo,

quisiésemos representar gráficamente las distancias que hay entre distintas ciudades de un

país que están conectadas de modo directo entre sí esta sería la estructura más adecuada

representar esta información.

.

Programación 21/56

5 Nuestro primer programa en C

Un programa en C es un fichero de texto, normalmente con extensión .c, que contienen las

sentencias del lenguaje. A continuación mostramos un programa muy simple que imprime

en pantalla el texto "¡Hola mundo!":

/* *Nuestro primer programa. *ejemplo4_1.c */ #include <stdio.h> int main(void) { printf("¡Hola mundo!"); }

El texto que está delimitado entre /* */ es un comentario para el programador y, por tanto,

no hace que se ejecute ninguna sentencia. La ejecución del programa comienza por la

primera línea de la función principal, el main. En este caso la única sentencia que tiene la

función principal es un printf, que imprime por pantalla el texto que se le pasa como

argumento.

Para compilar este programa desde la línea de comandos empleando, por ejemplo, con el

compilador gcc emplearemos el comando "gcc ejemplo4_1.c". Como resultado tendremos

un fichero de nombre " a.out" en Linux, o " a.exe" en Windows. Si queremos indicar el

nombre del archivo ejecutable podemos emplear la opción "-o": "gcc -o ejemplo4_1

ejemplo4_1.c".

6 Componentes de un programa en C

Un programa puede contener los siguientes elementos: directivas del preprocesador,

comentarios, palabras reservadas, identificadores, símbolos, y literales. Ejemplo:

/* *ejemplo4_2.c */ #include "stdio.h" int numero; /* este programa es muy simple */

Programación 22/56

main() { numero=5; if (numero>3) printf("El numero es %d ", numero); }

En este programa:

• #include "stdio.h" : es una directiva del preprocesador.

• /* este programa es muy simple */ : es un comentario.

• int, main, if : son palabras reservadas.

• numero, printf son identificadores.

• ; ( ) > , " { } son símbolos.

• "El numero es %d ", 5 y 3 son literales.

El formato en que se deben describir los distintos componentes de un programa en C es

bastante libre, y se rige por las siguientes reglas:

• No se pueden pegar palabras (no es correcto escribir "intnumero").

• No se pueden partir palabras (no es correcto "ma in").

• Se diferencian las mayúsculas de las minúsculas (numero es distinto de Numero).

• El número de espacios en blanco entre palabras (1 o más) o símbolos (0 o más) no

importa.

• El formato de las líneas es libre, se puede escribir el mismo programa en una sola

línea o en varias, siempre que no dividamos una palabra o un literal en dos partes.

El ejemplo siguiente muestra dos formas equivalentes de escribir una sentencia de

C.

if (numero>3) printf("El numero es %d ", numero);

es lo mismo que

if (numero>3) printf("El numero es %d ", numero);

• Cada sentencia (no cada línea) finaliza con un punto y coma (generalmente).

Programación 23/56

• Cualquier identificador (variable, constante o función) que usemos en el programa

debe ser definido antes de ser utilizado. El compilador traduce el programa

leyéndolo de arriba a abajo y da un error si encuentra que se utiliza algo que no ha

sido definido previamente.

A continuación veremos más en detalle cada uno de los componentes que pueden aparecer

en un programa C.

6.1 Directivas del preprocesador

El preprocesador de C es un programa que se ejecuta automáticamente con el compilador y

que realiza ciertas modificaciones sobre el código fuente. El compilador actuará sobre el

código fuente modificado. Algunas de estas modificaciones son automáticas, mientras que

otras pueden ser indicadas por el usuario sobre el programa antes de que se compile. Las

modificaciones a realizar se indican por una directiva del preprocesador, que comienza por

el símbolo #. Algunas de las directivas existentes son:

#define #if #line #elif #ifdef #pragma #else #ifndef #undef #error #include

Las directivas #include y #define son las que se utilizan con más frecuencia, por lo que se

describirán a continuación.

6.1.1 La directiva #include

Esta directiva hace que el código fuente contenido en un fichero se lea de disco y se

compile como si estuviese dentro del programa que se está compilando. El formato de esta

directiva es:

# include "fichero"

o

#include <fichero>

Cuando se utilizan los caracteres < >, el preprocesador busca el fichero a incluir en un

directorio o directorios definidos en la instalación del compilador (generalmente, un

directorio llamado INCLUDE). Cuando se utilizan los caracteres " ", se busca el fichero

Programación 24/56

primero en el directorio actual (desde el que se utiliza el compilador), y después en dichos

directorios.

Esta directiva funciona del siguiente modo. Supongamos que tenemos un fichero de

programa (prueba.c) y un fichero con definiciones adicionales (cosas.h) a incluir en el

mismo. Cuando el preprocesador encuentra el #include "cosas" lo elimina, incluye las

líneas de cosas.h el fichero prueba.c y después compila el fichero modificado. El

preprocesador no modifica realmente el fichero en disco, sino que hace todo el trabajo en

memoria o en una copia temporal del fichero, que borra después de la compilación. Todo

este proceso se realiza de forma automática al compilar el programa, sin requerirse la

intervención del programador. Veamos un ejemplo:

/* *ejemplo4_3.c */ #include "cosas.h" float Area; main() { Area=Pi*radio*radio; printf ("Area: %f", Area); }

el programa a incluir será: cosas.h

float Pi=3.1416; int radio=5;

El programa que genera el preprocesador y que será compilado por el compilador es:

float Pi=3.1416; int radio=5; float Area; main() { Area=Pi*radio*radio; printf ("Area: %f", Area); }

Los archivos que se incluyen suelen ser fragmentos de programa con definiciones de tipos,

variables o funciones, para utilizarlos en el programa principal. Se denominan archivos de

Programación 25/56

cabecera, header files, de ahí la extensión .h. Además de los que pueda crear el usuario, el

compilador de C dispone de varios archivos de cabecera que es necesario incluir para

utilizar la mayoría de las funciones de las librerías que se suministran con el compilador.

En los manuales del compilador se indica qué fichero .h es necesario incluir para cada

función. En las versiones de C para PC's esta información suele estar disponible en la

ayuda interactiva del compilador. También es posible descargarse archivos cabecera como

librerías creadas por terceras partes.

6.1.2 La directiva #define

Esta directiva sirve, entre otras cosas, para definir constantes simbólicas. Permite asignar a

un identificador un valor determinado que nunca cambiará. Cuando se compile el

programa, el preprocesador substituye cada aparición de dicho identificador por su valor

antes de compilar esa línea. Al igual que en el caso de la directiva #include, el proceso es

transparente al usuario. Veamos cómo funciona con un ejemplo:

/* *ejemplo4_4.c */ #define PI 3.1416 int radio=5; float Area; main() { Area=PI*radio*radio; }

Generalmente las constantes simbólicas se definen con mayúsculas, para diferenciarlas de

los nombres de las variables, que generalmente se escriben con minúsculas.

6.2 Comentarios

Los comentarios en un programa sirven para documentarlo, de forma que sea comprensible

tanto para el que lo ha escrito como para otras personas. Los comentarios en C van entre

los caracteres /* y */. El compilador hace caso omiso del contenido de los comentarios. Un

comentario puede colocarse en cualquier parte del programa, salvo en medio de una

palabra o un literal, y puede ocupar varias líneas.

/* este programa es un ejemplo */ int a,b, /* coeficientes */ err; /* error */

Programación 26/56

/* comentario de 2 lineas */ main(){ /* comienzan las sentencias */ /* hacer aqui la lectura de datos */ err=a-(a/b)*b; /* bonita formula */ /* esto no lo compilo porque esta incompleto error2= printf(); */ } /* fin de main */

Marcando como comentario una parte del programa se evita que el compilador la procese.

Esto puede ser útil para poder compilar un programa en el que algunas partes del mismo

están incompletas o con errores.

En C99 se ha añadido un nuevo tipo de comentario: "//". Este comentario afecta todo el

texto que se encuentre en la misma línea donde aparecen las dos barras y después de ellas.

A continuación volvemos a presentar el mismo código del ejemplo anterior empleando

este nuevo tipo de comentario en todas las situaciones en las que es posible

// este programa es un ejemplo int a,b, /* coeficientes */ err; // error /* comentario de 2 lineas */ main(){ // comienzan las sentencias // hacer aqui la lectura de datos err=a-(a/b)*b; // bonita formula /* esto no lo compilo porque esta incompleto error2= printf(); */ } // fin de main

6.3 Literales

Son caracteres cuyo valor es literalmente el que aparece escrito en el programa.

Los literales pueden ser numéricos o alfanuméricos. Los números que aparecen en

cualquier programa son literales numéricos. Los literales alfanuméricos son caracteres o

cadenas de caracteres, que pueden contener letras, números y otros símbolos. Los

caracteres individuales se encierran entre comillas simples ('a') y las cadenas de caracteres

entre comillas dobles ("hola"), aunque sean cadenas de un sólo carácter.

Programación 27/56

Los literales numéricos pueden estar en base 10 (decimal) u otras bases como la base 16

(hexadecimal) o la base 8 (octal). Los números pueden ser enteros o reales. Para los reales

se utiliza el punto decimal (.) y en ningún caso se utiliza la coma (,). Los números reales se

pueden escribir en notación científica, como 1.34E-18.

6.4 Palabras reservadas

Son palabras de C que tienen un uso establecido y no pueden ser utilizadas para ninguna

otra función como, por ejemplo, para ser identificadores. El compilador de C produce un

error si estas palabras se utilizan de forma no permitida. Son palabras reservadas los

nombres de tipos de datos, instrucciones y algunas más:

auto extern sizeof break float static case for struct char goto switch const if typedef continue int union default long unsigned do register void double return volatile else short while enum signed

De estas palabras, 27 existían en la versión original de C y las 5 siguientes fueron añadidas

en el ANSI C 85: const, enum, signed, void y volatile. En C99 se han añadido las palabras

inline, _Bool, _Complex, y _Imaginary. Además, cada compilador puede añadir algunas

palabras reservadas adicionales, lo que puede romper la portabilidad de los programas.

6.5 Identificadores

Son palabras incluidas en el compilador (realmente, incluidas en las librerías que tiene el

compilador) o creadas por el usuario que sirven para dar nombre a variables, funciones y

tipos de datos. Tienen que cumplir las siguientes reglas de formación:

• Su longitud debe estar entre 1 y 32 caracteres.

• Pueden contener letras, números y el símbolo _ (guión bajo). No pueden contener

espacios y se distinguen las mayúsculas de las minúsculas.

• El primer carácter debe ser una letra o _.

Programación 28/56

A diferencia de las palabras reservadas, cuyo significado no puede ser modificado por el

usuario, los identificadores incluidos en las librerías del compilador pueden ser redefinidos

(es decir, definidos con un nuevo significado), aunque no es conveniente, ya que en este

caso pierden su significado original.

6.6 Símbolos

En C se usan los símbolos siguientes:

• ; para indicar el final de una sentencia.

• {} para indicar el principio y el fin de un bloque de sentencias o de una función.

• () para indicar los argumentos de una función, para encerrar una expresión

lógica en las sentencias de control de flujo y para indicar la precedencia en

expresiones aritméticas.

• /* */ para indicar el principio y el fin de un comentario.

• # para indicar una directiva del preprocesador.

• , para separar varias variables en una declaración, varios parámetros de una

función. También puede separar varias sentencias en una secuencia de sentencias

(algo poco usado).

• . para separar el nombre de una variable unión o estructura del nombre de sus

campos.

• -> para separar el nombre de un puntero del de un campo.

• ? : en la sentencia-expresión condicional.

Además se utilizan los símbolos de operadores aritméticos, relacionales, lógicos y de bits:

Operadores Aritméticos

• = Asignación.

• + Suma.

• - Resta, signo negativo.

• * Multiplicación.

• / División (cociente); aplicado sobre datos enteros en la división de números

enteros (9/4=2).

• % División en módulo (resto).

Programación 29/56

• ++ Incremento en 1.

• -- Decremento en 1.

Operadores Relacionales

• = = Igual que.

• != Distinto que.

• > Mayor que.

• >= Mayor o igual que.

• < Menor que.

• <= Menor o igual que.

Operadores Lógicos

• && Y.

• || O.

• ! No.

Operadores de bits

• & AND

• | OR.

• ~ NOT (complemento a 1).

• ^ XOR (OR exclusivo).

• >> desplazamiento a la derecha.

• << desplazamiento a la izquierda.

A continuación indicamos la precedencia (de mayor a menor), esto es, el orden de

evaluación, de estos operadores. Ésta nos permitirá saber si al escribir 5+4*2 se ejecuta la

operación (5+4)*2 ó 5+ (4*2). También mostramos su asociatividad, es decir, el orden en

el que se evaluarán los operadores cuando haya varios que tengan la misma precedencia:

• ( ) [ ] . -> a la derecha.

• ! ~ - ++ -- & * sizeof a la izquierda.

• * / % a la derecha.

Programación 30/56

• + - a la derecha.

• << >> a la derecha.

• < <= > >= a la derecha.

• == != a la derecha.

• & a la derecha.

• ^ a la derecha.

• | a la derecha.

• && a la derecha.

• | | a la derecha.

• ?: a la izquierda.

• = += -= etc. a la izquierda.

• , a la derecha .

La precedencia disminuye de arriba a abajo. La asociatividad normal es a la derecha, por

ejemplo, a+b+c se calcula como (a+b)+c. Habitualmente, para simplificar tanto la escritura

como la lectura del programa y para evitar cometer errores, no se abusa de los órdenes de

precedencia y asociatividad por perfecto de los operadores y se emplean paréntesis (a) para

indicar el orden de las operaciones.

A continuación mostramos un código de ejemplo que muestra cómo funcionan los

operadores de incremento y decremento, así como los operadores que permiten realizar

una operación aritmética sobre una variable y asignar el resultado a esa misma variable:

/* *ejemplo4_5.c */ #include <stdio.h> main () { int entero = 1; float real = 10.0F; printf("entero: %d\n", entero); //imprimirá 1 ya que el entero se muestran antes de incrementarse printf("entero++: %d\n", entero++); //pero después de ejecutar la sentencia entero vale 2 printf("entero: %d\n", entero); //imprimirá 3 ya que se incrementa antes de mostrarse

Programación 31/56

printf("++entero: %d\n", ++entero); //imprimirá 3 ya que el entero se muestra antes debe incrementarse printf("entero--: %d\n", entero--); //pero tras ejecutar la sentencia entero vale 2 printf("entero: %d\n", entero); //imprimirá 1 ya que se decrementa antes de mostrarse printf("--entero: %d\n", --entero); printf("real: %f\n",real); //equivalente a real = real + 2; real += 2; printf("real += 2: %f\n",real); //equivalente a real = real - 6; real -= 6; printf("real -= 6: %f\n",real); //equivalente a real = real * 3; real *= 3; printf("real *= 3: %f\n",real); //equivalente a real = real / 9; real /= 9; printf("real /= 9: %f\n",real); system ("pause"); }

es continuación presentamos un código de ejemplo que muestra cómo emplear variables

enteras para representar valores lógicos y como los operadores lógicos actúan sobre ellas:

/* *ejemplo4_6.c */ #include <stdio.h> main () { int variableLogica = 0, variableLogica2; variableLogica2 = !variableLogica; printf("variableLogica: %d, variableLogica2: %d\n", variableLogica,variableLogica2); printf("variableLogica&&variableLogica2: %d\n", variableLogica&&variableLogica2); printf("variableLogica||variableLogica2: %d\n", variableLogica||variableLogica2); printf("\n\nTabla de verdad del operador &&:\n"); printf("0 && 0: %d\n",0&&0); printf("0 && 1: %d\n",0&&1); printf("1 && 0: %d\n",1&&0);

Programación 32/56

printf("1 && 1: %d\n",1&&1); printf("\n\nTabla de verdad del operador ||:\n"); printf("0 || 0: %d\n",0||0); printf("0 || 1: %d\n",0||1); printf("1 || 0: %d\n",1||0); printf("1 || 1: %d\n",1||1); system ("pause"); }

7 Sentencias

Las sentencias de un programa especifican una acción a realizar. Las sentencias en C

suelen finalizar con un punto y coma. Una expresión es una secuencia de operadores y

operandos que especifica un valor. Por ejemplo, la sentencia 4+5; está formada por la

expresión 4+5, que utiliza el operador suma sobre dos operandos constantes enteras y cuyo

valor es 9.

En un programa C hay una parte de declaraciones y otra de instrucciones. En la parte de

declaraciones se pueden utilizar sentencias para:

• Declaración de tipos de datos.

• Declaración de variables.

• Declaración de funciones.

Y en la parte de instrucciones, se utilizan tres tipos principales de sentencias:

• Sentencias de asignación.

• Sentencias de control.

• Llamadas a funciones.

Programación 33/56

8 Sentencias de control de flujo

Las sentencias que se tratan en este apartado permiten realizar las estructuras de

bifurcación y bucle estudiadas en el tema sobre algoritmos. Permiten, por tanto, construir

programas cuyo flujo de ejecución sea más versátil que la ejecución secuencial.

8.1 Bloque de sentencias

Se denomina bloque de sentencias a un grupo de sentencias cualquiera encerradas entre

llaves { }. Los bloques de sentencias sirven para agrupar varias sentencias bajo el control

de otra (bifurcación, bucle). En cualquier parte del programa donde pueda ir una sentencia,

puede ir también un bloque de sentencias.

8.2 Sentencia condicional simple : if

La sentencia if se utiliza para hacer una bifurcación en el flujo del programa. Puede

aparecer de modo aislado o junto con la sentencia else:

if (condicion) if (condicion) sentencia1; sentencia1; else sentencia2;

En el ejemplo de la izquierda, si la condición es cierta, se ejecuta la sentencia1; si es falsa,

se salta. En cualquier caso, después se ejecutan las sentencias que sigan al if. En el de la

derecha, si la condición es cierta, se ejecuta la sentencia1 y si es falsa, se ejecuta la

sentencia2. Después se ejecutan las sentencias que sigan al if. Obsérvese que o bien se

ejecutan las sentencias de la parte if, o las de la parte else, nunca ambos grupos. Como se

aprecia en el ejemplo de la izquierda, la parte de else es opcional. En caso de ser necesarias

varias sentencias en la parte if o en la else se utilizará un bloque de sentencias, como se

muestra en el siguiente ejemplo:

if (a>0) { printf("a es positivo"); b=a; } else printf("a es negativo");

Programación 34/56

En el ejemplo anterior, la rama if utiliza un bloque de sentencias, que va desde la llave {

hasta la }. La rama else comienza justo después de dicha palabra y termina en el primer

punto y coma después de ella. Para saber el alcance de una sentencia if o else se sigue la

siguiente regla: si no se utiliza un bloque de sentencias, tanto la rama if como la else

terminan en el primer punto y coma después de dichas palabras. Por tanto, en el ejemplo

siguiente

if (V1>0) V2=2*PI*R*V1/60; printf("Vel lineal: %f\n",V2);

la sentencia printf no está comprendida dentro del if, ya que éste termina en el punto y

coma que sigue al cálculo de V2. Si se coloca una sentencia else después del printf, el

compilador no la reconocerá como parte del if y se producirá un error. Aunque no es

obligatorio, es recomendable emplear siempre las llaves detrás del if y del else; de este

modo se incrementa la legibilidad del código y disminuye la posibilidad de cometer errores

en el futuro cuando, por ejemplo, añadamos más sentencias que estén bajo la acción del if.

A continuación mostramos el diagrama de flujo correspondiente con un condicional tipo if

else:

El formato de espacios o saltos de línea no influyen en la ejecución de la sentencia, sólo se

utilizan para que la estructura del programa resulte más clara al leerlo. Normalmente, las

sentencias que se ejecutan bajo el control de otra se indentan dos espacios hacia la derecha.

Programación 35/56

Por lo tanto, la sentencia printf del último ejemplo, que no pertenece al if, debería

comenzar dos espacios hacia atrás.

8.2.1 Expresiones condicionales

Se denomina expresión condicional a la que se evalúa como VERDADERO o FALSO.

Generalmente relaciona 2 o más expresiones mediante operadores relacionales y/o lógicos.

Los operadores relacionales se utilizan para comparar expresiones numéricas, como b*b -

4*a*c > 0 o K<=sin(x). Los operadores lógicos se utilizan hacer una expresión

condicional compleja a partir de condiciones más simples, por ejemplo, la condición de la

sentencia if siguiente :

if (aciertos>=11 && sellada==1) printf(" Tengo premio ! ");

En C, cualquier expresión puede ser una expresión condicional. A diferencia de otros

lenguajes, no existe un tipo de datos para valores lógicos ni una sintaxis estricta para

dichas expresiones. Simplemente, cualquier expresión que de un resultado igual a 0 se

considera falsa y si da un resultado distinto de 0, es verdadera. Por ello, es posible ver

programas con sentencias como la siguiente:

if (a-5) x=x+1;

Esta condición es cero (FALSO) cuando a vale 5, y distinto de cero (VERDADERO) en

otro caso. Por ello es equivalente a la condición a!=5. Por claridad, es preferible utilizar la

comparación en lugar de la operación aritmética. En el ejemplo siguiente, las dos

condiciones son equivalentes:

if (opcion!=0) printf("Calculando..."); if (opcion) printf("Calculando...");

aunque la primera es más legible.

Es frecuente confundir el operador de asignación ( = ) con el de comparación ( == ). En las

expresiones lógicas hay que utilizar el de comparación, si no, la sentencia no funcionará

correctamente. Por ejemplo, en la sentencia

Programación 36/56

if (a=5) printf("Hola")

se asigna el valor 5 a la variable "a", perdiéndose el que tuviese antes. El valor de esta

expresión es 5, por lo tanto su valor lógico es CIERTO y siempre se ejecutará el printf.

8.2.2 Sentencias if anidadas

Se dice que una sentencia if está anidada si se ejecuta dentro de una de las ramas de otra

sentencia if. El programa siguiente muestra un grupo de sentencias if anidadas para

traducir una nota numérica a un mensaje.

/* *ejemplo4_7.c */ #include <stdio.h> float Nota; main() { printf("\nDame la nota numerica : "); scanf("%f",&Nota); if (Nota<5) { printf("\nSuspenso\n"); } else { if (Nota<7) { printf("\nAprobado\n"); } else if (Nota<8.5) { printf("\nNotable\n"); } else if (Nota<9.5) { printf("\nSobresaliente\n"); } else { printf("\nMatricula de Honor\n"); } } system("pause"); }

Si se quisiera añadir dos mensajes, uno de “Por los pelos...” para los aprobados con nota

menor de 5.5 y uno de “Que fiera..” para los alumnos con sobresaliente o matricula, habría

que dejar el programa así:

/* *ejemplo4_8.c */ #include <stdio.h> float Nota; main()

Programación 37/56

{ printf("\nDame la nota numerica : "); scanf("%f",&Nota); if (Nota<5) { printf("\nSuspenso\n"); } else { if (Nota<7) { printf("\nAprobado\n"); if (Nota<5.5){ printf("Por los pelos...\n"); } } else if (Nota<8.5) { printf("\nNotable\n"); } else if (Nota<9.5) { printf("\nSobresaliente\n"); } else { printf("\nMatricula de Honor\n"); printf("Que fiera...\n"); } } system("pause"); }

8.3 Bucles en C

Un bucle está formado por un conjunto de sentencias que se repiten bajo el control de una

condición. A diferencia de otros lenguajes como BASIC o PASCAL, donde las sentencias

del bucle se repiten hasta que una condición es cierta, en C se repiten mientras la condición

es cierta. Por lo tanto, hay que buscar una condición adecuada para que el bucle se

comporte como deseamos. Los bucles pueden llevar la condición al principio o al final.

Los bucles con la condición al final se realizan con la sentencia do - while y los que llevan

la condición al principio con las sentencias while y for.

8.3.1 Bucle do-while

La forma general de un bucle do-while es:

do sentencia; while (condicion);

Programación 38/56

En caso de querer incluir más de una sentencia, se utiliza un bloque de sentencias. El

principio del bucle es la sentencia do y el final la sentencia while. Las sentencias entre

ambas son el cuerpo del bucle. La ejecución de este bucle es como sigue: se ejecutan las

sentencias que siguen al do, hasta el while. Se comprueba la condición. Si es cierta, se

vuelve a la primera sentencia del bucle. Si es falsa, se sale del bucle y se ejecuta la

sentencia que sigue al while. Como la condición se comprueba al final, el bucle siempre se

ejecuta al menos 1 vez. Veamos un ejemplo:

/* *ejemplo4_9.c */ #include <stdio.h> #include <stdlib.h> #include <time.h> main() { intr; srand(time(NULL)); do { r =rand(); printf("%g\n",r); } while(r <RAND_MAX- RAND_MAX/100); }

El bucle anterior se repite mientras el número aleatorio deseado sea menor que

RAND_MAX- RAND_MAX/100. A continuación presentamos el diagrama de flujo

correspondiente a este programa:

Programación 39/56

8.3.2 Bucle while

Es similar al anterior, pero con la condición al principio. En este caso, se prueba la

condición antes de ejecutar la primera vez el bucle. Si la condición es falsa inicialmente, el

bucle no se ejecuta ninguna vez.

while (condicion) sentencia;

Nuevamente, en caso de requerir más de una sentencia, se utiliza un bloque de sentencias.

Mostramos el código del ejemplo anterior rehecho con este nuevo tipo de bucle:

/* *ejemplo4_10.c */ #include <stdio.h> #include <stdlib.h> #include <time.h> main() { srand(time(NULL)); int r=0; while(r <RAND_MAX- RAND_MAX/100) { r =rand(); printf("%g\n",r); } }

Programación 40/56

Su diagrama de flujo correspondiente será:

8.3.3 Bucle for

Es un bucle con la condición al principio, como el while, pero que además permite incluir

una sentencia de inicialización del bucle y otra de actualización del mismo. Estos tres

elementos se separan por un punto y coma.

for (expresion_inicio ; condicion ; expresion_actualizacion) { sentencia; }

Todo bucle for es equivalente a un bucle while con la expresión inicial antes del bucle y la

expresión de actualización al final del mismo.

expresion_inicio; while (condicion) { sentencia; expresion_actualizacion; }

La expresión de inicialización se ejecuta una sola vez al comenzar el bucle. La expresión

de actualización se ejecuta cada vez que se llega al final del bucle, antes de comprobar la

Programación 41/56

condición. Debido a esta característica, el bucle for se utiliza con frecuencia para bucles

con contadores fijos como, por ejemplo, este bucle for para sumar los cuadrados de los

números de 1 a 10 y bucle while equivalente:

for (i=1 ; i<11 ; i=i+1) { cuadrado=i*i; suma=suma+cuadrado; }

i=1; while(i<11) { cuadrado=i*i; suma=suma+cuadrado; i=i+1; }

La utilización de for en lugar de while se hace sólo por claridad, al estar agrupadas las

sentencias de control del bucle en una misma línea. A continuación mostramos el diagrama

de flujo correspondiente con este bucle:

Programación 42/56

En un bucle for puede omitirse cualquiera de las dos sentencias, o la condición, o todas

ellas; por ejemplo, es válido el siguiente bucle:

for ( ; ; ) { sentencia1; sentencia2; }

En este caso, el bucle no termina nunca, salvo que una sentencia interna break o exit (que

veremos más tarde en este tema) lo haga terminar. Si se desea que en un bucle se inicialice

o actualice más de una variable se utiliza la secuencia de expresiones. Consiste en separar

las diferentes sentencias por una coma ( , ), como en el ejemplo siguiente:

for (i=0,j=1,s=0,a=0 ; i<10 ; i=i+1,j=j+i*2) { s=s+i; a=a+j; }

A continuación mostramos un programa que imprime los 127 primeros caracteres ASCII

junto con su valor entero equivalente:

/* *ejemplo4_11.c */ #include <stdio.h> #include <stdlib.h> main() { char c; for( c = 0; c < 127; c++){ printf("E valor: %i se corresponde con el carácter %c\n",(int)c,c); } }

8.3.4 Bucles anidados

Se dice que un bucle está anidado cuando está dentro de otro bucle más amplio. En esta

estructura, el bucle interno se ejecuta completamente (todas las repeticiones) para cada

paso del bucle externo. Este tipo de construcciones son muy empleadas para procesar

matrices, y para otros programas, como el siguiente, que escribe en pantalla una tabla de

potencias:

Programación 43/56

/* *ejemplo4_12.c */ #include <stdio.h> #include "math.h" #define MAXBASE 15 #define MAXEXP 7 int base,exponente; main() { /* cabecera de la tabla, con los exponentes y una raya */ printf("\n\n\n "); for (exponente=1;exponente<=MAXEXP;exponente++) printf("%10i",exponente); printf("\n-------------------------------------------" "-------------------------------\n"); /* bucles anidados */ for (base=1;base<=MAXBASE;base++) { printf("%2d %c",base,179); /* base y palito (ASCII 179) */ for (exponente=1;exponente<=MAXEXP;exponente++) printf("%10li",(long) pow(base,exponente)); printf("\n"); } }

El bucle externo hace variar la base. Para cada base (cada paso del bucle externo), el bucle

interno hace variar el exponente de forma que tome todos sus valores. Esto se repite para

todos los valores de la base. La función pow(x,y) calcula x elevado a y; está definida en

math.h. Al imprimir el resultado, se convierte de double, que es el que devuelve la función,

a long, mediante el uso de un molde o cast.

8.3.5 Sentencias break y continue

A veces resulta necesario abandonar un bucle por alguna condición que se verifica cuando

se está dentro del bloque de código, sin esperar a evaluar la condición del bucle. Para salir

de un bucle puede utilizarse la sentencia break, en combinación con un if, como en el

siguiente ejemplo:

/* *ejemplo4_13.c */ #include <stdio.h> #include <stdlib.h> #include <time.h> int secreto,numero; main() { srand(time(NULL));

Programación 44/56

secreto=rand()% 10; printf("\nAdivina el Numero secreto." " Para salir da el numero 0\n\n"); do { printf("\nDame un numero del 1 al 10 : "); scanf("%d",&numero); if (numero==0) break; /* == corta el bucle == */ if (numero!=secreto) { printf("Ese no es el numero secreto."); if (secreto>numero) printf("El numero secreto es mayor.\n"); else printf("El numero secreto es menor.\n"); } else printf(" Has acertado ! \n"); } while (numero!=secreto); }

La sentencia continue se utiliza dentro de un bucle para iniciar una nueva iteración del

mismo, saltándose las sentencias que la siguen. Es equivalente a un salto al final del bucle,

por tanto, si es un bucle for, se ejecuta la sentencia de actualización (aunque está colocada

al principio, se ejecuta al final del bucle) y si es un bucle do while se comprueba la

condición.

El siguiente programa salta los números impares, con una sentencia continue, y

descompone los pares hasta saber si son potencias de 2 o no:

/* *ejemplo4_14.c */ #include <stdio.h> Unsigned int num,pot; main() { for (num=1;num<64000;num++) { if (num%2!=0) continue; /* == siguiente iteracion == */ pot=num; printf("\n\n %u ",pot); while (pot%2==0) { pot=pot/2; printf("%u ",pot); } if (pot==1) printf("\n %u es potencia de 2\n",num); else printf("\n %u no es potencia de 2\n",num); } }

Programación 45/56

8.4 Bifurcación múltiple: sentencia switch

La bifurcación múltiple permite que un programa tome distintas acciones según el valor de

una expresión de tipo int o char. Se realiza mediante la sentencia switch, que tiene la

siguiente sintaxis:

switch(selector) { case valor1 : Grupo de sentencias1; break; case valor2 : Grupo de sentencias2; break; case valor3 : Grupo de sentencias3; break; case valor4 : Grupo de sentencias4; break; case valor5 : Grupo de sentencias5; break; // ... default: statement; }

Dentro de los paréntesis del switch se coloca la expresión. Para cada valor para el cual se

desea ejecutar un conjunto diferente de sentencias se pone una sentencia case seguida de

dicho valor (en forma de constante, no sirve una variable). Cuando se ejecuta la sentencia

switch, se compara el valor de la expresión con cada constante por orden, y si coincide con

una, se ejecutan las sentencias que siguen al case. Una vez que se verifica una sentencia

case, se ejecutan las sentencias asociadas y las de todos los case que haya a

continuación.

Para cortar la ejecución de las sentencias y salir del switch, puede ponerse donde resulte

oportuno una sentencia break, esto es, un salto incondicional fuera del switch (la sentencia

break será explicada en detalle más adelante en este tema). Si para varios valores de la

expresión deben ejecutarse las mismas sentencias, pueden ponerse los case seguidos, con

las sentencias en el último de ellos. La etiqueta default es opcional y, si se llega a ella (si

no se sale con un break), siempre se ejecuta sea cual sea el valor de la expresión. A

continuación mostramos el diagrama de control de flujo correspondiente con un switch:

Programación 46/56

Veamos un ejemplo de uso de esta estructura de control de flujo:

/* *ejemplo4_15.c */ #include <stdio.h> int mes, dias; main() { printf("\n\nDame el numero del mes : "); scanf("%d",&mes); switch(mes) { case 2 : dias=28; break; case 4 : case 6 : case 9 : case 11 : dias=30; break; default : dias=31; } printf("El mes %d tiene %d dias\n",mes,dias); }

Cuando el mes es 2, días toma el valor 28 y con el break se sale del switch. Si es 4, 6, 9 o

11, toma el valor 30 y se sale. Cualquier otro valor hace que tome el valor 31. Veamos a

continuación otro ejemplo; en esta ocasión se generan caracteres aleatorios y el programa

determina si los caracteres generados son consonantes o vocales:

Programación 47/56

/* *ejemplo4_15.c */ #include <stdio.h> #include <time.h> int mes, dias; main() { int i = 0; srand(time(NULL)); for(i = 0; i < 100; i++) { char c = (char)(rand()%26 + 'a'); printf("%c: ",c); switch(c) { case 'a': case 'e': case 'i': case 'o': case 'u': printf("vocal\n"); break; default: printf("consonante\n"); } } }

8.5 El operador condicional

Aunque estrictamente hablando no es una sentencia de control de flujo sino un operador, el

operador condicional permite elegir de entre dos expresiones cuál de ellas se ejecuta y

devuelve el resultado de la ejecución de dicha sentencia. Este operador es muy similar a un

if-else. Veamos su sintaxis:

expresión1 ? expresión2 : expresión3;

esta sentencia es análoga a:

if( expresión1 ) expresión2; else expresión3;

Veamos un ejemplo donde se emplea el operador condicional para imprimir el mayor y el

menor de dos números perdidos al usuario:

/*

Programación 48/56

*ejemplo4_17.c */ #include <stdio.h> int main(void) { int n1, n2, mayor, menor; printf("Primer número: "); scanf("%d", &n1); printf("Segundo número: "); scanf("%d", &n2); mayor = (n1 > n2) ? n1 : n2; menor = (n1 < n2) ? n1 : n2; printf("El mayor es %d\n", mayor); printf("El menor es %d\n", menor); }

Si las expresiones 2 y 3 del operador condicional son compuestas, cada proposición debe

estar separada por coma ','. Por ejemplo:

(radio > 4) ? printf("\n Funciona\n"), printf("A ver??\n") : printf("\n Es más pequeño");

El empleo de un operador condicional es más compacto que el empleo de la estructura if-

else; sin embargo la segunda resulta mucho más legible que la primera, por lo que se

recomienda no emplear el operador condicional.

8.6 Terminación del programa: función exit()

El objetivo de este apartado no es ni un operador ni una estructura de control de flujo, sino

una función que influye sobre el flujo de ejecución del programa. Esta función puede

utilizarse en cualquier punto de un programa para terminar su ejecución. Su sintaxis es:

exit(N);

donde N es un entero cualquiera. El valor de N se devuelve al Sistema Operativo como

código de retorno del programa; no tiene importancia para el programa en sí. Este valor

puede utilizarse desde el S.O.; por ejemplo, en MS-DOS puede averiguarse con la

Programación 49/56

instrucción ERRORLEVEL y utilizarse en un fichero .BAT. La función exit() está definida

en stdlib.h y en process.h. Veamos un ejemplo:

/* *ejemplo4_18.c */ #include <stdio.h> #include <stdlib.h> unsigned char caracter; main(){ printf("\n\nTeclea caracteres, pulsa q para salir.\n\n"); while (1) { caracter=getchar(); printf(" ASCII %d \n",caracter); if (caracter==113) exit(0); } }

El bucle while del programa es infinito; para salir de él (y terminar el programa) se emplea

la función exit().

Programación 50/56

9 Ejercicios

1. Escribir un programa que defina una variable en la que se almacene un número de

meses determinado, otra en la que se almacene un número de días, y otra la que se

almacene un número de horas. Darle un valor inicial cualquiera a estas variables y

a continuación calcular cuántos segundos son los meses, días y horas almacenados

en esas variables.

2. Escribir un programa que defina variables que representen el número de días de un

año, el número de horas que tiene un día, el número de minutos que tiene una hora

y el número de segundos que tiene un minuto. Emplear las variables que ocupen el

mínimo espacio de memoria posible. A continuación, calcular el número de

segundos que tiene un año y almacenar el valor del cálculo en otra variable.

3. Escribir un programa que almacene los coeficientes de una función de segundo

grado (ax2+bx+c) en variables reales. Inicializar con cualquier valor dichas

variables y a continuación calcular el valor de la función definida en los puntos x=

42.32 y x=27.35.

4. Modificar el programa anterior de tal modo que sólo se imprima la parte entera del

valor de la función en los mismos puntos que en el apartado anterior.

5. Escribir un programa que defina tres variables de tipo carácter contengan,

respectivamente, los caracteres J,n,8 y ?. Almacenar en una variable el valor de la

suma de las cuatro variables tipo carácter y comprobar mediante la tabla que se

proporciona en este tema el resultado de dicha suma.

6. Calcular el factorial de un número introducido por teclado. Antes de realizar el

cálculo deberá comprobarse que el número es positivo y en caso contrario se

imprimirá un mensaje de error.

7. Escribe un programa que calcule el mínimo y el máximo de una lista de números

enteros positivos introducidos por usuario. La lista finalizará cuando se introduzca

un número negativo.

8. Escribe un programa que lea un mes en número (1 para enero, 2 para febrero, etc.)

y un año e indique el número de días de ese mes. Recuerda, el año es bisiesto si es

divisible por cuatro, excepto cuando es divisible por 100, a no ser que sea divisible

por 400.

Programación 51/56

9. Escribe un programa que lea un número entero de teclado y lo descomponga en

factores primos; por ejemplo 40 = 2 * 2 * 2 * 5.

10. Escribe un programa que muestre por pantalla la lista de los 100 primeros números

primos.

11. Escribe un programa que lea un número entero y que devuelva su correspondiente

representación binaria usando la técnica de divisiones sucesivas.

12. Escribe un programa que realice las operaciones de suma, resta, multiplicación y

división. El programa deberá mostrar siguiente menú:

Programa calculadora

1) Realizar una suma

2) Realizar una resta

3) Realizar una multiplicación

4) Realizar una división

5) Salir del programa

Introduzca su opción:

Ayuda para los ejercicios:

int main(){ int i; float k; printf ("Imprime el valor entero de la variable i: %d",i); printf ("Imprime el valor entero de la variable i: %f",k); //leemos un dato entero de la consola y lo almacenamos en la //variable i scanf ("%d",&i); //leemos un dato real de la consola y lo almacenamos en la //variable k scanf ("%f",&k); }

Programación 52/56

10 Apéndice I: sistemas binario, octal y hexadecimal

Los sistemas de numeración son conjuntos de símbolos y reglas empleados para

representar información numérica. Se denomina sistemas posicionales a aquellos sistemas

de numeración en los que el valor de un símbolo depende de su valor absoluto y de su

posición relativa a la coma decimal.

El teorema fundamental de la numeración nos dice que: “En un sistema posicional

cualquier número puede expresarse mediante su polinomio equivalente”

NúmeroB = xixi-1... x2 x1x0 = xi*Bi + xi-1*Bi-1 +...+ x2*B2 + x1*B1 + x0*B0

donde:

• B es la base

• xi son los coeficientes, siendo todos ellos mayores o igual que cero y menores o

igual que la base.

• i es el índice

Existen básicamente 3 tipos de sistemas de numeración, a parte del sistema decimal que

todos conocemos y estamos acostumbrados a usar:

• Sistema Binario. Se compone de 2 dígitos: 0 y 1. Los primeros números de este

sistema son 0,1,10,11,100,101,110,...

• Sistema Octal. Se compone de 8 dígitos: 0,1,2,3,4,5,6,7. Los primeros números de

este sistema son: 0,1,2,3,4,5,6,7, 10, 11,..., 17, 20,...

• Sistema Hexadecimal. Se compone de 15 dígitos: 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F.

los primeros números de este sistema son: 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, 10,

11...,1A,1B,...,1F, 20,...

Programación 53/56

10.1 Conversiones entre bases

Para convertir un número de base B a base decimal basta con convertir el número inicial

en su polinomio equivalente y realizar el cálculo correspondiente operando en decimal.

Para convertir un número de base B a base B’ (siendo B’ distinto de B10) se suele emplear

B10 como intermediario: primero se pasa el NúmeroB a Número10 y a continuación se pasa

de Número10 a NúmeroB’. Para ello se divide sucesivamente el número decimal entre la

nueva base; la representación del número en la nueva base será el conjunto de los restos

obtenidos en las divisiones tomados en orden inverso.

Para convertir entre dos bases B y B’ tales que B’ es potencia exacta de B no es necesario

utilizar B10 como intermediario. Para realizar la conversión basta con agrupar los dígitos

del número en base B de n en n, siendo n tal que Bn= B’ y a continuación sustituir cada

grupo de dígitos por el dígito correspondiente en base B’. Para convertir de B’ a B

simplemente cambiamos cada digito de la primera base por el número correspondiente en

la segunda base (B) representado éste último con n dígitos.

10.2 Conversión Binario - Hexadecimal

Cuando se tiene un número en binario y se desea

pasarlo al sistema hexadecimal se forman parejas

de 4 en 4 empezando de derecha a izquierda. A

continuación sustituimos cada grupo de cuatro 1s y

0s por su número se ha decimal correspondiente.

Por ejemplo, para pasar el número 111011101 a

hexadecimal:

1 , 1 1 0 1 , 1 1 0 1 = 1 D D

En la tabla mostramos el número binario

correspondiente con cada uno de los 16 dígitos del

código hexadecimal.

Programación 54/56

10.3 Conversión Hexadecimal - Binario

Para transformar un número hexadecimal a binario simplemente cogemos cada dígito del

número hexadecimal y los sustituimos por un conjunto de cuatro unos y ceros que

representen el dígito en binario. Así, por ejemplo, para pasar el número F87 a binario

simplemente tenemos en cuenta que F= 1111, 8= 1000 y 7= 0111 y por tanto F87= 1111,

1000, 0111.

10.4 Conversión Binario - Decimal

Cuando se tiene un número en binario y se desea pasarlo al sistema decimal se toma cada

uno de los dígitos empezando por la última posición hasta la primera y multiplicamos cada

dígito por la respectiva posición de la potencia de 2. Por ejemplo, para pasar el número

111011101 a decimal

1 1 1 0 1 1 1 0 1

2^0 x 1 = 1 x 1 = 1

2^1 x 0 = 2 x 0 = 0

2^2 x 1 = 4 x 1 = 4

2^3 x 1 = 8 x 1 = 8

2^4 x 1 = 16 x 1 = 16

2^5 x 0 = 32 x 1 = 0

2^6 x 1 = 64 x 1 = 64

2^7x 1 = 128 x 1 = 128

2^8 x 1 = 256 x 1 = 256

Y sumando obtenemos 477

Programación 55/56

10.5 Conversión Decimal - Binario

Para transformar un número decimal al sistema binario, se realizan divisiones enteras

sucesivas entre 2, hasta que el resultado sea cero. El número binario correspondiente es el

conjunto de todos los restos de la división tomados en orden inverso. Por ejemplo, para

pasar a binario él número 67:

33 16 8 4 2 1 0

2 | 67 2 | 33 2 | 16 2 | 8 2 | 4 2 | 2 2| 1

1 1 0 0 0 0 1

Por tanto, 67 =1000011.

10.6 Conversión Decimal - Octal

Para transformar un número decimal al sistema octal, se realizan divisiones enteras

sucesivas entre 8, hasta que el resultado sea cero. El número es tal correspondiente se

obtiene tomando todos los restos de la división tomados en orden inverso. Por ejemplo,

para pasar a octal el número 63:

7 0

8 | 63 8 | 7

7 7

Entonces 63 10 =77.

10.7 Conversión Decimal – Hexadecimal

Para transformar un número decimal al sistema hexadecimal, se realizan divisiones enteras

sucesivas entre 16 hasta que el resultado sea cero. A continuación se sustituye cada uno de

los restos por el número se ha decimal correspondiente y se toman en orden inverso. Por

ejemplo pasar a hexadecimal el número 1000:

62 3 0

Programación 56/56

16 | 1000 16 | 62 16 | 3

8 14=E 3

por tanto, 1000 = 3E8.

10.8 Conversión Binario – Octal

Cuando se tiene un número en binario y se desea pasarlo al sistema octal se forman parejas

de 3 en 3 empezando de derecha a izquierda y a continuación se sustituye cada grupo de 1

y 0s por su respectivo número tal. Por ejemplo, para pasar el número 111011101

1 1 1 , 0 1 1 , 1 0 12 =7 3 5 8