Está en la página 1de 432

Capítulo

Modelo de Von Neumann


"Las computadoras en el futuro podrían tener 1,000 tubos de
vacío y probablemente pesar 1.5 toneladas"

1949, Popular Mechanics Magazine.

OBJETIVO:
Al finalizar este capítulo el estudiante:

o Identificará los elementos que integran el Modelo de Von Neumann, así como los procesos que
se llevan a cabo en cada uno de ellos, con el fin de explicar el funcionamiento de una
computadora al ejecutar un programa.

1.1 INTRODUCCIÓN
Existen diferentes mitos alrededor de las computadoras, uno de los más comunes es creer que “son
capaces de pensar” (nada más lejos de la realidad); las computadoras son artefactos “tontos” a los que
es necesario decirles paso a paso que deben hacer, su potencial radica en la cantidad de datos que
pueden manipular por segundo, aunado a la capacidad de información que pueden almacenar. Ahora
bien, si las computadoras no son capaces de resolver un problema por sí mismas, entonces ¿cómo
podemos instruirlas para que sean capaces de resolver un problema específico? Para que una
computadora resuelva un problema requiere de un conjunto de instrucciones escritas en un lenguaje de
programación1, lo cual se conoce como programa.
Partiendo de la premisa que “es más fácil comunicarse con una persona a la que conocemos que con
un desconocido”, si nuestro fin es comunicarle a la computadora cómo debe solucionar un problema
mediante un programa, resulta conveniente conocer los procedimientos que ésta realiza al ejecutarlo.

A lo largo de este capítulo se dará un panorama general de la manera en la que las computadoras
ejecutan un programa. Basándonos en el Modelo de Von Neumann - arquitectura base de las
computadoras actuales- describiremos algunos aspectos básicos relacionados las computadoras, como
por ejemplo, ¿qué son?, ¿cómo están constituidas?, ¿cómo almacenan la información?, ¿cómo se
comunican?, en esencia, ¿cómo funcionan?

1Lenguaje de programación. “Los lenguajes de programación sirven para escribir programas que permiten la comunicación usuario-
máquina.” (Página 21,[ Joyanes,2003])

Capítulo 1 | Página 1
1.2 ESTRUCTURA DE UNA COMPUTADORA
Una computadora es un equipo electrónico —llamado hardware—, que recibe instrucciones en forma
de programas, también llamados software, para resolver diferentes tareas. La computadora recibe datos
de entrada proporcionados por el usuario, y los procesa de acuerdo al conjunto de instrucciones
definidas por el programa, para obtener y entregar los datos de salida que son el resultado del proceso.

Por el momento, podemos considerar a las computadoras como una caja negra 2 cuyos datos de salida
dependen de la programación y de los datos de entrada, como se observa en la Figura 1.1.

Programa

Datos COMPUTADORA Datos


de entrada de salida

Figura 1.1. La computadora como caja negra

La primera computadora digital totalmente electrónica fue la “ENIAC” (siglas de su nombre en inglés:
Electronic Numerical Integrator and Computer), elaborada por John Presper Eckert y John W.
Mauchly entre 1942 y 1945. Este invento rompe la atadura que tenía el hardware con la aplicación,
pues podría programarse para resolver diferentes problemas que involucraran cálculos. En este sentido,
se trataba de un componente de propósito general, en el cual las instrucciones eran introducidas
cambiando el cableado, es decir, se sustituían y reconstruían algunas partes del equipo o hardware para
la instalación de cada nuevo programa.

En 1945, el matemático John Von Neumann publicó el artículo First Draft of a Reporter: on the
EDVAC, donde consideraba que las instrucciones no debían proporcionarse por medio de cables, ya
que resultaba poco práctico; por lo tanto, propuso que el programa fuera almacenado en la memoria
principal, al igual que se hacía con los datos. El modelo propuesto para implementar esta innovación
se conoce como Arquitectura o Modelo de Von Neumann, el cual representa la base sobre la que
actualmente continúan construyéndose las computadoras.

La Arquitectura de Von Neumann implica que, tanto el programa como los datos de entrada, deben ser
proporcionados por el operador o usuario a través de unidades de hardware llamadas dispositivos de
entrada, para después ser almacenados en la memoria. Cuando se ejecuta un programa, la Unidad de
Control lee una a una las instrucciones almacenadas en memoria, las interpreta y envía las señales
adecuadas a los dispositivos que deben intervenir en la ejecución de las mismas, estos pueden ser la
2Las consideramos cajas negras porque no podemos ver su contenido; la única información que tenemos acerca de ellas es qué datos de
entrada reciben y qué resultados devuelven.
Capítulo I | Página 2
unidad aritmético lógica, la memoria y los dispositivos de entrada y/o salida. En resumen, el Modelo
de Von Neumann consta de las siguientes unidades elementales: la memoria, los dispositivos de
entrada / salida y la unidad central de procesamiento, integrada por la unidad aritmética-lógica y la
unidad de control, como se muestra en la Figura 1.2.

Figura 1.2. Modelo de Von Neumann

A continuación se describe brevemente la función de cada uno de los elementos que integran el Modelo
de Von Neumann:

 Unidad Central de Procesamiento (CPU, Central Process Unit) controla y coordina la ejecución
de las instrucciones, para ello utiliza la Unidad Aritmético-Lógica encargada del procesamiento
de los datos y la Unidad de Control para el procesamiento de las instrucciones.

o La Unidad Aritmético-Lógica (ALU, Arithmetic Logic Unit) realiza todas las operaciones
aritméticas (suma y resta) y lógicas (operaciones del Algebra de Boole). Además de los
circuitos que le permiten realizar dichas operaciones, la ALU incluye un elemento auxiliar
donde se almacenan temporalmente los datos que manipula conocido como acumulador o
Registro Temporal (TR, Temporal Register).

o La Unidad de Control (CU, Control Unit) se encarga de leer las instrucciones almacenadas
en memoria, decodificarlas y después enviar las señales a los componentes que están
involucrados en su ejecución, para lo cual tiene dos elementos auxiliares el Contador del
Programa (PC, Program Counter) y el Registro de Instrucción (IR, Instruction Register). En
el IR se guarda temporalmente la instrucción que debe ser ejecutada, mientras que en el PC se
almacena la dirección de memoria que contiene la siguiente instrucción que se ejecutará.
Capítulo I | Página 3
 La memoria principal es la parte de la computadora donde se almacenan los datos y las
instrucciones durante la ejecución de un programa. Físicamente está compuesta por circuitos
integrados. Las computadoras actuales cuentan con un área de memoria de sólo lectura - a la que
se le conoce como memoria de tipo ROM (Read Only Memory)- y otra en la cual es posible escribir
y leer datos - denominada de tipo RAM (Random Access Memory). La memoria RAM tiene el
inconveniente de ser volátil pues al apagarse la computadora los datos almacenados se pierden.

La memoria está dividida en varias celdas, en cada una de ellas se puede almacenar únicamente 0
ó 1, a estos valores se les denomina valores binarios o BITs 3 (BInary digiT). De este modo, un
bit se considera la mínima unidad de información que una computadora puede almacenar; sin
embargo, para poder almacenar un dato o instrucción se requiere de más de un bit. Por esta razón
las celdas se agrupan para formar palabras, a cada una le corresponde una dirección de memoria
de tal manera que, cuando se desea escribir o leer un dato o una instrucción de la memoria, se debe
especificar la dirección dónde se encuentra. En el siguiente capítulo profundizaremos en la forma
en la cual se codifican los datos en bits.

La memoria principal de tipo RAM se considera de rápido acceso pues se pueden leer o escribir
millones de palabras por segundo, sin embargo hemos mencionado que la información se pierde
una vez que se apaga la computadora. Para resolver este inconveniente, se cuenta con otro tipo de
memoria denominada memoria secundaria, en ella se pueden almacenar una gran cantidad de
información permanentemente, mientras el usuario no la borre. La desventaja de este tipo de
dispositivos es que no son tan rápidos como la memoria RAM. Los discos duros, los discos ópticos
(CD o DVD), la memoria flash (USB), cintas magnéticas son ejemplos de dispositivos de
almacenamiento secundario.

 Los dispositivos de entrada y salida (Input/Output) son responsables de la comunicación con el


usuario del sistema. Los dispositivos de entrada permiten introducir en la computadora datos e
instrucciones, mismas que transforman en señales binarias de naturaleza eléctrica para
almacenarlas en la memoria. Por otro lado, los dispositivos de salida permiten enviar los resultados
a los usuarios de las computadoras, transformando las señales eléctricas binarias en información
que el usuario pueda entender. El teclado está considerado como el dispositivo de entrada
estándar pero existen otros del mismo tipo, por ejemplo: el ratón, el escáner, la lectora óptica, el
micrófono o la tabla digital. A su vez el monitor es el dispositivo de salida estándar; otros
ejemplos de dispositivos de salida son: impresora, bocinas, plotter.

Todas las unidades de la computadora se comunican enviándose señales mediante un sistema de buses;
cada bus es un conjunto de conductores interconectados que transmiten información entre los diferentes
componentes. Existen 3 tipos de buses, dependiendo de la información que transmiten: datos, dirección
y control. El bus de datos, como su nombre lo indica, transporta los datos entre los dispositivos de
hardware de entrada y / o salida, además de los dispositivos de almacenamiento. El bus de direcciones
está vinculado a la unidad de control para leer y guardar datos en la unidad de memoria; se encarga de
transmitir la dirección correspondiente al dato en tránsito. El bus de control es usado por la unidad de
control para comunicarse con otros dispositivos; transmite comandos desde el CPU y devuelve señales
de estado desde el dispositivo.
3Un bit se define como la la unidad mínima de contenido de información que una computadora puede representar, y que solamente puede
tomar los valores 0 ó 1.
Capítulo I | Página 4
1.3 ALMACENAMIENTO y EJECUCIÓN DE PROGRAMAS
Para explicar cómo se almacenan los “datos” e “instrucciones” que componen la programación de una
computadora, es preciso partir de la definición de estos conceptos:

 Un dato es un conjunto de símbolos que describen condiciones, hechos, situaciones o valores. Se


caracterizan por no contener ninguna información. Un dato puede significar un número, una letra,
una cantidad, una medida, una palabra.

Por ejemplo: 2001, septiembre, 16, 11, 1810. Por sí mismos, los datos no dicen nada; cobran
significado al insertarse dentro de un contexto, y es entonces cuando se convierten en
información.

 La información es un conjunto de datos estructurados y procesados.

Por ejemplo, de los datos anteriores podríamos obtener la siguiente información: 16 de


septiembre de 1810, inicio del movimiento insurgente de la independencia de México; 11 de
septiembre de 2001, ataque a las Torres Gemelas de Nueva York

 Una instrucción es un conjunto de símbolos que representan una operación que debe ser ejecutada
por la computadora.

Por ejemplo, “Guarda 20”. Esta instrucción almacena en la dirección de memoria ‘20’ el dato
que se encuentra en el acumulador. “Imprime x”. Esta instrucción imprime en la pantalla el
valor de la variable ‘x’.

Para que los datos y las instrucciones sean almacenados en la memoria y la unidad de control sea capaz
de interpretarlos deben representarse en lenguaje de máquina, esto es, como cadenas de 1´s (unos) y
0´s (ceros), como se ilustra en la figura 1.3.

10100111011
10111101110

Figura 1.3. Lenguaje de máquina

La razón es porque todos los dispositivos de una computadora trabajan con dos únicos estados:
activados-desactivado, abierto-cerrado, pasa corriente-no pasa corriente. Sin embargo, el lenguaje de

Capítulo I | Página 5
máquina tiene diversos inconvenientes entre los cuales destaca lo difícil que es para los seres humanos
entenderlo y por consecuencia escribirlo.
Con la finalidad de remediar estos problemas se han desarrollado otros lenguajes de programación que
se clasifican en dos tipos: lenguajes de bajo nivel y lenguajes de alto nivel. Los primeros se
caracterizan porque sus instrucciones se parecen más a las acciones elementales que ejecuta una
computadora (sumar, restar, guardar en memoria, etc), mientras que en los segundos las instrucciones
son más parecidas a un lenguaje natural (generalmente escritas en inglés). Por otro lado, los programas
escritos en bajo nivel describen a detalle que sucede a nivel de hardware, mientras que los programas
escritos en un lenguaje de alto nivel los ocultan, teniendo como ventaja que son más fáciles de entender
para los humanos.4
En síntesis, un programa es un conjunto de instrucciones que describen su funcionamiento, las cuales
están escritas en un lenguaje de programación, ya sea de alto o bajo nivel, o lenguaje de máquina.
Dependiendo de quién lo ha escrito, y en qué lenguaje está escrito, un programa se conoce como
programa fuente o programa objeto. El programa fuente está escrito por un programador en un
lenguaje de programación de alto o bajo nivel, por lo que no puede ser ejecutado directamente por la
computadora; sino que debe traducirse a lenguaje de máquina. Para realizar esta tarea se utilizan otros
programas dedicados únicamente a traducir instrucciones de un lenguaje de programación específico a
cadenas de ceros y unos (figura. 1.4).

Programa Objeto
Programa Fuente
#include <stdio.h> main(){ 1000111100
int a,b,s;
1111011110
printf("Ingresa dos números
enteros:"); COMPILADOR 0011101111
scanf("%d,%d",&a,&b);
s=a+b;
printf(“suma=%d”,s);
1001111011
}

Figura 1.4. Programa fuente y programa objeto

De acuerdo al procedimiento que sigan estos programas para traducir un programa fuente, se clasifican
en compiladores o intérpretes. Los compiladores traducen todo el programa fuente, generando un
programa escrito en lenguaje de máquina llamado programa objeto, que puede ser ejecutado
directamente por la computadora. En cambio, los intérpretes traducen y ejecutan instrucción por
instrucción; es decir, codifican la instrucción en binario para que la pueda entender la computadora y

4 A lo largo de este material aprenderemos a utilizar el lenguaje de programación C, definido como un lenguaje de alto nivel.

Capítulo I | Página 6
enseguida la ejecutan. Una vez finalizada la ejecución continúan con la traducción y ejecución de la
siguiente, y así sucesivamente hasta terminar todo el programa fuente. En otras palabras, los intérpretes
no generan un programa objeto.5

Para ilustrar los conceptos anteriores y con la finalidad de presentar la manera en la que interactúan
todas las unidades de una computadora durante la ejecución de un programa, presentamos el siguiente
programa fuente escrito en un “lenguaje de alto nivel”. 6

Ejemplo 1.1. El siguiente conjunto de instrucciones calcula la edad de una persona a partir de la fecha
de nacimiento y del año actual; incluyendo las instrucciones que leen los datos de entrada e imprimen
el resultado.

PROGRAMA FUENTE QUE CALCULA LA EDAD DE UNA PERSONA

1. Imprimir “Ingresa el año actual”


2. Leer fechaAct
3. Imprimir “Ingresa fecha de nacimiento”
4. Leer fechaNac
5. edad ← fechaAct – fechaNac
6. Imprimir “Edad:”,edad

Programa 1.1. Edad de una persona (programa fuente - lenguaje alto nivel)

Los nombres fechaAct, fechaNac y edad utilizadas en el Programa 1.1, hacen referencia a las
áreas de memoria en donde se almacenan los datos; a esto se le conoce como variables, ya que los
valores guardados pueden cambiar. En caso de que los valores permanezcan fijos se les denomina
constantes.

Antes de definir paso a paso la ejecución de este programa describiremos de manera general la función
de cada una de las instrucciones que lo integran.

5En el caso del lenguaje C se utiliza un compilador para traducir los programas fuentes, esto se abordará en el Capítulo 4.
6 Las instrucciones que aparecen en el programa están escritas en pseudocódigo. Este lenguaje no se considera un lenguaje de
programación; pero por el momento nos sirve para nuestro propósito. En los capítulos siguientes estudiaremos más sobre el pseudocódigo
y la forma en la cual se puede traducir a Lenguaje C.
Capítulo I | Página 7
INSTRUCCIÓN DESCRIPCIÓN
Imprime en la pantalla el mensaje indicados en la instrucción,
Imprimir <Mesj>
los cuales están almacenados en la memoria RAM.
Lee por medio del teclado un dato y lo almacena en la memoria
Leer <Var>
RAM, en el espacio identificado por Var
La flecha representa una asignación; esta acción actualiza
la dirección de memoria asignada a X con el dato Valor.
En los lenguajes de alto nivel, las direcciones de memoria
<X> ← <Valor> se representan por medio de variables 7 para hacerlas más legibles.
Entonces, X representa una variable y Valor puede ser
el resultado de una operación o el contenido de un registro
de la memoria representado con una variable.

Tabla 1.1. Lista de instrucciones en lenguaje de alto nivel

Observa que las palabras encerradas entre mayor y menor que “< >” denota la parte de la instrucción
que pueden cambiar, mientras que las palabras que no están encerradas deben escribirse tal cual, por
ejemplo, en el programa 1.1 la primera instrucción Imprimir “Ingresa año actual”
coresponde a una instrucción de la forma Imprimir <Mesj>.8

De acuerdo con la información anterior, cuando se ejecuta una instrucción del tipo Imprimir
<Mesj> las unidades—de la computadora—que interactúan son: la unidad de control que se encarga
de enviar las señales adecuadas, el monitor (dispositivo de salida) y los buses por los cuales envía los
datos. Así la ejecución de una instrucción de la forma Leer <Var> involucra: la unidad de control,
los buses y el teclado (dispositivo de entrada). Finalmente, una instrucción de asignación <X>←<Valor>
involucra la unidad de control, los buses y la memoria, en este caso ningún dispositivo de entrada o
salida interviene.

En la tabla 1.2 se describen paso a paso las acciones que realiza la unidad de control junto con el resto
de las unidades durante la ejecución del programa 1.1, suponiendo que el año de nacimiento de una
persona es 1989 y el año actual es 2009.

7 En el Capítulo 3 definiremos formalmente una variable.


8 Las símbolos “<” y “>” nos servirán para indicar la parte de las instrucciones que puede cambiar. Esta notación la adoptaremos a lo
largo del libro.
Capítulo I | Página 8
EJECUCIÓN DEL PROGRAMA 1.1
CALCULA LA EDAD DE UNA PERSONA
# Instrucción Descripción de la instrucción
La unidad de control coordina las acciones necesarias para
que el monitor imprima la leyenda “Ingresa el año actual”.

Imprimir Ingresa el año actual: _

1. “Ingresa el año
actual”

La unidad de control coordina las acciones necesarias


para que el teclado reciba el valor de la fecha actual, mismo
que almacena en la memoria RAM en el espacio de memoria
identificado por fechaAct,
.
Leer 2009
M e m o r i a RAM
2.
fechaAct
fechaAct
2009

La unidad de control coordina las acciones necesarias


para que el monitor imprima el mensaje “Ingresa año de
nacimiento”.
Imprimir
Ingresa el año actual: 2009
3. “Ingresa año de Ingresa año de nacimiento:
nacimiento” _

La unidad de control coordina las acciones necesarias


para que el teclado reciba el valor del año de nacimiento y
se almacene en la memoria RAM en el espacio identificado
por fechaNac.
1989
M e m o r i a RAM
Leer
4. fechaAct fechaNac
fechaNac
2009 1989

Capítulo I | Página 9
La unidad de control envía la señal indicada a la ALU
para que realice la resta indicada1 y después almacena el
resultado en el espacio de la Memoria RAM identificado por
la variable edad.

M e m o r i a RAM
ALU
fechaAct fechaNac edad
edad ←
2009 1989 20
5. fechaActual – 2009 - 1989
fechaNacimiento

La unidad de control trae de memoria el valor almacenado


en el espacio de memoria identificado por la variable edad
y coordina las acciones para que el monitor lo imprima
en pantalla.
Imprimir “Edad:”, M e m o r i a RAM
6.
edad Ingresa año de
fechaAct fechaNac edad nacimiento: 1989

2009 1989 20 Edad: 20

Tabla 1.2. Ejecución paso a paso del Programa 1.1

En la siguiente sección presentaremos con más detalle que ocurre dentro del CPU cuando se ejecuta
una instrucción.

1.4 EJECUCIÓN DE LAS INSTRUCCIONES EN EL CPU


Los lenguajes de bajo nivel están estrechamente relacionados con la parte física del CPU, es por ello
que cada procesador tiene su propio lenguaje ensamblador que puede ejecutar. En general una
instrucciones en alto nivel se traduce a un conjunto de instrucciones más simples en ensamblador, y
son estas instrucciones las que directamente se traducen a lenguaje máquina.

Con la finalidad de entender cómo se procesan los datos y las instrucciones en el CPU, en la tabla 1.3
se propone un conjunto de instrucciones en lenguaje de bajo nivel (para un procesador imaginario).
Cabe resaltar que, para escribir instrucciones en bajo nivel, se debe especificar tanto la acción u
operación que se quiere realizar seguida de la dirección de memoria en donde se encuentra el dato que
se va a operar. Cada una de estas instrucciones tienen una traducción uno a uno a cadenas en lenguaje
máquina; sin embargo, por el momento adoptaremos una codificación decimal para hacer más legibles

1
Los pasos que se ejecutan en el CPU para realizar la operación se describen más adelante.
Capítulo I | Página 10
el programa objeto, tomando en cuenta que en el siguiente capítulo se mostrará cómo convertir a binario
un número representado en decimal.

INSTRUCCIONES EN BAJO NIVEL


Acción Código Sintaxis general Descripción
Transfiere el dato almacenado en la posición
Carga 20 Carga <DIR> de memoria <DIR> al acumulador de la
ALU.
Guarda el contenido del acumulador
Guarda 02 Guarda <DIR>
en la posición de memoria <DIR>.
Divide el contenido del acumulador entre el
dato almacenado en la posición de memoria
Divide 38 Divide <DIR> <DIR>.
El resultado queda almacenado en el
acumulador.
Multiplica el contenido del acumulador por
el dato almacenado en la posición de
Multiplica
Multiplica 36 memoria <DIR>.
<DIR>
El resultado queda almacenado en el
acumulador.
Resta al contenido del acumulador el dato
almacenado en la posición de memoria
Resta 33 Resta <DIR> <DIR>.
El resultado queda almacenado en el
acumulador.
Suma al contenido del acumulador el dato
almacenado en la posición de memoria
Suma 30 Suma <DIR> <DIR>.
El resultado queda almacenado en el
acumulador.

Alto 70 Alto Indica el fin del programa.

Tabla 1.3. Lista de instrucciones en lenguaje de bajo nivel

De acuerdo con la sintaxis propuesta en la tabla 1.3, las instrucciones tienen un nombre fijo asociado a
una acción particular y todas, excepto Alto, requieren que se especifique la dirección de memoria
involucrada en la acción —diferente en cada caso—, la cual se representa de manera general por
<DIR>. Asumiremos que cada una de las instrucciones se almacena en una palabra de memoria.

Ejemplo 1.2. En la siguiente tabla se muestra un programa en bajo nivel que es equivalente a la quinta
instrucción del programa del ejemplo 1.1. En el cual se realiza la resta del año actual menos el año de
nacimiento. Suponiendo que a la variable fechaAct le corresponde la dirección de memoria 00
seguida de la variable fechaNac con dirección 01 y finalmente la variable edad con dirección 02.

Capítulo I | Página 11
PROGRAMA FUENTE ESCRITO
EN UN LENGUAJE DE BAJO
NIVEL
Carga 00
Resta 01
Guarda 02
Alto

Programa 1.2. Calculo de la edad (programa fuente - lenguaje de bajo nivel)

A continuación se muestra el programa objeto resultado de traducir el programa 1.2 a su codificación


decimal, de acuerdo con la tabla 1.3, además de su codificación en lenguaje de máquina, considerando
que cada palabra de memoria tiene 16 bits.

PROGRAMA EN LENGUAJE DE BAJO NIVEL


QUE CALCULA EL CUADRADO DE UN NÚMERO
Programa objeto Programa fuente
codificación decimal codificación binaria
20 00 00010100 00000000
33 01 00100001 00000001
02 02 00000010 00000010
70 01000110 00000000

Programa 1.3. Calculo de la edad (programa objeto)

De aquí en adelante utilizaremos la codificación decimal propuesta para facilitar la legibilidad. En la


siguiente sección se explica cómo se ejecutan las instrucciones de un programa en general.

Los pasos que sigue el procesador para ejecutar una instrucción se conocen como Ciclo de ejecución,
y se enlistan a continuación:

1. La unidad de control trae de la memoria RAM la instrucción indicada en el contador de programa


(PC) y la almacena en el registro de instrucción (IR). Después actualiza el PC con la dirección de
memoria donde se encuentra almacenada la siguiente instrucción del programa.

2. La unidad de control decodifica la instrucción del IR, detecta las unidades que deben interactuar
para ejecutarla y les envía señales de control para indicarles la acción que deben de realizar. Si la
acción requiere un dato que esté en memoria, se hará la lectura correspondiente, utilizando para
ello los buses apropiados.

3. Si las instrucciones contienen operaciones lógicas o aritméticas en los datos, este trabajo es
asignado a la ALU, la cual realiza la operación con el dato indicados en la instrucción y el dato
almacenado en el acumulador. El resultado es almacenado en el acumulador del ALU, perdiéndose
con ello el dato anterior.
Capítulo I | Página 12
Para ejemplificar el Ciclo de ejecución, analicemos el proceso que sigue la computadora con las
instrucciones del programa 1.2.

Ejemplo 1.3. Supongamos que las instrucciones están almacenadas a partir de la dirección “20”, y que
el dato guardado en la dirección “00” es 2008 y en la dirección “01” hay un 1989, tal y como se muestra
en la figura 1.5 a).

Dato 2008 1989 … 20,00 33,01 02,02 70


Dirección 00 01 02 20 21 22 23 24 …

a) Estado de la memoria antes de la ejecución

Dato 2008 1989 19 … 20,00 33,01 02,02 70


Dirección 00 01 02 20 21 22 23 24 …

b) Estado de la memoria después de la ejecución

Figura 1.5. Almacenamiento de datos e instrucciones en memoria (vista en decimal)

Recordemos que en realidad lo que hay guardado en memoria es código máquina como en la figura
1.6, sin embargo para facilitarnos la manipulación de los datos los trabajaremos en decimal como
aparecen en la figura 1.5.

00000111 00000111 00000000 00010100 00100001 00000010 01000110


Dato 11011000 11000101 00010011 … 00000000 00000001 00000010 00000000

Dirección 00 01 02 20 21 22 23 ---

b) Estado de la memoria después de la ejecución

Figura 1.6. Almacenamiento de datos e instrucciones en memoria (vista en lenguaje de máquina)

Al iniciar el programa, el PC contiene la dirección de memoria de la primera instrucción. De acuerdo


con la figura 1.5 a), se inicializa en 20. La primera acción que sigue la unidad de control es almacenar
el código de la instrucción “Carga 00” en el IR, y después actualiza el valor de PC con la dirección de
la siguiente instrucción, en este caso 22. El siguiente paso es decodificar la instrucción; al hacerlo,
detecta que requiere transferir el dato de la dirección 20 al acumulador del ALU, así que envía las
señales de control pertinentes.

La ejecución de la instrucción “Multiplica 00” es similar, sólo que además de traer un dato de memoria,
le envía la señal correspondiente al ALU para que realice la multiplicación. El resultado de la misma

Capítulo I | Página 13
se almacena en el acumulador, y no es hasta que se ejecuta la instrucción “Guarda 01”, que el resultado
se almacena en la memoria principal. En la figura 1.5 b) se refleja cómo luce la memoria después de la
ejecución del programa.

Lo anterior se resume en la tabla 1.4, en donde se muestra cómo van cambiando los valores
almacenados en la memoria, el contador de programa, el registro de instrucción y el acumulador, cada
vez que se ejecuta una instrucción del programa 1.1. Por limitaciones de espacio, sólo se muestran las
posiciones de memoria donde se almacenan los datos, entendiendo que el contenido de las direcciones
donde está almacenado el programa no cambia.

UNIDAD
ALU DE CONTROL
INSTRUCCIÓN MEMORIA
Operación Acumulador IR PC
- - - - 20 -
00 01 02
Carga 00 2008 20 00 21
2008 1989
00 01 02
Resta 01 2008-1985 19 36 00 22
2008 1989
00 01 02
Guarda 02 19 02 01 23
2008 1989 19
00 01 02
Alto - 70 -
2008 1989 19

Tabla 1.4. Estado de los registros y memoria al ejecutar el Programa 1.2

Observa en la tabla 1.4 que el valor de la columna PC (Program Counter) se va modificando en cada
ejecución ya que siempre debe tener la dirección de la siguiente instrucción a se ejecutada. Por otro
lado, cuando se ejecuta una operación aritmética o la acción “Carga”, se cambia el contenido del
acumulador de la ALU. Asimismo, es notorio que la única acción que afecta el estado de la memoria
principal es “Guarda”.

Es importante subrayar que cuando en un programa se utiliza el resultado de una operación, es necesario
guardar este resultado en la memoria (a esta acción se le conoce como asignación), ya que el
acumulador se actualiza cada vez que se realiza una operación en la ALU, lo cual provoca que el
resultado anterior se pierda.

En el programa visto no se incluye la forma en la que se almacenan en la memoria los datos de entrada,
ni tampoco la forma en la cual se imprime el resultado. Estas tareas, como hemos visto, corresponden
a los dispositivos de entrada y salida.

Capítulo I | Página 14
EJERCICIOS DEL CAPÍTULO No. 1

I. Determina si las siguientes afirmaciones son falsas (F) o verdaderas (V):

a) La principal aportación de Von Neumann proponía que tanto el programa ( )


como sus datos fueran almacenados en el CPU de la computadora.
b) En la ALU se realizan todos los cálculos aritméticos y todas las ( )
comparaciones lógicas.
c) En una celda se pueden almacenar uno o más bits. ( )
d) Los cables que sirven como canales de transmisión y permiten la ( )
comunicación entre los componentes de la computadora se llaman buses.
e) Existen tres tipos de buses: de dirección, de datos y de memoria. ( )
f) La dirección de memoria es un número que identifica a un conjunto de ( )
celdas de memoria.
g) En la memoria RAM los datos se almacenan de forma permanente. ( )
h) Las instrucciones escritas en lenguaje de máquina sólo contienen 0s y 1s. ( )
i) En el contador de programa se almacena la dirección de la siguiente ( )
instrucción que se debe ejecutar.
j) Siempre que se realiza una operación en el ALU, el resultado se almacena ( )
en memoria.
k) La unidad de control se encarga de decodificar las instrucciones. ( )
l) La comunicación con el exterior se realiza por medio de los dispositivos ( )
de entrada y salida.
m) En la memoria de tipo ROM se pueden leer y escribir datos. ( )
n) Se denomina programa fuente al conjunto de instrucciones escritas en un ( )
lenguaje de programación.
o) El Ciclo Fetch sólo se realiza una vez durante la ejecución del programa. ( )

II. Investiga tres definiciones diferentes de “computadora” y subraya las palabras comunes; especifica
la bibliografía consultada.

Capítulo I | Página 15
III. Investiga los siguientes conceptos (anota la bibliografía consultada):

a) Byte
b) Variable
c) Constante
d) Operador
e) Álgebra de Bool
f) Expresión
g) Algoritmo
h) Programa
i) Programa Fuente
j) Programa Objeto
k) Lenguaje de Programación
l) Compilador

IV. En la siguiente lista, escribe el número correspondiente al esquema del modelo de Von Neumman.

( ) Monitor ( ) Teclado ( ) RAM ( ) DVD


Canal de
( ) ( ) Micrófono ( ) Disco 3 ½ ( ) Disco Duro
Direcciones
Canal de Canal
( ) Impresora ( ) WebCam ( ) Control ( ) de Datos
( ) 5+6 ( ) Decodifica ( ) Bocinas ( ) CD-RW
Instrucciones
Coordina y Cinta
( ) ( ) 5<8 ( ) ( ) Scaner
Controla Magnética

2 Memoria
5 Bus

UNIDAD CENTRAL DE
PROCESAMIENTO (CPU)
1 Dispositivos 6 Dispositivos
3 (ALU)
de Salida
de Entrada
4 Unidad
de Control

7 Medios de
Almacenamiento

Capítulo I | Página 16
V. Considerando el siguiente programa en lenguaje de alto nivel (ver Programa 1.1 y Tabla 1.1), el cual
calcula el total a pagar de una compra a la cual se le aplica un porcentaje de descuento indicado por el
usuario, indica los elementos del modelo de Von Neumann que intervienen en la ejecución de cada una
de las instrucciones:

PROGRAMA FUENTE PARA CALCULAR EL MONTO TOTAL


DE UNA COMPRA A LA CUAL SE LE APLICA UN DESCUENTO

1. Imprimir “Ingresa el total de la compra sin descuento”


2. Leer compra
3. Imprimir “Ingresa el porcentaje de descuento (0 a 100)”
4. Leer descuento
5. total ← compra – compra * (descuento / 100)
6. Imprimir total

Programa 1.4. Programa fuente para calcular el monto total de una compra a la cual se le aplica un descuento.

VI. La siguiente tabla muestra un programa en lenguaje de bajo nivel equivalente a la instrucción 5 del
programa 1.5 del ejercicio anterior. Suponga que a la variable compra tiene almacenado un 250 y le
corresponde la dirección de memoria 10, en la variable descuento hay un 10 y tiene asignada la
dirección 11 de memoria, finalmente en la dirección 13 se encuentra un 100. La dirección a partir de la
cual se guarda el programa en memoria es la dirección 40

PROGRAMA EN LENGUAJE DE BAJO NIVEL PARA CALCULAR


UN DESCUENTO
Programa Programa
Descripción
Fuente objeto
Carga 11 Trae al acumulador el valor del descuento 20 11
Divide 13 Divide descuento / 100 38 13
Multiplica el resultado que esta en el
Multiplica 10 36 10
acumulador (descuento/100) por la compra
Guarda 12 Guarda el valor a descontar en memoria 02 12
Carga 10 Trae al acumulador el valor de la compra 20 10
Resta 12 Resta a la compra el descuento 33 12
Guarda 12 Guarda el resultado de la resta en total 02 12
Alto Termina programa 70

Programa 1.5. Programa en lenguaje de bajo nivel que calcula total ← compra – compra * (descuento / 100)

Capítulo I | Página 17
a) Realiza la ejecución paso a paso del programa anterior utilizando la siguiente tabla.

UNIDAD
ALU DE CONTROL MEMORIA
INSTRUCCIÓN
(Dirección)
Operación Acumulador IR PC
10 11 12 13
40
100
10 11 12 13
Carga 11
10 11 12 13
Divide 13
10 11 12 13
Multiplica 10
10 11 12 13
Guarda 12
10 11 12 13
Carga 10
10 11 12 13
Resta 12
10 11 12 13
Guarda 12
10 11 12 13
Alto

b) ¿En qué dirección de memoria se almacena el resultado final?

VII. Utiliza las instrucciones de bajo nivel que se muestran en la tabla 1.3 para crear un programa
fuente que realice la siguiente operación.

x ← y2 – 3ab
I.

Capítulo I | Página 18
Capítulo
Representación de datos en
la computadora
"I wish to God these calculations had been executed by steam”
Charles Babbage, 1821.

OBJETIVO:
Al finalizar este capítulo el estudiante:

o Reconocerá la necesidad de diferentes representaciones tanto de datos numéricos como


alfanuméricos en el ámbito de la programación, para lo cual manejará los sistemas numéricos
posicionales más utilizados.

2.1 INTRODUCCIÓN
En este capítulo estudiaremos los aspectos relacionados con la representación de la información en una
computadora. Como ya hemos mencionado en el capítulo anterior, la información es un conjunto de
datos que tienen un significado específico más allá de cada uno de estos; es decir que los datos por sí
mismo no tienen significado alguno, se convierten en información hasta que se colocan en un contexto.
La forma más común de representar información es mediante la representación simbólica, en la cual
las palabras se forman a partir de un conjunto de símbolos y reglas sintácticas. Por ejemplo, si queremos
representar el año en el que nació John von Neumann, en español sería “mil novecientos tres”, pero
también podemos representarlo numéricamente en decimal como “1903”, o bien, con números
romanos como “MCMIII”. El ejemplo anterior nos habla de que se pueden utilizar diferentes formas
de representación de un mismo dato.
Considerando que nuestro principal objetivo es solucionar problemas utilizando la computadora, en
este capítulo nos enfocaremos en los sistemas de representación simbólica más utilizados en
computación, tanto para representar datos numéricos como para representar datos alfabéticos y
alfanuméricos. A lo largo de este capítulo estudiaremos los sistemas de numeración posicional (SNP)
más utilizados en programación, así como los métodos para realizar conversiones entre ellos.
Asimismo, presentaremos las diferentes formas de representar números enteros signados (signo y
magnitud, complemento a uno y complementos a dos), números con punto decimal (punto fijo y
flotante) y datos alfanuméricos (ASCII).

Capítulo 2 | Página 1
2.2 DATOS
Como vimos en el capítulo anterior un dato es un conjunto de símbolos que puede significar un número,
una letra, una cantidad, una medida o una palabra, más no es información hasta que no está en un
contexto específico. Los datos se pueden clasificar como:
 Datos numéricos, generalmente combinaciones de dígitos del 0 al 9, pueden tener signo y punto decimal
(por ejemplo: 98, -122, 3.1416, -8.56)
 Datos alfabéticos, compuestos sólo por letras (por ejemplo: a, e, computadora, fin).
 Datos alfanuméricos, combinación de los dos anteriores y símbolos (por ejemplo: numero1, promedio2
2-a$)

En el capítulo anterior vimos que las computadoras no son capaces de procesar datos representados en
lenguaje natural, para que una computadora o cualquier otra máquina electrónica digital sea capaz de
interpretar y manipular datos necesita que estén representados en lenguaje de máquina, es decir, como
cadenas de 1´s y 0´s (recordemos que en la memoria sólo se pueden almacenar bits). Sin embargo, de
acuerdo con la teoría de la información “toda representación simbólica es posible cambiarla a una
representación en binario”, así que ahora sólo nos falta conocer que procedimiento seguir y es
justamente el tema de la siguiente sección.

2.3 SISTEMAS NUMÉRICOS POSICIONALES


Los sistemas numéricos posicionales (SNP) son una forma de representación simbólica de los
números, por medio de la cual se simplifica la representación de grandes cantidades. Se denomina
sistema numérico posicional porque dependiendo de la posición que ocupa un dígito dentro de la cifra
tiene una contribución distinta al valor total. El sistema decimal es un ejemplo de SNP.
Los SNP más utilizados en programación son decimal, binario, octal y hexadecimal, por lo cual nos
concentraremos únicamente en éstos. Antes de presentarlos definamos formalmente que es un sistema
de numeración.
Definición: Un sistema de numeración consta de un conjunto no vacío de símbolos, llamado alfabeto,
y las reglas que permiten representar cada uno de los números válidos en el sistema como una sucesión
de dichos símbolos. En el caso particular de los sistemas de numeración posicional la regla principal
indica que el valor de un símbolo del alfabeto depende de la posición que ocupe en la cadena. Al
número de símbolos que conforman el alfabeto se le conoce como base.
En el caso particular de los sistemas de numeración los símbolos del alfabeto se llaman dígitos (excepto
para el sistema binario en el cual los dígitos se conocen como bits). Si el alfabeto tiene menos de 10
dígitos se utilizan los dígitos decimales 0, 1, 2,…, 9, si es mayor de 10 se utilizan los 10 dígitos
decimales más las primeras letras del alfabeto como en el sistema Hexadecimal.
En la tabla 2.1 se muestran los alfabetos y la base de cada uno de los SNP que estudiaremos en esta
sección, además de la representación del número “ciento sesenta y cinco” en cada base.

Capítulo 2 | Página 2
Ejemplo: Representación del número
SNP Base Alfabeto
ciento sesenta y cinco

Decimal 10 {0,1,2,3,4,5,6,7,8,9} 165(10)

Binario 2 {0,1} 10100101(2)

Octal 8 {0,1,2,3,4,5,6,7} 245(8)

Hexadecimal 16 {0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F} A5(16)

Tabla 2.1. Sistemas de numeración posicional más utilizados en computación

En los SNP se utiliza una secuencia de dígitos para representar un valor entero o una cifra,
concatenando los dígitos que sean necesarios, por ejemplo, para representar “ciento sesenta y cinco”
en hexadecimal la secuencia de dígitos que se utiliza es “A5”. Además al final del número se escribe
la base en la cual está representado, tal y como se muestra en los ejemplos de la tabla 2.1.

En general, un número representado en alguno de SNP con 𝑛 dígitos tiene la siguiente forma:

𝑑 𝑑 …𝑑 𝑑 𝑑
En donde, cada 𝑑 representa un dígito válido dentro del alfabeto y el subíndice i corresponde a la
posición que ocupa el dígito dentro de la cifra, observa que los dígitos están enumerados de izquierda
a derecha empezando desde cero. Al dígito de más a la derecha (do) se le llama menos significativo y
al de más a la izquierda (dn-1) más significativo, esto por la cantidad de unidades que aportan al valor
del número. Por ejemplo, para el número ciento sesenta y cinto en base decimal 165(10), el dígito menos
significativo es el ‘5’ ya que sólo aporta cinco unidades al valor del número, mientras que el ‘1’ que es
el símbolo más significativo aporta cien unidades al valor del número.
En el caso del sistema decimal acostumbramos nombrar cada una de las posiciones: 𝑑 son las unidades,
𝑑 son las decenas, 𝑑 son las centenas, 𝑑 unidades de millar, etc. Como el sistema decimal es el que
se utiliza a nivel mundial el valor de un número se representa en esta base, es decir que el valor del
número “ciento sesenta y cinco” lo denotaremos por 165. Por tal motivo es importante saber cómo
convertir un número representado en cualquier base a decimal.

Capítulo 2 | Página 3
2.3.1 Conversión de cualquier SNP a sistema decimal
El valor de una cifra representada en cualquier sistema numérico posicional está dado por la siguiente
expresión:

𝑣𝑎𝑙𝑜𝑟 = 𝑑 𝑟 𝑑 𝑟 …𝑑 𝑟 𝑑 𝑟 𝑑 𝑟 (1)

Donde 𝑟 representa la base y 𝑛 el número de dígitos con los que se representa el número en la base 𝑟.

En general para convertir de cualquier base a decimal lo que la expresión (1) nos indica se puede
resumir en la siguiente lista de pasos.
1. Enumerar los dígitos de izquierda a derecha iniciando con cero.
2. De acuerdo con la numeración dada, multiplicar cada dígito 𝑖 por la base elevada a la 𝑖
𝑑 ∗𝑟
3. Sumar los resultados de los productos calculados en el paso anterior para obtener el valor
de la cifra.

Para ilustrar este procedimiento revisemos los siguientes ejemplos.

Ejemplo 2.1: Tomemos el número 245(8) en octal. Para el sistema octal la base es 8 (𝑟 =10), el valor
se representa mediante la concatenación de los dígitos: ‘2’, ‘4’ y ‘5’ (𝑛 = 3). Como ya mencionamos,
las posiciones en un SNP se enumeran desde cero de derecha a izquierda, por tanto la numeración
queda como:
2 4 5

𝑑 𝑑 𝑑

El valor representado lo obtenemos con la ecuación (1), sustituyendo los valores tenemos:
valor 245(8) = 2*82 + 4*81 + 5*80

valor 245(8) = 2*64 + 4*8 + 5*1

valor 245(8) = 128 + 32 + 5

valor 245(8) = 165

Capítulo 2 | Página 4
Ejemplo 2.3: Convertir B32(16) representado en hexadecimal a decimal

Enumeramos los dígitos de izquierda a derecha empezando desde cero

d2 d1 d0
B 3 2

Considerando que 𝑟=16 y 𝑛=3, realizamos las sustituciones en (1)

B x 162 + 3 x 161 + 2 x 160


11 x 256 + 3 x 16 + 2x1
2816 + 48 + 2 = 2866(10)

Ejemplo 2.4: Convertir 110101(2) representado en binario a decimal

Enumeramos los dígitos en la forma indicada

d5 d4 d3 d2 d1 d0
1 1 0 1 0 1

Considerando que 𝑟=2 y 𝑛=6, realizamos las sustituciones en (1)

1 x 25 + 1 x 24 + 0 x 23 + 0 x 22 + 0 x 21 + 1x 20
1 x 32 + 1 x 16 + 0x8 + 1x4 + 0x2 + 1x1
32 + 16 + 0 + 4 + 0 + 1 = 53(10)

Observemos que en todos los ejemplos el dígito que contribuye menos al valor final de una cifra es el
que se encuentra más a la derecha, ya que éste siempre se multiplicara por la menor potencia, es por
esto que se dice que es el dígito menos significativo; en cambio, el dígito que está más a la izquierda
es el que mayor contribución tiene al valor por estar multiplicado por la potencia más grande, de ahí su
nombre de dígito más significativo.

En la siguiente sección se muestra el método para convertir un número representado en sistema decimal
a cualquier otro sistema.

Capítulo 2 | Página 5
2.3.2 Conversión de sistema decimal a cualquier otro SNP
Para representar un número en cualquier SNP distinto al decimal tenemos que realizar una serie de
divisiones enteras donde los residuos nos darán los dígitos correspondientes a la representación del
número en el sistema deseado. Considerando que los dígitos se enumeran de izquierda a derecha, el
primer residuo corresponderá al dígito 𝑑 (el menos significativo), el segundo residuo será el dígito 𝑑 ,
el tercer residuo corresponde al dígito 𝑑 , y así sucesivamente hasta llegar a que el cociente es cero y
el residuo esta última división corresponde al dígito más significativo.
A continuación se enlista el algoritmo que nos ayuda a representar un número escrito en decimal en
cualquier otro sistema de numeración con base 𝐵:

1. Se divide el número entre la base 𝐵


2. El residuo es el dígito menos significativo de la cifra, 𝑑
3. Mientras el cociente sea mayor que cero realizar los siguientes pasos.
i) Dividir el cociente entre la base
ii) El residuo de la división corresponde al siguiente dígito de derecha a izquierda

Revisemos paso a paso el método anterior con el siguiente ejemplo:


Ejemplo 2.5: Representar 9379(10) en decimal a hexadecimal (base 16)

Paso 1: Dividimos 9379 entre 16.

Paso 2: El residuo es el dígito menos significativo de la cifra 𝑑 .

Paso 3 (iteración 1) El resultado de la división es 586 por lo tanto realiza los pasos i e ii.

i) El cociente 586 se vuelve a dividir entre la base.

ii) El residuo 10, al cual le corresponde la letra A, es el siguiente dígito 𝑑 .

Capítulo 2 | Página 6
Paso 3 (iteración 2): Como 36 es mayor a cero se realizan los pasos i e ii.

i) El cociente 36 se vuelve a dividir entre la base

ii) El residuo es el siguiente dígito 𝑑 .

Paso 3 (iteración 3): Como el cociente es mayor a cero se repiten los incisos del paso 3

i) El cociente se vuelve a dividir entre la base.

ii) El residuo es el siguiente dígito 𝑑 .

Paso 3 (iteración 4): como el cociente es cero se termina

Por lo tanto, la representación en hexadecimal del número 9379(10) representado en decimal es


24A3(16).

Capítulo 2 | Página 7
A continuación se presentan dos ejemplos, el primero convierte un número de decimal a octal y el
segundo de decimal a binario.

Ejemplo 2.6: Representar 1487(10) en decimal a octal (base 8)

Cociente: 185
8 1487 d0
Residuo: 7 7
23
8 185 d1 d0
1 1 7
2
8 23 d2 d1 d0
7 7 1 7
0
8 2 d3 d2 d1 d0
2 2 7 1 7

La representación de 1487(10) en octal es 2717(8).

Ejemplo 2.7: Representar 59(10) en decimal a binario (base 2)

29
2 59 d0
1 1
14
2 29 d1 d0
1 1 1
7
2 14 d2 d1 d0
0 0 1 1
3
2 7 d3 d2 d1 d0
1 1 0 1 1
1
2 3 d4 d3 d2 d1 d0
1 1 1 0 1 1
0
2 1 d5 d4 d3 d2 d1 d0
1 1 1 1 0 1 1
La representación de 59(10) en octal es 111011(2).

Capítulo 2 | Página 8
2.3.3 Conversión entre dos SNP distintos al decimal
El procedimiento que se sigue para convertir un número entre dos diferentes SNP distintos al decimal,
es decir, un número representado en un SNP con base 𝐵 (diferente de 10), a otro SNP con base 𝐵
(también distinto de 10), consiste en los siguientes pasos:
1. Realizar la conversión de base 𝐵 a sistema decimal
2. Realizar la conversión del número representado en sistema decimal al sistema con base 𝐵

Revisemos el siguiente ejemplo:

Ejemplo 2.8: Convertir 100111(2) a hexadecimal

Paso 1: Convertimos el número binario 100111 a decimal.

d5 d4 d3 d2 d1 d0
1 0 0 1 1 1

Considerando que 𝑟=2 y 𝑛=6, realizamos las sustituciones en (1)

1 x 25 + 0 x 24 + 0 x 23 + 1 x 22 + 1 x 21 + 1 x 20
1 x 32 + 0 x 16 + 0x8 + 1x4 + 1x2 + 1x1
32 + 0 + 0 + 4 + 2 + 1 = 39(10)

Paso 2: Convertimos el número decimal 39 a hexadecimal

2
16 39 d0
7 7
0
16 2 d1 d0
1 2 7

La representación de 100111(2) en hexadecimal es 27(16).

El método anterior funciona para realizar la conversión de un número entre cualesquiera dos SNP
distintos del decimal. Sin embargo, en el caso particular de los SNP que son potencias de 2, existe un
método más simple, el cual presentamos en la siguiente sección.

Capítulo 2 | Página 9
2.3.4 Conversión de binario a un SNP cuya base es potencia de 2

Para convertir un número representado en binario a cualquier otro sistema cuya base sea potencia de
dos (es decir, 2 ) − por ejemplo, el sistema octal (2 ) o el sistema hexadecimal (2 ), se debe
considerar el mínimo número de bits que se necesitan para representar a cualquiera de los dígitos de
estas bases.

De acuerdo con la tabla 2.2 con un solo bit únicamente se pueden representar dos valores (a saber 0 y
1); con dos bits se pueden representar cuatro valores (0,1,2,3); con tres bits se pueden representar ocho
valores (0,1,2, … ,7); con cuatro bits se pueden representar 16 valores (0,1,2, … ,15). En general con 𝑛
bits se pueden representar 2 valores (0, 1, 2, … , 2 − 1 ). Por lo tanto, para representar todos los
dígitos octales en binario se necesitan 3 bits y para los dígitos hexadecimales 4 bits. En general, se
necesitan 𝑛 bits para representar los dígitos de un SNP con base igual a 2 .

Decimal Binario Octal Hexadecimal


0 0 0 0
1 1 1 1
2 10 2 2
3 11 3 3
4 100 4 4
5 101 5 5
6 110 6 6
7 111 7 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F
Tabla 2.2. Equivalencias de las bases: binario, octal y hexadecimal

A continuación se describe el método general para convertir un número en sistema decimal a cualquier
otra base que sea la 𝑛-ésima potencia 2 (es decir, 2 ):

1. Los bits del número binario se agrupan de 𝑛 en 𝑛 de izquierda a derecha.


2. Obtener el valor de cada grupo de 𝑛 bits, esto es su representación en decimal.
3. Representar el valor de cada grupo de 𝑛 bits como un dígito del SNP con base 2 , escribiéndolos
en el mismo orden en el que aparecían los grupos de 𝑛 bits.
En nuestro caso únicamente nos centraremos en la conversión de decimal a octal y hexadecimal.

Capítulo 2 | Página 10
Conversión de Binario a Octal

Ahora bien, si queremos convertir un número binario a octal sólo se necesitan 3 bits, por lo tanto,
agrupamos de tres en tres de derecha a izquierda y cambiamos cada cuarteto por su dígito octal
equivalente (ver tabla 2.2). Veamos el siguiente ejemplo.
Ejemplo 2.9. Representar el número binario 11100010111 en octal.

Por lo tanto, el número binario 110010010111(2) se representa en octal como 3427(16)

Conversión de Binario a Hexadecimal

Si queremos cambiar de binario a hexadecimal lo que se hace es agrupar los bits de 4 en 4 de derecha
a izquierda y cambiar cada cuarteto por su dígito hexadecimal equivalente (ver tabla 2.2), como se
muestra a continuación.
Ejemplo 2.10: Representar el número binario 110010010111(2) en hexadecimal.

Por lo tanto, el número binario 110010010111(2) se representa en hexadecimal como C97(16)

Capítulo 2 | Página 11
2.3.5 Conversión de binario a un SNP cuya base es potencia de 2

Finalmente, para convertir de un SNP cuya base es 2 se deben realizar los siguientes pasos:
1. Obtener el valor de cada uno de los dígitos (convertirlos a decimal)
2. Codificar el valor de cada uno de los dígitos en binario utilizando 𝑛 bits, si es necesario se
agregan ceros a la izquierda de número binario hasta completar los 𝑛 bits requeridos.
3. La representación del número en base 2 corresponde a la concatenación de las secuencias de
bits obtenidas en el paso anterior respetando el orden de la representación original.
Nuevamente, nos centraremos en los sistemas octal y hexadecimal.

Conversión de Hexadecimal a Binario

Para convertir un número hexadecimal a binario lo que se debe hacer es cambiar cada dígito por su
representación en binario utilizando cuatro bits, observemos el siguiente ejemplo.
Ejemplo 2.11. Representar el número hexadecimal 1B0(16) en binario.

1 B 0
Valor de los dígitos 1 11 0
Representación binaria en 4 bits 0001 1011 0000

Por lo tanto 1B0(16) se representa en binario como 1 1011 0000(2)

Conversión de Octal a Binario

En el caso de la conversión de sistema octal a sistema binario, debemos representar cada dígito con 3
bits, de la misma forma que en el caso anterior.
Ejemplo 2.12. Representar el número octal 170(16) en binario.

1 7 0
Valor de los dígitos 1 7 0
Representación binaria en 4 bits 001 1011 0000

Por lo tanto 1B0(16) se representa en binario como 1 1011 0000(2)

Capítulo 2 | Página 12
2.4 REPRESENTACIÓN DE NÚMEROS ENTEROS
Pare representar números enteros se utilizan principalmente cuatro métodos para su codificación a
binario:
 Binario sin signo.
 Binario en signo y magnitud
 Binario en complemento a uno
 Binario en complemento a dos

2.4.1 Código binario sin signo


La codificación binaria sin signo se utiliza para representar números no negativos, el número se
representa en binario tal como se explicó antes, salvo que se utiliza un número de bits fijos así que si
el número requiere para su representación en binario menos bits que los establecidos se agregan ceros
a la izquierda para completar el total de bits predefinidos. Se acostumbra utilizar grupos de bits que son
potencias de dos, por ejemplo: 8, 16, 32. Además recordemos que el rango de números que se pueden
representar depende del número de bits disponibles (como se explico en la sección anterior). Por
ejemplo, con un octeto (8 bits) se puede representar 256 números (2 ), ya que se empieza desde el cero
se pueden representar números entre 0 y 255.
Tal cual se muestra en el siguiente ejemplo.
Ejemplo 2.13: Representa el número 29(10) en código binario sin signo utilizando un octeto.
Primero convertimos el número 29(10) a binario, lo cual resulta ser 1 1101(2)
Dado que el número en binario sólo ocupa 5 bits agregamos tres 0´s a la izquierda del número para
completar el octeto y obtener la representación deseada.
1 2 3 4 5 6 7 8
0 0 0 1 1 1 0 1
Por lo tanto, la representación de 29(10) en código binario sin signo es 0001 1101(2)

2.4. 2 Código binario en signo y magnitud

Ante la necesidad de tener que representar enteros negativos, se decidió reservar un bit para representar
el signo, ya que sólo se requiere representar alguna de dos opciones: positivo o negativo. Para este fin
se destina el bit más significativo (el que esta más a la izquierda), el cual es 0 para valores positivos y
1 para los negativos. La magnitud en valor absoluto del número se representa en binario en el resto de
los bits. A está representación se le denomina codificación binaria en signo y magnitud.

Capítulo 2 | Página 13
Ejemplo 2.14: Representar en signo y magnitud en 8 bits los números 29 y -29:
El valor absoluto de -29 y 29 es 29, la representación en binario es 11101.
Luego entonces, como se necesita representar en 8 bits distribuidos de la siguiente manera: el bit más
significativo reservado para el signo y los 7 bits restantes para el valor absoluto del número, tenemos
que agregar dos 0´s a la izquierda de la representación en binario para completar los 7 bits
correspondientes al valor absoluto del número.
1 2 3 4 5 6 7 8
0 0 1 1 1 0 1
Y por último asignar al bit más significativo 0 para representar el número 29 y 1 para representar el -
29. De esta manera, la codificación en signo y magnitud queda de la siguiente manera:
+29 0 0011101
- 29 1 0011101

Como en un octeto sólo quedan siete bits para representar la cantidad, en esta codificación un octeto
puede representar números en el rango de -127 a 127.
Un inconveniente en esta representación es que, el cero tiene dos posibles representaciones como
número negativo o como positivo.
+0 0 000 0000
-0 1 000 0000

En general, con 𝑛 bits podemos representar 2 números enteros con signo que está entre
[ −(2 − 1) , 2 − 1 ]1
Por ejemplo, para 16 bits el número de valores es 2 que es 65,536 y los valores están entre – (2 −
1) y ( 2 − 1), ya que (2 − 1 es igual a 32767, entonces el rango es de -32467 a 32467
(considerando que el cero tiene dos representaciones).

2.4. 3 Código binario en complemento a uno

En esta codificación los números positivos se representan como en el sistema binario en magnitud y
signo, es decir, representando la magnitud en valor absoluto en binario y reservando el bit más
significativo para el signo que en el caso de los positivos debe ser cero. En cambio, los números
negativos se utiliza el complemento a uno, que consiste en tomar la representación del correspondiente

1 Se resta menos uno porque se está considerando al cero.

Capítulo 2 | Página 14
número positivo e intercambiar los bits (se cambian los bits 0 por 1 y viceversa) de tal manera que el
bit más significativo del número positivo - que es cero- se vuelve 1.
Para explicar el método anterior revisemos el siguiente ejemplo:
Ejemplo 2.15: Representar en complemento a uno los números 29 y -29:
El valor absoluto de 29 en binario es: 11101
Por lo tanto, la representación en complemento a uno de +29 utilizando 8 bits es:

+29 00011101

Para el -29 tomamos su representación positiva y aplicamos complemento a uno (intercambiamos los
bits)

+29 0 0 0 1 1 1 0 1

-29 1 1 1 0 0 0 1 1

Por lo tanto, la representación de -29 en complemento a dos es:

- 29 1 1 1 00 10

En esta codificación para el cero seguimos teniendo una doble representación, los rangos se calculan
de la misma forma que en código binario con signo y magnitud.

2.4. 4 Código binario en complemento a dos

Para el manejo de operaciones básicas como el caso de la suma y resta, las representaciones previas no
son muy apropiadas, ya que el manejo del signo y la magnitud se tienen que operar por separado,
aunado a la doble representación del cero. La representación en complemento a dos resuelve estos
problemas, lo que la hace la representación de enteros con signo más común y de mayor uso en la
actualidad.

Capítulo 2 | Página 15
En esta codificación, se sigue considerando el bit más significativo para codificar el signo y los números
positivos se siguen representando como en las anteriores. En el caso de los números negativos, se utiliza
un sistema distinto, denominado complemento a dos, el cual se explica a continuación.
Para obtener el complemento a dos de un número binario utilizando 𝑛 bits, se puede utilizar el siguiente
método
1. Codificar en binario el valor absoluto del número utilizando 𝑛 bits
2. Buscar de derecha a izquierda el primer uno y a partir del siguiente bit (el
siguiente a la izquierda) intercambiar los bits, esto es cambiar unos por ceros
y ceros por unos.

Entonces para representar un número negativo se complementa a dos su representación positiva, de tal
forma que el uno para el signo negativo resulta de forma implícita.

Ejemplo 2.16: Representar en complemento a dos de los números +28 y -28:


La representación en complemento a dos del número 28 es igual que en las anteriores:
+28 00011100
Para obtener la representación de -28 utilizamos la representación anterior de 28 y localizamos el
primer bit de derecha a izquierda para intercambiar los bits siguientes (a la izquierda), como se muestra
a continuación.
0 0 0 1 1 1 0 0

1 1 1 0 0 1 0 0

Por lo tanto, -28 en complemento a dos se representa como 11100100.

Una ventaja de esta codificación es que no hay dos representaciones para el cero, la única
representación del cero es 0´s en todos los bits. Así que el número que tiene 1´s en todos los bits
corresponde al valor negativo – 2 . De esta manera, el rango se define como:
[−2 , 2 − 1]

Capítulo 2 | Página 16
2.5 REPRESENTACIÓN DE NÚMEROS CON PUNTO DECIMAL
En lo que respecta a los números con punto decimal existen dos codificaciones:
 Punto fijo
 Punto flotante
En ambas, el número se divide en dos partes: el entero y la fracción.
La representación de los números decimales será un tema en el cual no profundizaremos, ya que rebaza
el objetivo de este material. Únicamente describiremos de manera general ambas representaciones, si
se desea conocer más en el tema se recomienda consultar [Joyanes,2003].

2.5. 1 Código en punto fijo

Bajo esta codificación, la posición del punto es la que fija la potencia de la base por la que hay que
multiplicar el dígito correspondiente para obtener el valor representado. Considerando que los dígitos
que están antes del punto se enumeran de derecha a izquierda iniciando en 0, en tanto que los dígitos
que están después del punto se enumeran de izquierda a derecha iniciando con -1.

Ejemplo 2.17: El valor 21.75 representando en punto fijo en 8 bits es 10101.110 ya que:
10101.110 = (1×24 + 0×2³ + 1×2² + 0×2¹ + 1×20 )+ (1×2-1 + 1×2-2 + 0×2-3 )= 21.7510

La representación anterior recibe el nombre de punto fijo, ya que se fija el número de bits que se
reservan para la parte entera y para la fraccionaria. Como en el ejemplo anterior se están considerando
5 bits para parte entera y 3 para la parte fraccionaria.

Al usar la notación en punto fijo, queda muy limitado el número de cantidades que se pueden
representar y todas ellas deben tener la misma resolución, lo que ocasiona que se pierdan algunos
decimales en la representación.

2.5. 2 Código en punto flotante

Se denomina punto flotante (abreviado comúnmente como FP, del inglés floating point) al método de
representación de números con punto decimal que, en oposición al punto fijo, permite que la posición
del punto se mueva (flote) a cualquier posición del número, permitiendo con ello un rango mayor de
los números que es posible representar. Sin embargo, el hecho de que el punto se mueva hace que el
número de bits designados para la parte entera y para la parte decimal cambie de un número a otro, lo

Capítulo 2 | Página 17
que hace difícil realizar operaciones. La solución consiste en normalizar la representación con lo que
se desplaza el punto decimal de tal forma que siempre haya un uno a la izquierda del punto.

Los números representados en punto flotante siguen el siguiente patrón:

𝑟 =𝑠 ×𝑚×𝑏

Donde:

𝑠: signo con la convención de 0 para positivo y 1 para negativo.

𝑚: mantisa que es el número binario a la derecha del punto decimal después de la


normalización.

𝑏: base del sistema de representación (2 en sistema binario)

𝑒: exponente que indica la cantidad de posiciones hacia la izquierda del desplazamiento de la


coma al normalizar(el desplazamiento negativo denota desplazamiento de la coma hacia la
derecha).
Para almacenar un número codificado en punto flotante se guarda únicamente el signo, la matisa y el
exponente.

El Instituto de Ingenieros Eléctricos y Electrónicos (IEEE)2 ha definido dos estándares para guardar
representaciones en punto flotante, el formato de precisión doble y el formato de precisión simple.
Ambos formatos almacenan en el bit más significativo el signo, seguido del exponente y al final la
mantisa. En la Figura 2.4 se muestra el número de bits designados en cada formato para ambos
estándares.

1 bit 8 bits 23 bits


Signo Exponente Mantisa
a) Precisión simple

1 bit 11 bits 53 bits


Signo Exponente Mantisa
b) Precisión doble

Figura 2.4. Estándares IEE para representación en punto flotante

2 Por sus siglas en inglés Institute of Electrical and Electronics Engineers

Capítulo 2 | Página 18
Ejemplo 2.18: Codifiquemos el número decimal -96.75 usando el sistema de precisión simple de
IEEE.

Empecemos por codificar el signo, dado que es un número negativo, el signo es "1".

En seguida, escribimos el número (sin signo) usando notación binaria en punto fijo.

Decimal 96 - 75
Binario 1100000 - 110

Una vez expresada así, se desplaza el punto decimal a la izquierda, dejando sólo un 1 a su izquierda.
1100000.110=1.100000110·26 Esto es un número en coma flotante normalizado.

Una vez normalizado el número, la mantisa es la parte que queda a la derecha del punto decimal,
completando con ceros a la derecha hasta que obtengamos los 23 bits, la mantisa queda representada
entonces como:

10000011000000000000000.

Para codificar el exponente es necesario que el exponente más negativo sea 0 del tal forma que todos
los exponentes son solamente número binarios no negativos. Por lo anterior es necesario hacer un
desplazamiento de 127 sobre el exponente, para el formato IEEE de 32 bits, dado que se tienen 8 bits
para el exponente. Así que el exponente será 6+127=133.

En binario, esto se escribe como 10000101

Por lo tanto la representación en punto flotante en precisión simple (32bits) queda de la siguiente
forma (figura 2.2):

Figura 2.2. Representación en punto flotante en precisión simple de -118.625

Capítulo 2 | Página 19
2.6 REPRESENTACIÓN DE CARACTERES

Recordemos que básicamente, las computadoras sólo trabajan con números representados en binario.
Por lo tanto, para que la computadora almacene caracteres es necesario codificarlos de alguna forma.
Usar estándares aumenta la eficiencia y elimina errores al procesar datos.
El estándar americano de codificación para el intercambio de información, código ASCII (American
Standard Code for Information Interchange), es una de las alternativas más comunes para codificar
caracteres, este estándar nos ayuda a representar caracteres y símbolos en forma electrónica. La
codificación ASCII usa 7 bits para codificar 128 caracteres distintos. Se trata de valores numéricos
entre 0 y 127 que sustituyen a las letras del alfabeto y otros caracteres escritos.
Para poder usar más caracteres que no se incluyen en la especificación ASCII original, se decide usar
un bit más, aprovechando que el tamaño mínimo de almacenamiento de los registros reales de las
computadoras es comúnmente 8 bits, con lo que se extiende el código ASCII, permitiendo hasta 256
caracteres distintos (enumerados de 0 hasta 255), algunos de ellos utilizados muy frecuentemente como
las vocales acentuadas y la letra ñ (Figura 2.3).
En el código ASCII los primeros 32 símbolos corresponden a caracteres especiales no imprimibles
como lo son el salto de línea y el retorno de carro (Figura 2.1), los restantes son todos caracteres
imprimibles. Los caracteres correspondientes con letras mayúsculas y minúsculas tienen códigos
distintos ya que no son el mismo carácter; por ejemplo, el código ASCII de ‘a’ es 97 y de ‘A’ es 65.
Tanto las letras (mayúsculas y minúsculas) como los dígitos tienen códigos contiguos como podemos
ver en la figura 2.2.

Figura 2.1. Tabla de caracteres ASCII no imprimibles

Capítulo 2 | Página 20
Figura 2.2. Caracteres ASCII imprimibles

Capítulo 2 | Página 21
Figura 2.3. Caracteres ASCII extendidos

En el siguiente ejemplo se describe como se representa una palabra, utilizando el código ASCII.
Ejemplo 2.18: Escriba la palabra “UaCm” en código ASCII.
Si se sabe que en código ASCII del número decimal 65 al 90 son letras mayúsculas del abecedario y
del 97 al 122 corresponden las letras minúsculas.
caracter Código
ASCII
U 85
a 97
C 67
m 109

De esta manera, lo que se almacena en la memoria de la computadora es el número correspondiente a


cada símbolo representado en binario. En este caso:
caracter Código binario
(8 bits)

U 01010101
a 01100001
C 01000011
m 01101101

Capítulo 2 | Página 22
EJERCICIOS DEL CAPÍTULO No. 2

I. Completa los siguientes enunciados

a. Todo sistema numérico posicional se define a partir de una base y un _________________


, el cual incluye tantos dígitos como valor de la base.

b. Los dígitos válidos para el sistema numérico ____________________ son 0 y 1.

c. El bit más significativo es el que se encuentra en el extremo _________________________.

d. _____________________ es el método de representación de enteros signados más


difundido actualmente.

e. En la codificación binaria en signo y magnitud el bit más significativo se utiliza para


codificar _____________

f. Para almacenar un número en punto flotante se almacena únicamente 3 cosas: signo,


_____________ y ______________

g. La IEEE definió los estándares de precisión simple y doble para almacenar valores
representados en __________________

h. Los primeros 32 caracteres del código ASCII son _____________________________

i. Una desventaja de la representación en ______________________ es que hay una doble


representación para el cero, al igual que en signo y magnitud.

j. El código ASCII asigna valores contiguos a partir del 48 decimal a los


_________________.

II. Expresa los siguientes números decimales en binario, hexadecimal y octal

a. 35

b. 127

c. 16

d. 1324

Capítulo 2 | Página 23
III. Convierte a decimal los siguientes valores (el subíndice indica la base en la cual está
representado)

a. 231(8)

b. 1101(16)

c. 00111101(2)

d. 2A4B(16)

e. 10101010(2)

IV. Para cada uno de los siguientes números representados en complemento a uno, con 8 bits, indica
que signo tienen y su valor absoluto.

Número Signo Valor absoluto


10001111
01111101
01011010
11111111

V. Imagina que se guardó una palabra de 7 caracteres en la computadora y el contenido de la


memoria en donde se almaceno es:

Caracter1: 0 1 0 1 0 1 0 1
Caracter2: 0 1 0 0 0 0 0 1
Caracter3: 0 1 0 0 0 0 1 1
Caracter4: 0 1 0 0 1 1 0 1
Caracter5: 0 1 0 0 1 0 0 1
Caracter6: 0 1 0 1 0 1 0 0
Caracter7: 0 1 0 0 0 0 0 1

Descifra la palabra almacenada en memoria.

Capítulo 2 | Página 24
VI. Expresa en hexadecimal y octal los siguientes números binarios

a. 11110011

b. 00111011

c. 01101111

VII. Expresa en complemento a uno y en complemento a dos, utilizando 8 bits, los siguientes valores

a. -23

b. 15

c. -7

d. 23

e. 32

VIII. ¿Cuántos bits se designan para la mantisa y cuántos para el exponente según el estándar de
precisión doble de la IEEE ?

Capítulo 2 | Página 25
Capítulo
Algoritmos

“La pregunta de si una computadora puede pensar no es más interesante


que la pregunta de si un submarino puede nadar”.

Edsger Dijkstra

OBJETIVOS:
Al finalizar este capítulo el estudiante:

o Reconocerá los pasos que deben realizarse para solucionar un problema con una computadora,
enfocándose principalmente en el análisis del problema y diseño de la solución.
o Describirá las principales características de los algoritmos y desarrollará el pensamiento lógico
para escribir algoritmos.
o Diferenciará entre las dos formas de representar algoritmos: diagrama de flujo y pseudocódigo.
o Distinguirá las diversas estructuras de control involucradas en un algoritmo.

3.1 INTRODUCCIÓN
Para solucionar un problema utilizando como herramienta a la computadora necesitamos crear un
programa, en el cual se defina el conjunto de instrucciones que ésta debe realizar; tales órdenes deben
estar escritas en un lenguaje de programación para que puedan ser interpretadas por la computadora.
Llamamos ciclo de vida de software a las etapas que se siguen para crear un programa. Aunque existen
diferentes propuestas de cuáles deben ser estas fases, todas ellas comienzan con el análisis del
problema, en donde se define claramente qué se desea obtener y con qué se cuenta. El siguiente paso
es crear un modelo, que consta de los pasos que deben realizarse para obtener el resultado deseado –
lo que se conoce como algoritmo. Finalmente, la siguiente fase es su codificación en un lenguaje de
programación.
Por otro lado, la construcción de un programa demanda inteligencia, creatividad y mucha persistencia,
es por ello que en esta tarea se aplica el viejo y conocido refrán: “la práctica hace al maestro”, entre
más problemas solucionemos, más experiencia adquiriremos y será más fácil solucionar nuevos
problemas planteados. Podemos asegurar que programar es una experiencia muy satisfactoria pues el
resultado final del proceso es un programa ejecutado en una computadora, esto sin dejar de lado lo
excitante que puede volverse solucionar problemas cada vez más complicados.
En este capítulo nuestra meta será construir algoritmos que resuelvan problemas simples, de tal manera
que conforme avancemos en los capítulos subsecuentes la complejidad de éstos se irá incrementando

Capítulo 3 | Página 1
de acuerdo con la experiencia adquirida en el trayecto. Para alcanzar tal fin utilizaremos el paradigma
de la programación estructurada.

3.2 FASES EN LA RESOLUCIÓN DE UN PROBLEMA


El ciclo de vida del software describe las fases por la que atraviesa el desarrollo de un programa desde
el inicio hasta el final, de tal manera que se pueda garantizar que el programa cumpla con los requisitos
requeridos. El ciclo permite que los errores se detecten lo antes posible de tal modo que el programa
sea correcto y se termine en los tiempos esperados.
El ciclo de vida básico de un software consta de las siguientes fases:
Análisis del problema: En esta fase se debe establecer claramente qué debe hacer el programa. Es
necesario comprender a fondo el problema planteado, delinear su ámbito (dominio), características y
limitaciones, por lo que debemos prestar atención a todos los detalles y elementos que sean
indispensables para construir el modelo. El producto de esta fase es una especificación precisa del
problema, que incluya las condiciones iníciales o datos requeridos para resolverlo – a los cuales
llamaremos datos de entrada – y el resultado deseado – también conocido como datos de salida.
Adicionalmente se puede especificar o dar un bosquejo de algún método que se seguirá, como por
ejemplo: fórmulas matemáticas, algoritmos prediseñados, módulos en los que se divide, entre otros.1

Diseño de la solución: Durante este paso se define concretamente cómo el programa resolverá el
problema construyendo un algoritmo. Existen diferentes metodologías para solucionar un problema,
pero sin importar cual se utilice, la solución debe ser correcta, eficiente y efectiva. Otro requisito
deseable de la solución es que sea modular, delineando claramente la responsabilidad o tarea que tiene
cada módulo. Para tal fin, nos basaremos en la programación estructurada y modular que propone
dividir un problema en subproblemas más pequeños y fáciles de resolver.

Codificación o implementación: En esta etapa se debe traducir el algoritmo a un lenguaje de


programación (como por ejemplo C). Si el diseño de la solución cumple con la filosofía2 del lenguaje
entonces la implementación del algoritmo se facilita.

Validación y pruebas: La validación de un programa no necesariamente tiene que ocurrir hasta que el
programa se haya terminado, es conveniente realizar pruebas intermedias o parciales para garantizar
que el algoritmo “hace lo que debe hacer”. Existen dos tipos de pruebas: formales y empíricas, las
primeras conllevan una serie de demostraciones lógico-matemáticas que permiten validar que con
cualquier conjunto de datos de entrada el algoritmo y/o el programa funcionan correctamente; en tanto
que las segundas proponen ejecutarlos con datos de entrada tanto válidos como inválidos, sólo que en
este caso no puede afirmarse que la solución sea correcta sólo se puede verificar que la salida es el
resultado esperado para algunos datos.

1En este material los problemas planteados son simples de analizar y de resolver comparados con los proyectos reales, pues nuestro fin es
estudiar la programación estructurada y modular.

2Cada lenguaje de programación tiene una filosofía diferente, la cual tiene que ver con la forma en la que se modela el mundo y se conoce
como paradigma de programación. Algunos ejemplos son: modular y estructurado, orientado a objetos, orientado a eventos, lógico o funcional.

Capítulo 3 | Página 2
Documentación: En todo proyecto de software se debe realizar un documento que describa las fases
del ciclo de vida del mismo, esencialmente, análisis, diseño y codificación; además debe incluir un
manual de usuario y referencias, así como las normas de mantenimiento. Se recomienda generar la
documentación desde el inicio del desarrollo y de forma progresiva.

Mantenimiento: El programa se actualiza o modifica cada vez que sea necesario, ya sea para corregir
errores no detectados en el análisis, diseño o implantación, o bien, para extender su capacidad
incluyendo nuevas funciones.

El orden en el cual se realice cada una de las fases depende del modelo de ciclo de vida que se utilice,
en nuestro caso adoptaremos el Modelo de Cascada (ilustrado en la figura 3.1) el cual tiene como
principal característica la no linealidad debido a que en cualquier momento se puede regresar a
cualquiera de las etapas anteriores para corregir un error o modificar el comportamiento del software.
Este modelo será nuestra guía para construir programas a lo largo de este material.

Análisis del
problema
Ciclo de vida del
Diseño de la software
solución

Implementación o
codificación

Pruebas y validación

Documentación del
software

Mantenimiento

Figura 3.1. Ciclo de Vida del software (Modelo en Cascada)

Capítulo 3 | Página 3
Como podremos imaginar, las etapas que demandan mayor esfuerzo son el análisis y diseño, en la
primera tenemos que definir lo que debemos solucionar de forma precisa mientras que en la segunda
debemos utilizar todos nuestros conocimiento, inteligencia y creatividad para proponer una solución
empleando únicamente instrucciones permitidas. Podemos asegurar que si se dedica el tiempo
necesario en estas etapas se evitará cometer errores que nos obliguen a regresar a una de estas fases.
Por tal motivo, el resto del capítulo nos enfocaremos sólo en estas etapas.

3.2.1 Análisis del problema

El éxito para solucionar un problema inicia con el esfuerzo que hagamos para entenderlo. No podemos
pensar en cómo lo vamos a hacer sin haber comprendido a fondo qué es lo que vamos a hacer. Lo más
recomendable es extraer del enunciado del problema (el cual muchas veces puede ser impreciso) una
lista de tareas específicas que se deben realizar, definir claramente el resultado esperado y los datos de
entrada que el programa requerirá, en otras palabras, antes que cualquier cosa debemos contar con una
especificación precisa del problema, de lo contrario podríamos vernos en una situación parecida a la
que se ilustra en la figura 3.2, en la cual los distintos personajes tienen en mente cosas muy diferentes.

Solicitud del Lo que entendió el Lo que plantea el El funcionamiento


usuario desarrollador modelo. del programa

Figura 3.2. Ejemplo de una especificación ambigua3

Cuando empezamos a resolver problemas por medio de la computadora, es posible que nuestra
sensación sea similar a tener que ordenar nuestra casa después de una tremenda fiesta… ¡no sabemos
dónde empezar! Sin embargo, esto va desapareciendo conforme resolvemos más y nuevos problemas.

Si a partir del planteamiento del problema no es claro ¿qué se requiere? o ¿cómo se puede solucionar?
se recomienda realizar una simulación –en papel– de lo que debe hacer el programa, identificando qué

3 La figura es una adaptación de la ilustración “malos entendidos…”

Capítulo 3 | Página 4
datos se requieren, en qué momento y cómo se van a almacenar; además de las operaciones matemáticas
que se deben realizar y la información se debe hacer llegar al usuario. Una simulación paso a paso nos
permite estructurar nuestro pensamiento, de tal forma que podamos abstraer el procedimiento que
seguimos para resolver el problema y plasmarlo en un algoritmo. Podemos utilizar tablas, dibujos,
diagramas de flujo, fórmulas, esquemas, mapas conceptuales, mapas mentales, cuadros sinópticos,
esquemas organizativos, o cualquier cosa que nos permita llegar a nuestro objetivo. En la tabla 3.1 se
presenta una lista de preguntas que pueden ayudar en el análisis de un problema determinado.

Interrogante Información obtenida


¿Cuál es el resultado deseado? Datos de salida que se deben reportar.

¿Qué método(s) se puede(n) utilizar para llegar a la La forma en la que podemos obtener el resultado
solución? ¿Es posible dividir el problema en puede consistir en: fórmulas matemáticas, tablas,
una lista con los pasos generales para llegar a la
problemas más pequeños? solución, los cuales pueden representar los
subproblemas en los que se divide el problema
original, entre otros.
¿Qué información debe proporcionar el usuario del A partir de la especificación del problema y/o del
programa para llegar al resultado deseado? método se pueden identificar qué datos de entrada
– proporcionados por el usuario – se requieren para
llegar al resultado deseado. Se recomienda
asignarles un identificador nemotécnico,
opcionalmente se puede especificar su tipo:
numérico, carácter, cadenas.
¿Existe alguna restricción para los datos de Se refiere a las condiciones que deben cumplir los
entrada? datos de entrada para que sea posible llegar a la
solución. Por ejemplo, el radio de una
circunferencia debe ser un número positivo.
¿Qué datos o información adicional es necesaria Se refiere principalmente a datos que se requieren
para solucionar el problema? y que no son proporcionados por el usuario, por
ejemplo, para calcular el perímetro o área de una
circunferencia es necesario conocer el valor de 𝜋.
Tabla 3.1: Preguntas guía para el análisis de un problema

Con el fin de ilustrar lo anterior analicemos el siguiente problema.

Capítulo 3 | Página 5
Ejemplo 3.1:4 Se requiere un programa que calcule el volumen de un planeta ovoide, considerando que
es posible medir sus semiejes (ver figura 3.3).

Planeta ovoide

Semieje menor (m)

Semieje mayor (M)

Figura 3.3 Planeta ovoide

Tomando como referencia la Tabla 3.1, realicemos el análisis del problema planteado.

Interrogante Información Resumen

¿Cuál es el resultado Salida: Vol (volumen del


Volumen del planeta (vol)
deseado? planeta, numérico)

¿Qué método(s) se Para calcular el volumen de un


ovoide se tiene la siguiente
puede(n) utilizar para fórmula: 4
llegar a la solución? 4 vol = 𝜋 𝑀2 m
𝑣𝑜𝑙 = 𝜋 𝑀 m Método: 3
¿Cómo se puede dividir el 3
Donde M y m representan los
problema? semiejes mayor y menor,
respectivamente.
¿Qué información debe
De acuerdo con la fórmula, los
proporcionar el usuario datos que debe proporcionar M (eje mayor,
Datos de
del programa para llegar el usuario son: la medida del
entrada:
numérico)
al resultado deseado? semieje mayor(M) y la del m (eje menor,
semieje menor (m) numérico)

¿Existe alguna Ambos ejes deben ser


restricción para los datos positivos. Sin embargo, el dato
de entrada? que se ingrese al eje mayor, Restricciones:
M >m > 0
no pude ser más pequeño que
el que se ingresa al eje menor.

¿Qué datos o información


adicional es necesaria En las fórmula se utiliza el
Constantes:
para solucionar el valor de la constante 𝜋, el cual PI = 3.141592
tomaremos como 3.141592
problema?
Tabla 3.2 Análisis del problema del planeta ovoide

4 Este ejemplo es una adaptación de un problema que aparece en [Peñaloza2004], página 117.

Capítulo 3 | Página 6
3.2.2 Diseño de la solución

Desafortunadamente no existe un método universal que nos permita resolver todos los problemas, sin
embargo, uno de los métodos más utilizados está basado en la estrategia de Julio César que a la letra
dice “divide y vencerás”. En el contexto de programación esto se traduciría como “divide un problema
en unidades más pequeñas y fáciles de resolver”. Diversas metodologías de programación están
basadas en esta frase, entre ellas el diseño estructurado que será el que utilizaremos para diseñar
algoritmos.

Cabe mencionar que un algoritmo solo puede ser ejecutado por una computadora hasta que se traduce
a un lenguaje de programación, en otras palabras, un algoritmo no es un programa.

Definición 3.1 Un algoritmo es una sucesión de pasos precisos, definidos y finitos para resolver un
problema, el cual trabaja a partir de cero o más datos de entrada y produce un resultado, en otras
palabras, es un proceso que transforma un conjunto de datos de entrada en un conjunto de datos de
salida.

Donde:
 Pasos precisos, indica que cada paso está bien definido y debe realizarse en un orden
establecido.
 Definido, siempre que sigamos el algoritmo con el mismo conjunto de datos de entrada
obtendremos el mismo resultado (determinismo)
 Finito, el número de pasos está determinado y debe producir la salida en un tiempo finito
(terminación)

Existen diferentes formas de representar un algoritmo, tanto informales –como es el caso del lenguaje
español– o formales –como son las fórmulas matemáticas–, por ejemplo, un algoritmo para calcular el
volumen de un planeta ovoide es:
4
vol = 𝜋 𝑀2 m
3
O bien, podemos escribirlo en español de la siguiente manera.

Cálculo del volumen de un planeta ovoide

 Obtener el semieje mayor, representado por M


 Obtener el semieje menor, representado por m
 Calcular el cuadrado del eje mayor, es decir 𝑀
 Multiplícalo por tres cuartos de 𝜋, y
 Finalmente multiplica el resultado obtenido por el eje menor m.
Algoritmo 3.1 Volumen de un planeta ovoide (Lenguaje Natural)

Lo importante de una representación es que sea clara y concisa, por tal motivo utilizaremos dos
representaciones que tienen estas características: pseudocódigo y diagramas de flujo. El pseudocódigo

Capítulo 3 | Página 7
está compuesto por un pequeño conjunto de palabras –en nuestro caso en español– mientras que el
segundo es un conjunto de símbolos con un significado específico.

3.2.2.1 Pseudocódigo

El pseudocódigo es un lenguaje de especificación formal de algoritmos. La solución de un problema


se representa de manera narrativa utilizando palabras claves del un lenguaje natural (en nuestro caso
español), las cuales generalmente son verbos.
Recomendaciones para escribir un algoritmo en pseudocódigo:

1. Se escribe la palabra pseudocódigo seguida de dos puntos y un nombre que describa de manera
general el problema que resuelve el algoritmo.
2. Las instrucciones deben escribirse en el mismo orden en el que se deben ejecutar, el punto de
partida de un algoritmo en pseudocódigo es la palabra inicio y termina con la palabra fin. Se
recomienda dejar sangrías cuando una instrucción forma parte de otra más compleja para
facilitar la lectura.
3. Cuando el problema se ha dividido debemos especificar los diferentes módulos en los cuales
se dividió, seguido de una breve descripción de la subtarea que realiza.
4. En caso de existir valores constantes estos se pueden describir indicando su valor y
opcionalmente un nombre.
5. En caso de haber variables también es necesario especificarlas indicando su nombre,
descripción y opcionalmente el tipo.

En la tabla 3.3 se muestra la sintaxis en pseudocódigo de las instrucciones y en la tabla 3.4 la


sintaxis en pseudocódigo de las estructuras de control que se utilizan para resolver problemas con
la computadora. Las palabras que están resaltadas con negro son la palabras claves y no pueden
cambiarse ya que representan instrucciones, mientras que las palabras que están encerradas entre
mayor y menor que (“< >”) son los elementos de la instrucción que se modifican de acuerdo al
problema que se trate.

Tipo Palabra Clave Descripción


Inicio Representa el inicio del algoritmos
Terminal
Fin Representa el fin del algoritmo
Envía a pantalla un <mensaje> o el valor de
una <variable> indicada. En caso de que se
Imprimir
imprima un mensaje debe estar escrito entre
Entrada y <mensaje/variable>
comillas, si es el valor de una variable sólo se
Salida pondrá el nombre de la variable (sin comillas)
Leer <variable> Lee un dato del teclado y lo almacena en la
<variable> indicada.
Tabla 3.3 Palabras claves del Pseudocódigo.

Capítulo 3 | Página 8
Tipo Palabra Clave Descripción
Condicional simple
Si <condición> entonces
Si la <condición> es verdadera, se ejecuta el
<instrucciones>
conjunto de <instrucciones>.
Fin_Si

Si <condición> entonces
<instrucciones V> Condicional doble
Sino Si la <condición> se cumple se ejecuta el
conjunto de <instrucciones V>, en caso
< instrucciones F> contrario se realizan las <instrucciones F>.
Condiciones Fin_Si-Sino
Seleccionar <variable>
Caso <valor1> :
<instruccionesvalor1>
Caso <valor2> : Condicional múltiple
<Instruccionesvalor2> Dependiendo del valor de la <variable> ejecuta
… las instrucciones correspondientes a éste.
Otro:
<instruccionesotro>
Fin_Seleccionar

El conjunto de <instrucciones> se ejecuta


Mientras <condición>
repetidas veces siempre que la <condición>
hacer
sea verdadera, después de cada ejecución del
<instrucciones>
conjunto de instrucciones se revisa si la
Fin_Mientras
condición se sigue cumpliendo.

Hacer
Se ejecutan las <instrucciones> y después
<instrucciones>
verifica si la <condición> se cumple, en tal
Ciclos o Mientras <condición>
caso se repite el ciclo.
repeticiones Fin_Hacer-Mientras
Indica cuántas veces se va a realizar un
Desde <valor inicial> conjunto de instrucciones por medio de una
Mientras <condición>, variable contador. <valor inicial> se refiere al
<decremento/incremento>, valor con el cual la variable contador inicia el
ciclo, la <condición> generalmente incluye a la
<instrucciones> variable contador y mientras que ésta se
cumpla se realizarán las <instrucciones>, el
Fin_Desde <decremento/incremento> se hace sobre la
misma variable.
Llamar a <módulo> Indica que un <módulo> o función debe
Módulos
ejecutarse
Tabla 3.4 Representación de estructuras de control y módulos en Pseudocódigo.

Capítulo 3 | Página 9
Ejemplo 3.2: Representación en pseudocódigo del algoritmo del planeta ovoide

Pseudocódigo: planeta ovoide


Módulos:
Sólo el principal, no es necesario dividir el problema.

Algoritmo:

Inicio
Imprimir “Proporciona el semieje mayor”.
Leer M
Imprimir “Proporciona el semieje menor”.
Leer m
4
𝑣𝑜𝑙 ← 𝜋𝑀 m
3
Imprimir “Volumen =”, vol
Fin

Constantes utilizadas:
PI = 3.141592

Variables utilizadas:

o M: se utiliza para almacenar el valor del eje mayor (numérico).


o m: se utiliza para almacenar el valor del eje menor (numérico).
o vol: almacena el volumen del planeta ovoide (numérico)
Algoritmo 3.2 Volumen de un planeta ovoide (Pseudocódigo)

Nota:
Recuerda que el símbolo ← denota la operación de asignación, esta notación se seguirá empleando en
los algoritmos que presentaremos en lo sucesivo.

3.2.2.2 Diagramas de Flujo

Los diagramas de flujo son una representación gráfica de los algoritmos, utiliza símbolos para
representar las instrucciones y flechas – llamadas líneas de flujo – para unir e indicar el orden en que
deben ejecutarse. Estos símbolos fueron normalizados por el Instituto Norteamericano de
Normalización ANSI (American National Standars Institute, por sus siglas en inglés). Los símbolos
más utilizados se muestran en la tabla 3.5.

Capítulo 3 | Página 10
Símbolo Descripción
Terminal. Representa el inicio y el final de un algoritmo.
Terminal

Entrada y Salida (E/S). Representa la lectura de datos desde el


E/S dispositivo de entrada estándar, así como la impresión de datos en el
dispositivo de salida estándar.

Proceso. Representa cualquier tipo de operación que pueda originar un


cambio de la información almacenada en memoria, asignaciones u
Proceso
operaciones aritméticas.

Decisión. Nos permite analizar una situación, con base en los valores
verdadero y falso. Toma una decisión de las instrucciones que a
Decisión continuación ejecuta el algoritmo.

Conector. Sirve para enlazar dos partes cualesquiera del diagrama que
están en la misma página.

Línea de flujo. Indica el orden de la ejecución de las operaciones. La


flecha indica cuál es la siguiente instrucción que se debe realizar.
Conector. Conecta a dos puntos del diagrama cuando éstos se
encuentran en páginas diferentes. Representa el inicio y el final de un
programa.
Llamada a subrutina. Llama a un proceso determinado o subrutina. Una
Subrutina subrutina es un módulo independiente del módulo principal, que realiza
una tarea determinada y al finalizar regresa el control de flujo al módulo
principal.
Tabla 3.5 Símbolos de los diagramas de flujo.

La tabla 3.6 contiene los símbolos gráficos utilizados para representar instrucciones y la tabla 3.7 los
símbolos gráficos para representar las estructuras de control que nos permitirán resolver los
problemas propuestos a lo largo de los siguientes capítulos.

Tipo Diagrama Descripción

Representa el inicio del


inicio
algoritmos
Terminal
Representa el fin del
fin algoritmo

Capítulo 3 | Página 11
Envía a pantalla un
mensaje <mens>, el cual
imprimir <var/mens> debe ir entre comillas, o
Entrada y bien, el valor de una
Salida variable indicada <var>.
Lee un dato del teclado
leer <variable> y lo almacena en la
<variable> indicada.
Tabla 3.6 Representación gráfica de instrucciones en diagrama de flujo.

Tipo Diagrama Descripción

<condición>
V F Condicional Simple
Sólo si la <condición>
<instrucionesV> se cumple se realizan las
<instruccionesV>

Condicional Doble
<condición>
Si la <condición> se
V F
cumple se realizan las
Condiciones <instruccionesV> en
<instrucionesV> <instrucionesF> caso contrario se
realizan las
<instruccionesF>.

<variable> Condición Múltiple


Indica las instrucciones
Valor1
Valor2 ValorN que deben realizarse
para cada posible valor
<Instruc1> … <InstrucN>
que pudiera tomar la
<variable>.

Tabla 3.7 Representación gráfica de las estructuras de control en diagrama de flujo.

Capítulo 3 | Página 12
Tipo Diagrama Descripción

Indica cuántas veces se


va a realizar un conjunto
Inicialización
de instrucciones por
medio de una variable
contador. <valor inicial>
<condición>
F se refiere al valor con el
cual la variable
V contador inicia el ciclo,
<Instrucciones> la <condición>
generalmente incluye a
la variable contador y
mientras que ésta se
<Inc/Dec>
cumpla se realizarán las
<instrucciones>, el
<inc/dec> se hace sobre
la misma variable.

Ciclos o
Se ejecutan las
repeticiones
V <instrucciones> <instrucciones> y
después verifica si la
<condición> se cumple,
<condición>
en tal caso se repite el
ciclo.
F

El conjunto de
<instrucciones> se
ejecuta repetidas veces
siempre que la
F
<condición> <condición> sea
verdadera, después de
V cada ejecución del
<instrucciones conjunto de
instrucciones se revisa si
la condición se sigue
cumpliendo.

Llamar a
<módulo> Indica que un módulo o
Módulos
función debe ejecutarse

Tabla 3.7 (Continuación) Representación gráfica de las estructuras de control en diagrama de flujo.

Capítulo 3 | Página 13
Ejemplo 3.3: La representación en diagrama de flujo del algoritmo que calcula el volumen de un
planeta ovoide es la siguiente:

Diagrama de flujo: Volumen de un planeta ovoide

Inicio

Imprimir “Semieje mayor”

Lee M

Imprimir “Semieje menor”

Lee m

4
𝑣𝑜𝑙 ← 𝜋𝑀 m
3

Imprimir “Volumen=”, vol

Fin

Algoritmo 3.3 Volumen de un planeta ovoide (Diagrama de Flujo)

3.2.3 Prueba de escritorio


Es conveniente probar que nuestro algoritmo es correcto, uno de los métodos –no formales– más
utilizado es la prueba de escritorio, que consiste en realizar una ejecución –en papel– del algoritmo
con datos de entrada válidos e inválidos y verificar que funciona correctamente.
Recuerda siempre que un programa busca transformar los datos de entrada en el resultado deseado,
pero para ello debe existir un proceso de varios pasos intermedios que podemos ver como estados de

Capítulo 3 | Página 14
la memoria, es decir corresponden a los cambios de estado que sufren al realizar cada uno de los pasos
del algoritmo.
Para ilustrar como se puede hacer una prueba de escritorio, en la siguiente tabla mostramos la ejecución
paso a paso del algoritmo que calcula el volumen de un planeta ovoide; en la primera columna
escribimos la instrucción que se ejecuta; en la siguiente el o los datos que el usuario envía por el teclado;
en la tercera las operaciones que se hacen en la ALU; en la siguiente el estado de la memoria, es decir,
el valor que tienen las variables en ese momento; y en la última columna los datos que se imprimen en
pantalla.
Ejemplo 3.4: Supongamos que deseamos calcular el volumen de un planeta cuyo semieje mayor es
24.75 km y el semieje menor es 20.52 km.

Prueba de escritorio: Algoritmo que calcula el volumen de un planeta ovoide.

Dato de Dato de
Operaciones Estado de la memoria
Instrucción entrada salida
(variables y constantes)
- M m vol
Inicio - - - - -

Imprimir M m vol
- Proporciona el
“Proporciona el - - - - diámetro mayor
ejemayor”.
Leer M - M m vol
24.75 -
24.75 - -
M m vol
Imprimir - Proporciona el
24.75 - -
“Proporciona el eje -
diámetro menor
menor”.

Leer m - M m vol
20.52 -
24.75 20.52 -
vol = M m vol
4/3(3.141592)* 24.75 20.52 52652.17
vol ←4/3* PI*
M2 *m - (24.75)2 * 20.52 -

=52652.17

Imprimir “Volumen M m vol


- Volumen =
= ”, vol. - 24.75 20.52 52652.17 52652.17
Fin M m vol
-
- 24.75 20.52 52652.17 -

Tabla 3.8: Volumen de un planeta ovoide (prueba de escritorio)

La implementación del programa se presentará en el siguiente capítulo.

Capítulo 3 | Página 15
3.3 PROGRAMACIÓN ESTRUCTURADA

Los primeros lenguajes de programación de alto nivel permitían realizar “saltos” a diferentes líneas del
código mediante la instrucción GOTO, pero tenían el gran inconveniente de que al modificar el
programa, también se debían modificar todas las instrucciones GOTO para asegurar que los saltos se
hicieran a las líneas de código correctas. Además de lo tedioso que podía ser estar corrigiendo el
programa, las instrucciones GOTO lo hacían difícil de leer.

En 1966 Corrado Böhm y Giuseppe Jacopini demostraron que “cualquier algoritmo puede ser
implementado utilizando únicamente tres tipos de estructuras de control: secuenciales, condicionales y
repetitivas”, basándose en este resultado, a principios de los años 70´s Edsger Dijkstra se dio cuenta
que la forma en la que los lenguajes de programación de alto nivel pudieran modificarse sin problemas,
era eliminando las instrucciones GOTO (o similares), así que propuso un nuevo estilo de programación
al que llamó programación estructurada; el cual también tiene la propiedad de hacer más legible el
programa.

Como hemos mencionado anteriormente, un rasgo fundamental de la programación estructurada son


las estructuras de control, estas permiten controlar el orden en que se ejecutarán las instrucciones en un
programa, también conocido como el flujo de ejecución. Así mismo, “las estructuras de control
permiten combinar instrucciones individuales en una simple unidad lógica con un punto de entrada y
un punto de salida” ([Joyanes2005], pag 170).

Para explicarlas este paradigma de programación nos basaremos en el mundo de la ardilla.

El mundo de la ardilla

Por algunos meses un grupo de científicos se ha dedicado a entrenar una ardilla, hasta ahora han logrado
que la ardilla ejecute sobre un tablero, únicamente, las órdenes que se muestran en la tabla 3.9.

Capítulo 3 | Página 16
DESCRIPCIÓN DE LOS MOVIMIENTOS DE LA
INSTRUCCIÓN
ARDILLA
avanza() Se mueve una ubicación en la dirección actual

giraIzquierda() Voltea a la izquierda


dejaBellota() Coloca una bellota en la ubicación actual

hayBellota() Responde si hay o no bellotas en la posición actual

hayPared() Responde si hay o no pared en la ubicación siguiente


La ardilla coloca en su boca una bellota que está en la ubicación
recogeBellota()
actual5
bellotasRecogidas() Dice el número de bellotas que tiene en la boca
Tabla 3.9. Lista de instrucciones que puede ejecutar la ardilla

Observa que la ardilla no es capaz de voltear a la derecha y mucho menos de responder a órdenes más
complejas como “mueve una bellota que se encuentra en la primera casilla del tablero al final del
mismo”. Sin embargo, nosotros podemos hacer que ejecute ambas órdenes si construimos un algoritmo
en el cual únicamente utilicemos las instrucciones que ella conoce. Girar a la derecha es equivalente a
dar tres giros a la izquierda pero tampoco podemos darle la orden giraIzquierda() tres veces
pues no sabe que significa tres veces, así que tenemos que ser más precisos, lo cual significa que
tendríamos que ordenarle tres veces que girará a la izquierda, es decir, el algoritmo sería el siguiente:
giraIzquierda(); giraIzquierda(); giraIzquierda().

Al igual que la ardilla, la computadora sólo es capaz de ejecutar un conjunto específico de instrucciones
que se resumen en: enviar o recibir datos por alguno de los dispositivos de salida y entrada,
respectivamente; realizar operaciones aritméticas y lógicas; y leer o escribir datos en la memoria. Sin
embargo, dependiendo de la metodología que utilicemos para diseñar la solución y del lenguaje de
programación que empleemos para codificarlo, podemos generar otras instrucciones más complejas
definidas en términos de las básicas –de igual manera en que definimos la instrucción
giraDerecha() utilizando de la instrucción giraIzquierda()-.

Las instrucciones de programación estructurada se llaman estructuras de control ya que permiten


controlar el orden en que se ejecutarán las instrucciones en un programa, también conocido como flujo
de ejecución. De acuerdo con función se clasifican en: secuenciales, selectivas y repetitivas.

5 Recuerda que las ardillas poseen unas bolsas en las mejillas donde almacenan los alimentos para llevarlos a sus refugios.

Capítulo 3 | Página 17
3.3.1 Estructuras Secuenciales

Las estructuras secuenciales son un bloque de instrucciones que se ejecutan una tras otra, en el mismo
orden en el que están escritas. Por ejemplo, imaginemos el metro de la Línea 3 que va de Indios Verdes
a Universidad (ver figura 3.4), en el trayecto de una terminal a otra va haciendo una serie de paradas
consecutivas siempre en el mismo orden y sin saltar ninguna estación. Así también las instrucciones
secuenciales se ejecutan una después de la otra, en el mismo orden en que se escribieron.

Figura 3.4 Ilustración de Estructura Secuencial

Un ejemplo de este tipo de instrucción es la lista de instrucciones que debe ejecutar la ardilla para girar
a la derecha o las instrucciones que le permiten llevar la bellota de la primera a la última casilla, veamos
éste último.

Ejemplo 3.5: Supongamos que el mundo donde la ardilla debe mover la bellota de la primera a la
última casilla es el que se muestra en la figura 3.5. Nuestra tarea es encontrar un algoritmo para que la
ardilla realice la tarea indicada considerando los siguientes puntos:

a) El mundo es conocido, es decir, sabemos que el tablero está cercado por paredes y sólo tiene
seis casillas colocadas en línea.
b) Al inicio la ardilla está en la primera casilla volteando hacia arriba y no tiene ninguna bellota
en la boca, esto se representa por un triángulo indicando la dirección en la que está mirando.
c) En la primera casilla hay una bellota representada por un asterisco.

Capítulo 3 | Página 18
* *

a) Estado inicial b) Estado final

Figura 3.5. Primer mundo lineal de la ardilla

Análisis: Haciendo un rápido análisis del problema, nos podemos dar cuenta que la ardilla debe recoger
la bellota, avanzar cinco casillas y después soltar la bellota, esto traducido en un algoritmo queda de la
siguiente forma:

Inicio
recogeBellota()
giraIzquierda()
giraIzquierda()
giraIzquierda()
avanza()
avanza()
avanza()
avanza()
avanza()
dejaBellota()
Fin

Algoritmo 3.4. La ardilla mueve una bellota en un mundo lineal

Ejemplo 3.6: Ahora la ardilla se enfrenta a un nuevo mundo en el que su tarea consiste en recoger las
dos bellotas colocadas en la posiciones indicadas por la figura 3.6.a y llevarlas a la última casilla de la
primera fila, como se muestra en la figura 3.6.b. Considerando que tenemos un mapa del nuevo mundo
y sabemos en qué casillas están colocadas las bellotas diseñemos un algoritmo para que la ardilla realice
su cometido.

* **

a) Estado inicial b) Estado final

Figura 3.6. Segundo mundo de la ardilla, solución secuencial

Capítulo 3 | Página 19
Análisis: Nuevamente el problema planteado es muy sencillo de analizar, la ardilla debe hacer los
movimientos que le permitan recoger la primera bellota, después ir por la segunda y llevarlas a la casilla
indicada. Otra posible opción es que recoja la primera bellota, la lleve a casilla indicada, regrese por la
segunda bellota y también la lleve con la otra. Esta última opción requiere más esfuerzo de la ardilla,
dado que la ardilla no tiene limitado el número de bellotas que puede llevar en la boca, entonces la
primera opción es más eficiente. El algoritmo quedaría como:

Inicio
avanza()
giraIzquierda()
avanza()
avanza()
recogeBellota()
giraIzquierda()
avanza()
giraIzquierda()
giraIzquierda()
giraIzquierda()
avanza()
avanza()
recogeBellota()
avanza()
dejaBellota()
dejaBellota()
Fin

Algoritmo 3.5. La ardilla mueve una bellota en un mundo bidimensional

En el caso de la programación estructurada las instrucciones secuenciales más utilizadas en los


algoritmos son: imprimir datos a pantalla, leer datos a través del teclado, realizar operaciones
matemáticas y asignación de variables.

3.3.2 Estructuras Selectivas


Las estructuras selectivas nos permiten decidir entre dos o más opciones. Por ejemplo, en la red del
metro (ver figura 3.7) existen estaciones en las que se cruzan dos o más líneas y por tanto dependiendo
de dónde nos dirigimos podemos optar por abordar una u otra; pensemos que estamos en la estación
de Bella Artes y tenemos dos alternativas ir a la Línea 2 (azul) o la Línea 8 (verde), cuál tomar
dependerá de nuestro destino. En esencia, en las estructuras selectivas se ejecutan o no un bloque de
instrucciones dependiendo de si se cumple o no una condición en un momento determinado.

Capítulo 3 | Página 20
Figura 3.7 Ilustración de Estructuras Condicionales

Existen diferentes tipos de estructuras selectivas que de acuerdo con el número de alternativas se
clasifican en: condicionales simples (Si), dobles (Si-Sino) o múltiples (Seleccionar). La primera
involucra un único bloque de instrucciones que se ejecuta sólo si una condición se cumple. En cambio
la segunda abarca dos bloques de instrucciones, uno se ejecuta cuando la condición es verdadera y el
otro cuando es falsa. La última tiene uno o más de dos bloques de instrucciones que se pueden ejecutar
conforme al valor que tenga una variable, cada bloque equivale a un valor diferente. Por el momento
sólo nos enfocaremos en las dos primeras ya que en el capítulo 5 estudiaremos a profundidad estas
estructuras.

Capítulo 3 | Página 21
Ejemplo 3.7: Nuevamente la ardilla está en el mundo lineal y tiene que recoger una bellota y llevarla
a la última casilla como se muestra en la figura 3.8 b, sólo que ahora no sabe con precisión en que
casilla esta la bellota y la única información con la que cuenta es la siguiente:

a) En el tablero hay una sola bellota y las casillas donde puede estar son la tercera o la quinta, lo
cual se representa con un círculo en la figura 3.8.a.
b) Al inicio la ardilla no tiene ninguna bellota en la boca.
c) Es un error ordenar a la ardilla que recoja una bellota en una casilla cuando ésta no contiene
nada pues la ardilla no sabrá que hacer.

a) Estado inicial b) Estado final

Figura 3.8. La ardilla debe tomar una decisión

Análisis: En este caso es necesario asegurarnos que en la casilla hay una bellota, antes de ordenarle a
la ardilla que la recoja, para ello vamos a utilizar una estructura selectiva. Como la ardilla ya fue
entrenada para decir si hay una bellota, entonces esto lo utilizaremos como condición; como tenemos
dos posibles lugares dónde la ardilla puede encontrar la bellota, ordenaremos a la ardilla que avance
hasta la tercera casilla, si hay una bellota entonces la recoge y después la lleva a la última casilla, sino
la ardilla avanza hasta la quinta casilla y ahí recoge la bellota, esto sin preguntar si ahí se encuentra,
pues una de las aseveraciones en el planteamiento del problema es que en el tablero hay una bellota,
así que si ésta no estaba en la tercera casilla es seguro que está en la quinta.

Inicio
avanza()
avanza()
Si hayBellota() entonces
recogeBellota()
avanza()
avanza()
avanza()
dejaBellota()
Sino
avanza()
avanza()
recogeBellota()
avanza()
dejaBellota()
Fin Si-Sino
Fin
Algoritmo 3.6. La ardilla toma decisiones en un mundo lineal (versión 1)

Capítulo 3 | Página 22
Observa que tanto en el primer caso (Si) como en el segundo (Sino) cuando la ardilla está en la quinta
casilla y ya recogió la bellota, las siguientes órdenes es que avance y deje la bellota (ambas están
remarcadas), de tal manera que podemos modificar el algoritmo de la siguiente forma:

Inicio
avanza()
avanza()
Si hayBellota() entonces
recogeBellota()
avanza()
avanza()
Sino
avanza()
avanza()
recogeBellota()
Fin Si-Sino
avanza()
dejaBellota()
Fin
Algoritmo 3.7. La ardilla toma decisiones en un mundo lineal (versión 2)

También podemos utilizar la estructura Si dos veces, una para preguntar si la bellota está en la tercera
casilla y otra para preguntar en la quinta, como se muestra en el siguiente algoritmo.

Inicio
avanza()
avanza()
Si hayBellota() entonces
recogeBellota()
Fin Si
avanza()
avanza()
Si hayBellota() entonces
recogeBellota()
Fin Si
avanza()
dejaBellota()
Fin
Algoritmo 3.8. La ardilla toma decisiones en un mundo lineal (versión 3)

Capítulo 3 | Página 23
A diferencia de los dos algoritmos anteriores, en éste la ardilla va a verificar en las dos casillas si hay
bellota, aunque la haya encontrado en la primera opción, esto implica un poco más esfuerzo para la
ardilla. También es posible anidar estructuras selectivas como se verá en el siguiente ejemplo.

Ejemplo 3.8: En este caso la ardilla tiene que recoger una bellota y llevarla a la primera casilla de la
primera fila como se muestra en la figura 3.9.b., tomando en cuenta la siguiente información:

a) El mundo es conocido y al inicio la ardilla no tiene ninguna bellota en la boca.


b) En el tablero hay una sola bellota. Las casillas donde pueden estar se representan con un círculo
en la figura 3.9.
c) Es un error ordenar a la ardilla que recoja una bellota en una casilla cuando esta no contiene
nada pues la ardilla no sabrá qué hacer

a) Estado inicial b) Estado final

Figura 3.9. La ardilla toma diferentes decisiones

Análisis: Este problema se puede resolver si utilizamos la última estrategia del ejemplo anterior, es
decir, haciendo un solo recorrido y empleando tres estructuras Si en las casillas dónde es posible
encontrar una bellota. Aunque es una solución válida implica mayor esfuerzo para la ardilla pues
cuando la bellota se encuentra en la primera opción, debe recorrer el resto del tablero aunque ya
sabemos que no encontrará otra bellota. En este caso es más conveniente utilizar estructuras selectivas
dobles que nos permitan modelar los tres casos posibles, a partir de la información que obtenemos en
el trayecto. Por ejemplo, si la ardilla encuentra la bellota en la primera opción (caso 1) debe recogerla
y regresar a la primera casilla, en caso contrario debe avanzar a la siguiente opción en la cual
nuevamente tenemos otros dos posibles casos, si la bellota se encuentra en la segunda opción (caso 2)
la recoge y regresa, sino (caso 3) es seguro que está en la última casilla por lo tanto avanza hasta esta
y la recoge (no es necesario verificar que está ahí pues por la información obtenida en los casos
anteriores podemos asegurarlo). De acuerdo con nuestro análisis, a continuación definimos un
algoritmo para realizar la tarea encomendada, el único caso que está completo es el caso 1, en los otros
dos una vez que la ardilla ha encontrado y recogido la bellota faltan las instrucciones que le permitan
regresar a la primera casilla, esto se indica con la frase “PASOS PARA REGRESA A LA PRIMERA
CASILLA” y se dejan como ejercicio al lector.

Capítulo 3 | Página 24
Inicio
avanza()
giraIzquierda()
avanza()
avanza()
Si hayBellota() entonces /* CASO 1 */
recogeBellota()
giraIzquierda()
giraIzquierda()
avanza()
avanza()
giraIzquierda()
giraIzquierda()
giraIzquierda()
avanza()
dejaBellota()
Sino
giraIzquierda()
avanza()
giraIzquierda()
giraIzquierda()
giraIzquierda()
avanza()
Si hayBellota() entonces /* CASO 2*/
recogeBellota()
... PASOS PARA REGRESAR A LA PRIMERA CASILLA

Sino /* CASO 3*/


avanza()
avanza()
giraIzquierda()
giraIzquierda()
giraIzquierda()
recogeBellota()
... PASOS PARA REGRESAR A LA PRIMERA CASILLA
Fin Si-Sino
Fin Si-Sino
Fin
Algoritmo 3.9: La ardilla toma decisiones en un mundo bidimensional

Capítulo 3 | Página 25
3.5.2 Estructuras Repetitivas
Las estructuras repetitivas –también llamadas ciclos– nos permiten ejecutar varias veces un bloque de
instrucciones en función de una condición. Por ejemplo, ahora supongamos que un estudiante quedó
de verse con los 4 integrantes de su equipo de literatura en la estación del metro Balderas. El primero
en llegar al punto de encuentro es él, por tanto debe esperar a que lleguen los demás, cada vez que llega
un integrante del equipo deben verificar si falta alguien más, en tal caso deben seguir esperando.
Cuando han llegado todos los integrantes del equipo dejan de esperar y se dirigen a la biblioteca. La
acción de “esperar” puede representar la(s) instrucción(es) que se repite(n) mientras la condición es
verdadera, en este caso que “el grupo no está completo”. (Ver figura 3.10)
Inicio

Inicio del algoritmo.

Comentario. Esperando a integrantes del equipo de literatura.

Iteración 1

Iteración 1. Ha llegado el primer integrante del equipo de literatura. (El grupo no está completo, Verdadero)
}Estructura Repetitiva

2 1

Iteración 2
(Ciclo)

Iteración 2. Ha llegado el segundo integrante del equipo de literatura. (El grupo no está completo, Verdadero)

3 2 1

Iteración 3

Iteración 3. Ha llegado el tercer integrante del equipo de literatura. (El grupo no está completo, Verdadero)

Capítulo 3 | Página 26
4 3 2 1
Iteración 4

Iteración 3. Ha llegado el último integrante del equipo de literatura. (El grupo no está completo, Falso)
Ya están todos los integrantes del equipo de literatura. Fin del ciclo

Ya estamos completos, vamos a la


biblioteca.
Fin

FIN DEL ALGORITMO


Figura 3.10. Ilustración de Estructura Repetitiva

Al igual que en las estructuras selectivas, existen diferentes estructuras repetitivas que se diferencian
–principalmente– por el orden en el que se evalúa la condición. Por ejemplo, el ciclo Mientras primero
verifica que la condición sea verdadera y en tal caso se ejecuta el bloque de instrucciones, después
revisa nuevamente la condición; en cambio, el ciclo Hacer-Mientras primero realiza las instrucciones
y después verifica la condición, si ésta se cumple se ejecutan nuevamente las instrucciones. Sin
embargo, todas son equivalentes, pues cualquiera puede simular el comportamiento de las otras , por
tal motivo, en este capítulo sólo nos enfocaremos a la estructura Mientras. Las otras se estudiarán a
profundidad en el capítulo 6.

Ejemplo 3.9: En el ejemplo 3.5 la ardilla debe llevar una bellota desde la primera casilla hasta la última
en un mundo lineal (ver figura 3.5). Este problema se puede solucionar empleando una estructura
repetitiva; una vez que la ardilla ha recogido la bellota y está viendo de frente, debe avanzar –una y
otra vez– mientras no se tope con la pared. El algoritmo sería el siguiente:

Inicio
recogeBellota()
giraIzquierda()
giraIzquierda()
giraIzquierda()
Mientras no(hayPared()) has
avanza() /* instrucción que se repite */
Fin Mientras
dejaBellota()
Fin
Algoritmo 3.10. La ardilla mueve una bellota realizando un ciclo

Capítulo 3 | Página 27
Generalmente, un ciclo se utiliza cuando descubrimos un patrón. Veamos el siguiente ejemplo:

Ejemplo 3.10: Ahora la tarea de la ardilla es que cambie las bellotas que están en la primera fila a la
segunda y viceversa, dejándolas en la misma columna, tal y como se muestra en la figura 3.11. Las
condiciones de inicio son:

a) El mundo es conocido y sabemos exactamente dónde hay bellotas.

b) La ardilla no tiene ninguna bellota en la boca al inicio.

c) El mundo está encerrado por paredes y si la ardilla choca contra una se considerará un error
garrafal.

* * * * * *

* * * * * *

a) Estado inicial b) Estado final

Figura 3.11. La ardilla debe encontrar patrones

Análisis: De acuerdo con la figura 3.11, para que la ardilla cumpla con su tarea debe realizar los
siguientes pasos: recoger la bellota, girar a la derecha, avanzar, dejar la bellota, girar a la izquierda,
avanzar, recoger la bellota, girar a la izquierda, avanzar, dejar la bellota, voltear a la derecha y avanzar.
Hasta este punto las coordenadas de la ardilla son: primera fila y tercera casilla (volteando a la derecha,
como al inicio). Si la ardilla repite otra vez este bloque de instrucciones, logrará cambiar las siguientes
dos bellotas; al repetirlo nuevamente cambiaría las últimas dos, salvo que cuando la ardilla avance
después de haber dejado la bellota chocará contra la pared, por lo tanto, antes de que avance –última
instrucción del bloque – tenemos que verificar que no haya pared. La condición para que la ardilla
repita el bloque de instrucciones es que no haya pared.

Capítulo 3 | Página 28
El algoritmo es el siguiente.

Inicio
Mientras no(hayPared()) has
recogeBellota()
giraIzquierda()
giraIzquierda()
giraIzquierda()
avanza()
dejaBellota()
giraIzquierda()
avanza()
recogeBellota()
giraIzquierda()
avanza()
dejaBellota()
giraIzquierda()
giraIzquierda()
giraIzquierda()
Si no(hayPared()) entonces
avanza()
Fin Si
Fin Mientras
Fin
Algoritmo 3.11: La ardilla cambia bellotas mediante un ciclo

La representación en diagrama de flujo del algoritmo 3.11 es la siguiente:

Capítulo 3 | Página 29
inicio

No
no hahayPared()
Si
recogeBellota()

giraIzquierda()

giraIzquierda()

giraIzquierda()

avanza()

dejaBellota()

giraIzquierda()

avanza()

recogeBellota()

giraIzquierda()

avanza()

dejaBellota()

giraIzquierda()

giraIzquierda()

giraIzquierda()

No
no hayPared()
Si
avanza()

Fin

Algoritmo 3.12 La ardilla coloca bellotas en el tablero siguiendo un patrón (Diagrama de Flujo)

Capítulo 3 | Página 30
La clave para utilizar un ciclo es identificar el conjunto de instrucciones que debe repetirse y la
condición que debe verificarse para que se ejecuten. Estas estructuras se retomarán en el capítulo 6.

Para cerrar con este capítulo en la siguiente sección se presentan dos problemas para los cuales se
realiza el análisis, algoritmo y pruebas de escritorio.

3.6 MÁS EJEMPLO

Ejemplo 3.11: Realiza el análisis y diseño de un algoritmo que calcule la intensidad de la corriente (en
amperios) que circula a través de una fuente eléctrica, conociendo el voltaje (en voltios) y la resistencia
(en ohmios) de la fuente.

Análisis del problema: El resultado deseado es la intensidad de la corriente eléctrica que está
circulando; después de investigar un poco hemos encontrado que la Ley de Ohm establece que “La
intensidad de una corriente eléctrica (I) que circula por un dispositivo, es directamente proporcional
a la diferencia de potencia (V) aplicada e inversamente proporcional a la resistencia (R) del mismo”,
lo cual equivale a la siguiente fórmula matemática:
𝑉
𝐼=
𝑅

Desde el planteamiento sabemos que los datos que proporcionará el usuario son la diferencia de
potencia (V) y la resistencia (R); la primera puede tomar valores positivos o negativos ya que depende
de la dirección de la corriente; en cambio, la resistencia siempre debe ser un valor mayor a cero, en este
último caso es indispensable asegurarnos que el valor de R sea distinto de cero pues en tal caso la
división queda indefinida.

Análisis del problema


Datos de entada: Salida: Constantes:
Diferencia de potencia en Intensidad de la corriente Ninguna
voltios (V). eléctrica en amperios (I)
Resistencia en ohmios (R)
Método: Restricciones:
Ley de Ohm: 𝐼 = R>0

Tabla 3.10: Ley de Ohm (análisis)

Capítulo 3 | Página 31
Diseño de la solución

Inicio

Imprimir “Proporciona la
diferencia de potencia (voltios)”

Lee V

Imprimir “Proporciona la
resistencia (ohmios)”

Lee R

Sí No
R>0

𝑉
𝐼←
𝑅
Imprimir “Error: el valor
de la resistencia es
Imprimir “Intensidad de la incorrecto”
corriente”, I , “(amperios)”

Fin

Algoritmo 3.13 Ley de Ohm (Diagrama de Flujo)

Una vez diseñado el algoritmo el siguiente paso es verificar que funciona correctamente, para lo cual
haremos una prueba de escritorio considerando que el voltaje es 20 voltios y la resistencia es 5 ohmios.

Capítulo 3 | Página 32
Prueba de escritorio: Algoritmo que calcula la corriente eléctrica de una fuente eléctrica
Caso particular: V=20 voltios, R=5 ohmios

Dato de Estado de la memoria Dato de


Instrucción Operaciones
entrada (variables y constantes) salida

- V R I
Inicio - - - - -

Imprimir V R I Proporciona la
“Proporciona la
-
- - - - diferencia de
diferencia de potencia
potencia (voltios)” (voltios)
Leer V - V R I
20 -
20 - -
V R I
Imprimir 20 - - Proporciona la
“Proporciona la -
- resistencia
resistencia
(ohmnios)
(ohmios)”

Leer R - V R I
5 -
20 5 -
Si R>0 entonces… 5>0 verdadero V R I
- -
20 5 -
𝑉 20 V R I
𝐼← 𝐼←
𝑅 - 5 20 5 4 -

Imprimir V R I
“Intensidad de la 20 5 4 Intensidad de
-
corriente”, I , - la corriente 4
“(amperios)” (amperios)

Fin del Si y Fin del V R I


algoritmo - 20 5 4
- -

Tabla 3.11: Ley de Ohm (prueba de escritorio)

Si R fuera 0 o un valor negativo, la condición R>0 no se cumpliría y se imprimiría el mensaje “Error:


el valor…”.

Ejemplo 3.12: El gobierno del Distrito Federal requiere un programa que simule el comportamiento
de una máquina dispensadora de boletos del metro. A partir de una cantidad entera positiva de boletos
el programa debe determinar el total de la venta, leer el monto del pago (cantidad entera y positiva
mayor al total de la venta) y calcular el cambio, así como el número de billetes y monedas de cada

Capítulo 3 | Página 33
denominación que se necesitan para devolverlo. El tipo de billetes y monedas que la máquina devolverá
son: billetes $200, $100, $50 y $20; monedas $10, $5, $2, $1.

Análisis: El software debe calcular el monto a pagar por el cliente (total) dado el número de boletos
(nboletos) que desea comprar, considerando que cada boleto cuesta $2 (PRECIO); después de calcular
y reportar el total a pagar, debe conocer el monto del pago (pago) y a partir de este imprimir el monto
del cambio (cambio), incluyendo el número de billetes o monedas de cada denominación. Para calcular
el total a pagar y el cambio se pueden aplicar las siguientes fómulas:

total = nboletos * PRECIO

cambio = pago – total

El número de billetes de cierta denominación Y que se deben devolver dada la cantidad X, se obtiene al dividir
la cantidad (X) entre la denominación (Y). Por ejemplo, el número de billetes de 200 que se deben devolver si
la cantidad es $365.00 es uno, y lo que faltaría por entregar son 165 pesos.

1 Número de billetes
200 365
165 Restante

El número de billetes de 100 que se deberían devolver si la cantidad es $365.00 son tres, y lo que faltaría por
entregar son 65 pesos.

3 Número de billetes
100 365
65 Restante

Y de esta manera se procede con cada denominación. Generalizando, tendríamos que para un monto dado
(cambio) el número de billetes (billetesX) de cierta denominación (X) que se deben entregar es igual a

𝐜𝐚𝐦𝐛𝐢𝐨
𝐛𝐢𝐥𝐥𝐞𝐭𝐞𝐬𝐗 =
𝐗

mientras que el cambio que faltaría por entregar es igual al residuo de la división anterior, lo cual es el resultado
del operador módulo (mod).

cambio = cambio mod denominación

Otra forma de calcular el cambio faltante, es aplicar la siguiente fórmula:

cambio = cambio – (billetesX * denominación)

Por ejemplo, para el caso de 365 tenemos que el número de billetes de $200 es 1, así que el nuevo
monto del cambio es 365- (1)(200), esto es 165.

Capítulo 3 | Página 34
De cualquiera de las dos formas podemos obtener el monto del cambio que falta por devolver, así que
nosotros optaremos por el módulo.

Es conveniente representar el precio del boleto una constante para facilitar futuras modificaciones.

En la siguiente tabla se resume el análisis del problema.

Análisis del problema


Datos de entada: Salida: Constantes:
Número de boletos (nboletos). El monto que se debe pagar por los boletos PRECIO=2
Monto del pago (pago) (total)
El monto del cambio (cambio)
El número de billetes o monedas de cada
denominación que completan el cambio
(billetes200, billetes100, billetes50,
billetes20, monedas10, monedas5,
monedas2, monedas1)
Método: Restricciones:
nboletos > 0
Total venta: total = nboletos * PRECIO pago ≥ total

Total cambio: cambio = pago – total

Número de billetes de denominación X, con X igual a 200, 100, 50,


20.
cambio
billetesX =
X

Número de monedas de denominación X igual a 10, 5, 1


cambio
billetesX =
X

Cambio que falta por devolver una vez que se entrego una
cantidad de billetes o monedas de cierta denominación

cambio = cambio mod denominación

Tabla 3.12 Venta de Boletos (Análisis).

Una vez que se ha realizado el análisis del problema, el siguiente paso es elaborar un algoritmo.

Capítulo 3 | Página 35
Diseño de la solución
Algoritmo (Pseudocódigo)

Inicio
Imprimir “ Introduzca el número de boletos que desea comprar”
Leer nboletos
total ← nboletos * PRECIO /*Cálculo del total a pagar*/
Imprimir “El total a pagar es ”, total
Imprimir “Proporciona tu pago”
Leer pago
Si pago ≥ total entonces /*Caso en el que el pago es correcto*/
/* Cálculo e impresión del monto del cambio*/
cambio←pago – total
Imprimir “Tu cambio es”, cambio

/* Operaciones para determinar cuántos boletos de cada tipo se necesitan*/


cambio
billetes200 ←
200
cambio ←cambio mod 200
cambio
billetes100 =
100
cambio ← cambio mod 100
cambio
billetes50 ←
50
cambio ← cambio mod 50
cambio
billetes20 ←
20
cambio ← cambio mod 20
cambio
monedas10 ←
10
cambio ← cambio mod 10
cambio
monedas5 ←
5
cambio ← cambio mod 5
cambio
monedas2 ←
2
monedas1 ← cambio mod 2 /*Este siempre será cero pues el precio es un número par,
así que se puede omitir*/
Imprimir “Se le entregarán los siguientes billetes y monedas:”
Imprimir “Billetes de 200:”, billetes200
Imprimir “Billetes de 100:”, billetes100
Imprimir “Billetes de 50:”, billetes50
Imprimir “Billetes de 20:”, billetes20
Imprimir “Monedas de 10:”, monedas10
Imprimir “Monedas de 5:”, monedas5
Imprimir “Monedas de 2:”, monedas2

Capítulo 3 | Página 36
Imprimir “Monedas de 1:”, monedas1
Imprimir “Venta realizada…”

Sino
Imprimir “El pago es incorrecto”
Imprimir “Venta NO realizada”
Fin Si-Sino

Fin

Variables utilizadas:
nboletos: entero, representa el número de boletos que quiere el cliente.
total: entero, es la cantidad de dinero que el cliente debe pagar.
pago: entero, monto del pago del cliente.
cambio: entero, monto del cambio.
billetes200: entero, número de billetes de $200 necesarios para devolver el cambio
billetes100: entero, número de billetes de $100 necesarios para devolver el cambio
billetes50: entero, número de billetes de $50 necesarios para devolver el cambio
billetes20: entero, número de billetes de $20 necesarios para devolver el cambio
monedas10: entero, número de monedas de $10 necesarios para devolver el cambio
monedas5: entero, número de monedas de $5 necesarios para devolver el cambio
monedas2: entero, número de monedas de $2 necesarios para devolver el cambio
monedas1: entero, número de monedas de $1 necesarios para devolver el cambio

Algoritmo 3.14 Venta de Boletos (Pseudocódigo)

Capítulo 3 | Página 37
Prueba de escritorio
Caso particular: Supongamos que deseamos comprar 20 boletos con un billete de 100 pesos.

Dato
Estado de la memoria
de Dato de
Instrucción Operaciones
entra salida
(variables)
da

nBoletos total pago cambio


- - - -
billetes200 billetes100 billetes50 billetes20
Inicio - - -
- - - -
monedas1 monedas5 monedas2 Monedas1
- - - -
Imprimir “Introduzca Introduzca
No cambia
el número de boletos - - el número
que desea comprar”. ..
nBoletos total pago cambio
20 - - -
Leer nBoletos billetes200 billetes100 billetes50 billetes20
20 - -
- - - -
monedas1 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
20 40 - -
total ← total ← 20*2 billetes200 billetes100 billetes50 billetes20 -
nBoletos*PRECIO -
total ← 40 - - - -
monedas1 monedas5 monedas2 monedas1
- - - -
Imprimir No cambia Proporcio
- -
“Proporciona tu pago” na tu pago
nBoletos total pago cambio
20 40 100 -
Leer pago billetes200 billetes100 billetes50 billetes20
100 - -
- - - -
monedas1 monedas5 monedas2 monedas1
- - - -
¿100 ≥40?
Verdadero
Si pago ≥ total (realiza el No cambia
- -
entonces conjunto de
operaciones
del Si)
nBoletos total pago cambio
20 40 100 60
cambio ← 100
cambio ← pago – billetes200 billetes100 billetes50 billetes20
- - 40
total - - - -
cambio ← 60
monedas1 monedas5 monedas2 monedas1
- - - -
Tabla 3.13 (Parte I) Venta de Boletos (Prueba de escritorio).

Capítulo 3 | Página 38
Prueba de escritorio (continuación)
Datos Dato de
Instrucción Operaciones Estado de la memoria
entrada salida
(variables)

Imprimir “Tu cambio - No cambia Tu cambio es


-
es”, cambio. 60

𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠200 nBoletos total pago cambio


60 20 40 100 60
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠200 ← billetes200 billetes100 billetes50 billetes20
200
𝑐𝑎𝑚𝑏𝑖𝑜 - -
← 0 - - -
200 𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠200 ← 0 monedas10 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
𝑐𝑎𝑚𝑏𝑖𝑜 20 40 100 60
← 60 𝑚𝑜𝑑 200
𝑐𝑎𝑚𝑏𝑖𝑜 billetes200 billetes100 billetes50 billetes20
- -
← 𝑐𝑎𝑚𝑏𝑖𝑜 𝑚𝑜𝑑 200 0 - - -
𝑐𝑎𝑚𝑏𝑖𝑜 ← 60
monedas10 monedas5 monedas2 monedas1
- - - -
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠200 nBoletos total pago cambio
60 20 40 100 60
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠100 ← billetes200 billetes100 billetes50 billetes20
100
𝑐𝑎𝑚𝑏𝑖𝑜 -
← 0 0 - -
100 𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠200 ← 0 monedas10 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
𝑐𝑎𝑚𝑏𝑖𝑜 20 40 100 60
← 60 𝑚𝑜𝑑 100
𝑐𝑎𝑚𝑏𝑖𝑜 billetes200 billetes100 billetes50 billetes20
- -
← 𝑐𝑎𝑚𝑏𝑖𝑜 𝑚𝑜𝑑 100 0 0 - -
𝑐𝑎𝑚𝑏𝑖𝑜 ← 60
monedas10 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
60 20 40 100 60
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠50 ←
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠50 50 billetes200 billetes100 billetes50 billetes20
𝑐𝑎𝑚𝑏𝑖𝑜 -
← 0 0 1 -
50 𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠50 ← 1
monedas10 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
𝑐𝑎𝑚𝑏𝑖𝑜 20 40 100 10
← 60 𝑚𝑜𝑑 50
𝑐𝑎𝑚𝑏𝑖𝑜 billetes200 billetes100 billetes50 billetes20
- -
← 𝑐𝑎𝑚𝑏𝑖𝑜 𝑚𝑜𝑑 50 0 0 1 -
𝑐𝑎𝑚𝑏𝑖𝑜 ← 10
monedas10 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
10 20 40 100 60
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠20 ←
𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠20 20 billetes200 billetes100 billetes50 billetes20
𝑐𝑎𝑚𝑏𝑖𝑜 -
← 0 0 1 0
20 𝑏𝑖𝑙𝑙𝑒𝑡𝑒𝑠20 ← 0
monedas10 monedas5 monedas2 monedas1
- - - -
nBoletos total pago cambio
𝑐𝑎𝑚𝑏𝑖𝑜 20 40 100 10
← 10 𝑚𝑜𝑑 20
𝑐𝑎𝑚𝑏𝑖𝑜 billetes200 billetes100 billetes50 billetes20
- -
← 𝑐𝑎𝑚𝑏𝑖𝑜 𝑚𝑜𝑑 20 0 0 1 0
𝑐𝑎𝑚𝑏𝑖𝑜 ← 10
monedas10 monedas5 monedas2 monedas1
- - - -
Tabla 3.13 (Parte II) Venta de Boletos (Prueba de escritorio).

Capítulo 3 | Página 39
Prueba de escritorio (continuación)
Datos
Operacione Estado de la memoria Dato de
Instrucción entra
s salida
da (variables)

𝑚𝑜𝑛𝑒𝑑𝑎𝑠10 nBoletos total pago cambio


10 20 40 100 10
𝑚𝑜𝑛𝑒𝑑𝑎𝑠10 ←
10 billetes200 billetes100 billetes50 billetes20
𝑐𝑎𝑚𝑏𝑖𝑜 - -
← 0 0 1 0
10 𝑚𝑜𝑛𝑒𝑑𝑎𝑠10 ← 1 monedas10 monedas5 monedas2 monedas1
1 - - -
nBoletos total pago cambio
𝑐𝑎𝑚𝑏𝑖𝑜
20 40 100 0
← 10 𝑚𝑜𝑑 10
𝑐𝑎𝑚𝑏𝑖𝑜 billetes200 billetes10 billetes50 billetes20
- -
← 𝑐𝑎𝑚𝑏𝑖𝑜 𝑚𝑜𝑑 10
𝑐𝑎𝑚𝑏𝑖𝑜 ← 0
0 0 1 0
monedas10 monedas5 monedas2 monedas1
1 - - -
Las siguientes operaciones devuelven cero de tal manera que antes de la impresión el estado de la memoria es
nBoletos total pago cambio
20 40 100 0
billetes200 billetes100 billetes50 billetes20
0 0 1 0
monedas10 monedas5 monedas2 monedas1
1 0 0 0
Finalmente, de acuerdo con el estado de la memoria los mensajes de salida son los siguientes:
Se le entregarán los siguientes billetes y monedas:
Billetes de 200: 0
Billetes de 100: 0
Billetes de 50: 1
Billetes de 20: 0
Monedas de 10: 1
Monedas de 5: 0
Monedas de 2: 0
Monedas de 1: 0
Venta realizada…

Tabla 3.13 (Parte III) Venta de Boletos (Prueba de escritorio).

La solución presentada anteriormente imprime la cantidad de billetes o monedas de todas las


denominaciones que se deben devolver para completar el cambio, incluso si éste es cero. Se deja al
lector modificar el algoritmo para que sólo se impriman el tipo de monedas o billetes cuya cantidad sea
diferente de cero.

Capítulo 3 | Página 40
EJERCICIOS DEL CAPÍTULO No. 3

I. Descubriendo algoritmos falsos


Ahora la ardilla se encuentra en el siguiente mundo.

Estado inicial Estado final


*

a) Estado inicial b) Estado final

Figura 3.12. La ardilla debe tomar una decisión

En este caso, la tarea de la ardilla es buscar una bellota en las casillas señalas con un círculo en la figura
3.12.a) y llevarla a la última casilla de la segunda fila como se indica en la figura 3.12.b). Considerando que
el mundo está encerrado entre paredes y que al inicio la ardilla no tiene ninguna bellota en la boca, además,
las casillas remarcadas con negro también representan paredes.

A continuación se muestras tres diferentes listas de instrucciones que pretender realizar la tarea
encomendada, sin embargo, no todas pueden considerarse algoritmo ya que no cumple con las todas las
propiedades de este (preciso, definido y finito). Revísalas y determina cuáles son algoritmo y cuáles no.
Justifica tus respuestas.

Capítulo 3 | Página 41
Lista de Instrucciones 1 Lista de Instrucciones 2 Lista de Instrucciones 3
Inicio Inicio Inicio
avanza() avanza() avanza()
Si (hayBellota()) entonces giraIzquierda() giraIzquierda()
recogeBellota() giraIzquierda() Mientras (hayPared()) has
Fin Si giraIzquierda() giraIzquierda()
gira() avanza() giraIzquierda()
avanza() Si hayBellota() entonces avanza()
Si (hayBellota()) entonces recogeBellota() Si hayBellota() entonces
recogeBellota() Fin Si recogeBellota()
Fin Si Si hayPared() entonces Fin Si
gira() giraIzquierda() Fin Mientras
gira() giraIzquierda() giraIzquierda()
avanza() avanza() avanza()
gira() Fin Si avanza()
avanza() Si hayBellota() entonces giraIzquierda()
avanza() recogeBellota() avanza()
gira() Fin Si dejaBellota()
avanza() giraIzquierda() Fin
dejaBellota() Mientras (no hayPared()) has
avanza()
Fin Mientras
giraIzquierda()
avanza()
dejaBellota ()
Fin
Algoritmo 3.15 Lista de Instrucciones

Capítulo 3 | Página 42
II. Poniendo en orden la Multiplicación Rusa

Hasta hace más o menos un siglo en Rusia se calculaba el producto de dos números enteros utilizando
un método llamado LA MULTIPLICACIÓN RUSA, el procedimiento es muy distinto al que
aprendimos en la primaria. En la siguiente tabla se muestra el producto de 23 y 54.

Izquierda Derecha

23 54

11 108

5 216

2 432

1 864

Suma=1242

Figura 3.13. Multiplicación Rusa

Analiza el ejemplo y ordena los 5 pasos que se listan a continuación para obtener el algoritmo.
Comprueba tu resultado multiplicando 20 por 12.

 Suma todos los números de la columna derecha que no estén tachados y obtendrás el producto
de los números.

 Escriba del lado izquierdo el MENOR de los números que desea multiplicar y del lado
derecho el MAYOR, formando dos columnas. En caso de que los números sean iguales
escribe un número a la izquierda y el otro a la derecha.

 Tacha todos los números pares que encuentres en la columna izquierda y su correspondiente
de la columna derecha, es decir, elimina de la tabla todas las filas que en la columna izquierda
tengan un número par.

 Calcula el doble del número que tienes escrito en la columna derecha y la mitad entera (sin
decimales) de la columna izquierda y escribe estos valores debajo de los anteriores.

 Repite el paso 2, hasta que el resultado de la columna izquierda sea igual a uno.

Capítulo 3 | Página 43
III. Programando un robot móvil

Problema 1: Un automóvil debe ir desde la casilla indicada con SALIDA en la figura 3.13 hasta la
casilla señalada con META, sólo que existen algunas restricciones que hay que considerar para poder
hacer el recorrido:

a) Un auto siempre debe intentar cargar gasolina si encuentra una casilla donde haya una
gasolinera (señalada con dos triángulos en la figura 3.13).
b) El auto podrá cargar gasolina sólo cuando el color de ésta sea verde.
c) Un auto solo puede avanzar máximo 6 casillas sin cargar gasolina, es decir, si un auto ha
avanzado 5 casillas y no encuentra en su siguiente movimiento una gasolinera no puede
continuar avanzado.

Los movimientos del auto están restringidos por las siguientes operaciones:

INSTRUCCIÓN DESCRIPCIÓN DE LOS MOVIMIENTOS

avanza() Se mueve una ubicación en la dirección actual


giraIzquierda() Voltea a la izquierda
colorGas() Coloca una bellota en la ubicación actual

cargaGas() Responde si hay o no bellotas en la posición actual


Tabla 3.14. Lista de instrucciones que puede ejecutar un robot móvil

Observa que la dirección en que puede avanzar el auto esta señalado con la flecha.

Figura 3.14. Primer mapa del sitio que debe recorrer el robot móvil

Capítulo 3 | Página 44
a) Diseña un algoritmo que le permita al automóvil llegar de la casilla salida a la casilla meta,
considerando las restricciones dadas.
b) Dibuja sobre la figura 3.14 la ruta que sigue el auto para llegar a la meta siguiendo tu algoritmo.
c) Imagina que partimos del mapa de la figura 3.14, pero además se realiza un cambio en los
movimientos del auto, los cuales ahora estarán restringidos por las siguientes operaciones:

o avanza(): Se mueve una ubicación en la dirección actual


o gira(): Voltea el auto.
o colorGas(): Regresa verde si se puede cargar gasolina y sino regresa rojo.
o cargaGas(): El auto carga gasolina
Revisa las características de los algoritmos y menciona cuál de ellas se ve afectada al plantear
esta nueva restricción.

d) Ahora imagina que las ordenes que puede ejecutar el automóvil son las que se plantearon al
inicio pero se agrega una restricción que dice que antes de avanzar debemos tirar un dado y el
número que salga es el número de casillas que debe avanzar el auto en la dirección que se
encuentra. Revisa las características de los algoritmos y menciona cuál de ellas se ve afectada
al plantear esta nueva restricción, justifica tu respuesta.

Problema 2. Analiza el mapa de la figura 3.15 y contesta las siguientes preguntas.

a) ¿Podrá el auto llegar de la casilla de salida a la de meta, considerando las restricciones que se
describieron anteriormente? Si ( ) No ( ).

b) Revisa las tres características que tienen los algoritmos y menciona cuál de ellas no se cumple y
justifica ¿Por qué?

Capítulo 3 | Página 45
Figura 3.15. Segundo mapa del sitio que debe recorrer el robot móvil

IV. El mundo de la ardilla

Construye algoritmos que le permitan a la ardilla realizar las tareas planteadas en los siguientes mundos.
Utilizando únicamente las instrucciones dadas en la tabla 3.3.

Problema 1: Diseña un algoritmo que te permita pasar tres bellotas encontrados en la parte derecha
del tablero a la parte izquierda como se indica en la figura 3.16, considerando la siguiente información:
a) Asume que el mundo es conocido, es decir, tienes un mapa del sitio y sabes dónde hay
paredes y dónde se pueden encontrar las bellotas.
b) Al inicio la ardilla no tiene ninguna bellota en la boca.
c) En cada posición marcada con un círculo puede haber 0 ó 1 bellota.
d) Sólo hay tres bellotas.

*
*
*

a) Estado inicial b) Estado final

Figura 3.16. Mundo del Problema 1

Capítulo 3 | Página 46
Problema 2: La ardilla debe encontrar bellotas y colocarlas en la última casilla del tablero asumiendo
que:
a) El mundo es conocido y encerrado por paredes. En el estado inicial la ardilla se encuentra
en la primera casilla volteando hacia el frente (ver figura 3.17.a)
b) No sabe cuántas bellotas hay pero las casillas donde posiblemente hay semillas están
marcadas con un círculo (ver figura 3.17.a)
c) Al final la ardilla deja la bellota en la última casilla y se queda volteando a la derecha como
se muestra en el inciso b de la figura 3.17.
Escribe un algoritmo que le permita a la ardilla llevar a cabo su cometido.

**

a) Estado inicial b) Estado final

Figura 3.17. Mundo del Problema 2

Pista: Antes de crear el algoritmo trata de identificar algún patrón en los pasos que debe ejecutar la
ardilla.

Problema 3: En el siguiente mundo la ardilla debe recoger dos semillas que se encuentran distribuidas
en alguno de los puntos señalados con un círculo. En cuanto la ardilla haya encontrado las dos semillas
el algoritmo debe terminar, quedándose la ardilla en la casilla donde recogió la última. Considera que:

a) El mundo es conocido y encerrado por paredes.


b) La ardilla al inicio está en la primera casilla viendo hacia enfrente (ver figura 3.15) y no tiene
ninguna bellota en la boca
c) Si la ardilla intenta recoger una bellota en una casilla donde no haya se considera un error en el
algoritmo.

Figura 3.18. Mundo del Problema 3

Capítulo 3 | Página 47
PISTA: Nuevamente trata de identificar cuál es el patrón y utilizar una estructura repetitiva.

V. Una persona ha investigado una forma útil para redondear un número real a n cifras decimales.
Por ejemplo, para redondear el número 78.374625 a 3 cifras decimales, tenemos que realizar
los siguientes pasos:
Paso 1) 78.374625 multiplicado por 103 → 78374.625
Paso 2) Sumar 78374.625 y 0.5 → 78375.125
Paso 3) Conservar el número entero → 78375
Paso 4) 78375 dividido entre 103 → 78.375

Realiza las siguientes tareas:


a) Realizar el análisis necesario para generalizar el algoritmo anterior.

Análisis del problema que permite redondear cualquier número real a n cifras
decimales.

Datos de entada: Salida: Constantes:

Método: Restricciones:

a) Escribe el pseudocódigo completo en orden (deja las sangrías necesarias para mejor
legibilidad).
b) Representa el algoritmo con un diagrama de flujo.
c) Realiza una prueba de escritorio para una para el número 78.374625 redondeado a 3 cifras
decimales.
d) Codifica el algoritmo y ejecútalo con los mismos datos de entrada del inciso anterior.

VI. Realiza el análisis y diseño de un algoritmo que calcule la resistencia de un conductor eléctrico
encargado de unir los componentes de un circuito, utilizando la siguiente información: la
resistencia de un conductor depende de la longitud del mismo ( ), de su sección (S), del tipo de
material y de la temperatura. Si consideramos la temperatura constante (20 ºC), la resistencia
viene dada por la siguiente expresión:

Capítulo 3 | Página 48
𝑙
𝑅=𝜌
𝑆
donde ρ es la resistividad (una característica propia de cada material).

VII. Realiza el análisis y diseño de un algoritmo que dado un número de segundos se determine el
número equivalente en minutos, horas, días y semanas. Además, realiza una prueba de escritorio
para 385,200 segundos.
VIII. Realiza el análisis y diseño de un algoritmo (pseudocódigo) que calcule la fuerza gravitacional
entre dos cuerpos, utilizando la Ley de la Gravitación Universal de Newton: “La fuerza (F) que
ejerce una partícula puntual con masa m1 sobre otra con masa m2 es directamente proporcional
al producto de las masas, e inversamente proporcional al cuadrado de la distancia (d) que las
separa
𝑚 𝑚
𝐹=𝐺
𝑑

en la cual G es igual a 6,67 x 10 -11 N.

IX. El gobierno ha determinado que el precio del transporte variará en función de la evolución del
precio de la gasolina. Entre otros, ha establecido las nuevas condiciones de contratación de los
transportes de mercancías por carretera, describiendo el incremento (𝛥𝑃) que los transportistas
pueden hacer de acuerdo al tipo de vehículo,
a) Vehículos con un peso máximo autorizado igual o superior 20 toneladas será:

𝐺 × 𝑃 × 0.3
𝛥𝑃 =
50

b) Vehículos con un peso máximo autorizado de 3.5 e inferior a 20 toneladas, con excepción
de los de obras:

𝐺 × 𝑃 × 0.2
𝛥𝑃 =
50

c) Vehículos de obras con un peso máximo autorizado de 3.5 e inferior a 20 toneladas:


𝐺 × 𝑃 × 0.15
𝛥𝑃 =
50

d) Vehículos con un peso máximo autorizado inferior a 3.5 toneladas:

𝐺 × 𝑃 × 0.1
𝛥𝑃 =
50
Donde:
- 𝛥𝑃, cantidad que el transportista puede incrementar en el precio;

Capítulo 3 | Página 49
- G, índice de la variación del precio medio de la gasolina realizado por el gobierno entre el
momento en que se contrató el transporte y el momento en el que se realizó efectivamente;
- P precio anterior establecido por transporte de mercancía.

a) Realiza el análisis y diseño de un algoritmo que determine el nuevo precio de un transporte de


mercancía, de acuerdo con la información anterior.
b) Para probar tu algoritmo realiza una prueba de escritorio para calcular el precio por transporte
de mercancía para un vehículo de obras con una masa de 7 toneladas, considerando que el
costo del transporte era de $20,000.00 y la variación del precio medio de la gasolina es de un
5%.

X. La relación de transmisión entre dos engranajes circulares con un determinado número de


dientes Zi, está dada por la siguiente ecuación:
𝜔 𝑍
=−
𝜔 𝑍

Donde:
ω1 es la velocidad angular de entrada
ω2 es la velocidad angular de salida transmitida
Z1 es el número de dientes del engranaje de entrada.
Z2 es el número de dientes del engranaje de salida.
El signo menos indica que se invierte el sentido del giro.

Con la información anterior realiza el análisis y diseño de un algoritmo (diagrama de flujo o


pseudocódigo) que calcule el número de dientes del engranaje de salida.

XI. Tomando como base el algoritmo de la multiplicación rusa, realiza una prueba de escritorio, el
multiplicando será 15 y el multiplicador 7.

Capítulo 3 | Página 50
Capítulo
Lenguaje C: elementos
básicos
"Los programas deben ser escritos para que los lean las
personas, y solo incidentemente, para que los lean las
máquinas”

Abelson and Sussman

OBJETIVOS:
Al finalizar este capítulo el estudiante:

o Identificará qué es una variable y una constante, y cómo puede cambiar el estado de una variable
utilizando el operador de asignación y la función de lectura.
o Podrá identificar los datos involucrados en un problema así como sus tipos. Además conocerá
los tipos básicos existentes en el lenguaje C y en qué casos es recomendable utilizarlos.
o Identificará los conceptos de: operador, operando, expresión, sustitución y evaluación, para
diseñar expresiones aritméticas expresadas en lenguaje C que modelen situaciones reales.
o Evaluará expresiones aritméticas escritas en lenguaje C utilizando correctamente las reglas de
precedencia de los operadores junto con la sustitución de variables.
o Tendrá las nociones básicas de la declaración de variables y constantes en lenguaje C, así como
las reglas para asignar nombres a las mismas.
o Implementará programas simples en lenguaje C que resuelvan problemas reales de aplicación.

4.1 INTRODUCCIÓN
El lenguaje de programación C fue creado en el año de 1972 por Dennis M. Ritchie en los Laboratorios
de Bell de AT&T; adoptó el nombre de C debido a que muchas de sus
características fueron tomadas de un lenguaje de programación llamado B. C
es considerado un lenguaje de programación de alto nivel que “se define como
un lenguaje de programación de sistemas debido a su utilidad para escribir
compiladores y sistemas operativos” ([Kerninghan,1991], página1); sin
embargo es considerado de propósito general ya que se pueden desarrollar
programas de diferentes disciplinas.
El propósito de este capítulo es empezar a escribir programas simples en el
lenguaje de programación C; para ello centraremos nuestra atención únicamente en los siguientes
elementos básicos: variables y constantes, expresiones aritméticos y funciones de entrada y salida

Capítulo 4 | Página 1
estándar; dejando pendiente otras características de C que nos permitan construir programas más
complejos para capítulos posteriores.
Iniciaremos este capítulo presentando el proceso que se sigue para escribir un programa en un lenguaje
de programación, hablaremos de la forma en la cual se traduce un programa escrito en lenguaje C –que
no puede ser ejecutado por una computadora– a un programa en lenguaje de máquina –que puede si
puede ser ejecutado–. Después formalizaremos los conceptos de variable y constante, revisaremos los
tipos de datos permitidos en C así como los operadores y funciones con las que se pueden manipular.
Asimismo, utilizaremos las funciones de entrada y salida estándar para escribir programas que se
comuniquen con el usuario por medio del teclado y monitor.

4.2 PROCESO DE PROGRAMACIÓN


El proceso de programación en un lenguaje de alto o bajo nivel se ilustra en la figura 4.1. El
programador debe escribir el programa fuente en el lenguaje de programación elegido, por tanto debe
conocer la sintaxis que se refiere al conjunto de reglas que detallan la forma correcta de escribir
instrucciones válidas en el lenguaje y la semántica que el conjunto de reglas que explican que significa
cada instrucción. Una vez escrito el programa fuente en el lenguaje elegido, ya sea de alto o bajo nivel,
debe traducirse a lenguaje de máquina para que entonces si pueda ser ejecutado por la computadora. Si
el lenguaje de programación es de bajo nivel, el encargado de realizar la traducción es un programa
llamado ensamblador; pero si es un lenguaje de alto nivel el proceso se realiza mediante un intérprete
o un compilador.

Lenguaje de Programación
(Sintaxis + Semántica)

Traductor

(Ensamblador, Intérprete, o Compilador )

Lenguaje de Máquina
(cadenas de 0´s y 1´s)

FFFFf

Figura 4.1: Proceso de programación en un lenguaje de alto o bajo nivel1


1
Esta figura aparece en http://aows.wordpress.com/2008/05/

Capítulo 4 | Página 2
La diferencia principal entre un intérprete y un compilador es que el primero traduce y ejecuta el
programa fuente línea por línea; mientras que el segundo traduce todo el programa fuente produciendo
un nuevo programa llamado objeto escrito en lenguaje de máquina, mismo que después convierte en
un programa ejecutable utilizando un programa ligador.
Particularmente, el lenguaje C es compilado, razón por la cual describiremos de manera general el
proceso de compilación, aunque cabe resaltar que el proceso del ensamblador es semejante.
La compilación es un proceso que puede separar en dos grandes etapas:
1) Análisis que es la fase se revisa el programa fuente en busca de errores sintácticos y semánticos,
de acuerdo a las reglas del lenguaje.

2) Síntesis, etapa durante la cual se lleva a cabo la generación de código máquina y la optimización
del mismo. Si el programa fuente este dividido en diferentes archivos, el ligador es el programa
responsable de agruparlos en un solo programa objeto para generar, finalmente, el programa
ejecutable. Este proceso se ilustra en la siguiente figura.

Programa fuente: Programa objeto:


ejemplo.c ejemplo.obj
#include <stdio.h> COMPILADOR
main(){
int a,b,s; 100011110
printf("Ingresa dos
011110111
números enteros:");
scanf("%d,%d",&a,&b); Análisis Síntesis
s=a+b; 100011101
printf(“suma=%d”,s);
} 111100111
10110111

Biblioteca de subprogramas LIGADOR


1001
1111 1001
1001
1100 1111
1111
1100
1100

100011110
Programa ejecutable: 011110111
ejemplo.exe 100011101
111100111
10110111

Figura 4.2: Proceso de compilación y ligado

Capítulo 4 | Página 3
Algunos lenguajes como C tienen definidas instrucciones especiales que se llevan a cabo antes del
proceso de compilación por el preprocesador, a las cuales conocemos como directivas. Los
compiladores de lenguaje C ofrece distintas directivas, las más utilizadas son:

 #define que se utiliza para definir constantes y macros


 #include que se usa cuando se desea incluir un archivos de cabecera escritos por el
programador o una bibliotecas.
Entendiendo por biblioteca un archivo que contiene código objeto de una colección de funciones que
realizan tareas específicas y que pueden ser utilizadas en los programas que escribamos en lenguaje.
Algunas de las bibliotecas en lenguaje C que más utilizaremos son:

 stdio.h: contiene funciones de entrada y salida estándar,


 math.h: contiene funciones matemáticas,
 stdlib.h: incluye procesos del sistema operativo, y
 string.h: tiene definidas funciones para manejo de cadenas.

En el lenguaje C todas las directivas inician con el caracter almohadilla “#”, seguido del nombre de la
directiva y los elementos que la integran. Por ejemplo, para incluir la biblioteca stdio.h se utiliza la
directiva #include seguido del nombre encerrado entre mayor que y menor que “< >”2
#include<stdio.h>

4.3 UN PROGRAMA SIMPLE EN C


No hay mejor forma de aprender un lenguaje de programación que empezar a programar. En el
siguiente cuadro se muestra un programa simple en lenguaje C que imprime en pantalla el mensaje
“Hola mundo”.

#include <stdio.h>
#include <stdlib.h>

main( )
{
printf (“Hola Mundo...\n\n”);
system(“pause”);
}
Programa 4.1: holaMundo.c

Los programas en C constan de funciones y variables, entendiendo por función un conjunto de


instrucciones destinadas a realizar una tarea particular, en tanto que las variables son los espacios de
memoria donde se almacenan los datos que se emplean u obtienen durante el proceso. Dada la sencillez

2Si se trata de un archivo creado por el mismo programador entonces el nombre deberá ir encerrado entre comillas en lugar de los símbolos
menor que y mayor que.

Capítulo 4 | Página 4
de la tarea realizada por el programa 4.1 no se requirió de ninguna variables; sin embargo, se define
una función llamada main, que se distingue por ser el punto de partida de la ejecución de un programa,
en otras palabras, cualquier programa en C debe tener una función main.

Las instrucciones que forman parte de una función deben estar encerradas entre llaves “{}”, indicando
con ellas el inicio y fin de la función. Como puedes ver en el programa 4.1 las únicas instrucciones que
forma parte del main son:
printf (“Hola Mundo...\n\n”);

system(“pause”);

Ambas son llamadas a función –lo cual traduce a que se ejecuten las instrucciones que forman parte de
la función–, la primera invoca a la función printf que imprime en pantalla el mensaje encerrado
entre paréntesis. La segunda invoca a la función system que permite hacer una petición al sistema
operativo indicada en el argumento, en este caso se solicita que haga una pausa hasta que el usuario
presiones una tecla. A los datos que se le envían a una función cuando se invoca se le conocen como
argumentos. Observa que una función se invoca escribiendo el nombre de la función seguido de los
argumentos encerrados entre paréntesis. Ahondaremos más en el tema de las funciones en capítulos
posteriores.

Las dos primeras líneas de código corresponden a directivas #include, la primera para incluir la
biblioteca stdio.h donde se encuentra definida la función printf y la segunda stdlib.h que
contiene la definición de la función system.

La ejecución del programa 4.1 se ilustra en la siguiente figura.

Figura 4.3: Ejecución del programa holaMundo.c

Como puedes observar, el primer reto con el que nos encontramos al escribir un primer programa en
lenguaje C como hispano hablantes es que las instrucciones están escritas en inglés; sin embargo, esto
con la práctica se vuelven parte de nuestro lenguaje como programadores.
El lenguaje C es sensible a mayúsculas y minúsculas, así que las palabras reservadas deben de escribirse
siempre en minúsculas (tal cómo se muestra en la Tabla 4.2), de lo contrario se generará un error de
sintaxis en el momento de la compilación.

Capítulo 4 | Página 5
4.4 VARIABLES

Al diseñar la solución de un problema en un algoritmo o programa hemos visto que requerimos


variables para almacenar datos que se van a ocupar en la solución, estos pueden ser proporcionados por
el usuario o el resultado de una operación; desde el punto de vista de programación definimos a una
variable como un espacio de memoria donde se puede almacenar un valor que será utilizado en el
programa y que tiene asociados:

 un nombre o identificador con el cual haremos referencia al espacio de memoria,


 un tipo que corresponde al tipo de datos que pueden ser almacenados en el espacio de memoria
y,
 un estado que se refiere al dato que está almacenado en el espacio de memoria correspondiente
–sino ha sido asignado ningún valor se dice que es indefinido–.

Implícitamente las variables utilizadas en un programa también tienen asociada la dirección de


memoria física que le corresponde.

4.4.1 Identificadores

¿Para qué utilizar identificadores?

Imaginemos por un momento la vida de un programador sin los identificadores: En el capítulo 1


observamos la importancia que tiene la memoria en la ejecución de un programa, pues es ahí donde se
almacenan tanto las instrucciones como los datos. En los programas que hicimos en dicho capítulo si
deseábamos guardar un resultado en un espacio de la memoria teníamos que indicar la dirección física
en la cual se deseaba almacenar (ver programa 1.1); de igual manera, cuando queríamos realizar una
operación que involucrara alguno de los datos escribíamos explícitamente la localidad de la memoria.
Un identificador es una etiqueta que se designa a un espacio de memoria físico para poder acceder al
dato que contiene. Si un programador no contara con los identificadores tendría que conocer qué
dirección de memoria será asignada a cada uno de los datos y resultados para que pudiera escribir un
programa, lo cual es imposible ya que la asignación de memoria se hace hasta el momento de la
ejecución.

En lenguaje C existen algunas restricciones en los identificadores:

a) Los nombres de las variables se integran de letras3 y dígitos, empezando siempre con una letra.
Por ejemplo: enteroA, arco3, S184.

3Sólo se pueden utilizar letras dentro del código ASCII (ver Figura 2.1), por lo tanto letras acentuadas y la ñ no pueden utilizarse, ya que
pertenecen al código ASCII extendido. (Figura 2.2 y Figura 2.3)

Capítulo 4 | Página 6
b) El carácter de subrayado “_” se considera como letra y generalmente se usa para darle una mejor
legibilidad al nombre de una variable, pero no se puede comenzar el nombre de la variable con
este carácter. Algunos ejemplos son entero_A, area_Circulo, dato_1.
c) Los nombres de las variables no pueden contener espacios en blanco.
d) Las palabras reservadas que se muestran en la tabla 4.1 no pueden utilizar como identificadores
ya que tienen un significado especial para C.

Palabras reservadas
asm continue float register Struct volatile
auto default for Return Switch while
break Do goto Short Typedef
case double If Signed Unión
char Else int Sizeof Unsigned
const Enum long Static Void
Tabla 4.1: Palabras reservadas en lenguaje C

Es importante tener en cuenta que en el lenguaje C se distingue entre letras mayúsculas y minúsculas,
por lo tanto, los identificadores area, Area y AREA se referirían diferentes espacios de memoria.


Nota:
Es común que las personas que comienzan a programar cometan errores al asignar nombres de variables
como por ejemplo:

1. Radio Circulo: error, el identificador contiene un espacio entre las palabras Radio y
Circulo.
2. 8_radio: error, no es posible que inicie con un dígito.
3. radio_círculo: error, el identificador no puede tener letras acentuadas.
4. auto: error, es una palabra reservada.

Finalmente, se recomienda que los identificadores hagan referencia al uso que se le va a dar a la
variable.

Capítulo 4 | Página 7
4.4.2 Tipos de datos
¿Por qué son importantes los tipos de datos?

Todos los datos que son procesados por una computadora –como lo hemos visto– son almacenados en
la memoria y no importa de qué tipo sean se representan mediante bits; sin embargo, dependiendo de
su tipo es el número de bits que se emplean y las operaciones que se pueden realizar sobre estos.
En general, los tipos de datos se pueden clasificar en simples y estructurados independientemente del
lenguaje de programación y estos grupos a su vez se pueden agrupar como se muestra en la siguiente
tabla:

Tipos de datos
Enteros
 Numéricos
Puntoflotante
Simples
 Lógicos (verdadero o falso)

 Alfanuméricos (caracteres)

Unidimensionales
 Arreglos
Estructurados Multidimensionales

 Estructuras
Tabla 4.2: Clasificación de tipos de datos independiente de un lenguaje de programación

Por el momento sólo nos centraremos en los tipos simples que ofrece el lenguaje C, los cuales en
realidad son pocos, mas no obstante suficientes, y se muestran en la tabla 4.3.
Tipo Descripción Ejemplo Tamaño4
Capaz de contener un ´a´
Símbolos del código ASCII
char carácter del conjunto de ´C´
1 byte
caracteres ASCII. ´3´
2 bytes
int Enteros 1024
-258
Número de punto flotante de 10.5
float -11.6 4 bytes
precisión normal.

Punto flotante de doble


double 0.00045
precisión 8 bytes
-0.55236
Tabla 4.3: Tipos de dato básicos en C

4El tamaño puede variar de un compilador a otro y dependiendo de éste será el número máximo o mínimo que se puede representar. En la
biblioteca <limits.h> se encuentran definidas constantes simbólicas para todos los tamaños.

Capítulo 4 | Página 8
Es posible aumentar o disminuir la capacidad de representación de las variables de tipo int utilizando
los modificadores: long que corresponde a un entero de mayor capacidad, short que es un entero
más corto o unsigned un entero sin signo.

En el caso del tipo de datos lógicos, también llamados booleanos, es importante destacar que el modo
en que el lenguaje C maneja estos valores es por medio de valores enteros: cero equivale a falso y
cualquier entero distinto de cero representa verdadero. Por tal motivo, si se requiere definir un dato
booleano se recomienda usar el tipo short.

Nota:
No debes olvidar que en lenguaje C todos los tipos se escriben con minúscula.

4.4.3 Declaración

A la acción de definir una variable en un lenguaje de programación se conoce como declaración de


variable. La declaración de una variable es la instrucción para reservar espacio de memoria conforme
al tipo de datos indicado. La sintaxis para declarar variables en C es:
<tipo> <identificador>;

También es posible declarar una lista de variables en una misma instrucción, siempre y cuando sean
del mismo tipo, la forma correcta es la siguiente:

<tipo> <identificador1>, . . . , <identificadorN>;

Ejemplo 4.1: Veamos algunas declaraciones que ilustren los puntos expuestos.

Declaración Descripción

Reservar espacio de memoria para almacenar el


float costo; número de estudiantes en un grupo que
corresponde a un número entero.

Declaración de una variable para almacenar el


unsigned int
costo de un artículo, considerando que el costo
total_estudiantes;
es un número con decimales.

Declaración de dos variables de tipo char para


char sexo1, sexo2;
almacenar el sexo de dos estudiantes.

Tabla 4.4: Ejemplos de declaración de variables

Capítulo 4 | Página 9
En la figura 4.4 se plantea de forma gráfica la declaración de las variables, el área sombreada representa
el espacio de memoria reservada para guardar los valores asignados a la variable, como podemos ver
los datos de tipo float requieren mayor espacio en memoria que los de tipo int o char.

MEMORIA

Dirección Contenido

01

02

03

04 costo

05

06

51

52 total_estudiantes

53

102 sexo1

103

104 sexo2

105

Figura 4.2: Representación de la memoria al declarar variables

Nota:
Todas las variables que se utilizan en un programa deben ser declaradas antes de empezar a realizar
operaciones o asignaciones sobre de ellas.

Capítulo 4 | Página 10
4.4.4 Asignación

La forma en que se asigna un valor a una variable es a través del operador de asignación que en
pseudocódigo o diagrama de flujo se representa mediante una flecha dirigida hacia la derecha “←” ;
sin embargo en lenguaje C se representa por el símbolo de igual “=”.
La sintaxis general es la siguiente:
<variable> = <expresión>;

El objeto que aparece del lado derecho de la asignación siempre debe ser el identificador de una
variable, mientras que el lado izquierdo debe ser una expresión que al ser evaluado el resultado
corresponda a un dato del mismo tipo de la variable en cuestión.
Una asignación siempre afecta el estado de una variable, pues su significado se puede interpretar como
“guarda un valor en el espacio de memoria asignado a la variable indicada del lado derecho”.
Para ilustrar los conceptos anteriores revisemos el siguiente ejemplo.
Ejemplo 4.2: Suponiendo que hemos realizado la siguiente declaración de variables:

int hombres, mujeres, total;

En la tabla 4.5 se ilustran tres ejemplos de asignación. En la primera columna se describe la instrucción,
en la segunda aparece su representación en pseudocódigo y en la tercera la codificación en lenguaje C.

Descripción Pseudocódigo Lenguaje C


Almacena 20 en la
hombres ←20 hombres = 20;
variable hombres

Asigna 10 a la
mujeres ←10 mujeres = 10;
variable mujeres

El total de total = hombres +


estudiantes es igual a mujeres;
total ← hombres + mujeres
la suma de hombres
más mujeres

Tabla 4.5: Ejemplos de declaración y asignación de variables

En el último caso, el valor asignado a la variable totalEstudiantes está determinado por la


evaluación de la expresión hombres + mujeres, dados los valores de las variables el valor
almacenado en totalAlumnos es 30.

Capítulo 4 | Página 11
4.4.5 Inicialización

En lenguaje C es posible asignarle valores a las variables cuando se declaran, a lo cual llamamos
inicialización. La forma general de hacerlo es la siguiente:
<tipo> <identificador> = <valor>;

Si se deseas declarar e inicializar una lista de variables entonces la sintaxis es la siguiente:

<tipo> <ident1> = <valor1>, … , <identN> = <valorN>;

Ejemplo 4.2: En la siguiente línea se ilustra la declaración e inicialización de dos variables de tipo
entero.

int radio = 20, diametro = 2*radio;

A la primera le asignamos el valor constante 20; mientras que a la segunda le asignamos el resultado
de la evaluación de una expresión numérica 2*radio que en este caso es 40 –un entero–.
Suponiendo que almacenamos un entero en 8 bits5 y tomando en cuenta que la representación binaria
de 20 y 40 es 00010100 y 00101000, respectivamente, en la siguiente figura se ilustra el estado de la
memoria resultado de esta instrucción.
MEMORIA

Dirección Contenido
01
02

03 0 0 0 1 0 1 0 0 radio

08 0 0 1 0 1 0 0 0 diámetro

09
Figura 4.5: Representación de la memoria al declarar variables
e inicializar variables

5 Lo cual en la práctica no es cierto pues un entero generalmente se almacena en 32 bits.

Capítulo 4 | Página 12
Nota:
No debes olvidar escribir punto y coma “ ; ” al final de una declaración o asignación de variables pues
con ello se indica el final de la instrucción, sino lo haces el compilador reportará un error de sintaxis.

4.5 CONSTANTES

Las constantes se caracterizan por tener un valor que se establece en tiempo de compilación y no puede
variar durante la ejecución de un programa. En un programa pueden aparecer constantes de dos tipos:
literales y simbólicas. Las constantes literales son valores de un determinado tipo escritos directamente
en un programa, pueden ser números como 3.1419 o 7, o bien, caracteres por ejemplo ‘a’ o ‘Z’. Las
constantes simbólicas se refieren a valores constantes a los cuales se les asocia un identificador, por
ejemplo, el identificador PI al cual se le asigna el valor constante 3.1416.

En lenguaje C es posible tatar con ambos tipos de constantes.

4.5.1 Constantes literales

En el caso se de las constates literales numéricas algunas veces se estila escribir una letra que indique
el tamaño o tipo de la constante.

Las constantes enteras como 2010 o -2010 se consideran de tipo int y se suele escribir tal cual.
Cuando la constante entera supera el valor máximo representado por un int, por ejemplo el número
213458789, en este caso le agrega el sufijo “ l ” (ele) o “ L” para indicar que es una constante de tipo
int, la representación sería 213458789L. En el caso de las constantes enteras sin signo se agrega al
final del número la letra “ u ” o “ U ” para indicar que es de tipo unsigned, o bien, “ ul ” o “ UL ” si
es un entero largo sin signo.
Los valores numéricos que implican precisión decimal o constantes punto flotante – cuya
representación contienen un punto decimal (14324.983) o un exponente (2E-5)6 o ambos (1.14-5) – se
consideran de tipo double a menos que se indique el tipo al final, esto es, si son de tipo float se
debe escribir el sufijo “f ” o “F”, o bien, “ l ” o “ L ” para especificar que es de tipo double.
Una constante carácter se representa internamente con su código ASCII –mediante un número– y se
escribe entre apóstrofes para evitar que se confundan con el identificador de alguna variable, por
ejemplo: la constate “A” se representa como ‘A’. Cabe resaltar que al representar internamente una

6 La letra E indica que el siguiente dato es el exponente al cual se eleva el número que aparece al inicio.

Capítulo 4 | Página 13
constante carácter como un número entero, éstas pueden ser utilizadas en operaciones definidas para
este tipo de datos, aunque aparentemente no se hayan declarado como tal.
Existe una serie de caracteres especiales que no tiene una representación gráfica, como por ejemplo el
salto de línea o el retorno de carro, estos se representan mediante lo que se conoce como secuencias
de escape, las cuales se forma escribiendo una diagonal invertida “ \ ” seguida de otro carácter, las más
utilizadas se muestran en la siguiente tabla.

Secuencias de escape
‘\n’ Salto de línea
‘\t’ Tabulador
‘\b’ Espacio hacia atrás
‘\f’ Salto de página
‘\”’ Comillas
‘\\’ Barra invertida
‘\0’ Caracter nulo
Tabla 4.6: Secuencias de escape

Una constante cadena es un tipo de dato estructurado que se define como la concatenación de cero o
más caracteres, por ejemplo: la palabra Nada o la frase Hola mundo. En C las constantes de este tipo
se escriben entre comillas dobles, por ejemplo “Nada” y “Hola mundo”7
Las secuencias de escape también nos ayudan cuando necesitamos incluir dentro de una cadena
símbolos que pueden causar confusión a la hora de compilar el código. Por ejemplo, la cadena “Hola
mundo” se representa como “\“Hola mundo\” ”, las comillas exteriores indican que es una constate
cadena y las interiores antecedidas por la diagonal invertida “\” se refieren a las comillas que si forman
parte de la cadena.

4.5.3 Constantes simbólicas

Se recomienda utilizar constantes simbólicas cuando tenemos datos que no varían en un problema y
tienen un significado especial, por ejemplo, si queremos calcular el área y perímetro de un círculo, el
número 3.1416 como valor de π se debe representar mediante una constante simbólica.

En C existen dos formas de declarar una constante simbólica. La primera es utilizando la directiva de
preprocesador #define, la cual asocia un identificador a un valor constante sin reservar espacio en
memoria. La sintaxis general es la siguiente:

7 Las comillas no forman parte de la cadena, sólo sirven para delimitarla.

Capítulo 4 | Página 14
#define <identificador> <valor_constante>

Ejemplo 4.3: En la siguiente línea se ilustra la definición de la constante π


#define PI 3.1416

Con esta instrucción cada vez que en el programa se escriba el identificador PI éste será sustituido por
el compilador con el valor de 3.1416 (no se reserva espacio en memoria para el valor 3.1416 sólo se
hace una sustitución textual en el momento de compilación).
La segunda forma de declarar una constante simbólica es reservando espacio de memoria –declaración
de una variable – que tenga la restricción de sólo lectura para impedir que el valor sea cambiado.
La sintaxis general es similar a la forma de declarar una variable sólo que se antepone al tipo la palabra
reservada const, indicando con ello al compilador que no puede modificarse en lo sucesivo; asimismo
es obligatorio inicializar.
const <tipo> <identificador> = <valor_constante>;

Ejemplo 4.4: La definición de la constante π como un espacio de memoria de sólo lectura,


considerando que el dato es un decimal que puede representarse con un float sería de la siguiente
manera:
const float PI = 3.1416 ;

La principal diferencia es que con la primera definición no se reserva espacio en memoria sino que se
hace una sustitución textual en el momento de la compilación, mientras que con la segunda se asigna
un espacio de memoria del tamaño indicado por el tipo de dato y este permanece bloqueado para evitar
se realicen cambios. Otra diferencia es que con #define la declaración debe ser al principio del
programa en la parte destinada a las directivas y por tal razón la constante se puede utilizar en cualquier
parte del programa, en cambio con la palabra reservada const puede ser dentro o fuera de las funciones
al igual que las declaraciones de variables y solo puede ser utilizada en el bloque de instrucción en el
haya sido declarada.8
Las reglas que se siguen para formar el identificador de una constante son las mismas expuestas para
las variables; sin embargo, recomendamos utilizar letras mayúsculas para diferenciarlas.

 

8 Esto lo abordaremos más a detalle en el capítulo correspondiente a funciones.

Capítulo 4 | Página 15
Nota:
Observa que la definición de una constante mediante #define no lleva punto y coma al final, a
diferencia de la declaración de una constante.

4.6 Operadores y Expresiones

Un operador es un artefacto que representa una acción (operación) que actúa sobre uno o más datos,
denominados operandos. Por ejemplo, el operador + (más) está asociado a la suma de dos número o
indica que el operando es positivo; por otro lado, el operador - (menos) representa la acción de restar
o indica que el operando es negativo. Dependiendo del número de operandos sobre los cuales actúen
se clasifican en:

 unarios cuando sólo actúan sobre un operando, por ejemplo -3 ó +5.


 binarios cuando actúan sobre dos operandos, por ejemplo 3+5 o 5-2.
 n-arios actúan sobre n operandos operandos.

Otra característica más de los operadores es que pueden ser:

 prefijos si se escriben antes de los operandos, por ejemplo: + 5 4


 infijos cuando se escriben en medio de los operadores: 5 + 4
 postfijos si se escriben después de los operandos: 5 4 +

Los tres ejemplos presentados representan la misma acción suma 5 más 4. En el caso de los operadores
binarios lo más común es que aparezcan en notación infija.

Las constantes (literal o simbólica) y variables se consideran una expresión simple, y cuando
combinamos expresiones simples con operadores o funciones de una forma válida tenemos una
expresión compuesta. Por ejemplo, 5 y x son expresiones simples (la primera es una constante literal
y la segunda una variables) al combinar utilizando el operados + obtenemos la expresión compuesta
5+x.

Por otro lado, evaluar una expresión es el proceso sustituir las variables por un valor fijo y realizar
todas las operaciones indicas en la expresión para obtener un resultado.

En resumen, las expresiones nos permiten manipular diferentes valores para obtener uno nuevo. Los
artefactos que nos permiten describir dicha manipulación son los operadores y el mecanismo que nos
lleva a generar un nuevo valor se llama evaluación.

En lenguaje C toda expresión tiene asociados implícitamente un tipo de dato que corresponde al tipo
de dato que se obtiene al evaluarla, en otras palabras, depende de los tipos de datos y operadores que
contiene. Veamos el siguiente ejemplo:

Capítulo 4 | Página 16
Ejemplo 4.5: Dada la siguiente declaración de variables.
int x = 20, y = 30;

El tipo de datos correspondiente a la expresión x+y (20+30) es int ya que ambos datos son
enteros.

Cuando una expresión contiene datos de diferentes tipos el tipo de la expresión corresponderá al tipo
de datos de mayor precisión que aparece, a esto se le conoce como promoción de tipos. La promoción
entre tipos siempre se hace hacia arriba, es decir, los de menor precisión se promueven a los tipos de
mayor precisión, por ejemplo un tipo int se promueve a un tipo float.

Ejemplo 4.6: Supongamos que se ha realizado la siguiente declaración


int x = 20;

float y = 30;

Dada la expresión x+y, el tipo de dato resultado de la evaluación es float aunque aparentemente el
resultado sea un entero (50).

4.6.1 Operadores aritméticos

El lenguaje C proporciona una serie de operadores aritméticos para los tipos numéricos definidos en la
sección anterior. Así tenemos los operadores binarios:

Operador Acción
+ Suma
- Resta binaria y operador unario de signo negativo.
* Multiplicación
/ División
% Módulo(residuo)
Tabla 4.7: Operadores aritméticos binarios

Ejemplo 4.7: El operador módulo (%) representa el residuo de dividir dos números enteros (división
entera)9, de tal manera que, el resultado de la expresión 15 % 6 es 3
2
6 15
3 Residuo

9 La división entera trunca la parte fraccionaria dando como resultado un dato de tipo int.

Capítulo 4 | Página 17
Nota:
 El operador % sólo puede ser aplicado a datos de tipo enteros.
 Los operadores + y – también se pueden utilizar como unarios para indicar el signo.

C también proporciona dos operadores para incrementar y decrementar el valor de una variable,
mismos que se muestran en la siguiente tabla.
Operador Acción

++ Incremento (en una unidad)

-- Decremento (en una unidad)

Tabla 4.8: Operadores de incremento y decremento

El operador ++ aumenta una unidad el valor de la variable, mientras que el operador –- lo disminuye
una unidad.

Ejemplo 4.8: Supongamos que estamos trabajando con la variable x y tenemos:

x++; equivale a hacer: x = x + 1;

x--; equivale a hacer: x = x – 1;

Si el valor de x es 5 el resultado de aplicar ++ y -- será:


Estado de la variable “x”
Operación
Antes Después
x++; 5 6
x--; 5 4
Tabla 4.9: Ejemplo de los operadores de incremento y decremento

Estos operadores tienen una característica muy particular, ambos pueden utilizarse como prefijos y
sufijos. Si aparece antes de la variable (++x) entonces se incrementa antes de que se utilice su valor
pero si aparece después (x++) entonces se usa el valor y después se incrementa. Esto tiene sentido
cuando forman parte de una expresión más compleja donde su valor sea utilizado. Veamos algunos
ejemplos.
Ejemplo 4.9: Suponiendo que tenemos la siguiente declaración de variables:
int x = 0, y = 5;

La instrucción
x = y++;

cambia el estado de la variable x a 5 (primero asigna y después incrementa) mientras que y se


vuelve 6.

Capítulo 4 | Página 18
En cambio, la instrucción
x = ++y;

deja a x como 6 (primero incrementa y después asigna) y la variable y también es 6.

Si se desea hacer incrementos (o decrementos) de más de uno se debe utilizar el operador de asignación
“=” y el operador “+” en la expresión. Por ejemplo, si se desea incrementar la variable x en tres unidades
la instrucción es:
x = x+3;

Lo cual se puede simplificar de la siguiente forma:


x+=3;

En general se puede utilizar un operador algebraico binario seguido de una asignación, para abreviar
expresiones en las cuales la variable del lado derecho se repite inmediatamente en el lado derecho,
estos operadores se conocen como operadores de asignación y se resumen en la tabla 4.10.

Operador /Asignación (op =)

Operador Acción Ejemplo Equivalente a:

+= Suma los dos operandos asignando x+=3; x = x + 3;


el resultado al primer operando

-= Resta los dos operandos asignando x-=3; x = x - 3;


el resultado al primer operando

*= Multiplica los dos operandos x*=2; x = x * 2;


asignando el resultado al primer
operando

/= Divide los dos operandos asignando x/=2; x = x / 2;


el resultado al primer operando

%= Da el modulo los dos operandos x%=3; x = x % 3;


asignando el resultado al primer
operando

Tabla 4.10: Operadores de asignación

Capítulo 4 | Página 19
4.6.2 Operadores relacionales

Los operadores que permiten construir expresiones booleanas son: operadores relacionales y
operadores booleanos.
En la tabla 4.11 se muestran los operadores relacionales que tiene C.

Representación Representación Acción


matemática en lenguaje C

> > Mayor que

< < Menor que

≥ >= Mayor o igual

≤ <= Menor o igual

= == Igualdad

≠ != Diferente a

Tabla 4.11: Operadores relacionales en C

Ejemplo 4.10: Supongamos que tenemos la declaración

int x=2, y=3, z=5

en la siguiente tabla dan algunos ejemplos de expresiones booleanas construidas a partir de operadores
relacionales, en la segunda columna aparece el resultado de la evaluación en lenguaje C en tanto que
en la última columna su significado.

Expresión Valor en C Valor Booleano

x > y; 0 Falso

x != z; 1 Verdadero

x+y == z; 1 Verdadero

Tabla 4.12: Ejemplos de operadores relacionales en C

 

Capítulo 4 | Página 20
Nota:
Observa que el operador de igualdad se escribe con dos símbolos de igualdad seguidos (==), uno de
los errores error más común cuando empezamos a programar en C es escribir una comparación con un
sólo símbolo de igualdad (=), lo cual es incorrecto ya que éste corresponde a una asignación.

4.6.3 Operadores lógicos

Los operadores denominados lógicos manipulan únicamente expresiones booleanas, las operaciones
lógicas definidos en lenguaje C son la negación (NO), la disyunción (O) y la conjunción (Y). La sintaxis
se muestra a continuación.

Representación Representación Acción


matemática en lenguaje C

Conjunción, es verdadero sólo cuando los


 &&
dos operandos son verdaderos.

Disyunción, es falso sólo cuando ambos


 || operandos son falsos; en caso contrario, se
evalúa como verdadero.

~ Negación, invierte el valor de verdad del


!
operando.

Tabla 4.13: Operadores lógicos en C

Estos operadores serán estudiados a detalle en el siguiente capítulo.

4.6.4 Precedencia de operadores

La precedencia de un operador nos indica el orden en el que debe ser evaluado cuando existe más
operadores en la expresión. Por ejemplo, si tenemos:
2+3*5

Capítulo 4 | Página 21
Podríamos evaluarla de dos diferentes formas,
 La primera –la correcta– sería realizar primero la multiplicación 3*5 (=15) y sumar el resultado
a 2, obteniendo 17 como resultado final. Indicando el orden con paréntesis tendríamos la
siguiente expresión:
(2 + (3 * 5)) = 17
 Otra manera –la incorrecta– es hacer primero la suma 2+3 (=5) y después multiplicar el
resultado por 5. De tal manera obtendríamos como resultado final 25.

((2 + 3) * 5) = 25
Sin embargo, la precedencia de los operadores elimina toda ambigüedad pues de acuerdo con ésta la
multiplicación se realiza antes que la suma, por tanto la evaluación correcta es la primera.
En la tabla 4.14 se muestra la precedencia de todos los operadores presentado en esta sección.
Operadores en lenguaje C Prioridad
( ) Mayor
-(unario), ++, --,!
*, /, %
+, -
<, >, >= , <=
==, !=
&&
|| Menor
= +=,*=,-=,%=
Tabla 4.14: Precedencia de operadores en C

Cuando hay dos o más operadores con la misma precedencia en una expresión las operaciones se
evalúan según aparece de izquierda a derecha, excepto para los operadores =, ++, -- y !; en otras
palabras la asociatividad de los operadores es de izquierda a derecha, salvo las excepciones cuya
asociatividad es de derecha a izquierda.

Ejemplo 4.11: Conforme a la prioridad y asociatividad de los operadores, agrega paréntesis a la


siguiente expresión para indicar el orden en el cual se evalúa y determina el resultado si el estado de
las variables es x = 1, y = 2 y w= 4.

z=5 * y + 2 * x – 8 / 2 * w
1) primero se realizan las multiplicaciones y divisiones y su asociatividad es de izquierda a derecha
por lo que tenemos:

z = (5 * y) + (2 * x) – ((8 / 2) * w)
1 2 3
4

Capítulo 4 | Página 22
2) después se hace la suma y resta de izquierda a derecha conforme a su asociatividad:
6

z = ( ( (5 * y) + (2 * x)) – ((8 / 2) * w))


1 2 3
4

3) sustituyendo las variables por su valor obtenemos:

z = ( ( (5 * 2) + (2 * 1)) – ((8 / 2) * 4))

4) Finalmente, se lista las operaciones conforme al orden dado:

z = ( ( 10 + (2 * 1)) – ((8 / 2) * 4))


z = ( ( 10 + 2) – ((8 / 2) * 4))
z = ( ( 10 + 2) – (4 * 4))
z = ( ( 10 + 2) – 16)
z = ( 12 – 16)
z = -4

Ejemplo 4.12: Dada siguiente expresión en lenguaje matemático

𝑧 = 5𝑦 + 2(𝑥 + 𝑦) + 7
La codificación en C –tomando en cuenta que la potencia se puede codificar como una multiplicación–
sería:

z = 5*y+2*(x+y)*(x+y) + 7;

A continuación se evalúa la expresión tomando en cuenta el estado inicial expuesto en la tabla. Los
círculos arriba de la expresión indican el orden en el que se realizan los operadores. Cabe resaltar que
el único operador en esta expresión que cambia el estado de una variable es el de asignación “=”.

Capítulo 4 | Página 23
6 3 1 4 2 5
Estados de las variables
z = 5*(x+y)*(x+y) + 7
Estado inicial

z = 5*(x+y)*(x+y) + 7 x y z

z = 5*(5)*(x+y) + 7 2 3 0

z = 5*(5)*(5) + 7
Estado final
z = 25*(5) + 7
X y z
z = 125 + 7
2 3 132
z= 132

Tabla 4.15 Ejemplo de precedencia de operadores

Ejemplo 4.13: Suponiendo que el estado de las variables es x=2, y=3 y z=0, evalúa la siguiente
expresión y determina el nuevo estado de variables.
z = 5*(y++)+2*(x+y)*(x+y)+7;

a) El operador de incremento aparece como sufijo, por lo tanto primero se utiliza el valor y después
se hace el incremento, entonces la sustitución de las variables por su valor queda de la siguiente
manera, sin olvidar el incremento de la variable y:
z = 5*3+2*(2+3)*(2+3)+7;

b) El siguiente paso es resolver los paréntesis


z = 5*3+2*5*5+7;
c) Ahora se realizan las multiplicaciones
z = 15+2*5*5+7;
z = 15+10*5+7;
z = 15+50+7;

d) Finalmente, realizamos las sumas


z = 65+7;
z = 72;

Por lo que el estado el nuevo estado de las variables es: x=2, y=4 y z=72. Observa que cambió el
estado de las variables: y, z.

Capítulo 4 | Página 24
Si la expresión a evaluar fuera

z = 5*(++y)+2*(x+y)*(x+y)+7;

el resultado obtenido sería: x=2, y=4 y z=99. Se deja como ejercicio al lector verificarlo.

4.7 FUNCIONES MATEMÁTICAS

El lenguaje C incluye una biblioteca de funciones matemáticas llamada math.h10 En la tabla siguiente
se muestran algunas de las funciones definidas en esta biblioteca. La columna Argumento describe el
tipo de datos que recibe la función en tanto que la columna Resultado corresponde al tipo de dato que
devuelve como resultado.
Función Descripción Argumento Resultado Ejemplo
sqrt(x) Raíz cuadrada double double sqrt(900) = 90
exp(x) Function exponencial Double double exp(2.0)= 2.718282
fabds(x) Valor absolute Double double fabs(-5) = 5
log(x) logaritmo neperiano de x Double double log(0.5) = -0.693
Log10(x) logaritmo decimal de x Double double Log10(0.5) = -0.301
floor(x) redondeo hacia abajo Double int floor(6.5)=6
ceil(x) redondeo hacia arriba Double int ceil(6.5)=7
pow(x,y) Devuelve la potencia de x elevada a la y. Double double pow(5,2) = 25
cos(x) coseno de x Double double cos(0.5) = 1
sin(x) seno de x Double double sin(0.0) = 0
tan(x) Tangente de x Double double tan(0.0) = 0
Tabla 4.16: Funciones de la biblioteca math.h

Cabe mencionar que, de acuerdo con la promoción de tipos si la función recibe un double entonces
podemos pasar como parámetro cualquier dato de tipo numérico (int, long, short, unsigned,
float).

Ejemplo 4.14: La codificación de la fórmula para calcular el volumen de un ovoide


4
𝑣𝑜𝑙 = 𝜋𝐷 𝑑
3

Considerando que PI se definió como la constante 3.1415 y que las variables vol, D y d se declararon
de tipo float o doublé, sería:

vol = 4/3 * PI * pow(D,2) * d

10 Siempre que se desee utilizar alguna de estas funciones es necesario incluir en el programa el archivo math.h.

Capítulo 4 | Página 25
Ejemplo 4.14: De acuerdo con el Teorema de Pitágoras el valor de la hipotenusa c se puede obtener
a partir del valor de los catetos a y b con la siguiente fórmula:

𝑐= 𝑎 +𝑏

La codificación de esta expresión quedaría como:

c = sqrt( pow(a,2)+ pow(b,2))

Nota:
Cuando un programa utiliza alguna función matemática debe contener la línea

#include <math.h>

4.8 FUNCIONES DE ENTRADA Y SALIDA

El lenguaje C no posee instrucciones para la entrada y la salida estándar, estas operaciones se realizan
haciendo uso de funciones de biblioteca. No es nuestro objetivo estudiarlas todas, así que nos
centraremos en las funciones putchar, printf, getchar y scanf definidas en la biblioteca
stdio.h.

4.8.1 Impresión de caracteres


El mecanismo de salida más simple es imprimir un carácter en la pantalla con la función putchar que
recibe como parámetro de entrada un carácter. La forma general para invocarla es:

putchar(<caracter>);

donde <caracter> puede ser una constante carácter encerrada entre apóstrofos (‘’) o una variable o de
tipo char o una función que devuelva un dato de tipo indicado o un entero que corresponda al código
ASCII de un carácter.

Ejemplo 4.15: Dada la siguiente declaración e inicialización de variables

int i = 65;
char c = ‘e’;

Capítulo 4 | Página 26
En la primera columna de la tabla 4.17 se ilustran algunas llamadas a la función putchar y en la
segunda columna se muestra lo que se imprime en pantalla.

Instrucción Salida Observaciones


No imprime los apóstrofos sólo el
putchar(‘A’); A
carácter
putchar(c); e Imprime el valor de la variable
El valor de i es 65 que corresponde al
putchar(i); A
código ASCII de la letra ‘A’
La función toupper recibe un dato
putchar(toupper(‘a’)); A
de tipo carácter (puede ser
representado por su código en ASCII)
putchar(toupper(c)); E y lo convierte a mayúsculas.

putchar(tolower(‘A’)); a La función tolower recibe un dato


de tipo carácter (puede ser el entero
putchar(tolower(i)); a correspondiente al código ASCII) y lo
convierte a minúsculas.
Tabla 4.17: Ejemplos de la función putchar

Nota:
Las funciones toupper y tolower se encuentran definidas en la biblioteca ctype.h; por lo que
deberá incluirse la línea de código
#include <ctype.h>

siempre que se utilice en un programa.

4.8.2 Lectura de caracteres

La función getchar sirve para almacenar en una variable –de tipo char- un carácter escrito
mediante el teclado, la sintaxis general para invocarla es:

variable = getchar();

Capítulo 4 | Página 27
Ejemplo 4.16: En el siguiente recuadro se muestra un programa simple donde se utiliza la función.

/* Directivas al procesador */
#include <stdio.h>
#include <ctype.h>

/* Función Principal */
main( )
{

/*Declaración de variables*/
char letra;

/*Instrucciones*/
letra = getchar(); /*Lee un character*/
putchar(toupper(letra));/*Imprime el carácter en mayusculas*/
putchar('\n'); /*Imprime un salto de línea*/
system("pause");

}
Programa 4.2: lecturaCaracter.c

La ejecución del programa se muestra en la siguiente figura:

Antes de la lectura: Después de la lectura:

Figura 4.6: Ejecución del programa lecturaCaracter.c

Nota:
Cuando se lee un dato numérico se presiona enter después del dato, de tal forma que, si la siguiente
instrucción es una llamada a la función getchar ésta leerá el salto de línea indicado por enter y en
consecuencia no esperará a que el usuario introduzca otro carácter. Para evitar este problema se puede
utilizar la instrucción
fflush(stdin);
antes de una instrucción getchar(), con la cual se vacía el contenido del bus de entrada al archivo
de entrada estándar stdin, en otras palabras, limpiamos el buffer de entrada.

Capítulo 4 | Página 28
4.8.3 Impresión con formato

La función printf permite definir un formato para la salida de datos, traduciendo valores internos a
caracteres. La sintaxis para llamar a esta función es:
printf(<cadena_de_control>, <argumento1>, <argumento2>, …);

La <cadena_de_control> es el mensaje de texto que se desea desplegar en el monitor y siempre


tiene que ir en comillas, puede incluir:

 Caracteres ordinarios, que son impresos tal cual,


 Secuencias de escape (ver tabla 4.6) que permiten imprimir caracteres especiales y
 Especificadores de formato, los cuales causan la conversión e impresión de los datos indicado
en la lista de argumentos siguiente – el número de especificadores debe coincidir con el número
de argumentos –.

Los argumentos.<argumentoK> pueden ser una constante, una variable, la invocación de una función
o una expresión, pero siempre debe corresponder al tipo de dato indicado por el K-ésimo especificador
de formato que aparece en la cadena de control.

Los especificadores de formato se utilizan para indicar que en determinada posición del mensaje de
salidas se van a imprimir valores internos, por ejemplo el valor de una variable o constante, o el
resultado de una expresión; además del formato en el cual se van a mostrar. Cada especificación de
conversión empieza con el símbolo “ % ” y termina con un carácter de conversión para indicar que se
va a imprimir. En la tabla 4.17 se muestran los especificadores de formato más utilizados en la función
printf.

Capítulo 4 | Página 29
Especificador Acción

%d, %i Imprime un entero en decimal (int)


%o Imprime un octal sin signo (int)

%x %X Imprime un hexadecimal utilizando abcdef y


ABCDEF, respectivamente para 10,…,15 (int)
%u Imprime un entero sin signo (int)
%c Imprime un carácter (char)
%s Imprime una cadena (char [ ])
%f Imprime un número con punto decimal (double)11

%e %E Imprime un número con punto decimal en notación


científica.
%% No se convierte en ningún argumento sólo imprime %

Tabla 4.18: Especificadores de formato de printf

Entre el carácter “ % ” y el carácter de conversión puede haber:

a) Un signo menos “-”, para indicar el ajuste a la izquierda del argumento convertido.
b) Un número que indica el ancho mínimo del campo en el que será impreso el argumento, si es
necesarios se rellenará con ceros para completar el ancho indicado.
c) Un punto, que sirve para separar el ancho del campo de la precisión.
d) Un número, la precisión, para especificar el número máximo de caracteres que serán impresos
de una cadena; o bien, el número de dígitos que se imprimirán después del punto decimal si se
trata de un flotante; o el número máximo de dígitos para un entero.

Ejemplo 4.17: Dada la siguiente declaración e inicialización de variables

int i = 90; /*Declaración de un entero*/


float x = 3.14159; /*Declaración de un flotante*/
char a = 'Z'; /*Declaración de un carácter*/
char saludo[5] = "Hola"; /*Declaración de una cadena*/

11 Si imprime un double entonces también se puede imprimir un float

Capítulo 4 | Página 30
Observa que imprime cada una de las siguientes instrucciones:
Instrucción Salida Observaciones
Se imprime el valor en decimal de la
printf("%d",i); 90
variable i.
En este caso el especificador de
conversión corresponde a un entero en
printf("%o",i); 132
octal, por lo tanto imprime el valor de
i en octal.
Imprime el valor de i en
printf("%X\n",i); 5A hexadecimal, utilizando letras
mayúsculas.
El primer especificador de formato %c
se convierte en el carácter á al cual le
printf("El car%ccter es corresponde el código ASCII 160,
El carácter es Z mientras que el segundo especificador
%c\n",160,a);
%c se convierte en el valor de la
variable a que es Z.

Imprime el valor de la variable f con


printf("%f",x); 3.141590 seis decimales (agrega un cero al final
para completar los 6 dígitos)
Imprime el valor de f en 10 espacios,
printf("%10.2f",x); 3.14 alineando a la derecha y únicamente
con dos decimales.
Imprime el valor de f en notación
printf("%.2E\n",x); 3.14E+000 científica utilizando sólo dos
decimales
Imprime el valor de s seguido de un
printf("%s Mundo\n
Hola Mundo espacio y la palabra “Mundo”, tal cual
",saludo);
aparece.
Sólo imprime dos caracteres de s
printf("%.2s Mundo\n
Ho Mundo seguido de los caracteres que
",saludo);
aparecen.
printf("%10s Mundo\n Reserva 10 espacios para imprimir el
Hola Mundo
",saludo); valor de s alineándola a la derecha.
printf("%-10s Mundo\n Igual que el anterior pero alinea la
Hola Mundo
",saludo); cadena a la izquierda.
Tabla 4.19: Ejemplos de la función printf

Capítulo 4 | Página 31
Ejemplo 4.18: En el siguiente programa se ilustra como se imprime el valor de una expresión utilizando
especificadores de formato. El programa calcula el diámetro, área y perímetro de un círculo de radio 3
y muestra los resultados con dos decimales.

/* Directivas al procesador */
#include <stdio.h> /* Funciones de entrada y salida */
#define PI 3.1415 /* Declaración de la constante PI */

/* Función Principal */
main( )
{

/*Declaración de variables*/
int radio = 3, diametro = 2*radio;

/*Instrucciones*/
printf("\Medidas de la circunferencia de radio %d\n\n",
radio);

printf("\tDiametro = %d\n\n\tPerimetro = %.2f\n\n\tArea =


%.2f\n\n\n",diametro, PI*diametro,PI*radio*radio);

system("pause");
}
Programa 4.3: circunferencia.c

El único especificador de formato del primer printf corresponde al valor de la variable radio; en el
caso del segundo printf se incluyen en la cadena de control tres especificadores: el primero para
imprimir el valor de diámetro de la circunferencia (almacenado en la variable diametro que es de
tipo int) y el segundo y tercero para imprimir dos datos de tipo float (con dos decimales)
correspondientes al perímetro y radio, cuyos valores se obtienen de la evaluación de las expresiones
PI*diametro y PI*radio*radio, respectivamente. Ambas cadenas de control incluyen
secuencias de escape para saltos de línea (\n) y tabulador (\t). La salida del programa se muestra en
la figura 4.6.

Figura 4.7: Ejecución del programa circunferencia.c

Capítulo 4 | Página 32
4.8.4 Lectura con formato

La función scanf incluida en la biblioteca stdio.h permite asignar a una o más variables, uno o más
valores (datos) recibidos desde el teclado. La sintaxis de su llamada es:

scanf(<cadena_de_control>, <argumento1>, <argumento2>,…);

En la <cadena_de_control> se debe indicar el formato de entrada de los datos que se van a leer, se
pueden incluir:

 Blancos o tabuladores, que son ignorados.


 Caracteres ordinarios (excepto %), que se espera que coincidan con el siguiente carácter que
se lee (ignorando los espacios).
 Especificadores de formato, cuyo número y tipo debe coincidir con el número y tipo de
argumentos (variable) que se le pase.

Por otro lado, <argumento> es un identificador de variables y siempre van precedidos por el operador
dirección “&” (ampersand), el cual actúa sobre un operando (normalmente una variable) y de su
evaluación se obtiene la dirección de memoria que le corresponde. Ya que, en realidad, a la
función scanf no se le pasa una lista de variables, sino las direcciones de memoria de los espacios
físicos de memoria que éstas representan.

Ejemplo 4.19: Supongamos que la dirección de memoria asignada a la variable radio es 0021AF53
(ver figura 4.5)

MEMORIA

Dirección Contenido
0021AF51
0021AF52

0021AF53 0 0 0 1 0 1 0 0 radio


0021AF6F
Figura 4.8: Ejemplo del operador &

El operador dirección (&) entra dentro de la categoría de operadores unarios y siguiendo con el ejemplo
de la evaluación de la expresión
&radio

se obtiene el valor 0021AF53, que es la dirección de memoria de la variable radio.

Capítulo 4 | Página 33
Ahora bien, los especificadores de formato que se utilizan en la función scanf se muestran en la tabla
4.20.

Especificador Acción

%d Lectura de un entero en decimal (int)

%i Lectura de un entero (int) en decimal, octal (empezando


con 0) o hexadecimal (empezando con 0x ó 0X)
%o Lectura de un entero en octal (con o sin cero inical) (int)

%x Lectura de un hexadecimal (iniciando o no con 0x ó 0X)


(int)
%u Lectura de un entero en decimal y sin signo (int)
%c Lectura de un carácter (char)
%s Lectura de una cadena (char [ ])
%e, %f, Lectura de un número con punto decimal con signo, punto
%g decimal y exponente optativos (float)

%% % o se convierte en ningún argumento sólo imprime %

Tabla 4.20: Especificadores de formato de scanf

Los caracteres de conversión d, i, u y x pueden ir precedidos por h para indicar que el dato que se lea
se almacenará en una variable de tipo short en vez de int; o por l para indicar que se almacenará en
una variable de tipo long. De igual forma, los caracteres de conversión e, f y g pueden ser precedidos
por l para indicar que se almacenarán en una variable de tipo doublé en ligar de float.

Ejemplo 4.20: Dada la siguiente declaración e inicialización de variables

int i, hh,mm; /*Declaración de un entero*/


float x,y; /*Declaración de un flotante*/
float z; /*Declaración de un flotante*/
char letra; /*Declaración de un carácter*/
char nombre[10]; /*Declaración de una cadena*/

Observa que imprime cada una de las instrucciones que se muestran en la tabla 4.21.

Capítulo 4 | Página 34
Instrucción Observaciones
scanf("%d",&i); Lee un entero y lo almacena en la variable i, por ejemplo, 567.
Lee un entero en decimal, octal o hexadecimaldo, por ejemplo,
scanf("%i",&i);
el decimal 165, el hexadecimal 0XA5 o el octal 0245
Lee dos enteros separados por “:” y los almacena en las
scanf("%d:%d",&hh,&mm);
variables hh y mm, por ejemplo, 12:30
Lee un entero representado en hexadecimal y lo almacena en
scanf("%X",&i);
la variable i, por ejemplo, A5 (16510)
Lee un número flotante y lo almacena en una variable de tipo
scanf("%lf",&z);
double, por ejemplo, 2.123456789
Lee dos flotantes separados por comas y encerrados entre
scanf("(%f,%f)",&x,&y); paréntesis, el primero lo almacena en x y el segundo en y, por
ejemplo, (3.5,4.8)
Lee una cadena –sin espacios– y la almacena en la variable
scanf("%s",nombre); nombre.12

Tabla 4.21: Ejemplos de la función scanf

Ejemplo 4.21: El siguiente programa calcula el área y perímetro de un rectángulo, dadas las medidas
de su base y altura.

/* Directivas al procesador */
#include <stdio.h>

/* Función Principal */
main( )
{
/*Declaración de variables*/
float base, altura, area = 0, perimetro = 0;

/*Instrucciones*/
printf(“\nProporciona base y altura del rectangulo separadas
por coma: ”);
scanf(“%f,%f”,&base,&altura);
perimetro = 2*base + 2*altura;
area = base*altura;
printf(“\nArea = %.2f \nPerimetro = %.2f\n\n”,area,perimetro);
system(“pause”);

}
Programa 4.4: rectangulo.c

12Observa que cuando se lee una cadena se omite el símbolo &, ya que el nombre de una cadena corresponde a un apuntador a la dirección
de memoria donde se encuentra el primer símbolo de la cadena. De esto hablaremos más en el capítulo 10.

Capítulo 4 | Página 35
A continuación se muestra la ejecución del programa para el rectángulo de base 10.5 unidades y altura
5.2 unidades.

Figura 4.8: Ejecución del programa rectangulo.c

Nota:
Cuando invoques a la función scanf no olvides escribir & antes del identificador de una variable, de
lo contrario se generará un error.

4.6 COMENTARIOS

Un comentario en programación es una línea de texto que no representa ni una instrucción ni una
directiva, es decir, se trata de texto que no forma parte del programa y por lo tanto el compilador no lo
toma en cuenta. Los comentarios nos sirven para hacer anotaciones en el código para hacerlo más
legible para otros programadores y para uno mismo. De acuerdo con Ray Campell "comentar el código
es como limpiar el cuarto de baño; nadie quiere hacerlo, pero el resultado es siempre una experiencia
más agradable para uno mismo y sus invitados".
El estándar del lenguaje C define que un comentario se delimita utilizando los símbolos /* para iniciar
el comentario y */ para finalizarlo.
/* Comentario */

Los comentarios pueden aparecer en cualquier lugar del programa donde pueda existir un espacio en
blanco, un tabulador o un salto de línea. Asimismo, un cometario puede abarcar varias líneas, siempre
y cuando estén delimitadas por éste.

Capítulo 4 | Página 36
4.7 ESTRUCTURA DE UN PROGRAMA EN C
En general un programa en lenguaje C contiene las siguientes partes:

 Datos del programa y programador (opcional)


 Bibliotecas o Directivas
 Declaración de variables y constantes globales
 Prototipo de funciones
 Definición de la función main
 Definición de funciones

Ejemplo 4.21: El programa 4.5 calcula el volumen de un ovoide. Además de la definición de la función
main se incluye la declaración y definición otra función que calcula el volumen de un ovoide. En
capítulos posteriores ahondaremos en este tema.

/* Datos del programa y programador:


Autor: Pedro Picapiedra
Nombre del Programa: ovoide.c
Descripción: El programa calcula el volumen de un ovoide.
*/

/* Bibliotecas */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* Declaraciones de variables y constantes globales*/


const float PI = 3.14159;

/* Prototipos de las funciones */


float volumen_ovoide(float menor, float mayor);

/* Función Principal */
main( )
{
/* Declaraciones de variables locales la función main*/
float dia_menor, dia_mayor, volumen;

/* Lectura de los datos de entrada*/


printf(“\t***********************************\n”);
printf(“\t* VOLUMEN DE UN OVOIDE *\n”);
printf(“\t***********************************\n”);

printf(“\n\tIngresa di%cmetro menor: ”,160);


scanf(“%f”,&dia_menor);
printf(“\n\tIngresa di%cmetro mayor: ”,160);
scanf(“%f”,&dia_mayor);

Capítulo 4 | Página 37
/*Impresión del resultado devuelto por la función
volumen_ovoide*/
volumen = volumen_ovoide(dia_menor, dia_mayor);
printf(“\n\tEl volumen del ovoide es %.2f\n\n\t”, volumen);
system(“pause”);

/* Definición de funciones */
float volumen_ovoide(float menor, float mayor)
{

/* Declaraciones de variables locales la función */


float volumen;

volumen = 4/3*PI*pow(mayor,2)*menor; /* cálculo del volumen*/

return volumen; /* se devuelve el resultado*/

}
Programa 4.5: ovoide.c

Las instrucciones que forman parte de una función están encerradas entre llaves { }; éstas pueden
clasificarse en instrucciones simples o de control. Las primeras son enunciados que corresponden a una
declaración de variable, una llamada a función o una expresión, todas ellas siempre deben terminar
con punto y coma (;); en cambio, las sentencias de control son bloques de enunciados y se relacionan
con las estructuras de control básicas (expuestas en el capítulo anterior).

El resultado de la ejecución tomando como datos de entrada 56.90 como diámetro menor y 90.32 el
diámetro mayor.

Figura 4.6: Ejecución del programa ovoide.c

Capítulo 4 | Página 38
4.8 MÁS EJEMPLOS
Ejemplo 4.22: En el ejemplo 3.13 construimos un algoritmo para un sistema de venta de boletos del
Metro. Considerando que el número de boletos no es muy alto y que la máquina sólo aceptan billetes
de $200, $100, $50 y $20 y monedas de $10, $5, $2, $1, se propone que el tipo de todas las variables
sea unsigned short.

Por otro lado, el algoritmo contiene una estructura de control condicional doble (si-entonces/if-else), la
cual se traduce en lenguaje C como if y else, respectivamente, y se encierra entre llaves {} el bloque
de instrucciones que compone cada parte, tal como se muestra en el programa 4.6. Cabe mencionar,
que en el siguiente capítulo revisaremos la codificación de las estructuras de control condicionales.

/* Simulador de una máquina dispensadora de boletos del metro */


/* Bibliotecas */
#include <stdio.h> /* Funciones de entrada y salida */
#include <stdlib.h> /*Funciones del sistema*/
#include <math.h> /* Funciones matemáticas */

/*Declaración de constantes */
#define PRECIO 3

/* Función Principal */
main( )
{
/* Declaración de variables */
unsigned short nboletos, /*Número de boletos*/
pago, /* monto del pago realizado */
cambio, /* monto del cambio */
total, /* monto total de la venta*/
billetes200, /*número de billetes de $200 */
billetes100, /*número de billetes de $100 */
billetes50, /*número de billetes $50 */
billetes20, /*número de billetes de $20 */
monedas10, /*número de monedas de $10 */
monedas5, /*número de monedas de $5 */
monedas2, /*número de monedas de $2 */
monedas1; /*número de monedas de $1 */

/*Lectura de datos de entrada */


printf("\n *** Venta de boletos del Sistema de Transporte Colectivo
METRO ***");
printf(" \n\nIntroduzca el n%cmero de boletos que desea comprar:
\t",163);
scanf("%d", &nboletos);

/* Cálculo del total de la venta*/


total = nboletos * PRECIO;

Capítulo 4 | Página 39
/* Impresión del total */
printf("\nEl total de la venta es $%d ", total );

/*Lectura del pago del usuario */


printf("\n\nDeposite su pago: ");

scanf("%d",&pago);

if(pago >= total) /*Si el pago es correcto*/


{
cambio=pago - total;
printf("\nSu cambio es: $%d ", cambio);
/* Operaciones para calcular el número de billetes y monedas
necesarios de cada denominación*/
billetes200= cambio/200;
cambio = cambio % 200;
billetes100= cambio/100;
cambio = cambio % 100;
billetes50= cambio/50;
cambio = cambio % 50;
billetes20= cambio/20;
cambio = cambio % 20;
monedas10= cambio/10;
cambio = cambio % 10;
monedas5= cambio/5;
cambio = cambio % 5;
monedas2= cambio/2;
monedas1 = cambio % 2;

/* Impresión de número de billetes y monedas */


printf("\n\nSe le entregar%Cn los siguientes billetes y
monedas:",160);
printf("\n\t%d billetes de $200", billetes200);
printf("\n\t%d billetes de $100", billetes100);
printf("\n\t%d billetes de $50", billetes50);
printf("\n\t%d billetes de $20", billetes20);
printf("\n\t%d monedas de $10", monedas10);
printf("\n\t%d monedas de $5", monedas5);
printf("\n\t%d monedas de $2", monedas2);
printf("\n\t%d monedas de $1", monedas1);
printf("\n\n\t\tVenta realizada . . .\n\n");
}

else /*pago incorrecto*/


{
printf("\nEl pago es incorrecto ");
printf("\n\n\t\tVenta NO realizada . . .");
}

printf("\n\n\t");
system("pause");
}
Programa 4.6: ventaBoletos.c
Capítulo 4 | Página 40
En la siguiente figura se ilustra la ejecución del programa cuando el usuario compra 3 boletos
pagando $200.

Figura 4.10: Ejecución del progama ventaBoletos.c

Capítulo 4 | Página 41
EJERCICIOS DEL CAPÍTULO No. 4

I. Indica cual es la diferencia que existe entre una directiva y una instrucción, y da dos ejemplos
de directivas que se utilicen en C.

II. La directiva #include, se utiliza en todo programa en C, para incluir archivos de cabecera que
permiten el uso de las funciones de las bibliotecas disponibles en el compilador. Escribe la línea
de código completa que incluye al archivo de cabecera necesario para cada caso.
a) Hacer uso de las funciones estándar para entrada y salida de datos printf y scanf.
b) Incluir en una expresión matemática la función que permite sacar la raíz cuadrada de un
número (sqrt).

III. Indica por qué no son correctos los siguientes códigos:

#include <stdio.h>
int a;
Main(){
printf("Edad:");
scanf("%d",&a);
}
a.

#include <stdio.h>
main()
int a;
printf("Edad:");
scanf("%d",&a);

b.

Capítulo 4 | Página 42
#include <stdio.h>
int a;
{
printf("Edad:");
scanf("%d",&a);
}
c.

IV. Para que un identificador en lenguaje C sea válido debe cumplir con ciertas reglas. De la
siguiente lista indica cuáles de ellos no son válidos, indicando cuál de las reglas están violando.

Identificador Válido o inválido Por Qué


menú2
int
5_y48
D1fx3
num_cta
#empleado
contador.1
NomMbRe

V. Para cada uno de los siguientes enunciados, realiza la declaración de las variables necesarias y la
codificación de la expresión involucrada (recuerda que una buena práctica de programación es
utilizar nombre mnemotécnicos para los identificadores de las variables).
a) Se desea un programa que calcule el tiempo que tarda un tren de una estación a otra, suponiendo
que el movimiento del tren se puede considerar como un movimiento rectilíneo uniforme puesto
que va a una velocidad promedio de 60 km/h. En este tipo de movimiento el tiempo resulta ser
el cociente de la distancia entre la velocidad.
b) El área de un polígono regular con n número de lados y radio r es la mitad del producto de n
por el cuadrado de r por el seno del doble de π entre n.
c) Un estudiante desea realizar un programa para saber cuánto tarda en caer una moneda hasta el
fondo de un pozo, para ello se puede basar en la fórmula de caída libre, según la cual, si
despejamos el tiempo, éste será la raíz cuadrada del cociente del doble de la distancia recorrida
entre la gravedad de la tierra.

VI. Una compañía de seguros requiere un programa que realice una serie de cálculos que involucran
un valor de porcentaje que típicamente no cambia, por ejemplo, el IVA. El programador encargado
de desarrollar el sistema debe decidir si trata a este valor como variable o constante. ¿Qué le
recomendarías y por qué?

Capítulo 4 | Página 43
VII. La escuela “Juárez” requiere un sistema de registro escolar, mediante el cual se administre la
inscripción de los estudiantes, el cupo máximo de los grupos es fijo (actualmente 25 estudiante), por
lo que el programador decidió que este dato será una constante en el programa y, además, se utilizará
varias veces a lo largo del programa y existe la posibilidad en un futuro de incrementar el cupo de
los grupos ¿Si tú fueras el programador tratarías a este dato como constante simbólica o literal?
Justifica tu respuesta.

VIII. En las siguientes expresiones agrega paréntesis para indicar la prioridad de los operadores y
determina el valor de la variable z en cada caso tomando en cuenta la siguiente declaración.

int x = 2, y = 3, w = 4, z;
a) z = -x * y;
b) z = x + y * w + 2 / x;
c) z = -x * (w + 2) – 30 / y * 100;
d) z = w - 6 / (y / x) * 4 % 2;

IV. completa la tabla con el estado de las variables al momento de irse ejecutando cada una de las
instrucciones del siguiente código, suponga como dato de entrada el valor de 5

Instrucción Entrada Estado de la variable Salida


z y x
main(){ * * *
int y=2,z,x;
printf(“x=%d”);
scanf(“%d”,&x)
z = x - y++;
printf(“z=%d”);
z = x - ++y;
printf(“y=%d =%d”,y,z);
}

Capítulo 4 | Página 44
VIII.-Codifica las siguientes expresiones matemáticas en lenguaje C tomando en cuenta la precedencia
de los operadores y usando las funciones matemáticas de la biblioteca math.h

Expresión matemática Codificación

w = 3x - 2y
ab

z = 3y+ x2 + 7
2

x = (y+4)3 – 3(w +2)

F = 9 C + 32
5
𝑇√𝑌 − 2 + 𝑅
𝑍=
𝑆

seno 𝜃
𝑚= 𝑟 + 2𝜋
coseno 𝜃

IX. Para cada una de las siguientes invocaciones, describe que acción se realizará al ejecutarse,
como se muestra en el ejemplo. Supón la siguiente declaración de variables:
int x=0, i=4, c=0, m;
char letra=’d’, C=’a’ cad[]=”hola” ;
float n,

Invocación a función Acción

Lee un valor entero del teclado y lo almacena en


scanf(“%d”,&x);
la variable x

printf(“A%d”,i); Imprime en pantalla el texto “A4”


putchar(letra)

scanf(“%d-%c”,&c,&C);

printf(“Ingresa un valor
flotante”);

scanf(“%f %d”,&n,&m);

Capítulo 4 | Página 45
printf(“%s escribe tu
nombre”,cad)

scanf(“%s”,cad);

C=getchar( );

printf(“&a&b&c&%c”,letra);

X. El algoritmo siguiente calcula la distancia entre dos puntos. Realiza su codificación en C.

Inicio
Imprime “ Ingresa el valor de (xini,xfin) (yini,yfin) "
Leer xini, xfin, yini, yfin
A←(xfin-xini)2
B← (yfin-yini)2
distancia←√𝐴 + 𝐵
Imprimir “La distancia es", distancia

Algoritmo 4.1: distanciaEntrePuntos.c

XI.- De acuerdo a los errores reportados después de compilar el archivo cilindro .c corrige el código
(ver figura 4.11). Los mensajes de error se muestran en la parte inferior de la imagen, en cada uno se
indica la línea en donde se encuentra.

Figura 4.11: Ejecución del progama cilindro.c

Capítulo 4 | Página 46
XI. Realiza la ejecución paso en papel del programa 4.7 y contesta las siguiente preguntas:
a. ¿Cuál es la salida a pantalla del programa si la entrada es 492
b. ¿Qué problema consideras que resuelve el programa?

#include <stdio.h>
main(){
int a,r;
printf("Ingresa una n%cmero de 3 cifras: ",163);
scanf("%d",&a);
r=a%10;
a=a/10;
printf("%d",r);
r=a%10;
a=a/10;
printf("%d",r);
r=a%10;
a=a/10;
printf("%d\n",r);
system("pause");
}

Programa 4.7: incognito.c

XII. Realiza el análisis, diseño e implementación de un programa en lenguaje C para cada uno
de los siguientes enunciados

c. Escribe un programa que lea una hora del día en el formato hh:mm:ss y determine cuántos
segundos han transcurrido a partir de la hora 00:00:00.

Figura 4.12: Conversión de una hora del día en segundos

Capítulo 4 | Página 47
d. Realizar un programa que lea una cantidad de grados Celsius y los convierta a grados
Fahrenheit.
e. Realiza un programa que dados los catetos opuestos determine el valor de la hipotenusa,
utilizando el teorema de Pitágoras.
f. Realiza un programa que el área de un polígono regular dados el número de lados y el radio.

Capítulo 4 | Página 48
Capítulo
Estructuras de control
selectivas
Cuando llegues a un lugar en el que el camino se divide,
síguelo.
Atribuido a Yogi Berra

OBJETIVOS:
Al finalizar este capítulo el estudiante:
o Conocerá los operadores lógicos y relacionales, su representación matemática y en lenguaje
C.
o Será capaz de evaluar expresiones booleanas.
o Construirá expresiones booleanas para representar situaciones de toma de decisiones.
o Identificará los tres tipos de estructuras de control selectivas (simple, doble y múltiple) con el
fin de utilizarlas en el diseño de soluciones de problemas que impliquen la toma de
decisiones.
o Diseñará algoritmos en pseudocódigo y diagrama de flujo utilizando correctamente las
estructuras selectivas.
o Aprenderá a implementar las estructuras selectivas en lenguaje C.

4.1 INTRODUCCIÓN

En este capítulo estudiaremos detalladamente a las estructuras selectivas o condicionales introducidas


en el Capítulo 3 (ver Tablas 3.3 y 3.4), mediante las cuales se instruye a la computadora para que
tome decisiones lógicas; por ejemplo, supongamos que necesitamos un programa para controlar los
movimientos de un robot, el cual debe seguir una línea negra; si en algún punto hay un cruce de dos
líneas, una amarilla que dobla a la derecha y otra negra que dobla a la izquierda (ver Figura 5.1), el
robot debe ser capaz de distinguir hacía dónde debe doblar (decide). Otro ejemplo, es cuando dado un
conjunto de números queremos determinar cuál de ellos es el mayor, en este caso también se deben
realizar comparaciones y tomar una decisión. Definitivamente, el uso de las estructuras selectivas es
indispensable en la solución de muchos problemas que se pueden resolver utilizando una
computadora. Lo más importante al construir estructuras selectivas es precisar la condición de cada
una de las posibles opciones, representándolas por medio de expresiones booleanas (también
llamadas expresiones lógicas).

Capítulo 5 | Página 1
El lenguaje C tiene tres estructuras selectivas: if (Si), if-else (Si-Sino) y switch-case(Seleccionar-
caso) . Sin embargo, las tres tienen la misma función que es decidir sobre el flujo que tomará el
programa de acuerdo al valor de una condición (elegir entre diversos caminos). A lo largo de este
capítulo revisaremos la semántica de las tres estructuras selectivas, presentaremos su sintaxis en
lenguaje C y desarrollaremos ejemplos. Asimismo, introduciremos la semántica y sintaxis en lenguaje
C de los operadores lógicos y relacionales necesarios para construir expresiones booleanas.

Figura 5.1 Movimientos de un robot

5.2 EXPRESIONES BOOLEANAS

Las expresiones booleanas son la base para tomar una decisión acerca de si se realiza o no un
conjunto de instrucción dependiendo de una situación particular. Se les llama expresiones booleanas
porque al ser evaluadas el resultado es un valor booleano (verdadero o falso), de esta manera el tipo
de operadores mediante las cuales se construyen son: relacionales (mayor que, menor que, mayor
igual, menor igual, igual y distinto) y lógicos (conjunción, disyunción y negación), ya que ambos se
distinguen porque devuelven un valor de verdad. Formalmente,

Definición 5.1: Una expresión booleana es una expresión matemática que devuelve un valor de
verdad, en otras palabras, es una constante o una variable booleana, dos expresiones aritméticas
combinadas con un operador relacional, o bien, una combinación de expresiones booleanas y
operadores lógicos.

Es importante destacar que el lenguaje C no tiene un tipo particular para variables booleanas, en C un
valor booleano se representa como un dato de tipo entero (int) considerando que:

 falso se representa como 0 (cero).


 verdadero se interpreta como cualquier valor diferente de 0.

Capítulo 5 | Página 2
5.2.1 Operadores relacionales
Los operadores relacionales permiten comparar dos valores, que pueden estar representados por
constantes, variables o cualquier otra expresión aritmética. Los operadores relacionales son el mayor
que, menor que, igual y las combinaciones de los anteriores, además de distinto. En la siguiente tabla
se muestra la representación matemática de los operadores relaciones y su codificación en lenguaje C.

Operadores relacionales
Representación Representación Acción
matemática en Lenguaje C
> > Mayor que
< < Menor que
≥ >= Mayor o igual
≤ <= Menor o igual
= == Igualdad
≠ != Distinto
Tabla 5.1 Operadores relacionales en lenguaje C

Error Común.
Observe que el operador de igualdad se escribe con dos símbolos de igualdad seguidos “==” El error
más común al programar es escribir una comparación con un sólo símbolo de igualdad, recuerda que
“=” en lenguaje C representa al operador de asignación.

Ejemplo 5.1: Supongamos que tenemos las siguientes asignaciones x←2, y←3, z←5, entonces:
 x > y 0 (falso)
 x != z 1 (verdadero)
 x+y == z 1 (verdadero)

5.2.2 Operadores lógicos


Los operadores lógicos manipulan únicamente expresiones booleanas, en otras palabras, sus
operandos sólo pueden ser expresiones booleanas. Los operadores lógicos definidos en el lenguaje C
son: la negación (No/NOT), la disyunción(O/OR) y la conjunción (Y/AND). 1 En la Tabla 5.2 se
muestra la representación matemática de los operadores lógicos definidos en lenguaje C.

1Cabe mencionar que basta con la negación y la conjunción (o bien, la disyunción) para definir cualquiera de los otros operadores lógicos
existentes como el X-OR, X-AND, IMPLICACIÓN, EQUIVALENCIA, etc.

Capítulo 5 | Página 3
Operadores lógicos
Operador Representación Representación Acción
matemática en lenguaje C
Conjunción (Y)  && A  B es verdadera sólo
cuando A y B son
verdaderos
Disyunción (O)  || A  B es verdadera cuando
al menos uno de los dos
operandos (A o B) es
verdadero.
Negación (NO) ~ ! ~A es verdadera cuando A
es falsa y viceversa.
Tabla 5.2 Operadores relacionales en lenguaje C

Una de las formas más comunes de presentar la semántica de los operadores relacionales son las
tablas de verdad. La tabla de verdad de un operador lógico se construye escribiendo en una tabla
todas las combinaciones de valores de verdad que pueden adoptar sus operandos, seguidas del valor
que resultaría al aplicar el operando en cada caso, cada combinación junto con su resultado es un
renglón de la tabla. Si el operando es unario, las posibles combinaciones son 2 (21); si el operador es
binario las combinaciones son 4 (22); si el operador es ternario son 8 (23); y así sucesivamente.
Asimismo, cada expresión lógica tiene asociada una tabla de verdad, y decimos que dos expresiones
lógicas son equivalentes si su tabla de verdad es la misma.

Negación (~)
El símbolo utilizado en C para representar el operador de negación (NO, en inglés NOT) es “ ! ”; su
función es invertir el valor de verdad de la expresión a la cual se le aplica. Por ejemplo, si la
expresión X es verdades entonces la negación de X (!X) es falsa. La tabla de verdad de este operador
se muestra a continuación:

Expresión Representación Representación


Matemática en C
X ~ X !X
0 1 1
1 0 0
Tabla 5.3 Tabla de verdad de la negación (NO)

Ejemplo 5.2: Sean x←2, y←3, z←5


 ~ (z > x ) se evalúa como 0 (falso), mientras que
 !(x > y) se evalúa como 1 (verdadero)

Capítulo 5 | Página 4
Conjunción (  )
La conjunción (Y, en inglés AND) es un operador binario que se evalúa como verdadero sólo cuando
las dos expresiones involucradas son verdaderas. En lenguaje C este operador se representa con
“&&” y su tabla de verdad es:

Operando1 Operando2 Representación Representación en


Matemática C
X Y X Y X && Y
0 0 0 0
0 1 0 0
1 0 0 0
1 1 1 1
Tabla 5.4 Tabla de verdad de la conjunción (Y)

Ejemplo 5.3: Suponiendo que x←2, y←3, z←5 tendríamos:


 (x>y)  (z>y) es 0 (falso) y
 !(x > y) && (z>y) es 1 (verdadero).

Disyunción (  )
El operador de disyunción (OR) también es binario, se evalúa como verdadero cuando al menos una
de las dos expresiones es verdadera, en otras palabras, sólo es falso cuando ambas expresiones son
falsas. En lenguaje C este operador se representa con “||”, su tabla de verdad es la siguiente:

Operando1 Operando2 Representación Representación


Matemática en C
X Y XY X || Y
0 0 0 0
0 1 1 1
1 0 1 1
1 1 1 1
Tabla 5.5 Tabla de verdad de la disyunción (Y)

Ejemplo 5.4: Si x←2, y←3, z←5 entonces


 (x>y)  (z>y) es 1 (verdadero),
 (x > y) || !(z>y) es 0 (falso)

Las expresiones booleanas

Como en el lenguaje C los valores booleanos se representan con números enteros, la expresión !(3+2)
se considera válida y su valor de verdad es falso, ya que 3+2 es 5 que se evalúa como verdadero y
entonces !5 es falso, sin embargo no es propiamente una condición, ni mucho menos una expresión
lógica correcta; dado lo anterior es importante remarcar que los operadores lógicos sólo pueden
aplicarse a expresiones booleanas.

Capítulo 5 | Página 5
Las expresiones booleanas se utilizan para definir pertenencia: cuando la expresión se evalúa como
verdadera significa que el elemento está dentro del conjunto descrito por la expresión, cuando es falsa
indica que el elemento no pertenece al conjunto. Veamos los siguientes ejemplos:

Ejemplo 5.5: Dada la definición de los siguientes conjuntos escribir una expresión booleana en
notación matemática que determine los defina:

a) El conjunto de todos los número pares


Sabemos que un número es par si es divisible entre 2, en otras palabras, si el residuo de la
división entre dos es cero, lo cual se puede expresar con el operador de módulo %
x mod 2 = 0

b) El conjunto de todos los número impares


La opción más natural es pensar que un número es impar si no es par, cuya expresión sería:
~ (x mod 2 = 0)

Pero también se nos puede ocurrir que un número es impar si no es divisible entre 0:
x mod 2 ! ≠ 0

O bien, pensar que el residuo de la división entre dos es uno:


x mod 2 = 1

Las tres opciones son correctas, todas ellas son expresiones equivalentes.

c) El conjunto de todos los números menores que 500 y mayores que 100
Primero, que un número sea menor que 500 se expresa como x<500. De igual forma, un
número es mayor que 100 se representa como x>100. Y para indicar que el número cumple
con las dos condiciones se usa la conjunción
x < 500  x > 100

d) Escribe una expresión que decida cuándo una calificación es aprobatoria, considerando que
esto ocurre cuando está en el rango [7-10]
En este caso podemos parafrasear el enunciado de la siguiente manera: una calificación es
aprobatoria si es mayor o igual a 7 y menor o igual a 10, lo cual se modela en lenguaje C de la
siguiente forma:

calif ≥ 7  calif ≤ 10

e) Los meses del año con 30 días (representados numéricamente, por ejemplo, enero es 1)
Los meses del año con 30 días son: abril (4), junio (6), septiembre (9) y noviembre (11); así
que la expresión debe ser verdadera cuando el número de mes sea alguno de estos, entonces
necesitamos al operador disyunción:
mes=4  mes=6  mes=9  mes=11

Capítulo 5 | Página 6
f) Todos los años que son bisiestos
Un año es bisiesto si tiene 366 días, esto ocurre con los años que son divisibles por 4, excepto
el último de cada siglo (aquel divisible por 100), salvo que este último sea divisible por 1002
Podemos desglosarla en dos casos, en ambos debe ser divisible por 4 (anho%4==0)
En el primer caso no es divisible entre 100, por tanto la expresión del caso 1 sería:
(año mod 4 = 0)  ~(año mod 100 = 0).

El segundo caso es que sea divisible entre 100 pero también entre 400:
(año mod 4 = 0)  (año mod 100 = 0)  ( año mod 400=0)

Que considerando que un número que es divisible entre 400 también lo es entre 100, en la
segunda expresión sale sobrando la de en medio:
(año mod 4 = 0)  ( año mod 400 = 0)

Entonces, la expresión final es la unión de los dos casos:


[(año mod 4 =0)  ~(año mod 100 = 0) ]  [(año mod 4 = 0)  ( año mod 400 = 0)]

La siguiente expresión es equivalente:


(año mod 4 = 0)  [(año mod 100 ≠ 0)  (año mod 400 = 0)]

g) Las ternas de números que corresponden a las longitudes de los lados de un triángulo
Sabemos que la suma de cualesquiera dos lados de un triángulo siempre es mayor que el
restante; entonces tres números (a,b,c) pueden representar la medidas de los lados de un
triángulo si cumplen esta condición. Así que tenemos que asegurarnos que esta condición se
cumpla para cualquier posible combinación, por lo tanto tenemos la siguiente expresión:
(a+b > c)  (b+c > a)  (c+a > b)

Ejemplo 5.6: Traduce las expresiones del ejercicio anterior a lenguaje C

Expresión Matemática Lenguaje C


x mod 2 = 0 x%2 == 0
~ (x mod 2 = 0) !(x%2 == 0)
x mod 2 ! ≠ 0 x%2 != 0
x mod 2 = 1 x%2 == 1
x < 500  x > 100 x<500 && x>100
calif ≥ 7  calif ≤ 10 (calif>=7) && (calif<=10)
x1≠ x2≠x3 (x1!=x2) && (x1!=x3)

2 Esta definición se tomó de la wikipedia (http://es.wikipedia.org)

Capítulo 5 | Página 7
Expresión Matemática Lenguaje C
mes=4  mes=6  mes=9  mes=11 mes==4 || mes==6 || mes==9 || mes==11

[(año mod 4 =0)  ~(año mod 100 = 0) ]  ( (anho%4==0)&&!(anho%100==0) ) ||


[(año mod 4 = 0)  ( año mod 400 = 0)] ((anho%4==0)&&( anho%400==0))
Recuerda que la “n” no es un símbolo válido en lenguaje
C
(año mod 4 = 0)  [(año mod 100 ≠ 0)  (anho%4==0) &&
(año mod 400 = 0)] ((anho%100!=0)||(anho%400==0))
(a+b > c)  (b+c > a)  (c+a > b) (a+b > c) && (b+c > a) && (c+a > b)

Evaluación de expresiones booleanas

Para determinar el valor de una expresión booleana se debe considerar, al igual que las expresiones
aritméticas, la prioridad y asociatividad de los operadores involucrados en la expresión. En la Tabla
5.63 se muestra la prioridad de los operadores booleanos.

Operadores relacionales y lógico Prioridad


! (negación) Mayor
<, >, >= , <=
==, !=
&&
|| Menor
Tabla 5.6 Prioridad de los operadores booleanos

Por lo tanto, la evaluación de la expresión !x || y && z sigue el siguiente orden: primero se evalúa la
negación de x; después la conjunción de y con z, y finalmente la disyunción de ambos resultados.
Agregando paréntesis para enfatizar la prioridad de los operadores tenemos ((!x) || (y && z)).

La asociatividad de cada uno de los operadores lógico y relacionales, excepto de la negación es de


izquierda a derecha, es decir, la expresión x && y && z se asocia como ((x && y) && z ).4 En
cambio, la expresión !!x es equivalente con !(!x).

En el caso particular del lenguaje C, cuando se evalúa una expresión de la forma A&&B primero se
evalúa la sub-expresión A, de tal manera que si A es falsa entonces determina que la expresión A&&B
es falsa sin importar el valor que tenga B (recuerda que && es verdadero cuando ambos son
verdaderos). Similarmente, si la expresión que se evalúa es de la forma A||B, primero se evalúa la
sub-expresión izquierda, si A es verdadera, entonces determina que la evaluación de A||B es
verdadera, independientemente de la evaluación de B (en el || basta con que uno sea verdadero). A
esta forma de evaluación se le denomina evaluación en cortocircuito.

3 En el capítulo 4 en la tabla 4.14 se muestra la prioridad de todos los operadores tanto aritméticos como booleanos.

4 Cabe mencionar que tanto la conjunción (&&) como la disyunción (||) son operadores asociativos, esto implica que no importa hacia que
lado se agrupen el resultado será el mismo.

Capítulo 5 | Página 8
Ejemplo 5.7: Utilizando las expresiones booleanas definidas en el Ejemplo 5.4 determina si los
siguientes elementos pertenecen al conjunto.

Pregunta Evaluación de expresión booleana (lenguaje C) Interpretación


de la evaluación
x%2 == 0
7%2 == 0 (sustituimos x por 7)
¿7 es un 7 no es un
1 == 0 (resolvemos 7%2)
número par? número par
Falso

!(x%2 == 0)
¿33 es un
!(33%2 == 0) (sustituimos x por 33)
número impar?
!(1 == 0) (resolvemos 33%2 = 0) 33 es impar
(primera
!(1) (evaluamos igualdad)
expresión)
Verdadero
¿33 es un x%2 != 0
número impar? 33%2 != 0 (sustituimos x por 33)
(segunda 1 != 0 (resolvemos 33%2 = 0)
expresión) Verdadero
x<500||x>1000
¿x=250 está en (250 < 500) || (250>1000) 250 está en el
el conjunto? 1 || 0 conjunto
Verdadero5
(calif>=7) && (calif<=10)
¿5 es una 5 no es una
(5>=7) && (7<=10)
calificación calificación
0 && 1
aprobatoria? aprobatoria
Falso6
mes==4 || mes==6 || mes==9 || mes==11
¿Julio(7) es un
7==4 || 7==6 || 7==9 || 7==11 Julio (7) no tiene
mes con 30
0 || 0 || 0 || 0 30 días
días?
Falso
(anho%4==0)&&((anho%100!=0)||(anho%400==0))
(2050%4==0)&&((2050%100!=0)||(2050%400==0))
¿2050 será un 2050 no es
(2==0)&& ((50!=0)||(50==0))
año bisiesto? bisiesto
0 && ---
Falso7
La terna (a+b > c) && (b+c > a) && (c+a > b)
(2,3,4) no son la
(2,3,4) (1+3>4) && (3+4>2) && (4+2>3)
medida de los
corresponde a 0 && ---
lados de un
los lados de un Falso8
triángulo
triángulo

5 En este caso no era necesario evaluar la subexpresión derecha, estamos haciendo evaluación en corto circuito.
6 Idem.
7 Idem
8 Idem

Capítulo 5 | Página 9
5.3 ESTRUCTURA SELECTIVA SIMPLE (Si/ if)

La estructura selectiva simple Si(if) realiza un conjunto de instrucciones siempre y cuando se cumple
una condición; por tal motivo se considera una estructura de selección única porque selecciona o pasa
por alto un conjunto de acciones, sus representaciones y sintaxis en lenguaje C se muestran en la
siguiente tabla.

Estructura Condicional Simple (Si/if)


Pseudocódigo Diagrama de Flujo

Si <condición> entonces <condición>


V F

< instruccionesV>
<instrucionesV>
Fin-Si

Lenguaje C
Código en C Descripción
Donde:
if ( <condición> ) if ……………. Es una palabra reservada del lenguaje
<instruccionesV> C para indicar el inicio de una estructura
selectiva if o if-else.9
<condición>…… Es una expresión booleana que representa
la condición que debe ser evaluada.
Siempre se debe escribir entre paréntesis.
<intruccionesV>… Instrucción que deben
ejecutarse cuando la <condición> resulte
verdadera.
Tabla 5.7 Representaciones de la estructura condicional simple (Si/If)

Notación: Los símbolos o palabras remarcadas en negritas tienen un significado especial en lenguaje
C y no pueden faltar. El texto encerrado entre menor que y mayor que (<…>) es el que debe escribir
el programador, depende del problema que se esté resolviendo.

Al conjunto de instrucciones que se ejecuta cuando la condición de una estructura condicional simple
(Si/If) se cumple se le llama cuerpo de la estructura Si (if). Cuando el cuerpo de una estructura
contiene dos o más instrucciones éstas deben encerrarse entre llaves “{<instruccionesV>}”, para
indicar el inicio ({) y fin (}) del cuerpo de la estructura.

9 La estructura if-else (en español si-sino) se estudiará a detalle más adelante.

Capítulo 5 | Página 10
Funcionamiento de la estructura condicional simple Si(if)

La manera en la que se ejecuta una instrucción Si(if) es la siguiente:


Se evalúa la <condición> (expresión booleana)
1. Si el resultado de la evaluación es verdadero
a) Se ejecutan las instrucciones señaladas por <instruccionesV> y después
b) Continúa con la ejecución de las instrucciones que aparecen al final de la
estructura if.
2. Si la evaluación de la condición es falsa termina la ejecución de la instrucción Si(If) sin
ejecutar ninguna instrucción del cuerpo, siguiendo con la instrucción que aparece después del
cuerpo de la estructura Si(If).

 Información de interés.

 Observa que la palabra “entonces” utilizada en el pseudocódigo no se traduce al


lenguaje C.

Cuando el cuerpo de una estructura selectiva o repetitiva (como veremos más adelante) tiene más de
una instrucción, éstas deben encerrase entre llaves para indicar que es un bloque de instrucciones.
Por ejemplo, las siguientes líneas de código no son iguales:

if( x%2 == 0){ if( x%2 == 0)


printf(“El número es par”); printf(“El número es par”);
x ++; x ++;
}

En el primer caso (izquierda) el incremento de x forma parte del cuerpo del if, a diferencia del
segundo caso (derecha). Esto implica que en el segundo caso el incremento se realizará
independientemente de la evaluación de la condición. Para prevenir y/o detectar este tipo de errores
(que no son de sintáxis) se recomienda dejar sangrías que resalten cuando un bloque de instrucciones
es parte de una estructura; en este sentido, en el segundo caso x++ debería estar alineado al if.

En el siguiente ejemplo se utiliza una estructura condicional doble, representada en pseudocódigo,


diagrama de flujo y lenguaje C.

Ejemplo 5.8: Los ángulos de acuerdo con su medida en grados se clasifican en:
 Ángulo Agudo: Menor de 90 grados
 Ángulo Reto: Es igual a 90 grados
 Ángulo Obtuso: Es mayor que 90 pero menor a 180 grados
 Ángulo llano: Es igual a 180 grados
 Ángulo Cóncavo: Es mayor a 180 pero menor a 360 grados

Escribe un programa que determine si un ángulo dado es agudo, recto, obtuso, llano o cóncavo.

Análisis: De acuerdo con la descripción del problema, el resultado sólo es un mensaje indicando el
tipo de ángulo, para lo cual se requiere saber cuánto mide (en grados), con ese dato el programa debe

Capítulo 5 | Página 11
decidir que mensaje es el correcto, lo cual se puede modelar utilizando if’s, como se muestra a
continuación:

Análisis del problema


Método:
Datos de Salida: Representar las
entada: Mensaje 1: “Agudo” si ángulo está en el intervalo [ 0,90) grados. condiciones
angulo Mensaje 2: “Recto” si el ángulo es igual a 90 grados. expuestas
(medida del Mensaje 3: “Obtuso” si el ángulo pertenece al intervalo (90,180) mediante
ángulo:entero) Mensaje 4: “Llano” si el ángulo es 180 grados. estructuras
Mensaje 5: “Cóncavo” si está en el intervalos (180,360). selectivas
simples.
Diseño del problema
Algoritmo(Pseudocódigo):
Inicio
Imprimir "Ingrese la medida del ángulo (grados): "
Leer angulo

Si angulo<0  angulo>360 entonces


Imprimir “Valor inválido"
Fin_Si

Si (angulo<90)  (angulo = 360) entonces


Imprimir "El ángulo es agudo"
Fin_Si

Si angulo=90 entonces
Imprimir "El ángulo es recto"
Fin_Si

Si angulo>90  angulo<180 entonces


Imprimir “El ángulo es obtuso"
Fin_Si

Si angulo =180 entonces


Imprimir "El ángulo es llano"
Fin_Si

Si angulo>180  angulo<360 entonces


Imprimir “El ángulo es cóncavo"
Fin_Si
Fin

Variables utilizadas en el algoritmo:


angulo: almacena la medida del ángulo (entero)
Algoritmo 5.1 Tipo de ángulo (Pseudocódigo)

Capítulo 5 | Página 12
Diseño del problema (Diagrama de Flujo)
Inicio

Imprimir “Ingrese la medida del


ángulo (grados):”

Leer angulo

angulo<0 
angulo>360
Imprimir ”Valor inválido”

(angulo<90)
 (angulo=360)
Imprimir ”El ángulo es agudo”

angulo=90

Imprimir ”El ángulo es recto”

angulo>90 
angulo<180
Imprimir ”El ángulo es obtuso”

angulo=180

Imprimir ”El ángulo es llano”

angulo>180 
angulo<360
Imprimir ”El ángulo es cóncavo”

Fin

Algoritmo 5.2 Tipo de ángulo (Diagrama de flujo)

Capítulo 5 | Página 13
Una vez diseñado el algoritmo el siguiente paso antes de implementarlo es realizar una o varias
pruebas de escritorio para asegurarnos que funciona. Revisemos el algoritmo con la medida del
ángulo igual a 120 grados.

Prueba de escritorio
Supongamos que ingresamos
un ángulo de 120º. Analicemos Estado de
Dato de
el estado de la variable angulo Operaciones las Dato de salida
entrada
cada vez que se ejecuta una variables
instrucción.Instrucción
angulo
Inicio - -
-
-
Ingrese la medida
Imprimir “Ingrese la medida del angulo
- - del ángulo
ángulo (grados):”. -
(grados):
Leer angulo angulo
120 -
120
-
(angulo≤0) 
(angulo>360)
(120 ≤ 0) 
Si angulo<0  angulo >360 angulo
- (angulo>360)
120
entonces… 0  (120>360) -
00
Falso10
(angulo<90) 
(angulo=360)
Si (angulo<90)  (angulo = angulo
- (120<90)  -
360)entonces … 120
(angulo=360)
Falso
(angulo = 90) angulo
Si (angulo=90) entonces … - (120 = 0)
120
-
Falso
(angulo>90 
angulo<180)
Si (angulo>90  angulo<180) (120>90 
entonces angulo<180)
(1  120<180) angulo
(1  1)
(La condición se evalúo como -
120
verdadera por lo tanto se ejecuta la Verdadero
instrucción del cuerpo) (por tanto realiza las
instrucciones del
cuerpo del Si
correspondiente)
Imprimir “El ángulo es angulo El ángulo es
- -
obtuso” 120 obtuso
(angulo = 180) angulo
Si (angulo=180) entonces … - (120 = 180)
120
-
Falso
(angulo > 180 
Si (angulo >180 AND angulo<360) angulo
- -
angulo<360) entonces… (120>180  120<360) 120
Falso
angulo
Fin - -
120
-

Tabla 5.8 Prueba de escritorio de los Algoritmos 5.1 y 5.2 (Tipo ángulo)

10 Para ahorrar tiempo realizaremos las evaluaciones de las expresiones booleanas por cortocircuito cómo lo hace C.

Capítulo 5 | Página 14
Al ejecutar paso a paso el algoritmo la única condición que satisface el estado de la memoria es la
que sombreamos (angulo>90  angulo<180), por lo tanto, la única instrucción que se toma en cuenta es la
del cuerpo del Si correspondiente.
El siguiente paso es codificar el algoritmo en lenguaje C.
Código en C de los Algoritmos 5.1 y 5.2
/* tipoAngulo.c: Programa que determina el tipo de ángulo
Ángulo Agudo: Menor de 90 grados
Ángulo Reto: Es igual a 90 grados
Ángulo Obtuso: Es mayor que 90 pero menor a 180 grados
Ángulo llano: Es igual a 180 grados
Ángulo Concavo: Es mayor a 180 pero menor a 360 grados*/

/* Bibliotecas */
#include <stdio.h>
#include <stdlib.h>

/* Función Principal*/
main ()
{ /*Inicio de la función Principal */

/* Declaración de variables */
int angulo;
/* Mensaje de bienvenida */
printf ("\nEste programa determina de que tipo es el angulo
dado.");
/* Dato de entrada */
printf ("\n\nIngrese la medida del angulo (grados): ");
scanf ("%d",&angulo);
/* Casos */
if (angulo<0 || angulo>360)
printf ("\n Valor invalido");
Observa que el cuerpo de cada
if (angulo<90 || angulo == 360) uno de los if’s sólo constan de
printf ("\n El angulo es agudo"); una instrucción por lo tanto no
es necesario utilizar llaves {}.
if (angulo==90)
printf ("\n El angulo es recto");

if (angulo>90 && angulo<180)


printf ("\nEl angulo es obtuso");

if (angulo ==180)
printf ("\n El angulo es llano");

if (angulo>180 && angulo<360)


printf ("\nEl angulo es concavo");

printf ("\n\n\t");
system ("pause");
} /*Fin de la función Principal*/
Programa 5.1 tipoAngulo.c

Capítulo 5 | Página 15
La función system(“pause”) le indica al sistema operativo que realice una pausa hasta que el usuario
presione una tecla, se encuentra definida en la biblioteca “stdlib.h”. En la Figura 5.2. se muestra la
ejecución del programa utilizando el mismo dato de entrada (120 grados).
Ejecución vs Prueba de escritorio

Figura 5.2 Ejecución del programa tipoAngulo.c

5.4 ESTRUCTURA SELECTIVA DOBLE (Si-Sino / if-else)

La estructura de selección doble Si-Sino/if-else tiene dos posibles caminos: el primero se sigue
cuando la condición se evalúa como verdadera y, el segundo en el otro caso. La diferencia entre un Si
(if) y un Si-Sino(if-else), es que el segundo siempre realiza alguna(s) instrucción(es) se cumpla o no
una condición, cuáles sean depende de la evaluación de ésta; mientras que en el Si(if) únicamente se
ejecuta un conjunto de instrucciones si la condición es válida, si es falsa no hace nada.

Estructura Condicional Doble (If-Else)


Pseudocódigo Diagrama de Flujo

Si (<condición>) entonces
<condición>
<instruccionesV> V F

sino <instrucionesV> <instrucionesF>

<instruccionesF>

Fin_Si

Tabla 5.9 (ParteI) Representaciones de la estructura condicional doble (Si-Sino / If-Else)

Capítulo 5 | Página 16
Lenguaje C
Código en C Descripción
Donde:
if ( <condición> ) if ……………. Palabra reservada del lenguaje
<instruccionesV> C para indicar el inicio de una estructura
selectiva if o if-else.
else <condición>…… Es una expresión booleana que representa
<instruccionesF> la condición que debe ser evaluada.
Siempre se debe escribir entre paréntesis.
<intruccionesV>…Conjunto de instrucciones que deben
ejecutarse cuando la <condición> resulte
verdadera.

else……………. Palabra reservada del lenguaje C para


representa la instrucción Sino
<intruccionesF>… Instrucción(es) que se ejecutarán en
caso de que la <condición> no se
cumpla
Tabla 5.9 (ParteII) Representaciones de la estructura condicional doble (SI-SINO / IF-ELSE)

Al conjunto de instrucciones que se ejecuta cuando la condición se cumple se le llama cuerpo del
Si(if) y al conjunto de instrucciones de que se ejecutan en caso contrario se le denomina cuerpo
del Sino(else). Nuevamente, si tienen más de una instrucción éstas deben encerrarse entre llaves
“{ }” para indicar que se trata de un bloque de instrucciones.

Funcionamiento de la condicional doble (Si-Sino/if-else)

El flujo de control que sigue la ejecución de una instrucción Si-Sino(if-else) es el siguiente:

1. Se evalúa la <condición> y dependiendo del resultado (verdadero o falso) se tienen los


siguientes casos:
a) Cuando la condición es verdadera se ejecuta sólo el conjunto de instrucciones
<instruccionesV>.
b) Si la evaluación de la condición resulta falsa se ejecutan directamente las instrucciones
<instruccionesF>, saltándose las instrucciones correspondientes al Si.

 Información de interés.

Tanto la palabra else como if son reservadas en lenguaje C y siempre se deben escribir con
minúsculas, de lo contrario el compilador marcará un error.

En el capítulo 3 hemos realizamos ejemplos con esta estructura (ver Ejemplo 3.2 y 3.3), sin llegar a la
etapa de la codificación, se sugiere al lector traducir los algoritmos a lenguaje C una vez que se revise
el siguiente ejemplo.

Capítulo 5 | Página 17
Ejemplo 5.9: Se requiere un programa que calcule las raíces reales para una ecuación de segundo
grado de la forma 𝑎𝑥 + 𝑏𝑥 + 𝑐 = 0.

Análisis: Uno de los métodos para calcular las raíces de una ecuación de segundo grado es la fórmula
general:
−𝑏 ± √𝑏 − 4𝑎𝑐
𝑥=
2𝑎

Los únicos datos que requerimos son los coeficientes y, las restricciones de éstos son: 𝑎 ≠ 0 (para
que la división esté definida) y 𝑏 − 4𝑎𝑐 ≥ 0 (para que tenga raíces reales).

Análisis del problema


Datos de entada:
Salida: Constantes: No hay
Coeficientes de la ecuación
Las raíces reales x1 y x2
a, b y c
Método: Restricciones:
±√ 𝑏 − 4ac ≥ 0, 𝑎 ≠ 0
𝑥=

Diseño de la solución (Pseudocódigo)


Algoritmo(Pseudocódigo):
Inicio
Imprimir “Ingresa los coeficientes a,b y c:”
Leer a, b, c
det ← 𝑏 − 4𝑎𝑐
Si (det ≥ 0  𝑎 ≠ 0) entonces

𝑥1 =


𝑥2 =
Imprimir “Las raíces son: ”, x1 , x2
Si no
Imprimir “No existen raíces reales”
Fin_Si-Sino
Fin

Variables utilizadas:
o a: coeficiente de término cuadrático ax2 (decimal).
o b: coeficiente del término lineal bx (decimal).
o c: término independiente c (decimal)
o det: almacena el valor del determinante (decimal)
o x1: primera raíz de la ecuación de segundo grado (decimal)
o x2: segunda raíz de la ecuación de segundo grado (decimal)

Algoritmo 5.3 Raíces de una ecuación de segundo grado (Pseudocódigo)

Capítulo 5 | Página 18
Nota: Observemos que en el algoritmo utilizamos una variable auxiliar det para guardar el resultado
del 𝑏 − 4𝑎𝑐, esto se sugiere porque se utiliza más adelante, de esta manera se evita calcularlo varias
veces.

En la siguiente tabla se muestra la representación del Algoritmo 5.3 en diagrama de flujo.

Diseño de la solución (Diagrama de Flujo)

Inicio

Imprimir “Ingresa los coeficientes


a,b y c:”

Leer a,b,c

det ← 𝑏 − 4𝑎𝑐

det ≥ 0 𝑎 ≠ 0

𝑥1 =
Imprimir “No existen raíces
reales”

𝑥2 =

Imprimir “Las raíces son:”,x1,x2

Fin

Variables utilizadas:
o a: coeficiente de término cuadrático ax2 (decimal).
o b: coeficiente del término lineal bx (decimal).
o c: término independiente c (decimal)
o det: almacena el valor del determinante (decimal)
o x1: primera raíz de la ecuación de segundo grado (decimal)
o x2: segunda raíz de la ecuación de segundo grado (decimal)

Algoritmo 5.4 Raíces de una ecuación de segundo grado (Diagrama de Flujo)

Capítulo 5 | Página 19
Verifiquemos que nuestro algoritmo funciona considerando la ecuación x2+5x+4=0.

Prueba de escritorio
Dada la ecuación, los datos de entrada son: a=1 b=5 c=4.
Dato de Operaciones Dato de
Instrucción Estado de las variables
entrada (ALU) salida
a b c det x1 x2
- - -
Inicio - - - - - -
Ingresa los
Imprimir “Ingresa los a b c det x1 x2
- - coeficientes
coeficientes a,b y c”. - - - - - -
a,b y c
Leer a,b,c a b c det x1 x2
1,5,4 - -
1 5 4 - - -
det ← 5 − 4 ∗ 1 ∗ 4 a b c det x1 x2
det ← 𝑏 − 4𝑎𝑐 -
det ← 9 1 5 4 9 - - -

Si (det≥0  𝑎 ≠ 0) - (9 ≥ 0  1 ≠ 0 ) a b c det x1 x2
-
entonces verdadero 1 5 4 9 - -
−5 + √9 a b c det x1 x2
−𝑏 + √det - 𝑥1 ← -
𝑥1 ← 2∗1 1 5 4 9 -1 -
2𝑎 x1 ← −1
−5 − √9 a b c det x1 x2
−𝑏 − √det - 𝑥2 ← -
𝑥2 ← 2∗1 1 5 4 9 -1 -4
2𝑎 x2 ← −4
Imprimir “Las raíces a b c det x1 x2 Las raíces
- -
son:”, x1, x2 1 5 4 9 -1 -4 son: -1,-4
a b c det x1 x2
Fin - - -
1 5 4 9 -1 -4

Tabla 5.10 Raíces de una ecuación de segundo grado (Prueba de escritorio)

En la prueba de escritorio se resaltan las instrucciones que forman el cuerpo del Si, mismas que se
ejecutan porque la condición se cumple.
El siguiente paso es codificar el algoritmo.

Código en C
/* raicesEc2Grado.c : Solución de Ecuaciones de Segundo Grado */
/*Bibliotecas */
#include <stdio.h> /*Biblioteca de entrada y salida estándar*/
#include <stdlib.h> /*Biblioteca para las funciones del sistema */
#include <math.h> /*Biblioteca para utilizar funciones
matemáticas:
pow para calcular la potencia
sqrt para calcular la raíz cuadrada */
/*Función principal*/
main ( )
{
/*Declaración de variables */
double a, b, c, x1, x2, det;

/*Mensaje de bienvenida*/
printf("El siguiente programa calcula las raices de un
polinomio de segundo grado\n");
printf("\n\t\t ax^2 + bx + c = 0");
Programa 5.2 raicesEc2Grado.c

Capítulo 5 | Página 20
/*Instrucciones*/
/*Datos de entrada*/
printf ("\n\nIntroduzca los coeficientes de la ecuación
a,b,c:\t");
scanf ("%lf,%lf,%lf",&a,&b,&c);
det = pow (b,2)-4*a*c;
/*Verificamos que la ecuación tenga raíces reales*/
if (det >= 0 && a!=0)
{
x1=(-b + sqrt(det))/2*a;
x2=(-b - sqrt(det))/2*a;
printf ("\n La raices son:s%.2lf, %.2lf",x1, x2);
}
else
printf ("\nNo existen raices reales.");

printf ("\n\n\n\t");
system ("pause");
} /*Fin de la Función Principal*/
Programa 5.2 (continuación) raicesEc2Grado.c

En la Figura 5.3 se muestra la ejecución del programa con los mismos datos de entrada utilizados en
la prueba de escritorio (Tabla 5.10):

Ejecución vs Prueba de escritorio

Figura 5.3 Ejecución del programa raicesEc2Grado.c con coeficientes válidos

En la Figura 5.4 se muestra una ejecución con los datos de entrada correspondientes a la ecuación
5x2+2x+1=0, la cual no tiene solución dado que su discriminante es igual a -16. Por tanto, el
programa realiza las instrucciones correspondientes al cuerpo del else.

Capítulo 5 | Página 21
Ejecución vs Prueba de escritorio

Figura 5.4 Ejecución del programa raicesEc2Grado.c con coeficientes inválidos

Cuando el problema consiste en escoger una de tres o más opciones, como en el Ejemplo 5.8, se
sugiere utilizar estructuras if anidadas de la forma en que se ilustra en la Figura 5.5, a menos que se
trate de posibles valores que pueda tomar una expresión, en tal caso es más conveniente utilizar una
estructura selectiva múltiple como se verá en la siguiente sección.

Diagrama de Flujo Pseudocódigo Lenguaje C

Si <condición1> entonces if (<condición1>)


Si No <instrucc1> <instrucc1>
Condición 1 sino else if <condición2>)
Si <condición2> <instrucc2>
else if <condición3>)
entonces
Instruc 1 <instrucc3>
Si No <instrucc2> else
Condición 2
sino <instrucc4>
Si <condición3>
entonces
Instruc 2 Si No <instrucc3>
Condición 3
sino
<instrucc4>
Fin Si-Sino
Instruc 3 Instruc 4
Fin Si-Sino
Fin Si-Sino

Figura 5.5 If’s anidados

Capítulo 5 | Página 22
En el siguiente ejemplo se ilustra el uso de If’s anidados.

Ejemplo 5.10 Se requiere un programa que lea la calificación de un estudiante y determine su


equivalente en letra de acuerdo con la siguiente tabla:

Calificación Numérica Calificación en letra


Mayor que o igual a 90 MB (muy bien)
Menor que 90 pero mayor que o igual a80 B (bien)
Menor que 80 pero mayor que o igual a70 S (suficiente)
Menor que 70 NA (no aprobado)

En la siguiente tabla se muestra el análisis y diseño de la solución del problema planteado.


Análisis del problema
Salida: Constantes: No hay
Datos de entada:
Mensaje correspondiente: Módulos: Sólo el módulo
La nota o calificación del
MB, B, S o NA principal.
estudiante (nota)

Diseño del problema


Algoritmo(Pseudocódigo):
Inicio
Imprimir “ Inserte una calificacion: "
Leer calif

Si (calif < 0  calif > 10) entonces


Imprimir “Calificación no válida”
Sino
Si calif≥90 entonces
Imprimir “MB”
Sino
Si (calif≥80) entonces
Imprimir “B”
Sino
Si (calif≥70) entonces
Imprimir “S”
Sino
Imprimir “NA”
Fin Si-Sino
Fin Si-Sino
Fin Si-Sino
Fin Si-Sino
Fin

Variables utilizadas:
o calif: almacena la calificación del estudiante representada por un número (entero)

Algoritmo 5.5 Calificación numérica (Pseudocódigo)

Capítulo 5 | Página 23
La representación del algoritmo en diagrama de flujo y la prueba de escritorio se dejan como ejercicio
al lector. En la siguiente tabla se muestra la codificación del algoritmo en lenguaje C.

Código en C
/* calificacionNumerica.c: Dada una calificación numérica entre 0
y 10 determina su equivalente en letra: MB (muy bien), B
(bien), S (suficiente), NA (no aprobado)*/

/*Bibliotecas*/
#include <stdio.h>
#include <stdlib.h>

/*Función principal*/
main() {

/*Declaración de variables*/
int calif;

/*Mensaje de bienvenida*/
printf("\n\tEl siguiente programa lee la calificacion numerica
de un estudiante \n\t e imprime su equivalente en
letra");

/*Instrucciones*/
printf("\n\n\tInserte una calificacion: ");
scanf("%d",&calif);
if(calif<0 || calif>10)
printf("\n\n\t\"Calificación incorrecta...\"\n");
else if(calif >= 9)
printf("\n\n\tEl estudiante tiene MB");
else if(calif >= 8)
printf("\n\n\tEl estudiante tiene B");
else if(calif >= 7)
printf("\n\n\tEl estudiante tiene S");
else
printf("\n\n\tEl estudiante tiene NA");

printf ("\n\n\t\t\t");
system ("pause");
}
Programa 5.3 calificacionNumerica.c

Observa que a diferencia entre los ejemplos 5.8 y 5.9, es que al utilizar estructuras selectivas simples
(Ejemplo 5.8) cada condición debe ser precisa e incluir todos las opciones sin poder presuponer nada
de los resultados de otro if (no están anidados); en cambio, cuando utilizamos if-anidados (Ejemplo
5.9) en cada else tenemos la certeza de que la condición del if no se cumplió por tanto, hay opciones
que se han descartado y no es necesario que aparezcan nuevamente en la condición de los siguientes
if-anidados; por ejemplo, cuando se evalúa la instrucción else if(calif >= 8)del Programa

Capítulo 5 | Página 24
5.3, es porque las condiciones de los if’s anteriores no se cumplieron, así que calif no es menor a
cero, ni mayor a 10, ni a 9, en está condición sólo se debe verificará si es mayor a 8, si fuera caso no
se ejecutarán las instrucciones del else, y por lo tanto, tampoco los siguientes if, como se muestra en
la siguiente ejecución.

Ejecución

Figura 5.6 Ejecución del programa calificacionNumerica.c con coeficientes válidos

5.4 ESTRUCTURA SELECTIVA MÚLTIPLE (Casos-Para / switch-


case)

Se denominan condiciones múltiples a las estructuras en las que se puede escoger uno de varios
caminos posibles. La estructura condicional múltiple Casos-Para (switch-case) se utiliza cuando
existen múltiples posibilidades para la evaluación de una expresión matemática (generalmente una
variable), pues de acuerdo al valor que tome la expresión será el conjunto de instrucciones que se
ejecute.

Estructura Condicional Multiple (Selecciona-Casos/Switch-Case)


Pseudocódigo y Diagrama de Flujo
Seleccionar <expresión>
caso <valor1> :
<instrucciones1>
<expresión>
caso <valor2> :
<Instrucciones2>
.
valor1 valor2 otro
.
<Instruc1> <Instruc2> <InstrucOtro>
.
otro:
<instruccionesOtro>
Fin_Seleccionar
Tabla 5.11 (Parte I) Representaciones de la estructura condicional múltiple (Selecciona-Casos / Switch-Case)

Capítulo 5 | Página 25
Estructura Condicional Multiple (Selecciona-Casos/Switch-Case)
Código en C Descripción
Donde:
switch …… Palabra reservada de C para representar la
switch ( <expresión> ) la estructura de selección múltiple
{ Selecciona
case <valor1>: <expresión>… Debe ser de tipo entero (int) o carácter
<instrucciones1>; (char)
break; case ………. Palabra reservada de lenguaje C para
case <valor2>: representar cada uno de los casos de la
<instrucciones2>; estructura de selección múltiple.
break; <valorK> … Corresponde a uno de los posibles
. valores que pueden resultar de la
. evaluación de <expresión>
. <instruccionesK> …Son las acciones que se
case <valorK>: realizarán cuando el valor de la
<instruccionesK>; expresión coincide con <valorK>.
break; break………. Palabra reservada para finalizar la
. ejecución de un conjunto de
. instrucciones.
. default ……… Palabra reservada de C, considera las
default: acciones que se llevarán a cabo cuando el
<instruccionesOtras> resultado de la evaluación de
} <expresión> no fue ninguno de los casos
contemplados.
Tabla 5.11 (Parte II) Representaciones de la estructura condicional múltiple (Selecciona-Casos / Switch-Case)

Funcionamiento de la condicional múltiple (Selecciona-Casos/Switch-Case)

El flujo de control que sigue la ejecución de una instrucción Selecciona-Casos (switch-case) es el


siguiente:
Primero se evalúa la <expresión> y dependiendo del resultado los posibles escenarios son:
a) Si el valor obtenido corresponde al valor asociado a un Caso(case), es decir a un <valorK>, se
ejecuta el bloque de instrucciones <instruccionesK> hasta encontrar el final de la instrucción
switch o hasta encontrar la instrucción break.11
b) Si el valor no corresponde a ningún case se ejecuta el bloque de instrucciones de Otro
(defaul).

 Información de interés.

La estructura de selección múltiple se puede implementar con if-anidados, en realidad bastaría con la
estructura Si-Sino para resolver problemas que impliquen toma de decisiones. Sin embargo, tener
diversas opciones nos facilita el modelado.

11Es importante escribir al final de cada case su respectivo break, de lo contrario, cuando el valor de la expresión coincida con el valor
asociado a un caso se ejecutarán las instrucciones del case correspondiente y todas las que le sigan sin importar que sean parte de otro case.

Capítulo 5 | Página 26
Ejemplo 5.11: En este ejemplo se realiza el análisis, diseño y codificación de un programa que
emplea una estructura de selección múltiple para determinar el valor de una calificación representada
por una letra.

Descripción del Se requiere realizar un algoritmo y un programa que dada una calificación
problema con letra despliegue un mensaje de acuerdo con la siguiente información;
A - Sobresaliente
B - Bien
C - Suficiente
D - Reprobado
E - Reprobado
F - Reprobado
Análisis del problema
Constantes: No hay
Datos de entada: Salida:
Módulos: Sólo el módulo
La nota o calificación del Mensaje correspondiente.
principal.
estudiante (nota)

Diseño del problema


Algoritmo(Pseudocódigo):
Inicio
Imprimir “ Inserte una calificacion: "
Leer nota
Seleccionar (nota)

caso 'A': Imprimir "Sobresaliente"

caso 'B': Imprimir "Bien"

caso 'C': Imprimir "Suficiente"

caso 'D': Imprimir "Reprobado"

caso 'E': Imprimir "Reprobado"

caso 'F': Imprimir "Reprobado"

otro: Imprimir "Esa nota es incorrecta"

Fin_Seleccionar
Fin

Variables utilizadas:
o nota: almacena la calificación del estudiante representada por una letra (caracter)

Algoritmo 5.6 Calificación letra (Pseudocódigo)

Capítulo 5 | Página 27
En la siguiente tabla se muestra la representación del algoritmo en diagrama de flujo.

Diseño del problema (Diagrama de Flujo)

Inicio

Imprimir “Inserta una


calificación:”

Leer nota

nota

A B C D E F otro caso
Imprimir Imprimir Imprimir Imprimir Imprimir
“Sobresaliente” “Bien” “Suficiente” “Reprobado” “Esa nota es
incorrecta”

Fin

Algoritmo 5.7 Calificación letra (Diagrama de Flujo)

Agrupamos los casos D, E y F porque son equivalentes. El siguiente paso es probar el algoritmo,
hagámoslo con la nota D.

Prueba de escritorio
Supongamos que ingresamos como datos de entrada nota=’D’. Analicemos la tabla del
estado de las variables.
Dato de Estado de las Dato de
Instrucción Operaciones
entrada variables salida
nota
Inicio - - -
-
nota Inserta una
Imprimir “Inserta una calificación:” - -
- calificación:
Leer nota nota
D - -
D
Casos para (nota) - - -
caso ‘A’: Imprimir “Sobresaliente” - falso -
nota
caso ‘B’: Imprimir “Bien” - falso -
D
caso ‘C’: Imprimir “Suficiente” - falso -
caso ‘D’: - verdadero Reprobado
nota
Fin - - -
-

Capítulo 5 | Página 28
La codificación del algoritmo se muestra en la siguiente tabla.
Código en C

/* Programa: calificacionLetra.c
* Descripción: Dada una calificación con letra despliega un
* mensaje
* A - Sobresaliente B - Bien
* C - Suficiente D - Reprobado
* E - Reprobado F - Reprobado
*/

/*Bibliotecas*/
#include <stdio.h>
#include <stdlib.h>

/*Función principal*/
main() {
/*Declaración de variables*/
char nota;

/*Mensaje de bienvenida*/
printf("\n\tEl siguiente programa lee una nota de un
estudiante, \n\trepresentada mediante una letra mayúscula entre A
y F, \n\ty determina que tipo de calificacion tiene.\n");

/*Entrada*/
printf("\nInserte una calificacion: ");
scanf("%c",&nota);
/*selección del caso correspondiente a la entrada*/
switch(nota)
{
case 'A': printf("\n\n\t\"Sobresaliente\"");
break;
case 'B': printf("\n\n\t\"Bien\"");
break;
case 'C': printf("\n\n\t\"Suficiente\"");
break;
case 'D': case 'E':
case 'F': printf("\n\n\t\"Reprobado\"");
break;
default: printf("\n\n\t\"Esa nota es incorrecta\"");
}
printf ("\n\n\t\t");
system ("pause");
}
Programa 5.4 calificacionLetra.c

Observa que los casos en los que la nota toma el valor de ‘D’, ‘E’ o ‘F’ sólo se escribió un printf,
encadenando los casos correspondientes y sólo escribiendo un break al final del último caso (como en
el diagrama de flujo).

Capítulo 5 | Página 29
Ejecución vs Prueba de escritorio

Figura 5.7 Ejecución del programa calificacionLetra.c

Ejemplo 5.12: Implementa un programa que brinde información a los conductores de la Ciudad de
México respecto a los programas “hoy no circula” y “verificación vehicular”.

Antes que nada precisemos la definición del problema.


Descripción del problema: Se requiere un programa que dado el último dígito de la placa
permanente de circulación del vehículo determine el color del engomado, el día que no circula y el
periodo en que debe verificarse. De acuerdo con la siguiente información.

Último Color del Primer periodo Segundo periodo Hoy no


dígito engomado de verificación de verificación circula
5y6 Amarillo Enero y Febrero Julio y Agosto Lunes
7y8 Rosa Febrero y Marzo Agosto y Martes
Septiembre
3y4 Rojo Marzo y Abril Septiembre y Miércoles
Octubre
1y2 Verde Abril y Mayo Octubre y Jueves
Noviembre
9y0 Azul Mayo y Junio Noviembre y Viernes
Diciembre

Análisis: Considerando la tabla, nos enfrentamos a un problema que implica escoger una de diversas
opciones de acuerdo con el valor de entrada, el cual es un dígito. Aunque podríamos resolverlo con
if-anidados, resulta más práctico utilizar un switch que dependa del valor del último dígito de la placa
que ingrese el usuario, cada caso corresponderá a dos dígitos y las acciones a realizar únicamente es
imprimir el color del engomado, el día que no circula y los meses en los que debe verificarse.

Capítulo 5 | Página 30
Análisis del problema
Salida:
Datos de entada: Color del engomado
Periodos de verificación Constantes: No hay
Último dígito de la placa
(digito) Día que no circula

Diseño del problema


Algoritmo(Pseudocódigo):

Inicio
Imprimir “ Inserta el último dígito de la placa del vehículo: "
Leer digito
Seleccionar digito
caso1: caso 2: Imprimir “Engomado: Amarillo”
Imprimir “ Verificación 1er semestre: Enero y Febrero”
Imprimir “Verificación 2do semestre: Julio y Agosto”
Imprimir “No circula: Lunes”

caso 3: caso 4: Imprimir “Engomado: Rosa”


Imprimir “ Verificación 1er semestre: Febrero y Marzo”
Imprimir “Verificación 2do semestre: Agosto y Septiembre”
Imprimir “No circula: Martes”

caso 5: caso 6: Imprimir “Engomado: Rojo”


Imprimir “ Verificación 1er semestre: Marzo y Abril”
Imprimir “Verificación 2do semestre: Septiembre y Octubre”
Imprimir “No circula: Miércoles”

caso 7: caso 8: Imprimir “Engomado: Verde”


Imprimir “ Verificación 1er semestre: Abril y Mayo”
Imprimir “Verificación 2do semestre: Octubre y Noviembre”
Imprimir “No circula: Jueves”

caso 0: caso 9: Imprimir “Engomado: Azul”


Imprimir “ Verificación 1er semestre: Mayo y Junio”
Imprimir “Verificación 2do semestre: Noviembre y Diciembre”
Imprimir “No circula: Viernes”

otro: Imprimir "El dato de entrada es incorrecto”


Fin_Seleccionar
Fin

Variables utilizadas:
o digito: almacena el último dígito de la placa (entero)

Algoritmo 5.8 Programas vehiculares (Pseudocódigo)

Capítulo 5 | Página 31
Verifiquemos que nuestro algoritmo funciona con la placa número 956ADL.

Prueba de escritorio
Dato de Estado de las Dato de
Instrucción Operaciones
entrada variables salida
Digito
Inicio - - -
-
Digito Inserta el
Imprimir “Inserta el último dígito:” - -
- últimodígito:
Leer digito Digito
6 - -
6
Selecciona (digito) - - -
caso 1:
- falso -
caso2:
caso 3: - falso -
caso 4: - falso -
caso 5: - falso -
caso 6: Engomado:
Rojo

Verificación
digito 1er
Imprimir “Engomado: Rojo”
6 semestre:
Marzo y Abril
Imprimir “ Verificación 1er
semestre: Marzo y Abril”
- verdadero Verificación
2do
Imprimir “Verificación 2do
semestre:
semestre: Septiembre y Octubre”
Septiembre y
Octubre
Imprimir “No circula: Miércoles”
No circula:
Miércoles

Digito
Fin - - -
6

Tabla 5.12 Prueba de escritorio del Algoritmo 5.8

Al parecer, nuestro algoritmo funciona así que sólo falta codificarlo.


Código en C
/* hoyNoCircula.c: El programa lee el último dígito de una
placa e imprime la información correspondiente a
los programas "hoy no circula" y "verificación
vehicular".*/
/*Bibliotecas*/
#include <stdio.h>
#include <stdlib.h>

/*Función principal*/
main()
{
/*Declaración de variables*/
int digito;
/*Mensaje de bienvenida*/
printf("\nEl siguiente programa lee el ultimo digito de la
placa permanente de ");
Programa 5.5 hoyNoCircula.c

Capítulo 5 | Página 32
printf("\ncirculacion del vehiculo e imprime:");
printf("\n\tColor del engomado ");
printf("\n\tDia que no circula");
printf("\n\tPeriodos en que debe verificarse");

/*Lectura del último dígito de la placa*/


printf("\n\nInserta el ultimo digito de la placa: ");
scanf("%d",&digito);
/*Determinación del caso correspondiente al último dígito*/
switch(digito)
{
case 1: case 2: printf("\n\n\tEngomado: Amarillo");
printf("\n\tVerificacion 1er semestre:
Enero y Febrero");
printf("\n\tVerificacion 2do semestre:
Julio y Agosto");
printf("\n\tNo circula: Lunes");
break;
case 3: case 4: printf("\n\tEngomado: Rosa");
printf("\n\tVerificacion 1er semestre:
Febrero y Marzo");
printf("\n\tVerificacion 2do semestre:
Agosto y Septiembre");
printf("\n\tNo circula: Martes");
break;
case 5: case 6: printf("\n\tEngomado: Rojo");
printf("\n\tVerificacion 1er semestre:
Marzo y Abril");
printf("\n\tVerificacion 2do semestre:
Septiembre y Octubre");
printf("\n\tNo circula: Miercoles");
break;
case 7: case 8: printf("\n\tEngomado: Verde");
printf("\n\tVerificacion 1er semestre:
Abril y Mayo");
printf("\n\tVerificacion 2do semestre:
Octubre y Noviembre");
printf("\n\tNo circula: Jueves");
break;
case 9: case 0: printf("\n\tEngomado: Verde");
printf("\n\tVerificacion 1er semestre:
Abril y Mayo");
printf("\n\tVerificacion 2do semestre:
Octubre y Noviembre");
printf("\n\tNo circula: Jueves");
break;
default: printf("\n\t\"Error, el dato de entrada es
incorrecto...\"");
}
printf ("\n\n\t\t");
system ("pause");
}
Programa 5.5 (continuación) hoyNoCircula.c

Capítulo 5 | Página 33
Ejecución vs Prueba de escritorio

Figura 5.8 Ejecución del hoyNoCircula.c (Programa 5.5)

Para cerrar este capítulo analicemos el siguiente problema que es una adaptación de un problema
propuestos en [Gregorio2002] (pag 23), el cual se puede resolver utilizando estructuras selectivas:

Ejemplo 5.12: Dados un tablero de ajedrez y las posiciones de dos casillas, se quiere averiguar si es
posible desplazarse de una a otra con una pieza de ajedrez12 específica. El programa leerá el tipo de
pieza y las dos posiciones, e imprimirá un mensaje indicando si el movimiento es correcto. Para ello,
debes representar las piezas utilizando los siguientes caracteres:

Pieza Carácter
Rey R
Reyna Y
Torre T
Alfil A
Caballo C
Peón P

La posición de una casilla se dará por sus coordenadas (fila, columna) en el tablero de 8x8.
La forma de moverse de cada una de las piezas se ilustra en la Figura 5.9. El carácter “●” indica las
casillas a las que puede desplazarse el Rey; el símbolo “¤” hace lo propio para el Caballo y “♦”
corresponde al Peón (considerando una sola dirección del tablero (de arriba hacia abajo)); las flechas
indican la dirección en la que pueden moverse el número de casillas que deseen: la Torre, el Alfil y la
Reyna.

12 A las piezas de ajedrez se les llama trebejos.

Capítulo 5 | Página 34
● ● ●
● ● ♦ ↑
● ● ● ← →
¤ ¤ ↓
¤ ¤

¤ ¤ ← →
¤ ¤ ↓

Figura 5.9 Movimientos de las piezas de ajedrez

Análisis: Dadas las coordenadas de dos casillas representadas por (f1,c1) y (f2,c2), debemos
verificadas que correspondan a una casilla del tablero (estén en el rango [1,8]), si esto ocurre lo
siguiente es determinar si es un movimiento válido para la pieza que el usuario indicó, para lo cual
tenemos que construir condiciones que reflejen los movimientos válidos de cada pieza.

Movimientos de la Torre: ésta se mueve hacia la izquierda o derecha que significa que el número de
fila es el mismo para las dos casillas, o bien, se mueve hacia arriba o abajo en cuyo caso comparten la
columna. La expresión adecuada es:
(𝑓1 = 𝑓2)  (𝑐1 = 𝑐2)

Movimiento del Alfil: Se desplaza en diagonal, analicemos algunos casos para tratar de identificar un
patrón. Suponiendo que está en la casilla (4,3) analicemos que sucede si se desplaza a las casillas
(2,1), (1,6), (6,5) y (5,2), indicadas por * en la Figura 5.10.

1 2 3 4 5 6 7 8
1 *
2 *
3
4
5 *
6 *
7
8

Figura 5.10 Movimientos de un alfil

Capítulo 5 | Página 35
Empecemos con la casilla (2,1), en este caso retrocede (en diagonal) 2 casillas a la izquierda,13
fijándonos en las filas retroceder 2 lugares significa restar 2 a f1 para obtener f2 (2=4-2); lo mismo
ocurre con las columnas, c2 = c1-2 (1=3-2). Para un desplazamiento de k casillas hacia atrás y a la
izquierda se cumple f2 = f1-k y c1=c2-k, despejando k tenemos k=f1-f2 y k=c1-c2, luego entonces
𝑓1 − 𝑓2 = 𝑐1 − 𝑐2

Cuando se desplaza a la casilla (1,6) retrocede tres casillas a la derecha, nuevamente el número de
filas que separan a la casilla (4,3) de la casilla (1,6) es igual al número de columnas pero la expresión
cambia, pues ahora el valor de la casilla c2 es mayor que c1, entonces la expresión es:
𝑓1 − 𝑓2 = −(𝑐1 − 𝑐2)

Si se desplaza dos casillas hasta la casilla (6,5) – adelante-derecha, el razonamiento es parecido a los
casos anteriores con la sutil diferencia que ahora las f2 y c2, son mayores que f1 y c1, así que la
primera desigualdad también nos servirá para este caso, salvo que la diferencia nos da un número
negativo (en nuestro ejemplo, 4-6=3-5=-2).

Finalmente, si el movimiento es adelante pero a la izquierda, como es el caso de la casilla (5,2) donde
el alfil solo se desplaza una casilla, también se cumple la segunda expresión, aunque otra vez el
resultado de las diferencia es un valor negativo.

Juntando todos los casos, la expresión booleana que modela los movimientos de un alfil es:
(𝑓1 − 𝑓2 = 𝑐1 − 𝑐2)  (𝑓1 − 𝑓2 = −(𝑐1 − 𝑐2))

Pero podemos olvidarnos de los signos usando el valor absoluto, denotado por | ∙ |; de esta manera la
expresión se reduce a:
( |𝑓1 − 𝑓2| = |𝑐1 − 𝑐2|)

Otra forma de verlo es con proyecciones: si proyectamos las coordenadas de las casillas sobre el eje
de las abscisas (columnas) y de las ordenadas (filas), la distancia que guardan es la misma; lo cual
corresponde a la última expresión.

Movimiento del Caballo: Los movimientos de un caballo se pueden clasificar en dos casos: se mueve
dos casillas en horizontal y una en vertical, o bien, una en horizontal y dos en vertical. El
desplazamiento horizontal implica un incremento o decremento en la columna y el vertical en la fila.
Analicemos el primer caso (dos casillas horizontales y una vertical), si se desplaza a la derecha c2
debe ser c1+2, pero si es hacia la izquierda entonces c1 será c1-2; haciendo lo propio para el
movimiento vertical tendríamos que f2=f1+1 si se desplaza hacia adelante, f2=f1-1 si es hacia atrás.
Como el movimiento del caballo implica los dos desplazamientos, cada uno con sus posibles dos
posibilidades entonces la expresión correspondiente es:

[(𝑐2 = 𝑐1 + 2 )  𝑐2 = 𝑐1 − 2)]  [(𝑓2 = 𝑓1 + 1)  𝑓2 = 𝑓1 − 1)]

Por un razonamiento análogo, la expresión correspondiente al segundo caso es la siguiente:


[(𝑐2 = 𝑐1 + 1 )  𝑐2 = 𝑐1 − 1)]  [(𝑓2 = 𝑓1 + 2)  𝑓2 = 𝑓1 − 2)]

13 Considerando que la dirección del tablero corresponde a la numeración de las casillas.

Capítulo 5 | Página 36
De esta manera, la expresión verifica si el movimiento corresponde al de un Caballo es la disyunción
de las dos expresiones anteriores:
( [(𝑐2 = 𝑐1 + 2 )  𝑐2 = 𝑐1 − 2)]  [(𝑓2 = 𝑓1 + 1)  𝑓2 = 𝑓1 − 1)] )

( [(𝑐2 = 𝑐1 + 1 ) 𝑐2 = 𝑐1 − 1)] [(𝑓2 = 𝑓1 + 2)  𝑓2 = 𝑓1 − 2)] )
 

En [Gregorio2002] toman los cuadrados del número de casillas recorridas tanto en horizontal como
verticalmente, con el fin de olvidarse de los signos, con ese análisis llegan a la siguiente expresión:
(𝑓1 − 𝑓2) ∗ (𝑓1 − 𝑓2) + (𝑐1 + 𝑐2) ∗ (𝑐1 − 𝑐2) == 5

¿Podrías explicar por qué?

Los movimientos de los trebejos que faltan se al lector.

Ahora bien, como los movimiento de cada ficha son diferente entonces hay 6 casos, uno por cada
una; ya que el tipo de ficha se representa con un carácter lo más recomendable es utilizar una
estructura selectiva múltiple (switch).14 Por otro lado, en cada caso se debe verificar si el movimiento
es válido utilizando las expresiones antes diseñadas y enviar el mensaje indicado: “Movimiento
correcto” o “Movimiento inválido”, para lo cual se presta la estructura selectiva doble (if-else) pero
para no utilizar una en cada caso sugerimos guardar el valor de la condición en una variable que se
utilice en cada caso, y al final imprimir el mensaje utilizando únicamente el valor de la variable.

En la siguiente tabla se muestra un bosquejo del algoritmo.

/*Lee los datos de entrada: pieza, … */


Seleccionar (pieza)
caso ‘T’ : mov ← (𝑓1 = 𝑓2)  (𝑐1 = 𝑐2)
caso ‘A’ : mov ← ( |𝑓1 − 𝑓2| = |𝑐1 − 𝑐2|)
.
.
.
otro: imprimir “La pieza no es correcta”
Fin-Selecciona

Si mov=1 entonces
Imprimir “ ….”
Sino
Imprimir “ … ”
Algoritmo 5.9 Movimientos de las piezas de ajedrez (bosquejo del algoritmo)

Se deja como ejercicio al lector concluir el algoritmo y codificarlo en lenguaje C.

14 Hacerlo con if´s-anidados sería muy engorroso.

Capítulo 5 | Página 37
EJERCICIOS DEL CAPÍTULO No. 5
I.- Complementa los siguientes enunciados correctamente.

a) Las estructuras _____ comparan una variable contra otro(s) valor(es), para que en base al
resultado se sigan la(s) ______ dentro de un programa.
b) Las condiciones _____ solamente ejecutan acciones cuando la condición es verdadera.
c) Las condiciones _____ ejecutan acciones cuando la condición es verdadera, pero también
cuando la condición es _____
d) El if-else es una estructura de control _________
e) El if es una estructura de control ______
f) El switch es una estructura de control ________
g) El switch solo puede utilizar una variable de tipo ____ y _____ para que el programa no
marque error.
h) En el switch el default se utiliza para:
______________________________________________________________

II.- Dada la definición de los siguientes conjuntos escribir una expresión booleana que determine
cuando un elemento (valor numérico) está en el conjunto.

Descripción Expresión booleana


El conjunto de todos los números impares.
El conjunto de todos los número impares que sean
múltiplos de 5
El conjunto de todos los número enteros que NO
estén en el intervalo (10,20)
Todos los meses de 30 días
El conjunto de todas las vocales

Capítulo 5 | Página 38
Utilizando las expresiones booleanas representadas en lenguaje C determina si los siguientes
elementos pertenecen al conjunto.

Expresión booleana Pregunta Evaluación de Resultado


Lenguaje C expresión
booleana
¿44 es un número
x%2 == 0
par?
¿10 está en el
(x<=5)||(x>10)
conjunto?
(año%4==0)
&& ¿2004 es un año
((año%100!=0)||(año%400= bisiesto?
=0))

III.- Los siguientes ejercicios tienen como objetivo fortalecer las estructuras de control (Simples,
Dobles y Múltiples).

Instrucciones: Proponer un algoritmo (pseudocódigo o diagrama de flujo) y programa que de


solución a las siguientes situaciones:

a) Prueba de divisibilidad: Lee dos números enteros y comprueba si el primer número es divisible
entre el segundo. Observe que para que se cumpla la divisibilidad, el cociente es un número
entero.
Ejemplo 4 es divisible entre 2, pero 2 no es divisible entre 4.

b) Pregunta al usuario cuántos lados tiene su figura y dependiendo de los lados, diga si es un
cuadrado, triángulo o polígono. Considere que el valor valido de la variable lados sólo pueden
ser: tres, cuatro o cinco, en caso contrario imprima el mensaje “dato incorrecto”. Además, calcule
el perímetro de la figura.

c) Dado un número entero despliegue si es positivo, negativo o cero.

d) Escribe un programa que lea una fecha en el formato Día/Mes/Año, con Día, Mes y Año números
e imprima “Día de nombre de Mes de Año”.
Ejemplo, si lee 20/11/1910 deberá imprimir “20 de noviembre de 1910”

IV.- Dados los siguientes algoritmos representados en pseudocódigos realizar el diagrama de flujo y
codifícalos en lenguaje C. Antes de codificar define que variables se utilizan y de qué tipo son.

Capítulo 5 | Página 39
Algoritmo que determina el mayor de tres números distintos
Inicio
Imprimir "Dame 3 numeros: "
Leer n1,n2,n3
Si n1=n2  n1=n3  n2=n3 entonces
Imprimir “Los números no son diferentes
Fin_si
Si n1>n2  n1>n3 entonces
Imprimir "El mayor de los números es:", n1
Fin_si
Si n2>n1  n2>n3 entonces
Imprimir "El mayor de los números es:”,n2
Fin_si
Si n3>n1  n3>n2 entonces
Imprimir "El mayor de los números es:”,n3
Fin_si
Fin
Algoritmo 5.9 Mayor de tres números (Pseudocódigo)

El siguiente algoritmo calcula el total a pagar de una compra de CD’s, considerando que hay una
promoción: precio 1 por $15.00 y 2 por $25.00.

Algoritmo que calcula el total a pagar de una compra de CD’s


Inicio
pv1cd←15; //precio por 1 cd
pv2cd←25; //precio por 2 cds
Imprimir "CD's que compro el cliente: "
Leer ncd
Si ncd≤0 v entonces
Imprimir "Número de discos inválido”
Sino
total1x15←(ncd módulo 2)*pv1cd;
total2x25←ncd/2*pv2cd;
total←total1x15 + total2x15;
Imprimir " R E S U M E N D E V E N T A S "
Imprimir " Precio de venta: 1 x”,pv1cd , " y 2 x",pv2cd
Imprimir " Compro (CD's)", ncd
Imprimir "Total Pagado 1x15 ", total1x15
Imprimir " Total Pagado 2x25 ", total2x25
Imprimir "Total Pagado por el cliente", total
Fin_Si-Sino
Fin
Algoritmo 5.10 Compra CD’s (Pseudocódigo)

Capítulo 5 | Página 40
V.- El siguiente algoritmo representado en diagrama de flujo muestra un menú para calcular la
Fuerza, Aceleración o Masa. De acuerdo con la opción elegida realiza el conjunto de instrucciones
correspondiente. Enlista las variables que se utilizan y determina de qué tipo son, después codifica el
algoritmo en lenguaje C.

Algoritmo para calcular la fuerza, masa o aceleración

Inicio

Imprimir “1. Fuerza


2. Aceleracion 3. Masa”

Pedir y Leer Resp

Resp

Caso 1 Caso 2 Caso 3 otro

Pedir y leer masa M Pedir y leer masa Pedir y leer fuerza


y aceleración A M y fuerza F F y aceleración A

A←F/M M←F/A Imprimir


F←M*A
“Opción
Invalida”

Imprimir Imprimir Imprimir


“Fuerza=”,F “Aceleracion “Masa=”,M
=”,A

Fin

Algoritmo 5.11 Menú: Fuerza, Masa y Aceleración (Diagrama de Flujo)

Capítulo 5 | Página 41
VI.- Analiza los siguientes programas y realiza las actividades planteadas para cada uno.

Programa que determina cuándo un número es par o impar


1. #include <stdio.h>
2. #include <stdlib.h>

3. main ()
4. {
5. int n1;
6. printf ("\n Dame un numero entero:");
7. scanf ("%d", &n1);

8. if (n1==0)
9. printf ("El numero es igual a cero");

10. if (n1%2==0 && n1!=0)


11. printf ("El %d es par", n1);

12. if (n1!=0)
13. printf ("El %d es impar", n1);

14. printf ("\n");


15. system ("pause");
16. }

Programa 5.6 paresImpares.c

a) Marca que instrucciones se ejecutan del Programa 5.6 cuando el dato de entrada es 14. NO
UTILICES LA COMPUTADORA.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

b) ¿Escribe cuál es la salida del programa?

SALIDA

Capítulo 5 | Página 42
Programa que determina cuándo un número es par o impar
#include <stdio.h>
#include <stdlib.h>

main ()
{
int ht=38,ph=10,he,hd;
int pt,pthe,pthn;

if (ht<=40)
{
/*Todas las horas son normales, NO se trabajaron Horas
Extras*/
pthn=ht*ph;
printf ("\n\n\tPago Total (Horas Normales): %d", pthn);
}

else
{/*Se trabajaron Horas Extras */
/*Calculo del pago de solo las horas normales
13 printf ("\n\n\tEl total de horas normales trabajadas
es:40");
14 pthn=40*ph;
15 printf ("\n\n\tPago Total (Horas Normales): %d",pthn );

16 //Calculando horas extras.


17 he=ht-40;
18 printf ("\n\n\tEl total de hrs extras trabajadas
es:%d",he);
19 pthe=he*ph*2;
20 printf ("\n\n\tPago Total (%d Horas Extras): %d",he,pthe);
21 printf ("\n\n\tPago Total (HN+ HE): %d",pthn+pthe);}

22 printf ("\n\n");
23 system ("pause");
}

PROGRAMA A SER ANALIZADO ….. 1

1 #include <stdio.h>
2 #include <stdlib.h>

3 main ()
4 {int ht=38,ph=10,he,hd;
5 int pt,pthe,pthn;

6 if (ht<=40)
7 {//Todas las horas son normales, NO se trabajaron Horas Extras

Capítulo 5 | Página 43
8 pthn=ht*ph;
9 printf ("\n\n\tPago Total (Horas Normales): %d", pthn);}

10 else
11 {//Se trabajaron Horas Extras
12 //Calculo del pago de solo las horas normales
13 printf ("\n\n\tEl total de horas normales trabajadas es:40");
14 pthn=40*ph;
15 printf ("\n\n\tPago Total (Horas Normales): %d",pthn );

16 //Calculando horas extras.


17 he=ht-40;
18 printf ("\n\n\tEl total de hrs extras trabajadas es:%d",he);
19 pthe=he*ph*2;
20 printf ("\n\n\tPago Total (%d Horas Extras): %d",he,pthe);
21 printf ("\n\n\tPago Total (HN+ HE): %d",pthn+pthe);}

22 printf ("\n\n");
23 system ("pause");
}

Programa 2.

a) Revisa el Programa 2 y señala con una x solo las instrucciones que se ejecutan de acuerdo al
programa y tu conclusión, es muy importante que NO UTILICES LA COMPUTADORA.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

¿Qué esta calculando el programa 2?


____________________________________________________________________

b) ¿Escribe cual es la salida del programa?

SALIDA

Capítulo 5 | Página 44
PROGRAMA A SER ANALIZADO ….. 2
#include <stdio.h>
#include <stdlib.h>

main ()
{int ht=50,ph=10,he,hd;
int pt,pthe,pthn;

if (ht<=40)
{//Todas las horas son normales, NO se trabajaron Horas Extras
pthn=ht*ph;
printf ("\n\n\tPago Total (Horas Normales): %d", pthn);}

else
{//Se trabajaron Horas Extras
//Calculo del pago de solo las horas normales
printf ("\n\n\tEl total de horas normales trabajadas es: 40");
pthn=40*ph;
printf ("\n\n\tPago Total (Horas Normales): %d",pthn );

//Calculando horas extras.


he=ht-40;
printf ("\n\n\tEl total de hrs extras trabajadas es: %d",he);
pthe=he*ph*2;
printf ("\n\n\tPago Total (%d Horas Extras): %d",he,pthe);
printf ("\n\n\tPago Total (HN+ HE): %d",pthn+pthe);
}
printf ("\n\n");
system ("pause");
}

Capítulo 5 | Página 45
SECCIÓN VIII. Buscando Errores.

ERRORES DE SINTAXIS

Revisa los siguientes fragmentos de código.

Código 1.
if metros<=0
printf ("\n No se puede realizar la conversion");
else
mm= metros *1000;

Escribe cual es el error y como se corrige.

Código 2.
If (metros<=0)
printf ("\n No se puede realizar la conversion");
else
mm= metros *1000;

Escribe cual es el error y como se corrige.

Código 3.
if (metros+5=0)
printf ("\n No se puede realizar la conversion");
else
mm= metros *1000;

Escribe cual es el error y como se corrige.

Código 4.
if (metros<=0);
printf ("\n No se puede realizar la conversion");
else
mm= metros *1000;

Escribe cual es el error y como se corrige.

Capítulo 5 | Página 46
ERRORES DE EJECUCIÓN.

1. Analiza el siguiente programa y su ejecución.

/*Hacer un algoritmo para ingresarle una medida en metros, y que imprima


esa medida expresada en centimetros, pulgadas, pies y millas. Los factores
de conversion son los siguientes:
1 pulgada = 25.4 mm
1 pies = 0.189 millas
1 pulgada = 0.0833 pies*/

#include <stdio.h>
#include <stdlib.h>

main ()
{
float metros,cm,pulgadas,pies,millas;
//Entrada de Datos
printf ("\nIngrese la medida en metros:");
scanf ("%f",&metros);

//Procesos o Solución del problema


if (metros<=0)
printf ("\n No se puede realizar la conversion");
else
cm = metros * 100;
pulgadas = metros * 1000 / 25.4;
pies = pulgadas * 0.0833;
millas = pies * 0.189;
// salida
printf ("\nLa medida en cm es:%.2f", cm);
printf ("\nLa medida en pulgadas es:%.2f", pulgadas);
printf ("\nLa medida en pies es:%.2f", pies);
printf ("\nLa medida en yardas es:%.2f", millas);
printf ("\n\n");
system ("pause");
}

EJECUCIÓN 1.

Capítulo 5 | Página 47
2. Como puedes observar no tiene ningún sentido imprimir las medidas cuando se ingresa un valor de
entrada igual a cero. Sin quitar ninguna línea de código, ni agregar ninguna estructura de control selectiva
adicional, como harías para que tu salida fuera como en la ejecución 2.

EJECUCIÓN 2.

3. El estudiante Bart Simpson, ha querido modificar el programa pues necesita convertir los metros a
milímetros, como se puede observar ha realizado todas las modificaciones necesarias para que su programa
funciones, el problema es que su programa ya no marca errores, pero no imprime el valor que tiene
guardado en la variable mm (milímetros). Mencione que ha olvidado hacer.

/*Hacer un algoritmo para ingresarle una medida en metros, y que imprima


esa medida expresada en centímetros, pulgadas, pies, milímetros y millas. Los
factores de conversión son los siguientes: 1 pulgada = 25.4 mm 1 pies =
0.189 millas 1 pulgada = 0.0833 pies*/

#include <stdio.h>
#include <stdlib.h>

main ()
{ float metros,cm,pulgadas,pies,millas,mm;
//Entrada de Datos
printf ("\nIngrese la medida en metros:");
scanf ("%f",&metros);
//Procesos o Solución del problema
if (metros<=0)
printf ("\n No se puede realizar la conversion");
else
mm= metros *1000;
cm = metros * 100;
pulgadas = metros * 1000 / 25.4;
pies = pulgadas * 0.0833;
millas = pies * 0.189;
// salida
printf ("\nLa medida en mm es:", mm);
printf ("\nLa medida en cm es:%.2f", cm);
printf ("\nLa medida en pulgadas es:%.2f", pulgadas);
printf ("\nLa medida en pies es:%.2f", pies);
printf ("\nLa medida en yardas es:%.2f", millas);
printf ("\n\n");
system ("pause");
}

Capítulo 5 | Página 48
¿Qué olvido?

SECCIÓN IX. CONDICIONALES DOBLES EN CADENA.

Diseñar un algoritmo y un programa que utilizando condicionales dobles en cadena o estructuras if-else
anidadas resuelva el siguiente problema:

Se necesita un sistema para un supermercado, en el cual si el monto de la compra del cliente es mayor de
$5000 se le habrá un descuento del 30%, si es menor o igual a $5000 pero mayor que $3000 será del 20%,
si no rebasa los $3000 pero si los $1000 la rebaja efectiva es del 10% y en caso de que no rebase los $1000
no tendrá beneficio.

Capítulo 5 | Página 49
Capítulo
Estructuras Repetitivas
“Si los arquitectos hiciesen edificios de la misma forma en que
los programadores escriben programas, el primer pájaro
carpintero que pasase por aquí destruiría la civilización”.

Gerald Weimberg

OBJETIVOS:
Al finalizar este capítulo el estudiante:

o Utilizará las estructuras de control repetitivas para solucionar problemas.


o Estará familiarizado con las estructuras repetitivas definidas e indefinidas.
o Identificará claramente los casos en los cuales es conveniente usar esta estructura.
o Representará en pseudocódigo o diagrama de flujo correctamente las estructuras repetitivas.
o Utilizará la sintaxis correcta para codificar en lenguaje C los problemas propuestas que
requieran el uso de instrucciones repetitivas.
o Verificará usando pruebas de escritorio, los resultados de un algoritmo que utilice estructuras
repetitivas o ciclos.

6.1 INTRODUCCIÓN
Hay situaciones en las cuales es necesario repetir una serie de acciones para lograr un resultado
específico; pensemos, por ejemplo, que llegamos al supermercado y queremos estacionarnos, lo más
común es dar vueltas por el estacionamiento mientras no encontremos un lugar; otra situación similar
es cuando deseamos comprar un boleto para el cine, tenemos que formarnos en la fila y avanzar un
lugar cada vez que un cliente delante de nosotros es atendido, este proceso lo repetimos hasta que
llegamos a la ventanilla y compramos el boleto; de igual modo, podemos pensar que la vendedora
repite la acción de vender boletos (preguntar cuántos boletos quiere y para qué película, cobrarle y
entregarle los boletos) hasta que no hay ningún cliente en la fila; otro escenario que implica repetir
acciones, es definir las tres primeras posiciones de cualquier competencia deportiva, ya que los
jueces tendrán que calificar el desarrollo de cada participante, hasta el final de la competencia.
Observemos que siempre hay una condición que controla el que se repitan o no las acciones. En
programación las estructuras repetitivas son las que nos permiten codificar estas situaciones, a las que
se conoce como ciclos o bucles, y a cada una de las repeticiones se le llama iteración.

Capítulo 6 | Página 1
El uso de una estructura repetitiva será necesario en el diseño de la mayoría de los algoritmos, así que
la mayoría de los lenguajes de programación ofrecen varias estructuras repetitivas que podemos
utilizar, el lenguaje C es uno de ellos y tiene definidas tres estructuras repetitivas diferentes: for,
while y do-while; aunque las tres son equivalentes ya que tiene el mismo sentido (repetir una serie de
acciones mientras una condición sea verdadera). A lo largo de este capítulo revisaremos las
estructuras repetitivas en general y cada uno de los tres tipos particulares introducidos en el capítulo 3
(ver Tablas 3.2 y 3.4), resaltando los casos en los cuales una u otra resulta más convenientes.

Antes de empezar a estudiar las estructuras repetitivas, revisemos algunas variables auxiliares que se
utilizan comúnmente cuando se trabaja con estructuras repetitivas.

6.2 VARIABLES AUXILIARES DE UN ALGORITMO O PROGRAMA


Para las estructuras de control repetitivas es muy importante el uso de variables auxiliares, que por la
frecuencia con la que se utilizan dentro de un algoritmo y por la función que realizan dentro del
mismo toman un nombre especial: contadores, acumuladores y banderas.

6.2.1 Contadores
Los contadores se utilizan para contabilizar el número de veces que se repite un conjunto de acciones
o eventos, generalmente forman parte de las estructuras repetitivas, pero también pueden aparecer en
conjunto de instrucciones secuenciales. Un ejemplo, del uso de contadores son las fichas de atención
entregadas a los clientes en bancos, pago de telefóno, luz, agua, etc.

Definición 6.1: Un contador es una variable de tipo entero destinada a almacenar un valor que se
irá incrementando o decrementando en una cantidad constante.

Sobre una variable contadora se realizan dos operaciones básicas:

a) inicialización
b) incremento o decremento, según sea el caso.

Inicialización: Todo contador se debe inicializar con un valor determinado (0, 1,2,...), es decir,
asignar un valor inicial a una variable

contador ← valor_inicial

Incremento o decremento: Cada vez que aparezca el evento a contar se debe incrementar o
decrementar en una cantidad fija el valor del contador.

contador ← contador + incremento;

contador ← contador - decremento;

Capítulo 6 | Página 2
Lo más común es hacer incrementos o decrementos de uno en uno, para lo cual se pueden utilizar los
operadores de incremento (++) y decremento (--) que define C para simplificar la expresión como se
muestra en la Tabla 6.1

Expresión Algoritmo Expresión Lenguaje C Expresión Simplificada


en C
contador←contador +1 contador=contador +1; contador++;
contador←contador-1 contador=contador -1; contador--;
Tabla 6.1: Incremento o decremento de contador en una unidad

Si el incremento o decremento es diferente a uno, digamos k, entonces se pueden utilizar los


operadores de incremento (+=) o decremento (-=) definidos en lenguaje C, como se ilustra en la Tabla
6.2.

Expresión Algoritmo Expresión Lenguaje C Expresión Simplificada


en C
contador←contador +k contador=contador + k; contador += k;
contador←contador-k contador=contador - k; Contador -= k;
Tabla 6.2: Incremento o decremento de un contador en k unidades

6.2.2 Acumuladores
Un acumulador, al igual que el contador incrementa o decrementa su valor sucesivamente, sin
embargo, su objetivo no es contar sino acumular sucesivamente los resultados de una misma
operación (generalmente sumas o productos). Tiene un comportamiento parecido al contador, con la
diferencia de que el incremento o decremento puede ser variable y no necesariamente es de tipo
entero. Por ejemplo, cuando hacemos compras en el supermercado y la cajera pasa cada producto por
el lector óptico, además de registrar el precio se actualiza el total de la venta, la variable que
almacena el total es un ejemplo de una variable acumulador. Formalmente,

Definición 6.2: Un acumulador es una variable que acumula cantidades sucesivas obtenidas al
realizar la misma operación.

El uso más habitual de un acumulador es para implementar series de sumas (∑) o productos ( ∏ ). Al
igual que con los contadores, para poder utilizar un acumulador hay que realizar sobre ellos dos
operaciones básicas:

Inicialización: Todo acumulador se debe inicializar con un valor determinado. Generalmente, en


el caso de obtener sumas el contador se inicializa en cero y en el caso de los productos en uno.

sumaTotal ← 0

productoTotal ← 1

Capítulo 6 | Página 3
Acumulación: que se refiere a la operación de agregar al valor actual de la variable un nuevo
valor.

Variable Acumulador Cantidad a Acumular

SumaTotal = SumaTotal + cantidad;

ProductoFinal = ProductoFinal * cantidad;

6.2.3 Bandera
En algunas ocasiones se requiere de variables que registren la ocurrencia de un suceso específico
dentro de la ejecución de un algoritmo o programa, generalmente los valores que toma son verdadero
para indicar que el suceso ocurrió o falso en el otro caso. A este tipo de variables se le llama bandera1

Definición 6.3: Una variable bandera se utiliza para registrar la ocurrencia o no de un suceso,
generalmente se utiliza 1 para recordar que ocurrió o 0 en caso contrario, sin embargo, puede tomar
otros valores dependiendo del problema; por ejemplo, puede ir variando entre valores positivos y
negativos.

También debe ser inicializada indicando que no ha ocurrido el evento, sin embargo, a diferencia de
los contadores y acumuladores, una variable bandera no se incrementa ni decrementa, sólo se
actualiza una vez que ocurrió un evento.

Inicialización: Debe ser inicializada para indicar que no ha ocurrido el evento.

bandera ← valor_no_evento

Actualización: Se actualiza la variable con el valor asociado al acontecimiento del evento.

bandera ← valor_si_evento

1 También se le conoce como interruptor, conmutador o switch.

Capítulo 6 | Página 4
6.3 DISEÑO DE CICLOS
En la mayoría de los diseños que se plantean como solución a problemas específicos, nos
encontramos con instrucciones que deben ejecutarse varias veces para un conjunto de datos
diferentes. Pensemos por ejemplo que necesitamos hacer un programa para obtener la nómina de un
negocio, para lo cual necesitaremos repetir el cálculo del salario para cada uno de los empleados e ir
acumulándola para obtener el total de la nómina, las expresiones correspondientes son:

𝑝𝑎𝑔𝑜𝐸𝑚𝑝𝑙𝑒𝑎𝑑𝑜 ← 𝑠𝑢𝑒𝑙𝑑𝑜 𝑏𝑎𝑠𝑒 + 𝑝𝑎𝑔𝑜 𝑝𝑜𝑟 ℎ𝑜𝑟𝑎𝑠 𝑒𝑥𝑡𝑟𝑎𝑠 − 𝑑𝑒𝑠𝑐𝑢𝑒𝑛𝑡𝑜𝑠

𝑛𝑜𝑚𝑖𝑛𝑎 ← 𝑛𝑜𝑚𝑖𝑛𝑎 + 𝑝𝑎𝑔𝑜𝐸𝑚𝑝𝑙𝑒𝑎𝑑𝑜

Éstas se repetirán tantas veces como número de empleado hayan, sólo que con datos correspondientes
a cada empleado. Es claro que para solucionar el problema se requiere una estructura repetitiva.

Las estructuras repetitivas en general validan una condición, dependiendo de la cual, se ejecuta o no
una serie de instrucciones, pero a diferencia de las estructuras selectivas se regresa a evaluar la
condición, de tal forma que mientras la condición sea verdadera las instrucciones se repetirán varias
veces.

Las condiciones tanto en las estructuras de decisión como en las de repetición, siempre serán
expresiones booleanas, es decir, expresiones que involucran operadores relacionales y lógicos,
recordemos que los juicios de vedad para este tipo de expresiones en C se asocian a un valor
numérico: 0 para falso y cualquier valor distinto de cero para verdadero.

Entonces ¿cómo saber cuándo debemos utilizar ciclos y qué hay que considerar para diseñar la
solución? La necesidad de utilizar un ciclo o no dependerá de nuestro análisis, si identificamos una o
varias acciones que deban realizarse para un conjunto de datos diferentes, entonces, se requiere un
ciclo, como revisamos anteriormente en el ejemplo de la nómina. Sin embargo, es importante
identificar claramente la o las instrucciones que deben quedar dentro del ciclo, así como establecer
en qué momento dejar de repetirlas.

Al diseñar ciclos debemos considerar tres aspectos fundamentales:

 Condiciones iníciales: que se refieren al estado que tienen las variables antes de iniciarse el
ciclo.
 Condición: prueba que controla las iteraciones del ciclo, siempre que se cumple la condición
el ciclo sigue iterándose.2

2 Existe una estructura repetitiva que se repiten mientras una condición no se cumple, llamada Repite-Hasta (Repeat Until) pero no tiene una
instrucción equivalente en lenguaje C, por tal motivo no la presentamos en este material.

Capítulo 6 | Página 5
 Cuerpo del ciclo: todas aquellas operaciones que se realizan en cada iteración, las cuales
siempre son las mismas y que deben incluir la actualización de las variables involucradas en
la condición del ciclo.

Al diseñar ciclos suele tenerse mayor problema con establecer las condiciones iníciales necesarias,
además de la condición. Para establecer la condición debemos tener claro que los ciclos se repiten
mientras que es verdadera, de tal forma que la condición junto el estado de las variables al inicio del
ciclo deben garantizar el número de repeticiones deseado, así que tampoco debemos olvidar que las
variables involucradas en la condición deben cambiar de estado a lo largo de las iteraciones, evitando
ciclos infinitos y así garantizar que el algoritmo termine (característica esencial de todo algoritmo
como revisamos en el capítulo 3).

En términos generales podemos considerar dos tipos de situaciones para los ciclos:

Definidos: Cuando se sabe al momento de diseñar la solución el número de veces que se deben
repetir las instrucciones. Por ejemplo, retomando el caso del cálculo de la nómina si sabemos que el
negocio tiene solo 20 empleados, entonces sabemos que hay que calcular 20 salarios.

Indefinidos: Cuando el número de iteraciones no se conoce de antemano sino hasta el momento de la


ejecución. En el caso de la nómina ahora pensemos que no sabemos cuántos empleados son, así que
el programa preguntará si se desea calcular un empleado más cada vez que termina una iteración, sólo
si el usuario responde que sí continuará el ciclo.

6.3.1 Ciclos definidos


Como se mencionó antes, una repetición o ciclo definido es aquel para el cual se sabe antes de
iniciarse el ciclo el número de veces que deben repetirse las acciones. Es claro que éstos pueden
controlarse con un contador que nos ayude a ir registrando el número de veces que se han repetido las
instrucciones. Por lo tanto, la condición comprueba que el contador ha llegado al valor final deseado,
no hay que olvidar que las estructuras repetitivas ejecutan un conjunto de instrucciones mientras la
condición es verdadera, así que la condición para que se realice otra iteración es que el contador sea
menor que el número de repeticiones deseadas.

En los siguientes ejemplos se ilustra las ventajas que tienen los ciclos.

Ejemplo 6.1: Supongamos que deseamos imprimir la letrero de “¡Advertencia!”, cinco veces en la
pantalla, figura 6.1. Una forma nada elegante de poder llevar a cabo esta tarea es imprimir
secuencialmente cinco veces el letrero (ver Algoritmo 6.1).

Capítulo 6 | Página 6
Figura 6.1. Mensaje repetitivo en pantalla.

Impresión sin ciclo del letrero ¡Advertencia!


Diagrama de Flujo: Pseudocódigo:
inicio Inicio
Imprimir “¡Advertencia!”
Imprimir “¡Advertencia!”
Imprimir “¡Advertencia!”
Imprimir “¡Advertencia!” Imprimir “¡Advertencia!”
Imprimir “¡Advertencia!”
Imprimir “¡Advertencia!”
Fin
Imprimir “¡Advertencia!”

Imprimir “¡Advertencia!”

inicio

Algoritmo 6.1. Impresión sin ciclo de un letrero

Capítulo 6 | Página 7
Si ahora el problema fuera escribir 100 veces el letrero, tendríamos que escribir varias veces el
mismo código. Así que, resulta evidente que lo mejor es usar una estructura de repetición, como
conocemos el número de veces que se debe imprimir el letrero antes de iniciado el ciclo (repetición
definida) entonces lo más adecuado es usar una variable contador (llamémosle cont) para controlarlo.
Recordemos que los contadores es necesario inicializarlos y, decrementarlos o incrementarlos cada
vez que ocurre el evento que deseamos contabilizar. Por lo tanto, el estado del contador al inicio del
ciclo es cero (cont ← 0), que representa que no se ha impreso ninguna vez el letrero; dentro del
cuerpo del ciclo tenemos la impresión del letrero y el incremento al contador (cont ← cont + 1); y
finalmente, la condición está en función del contador, como se inicializó en 0 y se quieren imprimir
100 letreros, entonces el ciclo se repite mientras cont≤99, o bien, cont<100 (ver Algoritmo 6.2).

Impresión con ciclo del letrero ¡Advertencia!


Diagrama de Flujo: Pseudocódigo:
inicio Inicio
Desde cont←0, mientras cont<100,
cont←0
cont←cont+1
Imprimir “¡Advertencia!”
cont <100
F
Fin_Desde
V Fin
Imprimir
“¡Advertencia!”

cont ← cont + 1

fin

Algoritmo 6.2. Impresión con ciclo de un letrero

Observa las condiciones iniciales y la condición están ligadas, por ejemplo, que pasa si el contador
se inicializa en 1 en lugar de en 0, si la condición fuera la misma entonces se mostrarían 99 letreros
en lugar de 100, pero si cambiamos la condición a cont≤100 entonces tendremos las 100 impresiones.
Ahora bien, si quisiéramos cambiar el número de impresiones a 200, únicamente bastaría con
modificar la condición, en cambio, en la solución expuesta en el Algoritmo 6.1, tendríamos que
escribir 200 veces la instrucción Imprimir “¡Advertencia! Aunque en este caso el problema se puede
resolver sin utilizar ciclos, hay problemas para los cuales el uso de estructuras repetitivas es
indispensable en su solución, pues ni las estructuras secuenciales ni selectivas son suficientes.

Otro ejemplo con ciclo definido pude ser la implementación de una sumatoria finita, como se muestra
en el siguiente ejemplo.

Capítulo 6 | Página 8
Ejemplo 6.2: Una sumatoria finita se define como una suma sucesiva de un conjunto finito de
números {𝑛 , 𝑛 , … , 𝑛 , 𝑛 }, es decir:
𝑆 = 𝑛 + 𝑛 +⋯+ 𝑛 +𝑛
Y se denota como sigue:

𝑆= 𝑛

En donde:
- S es el resultado final de la suma;
- t es la cantidad de valores a sumar;
- i es el índice de la suma, que varía entre k y k+t;
- k es el punto inicial de la sumatoria;
- k+t es el punto final de la sumatoria;
- ni valor de la magnitud objeto de suma en el punto i.

Como podemos ver se trata de realizar varias sumas consecutivas, para lo cual podemos utilizar una
variable acumuladora para ir generando el valor de S, además el número de sumas que se deben
realizar es conocido (t), así que se trata de ciclos definidos.

Dado que vamos a utilizar una variable acumuladora S y un contador i, se inicializan las dos
variables S←0 e i←k, la condición del ciclo será i < k+t, y en el cuerpo del ciclo tendremos la suma
del valor ni más S, además del incremento en uno del contador k (ver Algoritmo 6.3)

Algoritmo para calcular una sumatoria


Diagrama de Flujo: Pseudocódigo:
inicio
Inicio
S←0
i←k
S←0
i←k
i ≤ k+t
F Mientras i ≤ k+t
V S←S+ni
S ← S + ni
i←i+1

i←i+1
Fin_Mientras
Imprimir “La sumatoria es”, S
Fin
Imprimir S

fin

Algoritmo 6.3. Sumatoria (Σ)

Capítulo 6 | Página 9
6.3.2 Ciclos indefinidos
Cuando no sabemos el número de veces que deben repetirse las instrucciones, entonces ¿cómo
sabemos cuándo terminar el ciclo? En estos casos la condición del ciclo ya no puede estar en función
de cuántas repeticiones se han realizado, la condición deberá estar en función de si ocurre o no una
evento durante la ejecución del algoritmo y/o programa que determine el fin del ciclo.

Se tienen ciclos indefinidos cuando el número de datos que se tienen que procesar se determina
durante la ejecución del algoritmo, es decir, depende del usuario que se sigan repitiendo las acciones.
Pensemos nuevamente en el programa que calcula la cuenta en una caja de supermercado, el número
de valores a sumar dependerá del número de artículos que lleva el cliente, los cuales pueden variar de
un cliente a otro, así que no podemos saber cuántas iteraciones habrá. Algunas soluciones son: que el
usuario del programa (el cajero) vaya indicando si se desea agregar o no más artículos, o que el
cajero nos indique que no hay más artículo ingresando un número negativo (no hay artículo con
precio negativo).

Los ciclos indefinidos generalmente se controlan utilizando banderas o valores centinelas. Se llama
valor centinela a un valor distinguido que puede tomar una variable para indicar que un evento
ocurrió, por ejemplo, el precio negativo en el caso del supermercado indica que ya no se desea
ingresar más artículos. En este caso, el ciclo termina cuando se lee el valor centinela. Se debe tener
mucho cuidado al escoger un valor centinela ya que no puede ser un valor que sea un dato válido.

Ejemplo 6.2: Resolvamos el ejemplo de la cuenta de supermercado utilizando una bandera, el ciclo
se repetirá mientras que el usuario indique con una ‘s’ (sí) que quiere seguir ingresando artículos,
cuando ya no haya más artículos que ingresa el usuario debe indicarlo con una ‘n’ (no).

Descripción del problema: Se requiere un programa que calcule la cuenta a pagar por un
cliente en un supermercado.
Análisis del problema

En este caso tenemos un ciclo indefinido controlado por una variable bandera, las
variables involucradas son total (acumulador) y continuar (bandera), total se inicializa en
cero (total←0) y el valor adecuado para la variable continuar es ‘s’ (continuar←‘s’), esto
con para asegurarnos que entre una vez al ciclo, ya que la condición es que se repita
mientras continuar ≠ ‘n’. Dentro del cuerpo del ciclo tendremos que incluir la petición del
precio de cada artículo y la operación de acumulación (total←total+precio), además de
preguntar al usuario si se desea ingresar más artículos.

Datos de entada: Salida: Método:


precio: Precio de los total: Total a pagar (Acumular precios)
artículos que lleva. total = total + precio
continuar: Variable
centinela para registrar si
hay o no más artículos.

Capítulo 6 | Página 10
Algoritmo (Pseudocódigo)
Inicio
total ← 0
continuar ← ‘s’
Mientras continuar ≠ ‘n’ hacer
Imprimir “Precio del artículo”
Leer precio
total ← total + precio
Imprimir “¿Deseas ingresar otro artículo (s:si n:no)?”
Leer continuar
Fin Mientras
Imprimir “Total a pagar:”, total
Fin

Algoritmo 6.4. Cajero supermercado (versión 1: variable centinela)

Ejemplo 6.3: Ahora veamos la solución considerando que los artículos terminan cuando se ingresa
un valor negativo, digamos -1 (valor centinela).

Descripción del problema: Se requiere un programa que calcule la cuenta a pagar por un
cliente en un supermercado.
Análisis del problema

También se trata de un ciclo indefinido, las condiciones iníciales para el ciclo dado que
se trata de una operación de acumulación son: la inicialización en cero del acumulador.
Dentro del cuerpo del ciclo tendremos que incluir la petición del precio de cada artículo y
la operación de acumulación (total←total+precio). La condición será precio≥0.

Datos de entada: Salida: Método:


precio: Precio de los total: Total a pagar (Acumular precios)
artículos que lleva. total = total + precio
Algoritmo (Pseudocódigo)
Inicio
total ← 0
Hacer
Imprimir “Precio del artículo”
Leer precio
total ← total + precio
Mientras precio ≥ 0,
Fin Hacer-Mientras
Imprimir “Total a pagar:”, total
Fin

Algoritmo 6.5. Cajero supermercado (versión 2: valor centinela)

Capítulo 6 | Página 11
Observa que en este caso estamos utilizando un variable y dos instrucciones menos, dado que
estamos aprovechando el valor de la variable de entrada para controlar el ciclo. Otra diferencia es que
en este caso estamos validando la condición al final, lo que hace que se ejecute al menos una vez el
ciclo.

En los ejemplos incluidos hasta el momento hemos utilizado tres tipos de estructuras de repetición
diferente: Desde, Mientras y Hacer-mientras; cada una de ellas puede ser implementada en el
lenguaje C, mediante las sentencias de control while, for y do-while, respectivamente. En las
siguientes secciones revisaremos a detalle la semántica y sintaxis de cada una de ellas.

6.4 ESTRUCTURA REPETITIVA DESDE (FOR)


La estructura repetitiva Desde (for) es conveniente utilizarla cuando conocemos el número de veces
que se debe repetir el ciclo (ciclos definidos), ya que controla la inicialización e incremento de las
variables que controlan el ciclo, lo cual resulta muy conveniente para manejar los detalles del
contador. En la siguiente tabla se muestran sus representaciones en pseudocódigo y diagrama de
flujo, además de que se introduce su sintaxis en lenguaje C.

Estructura de Repetición Desde (for)


Pseudocódigo Diagrama de Flujo

<Inicialización>

Desde <inicialización> mientras<condición>,


<incremento/decremento>
<condición> F
<instrucciones>
V
Fin_Desde <instrucciones>

<incr/dec>

Lenguaje C
Código en C

for (<inicialización>; <condición>; <inc/dec>)


<instrucciones>

Capítulo 6 | Página 12
Descripción
Donde:
for ……………. Es una palabra reservada del lenguaje C que indica el inicio de una
estructura repetitiva for.
<incialización>… Inicialización del contador del ciclo.
<condición>……. Es una expresión booleana que representa la condición que debe
cumplirse para que ejecute el ciclo.
<inc/dec>……….. Operación de incremento o decremento del contador.
<intruccionesV>… Instrucción(es) que debe(n) ejecutarse cuando la <condición> sea
Verdadera, se le denomina cuerpo del for.
Los paréntesis y puntos y comas indicados son obligatorios. Si el cuerpo del for tiene
más de una instrucción, éstas deben de ir encerradas entre llaves ya que se trata de un
bloque de instrucciones.
Tabla 6.3: Representación de la estructura Desde (for).

Funcionamiento de la estructura repetitiva Desde (for)

La manera en la que se ejecuta una instrucción Desde (for) es la siguiente:

1. La <inicialización> sólo se realiza una vez al inicio del ciclo, antes de cualquier iteración del
conjunto de instrucciones que forman el cuerpo del for;
2. Se evalúa la <condición> , si es verdadera se ejecutan las <instrucciones> del cuerpo del for, es
decir, se realiza una iteración;
3. Después se hace el incremento o decremento especificado en la tercera expresión dentro del
paréntesis del for.3
4. Una vez hecho el incremento o decremento se regresa a la evaluación de la condición, si la
condición es verdadera se inicia otra iteración. Este proceso se repite hasta que la condición es
falsa.

Ejemplo 6.4: Codifiquemos el Algoritmo 6.1.a mediante una estructura for.

3 En el caso de los operadores de incremento o decremento,++ y --, debemos tener cuidado donde aparecen: si están después de la variable
(por ejemplo, x++ ó x--), la ejecución de las instrucciones del for es como se plantea, pero si aparecen antes de la variable (es decir, ++x ó --
x) entonces primero se realiza el incremento o decremento y después las instrucciones del cuerpo del for.

Capítulo 6 | Página 13
Codificación del algoritmo con ciclos que imprime 100 veces el
letrero ¡Advertencia!
/*advertencia.c: programa que imprime 100 veces el mensaje
de ¡Advetencia */
#include <stdio.h>
#include <stdlib.h>
/*función principal*/
main()
{
int cont;
/* Ciclo for 100 iteraciones */
for (cont=0;cont<100;cont++)
/*Instrucciones del ciclo*/
printf(“\n¡Advertencia!”);

printf (“\n”);
system (“pause”);
}
Programa 6.1: Advertencia.c

Recuerda que las llaves que marcan el inicio y fin del ciclo delimitan el cuerpo del ciclo y son
indispensables cuando hay más de una instrucción y pueden omitirse cuando sólo hay una, como en
este caso. También es importante recalcar que la estructura for no lleva punto y coma después de los
paréntesis, pues indicarían el fin de la instrucción e implicaría que el cuerpo del for no incluye
ninguna instrucción.

 Información de interés.

- Si deseas comprobar que efectivamente envía 100 mensajes contándolos de 0 a 99, entonces puedes
modificar el programa en c, cambiando el mensaje de Advertencia de la siguiente forma:

printf("\n %d ....¡Advertencia!",cont);

Con esta instrucción se despliega el valor del contador.

Ejemplo 6.5: Ahora resolvamos el problema de calcular la nómina de trabajadores, considerando que
el usuario del programa conoce el número de trabajadores.

Descripción del problema: Se requiere un programa, que determine la nómina de la empresa “El
patito”, el sueldo a pagar de cada trabajador se obtiene de la siguiente forma:

𝑝𝑎𝑔𝑜 ← 𝑠𝑢𝑒𝑙𝑑𝑜 𝑏𝑎𝑠𝑒 + 𝑝𝑎𝑔𝑜 𝑝𝑜𝑟 ℎ𝑜𝑟𝑎𝑠 𝑒𝑥𝑡𝑟𝑎𝑠 − 𝑑𝑒𝑠𝑐𝑢𝑒𝑛𝑡𝑜𝑠


Se debe imprimir en pantalla el total a pagar para cada trabajador y al final el total de la nómina.

Capítulo 6 | Página 14
Análisis: Los valores que se muestren a la salida deben ser: el sueldo de cada trabajador (s) y el total
de la nómina (total_nom), que es igual a la suma de todos los sueldos.

De acuerdo con la fórmula para calcular el pago de un empleado, necesitamos conocer: el sueldo base
(sb), el pago por horas extras (phe) y el total de descuentos (d); estos datos deben ser proporcionados
por el usuario del programa. Como es la misma operación que debemos realizar para todos los
trabajadores entonces lo mejor es utilizar un ciclo, si pedimos antes de iniciar el ciclo el valor del
número de trabajadores (N) entonces podemos pensar en un ciclo definido, para el cual usaremos el
contador c para controlarlo.

El cálculo de la nómina total se puede realizar con una operación de acumulación, utilizando a
total_nom como acumulador, al cual vamos a ir sumando los sueldos s que se vayan calculando en
cada iteración.

De esta modo, las condiciones iníciales para el ciclo son la inicialización de total_nom en cero, así
como la inicialización del contador en este caso en 1, además conocer el valor de N (es decir, que
antes del ciclo se debe leer N). El cuerpo del ciclo tiene el cálculo y la acumulación del sueldo de
cada empleado, además del incremento en uno del contador. Y la condición estará dada por c≤N.
En la siguiente tabla se resumen los datos obtenidos del análisis, además del algoritmo.

Análisis del problema


Datos de entada: Salida: Método:
N: número de Trabajadores s: salario del trabajador Utilizar un ciclo para calcular el sueldo
(entero) (decimal) de cada trabajador, e ir acumulando el
sb: sueldo base (decimal) total_nom : Total de la total de la nómina. El ciclo se repite N
phe: pago por horas extras nomina (decimal) veces.
(decimal) (Calculo del salario)
d: descuentos (decimal) s ← sb + phe-d
(Acumular el salario)
total_nom←total_nom+s

Capítulo 6 | Página 15
Algoritmo (Pseudocódigo)

Inicio
Imprimir “Ingresa el total de empleados”
Leer N
Desde c←1, total_nom←0, mientras (c≤N), c←c+1
Imprimir “Salario base del empleado ”,c
Leer sb
Imprimir “Pago por horas extras del empleado ”,c
Leer phe
Imprimir “Descuento para el empleado ”,c
Leer d
s ← sb + phe - d
Imprimir “Pago empleado”,c “:” s
total_nom←total_nom + s
Fin_Desde
Imprimir “Nomina total:”,total_nom
Fin

Algoritmo 6.6. Cálculo nómina (pseudocódigo)

Antes de hacer una prueba de escritorio para verificar que el algoritmo funciona, primero daremos su
representación en diagrama de flujo en el Algoritmo 6.7. Observa que la inicialización de las
variables queda fuera del ciclo marcado con la flecha que regresa después del incremento hasta la
condición, a diferencia de la representación del pseudocódigo dónde aparece dentro de la instrucción
Desde, aunque también se ejecuta una sólo vez antes de la primera iteración del ciclo.

Capítulo 6 | Página 16
Diseño (Diagrama de Flujo)
inicio

imprimir “Ingresa el total de empleados”

leer N

c←1
total_nom←0

c≤N
F

V
imprimir “Salario base del empleado”,c

leer sb

imprimir “Pago por horas extras del


empleado”, c

leer phe

imprimir “Descuento para el empleado”,c

leer d

s ← sb +phe -d

imprimir “Pago empleado”,c “:” s

total_nom ← total_nom +s

c ←c+ 1

imprimir “Nomina total:” total_nom

fin
Algoritmo 6.7. Cálculo nómina (Diagrama de Flujo)

Capítulo 6 | Página 17
Ahora ejecutemos el algoritmo suponiendo que el número de empleados es 3 y sus datos son:
Datos empleado 1: sueldo base 100, pago horas extra 50, descuentos 0.
Datos empleado 2: sueldo base 150, pago horas extra 100, descuentos 5.
Datos empleado 3: sueldo base 200, pago horas extra 0, descuentos 10.
Prueba de escritorio
Datos de
Instrucción Estado de las variables locales Salida a monitor
entrada
N c total_nom s sb phe d
Inicio - -
- - - - - - -
Imprimir “Ingrese el total de
Ingrese el total de
empleados” - No cambia empleados

Leer N N c total_nom s sb phe d -


3
3 - - - - - -

total_nom←0 N c total_nom s sb phe d


Inicia ciclo

- - 0 - - - -

c←1 N c total_nom s sb phe d


- 3 0 -
1 - - - -

¿c ≤ N ?
¿1≤ 3? Verdadero, entra al
ciclo - No cambia -

1 ≤ 3? verdadero
imprime “Salario base del Salario base
empleado”,c - No cambia empleado 1

leer sb N c total_nom s sb phe d


100
3 1 0 - 100 - -

imprimir “Pago por horas Pago por horas


extras para el empleado”,c No cambia extras para el
empleado 1

leer phe N c total_nom s sb phe d


50
3 1 0 - 100 50 -
Iteración 1

imprimir “Descuento ras


Descuento para el
para el empleado”,c No cambia empleado 1

leer d N c total_nom s sb phe d


0
3 1 0 - 100 50 0

s ← sb +phe - d N c total_nom s sb phe d


-
s ← 100 +50 - 0 3 1 0 150 100 50 0

imprimir “Pago
Pago empleado 1:
empleado”,c “:” s No cambia 150

N c total_nom s sb phe d
total_nom ← total_nom + s
total_nom ← 0 +150 3 1 150 150 100 50 0

c ←c+ 1 N c total_nom s sb phe d


3 2 150 150 100 50 0

Tabla 6.4. Prueba de escritorio del algoritmo cálculo nómina

Capítulo 6 | Página 18
¿c ≤ N ?
- No cambia -
¿2≤ 3? Verdadero
imprime “Salario base del Salario base
- No cambia
empleado”,c empleado 2
leer sb N c total_nom s sb phe d
150
3 2 150 150 150 50 0
imprimir “Pago por horas Pago por horas
extras para el empleado”,c No cambia extras para el
empleado 2
leer phe N c total_nom s sb phe d
100
3 2 150 150 150 100 0
teración 2

imprimir “Descuento para el Descuento para el


empleado”,c No cambia empleado 2
leer d N c total_nom s sb phe d
5
3 2 150 150 150 100 5
s ← sb +phe – d N c total_nom s sb phe d
-
s ← 150 +100 – 5 3 2 150 245 150 100 5
imprimir “Pago empleado”,c Pago empleado 1:
“:” s No cambia 245
total_nom ← total_nom +s N c total_nom S sb phe d
total_nom ← 150+245 3 2 395 245 150 100 0
c ←c+ 1 N c total_nom s sb phe d
3 3 395 245 150 100 5
¿c ≤ N ?
- No cambia -
¿3≤ 3? Verdadero
imprime “Salario base del Salario base
- No cambia
empleado”,c empleado 3
leer sb N c total_nom s sb phe d
200
3 3 395 245 200 100 0
imprimir “Pago por horas Pago por horas
extras para el empleado”,c No cambia extras para el
empleado 3
leer phe N c total_nom s sb phe d
0
3 3 395 245 200 0 0
Iteración 3

imprimir “Descuento ras para Descuento para el


el empleado”,c No cambia empleado 3
leer d N c total_nom s sb phe d
5
3 3 395 245 200 0 10
s ← sb +phe - d N c total_nom s sb phe d
-
s ← 200 + 0 - 10 3 3 395 190 200 0 10
imprimir “Pago empleado”,c Pago empleado 3:
“:” s No cambia 190

total_nom ← total_nom +s N c total_nom S sb phe d


total_nom ← 395+190 3 2 585 245 150 100 0
c ←c+ 1 N c total_nom s sb phe d
3 4 585 245 150 100 5
¿c ≤ N ?
mina
Ciclo
Ter-

¿4≤ 3? Verdadero - No cambia -

imprimir “Nómina total”,total_nom - Nómina total: 585


No cambia
fin

Tabla 6.5. Prueba de escritorio del algoritmo cálculo nómina (continuación)

Capítulo 6 | Página 19
Antes de codificar el algoritmo, analicemos el tipo de variables. El número de empleados (N) y la
variable contador (c) son valores enteros, mientras que todas las variables asociadas al sueldo del
empleado son decimales, por lo tanto, las declararemos como flotantes (float) ya que las cantidades
tampoco son muy grandes.

Código en C

/*nomina.c: programa que calcula la nomina de empleados de la empresa "El


Patito" */
/*directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/*Función Principal*/
main ()
{
/*Declaración de variables*/
int N,c;
float total_nom, d,phe,sb,s;
printf(“El siguiente programa calcula el pago de los empleados de una
empresa y calcula el monto total de la nomina\n\n”);
printf ("Ingrese el total de empleados: ");
scanf ("%d",&N);
/*inicia ciclo*/
for(c=1,total_nom=0; c<=N; c++){
/*lectura de los datos del empleado c*/
printf("\nSalario base del empleado %d:",c);
scanf("%f",&sb);
printf("Pago por horas extras para el empleado %d:",c);
scanf("%f",&phe);
printf("Descuentos del empleado %d:",c);
scanf("%f",&d);
/*cálculo del pago del empleado c*/
s=sb+phe-d;
/*impresión del sueldo del empleado c */
printf("\n\t\t\t\tPago empleado %d:%.2f\n\n",c,s);
/*operación de acumulación para el total de la nómina*/
total_nom+=s;
}/*fin ciclo for*/

/*impresión del total de la nómina*/


printf("\t\t\t\tNomina total:%.2f",total_nom);

printf ("\n");
system ("pause");
}
Programa 6.2. nómina.c

Capítulo 6 | Página 20
En la Figura 6.2 Se muestra la ejecución del programa nómina.c ingresando los mismo datos de
entrada que se usaron en la prueba de escritorio.

Ejecución vs Prueba de escritorio

Figura 6.2. Ejecución nomina.c

Revisemos un ejemplo más que utiliza una sumatoria.


Ejemplo 6.5: El ISBN de un libro es un número de identificación que se le asigna a todo libro
publicado, consta de diez cifras que se dividen en dos partes: la primera, formada por los nueve
primeros dígitos, identifica el idioma del libro, la editorial y al libro; la segunda es el dígito de
control, que en realidad puede ser un dígito o una ‘X’. Si llamamos xi al dígito que aparece en
posición i-ésima, el décimo dígito x10 se define como:
Sea 𝑆 = ∑ 𝑖 ∗ 𝑥 entonces,
𝑆 𝑚𝑜𝑑 11 𝑠𝑖 𝑆 𝑚𝑜𝑑 11 ≠ 10
𝑥 =
𝑥 𝑒𝑛 𝑜𝑡𝑟𝑜 𝑐𝑎𝑠𝑜
Descripción del problema: Realizar un programa que genere el décimo dígito del ISBN de un libro,
si se tiene como entrada los 9 primeros dígitos.
Análisis del problema: En realidad se trata de implementar una sumatoria, los elementos del
conjunto son los datos de entrada, así que dentro del ciclo debemos incluir las instrucciones
correspondientes para obtenerlo. Necesitamos un contador para registrar cuántos dígitos hemos leído
y una variable acumulador para almacenar la suma de los i-ésimos elementos leídos hasta la i-ésima
iteración. Las condiciones iníciales del ciclo son x10 igual a cero (x10←0) y el contador i igual 1
(i←1); el ciclo debe repetirse 9 veces así que la condición es i≤9 y el cuerpo de ciclo es la lectura del
i-ésimo dígito y su acumulación en la suma de los anteriores, guardada en la variable x10. Al finalizar
el ciclo se debe calcular el módulo 11 de x10 y dependiendo del resultado se imprime un dígito o una
‘x’.

Capítulo 6 | Página 21
Análisis del problema
Datos de entada: Salida: Método:
Los primeros nueve dígitos x10:dígito de control Un ciclo definido que se repita 9
xi: i-esimo dígito veces. En cada iteración se pide un
dígito y se suma al valor
acumulado de x10. Dependiendo
del resultado de x10 módulo 11, se
imprime el resultado o una ‘X’
Algoritmo (Pseudocódigo)
Inicio
Desde i← 1, x10← 0, mientras i≤9, i←i+1
Imprimir “Proporciona el dígito”, i, “:”
Leer xi
x10 ← x10 + i * xi
Fin desde
x10←x10 módulo 11
Si x10<10 entonces
Imprimir “Dígito de control :” x10
Si no
Imprimir “Digito de control: X”
Fin si-sino
Fin
Algoritmo 6.8. Cálculo del dígito de control del ISBN

El ISBN del libro “Las mil y una noches” es: 970-666-855-1. Apliquemos el algoritmo a los primeros
nueve dígitos para verificar que funciona (sólo anotaremos las instrucciones del cuerpo del ciclo,
resaltando el estado el estado de las variables en cada iteración). Al inicio del ciclo i es 1 y x10 es 0.

iteración ¿i≤9? Xi x10 ← x10 + i * xi i←i+1


1 1≤9 9 x10 ← 0 + 1 * 9 i←1+1
V x10 ← 9 i←2
2 2≤9 7 x10 ← 9 + 2 * 7 i←2+1
V x10 ← 23 i←3
3 3≤9 0 x10 ← 23 + 3 * 0 i←3+1
V x10 ← 23 i←4
4 4≤9 6 x10 ← 23 + 4 * 6 i←4+1
V x10 ← 47 i←5
5 5≤9 6 x10 ← 47 + 5 * 6 i←5+1
V x10 ← 77 i←6
6 6≤9 6 x10 ← 77 + 6 * 6 i←6+1

Capítulo 6 | Página 22
V x10 ← 113 i←7
7 7≤9 8 x10 ← 113 + 7 * 8 i←7+1
V x10 ← 169 i←8
8 8≤9 5 x10 ← 169 + 8 * 5 i←8+1
V x10 ← 209 i←9
9 9≤9 5 x10 ← 209 + 9 * 5 i←9+1
V x10 ← 254 i←10
10 10≤9 Termina el ciclo
F

El valor de x10 al final del ciclo es 107 y la siguiente instrucción es:


x10←254 modulo 11
x10←1
Como x10 es menor que 10 (1≤10), entonces imprime como dígito de control 1.

El siguiente paso es la codificación en lenguaje C.

Código en C
/* isbn.c: calcula el dígito de control del ISBN de un libro */
#include <stdio.h>
#include <stdlib.h>

main(){
/*declaración de variables */
int xi,x10=0,i;

printf(“\n\t\tCalculo del digito de control del ISBN de un


libro\n\t\t\t a partir de los primeros 9 digitos\n”);
/*inicio del ciclo*/
for(i=1, x10=0;i<=9;i++){
printf("Digito %d: ",i);
scanf("%d",&xi);
/*acumulación*/
x10+=i*xi;
}/* fin for */
/*cálculo de x10 módulo 11*/
x10%=11;
/*elección del dígito de control de acuerdo con el módulo*/
if(x10<10)
printf("\nDigito de control: %d\n\n\t",x10);
else
printf("\nDigito de control: X\n\n\t");
system("pause");
}
Programa 6.3. isbn.c

Capítulo 6 | Página 23
Ejecución vs Prueba de Escritorio

Figura 6.3. Ejecución isbn.c

6.5 ESTRUCTURA REPETITIVA MIENTRAS (WHILE)


La estructura Mientras (while) se utiliza cuando queremos ejecutar cero o más veces un conjunto de
instrucciones, siempre y cuando una condición se cumpla. Es común utilizarla en los ciclos
indefinidos.

Estructura de Repetición (While)

Pseudocódigo Diagrama de Flujo

Mientras <condición> hacer F


Condición
<instrucciones> V
instrucciones
Fin_Mientras

Lenguaje C
Código en C Descripción
while…. Palabra reservada de C que indica el
inicio de un ciclo mientras.
while (<condición>) <condición>… condición que controla la
<instrucciones> ejecución del ciclo.
<instrucciones>…. Acciones que se realizan
cuando la <condición> se cumple, se
llama cuerpo de while. Si consta de dos
o más instrucciones éstas deben ir
encerradas entre llaves.
Tabla 6.6: Representaciones de la estructura Mientras (while)

Capítulo 6 | Página 24
Funcionamiento de la estructura repetitiva Mientras (while)

La manera en la que se ejecuta una instrucción Mientras(while) es la siguiente:

1. Se evalúa la <condición> (expresión booleana)


2. Si la <condición> fue verdadera se ejecutan todas las instrucciones del cuerpo del ciclo
3. Dentro de las instrucciones que se ejecutan en el paso 2, deberá existir una instrucciones que
modifique la o las variables involucradas en la condición, esto para que las instrucciones no se
repitan de forma infinita.
4. Una vez terminada la ejecución de las instrucciones, se regresa a evaluar de la condición.
5. Si la condición es verdadera se inicia otra iteración.
6. Cuando la condición es falsa, decimos que se terminan las iteraciones y entonces salimos del
ciclo.

Note que primero se evalúa la condición y dependiendo de ello se realizan o no las instrucciones por
tanto, al igual que el for, podría darse el caso de que la instrucción no se realice ni una sola vez.
También observa que los diagramas de flujos de las estructuras Mientras y Desde son muy parecidos,
excepto que en la estructura Mientras no está explícitamente la inicialización e
incremento/decremento de las variables que controlan el ciclo. Lo anterior nos habla de la
equivalencia entre las dos estructuras.

Para escribir una estructura Desde utilizando una estructura Mientras la inicialización debe ir antes
del Mientras y el incremento o decremento formar parte del cuerpo de la estructura, de esta manera se
puede escribir una estructura Desde como un Mientras.

<inicialización>
while (<condición>)
{
<instrucciones>
<incremento/decremento>
}

Al revés, una estructura Mientras se puede reescribir como una estructura Desde, haciendo nulas la
inicialización y el incremento, en lenguaje C se escribiría de la siguiente forma:

for( ; <condición>; )
{
<instrucciones>
}

Veamos algunos ejemplos utilizando la sentencia while.

Capítulo 6 | Página 25
Ejemplo 6.6: Codifiquemos el Algoritmo 6.4 que resuelve el problema de calcular el total de la
compra de artículos en un supermercado utilizando una bandera para determinar el fin del ciclo y una
variable contador para registrar el número de artículos que se compraron, el contador no controla el
ciclo sólo cuenta el número de artículos para imprimirlo junto con el total.

Código supermercado.c
/*Ejemplo de ciclo indefinido (variable cetinela)
supermercado.c: Código en C que imprime la cuenta a pagar en el
supermercado*/
/*directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
/*Función Principal*/
main ()
{ /*Declaración de variables*/
char continuar; /*variable bandera*/
float precio=0,total = 0;
int cont; /*variable contador*/
printf(“\t\t\t ****** Maquina registradora ****** \n ”);

/*inicialización*/
continuar = ‘s’; /*se asigna ‘s’ para asegurarnos que entre
una vez al ciclo */
cont = 0; /*hasta el momento se han leído 0 artículos*/
total = 0; /*dado que son sumas sucesivas se inicaliza en 0*/

/*inicio del ciclo*/


while (continuar != 'n')
{
/*Lectura del precio de los artículos*/
printf("\nValor del articulo: ");
scanf("%f",&precio);
/*acumulación*/
total+=precio;
/*sumamos un artículo*/
cont++;
/*verificamos si hay otro artículo*/
printf("\tDeseas ingresar otro articulo(s:si, n:no):");
fflush(stdin);
scanf("%c",&continuar);
}/*fin ciclo*/

/*impresión del resultado*/


printf("\n\n\t%d articulos comprados",cont);
printf("\n\tTotal a pagar %.2f pesos\n\n\t\t",total);
system ("pause");
}
Programa 6.4. supermercado.c

Capítulo 6 | Página 26
Ejecución

Figura 6.4. Ejecución del programa supermercado.c

Ejemplo 6.7: Realizar el análisis, diseño e implementación de un programa que sume dos números.
El programa debe repetirse mientras que el usuario quiera.

Análisis: El problema es similar al del supermercado, e incluso más sencillo pues no tenemos que
acumular ningún resultado, cada que se leen los datos se calcula la suma y se imprime el resultado.
También necesitamos una bandera para registrar cuando el usuario quiere salir del programa.

Análisis del problema


Datos de entada: Salida: Método:
Sumandos: num1 y num2 suma (decimal) Ciclo que realice la operación:
(decimal) suma=num1+ num2
Opción de continuar o no: opc mientras que el usuario elija
(caracter) continuar.
Algoritmo (Pseudocódigo)

Inicio
Imprimir “Programa que calcula la suma de dos números”
Imprimir “Deseas utilizar el programa (S/N):”
Leer opc
Mientras opc=’S’  opc=’s’ hacer
Imprimir “Dame dos números enteros:”
Leer num1, num2
Imprimir num1, “+”,num2,”=”, n1+n2
Imprimir "Deseas realizar otra suma (S/N): "
Leer opc
Fin_Mientras
Fin

Algoritmo 6.9. Suma dos enteros, repetidamente (Pseudocódigo)

Capítulo 6 | Página 27
Algoritmo (Diagrama de Flujo)

Inicio

Imprimir "Programa que suma


dos números enteros”

Imprimir “Deseas utilizar el programa (S/N)”

Leer opc

F
opc=’s’  opc=’S’

V
Imprimir "Dame dos números
enteros”

Leer num1, num2

Imprimir num1, “+”,num2,”=” num1,+num2

Imprimir “Deseas realizar otra suma (S/N)”

Leer opc

Fin

Algoritmo 6.10. Suma dos enteros, repetidamente (Diagrama de Flujo)

Antes de codificar probemos si nuestro algoritmo funciona con las parejas: (5,6), (15.5,12.5) y
(1.1,4.9)

Capítulo 6 | Página 28
Prueba de escritorio
Instrucción Datos de entrada Estado de las variables Salida a
locales monitor
Imprimir "Programa que suma - - Programa que
dos números enteros” suma dos
Imprimir “Deseas utilizar el - Deseas utilizar el
programa (S/N):” programa (S/N):

Leer opc S opc num1 num2


S - -
opc=’s’  opc=’S’ -
No cambia
S=’s’  S=’S’
Imprimir "Dame dos - No cambia Dame dos
números enteros” números enteros
Leer num1, num2 5,6 opc num1 num2
Iteración 1

S 5.0 6.0
-
Imprimir num1, - No cambia 5.0+6.0=11.0
“+”,num2,”=”
Imprimir “Deseas - Deseas utilizar el
realizar otra suma (S/N)” programa (S/N):
Leer opc S opc num1 num2 -
s 5.0 6.0
opc=’s’  opc=’S’ -
No cambia
‘s’=’s’  ‘s’=’S’
Imprimir "Dame dos - No cambia Dame dos
números enteros” números enteros
Leer num1, num2 15.5,12.5 opc num1 num2 -
Iteración 2

s 15.5 12.5
Imprimir num1, - 15.5+12.5=28.0
“+”,num2,”=”
Imprimir “Deseas - Deseas utilizar el
realizar otra suma (S/N)” programa (S/N):
Leer opc S opc num1 num2 -
S 15.5 12.5
opc=’s’  opc=’S’ -
No cambia
‘s’=’s’  ‘s’=’S’
Imprimir "Dame dos - No cambia Dame dos
números enteros” números enteros
Leer num1, num2 1.1, 4.9 opc num1 num2 -
Iteración 3

S 1.1 4.9
Imprimir num1, - No cambia 1.1+4.9=6.0
“+”,num2,”=”
Imprimir “Deseas - No cambia Deseas utilizar el
realizar otra suma (S/N)” programa (S/N):
Leer opc S opc num1 num2 -
n 5.0 6.0

Capítulo 6 | Página 29
opc=’s’  opc=’S’ -
termina

‘n’=’s’  ‘n’=’S’ -
ciclo

-
Falso

Tabla 6.7. Prueba de escritorio del algoritmo suma dos enteros repetidamente.

Código en C
/* Ejemplo de ciclo indefinido (controlado por una variable
centinela).
suma.c Código en C que calcula la suma de dos números enteros */

/*directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

/*Función Principal*/
main ()
{ /* declaración de variables*/
char opc;
float num1,num2;
printf ("\n\nPrograma que suma dos enteros");
printf ("\nDeseas utilizar el programa (S/N): ");
opc=getche();

/* inicio del ciclo*/


while(opc=='S' || opc=='s')
{ /*lectura de los dos enteros*/
printf ("\n\nDame dos numeros enteros:");
scanf ("%f,%f",&num1, &num2);
/*impresión de la suma*/
printf ("\n%.2f + %.2f = %.2f",num1,num2, num1+num2);
/*verificamos si el usuario desea hacer otra suma*/
printf ("\n\n\tDeseas realizar otra suma (S/N): ");
opc=getche();
} /*fin del ciclo*/
printf ("\n");
system ("pause");
}

Programa 6.5. suma.c

En la siguiente figura se muestra la ejecución del programa utilizando los mismos datos que se
emplearon en la prueba de escritorio del algoritmo.

Capítulo 6 | Página 30
Ejecución vs Prueba de escritorio

Figura 6.5. Ejecución del programa suma.c

Ejemplo 6.8: Un número entero M es triangular si es posible dibujar un triángulo rectángulo con M
asteriscos, como se muestra a continuación.

* *
* ** **
** *** ***
*** **** ****
***** M=21
M=6 M=10 ******

Descripción del problema: Realiza el análisis, diseño e implementación de un programa que lea un
número M y determine si es un número triangular, además debe imprimir la altura del triángulo
rectángulo que se puede dibujar con M asteriscos.

Análisis: De acuerdo con los ejemplos cualquier triángulo empieza con un asterisco, en la siguiente
línea tiene dos, en las siguiente 3, y así sucesivamente en cada nuevo renglón se agrega un asterisco
más. Entonces, un triángulo de altura N tiene 1+2+3+…+N asteriscos. Por lo tanto, podemos
replantear la definición de un número triangular del siguiente modo:
Un número M es triangular si es igual a la suma de los primeros N naturales, para algún natural N.

𝑀= 𝑖

Capítulo 6 | Página 31
Utilizaremos un ciclo para encontrar el valor de N, de tal manera que con cada iteración del ciclo
simularemos que dibujamos una nueva línea del triángulo: en la primera iteración el contador vale 1
que es el número de asteriscos de la primera línea, el número total de asteriscos es 1; en la segunda
iteración el contador es 2 que son los asteriscos de la segunda línea, pero el número total de asteriscos
es 3; en la tercera iteración el contador vale 3 y el número total de asteriscos es 6; así continuamos
hasta que encontremos el valor de N tal que la suma acumulada de asteriscos sea igual o mayor que
N. Sólo si N es igual a M concluimos que el número M es triangular. Las condiciones iníciales son N
igual a 0 y total de asteriscos (asteriscos) igual a 0, la condición es M<asteriscos y el cuerpo del ciclo
tiene la acumulación del número de asteriscos y el incremento de contador.

Análisis del problema


Datos de entada: Salida: Método:
M, número entero. M es triangular o M Ciclo indefinido para encontrar el
no es triangular; si es valor de N que representa la altura
triangular también del triángulo que se puede dibujar
imprime la altura del con M asteriscos.
triángulo rectángulo.
Algoritmo (Pseudocódigo)

Inicio
Imprimir “Proporciona un número entero”
Leer M
/*inicialización*/
N←0
asteriscos ← 0
Mientras M < asteriscos
N←N+1
asteriscos ← asteriscos + N
Fin_Mientras
Si M=asteriscos entonces
Imprimir “El número es triangular”
Imprimir “La altura del triángulo es”, N
Sino
Imprimir “El número no es triangular”
Fin Si-Sino
Fin

Algoritmo 6.11. Suma dos enteros, repetidamente (Pseudocódigo)

Capítulo 6 | Página 32
Código en C
/* numTriangular.c: El programa determina si un número dado es
triangular */
/*directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/*Función Principal*/
main ()
{ /* declaración de variables*/
int N,M, asteriscos;
int i,j; /*variables auxiliares para el mensaje de bienvenida*/
/*mensaje de bienvenida*/
printf ("\t\tPrograma que determina si un numero es
triangular\n\n");
printf("Un numero M es triangular si es posible dibujar un
triangulo rectangulo con \nM asteriscos");

/*los siguiente ciclos pintan un tiángulo rectángulo de altura 5*/


for(i=1; i<=5; i++){ /*número de línas del triángulo*/
printf("\n\t\t\t");
for(j=1; j<=i; j++) /* pinta los asteriscos de cada linea */
printf("*");
}

/* datos de entrada */
printf("\n\nDame un numero: ");
scanf("%d",&M);

/*inicialización*/
N=0;
asteriscos = 0;

/*inicio del ciclo que calcula el valor de N (altura del


triángulo)*/
while(asteriscos<M){
N++;
asteriscos += N;
} /*fin ciclo*/

if(M==asteriscos)
printf("\n\n\t%d es triangular, la altura del triangulo es
%d\n\n",M,N);
else
printf("\n\n\t%d no es triangular\n\n",M);
system("pause");
}
Programa 6.6. numTriangular.c

Capítulo 6 | Página 33
Observa que en el código se incluyeron dos ciclos for anidados al inicio del programa, la función de
estas instrucciones es impimir un triángulo rectángulo de altura 5, como se muestra en la ejecución
del programa (ver Figura 6.6).

Ejecución

Figura 6.6. Ejecución del programa numTriangular.c

¿Qué líneas de código tendríamos que agregar si queremos que el programa además de imprimir la
altura del triángulo, también lo dibuje?

6.6 ESTRUCTURA REPETITIVA HAS-MIENTRAS (DO-WHILE)


En la estructura Has-Mientras (do-while) existe una importante diferencia con respecto a las
estructuras repetitivas anteriores, en las estructuras Desde y Mientras la condición se evalúa al
principio del ciclo (esto permite que las instrucciones del ciclo no se ejecuten ni una sola vez si la
inicialización no cumple con la condición); en cambio, en la estructura Has-Mientras la evaluación de
la condición se lleva acabo al final del ciclo, es decir, después de que se ejecutaron las instrucciones
del cuerpo del ciclo, esto implica que el conjunto de instrucciones que se repite se ejecuta al menos
una vez.

Dadas sus características, es común utilizar la sentencia do-while para la validación de datos de
entrada o para implementar aplicaciones con menús. Para el primer caso, de no ingresarse el dato de
entrada en la forma que el programa lo requiere, podemos seguir solicitándolo hasta que el usuario lo
ingrese correctamente, en vez de terminar el programa, pero al menos se realiza una lectura de
mismo. En el caso del menú también se requiere desplegarlo al menos una vez y regresar a él después
de realizarse la acción elegida, generalmente existe una opción que nos permite salir del programa y
ésta marcará el final del ciclo.

Capítulo 6 | Página 34
En tabla 6.8 podemos ver la sintaxis en C para esta estructura, así como su representación en
diagrama de flujo y pseudocódigo. Observa en el código C, que es la única estructura de control, de
todas las que hemos visto, que tiene punto y coma después de la condición. Otra diferencia con
respecto a las estructuras anteriores es que las llaves del cuerpo del ciclo son necesarias incluso
cuando se tenga una sola instrucción a repetir.

Estructura de Repetición Hacer-Mientras (do-while)

Pseudocódigo Diagrama de Flujo

Hacer
instrucciones
<instrucciones>

Mientras <condición> V
Condición
Fin_Hacer-Mientras
F

Lenguaje C
Código en C Descripción
Do{ Do … indica el inicio del ciclo do-while
<instrucciones>… conjunto de acciones que
<Instrucciones> se ejecutan mientras la <condición>
}while(<condición>); sea verdadera. Siempre se ejecutan al
menos una vez. Si son dos o más
instrucciones se deben encerrar entre
llaves.
<condición>… expresión booleana que
controla el ciclo.
Tabla 6.8. Representaciones de la estructura Hacer-Mientras (do-while)

Funcionamiento de la estructura repetitiva Has-Mientras (do-while)

La estructura Has-Mientras funciona de la siguiente forma:

1. Se ejecutan las instrucciones del cuerpo del ciclo,


2. Se evalúa la condición,
3. Si la condición es verdadera se repite el ciclo (ir al paso 1),
4. Si la condición es falsa termina el ciclo.

Capítulo 6 | Página 35
Esta estructura también es equivalente con las anteriores, es decir, cualquier ciclo do-while se puede
implementar utilizando un ciclo for o un ciclo while, y viceversa. Veamos el caso del ciclo while,
para escribir una estructura while como una estructura do-while, tenemos que asegurarnos de que el
ciclo do-while sólo se ejecute si la condición es verdadera, esto se puede hacer mediante una
estructura if, como se ilustra a continuación.

while(<condición>) if(<condición>)
{ {
<instrucciones> do{
} <instrucciones>
}while(<condición>);
}

Ahora, si queremos escribir una instrucción do-while usando un while, debemos garantizar que el
conjunto de instrucciones se ejecute al menos una vez, así que lo copiaremos antes del ciclo while.

do{ <instrucciones>
<instrucciones> while(<condición>)
}while(<condición>); {
<instrucciones>
}

La equivalencia con el for se puede definir a partir de la equivalencia de éste con el ciclo while.

Como primer ejemplo de implementación de un ciclo do-while codifiquemos el Algoritmo 6.5.

Ejemplo 6.9: Realiza un programa en C para calcular el total de la compra en un supermercado


utilizando un valor centinela.

Codificación supermercado2.c
/*Ejemplo de ciclo indefinido (valor centinela)
supermercado2.c: Código en C que imprime la cuenta a pagar en el
supermercado*/
/*directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
main (){
/*declaración e inicialización de variables*/
float p,total=0;
printf(“\t\t\t ****** Maquina registradora ****** \n ”);
printf(“\n Para calcular el total introduce -1 como valor del
articulo”);
/*inicio del ciclo*/
do{
/*lectura del artículo*/
printf("\nValor del articulo:");

Capítulo 6 | Página 36
scanf("%f",&p);
/*acumulación*/
total+=p;
}while(p>=0); /*el ciclo se repite mientra el precio no se
un valor negativo*/

printf("\n\tTotal a pagar %.2f",total);


printf ("\n");
system ("pause");
}
Programa 6.7. supermercado2.c

Ejecución

Figura 6.7. Ejecución del programa supermercado2.c

Ejemplo 6.10: Ejemplo de la implementación de un menú que se repite hasta que el usuario elige la
opción salir.

Descripción del problema: Se requiere un programa que muestre los despejes de la 2ª. Ley de
Newton, mediante un menú de opciones (de tipo carácter), el programa sólo debe cerrarse cuando el
usuario elija la opción salir, de lo contrario debe continuar mostrando el menú. Las opciones y sus
respectivas fórmulas son:

 Fuerza: 𝑓 = 𝑚 ∗ 𝑎
 Aceleración: 𝑎 =
 Masa: 𝑚 =

Análisis: Para que el menú se muestre consecutivamente utilizaremos un ciclo Hacer-Mientras, ya


que al menos se debe imprimir una vez. Para imprimir la fórmula correspondiente a la opción elegida
por el usuario lo más conveniente es utilizar una estructura Selecciona-Casos, ya que tenemos varias
opciones que dependen del valor de la variable que almacena la opción del usuario.

Capítulo 6 | Página 37
Análisis del problema
Método:
Datos de entada: Un ciclo Hacer-Mientras para
Salida: mensajes
Opción del menú (opc :char) repetir el menú, y una estructura
Selecciona-Casos para imprimir la
fórmula elegida.
Algoritmo (Pseudocódigo)
Inicio
Hacer
Imprimir "Trabajando la 2a. Ley de Newton"
Imprimir " ------------------------------------------------"
Imprimir " a. Fuerza."
Imprimir " b. Aceleración."
Imprimir " c. Masa."
Imprimir " d. Salir."
Imprimir " Elegir una Opcion: "
Leer opc
Selecciona (opc)
Caso 1: Imprimir " f=m*a"
Caso 2: Imprimir " a=f/m"
Caso 3: Imprimir " m=f/a"
Caso 4: Imprimir "Adios"
Otro: Imprimir " Opcion invalida"
Fin_Selecciona
Mientras (opc≠4)
Fin_HacerMientras
Fin

Capítulo 6 | Página 38
Algoritmo (Diagrama de Flujo)
Inicio

Imprimir "Trabajando la 2ª ley de Newton”


Imprimir “----------------------------------“
Imprimir “a. Fuerza”
Imprimir “b. Aceleración”
Imprimir “c. Masa”
Imprimir “d. Salir”
Imprimir “Elegir una opción:”

Leer opc

(opc)

caso a caso b caso c caso d otro


Imprimir Imprimir Imprimir Imprimir Imprimir
“f=m*a” “a=f/m” “m=f/a” “Adios” “Opción
Invalida”

V
(opc≠d)

F
Fin
Algoritmo 6.12. Menú Segunda ley de Newton

Capítulo 6 | Página 39
Prueba de escritorio
Valor de los parámetros de entrada: opc=a,c,e,d
Estado de
Datos de
Instrucción las Salida a monitor
entrada
variables
Inicio - - -
Primer Caso (Despliega el menu). Cuando la opción es a
opc
DO - - -
Imprimir "Realiza los cálculos
trabajando la 2ª ley de Newton"
- No cambia Trabajando la 2ª ley de Newton
Imprimir "--------" - No cambia ----------------
Imprimir “a. Fuerza” - No cambia a. Fuerza
Imprimir “b. Aceleración” - No cambia b. Aceleración
Imprimir “c. Masa” - No cambia c. Masa
Imprimir “d. Salir” - No cambia d. Salir
Imprimir “Elegir una opción:” - No cambia Elegir una opción:
opc
Leer opc a a -
Casos para: (opc)
Caso ‘a’: “ f=m*a” opc
a f=m*a
verdadero
Iteración 1

FIN DE SELECCIONA
Mentras (opc≠’d’) opc
- a -
‘a’≠’d’ verdadero
Segundo Caso (Despliega el menu). Cuando la opción es c
Imprimir "Realiza los cálculos opc
trabajando la 2ª ley de Newton"
- a Trabajando la 2ª ley de Newton
Imprimir "--------" - No cambia ----------------
Imprimir “a. Fuerza” - No cambia a. Fuerza
Imprimir “b. Aceleración” - No cambia b. Aceleración
Imprimir “c. Masa” - No cambia c. Masa
Imprimir “d. Salir” - No cambia d. Salir
Imprimir “Elegir una opción:” - No cambia Elegir una opción:
opc
Leer opc c c -
Casos para: (opc)
Caso ‘a’: “ f=m*a” falso opc
c
Caso ‘b’: “ a=f/m” falso
m=f/a
Caso ‘c’: “m=f/a”
Iteración 2

verdadero
FIN DE SELECCIONA
Mientas (opc≠’d’) opc
- c -
‘c’≠’d’ veradero
Segundo Caso (Despliega el menu). Cuando la opción es e
Iteración

Imprimir "Realiza los cálculos opc


trabajando la 2ª ley de Newton"
- c Trabajando la 2ª ley de Newton
Imprimir "--------" - ----------------
3

No cambia

Capítulo 6 | Página 40
Imprimir “a. Fuerza” - No cambia a. Fuerza
Imprimir “b. Aceleración” - No cambia b. Aceleración
Imprimir “c. Masa” - No cambia c. Masa
Imprimir “d. Salir” - No cambia d. Salir
Imprimir “Elegir una opción:” - No cambia Elegir una opción:
opc
Leer opc e e -
Casos para: (opc)
Caso ‘a’: “ f=m*a” falso opc
e
Caso ‘b’: “ a=f/m” falso
Caso ‘c’: “m=f/a” falso
Caso ‘d’: “Adios” falso Opción invalida
Otro caso:
“Opción invalida”
veradero
FIN DE SELECCIONA
Mientas (opc≠’d’) opc
- e -
‘e’≠’d’ veradero
Segundo Caso (Despliega el menu). Cuando la opción es d
Imprimir "Realiza los cálculos opc
trabajando la 2ª ley de Newton"
- e Trabajando la 2ª ley de Newton
Imprimir "--------" - No cambia ----------------
Imprimir “a. Fuerza” - No cambia a. Fuerza
Imprimir “b. Aceleración” - No cambia b. Aceleración
Imprimir “c. Masa” - No cambia c. Masa
Imprimir “d. Salir” - No cambia d. Salir
Imprimir “Elegir una opción:” - No cambia Elegir una opción:
opc
Leer opc d d -
Casos para: (opc)
Caso ‘a’: “ f=m*a” falso opc
d
Caso ‘b’: “ a=f/m” falso
Adios
Caso ‘c’: “m=f/a” falso
Iteración 4

Caso ‘d’: “Adios” verdadero


FIN DE SELECCIONA
Mientas (opc≠’d’) opc
- c -
‘d’≠’d’ falso
FIN MIENTRAS
Fin - - -
Tabla 6.9: Prueba de escritorio para el algoritmo del Menú de la Segunda Ley de Newton

Capítulo 6 | Página 41
Código en C
/* menúNewton.c: Código en C que imprime un menú hasta que se elige
la opción salir */
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main (){
char opc;
do { /*inicia ciclo do-while*/
printf ("\n Trabajando la 2a. Ley de Newton");
printf ("\n ------------------------------------------------");
printf ("\n a. Fuerza. \n b. Aceleracion \n c. Masa \n d. Salir");
printf ("\n Opcion: ");
opc=getche(); /*Instrucción que lee una variable de tipo
carácter*/
switch (opc) { /*Inicio de la Selección Múltiple*/
case 'a': printf ("\n f=m*a\n");
system ("pause");
break;
case 'b': printf ("\n a=f/m\n");
system ("pause");
break;
case 'c': printf ("\n m=f/a\n");
system ("pause");
break;
case 'd': printf ("\nAdios\n");
system ("pause");
break;
default: printf ("\n Opcion Invalida");
}/*Fin de la Selección Múltiple*/
}/*Fin del ciclo*/
while (opc!='d');
}
Programa 6.8. menuNewton.c

Capítulo 6 | Página 42
Ejecución vs Prueba de escritorio

Figura 6.8. Ejecución del programa menuNewton.c

Al igual que las estructuras selectivas, también es posible anidar estructuras repetitivas, es decir, la
instrucción del cuerpo de un ciclo puede ser otro ciclo, como se mostró en el Programa 6.6. En el
siguiente ejemplo se refuerza esta idea.
Ejemplo 6.10: Una señal cuadrada es una señal periódica que varía regularmente entre dos valores
fijos de amplitud de la misma duración. La forma de una onda cuadrada ideal se muestra en la figura
siguiente:

Figura 6.9. Señal cuadrada

Descripción del problema: Realizar un programa que grafique una señal cuadrada a la frecuencia
dada (entre 25-100 Hz), considerando una resolución en tiempo de .5ms con amplitud de 0 a 5V,
imprimir el error para la frecuencia debido a la resolución considerada.

Capítulo 6 | Página 43
Análisis del problema: La salida deseada se trata de una serie de líneas horizontales y verticales;
dado que la impresión en pantalla en C se hace línea por línea primero analicemos como generar la
línea horizontal.

Para la primera línea de la impresión debemos generar segmentos de recta de tamaño n, alternados
con n espacios, los segmentos de recta los podemos dibujar mediante la impresión del un guión ‘-’.
Utilizando un ciclo definido que se repita n veces es posible imprimir n subrayados o n espacios,
según sea el caso. Primero se deben imprimir los guiones y la siguiente vez que se ejecute el ciclo se
deben imprimir espacios, e irse alternando cada vez que se completan n impresiones, esto lo podemos
controlar mediante una bandera que cambie de estado cada vez que el ciclo se ejecuta, si repetimos
este ciclo varias veces hasta cubrir el ancho de la pantalla entonces generaremos la secuencia
completa (tenemos dos ciclos anidados, uno para la secuencia y el otro para cubrir todo el renglón).
Consideremos un ancho de 80 caracteres.

El valor de n depende de la resolución en tiempo y de la frecuencia solicitada. La resolución en


tiempo es el mínimo de tiempo que vamos a representar, es decir, por cada subrayado
representaremos un intervalo de tiempo de .5ms.

Dado que el periodo es el intervalo de tiempo que abarca un pulso y el periodo es el inverso de la
frecuencia, el valor de n se pude obtener mediante la siguiente operación:

1
𝑛=
2𝑓𝑟

Donde:
n: número de caracteres y espacios a imprimir,
f: representa la frecuencia de la señal (en Hz),
r: es la resolución (.0005 s).

Dado que n representa el número de caracteres a imprimir entonces tiene que ser un valor entero, sin
embargo, el resultado de la operación no siempre será entero así que será necesario truncar o
redondear el resultado.

Ahora bien, las línea verticales podrían ser varios renglones de secuencias de n espacios seguidos de
un carácter “|”, lo cual también se puede realizar mediante ciclos definidos anidados. Uno que
imprima la secuencia de espacios seguidos de “|” hasta cubrir el ancho de la pantalla y otro que
genere 4 renglones iguales. Después de imprimir n-1 espacios tenemos que imprimir una línea
vertical “|”, así que si utilizamos un contador para registrar el número de caracteres que se vayan
pintando por línea, tenemos que pintar un “|” cuando el contador sea múltiplo de n (considerando que
el contador se inicializa en 1).

Finalmente, la última línea a imprimir debe ser casi igual a la primera con la diferencia de que se
deben imprimir primero los espacios y después los segmentos de recta, podemos utilizar la misma

Capítulo 6 | Página 44
estructura de ciclos que al inicio pero dando un valor inicial distinto a la bandera que controla la
impresión del símbolo correspondiente.

Consideraciones para el diseño del algoritmo:

Análisis del problema


Datos de entada: Salida: Método:
(Calculo de N)
f: frecuencia (entero) gráfica 1
opc: opción del error 𝑛←
2𝑓𝑟
menú que permite al Utilizamos el entero mayor menor que
usuario graficar más para truncar el resultado de la
frecuencias
(carácter) división y obtener un número entero
(Calculo del error)
Restricciones 1
𝑒←𝑓−
25 ≤ f ≤ 100 2𝑓𝑟
Se utilizan ciclos anidados para
imprimir cada renglón como se
muestra a continuación

Usaremos las variables i y c como contadores para los ciclos: la primera para dibujar las secuencias
de n espacios y n subrayados, y también para pintar los renglones de n espacios seguidos de barras (|);
la segunda variable se utilizará para registrar los caracteres impresos en una línea (los espacios
cuentan como un carácter). Además, utilizaremos una variable bandera, llamada b, para imprimir “_”
o espacio según se requiera, cuando se quiera imprimir “_” se debe asignar 1 y -1 cuando se quiera
imprimir espacio.

Capítulo 6 | Página 45
Para los ciclos de la primera línea horizontal tenemos:
Ciclo externo
Imprime secuencias alternadas de n subrayados y n espacios, hasta completar el
ancho de la pantalla (80 caracteres)
Inicialización b ←1 comienza pintando subrayados
c←0 número de caracteres impresos en el renglón
Cuerpo del ciclo Ciclo interno
Imprime una secuencia de n subrayados o n
espacios, según sea el valor de b
Inicialización i ←0
No se ha impreso
ningún carácter de
la secuencia de
subrayados o
espacios.
Cuerpo del ciclo selección para
imprimir “_” o
espacio, de acuerdo
com el valor de b
incremento de c
Condición i<n ^ c<79

b ← -b Una vez que se completo una secuencia, se


cambia la bandera para que en la siguiente iteración
del ciclo externo, el ciclo interno imprima la
secuencia del símbolo contrario.
Condición c<79 El número de caracteres pintados debe ser
menor que el limite permitido, el último caracter lo
reservamos para el salto de línea

Capítulo 6 | Página 46
Para los ciclos de las líneas verticales tenemos:
Ciclo externo
Imprime cuatro cuatro renglones de secuencias de espacios seguidos del
carácter ‘|’ para simular las líneas verticales que forman la señal
Inicialización i ←1 renglones impresos
Cuerpo del ciclo: Ciclo interno
Imprime un renglón con secuencias de n-1
espacios seguidos de ‘|’
Inicialización c ←1 número de
caracteres impresos en
el renglón.
Cuerpo del ciclo Selección para
imprimir espacio o “|” ,
solo si c es múltiplo de
n se imprime ‘|’
Incremento de c
Condición c<=79 se repite
mientras el número de
caracteres pintados no
rebase el límite.
Condición de termino: i<=4 debe imprimir cuatro renglones y empezó en 1.

Para la última línea horizontal lo único que cambia respecto a la primera es que la bandera se
incializa en -1.

Tenemos que validar que la frecuencia dada este entre 25 y 100 Hz, para lo cual utilizaremos un ciclo
do-while. También vamos a dar la posibilidad al usuario de graficar varias señales si así lo desea para
lo cual agregaremos un ciclo do-while que contenga a todo el código.

En las siguientes tablas se muestra un resumen del análisis

Capítulo 6 | Página 47
Algoritmo (Pseudocódigo)
Inicio
Hacer /* ciclo para graficar varias señales*/
Hacer /*ciclo para validar que la frecuencia */
Imprimir “frecuencia en Hz:”
Leer f
Si f<26  f>100 entonces
Imprimir “La frecuencia es incorrecta”
Fin Si
Mientras f<26  f>100 /*rangos no válidos de la frecuencia, se repite el ciclo*/
Fin Hacer-Mientras
/* Calculamos el valor de n, debe ser un entero así que lo truncamos */
𝑛← /* tomamos el mayor entero menor que */

/* PINTAMOS LA PRIMERA LÍNEA*/


c←0 /*Número de caracteres pintados es 0*/
b←1 /*pintamos guión ‘-’*/
Mientras c<79 hacer /*ciclo externo para pintar todo el renglón*/
Desde i←0, mientras i<n ^ c<79 , i←i+1 /*pinta secuencias de guiones o espacios*/
Si b>0 entonces
Imprimir “-”
Si no
Imprimir “ ”
Fin Si-sino
c←c+1 /*incrementamos el contador de caracteres en un renglón*/
Fin desde
b←-b /*cambiamos la bandera para que en la siguiente iteración de Mientras se pinte el
símbolo contrario*/
Fin Mientras
Imprimir “\n” /* Imprimimos salto de línea para inicia con el siguiente renglón*/

/* PINTAMOS 4 RENGLONES PARA DIBUJAR LAS LÍNEAS VERTICALES*/


Desde i←0 mientras i<4, i←i+1 /*ciclo para pintar 4 renglones*/
Desde c←1 mientras c<79, c←c+1
Si c mod n = 0 entonces /*después de n-1 espacios pintamos uma barra */
Imprimir “|”
Si no
Imprimir “ ”
Fin si-sino
Fin desde
Imprimir “\n” /*inicia otro renglón*/
Fin desde
Algoritmo 6.13. Graficación de frecuencia (Parte I)

Capítulo 6 | Página 48
/* PINTAMOS ÚLTIMO RENGLÓN */
/* Igual al primer ciclo anidado con la diferencia que la bandera b se inicializa en -1 para que
comience con la secuencia de espacios*/
/*Calculamos el error
Imprimir “Error =”, 𝑓 −
Imprimir “Deseas otra gráfica(S:si N:no)? : ”
Leer opc
Mientras opc≠ ‘N’ /*Termina el ciclo cuando el usuario ya no desea graficar otra frecuencia*/
Fin hacer-mientras
Fin

Algoritmo 6.13. Graficación de frecuencia (Parte I)

Se deja como ejercicio al lector codificar el algoritmo en lenguaje C.4 En la siguiente figura se
muestra un ejemplo de la ejecución del programa.
Ejecución vs Prueba de escritorio

Figura 6.10. Ejecución del programa graficadorFrecuencia.c

4 La función ceil definida en la biblioteca math.h recibe un entero n y regresa el mayor entero menor que n (es decir, trunca n).

Capítulo 6 | Página 49
EJERCICIOS DEL CAPÍTULO No. 6

I. Complementa los siguientes enunciados correctamente

a) Llamamos __________________ a cada una de las repeticiones en un ciclo.


b) En un ciclo __________ se conoce de antemano el número de iteraciones.
c) La estructura repetitiva ________________ maneja de manera automática todos los detalles
del contador.
d) En las estructura de repetición vistas en este capítulo se vuelven a ejecutar las instrucciones
mientras la condición es ____________________
e) En un ciclo___________________ no sabemos el número total de iteraciones al momento
de diseñar el algoritmo.
f) Cuando usamos un contador para controlar un ciclo es necesario ____________ al inicio del
ciclo y _________________ en cada iteración.
g) Los ciclos _________ y ________ se ejecutan cero o más veces, en cambio el ciclo
__________ se ejecuta al menos una vez.
h) La única estructura de control que lleva punto y coma después de la condición es
_________________
i) Cuando el cuerpo de un ciclo tiene más de una instrucción estás deben ir encerradas entre
______________

II. Analiza los siguientes algoritmos haciendo una prueba de escritorio con los datos de entrada
dados y contesta las preguntas planteadas:

Algoritmo 1
Inicio
Imprimir “Ingrese dos números enteros no negativos: ”
Leer b,n
Desde c←1, resultado←1, mientras c≤n, c←c+1
resultado=resultado*b
Fin_ Desde
Imprimir resultado
Fin

Capítulo 6 | Página 50
Algoritmo 1: Datos de entrada b =3 y n= 5
i. ¿Cuál es la salida del programa?
ii. ¿Cuántas iteraciones se realizan?
iii. ¿Cuánto valen al final las variables c y resultado?
iv. Si los datos de entrada fueran: b =5 y n= 10, ¿qué imprimirá el programa?
v. En general ¿qué calcula el programa?
a) resultado = nb b) resultado=bn c) resultado=b*n
Algoritmo 2
Inicio
Imprimir "Ingresa un intervalo [a,b]: "
Leer a,b
resultado ← 0
i←a
Mientras (i≤b)
Si i mod 2 = 0 entonces
resultado ← resultado + i
Fin_si
i←i+1
Fin_Mientras
Imprimir resultado
Fin

Algoritmo 2: Datos de entrada a = 10 y b= 20


i. ¿Cuál es la salida del programa?
ii. ¿Cuántas iteraciones se realizan?
iii. ¿Cuánto valen al final las variables i y resultado?
iv. Si los datos de entrada fueran: b = 10 y n= 50 ¿qué imprimirá el programa?
v. En general ¿qué calcula el programa?

Algoritmo 3
Inicio
Imprimir “Ingresa un número entero”
Leer n
Hacer
c ← n mod 10;
n ← (n – c)/10;
Mientras (n > 0)
Fin Hacer-Mientras
Fin

Capítulo 6 | Página 51
Algoritmo 3: Datos de entrada n = 12345
i. ¿Cuál es la salida del programa?
ii. ¿Cuántas iteraciones se realizan?
iii. ¿Cuánto valen al final las variables c y n?
iv. Si los datos de entrada fueran: n = 2560 ¿qué imprimirá el programa?
v. En general ¿qué problema resuelve el programa?

III. Buscando errores de sintaxis: Revisa los siguiente fragmentos de código y determina que
errores de sintaxis hay (supón que todas las variables fueron declaradas previamente)

Código 1
while i>10
{
printf (“\n %d ”, i);
i++;
};

Escribe cuáles son los errores y como se corrigen (2 errores).

Código 2
do
{
printf ("\n Introduce una calificacion (0-10): ");
scanf ("%f",&calif);
}
While (calif<0 || calif>10)

Escribe cual es el error y como se corrige (2 errores).

Código 3

for (c=10,c<=50,c++)
{printf ("\t%d",c);}

Capítulo 6 | Página 52
Escribe cual es el error y como se corrige (2 errores).

IV. Buscando errores de diseño: La salida de los siguientes programas no coincide con el problema
planteado, analiza el código y realiza los cambios pertinentes para corregirlo para que la salida sea
la deseada.

1. Se desea un programa que imprima los números impares menores a 100, tal como se muestra en
la siguiente figura:

Sin embargo el código siguiente imprime lo que se ilustra en la figura de la izquierda a éste:
main (){ Salida actual:
int c;
for (c=1;c<=100;c+=2);
printf ("%5d",c);

getch();
}

Escribe cuál es el error y como se corrige.

Capítulo 6 | Página 53
2. Ahora, se requiere un programa que imprima los números múltiplos de 5 menores a 100 en orden
descendente, como se muestra en la siguiente figura:

Sin embargo, el siguiente código los imprime en orden ascendente.


main (){ Salida actual:
int c;
for (c=1;c<=100;c+=2);
printf ("%5d",c);

getch();
}

Escribe cuál es el error y como se corrige.

Capítulo 6 | Página 54
3. Nuestra siguiente meta es crear un programa que le pregunte al usuario cuántos asteriscos desea
imprimir y los imprima. Por ejemplo:

El siguiente código realize esta tarea, pero tiene el inconveniente de que si la entrada es 0 imprime un
asterisco.
main ()
{
int a=0, n;
printf ("\n Ingresa el numero de asteriscos que deseas imprimir: ");
scanf(“%d”,&n);
do
{
printf (“*”);
a++;
}while(a<=n);
getch();
}

Escribe cuál es el error y cómo se puede corrige.

Capítulo 6 | Página 55
V. Dado el siguiente programa, realiza las tareas que a continuación se piden:

#include <stdio.h>
#include <stdlib.h>
main(){
int i,j,b;
b=1;
i=0;
while(i<24){
if(i%3==0)
b=-b;
for(j=0;j<24;j++){
if(j%3==0)
b=-b;
if(b<0)
printf("*");
else
printf(" ");
}
i++;
printf("\n");
}
}

a) Señala cual de las siguientes figuras corresponde a la salida del programa, es muy importante
que NO UTILICES LA COMPUTADORA.

Figura 1 Figura 2 Figura 3

b) Modifica el programa para generar las otras dos salidas.

Capítulo 6 | Página 56
VI. Nicómaco de Gerasa descubrió la siguiente propiedad:
Sumando el primer impar, se obtiene el primer cubo.
Sumando los dos siguiente impares, se obtiene el segundo cubo.
Sumando los tres siguientes impares, se obtiene el tercer cubo.
Sumando los cuatro siguiente impares, se obtiene el cuarto cubo.
Es decir,
13 = 1 = 1
23 = 3+5 = 8
33 = 7+9+11 = 27
43 = 13+15+17+19 = 64

Realiza un programa que dado un número natural n calcule los primeros n cubos utilizando la
propiedad de Nicómaco e imprima la suma de los números impares correspondientes a cada
cubo.

VII. Realiza un programa que evalúe la siguiente función entera en los puntos de un intervalo
[a,b] dado.

𝑥 +1 𝑠𝑖 𝑥 < 0
𝐹 (𝑥) = 𝑥 +1 𝑠𝑖 𝑥 > 0
1 𝑒𝑛 𝑜𝑡𝑟𝑜 𝑐𝑎𝑠𝑜

VIII. Realiza un programa en C que calcule el valor de f(x) definida de la siguiente forma:

(x − n)
𝑓 (𝑥) =
x+n

IX. Realiza un programa que lea dos horas inicial y final, en el formato hh:mm, y simule el
comportamiento de un reloj digital que avanza desde la hora inicial dada hasta la hora final.

X. Escribe un programa con ciclos anidados que imprima el siguiente triángulo.


1
222
33333
4444444
555555555
66666666666
7777777777777
888888888888888
99999999999999999
0000000000000000000

Capítulo 6 | Página 57
XI. Escribe un programa con ciclos anidados que imprima el siguiente triángulo.
1
232
34543
4567654
567898765
67890109876
7890123210987
890123454321098
90123456765432109
0123456789876543210

Capítulo 6 | Página 58
Capítulo
Diseño descendente

“Divide y Vencerás”

Julio César

OBJETIVOS:
Al finalizar este capítulo el estudiante:

o Comprenderá el concepto de módulo y diseño descendente.


o Diseñará algoritmos estructurados para resolver problemas que requieran diseño descendente.
o Implementará en lenguaje C algoritmos estructurados

7.1 INTRODUCCIÓN
En el capítulo 3 se introdujo el diseño descendente (top-down design), también llamado técnica de
refinamiento sucesivo o diseño modular, cuyo objetivo es resolver problemas dividiéndolos en sub-
problemas más simples. Hasta este punto hemos aplicado el refinamiento sucesivo para definir
acciones abstractas en términos de estructuras de control, sin embargo, aún no hemos requerido
dividir ninguno de los problemas planteados en problemas más simples. No obstante, en el mundo de
los problemas la mayoría de ellos son difíciles de resolver si pensamos en construir una solución
detallada de todo el problema en un mismo tiempo, así que lo más recomendable es utilizar la
estrategia “divide y vencerás”, dividimos el problema en sub-problemas más fáciles de resolver, los
resolvemos por separado y entonces tendremos una solución al problema general.
En este capítulo profundizaremos en el diseño descendente e introduciremos los conceptos
relacionados con la programación modular o modularización. Expondremos las ventajas que tiene
cada uno; así como las desventajas de no utilizarlo. Además, desarrollaremos dos ejemplos para
ilustrar cómo se diseña y representa un algoritmo modular tanto en pseudocódigo como en diagrama
de flujo, y cómo implementarlo en lenguaje C.

Capítulo 7 | Página 1
7.2 DISEÑO DESCENDENTE

El diseño descendente es una técnica que permite plantear la solución de un problema mediante la
modularización o segmentación del mismo, en otras palabras, la solución se divide en pequeños
módulos que se estructuran e integran jerárquicamente de acuerdo con las acciones que realizan.

Un algoritmo diseñado utilizando la metodología descendente se puede visualizar como una figura
hecha con bloques para construir, cada bloque representa un módulo, la figura es el ensamble de los
bloques así como el algoritmo es la integración de cada uno de los módulos, ambos con un orden
específico.

Cada módulo tiene un propósito específico, en este sentido un módulo se puede ver como una caja
negra o como una máquina abstracta capaz de realizar acciones complejas (es decir, instrucciones
que no equivalen ni a estructuras de control ni a instrucciones de algún lenguaje de programación).
Desde el enfoque de máquina abstracta los módulos necesitan alimentarse con ciertos datos de
entrada, mismos que procesan para devolver un resultado. La ventaja de considerar a los módulos
como cajas negras o máquinas abstractas en la solución global del problema, es que nos permite
concentrarnos en los aspectos generales de la solución y no perdernos en los detalles. Durante las
primeras etapas del refinamiento sólo tenemos que precisar qué hace cada módulo, sin importar cómo
lo hace (), en la medida en la que se refine cada módulo se consideran los aspectos particulares, de tal
manera que el refinamiento de un módulo termina cuando se define el cómo lo hace, es decir, con un
algoritmo que forma parte de la solución general, por tal motivo lo llamaremos sub-algoritmo.

Por lo tanto, el primer paso para encontrar una solución mediante el diseño descendente es describir
la solución en términos de acciones complejas; para cada uno de estas acciones se repite el
procedimiento buscando que en cada paso del refinamiento las acciones sean de menor nivel (es
decir, que manejan información y ejecutan instrucciones menos complejas), el proceso se repite hasta
que sea posible definirlas en términos de instrucciones básicas (por ejemplo, asignaciones,
condicionales y ciclos). Con lo anterior, podemos definir de forma precisa qué es un módulo.

Definición 7.1 Un módulo es un sub-algoritmo o procedimiento definido dentro de un algoritmo


para realizar una tarea específica, puede invocarse desde el algoritmo principal o desde otro sub-
algoritmo cuando éstos así lo requieran. Como cualquier algoritmo, un módulo tiene datos de
entrada, llamados parámetros de entrada, y una salida.

La forma de representar los módulos que integran la solución de un problema es mediante un


diagrama modular, que no es otra cosa más que un organigrama en el cual se simboliza una relación
jerárquica entre los módulos. En la Figura 7.1 se ilustra un ejemplo de un diagrama modular, el cual
se puede leer de la siguiente forma: El Módulo Principal requiere de los Módulos 1, 2 y 3 para
resolver el problema; el Módulo 1 utiliza el Modulo 1.1 mientras que el Módulo 3 necesita a los
Módulo 3.1 y 3.2 para cumplir con su objetivo; el Módulo 2 no se divide en ningún otro módulo, lo
cual indica que la función que se realizar se puede expresar con instrucciones básicas, así que el
refinamiento del módulo sólo es para construir dichas estructuras, al igual que los Módulos 1.1, 3.1 y
3.2.

Capítulo 7 | Página 2
En cualquier solución modular siempre existe al menos un módulo encargado de controlar y distribuir
el trabajo de los otros, por ejemplo, en el diagrama modular presentado den la Figura 7.1 el Módulo
Principal controla a los Módulos 1, 2 y 3, el Módulo 1 controla al Módulo 1.1 y el Módulo 3 controla
a los Módulo 3.1 y 3.2; pero entre todos ellos se distingue el que aparece en el primer nivel de la
jerarquía, pues éste tiene los puntos de inicio y fin de la solución completa del problema, representa
la solución global del problema original, por tal motivo lo llamaremos “módulo principal”. Decimos
que un módulo invoca o llama a otro (que está debajo de la jerarquía) cuando le da la orden de que
ejecute las acciones que lo definen.

Módulo
Principal

Módulo 1 Módulo 2 Módulo 3

Módulo 1.1 Módulo 3.1 Módulo 3.2

Figura 7.1. Diagrama modular

Cualquier problema puede resolverse sin modularizarlo, el inconveniente es que al hacerlo de esta
forma generalmente provoca que la solución tenga muchos errores u omisiones, pues no podemos
visualizar al mismo tiempo todos los detalles; incluso, poner atención en los detalles puede llevarnos
a perder de vista el objetivo general. Por otro lado, también existen problemas para los cuales es
inconveniente utilizar la modularización pues al analizarlos obtenemos un algoritmo de manera casi
inmediata, como es el caso de aplicar una fórmula matemática (por ejemplo, calcular el área de un
cuadrado). Entonces, la pregunta que surge es: ¿cuándo es útil la modularización y cuándo no?

Una vez que se ha hecho el análisis del problema y se tiene una vaga idea de cómo atacarlo, se
recomienda modularizar la solución cuando:

 Existe una tarea específica que debe ejecutarse más de una vez en diferentes partes del
algoritmo1 y con diferentes datos, esto nos permite optimizar el código

1 No como los ciclos que repiten una misma tarea de forma continua. En el caso de un módulo, este no necesariamente se repite

consecutivamente.

Capítulo 7 | Página 3
 Cuando el problema es extenso y/o complejo, de tal manera que pueden dividirse en tareas
(abstractas) específicas.

Antes de presentar un problema ejemplo ilustraremos la forma en la que se representa un módulo en


pseudocódigo y en diagrama de flujo; además de la forma de denotar una llamada.

7.2.1 Representación de un módulo

En cuando a la representación de módulos necesitamos saber cómo se definen y cómo se llaman.

¿Cómo se define un módulo?

Considerando que un módulo es un sub-algoritmo, la definición de éste es como la de cualquier


algoritmo, tenemos que indicar cuatro cosas:

 nombre, preferentemente nemotécnico;


 datos de entrada, llamados parámetros o argumentos de entrada, son opcionales ya que
podemos crear módulos que no reciban ningún parámetro (por ejemplo, un módulo que
imprime un menú). Los parámetros de entrada se representan con variables.

 resultado (denominado valor de retorno), al igual que los parámetros de entrada podemos
definir módulos que realicen una tarea que no implique devolver ningún resultado (por
ejemplo, un módulo que imprime una lista de valores almacenados en memoria);

 lista de instrucciones, acciones u operaciones que llevan al resultado deseado.

Ahora el punto es definir cómo expresar cada uno de estos elementos en pseudocódigo y diagrama de
flujo.

En el caso de pseudocódigo, para indicar que se trata del sub-algoritmo debemos escribir después de
la palabra Inicio el nombre del módulo seguido de los parámetros que recibe, encerrándolos entre
paréntesis. En el diagrama de flujo, omitimos la palabra Inicio y sólo escribimos el nombre del
módulo seguido de sus parámetros de entrada (ver Tabla 7.1). Si el módulo devuelve un valor esto se
indica utilizando la instrucción “Regresa <valor>” (<valor> puede se el resultado de una expresión),
cuando esta instrucción se ejecuta termina la ejecución del módulo y se devuelve el control al módulo
que hizo la llamada (el controlador); esto mismo ocurre cuando llegamos al Fin del módulo.

Un buen diseño sugiere que un módulo sólo contenga una instrucción Regresa (es decir, un solo
punto de salida).

Capítulo 7 | Página 4
Definición de un módulo
Pseudocódigo Diagrama de Flujo

Inicio <nombreMódulo> (<parámetros>)


<nombreMódulo> (<parámetros>)
<instrucciones>
Regresa <valor> /*opcional*/ <instrucciones>

Fin
Regresar <valor>

Fin

Tabla 7.1. Forma general de la definición de un módulo (Pseudocódigo y Diagrama de Flujo)

Ejemplo 7.1: Suponiendo que queremos definir el módulo Suma encargado de sumar dos enteros,
entonces, los parámetros de entrada son x y y número enteros, y z=x+y sería el valor de retorno. La
definición del módulo quedaría de la siguiente forma.

Definición módulo Suma

Suma (x,y)
Inicio Suma(x,y)
Regresa x+y
Regresar x+y
Fin

Fin
Algoritmo 7.1. Definición del módulo Suma(x,y) (Pseudocódigo y Diagrama de Flujo)

¿Cómo se invoca a un módulo?

Como antes mencionamos, la llamada o invocación a un módulo es una instrucción que representa la
petición de un módulo (el controlador) a otro (el invocado) para que éste último realice la tarea que
tiene encomendada, la llamada incluye (si es el caso) un conjunto de valores de entrada específico;
en este sentido, la invocación de un módulo que devuelve un resultado puede ser equivalente con la
evaluación de una expresión, el resultado depende de los valores con que se ejecuta o evalúa,
respectivamente. Cuando un módulo invoca a otro decimos que le pasa el control.

Capítulo 7 | Página 5
Para representar la invocación de un módulo, tanto en pseudocódigo como en diagrama de flujo,
vamos a diferenciar entre los devuelve un valor y los que no.2 Los primeros serán manipulados igual
que las expresiones, es decir, aparecer en el lado derecho de una asignación (en el caso del diagrama
de flujo, encerrado en un símbolo de proceso – un rectángulo), en una condición (encerrada en un
rombo), en una impresión (encerrada en un paralelogramo), o incluso, aparecer dentro de una
expresión o como parámetros de otras funciones.3 Así que para indicar la llamada a un módulo sólo
se escriben: el nombre del módulo seguido de los valores de entrada, separados por comas y
encerrados entre paréntesis como se muestra en la Tabla 7.2.

Invocación de un módulo que tiene un valor de retorno


(Pseudocódigo y Diagrama de Flujo)
<nombreMódulo> ( <valor1>, ..,<valorN>)

Tabla 7.2. Forma general de la llamada a un módulo que devuelve un valor

Cabe mencionar que los parámetros que aparecen en la definición de un módulo se conocen como
parámetros formales, y como ya se mencionó son representados por variables; los parámetros que
aparecen en la llamada de un módulo se conocen como parámetros reales, ya que son los valores
reales que se asignan a los parámetros formales, es decir, son los datos con los que se ejecuta el
módulo. Los parámetros reales pueden ser cualquier expresión (variables, constantes o expresiones
compuestas) siempre y cuando sea del tipo correcto. Revisemos los siguientes ejemplos:

Pseudocódigo Diagrama de Flujo Descripción


En este caso a la variable
resultado se le asigna el
resultado ← Suma(3,2) resultado ← Suma(3,2) resultado de ejecutar el módulo
Suma con los valores 3 y 2, el
cual es 5

Imprimir “La suma es “, Imprimir “La suma es“, Suponiendo que x es igual a 3,
Suma(x,x) Suma(x,x) imprime en pantalla 6

Suponiendo, nuevamente, que x


resultado ← Suma(22, x) / 2 es 3 asigna a resultado el
resultado ← Suma(22, x) / 2
resultado de la división que
este caso sería 12.5
Aquí la llamada forma parte de
Si Suma(x,y) ≤ 0 entonces … Suma(x,y) ≤0
un expresión booleana
Tabla 7.3. Ejemplos de invocación a módulos que devuelven un valor (Pseudocódigo y Diagrama de Flujo)

2 Algunos autores, dependiendo del lenguaje de programación, a los módulos que no devuelven ningún valor les llaman procedimientos, y a
los que regresan un valor les llaman funciones. En lenguaje C, ambos son considerados funciones.

3 Existen funciones que se llaman a si misma, en tal caso se dice que es una función recursiva.

Capítulo 7 | Página 6
Para los módulos que no devuelven ningún valor existen representaciones especial en pseudocódigo y
diagrama de flujo, para el primero se antepone al nombre la frase “Llamar a”, mientras que para el
segundo existe un símbolo especial (ver Tabla 7.4). En este caso, no se pueden manipular como una
expresión ya que no hay ningún valor de retorno resultado de su ejecución.

Pseudocódigo Diagrama de Flujo


Pseudocódigo Diagrama de Flujo

Llamar a <nombreMódulo> ( <valor1>, <nombreMódulo>(<valor1>, ..,<valorN>)

..,<valorN>)
Tabla 7.4. Forma general de la llamada a un módulo que devuelve un valor

Suponiendo que hemos definido un módulo encargado de imprimir un menú (el algoritmo es una
serie de instrucciones Imprime “…”), llamado ImprimeMenu, que no recibe ningún parámetro,
entonces la llamada a ImprimeMenu se representa de las siguientes maneras:

Pseudocódigo Diagrama de Flujo

ImprimeMenu( )
Llamar a ImprimeMenu( )

Tabla 7.5. Ejemplo de una invocación a un módulo que no devuelve ningún valor

Observa que aunque el módulo no tenga parámetros de entrada es necesario escribir los paréntesis.

Para lograr un “buen” diseño modular debemos tomar en cuenta el grado de identificación del
módulo con una tarea específica, a lo cual se le conoce como cohesión. También debemos buscar
que los módulos sean independiente unos de otros, de tal manera que si modificamos un módulo no
implique tener que cambiar otro, lo cual sugiere que en el diseño evitemos que dos módulos puedan
modificar un mismo dato, la comunicación entre los módulos debe estar restringida a los parámetros
de entrada y el valor de retorno - a está medida de independencia se le llama acoplamiento. Por lo
tanto, un buen diseño se mide por un alto grado de cohesión y bajo acoplamiento.

Para ilustrar los conceptos expuestos, diseñaremos un algoritmo modular para el siguiente problema
paso a paso:

Ejemplo 7.2: Realiza el análisis y diseño de un programa que reciba tres puntos en el plano
cartesiano y determine forman un triángulo, en tal caso calcule: perímetro, área y tipo de triángulo.

Capítulo 7 | Página 7
Descripción del problema: El programa leerá las coordenadas de tres puntos en el plano,
representadas por (x1,y1), (x2,y2) y (x3,y3). Si forman un triángulo en el plano cartesiano entonces
calculará su perímetro y área, y determinará si es un triángulo escaleno, isósceles o equilátero. En
caso contrario enviará un mensaje para informar que los puntos no forman un triángulo en el plano
cartesiano.

Análisis: El objetivo del algoritmo general (la salida) es calcular el perímetro, área y tipo de
triángulo, a partir de conocer las coordenadas de los puntos, es decir (x1,y1), (x2,y2) y (x3,y3).
Podemos empezar por identificar las acciones abstractas que conformar la solución general (primer
refinamiento), para lo cual escribiremos un primer bosquejo del algoritmo principal.

1. Leer coordenadas de los puntos


2. Comprobar que los puntos formen un triángulo
3. Si los puntos forman un triángulo entonces
3.1. Calcular el perímetro
3.2. Calcular el área
3.3. Determinar el tipo de triángulo
3.4. Imprimir los resultados
4. Si no forman un triángulo, enviar un mensaje.

A partir de este bosquejo podemos dividir la solución general en los siguientes módulos:

 formanTriangulo, este módulo se encargará de determinar si los tres puntos dados definen un
triángulo en el plano cartesiano.
 perimetroTriangulo, encargado de calcular el perímetro de un triángulo.
 areaTriangulo, calcula el área de un triángulo.
 tipoTriangulo, determina si es un triángulo isósceles, escaleno o equilátero.

El siguiente paso es analizar por separado los módulos definidos (refinarlos hasta obtener un sub-
algoritmo).

Respecto al módulo formanTriangulo, observemos las siguientes figuras:

a) Los puntos forman un línea b) Los puntos forman una línea


horizontal. vertical.

Capítulo 7 | Página 8
c) Los puntos forman un línea d) No forman ninguna línea
diagonal

Figura 7.2. Tres puntos en el plano cartesiano

Como podemos ver en el Figura 7.2, la única forma de que tres puntos en el plano formen un
triángulo es que no estén sobre la misma línea, es decir, que no sean colineales.4 Para verificar que
tres puntos sean colineales podemos elegir cualquiera de ellos y calcular la pendiente de las rectas
que forma con cada uno de los otros puntos, si la pendiente es la misma, entonces se trata de la misma
recta (no pueden ser paralelas ya que comparten un punto), si son diferentes entonces no son
colineales. No debemos olvidar que si la recta es paralela al eje de Y, la pendiente está indeterminada
(es infinito ∞) así que debe ser tratada de manera especial. Como debemos calcular la pendiente de
dos rectas, lo cual es una tarea específica que se utiliza al menos dos veces, podemos definir un
módulo para ello.

 pendiente, calcula la pendiente de la recta formada por dos puntos dados.

Ahora analicemos el módulo perimetroTriangulo. Para calcular el perímetro de un triángulo


necesitamos conocer las medidas de los lados, dato que desconocemos; sin embargo, podemos
obtenerlo calculando la distancia que hay entre cada par de puntos, lo cual propone otro módulo.

 distancia, calcula la distancia que hay entre dos puntos del plano cartesiano.

Del mismo modo, para calcular el área de un triángulo sólo necesitamos conocer sus lados pues existe
una fórmula para determinar el área de un triángulo basada en los lados del triángulo y el
semiperímetro (fórmula de Herón de Alejandría). De otro modo, tendríamos que calcular su base
(medida de uno de los lados) y altura (se puede determinar mediante el teorema de Pitágoras). Ya que
el lado de los triángulos se necesita en diversos puntos del algoritmo es recomendable calcular la
medida en el módulo principal y pasarla como parámetros de entrada a los módulos correspondientes.

En la Tabla 7.6 se resume el análisis realizado.

4 Un conjunto de puntos es colonial si están sobre la misma recta (un conjunto de dos puntos siempre es colineal).

Capítulo 7 | Página 9
Análisis del problema
Datos de entada: Salida: Restricciones:
Coordenadas del Caso 1: (existe un triángulo)
punto 1: x1, y1 Perímetro del triángulo Ninguna
Coordenadas del Área del triángulo
punto 2: x2, y2 Tipo de triángulo Constantes:
Coordenadas del Caso 2: mensaje indicando que “No se forma un Ninguna
punto 3: x3, y3 triángulo pues los puntos son coliniales”

Diagrama modular

Principal

Forman Perímetro Área Tipo


Distancia
Triangulo Triángulo Triángulo Triángulo

Perímetro
Pendiente
Triángulo

Figura 7.6. Análisis y diagrama modular del Ejemplo 7.2 (triángulo en el plano)

Diseño: El siguiente paso es seguir con el refinamiento de cada uno de los módulos hasta obtener un
algoritmo general y cada uno de los sub-algoritmo.

Primero definamos de manera precisa la tarea que desarrollará cada módulo, identificando los
parámetros de entrada y su salida.

 formanTriangulo: dadas las coordenadas de los tres puntos (𝑥1, 𝑦1), (𝑥2, 𝑦2), (𝑥3, 𝑦3),
determina si forman un triángulo en el plano cartesiano, en tal caso regresa 1, de lo contrario
devuelve 0.

 perimetroTriangulo, calcula el perímetro de un triángulo, los parámetros de entrada que


requiere son las medidas de los lados: 𝑎, 𝑏 y 𝑐, la salida es 𝑝𝑒𝑟í𝑚𝑒𝑡𝑟𝑜 = 𝑎 + 𝑏 + 𝑐.

Capítulo 7 | Página 10
 areaTriangulo, calcula el área de un triángulo (utilizando la fórmula de Zerón), los
parámetros de entrada que requiere son las medidas de los lados: 𝑎, 𝑏 y 𝑐, la salida es 𝑎𝑟𝑒𝑎 =
𝑠 (𝑠 − 𝑎)(𝑠 − 𝑏)(𝑠 − 𝑐) , donde 𝑠 es el semiperímetro.

 distancia, los únicos parámetros son los valores de 𝑥1, 𝑦1, 𝑥2 y 𝑦2 que corresponden a las
coordenadas de los puntos P1 y P2, lo que regresa el módulo es la distancia entre ellos.

 tipoTriangulo, Determina qué tipo de triángulo es e imprime en pantalla el mensaje


correspondiente (isósceles, escaleno o equilátero), los parámetros de entrada son los lados del
triángulo.

 pendiente, para calcular la pendiente necesitamos conocer las coordenados de dos puntos que
formen parte de la recta (𝑥1, 𝑦1) y (𝑥2, 𝑦2)

Ahora definamos los sub-algoritmo, correspondientes a cada módulo, iniciando con el módulo
pendiente.

Módulo: pendiente

La forma para calcular la pendiente una recta dados dos puntos es 𝑃1 = (𝑥1, 𝑦1)y 𝑃2 = (𝑥2, 𝑦2)

𝑦2 − 𝑦1
𝑚=
𝑥2 − 𝑥1

El único caso donde no es posible calcular la pendiente es cuando 𝑥2 es igual a 𝑥1 (implicaría


realizar una división entre cero), en este caso se dice que la pendiente está indefinida y se representa
con infinito (∞). El inconveniente que tenemos en computación es que no podemos representar el
infinito; así que no contemplaremos esta situación en la definición del módulo, por lo tanto, se
tendrán que verificar que las coordenadas en el eje X sean distintas antes de invocar el módulo.

Capítulo 7 | Página 11
Análisis y Diseño de la función pendiente(x1, y1, x2, y2)

Descripción:
Dadas las coordenadas de dos puntos en el plano cartesiano la función calcula la
pendiente de la recta que pasa por los puntos (x1,y1) y (x2,y2).

Parámetros de entrada:
 x1(decimal): Coordenada del eje de las abscisas del punto1.
 y1(decimal): coordenada del eje de las ordenadas del punto1
 x2(decimal): coordenada del eje de las abscisas del punto2.
 y2(decimal): coordenada del eje de las ordenadas del punto2.

Valor de retorno:
 m: devuelve la pendiente

Método:
y 2  y1
m
x 2  x1

Módulos:
No requiere de ningún otro módulo

Algoritmo (Diagrama de flujo) Algoritmo (Pseudocódigo)


Inicio pendiente(x1,y1,x2,y2)
pendiente(x1,y1,x2,y2)
𝑚 ←
𝑦2 − 𝑦1
𝑚 ←
𝑥2 − 𝑥1 Regresar m

Fin
Regresar m

Fin

Algoritmo 7.2. Definición del módulo pendiente(x1,y1,x2,y2) (continuación)

Para comprobar que nuestro módulo funciona correctamente, supongamos que se hizo la siguiente
llamada: pendiente(0,0,5,1).

Al invocar al módulo se hace una asignación implícita, los valores con los que se hace la llamada se
asignan a las variables que representan los parámetros de entrada en el mismo orden en el que se

Capítulo 7 | Página 12
definieron en el módulo. Así que el estado de las variables del módulo pendiente al inicio de la
ejecución sería el siguiente:
x1 y1 x2 y2 m
0 0 5 1 -

Posteriormente, al sustituir estos valores en la fórmula obtenemos:

𝑦2 − 𝑦1
𝑚 ←
𝑥2 − 𝑥1
1−0
𝑚 ←
5−0

𝑚 ← 0.2

De tal manera que el estado de las variables antes de regresar el valor (y con ello terminar la
ejecución del módulo) es:
x1 y1 x2 y2 m
0 0 5 1 0.2

Por lo tanto, el resultado de la llamada pendiente(0,0,5,1) es 0.2.

Módulo: formaTriangulo

El siguiente sub-algoritmo que definiremos es formaTriangulo, cómo vimos en el análisis la única


forma de que tres puntos formen un triángulo es que no sean colineales. Entonces, sin pérdida de
generalidad escogeremos el punto 1 como pivote y calcularemos las pendientes de las rectas que
forma con el punto 2 y con el punto 3, si son distintas regresamos 1 (forman un triángulo), en caso
contrario 0; de este modo resolvemos para los casos en que ninguna de las rectas es paralela al eje Y.
Los casos de las rectas verticales (rectas con pendiente indefinida) deben tratarse por separado,
verificando los dos posibles casos: 1) tienen la misma recta (ambas con pendiente infinita, los tres
puntos tienen la misma coordenada X); 2) las rectas que forman son diferentes (sólo una tiene
pendiente infinita).

Capítulo 7 | Página 13
Análisis y Diseño de la función: formanTriangulo(x1,y1,x2,y2,x3,y3)

Descripción: Dadas las coordenadas de tres puntos en el plano cartesiano determina si forman
un triángulo.

Parámetros de entrada:
 x1 y y1: Coordenadas del eje X yY, respectivamente, del punto1.
 x2 y y2: Coordenadas del eje X yY, respectivamente, del punto2.
 X3 y y3: Coordenadas del eje X yY, respectivamente, del punto3.

Parámetros de salida y valor de retorno:


 1 si los puntos forman un triángulo
 0 en caso contrario

Método:
Verificamos que las rectas formadas por los puntos (x1,y1)-(x2,y2) y por (x1,y1)-(x3,y3) no
sean verticales, en tal caso calculamos la pendiente para saber si son la misma. Si ambas tienen
pendiente indefinida, son la misma y por lo tanto no regresan forman ningún triángulo.

Módulos: Utiliza el módulo pendiente(x1,y1,x2,y2)


Algoritmo (Pseudocódigo)
Inicio formanTriangulo(x1,y1,x2,y2,x3,y3)
Si x1≠x2  x1≠x3 entonces /*no forman rectas verticales*/
m1←pendiente(x1,y1,x2,y2)
m2←pendiente(x1,y1,x3,y3)
Si m1 = m2
triangulo ← 0
Sino
triangulo ← 1
Fin Si-Sino
Sino
Si x1=x2  x1=x3 entonces /* los tres pertenecen a una recta paralela al eje Y*/
triangulo ← 0
Sino /* sólo dos están en una recta paralela a Y */
triangulo ← 1
Fin Si-Sino
Fin Si-Sino
Regresa triangulo
Fin
Algoritmo 7.3. Definición del módulo formanTriangulo(x1,y1,x2,y2,x3,y3)

Capítulo 7 | Página 14
Algoritmo (Diagrama de flujo)

formanTriangulo(x1,y1,x2,y2,x3,y3)

x1≠x2  x1≠x3
V F

m1←pendiente (x1,y1,x2,y2)
x1=x2  x1=x3
m2←pendiente (x1,y1,x3,y3)
V F

triangulo ← 0 triangulo ← 1
m1 = m2
V F

triangulo ← 0 triangulo ← 1

Regresar triangulo

Fin

Algoritmo 7.3. Definición del módulo formanTriangulo(x1,y1,x2,y2,x3,y3) (continuación)

Para comprobar que nuestro algoritmo funciona revisemos un ejemplo de cada uno de los posibles
casos:
a) Si graficamos los puntos (0,0),(5,1) y (1,4) nos daremos cuanta que forman un triángulo.
Realizando la llamada formanTriangulo(0,0,5,1,1,4), el estado de las variables al inicio de la
ejecución del módulo es:
x1 y1 x2 y2 x3 y3 triangulo m1 m2
0 0 5 1 1 4 - - -

Con estos valores la condición x1≠x2  x1≠x3, se cumple, así que invoca al módulo
pendiente dos veces:
- m1←pendiente (x1,y1,x2,y2), según el estado de las variables esta llamada equivale a
pendiente(0,0,5,1), la cual regresa 0.2 (m1 almacena 0.2).
- m2←pendiente (x1,y1,x3,y3), la llamada es pendiente(0,0,1,4), ejecutando el módulo
sabremos que el resultado es 4 (m2 es 4)

Capítulo 7 | Página 15
Ya que m1 es distinto de m2 (0.2≠4), entonces no se cumple la condición m1=m2, así que el
valor que se asigna a triangulo es 1. El estado de las variables al final de la ejecución es:
x1 y1 x2 y2 x3 y3 triangulo m1 m2
0 0 5 1 1 4 1 0.2 4

El resultado de la llamada formanTriangulo(0,0,5,1,1,4) es 1.

b) Los punto (0,2),(5,2) y (1,2) están sobre una línea recta paralela al eje Y. La llamada a la
función sería formanTriangulo(0,2,5,2,1,2).
El estado de las variables del módulo al inicio de la ejecución sería:
x1 y1 x2 y2 x3 y3 triangulo m1 m2
0 2 5 2 1 2 - - -

En este caso no se cumple la condición x1≠x2  x1≠x3, pero si se cumple x1=x2  x1=x3, así
que el valor que se asigna a triangulo es 0, entonces el estado de las variables antes de regresar
el valor es:
x1 y1 x2 y2 x3 y3 triangulo m1 m2
0 2 5 2 1 2 0 - -

Observa que los valores de m1 y m2 siguen indefinidos, nunca se hizo ninguna asignación que
los afectara.
El resultado de la llamada formanTriangulo(0,2,5,2,1,2) es 0.

Capítulo 7 | Página 16
Módulo distancia

El refinamiento de este módulo no implica gran ciencia, sólo hay que aplicar un fórmula y ¡listo!

Análisis y Diseño de la función: distancia(x1, y1, x2, y2)


Descripción: Dadas las coordenadas de dos puntos en el plano cartesiano calcula la distancia
entre ellos.

Parámetros de entrada:
 x1(decimal): coordenada del eje de las abscisas del punto1.
 y1(decimal): coordenada del eje de las ordenadas del punto 1.
 x2(decimal): coordenada del eje de las abscisas del punto 2.
 y2(decimal): coordenada del eje de las ordenadas del punto 2.

Retorno:
 dist: devuelve la distancia entre los puntos (x1,y1) y (x2,y2)

Método:
𝑑𝑖𝑠𝑡 = (𝑥2 − 𝑥1) + (𝑦2 − 𝑦1)

Módulos:
No requiere de ningún otro módulo
Algoritmo (Diagrama de flujo) Algoritmo (Pseudocódigo)

distancia(x1,y1,x2,y2)
Inicio distancia(x1,y1,x2,y2)
𝑑𝑖𝑠𝑡 ← (𝑥2 − 𝑥1) + (𝑦2 − 𝑦1)
𝑑𝑖𝑠𝑡 ← (𝑥2 − 𝑥1) + (𝑦2 − 𝑦1)

Regresar dist
Regresar dist
Fin
Fin

Algoritmo 7.4. Definición del módulo distancia(x1,y1,x2,y2) (continuación)

Capítulo 7 | Página 17
Suponiendo que se invocara al módulo distancia(0,0,5,1); el estado de las variables al inicio de esta
ejecución sería:
x1 y1 x2 y2 dist
0 0 5 1 -

Al evaluar la expresión considerando este estado de las variables tenemos:

𝑑𝑖𝑠𝑡 ← (𝑥2 − 𝑥1) + (𝑦2 − 𝑦1)

𝑑𝑖𝑠𝑡 ← (5 − 0) + (1 − 0)

𝑑𝑖𝑠𝑡 ← 5.1

El valor que se almacena en dist es 5.1, que es el mismo que se devuelve.

Continuemos ahora con el módulo encargado de calcular el perímetro de un triángulo a partir de las medidas
de sus tres lados a,b y c.

Módulo perimetroTriangulo
Ya que el módulo recibe las medidas de los lados, se vuelve trivial la definición del algoritmo.

Análisis y Diseño de la función: perimetroTriangulo(a,b,c)

Descripción: Dadas las medidas de los lados de un triángulo calcula su perímetro.

Parámetros de entrada: Medida de los tres lados que conforman el triángulo


 a (decimal): longitud del lado uno.
 b (decimal): longitud del lado dos.
 c (decimal): longitud del lado tres.

Parámetros de Salida y valor de retorno:


 p: devuelve el perímetro del triángulo con lados a,b y c.

Método:
p  abc
Módulos:
Ninguno

Capítulo 7 | Página 18
Algoritmo (Diagrama de flujo) Algoritmo (Pseudocódigo)
Inicio perimetroTriangulo(a,b,c)
perimetroTriangulo(a,b,c)
p  abc

𝑝 ← 𝑎+𝑏+𝑐 Regresa p
Fin

Regresar p

Fin

Algoritmo 7.5. Definición del módulo perimetroTriangulo(a,b,c)


No es necesario, probar que funciona.

Módulo areaTriangulo

Sigamos con el diseño del módulo encargado de calcular el área de un triángulo a partir de las
medidas de sus tres lados, para lo cual utilizaremos la fórmula de Herón de Alejandría.

Análisis y Diseño de la función: areaTriangulo(a,b,c)

Descripción: Dadas las medidas de los lados de un triángulo calcula el área.

Parámetros de entrada: Medida de los tres lados que conforman el triángulo


 a (decimal): longitud del lado uno.
 b (decimal): longitud del lado dos.
 c (decimal): longitud del lado tres.

Parámetros de salida y valor de retorno:


 área: devuelve el área del triángulo con lados a,b y c.

Método:
area  s( s  a )( s  b)( s  c)

Donde s es el semi perímetro que se calcula como sigue:

abc
s
2
Módulos utilizados:
perimetroTriangulo, utilizado para calcular el semiperimetro.

Capítulo 7 | Página 19
Algoritmo (Diagrama de flujo) Algoritmo (Pseudocódigo)
Inicio areaTriangulo(a,b,c)
decimal areaTriangulo(a,b,c)
𝑝𝑒𝑟𝑖𝑚𝑒𝑡𝑟𝑜𝑇𝑟𝑖𝑎𝑛𝑔𝑢𝑙𝑜(𝑎, 𝑏, 𝑐)
𝑠←
( , , )
2
𝑠←
𝑎𝑟𝑒𝑎 ← 𝑠(𝑠 − 𝑎)(𝑠 − 𝑏)(𝑠 − 𝑐)

Regresa area
𝑎𝑟𝑒𝑎 ← 𝑠(𝑠 − 𝑎)(𝑠 − 𝑏)(𝑠 − 𝑐) Fin

Regresar area

Fin

Algoritmo 7.6. Definición del módulo areaTriangulo(a,b,c)

En este módulo podemos ver claramente la forma en la cual un módulo invoca a otro, areaTriangulo
realiza una llamada al módulo perimetroTriangulo y los valores de los parámetros de entrada están
definidos por el valor de las variables a,b y c. Por ejemplo, si invocamos el módulo areaTriangulo con
los parámetros (5, 5.1, 4.1), los valores que toman las variables a, b y c son:
a b c
5 5.1 4.1

De tal modo que la ejecución de la instrucción


𝑝𝑒𝑟𝑖𝑚𝑒𝑡𝑟𝑜𝑇𝑟𝑖𝑎𝑛𝑔𝑢𝑙𝑜(𝑎, 𝑏, 𝑐)
𝑠←
2
Implica pasarle el control al módulo perimetroTriangulo para que realice su tarea con los parámetros
(5, 5.1, 4); el resultado de esta llamada es 7.1, hasta este punto el estado de las variables es:
a b c s area
5 5.1 4.1 7.1 -

La siguiente instrucción que se ejecuta es:

𝑎𝑟𝑒𝑎 ← 𝑠(𝑠 − 𝑎)(𝑠 − 𝑏)(𝑠 − 𝑐)

Sustituyendo tenemos
𝑎𝑟𝑒𝑎 ← 7.1(7.1 − 5)(7.1 − 5.1)(7.1 − 4.1)

𝑎𝑟𝑒𝑎 ← 9.5
Así que el resultado de areaTriangulo(5, 5.1, 4.1) es 9.5 (valor de la variable area).

Capítulo 7 | Página 20
Módulo tipoTriangulo

El siguiente módulo es tipoTriangulo el cual imprime en pantalla el tipo de triángulo que forman los lados a, b
y c. El módulo no devuelve ningún resultado su tarea se reduce a imprimir si el triángulo es isósceles, escaleno
o equilátero.

Análisis y Diseño del procedimiento: tipoTriangulo(a,b,c)

Descripción: Dados los lados de un triángulo imprime un mensaje indicando si es un triángulo


equilátero, isósceles o escaleno.

Parámetros de entrada: Medida de los tres lados que conforman el triángulo


 a (decimal): longitud del lado uno.
 b (decimal): longitud del lado dos.
 c (decimal): longitud del lado tres.

Parámetros de salida y valor de retorno:


 ninguno

Método:
Compara la longitud de los lados: 3 lados iguales es equilátero, 2 lados iguales isósceles,
ninguno de los anteriores es escaleno.

Módulos:
Ninguno.

Capítulo 7 | Página 21
Algoritmo (Diagrama de flujo) Algoritmo (Pseudocódigo)
Inicio tipoTriangulo(a,b,c)
tipoTriangulo(a,b,c)
Si a=b  b=c entonces
Imprimir “equilátero”
a=b  b=c
V F Sino
Si a=b  a=c  b=c entonces
a=b  a=c  b=c
Imprimir Imprimir “isósceles”
“equilá V F
tero” Sino

Imprimir Imprimir
Imprimir “escaleno”
“isósceles” “escaleno” Fin Si-Sino
Fin Si-Sino
Fin

Fin

Algoritmo 7.7. Definición del módulo areaTriangulo(a,b,c)

La prueba de escritorio de este módulo se deja como ejercicio al lector.

Ahora sólo nos falta refinar el algoritmo principal.


Diseño del Módulo Principal

Datos de entrada:

o X1 (decimal): almacena la coordenada X del punto 1.


o Y1 (decimal): almacena la coordenada Y del punto 1.
o X2 (decimal): almacena la coordenada X del punto 2.
o Y2 (decimal): almacena coordenada Y del punto 2.
o X1 (decimal): almacena la coordenada X del punto 3.
o Y3 (decimal): almacena coordenada Y del punto 3.

Módulos:
Utiliza los módulos, distancia(x1,y1,x2,y2), perimetroTriangulo(a,b,c),
areaTriangulo(a,b,c) y formaTriángulo(x1,y1,x2,y2,x3,y3).

Constantes:
Ninguna

Capítulo 7 | Página 22
Algoritmo(Pseudocódigo):

Inicio
Imprimir “Proporciona las coordenadas del primer punto (x1,y1)”
Leer x1,y1
Imprimir “Proporciona las coordenadas del primer punto (x2,y2)”
Leer x2,y2
Imprimir “Proporciona las coordenadas del primer punto (x3,y3)”
Leer x3,y3
Si formanTriangulo(x1,y1,x2,y2,x3,y3) = 1 entonces /*Si es un triángulo*/
a ← distancia(x1,y1,x2,y2)
b ← distancia(x2,y2,x3,y3)
c ← distancia(x1,y1,x3,y3)
Imprimir “Perímetro = ”, perimetroTriangulo(a,b,c)
Imprimir “Área=”, areaTriangulo(a,b,c);
Imprimir “El triángulo es”
tipoTriangulo(a,b,c)
Sino
Imprimir “Los puntos no forman un triángulo”
Fin Si-Sino
Fin

Algoritmo 7.7. Definición del algoritmo triángulo en el plano cartesiano (pseudocódigo)

La representación del algoritmo en diagrama de flujo se muestra a continuación, para enfatizar las
diferencias entre los módulos que devuelven un valor y los que no.

Capítulo 7 | Página 23
Diseño del Módulo Principal

Inicio

Imprimir “Proporciona (x1,y1)”

Leer x1, y1

Imprimir “Proporciona (x2,y2)”

Leer x2, y2

Imprimir “Proporciona (x3,y3)”

Leer x3, y3

formanTriangulo(x1,y1,x2,y2,x3,y3) =
V 1 F

a ← distancia(x1,y1,x2,y2)
b ← distancia(x2,y2,x3,y3)
c ← distancia(x1,y1,x3,y3)

Imprimir “Perímetro =”,


Imprimir “No se forma un
perimetroTriangulo(a,b,c)
triángulo”

Imprimir “Area =”,


areaTriangulo(a,b,c)

Imprimir “El triángulo es ”

tipoTriangulo(a,b,c)

Fin

Algoritmo 7.8. Definición del algoritmo triángulo en el plano cartesiano (diagrama de flujo)

Capítulo 7 | Página 24
Si hacemos un prueba de escritorio con los puntos (0,0),(5,1) y (1,4), el estado de la memoria después
de leer los datos de entrada sería:
x1 y1 x2 y2 x3 y3 a b c
0 0 5 1 1 4 - - -

1 El resultado de formaTriangulo(0,0,5,1,1,4) es 1 (hicimos la prueba de escritorio), así que la


condición se cumple.
2 El resultado de la llamada distancia(0,0,5,1) es 5.1
3 El resultado de distancia(5,1,1,4) es 5
4 El resultado de distancia(0,0,1,4) es 4.1
5 El resultado de perímetroTriangulo con los parámetros (5.1,5,4.1) es 14.2, así que el primer
resultado que se imprime es “Perimetro = 14.2”
6 Antes hicimos la prueba de escritorio para la llamada areaTriangulo(5.1,5,4.1) y el resultado fue
9.5. El siguiente mensaje en pantalla será “Area = 9.5”
7 Seguido de “El triángulo es”
8 Cuando se ejecuta el módulo tipoTriangulo con los parámetros (5.1,5,4.1), no se cumple ninguna
de las condiciones así que el mensaje que se imprime es “escaleno”

En resumen, la salida del programa es:


Perimetro = 14.2
Area = 9.5
El triángulo es escaleno

Y el estado de las variables al final de la ejecución del módulo es el siguiente:


x1 y1 x2 y2 x3 y3 a b c
0 0 5 1 1 4 5.1 5 4.1

Si los datos de entrada fueran los puntos (0,2),(5,2) y (1,2)


x1 y1 x2 y2 x3 y3 a b c
0 2 5 2 1 2 - - -

La invocación formaTriangulo(0,2,5,2,1,2) regresa 0 (lo probamos anteriormente), así que la


condición no se cumple y la salida sería:
No se forma un triángulo

El estado de las variables permanece igual al final de la ejecución.

Capítulo 7 | Página 25
7.3 PROGRAMACIÓN MODULAR EN C
La programación modular es un paradigma que consiste en dividir un programa en módulos (sub-
programas). En el lenguaje C los módulo se llaman funciones, y cualquier programa escrito en este
lenguaje está constituido a base de funciones; el programa principal no es la excepción, la palabra
reservada main indica el inicio de la función principal. Una función en C se puede definir como una
porción de código (un conjunto de instrucciones agrupadas por separado) enfocado a realizar una
tarea en específico.

Al igual que la función principal cada una de las funciones tiene sus propias variables, instrucciones
(delimitadas por llaves), datos de entrada y salida, además de un identificador (que debe cumplir las
mismas reglas que los nombres de las variables),

Las funciones en lenguaje C se pueden clasificar en dos tipos: funciones estándar y funciones
definidas por el usuario (programador). Las primeras las ofrece el lenguaje de programación (están
previamente definidas) y, se agrupan según su tarea y/o tipos de datos que manejan en diversos
archivos llamados bibliotecas estándar. En el caso del lenguaje C existen más de quinientas funciones
de biblioteca estándar ANSI (American National Estándar Institute), por ejemplo: printf y scanf, las
cuales forman parte de la biblioteca stdlib.h; sqrt y pow que se encuentran en la biblioteca math.h; las
funciones definidas por el programador también pueden agruparse en un solo archivo para que las
pueda utilizar en cualquier programa (como lo ilustraremos en el siguiente capítulo)5, con lo cual se
favorece la reutilización de código.

Al igual que los módulos, las funciones tienen dos vertientes: una es la definición y la otra la llamada
(ejecución). En C todo debe ser definido antes de utilizarse, las funciones no son la excepción. Para
que el compilador no marque ningún error, antes de cualquier llamada a una función debe haberse
definido o en su defecto declarado. La declaración de una función es similar a la de una variables, se
debe escribir el tipo de función (es decir, el tipo del valor de retorno), el nombre de la función y los
tipos parámetros de entrada (indicando su tipo). En las siguientes secciones se describe la sintaxis de
la definición, declaración e invocación de una función en lenguaje C.

7.3.1 Definición de funciones en C


En C la sintaxis para la definición de una función es la siguiente:

Definición de una función en C


<tipo dato retorno> <identificador de la función> (<lista de argumentos>)
{
<cuerpo de la funcion>

return <expresión>; /*Opcional pues hay funciones que no devuelven ningún valor*/
}
Tabla 7.7. Sintaxis para definir una función en C

5 Si deseas ahondar en el tema te recomendamos consultar [Joyanes???] o [Deitel???].

Capítulo 7 | Página 26
En donde,

<tipo dato retorno>: Indica el tipo de dato que la función va a devolver, puede ser un tipo básico
(int,char,double,float) o un apuntador;6 cuando la función no devuelve ningún resultado el tipo se pone
como void.

<identificador de la función: El identificador de la función es el nombre mediante el cual la vamos a


llamar. Sigue las mismas reglas que los identificadores de las variables y se recomienda que se
nemotécnico.

<lista de parámetros>: La lista contiene el tipo e identificador de cada uno de los parámetros de
entrada, separándolos por comas, se debe especificar explícitamente el tipo y nombre para cada uno
de los posibles datos de entrada aun cuando sean del mismo tipo, separados por comas; por ejemplo,
si la función tiene N parámetros, la lista sería:

<tipo1> <param1>, <tipo2> <param2>, …, <tipoN> <paramN>

Sin olvidar nunca los paréntesis al inicio y fin. En caso de que la función no recibiera ningún
parámetro de entrada, también se escriben los paréntesis (opcionalmente se puede escribir la palabra
reservada void en medio de ellos).

<cuerpo de la función>: Es el conjunto de instrucciones que definen las operaciones que la función
debe realizar, van encerradas entre llaves. El cuerpo de la función incluye la declaración de las
variables que se usarán en el módulo (llamadas variables locales). Cuando la función tiene algún
valor de retorno se utiliza la palabra reservada return (equivale a la instrucción Regresa).

Ejemplo 7.3: Definamos en lenguaje C, el módulo Suma(x,y) descrito en el Algoritmo 7.1, salvo que
en la definición que presentamos almacenamos el resultado de la suma en la variable s, y después
devolvemos el valor de s. Para definir en lenguaje C la función, tenemos que decidir qué tipo de datos
se reciben. Suponiendo que son decimales, tendríamos:

Definición de la función Suma(x,y) en lenguaje C


double suma(double x, double y)
{
return x+y;
}

Programa 7.1. Función Suma(x,y)

6Un apuntador es una dirección de memoria donde se encuentra almacenado un dato, según el tipo de dato será el tipo del apuntador. Para
profundizar en el tema se recomienda [Joyanes??] o [Deitel???]

Capítulo 7 | Página 27
La primera línea de la definición de una función se conoce como encabezado de la función (header),
y es justo lo que se necesita para declararla.

7.3.2 Declaración de funciones en C (Prototipos)


Como antes mencionamos, las funciones deben estar definidas o declarada antes de utilizarse (invocarse). La
declaración de una función se realiza escribiendo el encabezado de la función terminando con punto y como
(;), a esto se le llama prototipo de la función. El prototipo sólo indica al compilador que tipo y número de
datos recibe, y que tipo de dato regresa (es un modelo de la función), más no que acciones realiza, así que se
debe definir en algún momento. En la Tabla 7.8, se muestra la forma general del prototipo de una función:

Definición de una función en C

<tipo dato retorno> <identificador de la función> (<lista de argumentos>) ;

Tabla 7.7. Sintaxis para definir una función en C

Por ejemplo, la declaración de la función suma es:

double suma(double x, double y);

En el prototipo de un función no es necesario escribir las identificadores, basta con especificar su tipo, por
ejemplo:
double suma(double, double);

7.3.3 Llamada a una función en C


La llamada o invocación a una función se realiza cuando queremos que se ejecuten las instrucciones
que la conforman con valores de entrada determinados. La sintáxis general para invocar una función
ya sea predefinida o definida por el programador, es la siguiente:

Llamada de una función en C

<identificador de la función> (<valor1>,<valor2>,…,<valorN>)

Tabla 7.8. Sintaxis para llamar una función en C

Como puedes observar es idéntica al pseudocódigo y diagrama de flujo, se tiene que escribir el nombre de la
función seguido de la lista de los parámetros de entrada encerrados entre paréntesis, respetando el
mismo orden que en la definición. Por ejemplo, una llamada incorrecta a la función printf sería
printf(resultado, “El valor de resultado es %.2f”), ya que el primer argumento esperado debe ser una
cadena no una variable.

Capítulo 7 | Página 28
 Información de interés.

No debes olvidar que los parámetros de entrada de una función son valores: valores constantes (8,’a’, 5.2,
“cadena constante”), o el valor almacenado en una variable pues aunque la llamada a la función sea con una
variable (sqrt(x) ) lo que realmente se pasa a la función es el valor almacenado en x. Finalmente, un valor
también puede estar disfrazado por una expresión, imagina que se realiza la siguiente llamada sqrt(2*x),
suponiendo que el valor de x es 12.5, el parámetro de entrada real 25.

Ahora estamos listos para implementar en C los algoritmos propuestos en el Ejemplo 3.2. En esta
ocasión primero definiremos la función principal (main) antes que las funciones secundaria, pero
debemos declarar las funciones antes para que el compilador no marque error.

El tipo de datos que utilizaremos para las coordenadas será float.

Capítulo 7 | Página 29
Ejemplo 7.2(continuación) : Codificación del algoritmo triángulos en el plano

/*trianguloPlano.c: Dados tres puntos en el plano cartesiano


determina si forman un triángulo y calcula el
área, perímetro y tipo de triangulo*/

/* Bibliotecas*/
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

/* Declaración de las funciones */

/* pendiente: calcula la pendiente de la recta formada por dos


puntos,
(x1,y1) y (x2,y2)*/
float pendiente(float x1, float y1, float x2, float y2);

/* formaTriangulo: verifica si tres puntos (x1,y1), (x2,y2) y


(x3,y3)
forman un triángulo */
short formanTriangulo(float x1, float y1, float x2, float y2,
float x3, float y3);

/* distancia: calcula la distancia entre los puntos (x1,y1) y


(x2,y2)*/
float distancia(float x1, float y1, float x2, float y2);

/* perimetroTriangulo: calcula el perímetro de un triángulo dadas


las medidas de sus lados a, b y c */
float perimetroTriangulo(float a, float b, float c);

/* areaTriangulo: calcula el área de un triángulo a partir de la


medida
de sus lados a, b y c*/
float areaTriangulo(float a, float b, float c);

/* tipoTriangulo: Dados los tres lados de un triangulo a,b y c


imprime
que tipo de triangulo que es (isoceles, escaleno o
equilatero)*/
void tipoTriangulo(float a, float b, float c);

Programa 7.4. triángulo.c (Parte 1: declaración de las funciones)

Capítulo 7 | Página 30
/**************** Funcion principal ***************/
main()
{ /*Declaración de variables locales a main */
float x1, y1, x2, y2, x3, y3;
float a, b, c;
/*Mensaje informativo*/
printf("\n El siguiente programa calcula el area, perimetro
y tipo de \ntriangulo formado por tres puntos en el plano
cartesiano\n");
/*Datos de entrada*/
printf("\nProporciona el punto 1 (x1,y1): ");
fflush(stdin); /*Limpia el buffer*/
scanf("(%f,%f)",&x1,&y1);
printf("\nProporciona el punto 2 (x2,y2): ");
fflush(stdin); /*Limpia el buffer*/
scanf("(%f,%f)",&x2,&y2);
printf("\nProporciona el punto 3 (x3,y3): ");
fflush(stdin); /*Limpia el buffer*/
scanf("(%f,%f)",&x3,&y3);

/* Verificamos que los puntos no formen un triángulo,


haciendo una llamada a la función triángulo */
if(formanTriangulo(x1,y1,x2,y2,x3,y3) == 1)
{ /* Si forman un triángulo, el siguiente paso es calcular
la medida de los lados del triangulo y calcular su
perímetro, área y tipo, utilizando las funciones
respectivas */

/* Llamadas a la función distancia */


a = distancia(x1,y1,x2,y2);
b = distancia(x2,y2,x3,y3);
c = distancia(x1,y1,x3,y3);

/* Impresión del resultado de la función perímetro con los


argumentos a,b y c */
printf("\n\n\n*** Resultados ***”);
printf("\n Perimetro = %0.2f",perimetroTriangulo(a,b,c));
printf("\n Area = %0.2f",areaTriangulo(a,b,c));
printf("\n El triangulo es ");
tipoTriangulo(a,b,c);
}
else { /* no forman un triángulo */
printf("\n Los puntos no forman un triángulo ");
}
printf("\n\n\t");
system("pause");

} /*fin main */
Programa 7.4. triángulo.c (función principal main)

Capítulo 7 | Página 31
/************ Definición de funciones ********/

/*Definición de la función pendiente*/


float pendiente(float x1, float y1, float x2, float y2)
{
float m; /* variable local a pendiente */
m = (y2-y1)/(x2-x1);
return m;
}/*fin pendiente*/

/*Definición de la función formaTriangulo*/


short formanTriangulo(float x1, float y1, float x2, float y2,
float x3, float y3)
{ /* variables locales a formaTriangulo */
short triangulo;
float m1,m2;

if(x1 != x2 && x1 != x3)


{ /*los segmentos x1-x2 y x1-x3 no son paralelos al eje Y*/

m1=pendiente (x1,y1,x2,y2);
m2=pendiente (x1,y1,x3,y3);

if(m1 == m2) /* son coliniales, no forman un triángulo */


triangulo = 0;
else /* forman un triángulo */
triangulo = 1;
}
else if(x1==x2 && x1==x3)
{ /* los tres puntos están en una recta paralela a Y*/
triangulo = 0;
}
else
{ /* sólo dos puntos están en una recta paralela a Y*/
triangulo = 1;
}
return triangulo;

}/*fin formanTriangulo*/

Programa 7.4. triángulo.c (definición de funciones)

Capítulo 7 | Página 32
/*Definición de la función distancia*/
float distancia(float x1, float y1, float x2, float y2)
{
float dist; /* variable local a distancia */
dist = sqrt( pow(x2-x1,2) + pow(y2-y1,2));
return dist;
} /*fin distancia */

/*Definición de la función perimetroTriangulo*/


float perimetroTriangulo(float a, float b, float c)
{
float p; /* variable local a perimetroTriangulo */
p = a+b+c;
return p;
} /*fin perimetroTriangulo*/

/*Definición de la función areaTriangulo*/


float areaTriangulo(float a, float b, float c)
{
float s,area; /* variables locales a areaTriangulo */
/* Llama a perimetroTriangulo para calcular el
semi-perimetro*/
s = perimetroTriangulo(a,b,c)/2;

area = sqrt(s*(s-a)*(s-b)*(s-c));
return area;
}/*fin areaTriangulo*/

/*Definición de la función tipoTriangulo*/


void tipoTriangulo(float a, float b, float c)
{
if(a== b && b== c)
printf("equilatero");
else if (a==b || a== c|| c== b)
printf("isoceles");
else
printf("escaleno");
} /*fin tipoTriangulo*/

Programa 7.4. triángulo.c (definición de funciones)

Capítulo 7 | Página 33
Ejecución del programa

Figura 7.4. Ejecución del programa pasoPorReferencia

7.6 VENTAJAS DEL DISEÑO DESCENDENTE

Con el ejemplo anterior resaltan indudablemente varias ventajas que proporciona un diseño modular:
1. Es posible reutilizar código, es indudablemente una de las más importantes ya que no es
necesario escribir el mismo código cada vez que deseamos realizar una tarea semejante. Por
ejemplo, el módulo distancia fue utilizado tres veces a lo largo de toda la solución del
problema y sólo bastó definirlo una vez.
2. Fácil detección y corrección de errores, dado que el problema fue divido en pequeños
módulos cada uno responsable de una tarea, si en algún momento apareciera un error en la
solución global, basta identificar cuál de las tareas es la que no se está resolviendo
adecuadamente y corregir únicamente aquellos módulos que la involucren.
3. Fácil modificación o extensión, si se requiere modificar o agregar una tarea, no será necesario
rediseñar todo el algoritmo, basta con hacer las modificaciones en los módulos
correspondiente.
4. Un problema se vuelve más sencillo de solucionar si pensamos de manera modular. En
efecto, analizar todos los detalles del problema al mismo tiempo (generalmente) resulta ser
un caos; en cambio, si detectamos sub-problemas cuya solución es independiente del
problema original, pero contribuyen con la solución de éste y son más fáciles de resolver,
entonces la solución general se puede construir más fácilmente. Por ejemplo, el hecho de
calcular el perímetro de un triángulo es independiente de cómo calcular la distancia entre dos
puntos del plano cartesiano, sin embargo, su solución nos ayuda a resolver el problema
general.
5. Los módulos se pueden construir en paralelo por diferentes programadores. Esto se debe a la
independencia que existe entre ello.

Capítulo 7 | Página 34
Para reafirmar los conceptos vistos en este capítulo proponemos que el lector resuelva el siguiente
problema, a partir del análisis que se plantea.

7.6.1 Otro ejemplo

El teorema del binomio, propuesto por Newton, establece un algoritmo para desarrollar la potencia de
una suma:
𝑛
(𝑥 + 𝑦) = 𝑥 𝑦
𝑘

En donde, es el coeficiente binomial, representa el número de formas de escoger k elementos de


un conjunto con n elementos, y se calcula de la siguiente manera:

𝑛 𝑛!
=
𝑘 𝑘! (𝑛 − 𝑘 )!

𝑛! denota el factorial de un número (entero positivo) y se define como:

1 𝑠𝑖 𝑛 = 0
𝑛! =
1 ∗ 2 ∗ …∗ 𝑛 𝑒𝑛 𝑜𝑡𝑟𝑜 𝑐𝑎𝑠𝑜

Aunque también se puede definir recursivamente (en términos de si misma)

1 𝑠𝑖 𝑛 = 0
𝑛! =
(𝑛 − 1)! ∗ 𝑛 𝑒𝑛 𝑜𝑡𝑟𝑜 𝑐𝑎𝑠𝑜

Por ejemplo, 4! es igual 24, según la primera definición 4!= 1*2*3*4; utilizando la segunda
definición 4!=(4-1)!*4, necesitamos calcular el factorial de 3, en este caso decimos que es una
llamada recursiva; para calcular 3! debemos conocer el resultado de 2!; y así sucesivamente hasta
llegar a 0! que es el único caso donde no se hace una llamada recursiva, a esto se le llama caso base o
cláusula de escape. Las llamadas recursivas de una función terminan en el caso base, en la Figura 7.5
se ilustra la llamada recursiva con una flecha hacia la izquierda (→) y el resultado con la flecha
derecha (←).

3! 2! 1! 0!
4! = 3! * 4 3! = 2! * 3 2! = 1! * 2 1! = 0! * 1 0! = 1
24
| 6 2 1 1

Figura 7.5. Llamadas recursivas

Capítulo 7 | Página 35
La primera definición de la función factorial se puede implementar utilizando ciclos, la solución es
similar al ejemplo 7.6.1 XXXXX.

La segunda definición se traduce a lenguaje C de una manera muy sencilla, los casos los
implementamos con una estructura if-else y los resultados se escriben prácticamente igual.

Definición recursiva de la función factorial en lenguaje C


int factorial(int n)
{
int fact;
if (n==0) /* caso base */
fact = 1;
else /*llamada recursiva */
fact = factorial(n-1) * n;

return fact;
}
Programa 7.1. Función factorial(x,y), definición recursiva

En esta definición no hemos validado que el parámetro de entrada no sea un número negativo ¿qué
debes agregar si deseamos que la función regrese -1 si el parámetro real no es válido?

Ejemplo 7.3: Implementa un programa que dado un entero n, imprima la expansión binomial de
(𝑥 + 𝑦) .

Por ejemplo, si la entrada del programa es 2 la salida del programa sería:

(𝑥 + 𝑦) = 𝑥 + 2𝑥 + 𝑦

Análisis: La operación principal del teorema binomial es una sumatoria y ésta se puede implementar
con un ciclo (como vimos en el capítulo anterior). Por otro lado, las operaciones que se realizan en
cada iteración involucran la llamada a la función factorial (ya definida) y a la función que calcula las
combinaciones de k en n.

Además de estos módulos proponemos otro encargado de la impresión de un término de la forma


𝑎𝑥 𝑦 , recibe el coeficiente a, el exponente de x y el exponente de y. Ya que en C no podemos
escribir los exponentes cono superíndices, proponemos que los términos se representen del modo
siguiente: 𝑎𝑥 𝑦 se imprime como ax^iy^j, por ejemplo para n=2 la salida sería

(x+2)^2 = x^2 + 2x + y^2

Los módulos en los que se puede dividir la solución son:


 factorial(n). Recibe un número entero y devuelve n! (entero).
 coefBinomial(n,k). Recibe los parámetros n y k, y devuelve el coeficiente binomial .

Capítulo 7 | Página 36
 imprimeTermino(a,i,j). Imprime en la pantalla el término 𝑎𝑥 𝑦 en el formato ax^iy^j ,
sugerimos que si el valor del coeficiente es cero no imprima el término y si es uno no imprima el
coeficiente (sólo las variables); si un exponente es 0 debe omitir la variable y si es 1 omite el
exponente; que es como se estila en algebra.

El Algoritmo 7.9 es una propuesta del algoritmo principal:

Diseño del Módulo Principal

Algoritmo(Pseudocódigo):

Inicio
Imprimir “Proporciona n”
Leer n
Imprimir “(x+y)^”, n, “=”

Desde k=0 mientras k≤n, n←n+1


a ← coefBinomial(n,k)
Si k≠0 entonces
Imprimir “ + ”
Fin Si
imprimirTermino(a,n-k,k)
Fin Desde
Fin

Algoritmo 7.9. Teorema Binomial

Se deja como ejercicio al lector la definición de los módulos coefBinomial e imprimeTermino, así
como la codificación de los algoritmos. En la Figura 7.6 se ilustra una ejecución del programa que
pide. Cabe señalar que se agregó un ciclo al módulo principal para que se repita.

Capítulo 7 | Página 37
Ejecución del programa

Figura 7.6. Ejecución del programa teoremaBinomial.c

7.4 ALCANCE DE LAS VARIABLES EN LENGUAJE C


El alcance o ámbito de una variable se refiere al contexto en el cual la variables está definida (dónde
se reconoce, que fragmentos de código reconocen o tienen acceso a la variable). De acuerdo con su
alcance, las variables pueden ser locales o globales.

Definición 7.2: Una variable local es aquella que se declara dentro de una función, por ende su uso
queda restringido a la misma, se dice que la variable es local a esa función; en otras palabras, la
variable sólo puede ser usada dentro de la función y no puede hacerse referencia fuera de ella.
Cualquier variable que se declare dentro de las llaves de la función, se considera local a ésta.

Por ejemplo, la variable r es local a la función suma(double, double).

Asimismo, si una variable x es local a una función fun simboliza que ésta es dueña de la variable, de
modo que si otra función, digamos fon, necesita conocer el valor de x para realizar una tarea, la
función fun debe enviárselo como argumentos cuando la invoque a fon; pero esta última no podrá
modificar el valor de la variables, si fuera necesario entonces fon debe regresarle como resultado la
llamada el valor que debe ser asignado a la variable y será fun quien cambie su estado (con una
asignación).

Los parámetros de entrada de una función, definidos en el encabezado de la misma, también se


consideran variables locales, así que sólo son reconocidos dentro de ella.

Capítulo 7 | Página 38
Cuando dos funciones tienen variables con el mismo nombre, no significa que se trate del mismo
espacio en memoria (es semejante a dos personas que tienen el mismo nombre, son tocayos más no la
misma persona), cada función puede acceder únicamente al espacio de memoria que corresponde
asignado a su propia variable, nunca al de la otra.

Definición 7.3: Una variable global es si se declara fuera de cualquier función (generalmente al
inicio del programa); si una función es global entonces cualquier función la reconoce, es decir, puede
consultar o cambiar su valor (siempre y cuando se defina después de la declaración de la variable).

La declaración de las variables globales y locales se realiza de la misma forma (tipo seguido del
identificador).

Al utilizar variables globales todas las funciones pueden manipularlas, sin que haya un control entre
una y otra, esto puede provocar que cometamos errores de tipo lógico, pues una función podría
modificar el valor de una variable afectando el resultado en otra; detectar y corregir este tipo de
errores puede llegar a ser una tarea muy compleja. Las variables locales por otra parte favorecen
mucho la reutilización de código y la cohesión, ya que cada función declara y manipula sus propias
variables sin depender de lo que ocurra en otras funciones.

Otra diferencia entre las variables globales y locales es que las primeras existen durante la ejecución
del programa (tiene un espacio asignado desde que se declaran hasta que el programa termina),
mientras que las variables locales a una función únicamente existen durante su ejecución; cada vez
que se ejecuta la función se vuelve a asignar un espacio de memoria para las variables locales, pero
en cuanto finaliza ésta se libera (¡bum! desaparece). Se recomienda no utilizar variables globales.

7.5 PASO DE PARÁMETROS


El paso de parámetros se refiere a la forma en la que se transfiere los datos a una función, cuando en
la llamada de una función aparece una variable como uno de los argumentos, puede ser que sólo
transfiramos el valor almacenado en la variable (paso por valor) o la dirección asignada a ésta (paso
por referencia).

7.5.1 Paso por valor


Cuando una variable se pasa por valor, no puede ser modificada por la función que se invoca, si
dentro de la función llamada se modifica el argumento esto no se ve reflejado en la función que la
invoco, pues son localidades de memoria diferentes (aunque tuvieran e mismo identificador).
Recuerda que dijimos que en cada llamada a una función se crean nuevas variables y se destruyen
una vez que finaliza la ejecución.

Para ilustrar este concepto presentamos el Programa 7.2 junto con su ejecución:

Capítulo 7 | Página 39
/* pasoPorValor: ejemplifica el paso de parámetros por valor */

/* Bibliotecas */
#include<stdio.h>
#include<stdlib.h>

/* Función incrementa1: devuelve el sucesor de un número dado */


int incrementa(int a)
{
a++;
return a;
}

/* Función principal */
main()
{
int a=10, inc; /*variables locales a main*/

/*imprimimos el estado de la variable a, antes de la llamada a


incrementa*/
printf("\nEl valor de \"a\" antes de la llamada es %i\n\n\t ", a);

/*llamada a la función incrementa pasando como argumento el valor


almacenado en la variable a*/
inc = incrementa(a);

/*imprimimos el estado de la variables después de llamar a


incrementa */
printf("\nEl valor de \"a\" despues de la llamada es %i\n\n\t ",
a);
printf("\nEl resultado del incremento es %i\n\n\t ", inc);
system("pause");
}
Programa 7.2. pasoPorValor.c

Ahora revisemos la ejecución del programa para entender qué sucede con los parámetros.

Ejecución del programa pasoPorValor

Capítulo 7 | Página 40
Figura 7.3. Ejecución del programa pasoPorValor

De acuerdo con la ejecución del programa, el valor de la variable a no cambio dentro del main,
aunque aparentemente dentro de la función incrementa1(int) fue modificada, lo cual no es cierto, ya
que la función crea su propia copia que se borra cuando termina la ejecución de la función, así que
aunque se haya modificado no se ve reflejado en la variable local del main.

7.5.2 Paso por referencia

Cuando un variable se pasa por referencia a una función implica que ésta pueda modificar su valor, a
diferencia del paso por valor donde se pasa una copia, en el paso de parámetros por referencia lo que
se transfiere es la dirección que le corresponde a la variable, así que no se trata de una copia sino del
original. Este mecanismo sólo debe utilizarse cuando se desea que los cambios efectuados a las
variables en la llamada de una función se vean reflejados en la función que la invocó. Por ejemplo,
una función que intercambia los valores de dos variables debe reflejar el cambio de estado de las
variables en el módulo que la invoco (ver Programa 7.3).

Ejemplo 6.3: Programa que intercambia los valores de dos varibles


/* pasoPorReferencia : muestra el mecanismo de paso por
referencia*/
/* Biblioteca */
#include<stdio.h>
#include<stdlib.h>

/* Función intercambia: Intercambia los valores almacenados en dos


Variables. Recibe como parámetros las direcciones de las
Variables, esto es dos apuntadores (se utiliza * para indicar
que la variable apunta a la dirección de memoria de un tipo de
datos T)
*/
void intercambia(int* ptr_a, int* ptr_b)
{
int temp; /* variable local a intercambia */
temp =*ptr_a; /* hacemos una copia de la primera variable */
*ptr_a=*ptr_b; /* borramos el valor de la primera variable
para asignando el valor de la segundo */
*ptr_b=temp; /* asignamos el valor de la primera variable en
la segunda */
}

Capítulo 7 | Página 41
/* Función principal */
main()
{
int a=10, b =20; /* Variables locales a main */

printf("\nEl valor de \"a\" antes de la llamada es %i\n\n\t


", a);
printf("\nEl valor de \"b\" antes de la llamada es %i\n\n\t
", b);

/*llamada a la función intercambia */


intercambia(&a,&b); /* &a (&b) es la dirección asignada a la
variable a (b)*/

printf("\nEl valor de \"a\" despues de la llamada es


%i\n\n\t ", a);
printf("\nEl valor de \"b\" despues de la llamada es
%i\n\n\t ", b);

system("pause");
}

Programa 7.3. pasoPorReferencia.c

La ejecución del programa pasoPorReferencia.c se muestra en la siguiente figura.

Ejecución del programa

Figura 7.3. Ejecución del programa pasoPorReferencia

Capítulo 7 | Página 42
Notemos que en este caso los valores de las variables a y b del main fueron cambiados durante la
ejecución de la función intercambia(int*,int*).

Para realizar la llamada por referencia es necesario pasar la dirección de la variable, el operador &
seguido del nombre de la variable (&nombre) regresa la dirección asociada a la variable. Una
variable donde se almacena una dirección de memoria se llama apuntador. Se declara igual que
cualquier otra pero se escribe un asterisco entre el tipo de la variable y el identificador, para indicar
que es un apuntador (tal y como se hizo en el encabezado de la función intercambia). También se
utiliza asterisco para acceder al valor almacenado en la variable, como se ilustra en el cuerpo de la
función intercambia.

El tema de los apuntadores es muy interesante, sin embargo, no es parte de este primer curso de
programación pero si quieres ahondar en el tema puedes consultar [Kernighan???], [Joyanes??] o
[Deitel].

Capítulo 7 | Página 43
EJERCICIOS DEL CAPÍTULO No. 7

EJERCICIOS
I. Determina si los siguientes enunciados son falsos (F) o verdaderos (V)
a) Una función puede devolver uno o más valores ( )
b) La comunicación entre los módulos se realiza a través de los parámetros de entrada y el valor
de retorno ( )
c) Cualquier módulo tiene cero o más parámetros de entrada ( )
d) Una variable local puede ser utilizada por cualquier función de un programa ( )
e) La definición de una función sólo consta del tipo de valor de regreso, nombre y la lista de
parámetros ( )
f) Es posible definir una función dentro de otra ( )
g) Es posible llamar una función desde otra función distinta de main ( )
h) Cuando se realiza la llamada a una función se debe indicar el tipo de datos de los parámetros
a. ( )
i) Cuando se invoca una función pasando por valor una variable, la función puede modificar su
valor ( )
j) Los parámetros que aparecen en el encabezado de una función se llaman parámetros formales
( )

II. Diseña un programa modular que dado el día, mes y año indique cuantos días han transcurrido a
partir del 1-01-año. Por ejemplo, si los datos de entrada son 5-02-2005 la salida del programa debe
ser: “Han transcurrido 36 días”; Para los datos 3-03-2000 la salida es “Han transcurrido 63 días”
Debes tomar en cuenta los años que son bisiestos. Crea dos módulo: uno que devuelva los días que
tiene un mes, a partir del mes y año (toma en cuenta los años bisiestos), y otro que determine si el
año es bisiesto (regresa 1 si es bisiesto y 0 en otro caso).
III. Dadas las coordenadas (x,y) de una partícula, determinar el ángulo de inclinación con respecto
al eje de las abscisas y la distancia con respecto al origen.
IV. Se desea conocer la deflexión hacia abajo que sufre una barra de acero prismática empotrada
en la pared al aplicar una fuerza en el extremo. El módulo de corte es un valor constante indicativo
de la rigidez de un cuerpo y se calcula de la siguiente forma:
𝑺 = (𝑭/𝑨) / (𝒅/𝒍)
Donde:
F: es la fuerza tangencial aplicada
A: es el área de la sección transversal sobre la cual se aplica la fuerza

Capítulo 7 | Página 44
d: es la deflexión hacia abajo que experimenta la barra
l: es la longitud de la barra

El área de de un polígono regular se puede calcular mediante la siguiente fórmula:

𝐴 = 𝑛 ∗ 𝑟 ∗ 𝑠𝑖𝑛(𝜋/𝑛) ∗ 𝑐𝑜𝑠(𝜋/𝑛)
Donde:
r
A = área
n = número de lados
r = el radio

V. La función fibonacci se define recursivamente de la siguiente manera:


0 𝑠𝑖 𝑛 = 0
𝑓𝑖𝑏(𝑛) = 1 𝑠𝑖 𝑛 = 1
𝑓𝑖𝑏 (𝑛 − 1) + 𝑓𝑖𝑏 (𝑛 − 2) 𝑒𝑛 𝑜𝑡𝑟𝑜 𝑐𝑎𝑠𝑜

Codifica la función fibonacci en lenguaje C (n es un número entero positivo).

VI. una prueba de escritorio para el siguiente programa tomando como datos de entrada temp1 = 10.
Enumera las instrucciones de acuerdo al orden en el que se ejecutan y describe en cada paso:
datos de entrada, operaciones, estado de la memoria y salida.

Programa Fuente: Temperaturas.c


/*Bibliografía*/
#include<stdlib.h>
#include<stdio.h>

/* Definición de funciones*/
float funcionA(float g)
{
float r;
r=(g *9)/5 - 459.67;
return r;
}

float funcionB(float g)
{
float r;
r=(5*(g+459.67))/9;
return r;
}

/* Función principal */
main ( ){

Capítulo 7 | Página 45
float temp1, temp2;
printf("Proporciona la temperatura: ");
scanf("%f", &temp1);

temp2 =funcionB(temp1);
printf("\n La temperatura en grados ****** es: %.2f
\n", temp2);
}
Programa 7.X. Temperaturas.c

VII. Corrige los errores de sintaxis del Programa 7.Y


Programa fuente: mayor.c
/* mayor.c : Lee dos números y determina cual de ellos es
mayor utilizando
una función que calcula el mayor entre dos números*/
/* Biblioteca */
#include<stdio.h>

/* Prototipos */
int mayor(int a, int b)

/* Función principal */
main()
{
int x, y, z, M;

printf("Proporciona tres números: ");


scanf("%i,%i, %i", &x, &y, &z);

M = mayor(x, int y);


M = mayor(M, z);

printf("\n El numero mayor es %i \n", m);

system("pause");

/* Definición de funciones */
int mayor(int a, b)
{
int m;
if(a >= b)
m = a;
else
m = y;
return m;
}
Programa 7.Y. mayor.c

Capítulo 7 | Página 46
ERRORES DE SINTAXIS

Revisa los siguientes fragmentos de código.

Primer error . Error en el prototipo de la función


int mayor(int a, int b)

Escribe cual es el error y como se corrige.

Código 2.
M = mayor(x, int y);

Escribe cual es el error y como se corrige.

Código 3.
printf(“\n El numero mayor es %i”,m);

Escribe cual es el error y como se corrige.

Código 4. Error en la Definición de la función.


int mayor(int a, b)

Escribe cual es el error y como se corrige.

Código 5.
m = y;

Escribe cual es el error y como se corrige.

Capítulo 7 | Página 47
8
Capítulo
Arreglos Unidimensionales
“El hombre es el mejor computador que podemos poner a bordo
de una nave espacial… y el único que se puede producir en
masa con mano de obra no experimentada”.
Werner von Braun

OBJETIVOS:
Al finalizar está práctica el estudiante:

o Conocerá el concepto de arreglos unidimensionales y lo aplicará en la resolución de


problemas que involucren el procesamiento de conjuntos de datos homogéneos.
o Aprenderá a declarar un arreglo unidimensional y cómo manipularlo secuencialmente o
utilizando ciclos.
o Utilizará arreglos como parámetros de entrada de funciones.
o Conocerá las operaciones básicas que se pueden realizar sobre un arreglo e implementará
funciones en lenguaje C para cada una de ellas.

8.1 INTRODUCCIÓN
En la vida cotidiana existen diversas situaciones en las cuales se requiere procesar datos relacionados
entre sí, como es el caso de los siguientes problemas:

Problema 1.- Se requiere un programa que lea las calificaciones de un grupo de diez estudiantes
y que determine cuántos de ellos tienen una calificación superior al promedio.

Problema 2.- Una empresa que cuenta con 100 empleados desea llevar un registro sobre el
salario de los mismos para generar reportes que incluyan datos como el salario promedio, el
salario máximo, el número de empleados que tienen un sueldo superior al salario promedio.

Problema 3.- La Secretaría de Comunicaciones y Transportes (SCT) necesita un programa que


registre el número de accidentes automovilísticos en la Autopista del Sol a lo largo de un año
y que emita un informe con los siguientes datos: el número de accidentes en cada uno de los
meses, el total de accidentes ocurridos a lo largo de un año, el promedio de accidentes anual y
los meses en los cuales se reportó un mayor número de accidentes que el promedio.

Capítulo 8 | Página 1
Observemos que en las situaciones anteriormente expuestas, los datos que se requieren de entrada son
del mismo tipo (datos numéricos). Ahora bien, pensar en la solución de los problemas planteados que
sólo involucren datos simples (enteros o decimales), implicaría almacenar la información de entrada
a un nivel de variables independientes, lo cual resultaría poco práctico.

Por ejemplo, diseñar un algoritmo para el Problema 1 empleando únicamente variables


independientes para almacenar las 10 calificaciones, implicaría: que por cada calificación debe
utilizarse una variable de entrada, escribirse una instrucción secuencial para requerir el dato y leerlo,
cuando hablemos del proceso de determinar la calificación más alta las aproximadamente 100
comparaciones que se necesitan llevar a cabo en el peor de los casos, sin contar que también debe
calcularse el número de estudiantes que tienen calificación mayor al promedio y los demás
requerimientos. Este método resulta de lo más ineficiente, y por supuesto si consideramos la
posibilidad de modificar el programa para que sea capaz de procesar 30 o más calificaciones, el
programa además de extenderse, implica reestructurarlo en su totalidad y que éste sea más complejo
que la versión anterior.

Para facilitar y hacer más eficiente la declaración y manipulación de una colección de datos de un
mismo tipo que están relacionados entre sí, como es el caso de los problemas planteados
anteriormente, se cuenta con datos estructurados llamados arreglos.

8.2 DEFINICIÓN DE ARREGLO


Un arreglo puede verse como una colección de variables de un mismo tipo que se identifican bajo un
mismo nombre, la forma en que se diferencian los elementos del arreglo es a través de la posición que
ocupan.

Considerando lo anterior y tomando como ejemplo el Problema 1, los nombres de las variables
donde se almacenan las calificaciones podrían ser: lista[0], lista[1], lista[2],
lista[3], lista[4] …, lista[9]; en este caso el nombre en común es lista y lo único
que cambia es el número que le corresponde a cada variable según la posición que ocupa en la lista.
En la Figura 8.1 se muestra el arreglo unidimensional lista, en el cual se almacenan las calificaciones
de un grupo de 10 estudiantes.

Capítulo 8 | Página 2
Nombre del arreglo

lista[ 0 ] 9
lista[ 1 ] 10
lista[ 2 ] 8
lista[ 3 ] 5
lista[ 4 ] 9
lista[ 5 ] 6
lista[ 6 ] 7
lista[ 7 ] 9
lista[ 8 ] 4
lista[ 9 ] 8

Posición que ocupa un elemento dentro de arreglo

Figura 8.1. Representación gráfica de un arreglo unidimensional

La definición formal se presenta a continuación junto con los otros términos derivados de los
arreglos:

Definición 8.1: Un arreglo representa un conjunto de datos del mismo tipo dispuestos en memoria
de forma consecutiva, a los que se accede a través de su índice (posición). A cada uno de los datos
que componen el arreglo se les denomina elementos.

En lenguaje C el tipo de datos que se almacena en un arreglo puede ser cualquiera de los tipos
básicos de C, es decir: int, char, float, double.

Definición 8.2: La posición que ocupa un elemento dentro de un arreglo se le denomina formalmente
índice.

Definición 8.3: El tamaño o longitud de un arreglo se define como el número de elementos que lo
constituyen.

En lenguaje C la numeración de los arreglo empieza en cero, es decir, al primer elemento del arreglo
le corresponde la posición 0, al segundo la posición 1, al tercero la posición 2 y así sucesivamente
hasta llegar al elemento TAM-1, donde TAM corresponde al tamaño del arreglo (Ver Figura 8.1.).

Capítulo 8 | Página 3
8.3 DECLARACIÓN E INICIALIZACIÓN DE ARREGLOS

La declaración de un arreglo consiste en reservar espacio de memoria para el conjunto de datos


homogéneos, para declarar una variable de tipo arreglo se siguen las misma reglas que para las
variables simples; con la única diferencia que después del identificador se escribe el tamaño del
arreglo encerrado entre corchetes cuadrados “ [ TAM ]”.

Sintaxis de la declaración de un arreglo en Lenguaje C:

<tipo> <nombre> [ <tamaño> ];

Figura 8.2. Declaración de un arreglo

Como ya hemos visto, los identificadores de una variable de tipo arreglo deben cumplir con las
mismas reglas que cualquier otro identificador de variable simple.

Para ejemplificar la declaración de un arreglo regresemos al ejemplo de la Figura 8.1, la instrucción


en lenguaje C para declarar el arreglo lista sería:

int lista[10];

Esta instrucción de C, reserva un espacio de memoria continuo, el cual permite almacenar hasta 10
elementos de tipo entero bajo el nombre de lista.

Otro aspecto importante en lenguaje C, es que se pueden declarar e inicializar los valores de un
arreglo en una misma instrucción, basta con asignar el conjunto de elementos encerrado entre llaves
“{ }”, separándolos por comas.

Sintaxis de la declaración e inicialización de un arreglo en Lenguaje C:

<tipo> <nombre> [<tamaño>] = { <elem1>, <elem2>, …


,<elemTAM-1> };

Figura 8.3. Declaración e inicialización de un arreglo

El elemento <elem0> corresponde al dato almacenado en la posición 0, <elem1> es el dato


almacenado en la posición 1 y así sucesivamente hasta el último elemento <elemTAM-1>, el cual
será almacenado en la última posición del arreglo.

La siguiente línea de código corresponde a la declaración e inicialización del arreglo lista de la


Figura 8.1.

int lista[10] = {9,10,8,5,9,6,7,9,4,8};

Capítulo 8 | Página 4
Cuando se inicializa un arreglo al momento de su declaración no es necesario especificar el tamaño
del arreglo pues el compilador define por default el tamaño del arreglo como el número de elementos
con los cuales se está inicializando. La instrucción anterior se puede reemplazar por la siguiente:

int lista[] = {9,10,8,5,9,6,7,9,4,8};

Observa que no se especificó el tamaño del arreglo.

Es importante recordar que la instrucción que inicializa todos los elementos de un arreglo, al mismo
tiempo, sólo es válida cuando se declara el arreglo; si se intenta realizar en otro momento el
compilador marcará un error.

Una buena práctica de programación consiste en definir el tamaño de un arreglo por medio de una
constante, de esta manera cuando se requiera cambiar el tamaño del arreglo bastará con hacerlo en la
definición de la constante como se verá más adelante.

Otro aspecto importante a ser considerado dentro de la inicialización de los arreglos es que al
inicializar un arreglo al momento de su declaración con al menos un valor, este valor se asigna a la
posición 0 del arreglo y todos los demás elementos quedan inicializados con 0. Así, por ejemplo:

int lista[10] = {5};

Asigna un 5 a la posición 0 del arreglo lista, pero las posiciones 1,2,3,4,5,6,7,8 y 9 tendrán asignado
por default el valor 0.

8.4 ACCESO A LOS ELEMENTOS DE UN ARREGLO

Como mencionamos anteriormente, la forma de referirse a un elemento específico de un arreglo es


por medio del nombre del arreglo, seguido del índice del elemento encerrado entre corchetes.

Sintaxis para acceder a un elemento de un arreglo:

<nombre del arreglo> [ <índice del elemento> ]

Figura 8.4. Acceso a un elemento de un arreglo

La manipulación por separado de los elementos de un arreglo se hace de igual forma que para las
variables simples. Revisemos los siguientes ejemplos basados en el arreglo lista de la Figura 8.1.

a) Asignación de un valor a un elemento de un arreglo.

Acción Instrucción en pseudocódigo Instrucción en código C


Asignar 8 al cuarto elemento del
lista[3] ← 8 lista[3] = 8;
arreglo lista.

Capítulo 8 | Página 5
b) Impresión del elemento de un arreglo desde el dispositivo estándar de salida.

Acción Instrucción en pseudocódigo Instrucción en código C


Imprime el último elemento Imprimir lista[9] printf(“%d”,lista[9]);
del arreglo lista.

c) Lectura de un elemento del arreglo desde el dispositivo estándar de entrada.

Acción Instrucción en pseudocódigo Instrucción en código C


Lee un entero en la primera Leer lista[0] scanf(“%d”,&lista[0]);
posición del arreglo lista.

Observa que al igual que las variables simples, cuando se lee un valor en un elemento de un arreglo
también se utiliza el símbolo “&”.

d) Realizar operaciones con los elementos de un arreglo.

Acción Instrucción en pseudocódigo Instrucción en código C


Asigna a la variable x la
raíz cuadrada del segundo x ← (lista[1])1/2 x= sqrt(lista[1]);
elemento del arreglo
lista
Incrementar en uno el
tercer elemento del arreglo lista[2]←lista[2]+1 lista[2]++;
lista

e) Paso de parámetro de un elemento de un arreglo a una función definida por el programador.

Acción Instrucción en pseudocódigo Instrucción en código C


Llamada a la función
duplica con el primer
elemento del arreglo.
Suponiendo que la función x ← duplica(lista[0]) x= duplica(lista[0]);
duplica recibe como
parámetro de entrada un
entero y devuelve el doble
del mismo.

De acuerdo con el último ejemplo, concluimos que, los elementos de un arreglo se pueden pasar
como parámetros a cualquier función (cuyos parámetros correspondan al tipo de datos) de igual
forma que las variables simples; tal y como se hizo en los ejemplos anteriores con la función
duplica, printf, scanf y sqrt.

Capítulo 8 | Página 6
En el siguiente programa se ilustra con mayor detalle la manipulación de los elementos de un arreglo
en lenguaje C. El programa lee secuencialmente la calificación de cinco estudiantes e imprime el
promedio.

Programa 8.1. :calculaPromedioV1.c


Calcula el promedio de cinco calificaciones proporcionadas por el usuario
/* Autor: Nombre del Programador
Nombre Programa: calculaPromedioV1.c
Descripción: Calcula el promedio de un grupo de 5 estudiantes.
Fecha de elaboración: Día/Mes/Año
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definimos como constante el tamaño del arreglo*/


#define TAM 5

/* Definición de función Principal */


main( )
{
/*Declaración del arreglo calificaciones*/
int lista[TAM];
double promedio=0;

printf("************************************************\n");
printf("* El siguiente programa calcula el promedio de *\n");
printf("* un grupo de cinco estudiantes *\n");
printf("************************************************\n");

/*Lectura de las calificaciones*/


printf("Proporciona la calificacion 1: ");
scanf("%d", &lista[0]);
printf("Proporciona la calificacion 2: ");
scanf("%d", &lista[1]);
printf("Proporciona la calificacion 3: ");
scanf("%d", &lista[2]);
printf("Proporciona la calificacion 4: ");
scanf("%d", &lista[3]);
printf("Proporciona la calificacion 5: ");
scanf("%d", &lista[4]);

/*Impresión de las calificaciones*/


printf("\nLas calificaciones ingresadas fueron: \n");
printf("\nCalificacion 1: %d", lista[0]);
printf("\nCalificacion 2: %d", lista[1]);

Capítulo 8 | Página 7
printf("\nCalificacion 3: %d", lista[2]);
printf("\nCalificacion 4: %d", lista[3]);
printf("\nCalificacion 5: %d", lista[4]);

/*Cálculo e impresión del promedio*/


promedio = ( float )( lista[0] +
lista[1] +
lista[2] +
lista[3] +
lista[4] ) / TAM;

printf("\n\n\tPromedio = %.2f\n\n", promedio);


system("pause");

La ejecución del Programa 8.1 se muestra en el siguiente cuadro.

Ejecución Programa 8.1.: calculaPromedioV1.c

A diferencia de otros lenguajes de programación, el lenguaje C no verifica que los índices del arreglo
estén en el rango definido, por lo que se debe evitar sobrepasar el índice máximo ya que de lo
contrario puedes tener resultados impredecibles. Es responsabilidad del programador asegurar que

Capítulo 8 | Página 8
nunca se intente acceder a un elemento del arreglo inexistente, por lo cual se debe cuidar que los
índices siempre sean mayores o iguales que cero y menores que el tamaño del arreglo.

8.5 PROCESAMIENTO DE ARREGLOS CON CICLOS


Los arreglos y los ciclos están estrechamente relacionados, podríamos afirmar que en cualquier
programa que se emplee un arreglo unidimensional éste será manipulado por medio de una
estructura repetitiva. Anteriormente mencionamos que un arreglo se utiliza cuando queremos
almacenar y manipular datos relacionados entre si y, generalmente, cuando tenemos un arreglo
debemos ejecutar el mismo conjunto de operaciones sobre cada uno de sus elementos, tal y cual lo
hicimos en el Programa 8.1 donde se leyó e imprimió secuencialmente (uno por uno) cada uno de los
elementos; éstas operaciones pueden realizarse utilizando un ciclo. En el siguiente cuadro se muestra
las instrucciones secuenciales para imprimir los elementos del arreglo lista.

printf("\nCalificacion 1: %d", lista[0]);


printf("\nCalificacion 2: %d", lista[1]);
printf("\nCalificacion 3: %d", lista[2]);
printf("\nCalificacion 4: %d", lista[3]);
printf("\nCalificacion 5: %d", lista[4]);

Figura 8.5. Impresión secuencial de un arreglo

Observemos que lo único que cambia en cada una de las instrucciones es el índice del elemento que
se imprime. De lo anterior, podemos deducir que es posible utilizar un ciclo para imprimir los
elementos de un arreglo, sustituyendo el índice de los elementos por un contador como lo muestra en
la siguiente figura.

for(i=0; i < 5; i++)


printf("\nCalificacion 1: %d", lista[i]);

Figura 8.6. Impresión de un arreglo por medio de un ciclo

Observemos que el número de iteraciones que se ejecutan es 5, en la primera el valor de i es 0, en la


segunda 1, en la tercera 2, en la cuarta 3 y por último 4, de tal manera que se ejecutan los cinco
printf que se mostraron en el Cuadro 8.5.

Una de las principales ventajas de utilizar ciclos para manipular arreglos es la facilidad que existe
para modificar un programa, en el cual se requiere cambiar el tamaño del arreglo. Por ejemplo, si se
desea imprimir un arreglo de 30 elementos; en la versión secuencial sería necesario agregar 25

Capítulo 8 | Página 9
instrucciones más, mientras que en la versión con ciclos basta con cambiar la condición del ciclo por
i < 30.

Anteriormente hemos visto que todas las estructuras repetitivas son equivalentes, así que usaremos el
ciclo for para manipular arreglos, sin embargo también se podría utilizar cualquiera de las otras
estructuras repetitivas; utilizaremos el for principalmente, por la claridad con la que podemos
representar el procesamiento del arreglo: el contador se inicia en cero que es el índice del primer
elemento, el incremento del contador es de uno para recorrer todo el arreglo elemento por elemento y
la condición debe ser que el contador sea “menor estricto” que el tamaño del arreglo, o bien, menor o
igual al tamaño del arreglo menos uno (dado que los índices válidos para un arreglo de tamaño TAM
son: 0,1,2, …, TAM-1).

La forma general de procesar un arreglo por medio de un ciclo se muestra en el siguiente cuadro.

Sintaxis general del procesamiento de arreglos mediante un ciclo for

Pseudocódigo Lenguaje C
for (pos=0; pos<TAM; pos++)
Desde pos ← 0 Mientras pos < TAM, pos ← pos + 1
{
Procesar Arreglo[pos]
Procesar Arreglo[pos]

Fin_Desde }

Lenguaje C

pos ← 0

F
pos < TAM

Procesar Arreglo[pos]

pos ← pos + 1

Algoritmo 8.1. Procesamiento de un arreglo utilizando un ciclo (Pseudocódigo y Diagrama de Flujo)

Capítulo 8 | Página 10
Para ejemplificar lo anterior revisemos el siguiente código que al igual que el Programa 8.1 lee la calificación
de cinco estudiantes y obtiene el promedio, pero ahora, con un ciclo.

Programa 8.2.: calculaPromedioV2.c (Versión con ciclos)


Calcula el promedio de cinco calificaciones proporcionadas por el usuario
/* Autor: Nombre del Programador
Nombre Programa: calculaPromedioV2.c
Descripción: Calcula el promedio de un grupo de 5 estudiantes.
Fecha de elaboración: Día/Mes/Año
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definimos como constante el tamaño del arreglo*/


#define TAM 5

/* Definición de función Principal */


main( )
{
/*Declaración del arreglo calificaciones*/
int lista[TAM];
double promedio = 0;
int i;

printf("************************************************\n”);
printf(“* El siguiente programa calcula el promedio de *\n");
printf(“* un grupo de cinco estudiantes *\n”);
printf("************************************************\n”);

/*Lectura de las calificaciones*/

for(i=0; i < TAM; i++)


{
printf("Proporciona la calificación %d: ",i+1);
scanf(“%d”, &lista[i]);
}

/*Impresión de las calificaciones*/


printf("\nLas calificaciones ingresadas fueron: \n");

for(i=0; i < TAM; i++)


printf("\nCalificacion %d: %d",i+1, lista[i]);

Capítulo 8 | Página 11
/*Suma de todas las calificaciones*/
for(i=0; i < TAM; i++)
promedio = promedio + lista[i];

/*Cálculo e impresión del promedio*/


promedio = promedio/TAM;
printf("\n\n\tPromedio = %.2f\n\n", promedio);
system("pause");
}

La ventaja de esta versión es que si se desea modificar el programa para que obtenga el promedio de
30 calificaciones proporcionadas por el usuario sólo basta cambiar el valor de la constante TAM.

8.6 ARREGLOS COMO PARÁMETROS DE FUNCIONES Y


OPERACIONES BÁSICAS DE LOS ARREGLO.
En el lenguaje C se pueden definir funciones que reciban como parámetro arreglos, con la
peculiaridad de que todos los arreglos en lenguaje C se pasan por referencia. Recordemos que cuando
una variable, en este caso de tipo arreglo, se pasa por referencia significa que se está pasando la
dirección de memoria asignado a la variable, lo cual tiene como efecto que si la función modifica el
valor de la variable, el cambio se hace en la dirección de memoria correspondiente a la variable
original, por lo tanto, se verá reflejado en la función desde la cual se hizo la llamada. La razón
fundamental de pasar los arreglos por referencia es no desperdiciar memoria, si un arreglo se pasara
por valor cada vez que se hace una llamada a una función se realizaría una copia del arreglo, lo cual
implicaría un uso excesivo de memoria cuando pasáramos arreglos de gran tamaño.

La sintaxis general para definir una función que reciba como parámetro de entrada un arreglo, es la
misma que la de cualquier otra función, con la diferencia de que cuando se define o declara la función
en la lista de parámetros se utilizan los corchetes cuadrados después del identificador del arreglo
para indicar que se trata de un arreglo (igual que en la declaración del arreglo).

Sintaxis general para definir una función que recibe un arreglo:

<tipoFuncion> <nombreFuncion> ( <tipoArreglo> <nombreArreglo> [] )

{
/*Cuerpo de la función*/
}

Figura 8.7 Declaración de funciones que reciben arreglos

Capítulo 8 | Página 12
En cambio, cuando se llama una función sólo se escribe el nombre de la variable de tipo arreglo sin
emplear los corchetes cuadrados.

Sintaxis general para llamar una función que recibe un arreglo.

<nombreFuncion> ( <nombreArreglo> );

Figura 8.8. Paso de parámetros de tipo arreglo

Para ejemplificar lo anterior, en las siguientes secciones definiremos funciones que implementan
algunas de las operaciones básicas de los arreglos como son: lectura, escritura, inicialización,
búsqueda y ordenación.

Es recomendable, para cumplir con los principios de modularidad, pasar como parámetro de entrada
el tamaño del arreglo, de esta manera la función puede utilizarse para arreglos de cualquier tamaño,
por tanto en cada función también se pasará como parámetro el tamaño del arreglo.

Para la implementación en Lenguaje C de los algoritmos, supondremos que los arreglos son de tipo
entero, considerando que el algoritmo es el mismo para cualquier tipo de datos; para arreglos de otro
tipo sólo se debe cambiar el tipo de datos correspondiente.

8.7 LECTURA DE UN ARREGLO


La función LeeArreglo lee de la entrada estándar cada uno de los elementos del arreglo, recibiendo
como parámetros de entrada el arreglo y el tamaño del mismo. No regresa ningún resultado pero
cambia el estado del arreglo según los datos de entrada.

Algoritmo 8.2.: Lectura de un arreglo


Pseudocódigo Lenguaje C
void LeeArreglo(int Arreglo[],
LeeArreglo(Arreglo[ ],TAM) int TAM)
{
Inicio int pos;
for (pos=0; pos<TAM; pos++)
Desde pos ← 0 Mientras pos < TAM, pos ← pos + 1 {
printf(“\nProporciona el
Imprimir “Proporciona el elemento”, pos elemento %d”,
Leer Arreglo[pos] pos);
scanf(“%d”,
&Arreglo[pos]);
Fin_Desde
}/*fin for*/
Fin }/*fin LeeArreglo*/

Capítulo 8 | Página 13
Diagrama de Flujo

LeeArreglo(Arreglo[ ],TAM)

pos ← 0

F
pos < TAM

V
Imrprimir “Proporciona
elemento”,pos

Leer Arreglo[pos]

pos ← pos + 1

Fin LeeArreglo

Algoritmo 8.2. Lectura de un arreglo (Pseudocódigo y Diagrama de Flujo)

Como mencionamos anteriormente, en la llamada a una función que recibe un arreglo nunca
deben escribirse los corchetes para indicar que es un arreglo, al igual que todas las funciones sólo
se escribe el nombre de la variable que se pasa como parámetro. Por ejemplo, la instrucción para
llamar a la función LeeArreglo con el arreglo lista definido en los Programas 8.1 y 8.2 es:
LeeArreglo(lista,5).

Revisemos la prueba de escritorio de la llamada anterior considerando que el arreglo contiene ceros
cuando se realiza la llamada y que los valores proporcionados por el usuario son: 9, 10, 8, 5 y 9.

Prueba de escritorio correspondiente a la llamada LeeArreglo(lista,5)

Estado del arreglo lista antes de la llamada a la función LeeArreglo(lista,5)

[0] [1] [2] [3] [4]


0 0 0 0 0 lista

Capítulo 8 | Página 14
Proceso:
Instrucción Entrada Salida Estado de las variables locales
Iteración 0

[0] [1] [2] [3] [4] pos


pos = 0
0 0 0 0 0 0

TAM
5
¿pos < TAMAÑO?
¿0<5? [0] [1] [2] [3] [4] pos
VERDADERO 9 0 0 0 0 1
Iteración 1

Imprimir “Proporciona Proporciona el


el elemento”, pos elemento 0
Leer Arreglo[pos]
9 TAM
Leer Arreglo[0]
5
pos = pos+1
pos= 0+1 = 1
¿pos < TAMAÑO?
¿1<5? [0] [1] [2] [3] [4] pos
VERDADERO 9 10 0 0 0 2
Iteración 2

Imprimir “Proporciona Proporciona el


el elemento”, pos elemento 1
Leer Arreglo[pos]
10 TAM
Leer Arreglo[1]
5
pos = pos+1
pos= 1+1 = 2
¿pos < TAMAÑO?
¿2<5? [0] [1] [2] [3] [4] pos
VERDADERO 9 10 8 0 0 3
Iteración 3

Imprimir “Proporciona Proporciona el


el elemento”, pos elemento 2
Leer Arreglo[pos]
8 TAM
Leer Arreglo[2]
5
pos = pos+1
pos= 2+1 = 3
¿pos < TAMAÑO?
¿3<5? [0] [1] [2] [3] [4] pos
VERDADERO 9 10 8 5 0 4
Iteración 4

Imprimir “Proporciona Proporciona el


el elemento”, pos elemento 3
Leer Arreglo[pos]
5
Leer Arreglo[3] TAM
pos = pos+1 5
pos= 3+1 = 4
¿pos < TAMAÑO?
¿4<5? [0] [1] [2] [3] [4] pos
VERDADERO 9 10 8 5 9 5
Iteración 5

Imprimir “Proporciona Proporciona el


el elemento”, pos elemento 4
Leer Arreglo[pos]
9 TAM
Leer Arreglo[4]
5
pos = pos+1
pos= 4+1 = 5

Capítulo 8 | Página 15
Iteración 6 [0] [1] [2] [3] [4] pos
¿pos < TAMAÑO?
9 10 8 5 9 5
¿5<5?
FALSO
TAM
5

Parámetros de salida y valor de retorno: ninguno

Estado del arreglo lista después de la llamada a la función LeeArreglo(lista,5)

[0] [1] [2] [3] [4]


9 10 8 5 9 lista

Figura 8.9. Prueba de escritorio de LeeArreglo

8.8 IMPRESIÓN DE UN ARREGLO


La función ImprimeArreglo imprime en la salida estándar cada uno de los elementos del arreglo. Los
parámetros de entrada de la función son el arreglo y el tamaño del mismo. No regresa ningún
resultado y tampoco altera el estado del arreglo.

Algoritmo 8.3.: Impresión de un Arreglo


Pseudocódigo Lenguaje C

ImprimeArreglo(Arreglo[ ],TAM) void ImprimeArreglo(


int Arreglo[],
Inicio int TAM)
{
Desde pos ← 0 Mientras pos < TAM, pos ← pos + 1
int pos;
for (pos=0; pos<TAM; pos++)
Imprimir Arreglo[pos] printf(“%d”,Arreglo[pos]);

Fin_Desde }/*fin ImprimeArreglo*/


Fin

Capítulo 8 | Página 16
Diagrama de Flujo

ImprimeArreglo(Arreglo[ ],TAM)

pos ← 0

F
pos < TAM

Imprimir Arreglo[pos]
V

pos ← pos + 1

Fin LeeArreglo

8.9 INICIALIZACIÓN DE UN ARREGLO


La función InicializaArreglo asigna el mismo valor a todas los elementos del arreglo (elemento por
elemento ya que no es posible asignarle un valor a todo el arreglo de manera simultánea). Los
parámetros de entrada de la función son: el arreglo, el tamaño del mismo y el valor que se asignará a
los elementos. No regresa ningún resultado pero altera el estado del arreglo.

Algoritmo 8.4.: Inicialización de un arreglo


Pseudocódigo Lenguaje C

void InicializaArreglo(
InicializaArreglo(Arreglo[ ],TAM, valor)
int Arreglo[],
int TAM
Inicio int valor)
{
Desde pos ← 0 Mientras pos < TAM, pos ← pos + 1 int pos;
for (pos=0; pos<TAM; pos++)
Arreglo[pos] ← valor Arreglo[pos] = valor;
Fin_Desde }/*fin InicializaArreglo*/
Fin

Capítulo 8 | Página 17
Diagrama de Flujo

ImprimeArreglo(Arreglo[ ],TAM)

pos ← 0

pos < TAM F

V
Imprimir Arreglo[pos]

pos ← pos + 1

Fin ImprimeArreglo

8.9 INICIALIZACIÓN DE UN ARREGLO


La función InicializaArreglo asigna el mismo valor a todas los elementos del arreglo (elemento por
elemento ya que no es posible asignarle un valor a todo el arreglo de manera simultánea). Los
parámetros de entrada de la función son: el arreglo, el tamaño del mismo y el valor que se asignará a
los elementos. No regresa ningún resultado pero altera el estado del arreglo.

Algoritmo 8.4.: Inicialización de un arreglo


Pseudocódigo Lenguaje C

InicializaArreglo(Arreglo[ ],TAM, valor)


void InicializaArreglo(
Inicio
int Arreglo[],
Desde pos ← 0 Mientras pos < TAM, pos ← pos + 1
int TAM
Arreglo[pos] ← valor
int valor)

Capítulo 8 | Página 18
{
Fin_Desde
int pos;
Fin
for (pos=0; pos<TAM; pos++)

Arreglo[pos] = valor;

}/*fin InicializaArreglo*/

Diagrama de Flujo

InicializaArreglo(Arreglo[ ],TAM,valor)

pos ← 0

F
pos < TAM

V
Arreglo[pos] ← valor

pos ← pos + 1

Fin InicializaArreglo

La prueba de escritorio de la función InicializaArreglo se deja como ejercicio al lector.

8.10 BÚSQUEDA EN ARREGLO


Una búsqueda es el proceso mediante el cual es posible verificar si un elemento pertenece a un
arreglo. Terminamos con éxito cuanto el elemento es encontrado, devolviendo la posición en la cual
está almacenado, o bien, regresando -1 cuando no se encontró el elemento.

Capítulo 8 | Página 19
El método más sencillo para buscar un elemento en un arreglo se llama Búsqueda Secuencial y
consiste en iniciar la búsqueda en el primer elemento del arreglo avanzando de uno en uno hasta
encontrar el elemento indicado o hasta llegar al final del arreglo. El algoritmo se presenta a
continuación en pseudocódigo y se deja como ejercicio al lector la implementación en lenguaje C y la
prueba de escritorio.

Algoritmo 8.5.: Búsqueda secuencial en un Arreglo


Pseudocódigo del algoritmo Búsqueda Secuencial

BúsquedaSecuencial(Arreglo[ ],TAM,elemento)

Inicio

encontrado ← -1

Desde pos ← 0 Mientras (pos< TAM) AND (encontrado ≠ -1), pos ← pos + 1

Si elemento = Arreglo[pos] entonces


encontrado ← pos
Fin_Si

Fin_Desde

Regresa encontrado

Fin

En el algoritmo se utiliza la variable llamada encontrado como bandera, el valor inicial es -1


indicando que el elemento aún no se ha localizado; mientras se ejecuta el ciclo, el valor de la variable
encontrado cambia sólo si el elemento indicado se localiza. Por tanto, si el elemento nunca se
encontró el valor que regresará será -1.

Existen otros algoritmos de búsqueda más eficientes, sin embargo, tienen como precondición que el
arreglo esté ordenado.

8.11 Ordenación en Arreglo


En varias situaciones se requiere que los elementos almacenados en un arreglo estén ordenados 1, por
tanto existen diversos algoritmos de ordenamiento, algunos más eficientes que otros pero más

1 Principalmente porque la búsqueda de un elemento es más rápida.

Capítulo 8 | Página 20
complejos. No es un objetivo de este curso revisar los diferentes algoritmos de ordenamiento de
arreglos; sin embargo, consideramos provechoso conocer al menos una de las técnicas más simples
de ordenamiento: Selección Directa.

El método de Selección Directa consiste en seleccionar el elemento más pequeño (o más grande) de
la lista para intercambiarlo por el primer elemento de la lista, este proceso se repite con la lista
formada con los elementos restantes hasta que se tiene una lista ordenada. En la siguiente tabla se
muestra el algoritmo formal suponiendo que el arreglo se desea ordenar crecientemente.

Algoritmo 8.6.: Ordenamiento por Selección Directa

SeleccionDirecta(Arreglo[ ],TAM)

Inicio

Desde i ← 0 Mientras i< TAM, i← i + 1

Desde j ← i+1 Mientras j< TAM, j← j + 1

Si Arreglo[j] < Arreglo[i]

intecambia(Arreglo[j],Arreglo[i])

Fin_Si

Fin_Desde

Fin_Desde

Fin

El primer ciclo permite recorrer todo el arreglo asignando en cada una de las posiciones el elemento
que corresponde según su valor, en tanto, el segundo ciclo se encarga de recorrer los elementos del
arreglo que se encuentran después de la posición i. Suponemos que el menor elemento de la lista que
aún no ha sido ordenada está almacenado en la posición i y que todos los elementos que se
encuentran en las posiciones menores que i ya han sido ordenados; por tanto, el segundo ciclo busca
el menor elemento del resto de la lista si en alguna iteración del recorrido encuentra un elemento
menor a Arreglo[i], intercambia los valores. La función intercambia(int &a, int
&b) se definió en el capítulo 7 ver Programa 7.3 cambia entre si los valores almacenados en las
variables dadas. A continuación se muestra un programa completo en lenguaje C para probar la
función SeleccionDirecta(int[],int).

Capítulo 8 | Página 21
Programa 8.3: ordenaArreglos
Muestra como funciona la función SelecciónDirecta(int Arreglo[], int TAM)
/* Autor: Juan Pérez
* Nombre Programa: ordenaArreglo.c
* Descripción: El siguiente programa ejemplifica
* cómo se puede ordenar un arreglo de enteros de forma
* creciente utilizando el método de selección directa
* Fecha de elaboración: 16/09/2008
*/

/*Directivas de preprocesador*/
#include<stdio.h>
#include<stdlib.h>

/* intercambia: recibe por referencia dos variables


* e intercambia los valores almacenados en ellas
* parámetros: a,b variable enteras a intercambiar.
* regresa: nada
*/
void intercambia(int &a, int &b)
{
/*aux sirve como variable auxiliar para
almacenar el valor de a*/
int aux = a;
a = b;
b = aux;
} /*fin intercambia*/

/* ImprimeArreglo: imprime los elementos de un arreglo


* parámetros: Arreglo, el arreglo que va a imprimir
* TAM, tamaño del arreglo
* regresa: nada
*/
void ImprimeArreglo(int Arreglo[], int TAM)
{
int pos;
for (pos=0; pos<TAM; pos++)
printf(" %d ",Arreglo[pos]);

}/*fin ImprimeArreglo*/

/* SeleccionDirecta: ordena un arreglo de enteros


* parámetros: Arreglo, el arreglo que se desea ordenar
* TAM, tamaño del arreglo
* regresa: nada*/

Capítulo 8 | Página 22
void SeleccionDirecta(int Arreglo[], int TAM)
{
int i, j;
/* El primer ciclo permite recorrer el arreglo para
almacenar en cada posición el elemento correspondiente
de menor a mayor. */
for (i=0; i<TAM; i++)
/*El Segundo ciclo for permite recorrer el arreglo
para encontrar el siguiente elemento más pequeño*/
for (j=i+1;j<TAM; j++)
/*Comparamos los elementos para determinar si
se ha encontrado un elemento menor al de la posición i*/
if(Arreglo[j] < Arreglo[i])
intercambia(Arreglo[i],Arreglo[j]);

}/*fin SeleccionDirecta*/

/* Función principal */

main(){

/* Declaración del arreglo ejemplo de 5 elementos */


int a[] = {8,1,5,4,0};

/* Impresión del arreglo antes de ser ordenado */


printf("El arreglo original es : \n\t\t");
ImprimeArreglo(a,5);

/*invocación del método de selección directa para


ordenar el arreglo*/
SeleccionDirecta(a,5);

/*Impresión el arreglo ordenado*/


printf("\n\nEl arreglo ordenado es : \n\t\t");
ImprimeArreglo(a,5);

printf("\n\n\t\t");
system("pause");

Capítulo 8 | Página 23
La ejecución del programa anterior se muestra en la siguiente figura:

Ejecución Programa 8.3: ordenaArreglo.c

Capítulo 8 | Página 24
Finalmente se muestra como opera el algoritmo de acuerdo al ejemplo anterior gráficamente.
Método de Selección Directa

[0] [1] [2] [3] [4]


8 1 5 4 0

Aplicando Selección Directa. La estrella ubica el primer elemento de la lista y el círculo el


elemento más pequeño de la lista

[0] [1] [2] [3] [4]


8 1 5 4 0

Resultado. Al existir un elemento más pequeño se envía a la posición del primer elemento
de la lista.

[0] [1] [2] [3] [4]


0 1 5 4 8

Aplicando Selección Directa. La estrella ubica el primer elemento de la lista y el círculo el


elemento más pequeño de la lista, en este caso como los elementos que se encuentran en
las posiciones 2,3 y 4 no son más pequeños que el primer elemento de la lista, la lista
permanece igual.

[0] [1] [2] [3] [4]


0 1 5 4 8

Resultado. El arreglo no se modifica.

[0] [1] [2] [3] [4]


0 1 5 4 8

Aplicando Selección Directa. La estrella ubica el primer elemento de la lista y el círculo el


elemento más pequeño de la lista

[0] [1] [2] [3] [4]


0 1 5 4 8

Resultado. Al existir un elemento más pequeño se envía a la posición del primer elemento
de la lista.

[0] [1] [2] [3] [4]


0 1 4 5 8

Capítulo 8 | Página 25
Aplicando Selección Directa. La estrella ubica el primer elemento de la lista y el círculo el
elemento más pequeño de la lista, en este caso como el elemento que se encuentran en la
posición 4 no es más pequeño que el primer elemento de la lista, la lista permanece igual.

[0] [1] [2] [3] [4]


0 1 4 5 8

Resultado. El arreglo no se modifica.

[0] [1] [2] [3] [4]


0 1 4 5 8

Aplicando Selección Directa. Al encontrarse el primer elemento al final de la lista termina


el algoritmo.

[0] [1] [2] [3] [4]


0 1 4 5 8
Arreglo al final del Algoritmo de Selección Directa.

[0] [1] [2] [3] [4]


0 1 4 5 8

PROYECTO EJEMPLO
En esta sección presentaremos una solución al Problema 3 planteado al inicio de este capítulo.

Proyecto 8.1: Descripción del problema


La Secretaria de Comunicación y Transporte (SCT) requiere un programa que registre el número
de accidentes automovilísticos en la autopista del sol a lo largo de un año y que a partir de ellos
emita un informe con los siguientes datos: el número de accidentes en cada uno de los meses, el
total de accidentes ocurridos a lo largo de un año, el promedio de accidentes anual y los meses en
los cuales se reportó un número mayor de accidentes que el promedio.

Analizando el problema podemos percatarnos de que es necesario recurrir a un arreglo para


almacenar el número de accidentes mensuales, ya que es necesario obtener primero el promedio y
después verificar cuáles de los meses reportaron un número mayor de accidentes al promedio anual.
En el siguiente cuadro se presenta un análisis de los elementos más importantes del problema así
como un bosquejo del algoritmo.

Capítulo 8 | Página 26
Análisis del problema
Datos de entada: Salida: Restricciones:

Número de - Accidentes mensuales Ninguna


accidentes de - Total de accidentes anuales
cada uno de los - Promedio de accidentes anuales Constantes:
meses del año. - Meses que superaron el promedio anual de Número de meses en un
accidentes año (que corresponderá
al tamaño del arreglo)
Método: Para resolver el problema realizaremos los siguientes pasos:
1. Leer el número de accidentes ocurridos en cada uno de los meses
2. Realizar la suma de todos los accidentes
3. Calcular el promedio anual
4. Imprimir los accidentes mensuales, el total de accidentes anuales y el promedio anual de
accidentes.
5. Determinar e imprimir en que meses se reportaron un número mayor de accidentes que el
promedio.

Del análisis anterior concluimos que necesitamos definir los siguientes módulos:

Diseño modular

Principal

Imprimir Leer Suma Promedio Imprime


Arreglo Mayores
Arreglo Arreglo Arreglo
Promedio
Suma

Los módulos Imprimir Arreglo y Leer Arreglo ya han sido descrito anteriormente, sólo falta diseñar e
implementar los módulos: Suma Arreglo, Promedio Arreglo e Imprime Mayores Promedio. En esta parte sólo
incluimos el análisis, diseño e implementación dejando al lector la tarea de realizar las pruebas de escritorio.
Empecemos con el método Suma Arreglo:

Capítulo 8 | Página 27
Algoritmo 8.7.: Análisis y Diseño de la función
SumaArreglo(Arreglo[ ],TAM)
Descripción:
Dado un arreglo de enteros devuelve la suma de todos los elementos, es decir, implementa la
siguiente ecuación matemática:

TAM -1
suma =  Arreglo[i]
i=0

Parámetros de entrada:
• Arreglo[ ]: arreglo de enteros que se va a sumar
• TAM: tamaño del arreglo (entero)

Valor de retorno:
• suma: resultado de la suma de todos los elementos del arreglo.

Método:
Utilizaremos un ciclo para recorrer el arreglo y una variable (acumular) para almacenar la suma
acumulativa

Módulos:
No requiere de ningún otro módulo

Variables locales:
Sólo se utilizan las variables ya mencionadas, por lo tanto, no se requiere de ninguna variable extra.

Pseudocódigo Lenguaje C

SumaArreglo(Arreglo[ ],TAM) int SumaArreglo(


int Arreglo[],
Inicio int TAM
)
suma ← 0 {
Desde pos ← 0 Mientras pos < TAM, pos ← pos+1
int pos, suma = 0;

suma ← suma + Arreglo[pos] for (pos=0; pos<TAM; pos++)


suma += Arreglo[pos];
Fin_Desde
return suma;
Regresa suma
}/*fin SumaArreglo*/
Fin

Capítulo 8 | Página 28
Diagrama de Flujo

SumaArreglo(Arreglo[ ],TAM)

suma ← 0

pos ← 0

F
pos < TAM

V
suma ← suma + Arreglo[pos]

pos ← pos + 1

Regresa suma

Fin InicializaArreglo

El método PromedioArreglo se vuelve trivial una vez que hemos desarrollado la función
SumaPromedio(int[],int). El análisis, diseño e implementación se muestran en la siguiente tabla.

Algoritmo 8.8.: Análisis y Diseño de la función


PromedioArreglo(Arreglo[ ],TAM)
Descripción:
Dado un arreglo de enteros devuelve el promedio de sus elementos, es decir, implementa la siguiente
fórmula:

T AM-1

 Arreglo[i]
prom = i =0

TAM

Capítulo 8 | Página 29
Parámetros de entrada:
• Arreglo[ ]: arreglo de enteros del cual se requiere el promedio
• TAM: tamaño del arreglo (entero)

Valor de retorno:
• prom: resultado del promedio de los elementos del arreglo (flotante)

Método:
Calcularemos la suma utilizando el módulo SumaArreglo(int[],int) y a partir de ella el promedio.
No se requiere ningún ciclo.

Módulos:
SumaArreglo(int[], int)

Variables locales:
Además de las variables mencionadas se utiliza la variable suma de tipo entero para almacenar el
resultado de la llamada a la función SumaArreglo(int[],int), con el fin de que el algoritmo sea más
claro.

Capítulo 8 | Página 30
Pseudocódigo Lenguaje C

float PromedioArreglo(
int Arreglo[],
PromedioArreglo(Arreglo[ ],TAM) int TAM
)
Inicio {
int prom, suma;
suma  SumaArreglo( Arreglo, TAM )
suma=SumaArreglo(Arreglo,TAM);
suma
prom  prom = (float)suma/TAM;
TAM
Regresa prom
return prom;
Fin
}/*fin PromedioArreglo*/

Diagrama de Flujo

PromedioArreglo(Arreglo[ ],TAM)

suma ← SumaArreglo(Arreglo,TAM)

suma
prom 
TAM

Regresa prom

Fin PromedioArreglo

Capítulo 8 | Página 31
Ahora, sólo falta diseñar el módulo que imprime en la pantalla los meses en los cuáles ocurrieron más
accidentes que el promedio anual.

Algoritmo8.9.: Análisis y Diseño de la función

ImpMayoresProm (Arreglo[ ],TAM)

Descripción:
A partir de un arreglo dado y el promedio del mismo la función imprime los meses (en
representación numérica) en los que el número de accidentes sobrepaso el promedio.

Parámetros de entrada:
• Arreglo[ ]: arreglo de enteros que se va a sumar
• TAM: tamaño del arreglo (entero)
• prom: promedio del arreglo (flotante)

Valor de retorno:
• ninguno, la función imprime en pantalla los resultados

Método:
Utilizaremos un ciclo para recorrer el arreglo y determinar cuáles de los elementos son
mayores que prom.

Módulos:
No requiere de ningún otro módulo

Variables locales:
Sólo se utilizan las variables ya mencionadas, por lo tanto, no se requiere de ninguna variable
extra.

Capítulo 8 | Página 32
Pseudocódigo Lenguaje C

int ImpMayoresProm(

int Arreglo[],

int TAM,

ImpMayoresProm(Arreglo[ ],TAM,prom) float prom

Inicio )
Desde pos ← 0 Mientras pos< TAM, pos←pos+1 {
Si Arreglo[pos] > prom entonces int pos;
Imprimir “ Mes”, pos+1.
Fin_Si

Fin_Desde
for (pos=0; pos<TAM; pos++)
Fin
if(Arreglo[pos] > prom)

printf(“\n Mes %d”,

pos+1);

}//fin ImpMayoresProm

Diagrama de Flujo

Capítulo 8 | Página 33
ImpMayoresProm(Arreglo[ ],TAM,prom)

pos ← 0

F
pos < TAM

Arreglo[pos] > prom

V
F
Imprimir “ Meses “, pos+1

pos ← pos + 1

Fin ImpMayoresProm

En el diseño del módulo anterior se decidió pasar como parámetro el promedio del arreglo para hacer más
eficiente el programa, ya que en el algoritmo principal se calcula; sin embargo, podría omitirse y llamar a la
función correspondiente dentro del módulo.
Para finalizar con el ejemplo construyamos el módulo principal.

Algoritmo 8.10: Análisis y Diseño del método principal del programa que
genera el reporte de los accidentes anuales en la Autopista del Sol

Datos de entrada:
• Número de accidentes registrados en cada uno de los meses del año (meses[ ], arreglo de
enteros)

Capítulo 8 | Página 34
Salida:
• meses[]: número de accidentes registrados mensualmente.
• totalAnual: total de accidentes registrados a lo largo de un año
• promAnual: promedio anual de accidentes registrados mensualmente.
• Meses en los cuales se registro un número mayor al promedio.

Módulos:
ImprimeArreglo(int[], int)
LeerArreglo(int[], int)
SumaArreglo(int[], int)
PromedioArreglo(int[], int,int)
ImpMayorArreglo(int[], int,int)

Variables locales:
Sólo las antes mencionadas.

Algoritmo del módulo principal

Inicio

TAM ← 12

Imprimir “Proporciona el número de accidentes registrados”


LeeArreglo(meses,TAM)
totalAnual ← SumaArreglo(meses,TAM)
promAnual ← PromedioArreglo(meses,TAM)
Imprimir “Los accidentes reportados en el año fueron:”
ImprimeArreglo(meses,TAM)
Imprimir “El total de accidentes ocurridos durante el año es ”, totalAnual
Imprimir “El promedio anual de accidentes es ”, promAnual
Imprimir “ Los meses en los cuales se reportó un número mayor de accidentes”
ImpMayoresProm(meses,TAM,promAnual)

Fin

El código completo se presenta en la siguiente tabla, se realizaron pequeñas modificaciones a las


funciones definidas para adecuar las impresiones en pantalla al problema planteado.

Capítulo 8 | Página 35
Programa 8.11: reporteAccidentes.c
Genera un reporte con las estadísticas de los accidentes registrados en la Autopista del Sol
/* Autor: Nombre del Programador
* Nombre Programa: reporteAccidentes.c
* Descripción: el programa registra el número de
* accidentes automovilísticos en la autopista del sol
* a lo largo de un año e imprime en pantalla un informe
* con los siguientes datos:
* el número de accidentes en cada uno de los meses,
* el total de accidentes ocurridos a lo largo de un año,
* el promedio de accidentes anual y,
* los meses en los cuales se reportó un número mayor
* de accidentes que el promedio
* Fecha de elaboración: Día/Mes/Año */

/*Directivas de preprocesador*/
#include<stdio.h>
#include<stdlib.h>

/* ImprimeArreglo: imprime en pantalla los elementos de


* un arreglo, en este caso el número de accidente mensuales
* parámetros: Arreglo[], el arreglo que va a imprimir
* TAM, tamaño del arreglo
* regresa: nada*/
void ImprimeArreglo(int Arreglo[], int TAM)
{
int pos;
for (pos=0; pos<TAM; pos++)
printf("\nMes %d: %d accidentes",pos+1,Arreglo[pos]);

}/*fin ImprimeArreglo*/
/* LeeArreglo: lee los elementos de un arreglo, en este caso
* el número de accidentes mensuales
* parámetros: Arreglo[], donde almacenará los datos
* TAM, tamaño del arreglo
* regresa: nada*/
void LeeArreglo(int Arreglo[],
int TAM)
{
int pos;
for (pos=0; pos<TAM; pos++)
{
printf("Mes %d: ", pos+1);
scanf("%d",&Arreglo[pos]);

}/*fin for*/
}/*fin LeeArreglo*/

Capítulo 8 | Página 36
/* SumaArreglo: suma los elementos del arreglo
* parámetros: Arreglo[], el arreglo que va a sumar
* TAM, tamaño del arreglo
* regresa: suma*/
int SumaArreglo(int Arreglo[], int TAM )
{
int pos, suma = 0;

for (pos=0; pos<TAM; pos++)


suma += Arreglo[pos];

return suma;

}/*fin SumaArreglo*/

/* PromedioArreglo: calcula el promedio de los elementos


* de Arreglo[]
* parámetros: Arreglo[], el arreglo que se va a sumar
* TAM, tamaño del arreglo
* regresa: prom*/
float PromedioArreglo( int Arreglo[], int TAM )
{
int prom, suma;

suma=SumaArreglo(Arreglo,TAM);

prom = (float)suma/TAM;

return prom;

}/*fin PromedioArreglo*/

/* ImpMayoresProm: imprime los meses en los que se reportó


* un número mayor de accidentes que el promedio
* parámetros: Arreglo[], el arreglo que se va a sumar
* TAM, tamaño del arreglo
* prom, promedio del arreglo
* regresa: nada
*/
int ImpMayoresProm(int Arreglo[], int TAM, float prom)
{
int pos;

for (pos=0; pos<TAM; pos++)


if(Arreglo[pos] > prom)
printf("\n Mes %d: %d accidentes", pos+1, Arreglo[pos]);

}//fin ImpMayoresProm

Capítulo 8 | Página 37
/* Función principal */
main(){

// Declaración de variables

const int TAM = 12;


int meses[TAM], totalAnual;
float promAnual;

printf("***********************************************\n");
printf("* El siguiente programa lee el número de accidentes
que *\n");
printf("* ocurrieron a lo largo de un año en la autopista
del *\n");
printf("* Sol y realiza un reporte con el promedio anual,
el to- *\n");
printf("* tal de accidentes y los meses en los que se
reportó un *\n");
printf("* número mayor al promedio *\n");
printf("************************************************\n");

//Lectura de los datos


printf("\n\nProporciona el número de accidentes registrados
mensualmente\n");
LeeArreglo(meses,TAM);

//Calculos requeridos
totalAnual = SumaArreglo(meses,TAM);
promAnual = PromedioArreglo(meses,TAM);

//Impresión del reporte


system("cls"); //limpiamos la pantalla

printf("*************************************************\n");
printf("* REPORTE DE ACCIDENTES AUTOPISTA DEL SOL *\n");
printf("*************************************************");

printf("\n\nLos accidentes reportados en el año fueron:\n");


ImprimeArreglo(meses,TAM);
printf("\n\nTotal de accidentes ocurridos durante el año =
%d", totalAnual);
printf("\n\nPromedio anual de accidentes = %.0f", promAnual);
printf("\n\nMeses que se registraron más accidentes que el
promedio:\n");
ImpMayoresProm(meses,TAM,promAnual);
printf("\n\n\t\t");
system("pause");
}

Capítulo 8 | Página 38
Ejecución Programa 7.11: reporteAccidentes.c

Lectura de datos:

Impresión del Reporte

Capítulo 8 | Página 39
Los otros problemas planteados al inicio de este capítulo se resuelven de manera similar al anterior y se dejan
como ejercicio al lector. Con este ejemplo finalizamos el capítulo de Arreglos Unidimensionales, en el
siguiente capítulo revisaremos cómo se pueden manipular los arreglos de caracteres cuando éstos representan
cadenas.

Capítulo 8 | Página 40
EJERCICIOS DEL CAPÍTULO No. 8

EJERCICIO I.

1. Contesta las siguientes preguntas

a) ¿Qué es un arreglo?

b) ¿Qué diferencia hay entre un arreglo y una variable simple?

c) ¿En qué casos se utiliza un arreglo?

d) ¿Qué es el tamaño de un arreglo?

e) ¿Cuántos elementos tiene un arreglo cuyo último índice es 100?

f) ¿Qué ventajas tiene definir el tamaño de un arreglo cómo una constante?

g) Pueden almacenarse los siguientes datos en un arreglo: [34, 90.5, “hola”]. Explica tu respuesta.

h) Cuando una función en lenguaje C recibe como parámetro un arreglo, éste se pasa por referencia o por
valor. Explica.

2. Determina en cuáles de los siguientes casos los datos se pueden almacenar en variables simples y en cuales en
arreglo; declara en lenguaje C las variables. Por ejemplo:

Ejemplo 1: Almacenar el valor de las 10 resistencias conectadas en serie en un circuito electrónico.


Respuesta: Arreglo de 10 enteros (int resistencias[10]) DUDA: El valor de las resistencias es entero
Ejemplo 2: Resistencia equivalente a 20 resistencias conectadas en serie.
Respuesta: variable entera (int totalResistencia)

i. Puntaje de los 20 clavadistas que se inscribieron a la competencia de clavados en la Quebrada.

ii. Record mundial en 100 metros planos.

iii. Temperaturas registradas durante cada uno de los días del año.

iv. Ganancias mensuales de la compañía de circuitos electrónicos QinetiQ.

Capítulo 8 | Página 41
v. Placa de un automóvil.

vi. Máxima temperatura registrada en un mes.

vii. Número de lados de un polígono.

viii. Registro de la producción mensual de automóviles a lo largo de un año.

ix. Número de participantes inscritos a la competencia de clavados.

x. Registrar el consumo diario de luz para determinar la cuota mensual.

3. Realiza la prueba de escritorio para el modula SumaArreglo(int[ ], int ) con la siguiente llamada:

SumaArreglo( [4,7,6,-5,8], 5)

4. Modifica el Programa 8.11 para que el reporte también incluya el número mínimo y máximo de accidentes
mensuales reportados. (Diseña una función que regrese el valor mínimo de un arreglo y otra que devuelva el
valor máximo)

5. Realiza el análisis, diseño e implementación de los Problemas 1 y 2 presentados al inicio de este capítulo.

6. Construye programas en lenguaje C para cada uno de los siguientes problemas planteados. No olvides que
debes realizar el análisis y diseño del algoritmo antes de implementarlo.

i. Dado el vector a=(a1,a2,a3,a4,a5,a6), calcula la norma (denotada por |a|) del vector utilizando la
siguiente fórmula:

| a |= a1 + a2 + a3 + a4 + a5 + a6
2 2 2 2 2 2

ii. Escribe un programa que calcule la varianza de una lista de N números enteros (N<50). La fórmula para
calcular la varianza es

N −1
varianza =  ( xi − x) 2
i =0

donde x es el promedio de todos los números xi.

iii. Escribe un programa que calcule el centro de masa de un sistema con n masas puntuales. El usuario debe
proporcionar para cada punto i: su coordenada xi, su coordenada yi y su masa mi. El programa debe

Capítulo 8 | Página 42
imprimir las coordenadas del centro de masa de los puntos que se introdujeron, denotas por (xg,yg), las
cuales se calculan utilizando las siguientes fórmulas:

n n

 mi xi m y i i
xg = i =1
n
yg = i =1
n

m
i =1
i m
i =1
i

(Pista: puedes utilizar tres arreglos, uno para las coordenadas en x, otro para las coordenadas en y y el
último para las masas)

iv. Un polinomio puede representarse por medio de un arreglo pensando que los índices del arreglo
representan el grado del término mientras que los elementos almacenados representan el coeficiente del
término. Por ejemplo, el polinomio

5x 4 + 8x 2 + 1 = 0
Puede representar en un arreglo de tamaño 5 como:

1 0 8 0 5

0 1 2 3 4

En la posición 0 se encuentra el coeficiente del término con grado 0, en la posición 1 el coeficiente del
término con grado 1 y así sucesivamente, cuando en el polinomio no aparece un término de grado k,
entonces en la posición k del arreglo se almacena 0.
Tomando en cuenta la información anterior realiza un programa que calcule la suma de dos polinomios,
cuyo grado no sea mayor a 5.

Capítulo 8 | Página 43
Capítulo
Cadenas en lenguaje C
“La matemática es la ciencia del orden y la medida, de bellas cadenas de
razonamientos, todos sencillos y fáciles.”

René Descartes

OBJETIVOS:
Al finalizar este capítulo el estudiante:

o Manejará adecuadamente datos de tipo cadena en lenguaje C mediante arreglos y


o Utilizará las funciones estándar que el lenguaje C ofrece para el procesamiento de cadenas

9.1 INTRODUCCIÓN
Cuando diseñamos programas muchas veces nos enfrentamos con que algunos de los datos que debemos
manejar son palabras por ejemplo el nombre de una persona, el registro federal de contribuyentes (RFC), la
clave única del registro de población (CURP), etc. En programación a este tipo de datos se les conoce como
cadenas y su uso es muy común.

La mayoría de los lenguajes de programación cuentan con una serie de funciones estándar para poder
manipularlas y algunos incluso incorporan un tipo de dato especificó para cadenas, otros como el lenguaje C
utilizan arreglos de tipo caracter para poder manejarlas, es por ello que las cadenas se pueden considerar un
caso particular de arreglos unidimensionales.

En este capítulo revisaremos los elementos necesarios para el manejo de cadenas así como algunas de las
funciones que el lenguaje C ofrece para este tipo de datos.

9.2 CONCEPTOS BÁSICOS

9.2.1 Cadenas de Caracteres

Una cadena es un tipo de dato estructurado que se define como la concatenación de varios caracteres, es decir,
una secuencia ordenada de cualquier longitud finita de caracteres. Por ejemplo la cadena “palabra”1 es la
secuencia de los 7 caracteres: ‘p’,’a’,’l’,’a’,’b’,’r’,’a’2, o bien la frase “Saludos a mis 2 mejores amigos” es la

1 En lenguaje C las constates de tipo cadena se escriben entre comillas dobles.

2 Y las constantes de tipo carácter entre comillas simples.

Capítulo 9 | Página 1
secuencia de 30 caracteres ya que los números y los espacios son también caracteres. Como una cadena es
una serie de caracteres tratados como una sola unidad, como mencionamos anteriormente en algunos lenguajes
de programación existe un tipo específico para cadenas. El lenguaje C no incorpora este tipo de dato, por lo
tanto para poder almacenar en la computadora una cadena necesitamos no una variable de tipo carácter sino
tantas como longitud tenga la cadena. Los arreglos son por tanto la solución, un arreglo de tipo caracter de N
elementos me permitirá guardar en memoria un cadena.

[0] ‘c’

[1] ‘a’ Nombre del Arreglo: transporte

[2] ‘m’ Tamaño del arreglo: 7

[3] ‘i’ Contenido que puede ser guardado:


camión, avión, metro, auto y barco
[4] ‘o’

[5] ‘n’

[6]

Figura 9.1

Al almacenar cadenas en memoria se guarda al final un carácter especial que determina el fin de la cadena, en
C se utiliza el carácter ‘\0’ (carácter nulo cuyo valor ASCII es 0) como fin de cadena, por lo tanto para
guardar la cadena “camión” ( Figura 9.1 ) necesitamos mínimo un arreglo de tamaño 7, sin embargo la
longitud de la cadena será 6 ya que el carácter ‘\0’3 en realidad no representa ningún carácter .

[0] ‘c’

[1] ‘a’
Nombre del Arreglo: transporte
[2] ‘m’
Tamaño del arreglo: 7
[3] ‘i’
Contenido guardado: camión
[4] ‘ó’
Longitud de la cadena: 6
[5] ‘n’

[6] ‘\0’

Figura 9.2

3 Recuerda que el carácter espacio ‘ ’ no es igual al carácter nulo ‘\0’. El código ASCII del espacio es 32 y el del fin de cadena es 0.

Capítulo 9 | Página 2
Recuerda que los arreglos en C son de tamaño fijo y no pueden cambiar de tamaño durante la ejecución del
programa, esto no significa que tenga que conocer de ante mano la longitud de una cadena para poder declarar
el arreglo en el cual se va almacenar, es posible guardar un arreglo de tamaño N cualquier cadena siempre y
cuando la longitud de esta sea menor a N-1. Por ejemplo

Si revisamos la figura 9.1, el arreglo llamado transporte puede almacenar otros contenidos, revisemos como
quedaría la cadena para cada uno de estos casos.

Caso 2. ‘a’ ‘v’ ‘i’ ‘ó’ ‘n’ ‘\0’

[0] [1] [2] [3] [4] [5] [6]

Caso 3. ‘m’ ‘e’ ‘t’ ‘r’ ‘o’ ‘\0’

[0] [1] [2] [3] [4] [5] [6]

Caso 4. ‘a’ ‘u’ ‘t’ ‘o’ ‘\0’

[0] [1] [2] [3] [4] [5] [6]

Caso 5. ‘b’ ‘a’ ‘r’ ‘c’ ‘o’ ‘\0’

[0] [1] [2] [3] [4] [5] [6]

Figura 9.3

9.2.2 Declaración de cadenas en C

Ya se dijo que para almacenar cadenas en lenguaje C se necesita un arreglo de tipo caracter de una longitud
suficiente para contener a todos los caracteres de la cadena más el fin de cadena, por tanto la declaración de
una cadena no es otra cosa que la declaración de un arreglo de tipo char con la siguiente sintaxis.

char <nombre> [<tamaño4>];

Ejemplo:

char transporte[7];

[0] [1] [2] [3] [4] [5] [6]

4 El tamaño debe ser al menos la longitud de la cadena más uno para el fin de cadena.

Capítulo 9 | Página 3
Si se quiere inicializar el arreglo se puede hacer asignar una constante de tipo cadena de la siguiente forma:

char <nombre> [<tamaño>]= <”constante cadena”>;

Ejemplo:

c a m i ó n \0

char transporte[7]=”camión”; [0] [1] [2] [3] [4] [5] [6]

o bien se puede inicializar elemento por elemento como cualquier arreglo, especificando el conjunto de datos
iníciales.

char <nombre>[<tamaño>]={<conjunto de caracteres>};

char transporte[7]={‘c’,’a’,’m’,’i’,’ó’,’n’}; c a m I ó n \0

[0] [1] [2] [3] [4] [5] [6]

Recuerde que cuando inicializamos al menos un elemento del arreglo


el resto de los elementos por default se inicializan en cero por lo tanto estaremos incluyendo el fin de cadena,
siempre que el tamaño del arreglo sea mayor que la longitud de la cadena.

La asignación directa a una cadena sólo se puede realizar en la declaración, de otra forma se produciría un
error de compilación por ejemplo si tuviéramos el siguiente código:

char transporte[7];

transporte = "camión";

La forma correcta de asignar valores a una cadena fuera de la declaración es asignando valor a cada uno de
los elementos de la cadena, por ejemplo:

Capítulo 9 | Página 4
char transporte[7];
transporte[0] = 'c';
transporte[1] = 'a';
transporte[2] = 'm';
transporte[3] = 'i';
transporte[4] = 'ó';
transporte[5] = 'n';
transporte[6] = '\0';

9.2.3 Impresión y lectura de cadenas

Las cadenas regularmente se tratan como una sola unidad y por tanto en C existe un especificador de formato
para trabajar con cadenas, el especificador es %s.

Al utilizar el especificador de formato %s, comúnmente se realiza una llamada a la función printf;, lo que esta
función hace es imprimir en pantalla todos los elementos del arreglo especificado, hasta encontrar el fin de
cadena (carácter ‘\0’).

Revisemos el siguiente ejemplo para aclarar los aspectos anteriormente mencionados.

Ejemplo 9.1: cadena1.c


Declara, inicializa e imprime una cadena
/* Autor: Juan Pérez
Nombre Programa: cadena1.c
Descripción: Ejemplo que imprime una cadena con printf y %s.
Fecha de elaboración: 16/09/2008
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definición de función Principal */


main( )
{ //Declaración del arreglo
char cadena[8]="lunes";

//Inicialización de últimos dos elementos del arreglo


cadena[6]=’*’;
cadena[7]=’*’;

//impresión del arreglo usando %s


printf("%s \n\n",cadena);
system("pause");
}

Capítulo 9 | Página 5
Ejecución paso a paso

Estado de la memoria
Dato de
Instrucción
(variables y constantes) salida

l u n e s \0 \0 \0
char cadena[8]="lunes";
[0] [1] [2] [3] [4] [5] [6] [7]

l u n e s \0 * \0
cadena[6]=’*’; [0] [1] [2] [3] [4] [5] [6] [7]

l u n e s \0 * *
cadena[7]=’*’; [0] [1] [2] [3] [4] [5] [6] [7]

l u n e s \0 * *
lunes
printf("%s\n\n",cadena);
[0] [1] [2] [3] [4] [5] [6] [7]

Ejecución Ejemplo9.1: cadena1.c

En el código del ejemplo 9.1 se está declarando un arreglo de tipo carácter de tamaño 8, el cual es inicializado
con la cadena “lunes”. Esta inicialización solo asigna un valor explicitó a los primeros 5 elementos del arreglo
y a los tres restantes les asigna un cero que es el ASCII del fin de cadena, las siguientes dos líneas de código
están asignando el carácter “*” a los dos últimos elementos del arreglo, como se puede ver en el primer
renglón de la ejecución paso a paso ; sin embargo al imprimir mediante un llamado a la función printf el
contenido del arreglo, utilizando el especificador de formato %s, sólo aparece en pantalla la cadena lunes, ya
que la impresión termina al encontrar el fin de cadena.

Capítulo 9 | Página 6
Imprimiendo mediante un ciclo el contenido de todo el arreglo, si sólo queremos que se imprima la cadena y
no los asteriscos, la condición para terminar el ciclo debe ser mientras el carácter no sea el fin de cadena, ya
que si se imprime hasta el tamaño del arreglo se imprimirán los asteriscos, en el código ejemplo 9.2, esta
marcado en negritas la condición necesaria para solo imprimir la cadena.

Ejemplo 9.2: cadena2.c


Declara, inicializa e imprime una cadena mediante un ciclo
/* Autor: Juan Pérez
Nombre Programa: cadena2.c
Descripción: Ejemplo que imprime en pantalla una cadena mediante
un ciclo.
Fecha de elaboración: 16/09/2008
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definición de función Principal */


main( )
{
//Declaración del arreglo
char cadena[8]="lunes";
int i;

//Inicialización de últimos dos elementos del arreglo


cadena[6]='*';
cadena[7]='*';

/*Ciclo para imprimir el arreglo;


con la condición cadena[i]!=’\0’
el ciclo se repetirá mientras
el contenido del arreglo sea diferente al fin de cadena*/

for(i=0;cadena[i]!=’\0’;i++)
printf("%c",cadena[i]);

printf("\n\n");
system("pause");

Capítulo 9 | Página 7
Ejecución Ejemplo9.2: cadena2.c

Si cambiamos la condición del ciclo (cadena[i]!=’\0’)por i<8 para que ahora se imprima el
contenido de todo el arreglo entonces si se imprimirán los asterisco

Ejecución Ejemplo 9.3: cadena2.c cambiando la condición del ciclo

El omitir el fin de cadena tendrá consecuencias al imprimir la cadena mediante el especificador de


formato %s, ya que como mencionamos antes la función printf imprimirá todos los caracteres
contiguos en memoria desde el inicio del arreglo hasta encontrar el fin de cadena, sin considerar el
tamaño del arreglo. En el ejemplo 9.4 se muestra el resultado de imprimir una cadena en pantalla que
NO incluye el fin de cadena.

Capítulo 9 | Página 8
Ejemplo 9.4: cadena3.c
Imprime en pantalla una cadena que NO tiene fin de cadena
/* Autor: Juan Pérez
Nombre Programa: cadena3.c
Descripción: Ejemplo que imprime una cadena que NO tiene fin de
cadena.
Fecha de elaboración: 16/09/2008
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definición de función Principal */


main( )
{ //Declaración del arreglo de tamaño 3 inicializado con una
cadena de longitud 3
char cadena[3]="ABC";

printf("%s",cadena);

printf("\n\n");
system("pause");

Ejecución Ejemplo9.4: cadena3.c

Ejecución
Estado de la memoria

A B C

[0] [1] [2]

En este caso aunque en el arreglo sólo se encuentra la cadena “ABC” se imprimen en pantalla otros
caracteres, estos caracteres se imprimen porque la función printf imprimió todos los caracteres que
encuentra en memoria contigua desde el inicio del arreglo hasta encontrar el fin de cadena aunque
sobre pase las dimensiones del arreglo.

Capítulo 9 | Página 9
Pudiera darse el caso de que el fin de cadena se encuentre enseguida del arreglo, en cuyo caso se
mostraría la siguiente salida, esto dependerá directamente de la administración de memoria del
sistema operativo.

Otra forma de imprimir en pantalla una cadena es mediante la función puts(), esta función también se
encuentra en la librería estándar stdio y escribe una cadena a la salida estándar5 , y al igual que printf
deja de imprimir caracteres cuando encuentra el fin de cadena. Como parámetro de entrada esta
función necesita una cadena ya sea constante o bien un arreglo de tipo carácter y después de imprimir
la cadena imprime un salto de línea por ejemplo.

Ejemplo 9.5: cadena4.c


Imprime en pantalla una cadena utilizando puts()
/* Autor: Juan Pérez
Nombre Programa: cadena4.c
Descripción: Ejemplo que imprime una cadena con la función puts.
Fecha de elaboración: 16/09/2008*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
/* Definición de función Principal */
main( )
{ //Declaración de la cadena
char cadena[4]="ABC";
//impresión utilizando un llamado a la función puts
puts(cadena);
puts(“siguiente cadena”);

printf("\n\n");
system("pause");

5 La salida estándar generalmente es la pantalla

Capítulo 9 | Página 10
Ejecución Ejemplo9.5: cadena4.c

Ejecución: Estado de la memoria:

A B C \0

[0] [1] [2] [3]

Para la lectura de cadenas se puede utilizar el especificador de formato %s en una llamada a la


función scanf, el fin de cadena se agrega automáticamente después del último carácter leído. Veamos
un ejemplo:

Ejemplo 9.6: cadena5.c


Lectura de una cadena desde el teclado
/* Autor: Juan Pérez
Nombre Programa: cadena5.c
Descripción: Ejemplo que lee una cadena desde el teclado
mediante la función scanf y %s.
Fecha de elaboración: 16/09/2008*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definición de función Principal */


main( )
{ //Declaración de la cadena
char cadena[8];
printf(“ingresa una cadena:”);

/*Lectura de la cadena
En esta línea se hace el llamado a la función scanf
utilizando el %s y observa que se omite el & */
scanf(“%s”,cadena);
printf(“cadena leida:%s”,cadena);

printf("\n\n");
system("pause");
}

Capítulo 9 | Página 11
Ejecución Ejemplo9.6: cadena5.c

Primera Ejecución: Estado de la memoria:

h o l a \0

[0] [1] [2] [3] [4] [5] [6] [7]

Segunda Ejecución: Estado de la memoria:

L a \0

[0] [1] [2] [3] [4] [5] [6] [7]

Observa que en los parámetros de la función scanf se está omitiendo el & antes del nombre del
arreglo, esto es porque recuerda que al pasar como parámetro un arreglo se hace un paso por
referencia es decir se está pasando la dirección de inicio del arreglo y por tanto ya no necesita el
operador &.

Cuando leemos cadenas mediante la función scanf con el %s, sólo podemos leer una palabra, ya que
la función deja de leer caracteres al encontrar un espacio o un salto de línea.

Es por esto que en la segunda ejecución del ejemplo 9.6 aunque se ingresa una cadena de más de una
palabra, sólo la primera palabra se guarda en el arreglo.

Otra alternativa para leer una cadena es la función gets, la cual lee de la entrada estándar una cadena
y la guarda en el arreglo que recibe como parámetro. Esta función deja de leer caracteres al encontrar
un salto de línea, por lo tanto permite guardar cadenas que tengan más de una palabra, como se
muestra en la segunda ejecución del ejemplo 9.7

Capítulo 9 | Página 12
Ejemplo 9.7: cadena6.c
Imprime en pantalla una cadena utilizando puts()
/* Autor: Juan Pérez
Nombre Programa: cadena6.c
Descripción: Ejemplo que lee una cadena con la función gets.
Fecha de elaboración: 16/09/2008*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/* Definición de función Principal */


main( )
{ //Declaración de la cadena
char cadena[8];
printf(“ingresa una cadena:”);

/*Lectura de la cadena mediante la función gets


Como parámetro de entrada recibe el nombre del arreglo en el
cual se va a almacenar la cadena*/

gets(cadena);

printf(“cadena leida:%s”,cadena);

printf("\n\n");
system("pause");

Capítulo 9 | Página 13
Ejecución Ejemplo9.7: cadena6.c
Primera Ejecución: Estado de la memoria:

S a l u d o \0

[0] [1] [2] [3] [4] [5] [6] [7]

Segunda Ejecución: Estado de la memoria:

r i o p e z \0

[0] [1] [2] [3] [4] [5] [6] [7]

9.2.4 Procesamiento de Cadenas

Algunas de las operaciones más comunes al trabajar con cadenas son:

Obtener su longitud
Compararlas
Copiarlas
Hacer alguna búsqueda
Concatenarlas

Para estas operaciones con cadenas el lenguaje C ofrece una serie de funciones, todas ellas contenidas
dentro de la librería estándar string.h La tabla 9.1 resume algunas de ellas, incluyendo un breve
ejemplo de como usarlas.

Capítulo 9 | Página 14
Prototipo:
char *strcat(char*s1, const char *s2);
Nombre Parámetros Descripción Ejemplo

strcat Entrada: Añade una copia #include <stdio.h>


Dos cadenas s1 de la cadena s2 al #include <string.h>
y s2. final de la cadena
main( ){
s1
Salida: char s1[15]=”Oscar”;
apuntador6 a char s2[15]=”Gael”;
cadena. printf(“s1=%s s2=%s”,s1,s2);
strcat(s1,s2);

/*Al contenido de la cadena s1


se agregó el contenido de la
cadena s2*/

printf(“s1=%s s2=%s”,s1,s2);
system (“pause”);
}
Ejecución
Observa la cadena s1 después de la concatenación.

Prototipo:
int strcmp(const char *s1, const char *s2);
Nombre Parámetros Descripción Ejemplo
La función compara #include <stdio.h>
strcmp Entrada: la cadena s1 con la #include <string.h>
Dos cadenas s1 cadena s2 y retorna
main( ){
y s2. un valor entero n
char s1[8]="Alma";
cuyo valor es: char s2[8]="Laura";
Salida: char s3[8]="Lalo";
número entero  > 0 cuando la char s4[8]="Laura";
mayor, igual, o cadena s1 es int n;
menor que cero mayor que la

6 Un apuntador es una variable que guarda una dirección, aunque la función devuelve un apuntador generalmente el valor de retorno no se

utiliza.

Capítulo 9 | Página 15
cadena s2. /*Se compara las cadenas s1 y
 = 0 si las cadenass2 como s1 es menor que s2
son iguales (alfabéticamente va primero
 < 0, cuando la s1) n tiene un valor menor a
cadena s1 es cero*/
menor que s2. n=strcmp(s1,s2);
printf("%s < %s
Una cadena es n=%d",s1,s2,n);
menor a otra
/*Ahora se compara s2 con s3 y
cuando la precede como en este caso s2 es mayor
alfabéticamente. por lo tanto n es mayor a
cero*/
printf("\n");
n=strcmp(s2,s3);
printf("%s > %s
n=%d",s2,s3,n);

/*Por último se compara la


cadena s2 con s4 y como son
iguales el valor de n es
cero*/
printf("\n");
n=strcmp(s2,s4);
printf("%s = %s
n=%d",s2,s4,n);
printf("\n");
system ("pause");
}
Ejecución

Observa los valores que toma la variable n, cuando al comparar 2


cadenas es mayor, menor o igual.

Prototipo:
char *strcpy(char *s1, const char *s2);
Nombre Parámetros Descripción Ejemplo
#include <stdio.h>
strcpy Entrada: #include <string.h>
Dos cadenas s1 Copia la cadena s2
main( ){
y s2.

Capítulo 9 | Página 16
(incluyendo el char s1[8]="piensa un
Salida: carácter nulo) a la numero";
Valor de s1 char s2[8]="pense en el 10";
cadena s1.
printf("s1=%s s2=%s\n",s1,s2);

/*Se copia el contenido de la


cadena s2 en la cadena s1*/

strcpy(s1,s2);

printf("Despues de invocar a
ala funcion: s1=%s s2=%s
\n",s1,s2);

system (“pause”);

Ejecución
Observa los valores de s1 y s2 antes y después de utilizar la función
copiar.

Prototipo:
size_t strlen(const char *s);
Nombre Parámetros Descripción Ejemplo
#include <stdio.h>
strlen Entrada: #include <string.h>
Una cadena s1 La función devuelve
main( ){
el número de
Salida: caracteres que tiene char s1[16]="saludos a
Valor entero la cadena hasta el todos";
caracter nulo (el int n;
carácter nulo no se
cuenta) /*Se pasa como parámetro la
cadena s1 para que devuelva su
longitud */

Capítulo 9 | Página 17
n=strlen(s1);

printf(“s1=%s\n la cadena s1
tiene %d caracteres\n”,s1,n);
system (“pause”);
}
Ejecución
Observa que la función devuelve el tamaño de la cadena, asimismo
verifica que los espacios también son considerados como caracteres.

Prototipo:
int strncmp(const char *s1, const char *s2, size_t n);
Nombre Parámetros Descripción Ejemplo
Entrada: Esta función al igual #include <stdio.h>
strncmp Dos cadenas s1, que strcmp compara #include <string.h>
s2 y un entero nc
las cadenas s1 y s2
main( ){
pero solo compara
Salida: los primeros nc char s1[8]="saludo";
Número entero caracteres de s1 char s2[8]="salado";
mayor, igual, o contra la cadena s2 int n,nc=3;
menor que cero
El valor de retorno /*Se van a comparar las
primeras 3 lertras de las
sigue la misma
cadenas */
convenciones que
strcmp. n = strncmp( s1, s2, nc );

printf( "Las 3 primeras letras


de s1 son " );

if( n < 0 )
printf( "menores que" );
else if( n > 0 )
printf( "mayores que" );
else printf( "iguales a" );
printf( " s2\n" );
system (“pause”);
}
Ejecución
Observa que las cadenzas s1 y s2 son diferentes, sin embargo las 3
primeras letras son iguales.

Capítulo 9 | Página 18
Tabla 9.1 Funciones de la librería string

Si bien existen estas y otras funciones para manipular cadenas, el procesar cadenas es igual a procesar
cualquier otro arreglo, es decir, si necesitáramos conocer el número de veces que aparece un carácter
dentro de una cadena el proceso a seguir consistirá en ir recorriendo el arreglo y llevar un contador
que se incrementará cada vez que se encuentre el carácter buscado.

Hay que recordar que cuando trabajamos con cadenas generalmente el tamaño del arreglo no es el
mismo que el tamaño de la cadena, es por eso que el ciclo que recorre la cadena debe terminar al
encontrar el fin de cadena y no hasta el tamaño del arreglo.

A continuación presentamos el ejemplo 9.8 y construimos una función encargada de contar


caracteres.

Ejemplo 9.8 Función: cuenta_c(s[ ],c)


Descripción:
Cuenta el número de veces que aparece el carácter “c” en la cadena “s"

Parámetros de entrada:
 s[ ](alfanumerico): arreglo de caracteres en el cual se almacenó la cadena
 c (alfanumerico):carácter a buscar
Parámetros de salida y valor de retorno:
 l (entero): número veces que se encuentra el caracter dentro de la cadena

Capítulo 9 | Página 19
Pseudocódigo Lenguaje C
entero cuenta_c(caracter s[], int cuenta_c(char s[], char c){
caracter c) int i,l=0;
Desde i ← 0 Mientras s[i] ≠ ‘\0’, i← i + 1 for( i=0; s[i]!=‘\0’; i++){
if(s[i]==c) l++;
Si s[i]=c entonces
l← l + 1 }
Fin si
return l;
Fin Desde
Regresa l }
Fin
Diagrama de Flujo

entero cuenta_c ( caracter s[], caracter c )

i←0, l ← 0

s[ i] ≠ ‘\0’

s[ i] = c

l←l+1

i←i+1

Regresa l

Capítulo 9 | Página 20
En la siguiente tabla encontramos el resto de las funciones de la librería string y una breve
descripción de ellas.
Nombre Descripción

memcpy Copia n bytes entre dos áreas de memoria que no deben solaparse.

memmove Copia n bytes entre dos áreas de memoria; al contrario que memcpy las áreas deben solaparse.

memchr Busca un valor a partir de una dirección de memoria dada y devuelve un puntero a la primera
ocurrencia del valor buscado o NULL si no se encuentra.

memcmp Compara los n primeros caracteres de dos áreas de memoria.

memset Sobrescribe un área de memoria con un patrón de bytes dado.

strcat Añade una cadena al final de otra.

strncat Añade los n primeros caracteres de una cadena al final de otra.

strchr Localiza un carácter en una cadena, buscando desde el principio.

strrchr Localiza un carácter en una cadena, buscando desde el final.

strcmp Compara dos cadenas numéricamente ('a'!='A').

strncmp Compara los n primeros caracteres de dos cadenas numéricamente ('a'!='A').

strcoll Compara dos cadenas según la colación actual ('a'=='A').

strcpy Copia una cadena en otra.

strncpy Copia los n primeros caracteres de una cadena en otra.

strerror Devuelve la cadena con el mensaje de error correspondiente al número de error dado.

strlen Devuelve la longitud de una cadena.

strspn Devuelve la posición del primer carácter de una cadena que no coincide con ninguno de los caracteres
de otra cadena dada.

strcspn Devuelve la posición del primer carácter que coincide con alguno de los caracteres de otra cadena
dada.

strpbrk Encuentra la primera ocurrencia de alguno de los caracteres de una cadena dada en otra.

strstr Busca una cadena dentro de otra.

strtok Parte una cadena en una secuencia de tokens.

Tabla 9.2. Funciones de la librería estándar string

Capítulo 9 | Página 21
Otra de las librerías ampliamente utilizadas para el manejo cadenas, es la librería ctype, la cual
incluye rutinas de conversión y clasificación de caracteres.

Todas las rutinas para clasificar caracteres tienen el mismo prototipo general:

int isfunc(int);

Donde func es la parte del nombre de la subrutina que cambia de acuerdo a la clasificación que
realiza, todas ellas reciben como parámetro de entrada un entero, el cual representa el valor ascii del
carácter, por tanto es posible enviarle un dato de tipo char, recuerda que los caracteres se codifican
mediante un valor entero entre 0 y 127(código ASCII). Todas las funciones devuelven un valor de
cero cuando el carácter no esta dentro de la clasificación que evalúan y un valor diferente a cero
cuando la validación se cumple La tabla 9.3 resume brevemente las funciones de clasificación de
caracteres.

Función Valores que clasifica

isalnum (A - Z o a - z) o (0 - 9)

isalpha (A - Z o a - z)

isascii 0 - 127 (0x00-0x7F)

iscntrl (0x7F o 0x00-0x1F)

isdigit (0 - 9)

isgraph Imprimibles menos ' ' (espacio)

islower (a - z) Letras minúsuclas

isprint Imprimibles incluido ' ' (espacio)

ispunct Signos de puntuación

isspace espacio, tab, retorno de línea, cambio de línea, tab


vertical, salto de página (0x09 a 0x0D, 0x20).

isupper (A-Z) Letras mayúsculas.

isxdigit (0 to 9, A to F, a to f) Dígito hexadecimal

Tabla 9.3 Funciones para clasificar caracteres dentro de la librería estándar ctype.

Sólo existen dos funciones para convertir caracteres, las cuales se presentan en la tabla 9.4

Capítulo 9 | Página 22
Nombre Prototipo Descripción
Tolower int tolower(int ch); Convierte el carácter que recibe como parámetro a
minúscula. Devuelve el valor ascii de nuevo caracter
Toupper int toupper(int ch); Convierte el carácter que recibe como parámetro a
mayúsculas. Devuelve el valor ascii de nuevo caracter
Tabla 9.4 Funciones para convertir caracteres

9.2.5 Ejemplos de uso de funciones de ctype

Prototipo Valor de retorno Ejemplo


int isalnum(int c); El valor de retorno será #include <stdio.h>
no nulo si c es una letra o #include <ctype.h>
un número, y cero en
main()
caso contrario.
{ char cadena[] = ":9vT?h%J3-";
int i,l=0;

for(i = 0; cadena[i]; i++)


{
printf("%c, %d\n", cadena[i],
isalnum(cadena[i]));
if (isalnum(cadena[i])!=0) l=l+1;
}
printf ("Total de caracteres:%d",l);
system ("pause");
}

Ejecución

Capítulo 9 | Página 23
Prototipo Valor de retorno Ejemplo
int islower(int c); El valor de retorno será #include <stdio.h>
no nulo si c es un #include <ctype.h>
carácter en minúscula.
main()
{ char cadena[] = ":9vT?h%J3-";
int i,l=0;

for(i = 0; cadena[i]; i++)


{
printf("%c, %d\n", cadena[i],
islower(cadena[i]));
if (islower(cadena[i])!=0) l=l+1;
}
printf ("\n\nTotal de
minusculas:%d\n",l);
system ("pause");
}
Ejecución

Prototipo Valor de retorno Ejemplo


int ispunct (int c); El valor de retorno será #include <stdio.h>
no nulo si c es un signo #include <ctype.h>
de puntuación.
main()
{ char cadena[] = ":9vT?h(=#.;%J3-";
int i,l=0;

for(i = 0; cadena[i]; i++)


{
printf("%c, %d\n", cadena[i],
ispunct(cadena[i]));
if (ispunct(cadena[i])!=0) l=l+1;
}
printf ("\n\nTotal de signos de

Capítulo 9 | Página 24
puntuacion:%d\n",l);
system ("pause");
}
Ejecución

Se propone al lector revisar y desarrollar ejemplos con las funciones faltantes de la tabla 9.3.

Capítulo 9 | Página 25
A continuación revisaremos las dos funciones de la tabla 9.4

Prototipo Valor de retorno Ejemplo


int tolower(int ch); Si está entre A y Z lo #include <stdio.h>
convierte a su #include <ctype.h>
equivalente en el rango a
a z, el resto de los valores main()
{
no son modificados. char cadena[] = "UACM, NADA HUMANO
ME ES AJENO";
Nota: los caracteres en int i;
acentuados, o con
diéresis, en mayúscula y for(i = 0; cadena[i]; i++)
la Ñ no sufren cadena[i] = tolower(cadena[i]);
modificaciones.
printf("%s\n", cadena);
system ("pause");
}
Ejecución

Prototipo Valor de retorno Ejemplo


int toupper(int ch); Si está entre a y z lo #include <stdio.h>
convierte a su #include <ctype.h>
equivalente en el rango A
main()
a Z, el resto de los
{
valores no son char cadena[] = "uacm, nada humano
modificados. me es ajeno";
int i;
Nota: los caracteres en
acentuados, o con for(i = 0; cadena[i]; i++)
diéresis, en mayúscula y cadena[i] = toupper(cadena[i]);
la Ñ no sufren
modificaciones. printf("%s\n", cadena);
system ("pause");
}

Capítulo 9 | Página 26
Ejecución

9.2.6 Ejemplo de cadenas uso de funciones de ctype

Veamos un primer ejemplo en que se manipulen cadenas y se utilicen las funciones anteriormente
descritas.

Ejemplo 9.10 Validación de placa


Descripción del Se requiere un programa que dada una cadena determine si puede o no ser una
problema placa de automóvil válida. Considerando que el formato de toda placa consta
de 3 letra seguidas de 3 dígitos.
Análisis y Diseño
Datos de entrada: cadena que represente la placa.

Datos de salida: mensaje de error o validación.

Módulos:
Se utilizaran las funciones de la librería ctype y string

Constantes:
ninguna

Variables:

Placa (cadena):Variable que recibirá el dato de entrada

i (entero): Contador para el ciclo

error (entero): Bandera de error

Capítulo 9 | Página 27
Algoritmo (Pseudocódigo)

Inicio
Hacer
error←0;
Imprimir "Ingresa una placa:"
Leer placa
Si strlen(placa) =6 entonces
Desde i←0 mientras i<6 y error ≠0
Si i<=2 entonces
Si isalpha(placa[i]) ≠ 0 entonces
Imprimir "Los primeros 3 caracteres deben ser
letras"
error←1
Fin si
Sino
Si isdigit(placa[i]) ≠ 0
Imprimir "Los ultimos 3 caracteres deben ser
digitos”
error←1
Fin si
Fin Si- sino
i←i+1
Fin Desde
Si error ≠ 0 entonces
Imprimir placa “puede ser una placa valida”
Sino
Si strlen(placa)>6 entonces
Imprimir "sobran caracteres”
Si no
Imprimir "faltan caracteres”
Fin si-sino
Fin si-sino
Imprimir “Si deseas salir teclea 0, cualquier tecla para continuar”
Mientras getchar( ) ≠ 0'
Fin Hacer mientras
Fin
Codificación en C
Ejemplo 9.10: cadenaeje910.c
Valida que una cadena pueda ser una placa.

Capítulo 9 | Página 28
/* Autor: Juan Pérez
Nombre Programa: cadena1.c
Descripción: Ejemplo que lee una cadena con la función gets.
Fecha de elaboración: 16/09/2008*/

/*Directivas de preprocesador*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>

/*Definición de la función principal*/

main(){

char placa[15]={0}; //Declaración del arreglo para la cadena de entrada


int i,error; //Variables auxiliares

/*El programa se repite mientras el usuario lo desee, pero al menos se


ejecuta una vez, por eso se utiliza un ciclo do-while*/
do{
error=0; /*La variable error se inicializa en cero
y se pone a uno cuando hay error en la placa*/

printf("Ingresa una placa:");


gets(placa); //La cadena se lee mediante la función gets

if (strlen(placa)==6){ /*Se valida que la cadena solo tenga


caracteres 6 caracteres*/

for (i=0;i<6 && !error; i++){ /*En el ciclo termina cuando


ya se revisaron los 6 caracteres
o bien hubo un error en alguno*/
if (i<=2){ //Los primeros 3 deben ser letras o hay error

if(!isalpha(placa[i])){
printf("Los primeros 3 caracteres deben ser letras\n");
error=1;
}
}
else //Si no son los tres primeros entonces deben ser números
if (!isdigit(placa[i])){
printf("Los ultimos 3 caracteres deben ser digitos\n");
error=1;
}
}

/*Ya que se analizó la cadena entonces si no hay error se dice que es

Capítulo 9 | Página 29
valida */

if (!error)printf("%s puede ser una placa valida\n",placa);


}

else /*En caso de que la cadena no tenga 6 caracteres


se indica que la cadena tiene más o menos caracteres*/
if(strlen(placa)>6) printf("sobran caracteres\n");
else printf("faltan caracteres\n");

printf("Si deseas salir teclea 0, cualquier tecla para continuar \n");

fflush(stdin); //Se limpia la entrada estándar antes de la lectura

}while(getchar()!='0'); //Fin del do-while

system("pause");
}

Al codificar en C se están utilizando dos funciones importantes al trabajar con caracteres, getchar y
fflush. En el ciclo do-while se espera leer un carácter diferente al ‘0’ para que el ciclo continué, el
carácter de control se esta leyendo mediante la función getchar que sirve para leer caracteres desde el
teclado, esta función no requiere parámetros de entrada y devuelve el carácter leído desde la entrada
estándar, sin embargo en ocasiones en la entrada estándar se van quedado caracteres de lecturas
anteriores y esto provoca lecturas erróneas cuando se leen caracteres o cadenas.

El problema anteriormente mencionado NO se presenta cuando leemos datos de tipo numérico. Sin
embargo, como en múltiples ocasiones requerimos la lectura de caracteres o cadenas, la solución es
limpiar la entrada antes de hacer la lectura de los datos de entrada y para ello utilizamos a la función
fflush pasándole como parámetro la constate stdin ambas funciones y la constante se encuentran en la
librería estándar stdio.

A continuación revisemos un segundo ejemplo en donde se utilicen cadenas de más de una palabra.

El ejemplo consiste en realizar un programa que remplace una palabra dentro de un párrafo, para ello
es necesario el poder ir separando las palabras dentro del texto. Además será necesario diseñar un
módulo que nos permita extraer una palabra del texto a partir de una posición i al que llamaremos
obtener_palabra(), se diseñará un segundo módulo que sea quien realice el remplazo
correspondiente.

El diseño y codificación de estos módulos (ejemplo 9.11.a y 9.11.b) así como el del módulo principal
(ejemplo 9.11.c) se muestran a continuación.

Capítulo 9 | Página 30
Ejemplo 9.11 a: Análisis y Diseño de la función

obtener_palabra(texto[ ],palabra[], i)

Descripción:
Dado un texto devuelve la siguiente palabra a partir de la posición i

Parámetros de entrada:
 texto[ ]: arreglo de caracteres que contiene el texto
 palabra[ ]: arreglo de caracteres en donde se guarda la palabra obtenida
 i: entero que indica la posición a partir de la cual se extrae la palabra

Valor de retorno:
 i: entero que se actualiza con la posición final de la palabra extraída

Método:
Utilizaremos un ciclo para recorrer el texto copiando carácter por carácter en la palabra hasta
encontrar un espacio o signo de puntuación.

Módulos:
No requiere de ningún otro módulo

Variables locales:
Se utilizan las variables ya mencionadas en los parámetros de entrada y valor de retorno y la variable j de
tipo entero para controlar el ciclo.

Capítulo 9 | Página 31
Algoritmo (Pseudocódigo)

Inicio obtener_palabra(caracter t[],caracter p[],entero i)


Desde j←0 mientras isalpha(t[i]) es verdadero
p[j] ←t[i]
i←i+1
j←j+1
Fin Desde
p[j] ←'\0'
i←i+1;
Regresa i
Fin

Codificación en C

int obtener_palabra(char t[],char p[],int i){


int j;

for(j=0;isalpha(t[i]);j++){
p[j]=t[i]; i++;
}
p[j]='\0';
i++;
return i;
}

Ejemplo 9.11 b: Análisis y Diseño de la función

remplaza(texto[ ],palabra[], i)

Descripción:
Dado un texto remplaza la palabra indicada con otra palabra

Parámetros de entrada:
 texto[ ]: arreglo de caracteres que contiene el texto
 p_orig[ ]: arreglo de caracteres en donde se guarda la palabra original
 p_remp[ ]: arreglo de caracteres en donde se guarda por la cual se va a remplazar

Capítulo 9 | Página 32
Valor de retorno:
 r: contador que lleva el número de remplazos realizados, variable de tipo entera.

Método:
Mediante un ciclo se recorrerá el texto hasta encontrar el fin de cadena y se ira comparando palabra
por palabra para realizar los remplazos necesarios, para generar el nuevo texto se concatenaran en una
nueva auxiliar las palabras analizadas.

Módulos:
entero obtener_palabra(caracter t[ ], caracter p[], entero i);

Variables locales:
Texto2[]:arreglo de tipo carácter que se usará como auxiliar

I: variable entera para llevar la posición del texto durante el proceso

Palabra[]:arreglo de tipo carácter para guardar la palabra extraída del texto

Además se utilizan las variables ya mencionadas en los parámetros de entrada y valor de retorno.

Capítulo 9 | Página 33
Algoritmo (Pseudocódigo)

Inicio remplaza(char p_orig[],char p_remp[],char texto1[])


i←0
r←0
Mientras texto1[i]≠'\0’ hacer
i←obtener_palabra(texto1,palabra,i)
Si strcmp(palabra,p_orig)=0 entonces
strcat(texto2,p_remp)
r←r+1
sino
strcat(texto2,palabra)
Fin si- sino
Si ispunct(texto1[i-1])es verdadero
texto2[strlen(texto2)]=texto1[i-1]
Fin si
strcat(texto2," ")
Fin mientras
strcpy(texto1,texto2)
Regresa r
Fin
Codificación en C

int remplaza(char p_orig[],char p_remp[],char texto1[]){


int i=0,r=0;
char texto2[100]={0};
char palabra[15]={0};

while (texto1[i]!='\0'){
i=obtener_palabra(texto1,palabra,i);

if(!strcmp(palabra,p_orig)){
strcat(texto2,p_remp); r++;}
else
strcat(texto2,palabra);

if(ispunct(texto1[i-1])) texto2[strlen(texto2)]=texto1[i-1];
strcat(texto2," ");
}
strcpy(texto1,texto2);
return r;
}

Capítulo 9 | Página 34
Ejemplo 9.11 c: Remplazar palabra
Descripción del problema Realizar un programa que remplace una palabra por otra dentro de un
párrafo e indique cuantos remplazos se realizaron.
Análisis y Diseño del módulo principal

Datos de entrada:
texto1[100]: Arreglo de caracteres para el texto de entrada.

p_busca[15]: Arreglo de caracteres para la palabra a remplazar.

p_remplazo[15]: Arreglo de caracteres para la palabra con la que se va a remplazar.

Datos de salida:
texto1[100]: Arreglo de caracteres para el texto de entrada.

nr: Entero que indica el número de remplazos realizados

Módulos:
entero remplaza(caracter p_orig[],caracter p_remp[],caracter texto1[]);

Variables:
Sólo se utilizan las variables ya mencionadas en los parámetros de entrada y valor de retorno

Algoritmo (Pseudocódigo)

Inicio
Imprime "Escribe un texto:"
Leer texto1
Imprime "\nPalabra a remplazar:"
Leer p_busca
Imprimir "Remplazar con:”
Leer p_remplazo
Imprimir “Texto original:”texto1
nr←remplaza(p_busca,p_remplazo,texto1)
Imprimir "Nuevo texto:” texto1 “Se remplazaron” nr “palabras”
Fin

Capítulo 9 | Página 35
Codificación en C
Ejemplo 9.10: cadenaeje910.c
Valida que una cadena pueda ser una placa.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int obtener_palabra(char t[],char p[],int i);


int remplaza(char p_orig[],char p_remp[],char texto1[]);

main(){

char texto1[100]={0};
char p_remplazo[15]={0};
char p_busca[15]={0};
int nr=0;

printf("Escribe un texto:");
gets(texto1); /*para poder leer más de una palabra se utiliza la
función gets para leer el texto de entrada*/
printf("\nPalabra a remplazar:");
fflush(stdin); /*es necesario limpiar la entrada estándar par no
tener una lectura errónea*/
scanf("%s",p_busca); /*las palabras se leen con la función scanf*/
printf("Remplazar con:");
fflush(stdin);
scanf("%s",p_remplazo);
printf("Texto original:%s\n\n\n",texto1);
nr=remplaza(p_busca,p_remplazo,texto1); /*Se llama a la función
remplaza que a su ves llamará a la función obtener_palabra*/

printf("Nuevo texto:%s\n Se remplazaron %d palabras\n",texto1,nr);

system("pause");

Capítulo 9 | Página 36
Ejecución

Para concluir con este capítulo, trabajaremos un último ejercicio a nivel de algoritmo y de diseño de
la solución para dejar al lector la implementación del mismo. Este ejercicio tiene como objetivo
reforzar muchos de los conocimientos aprendido en este capítulo.

Ejemplo 9.12 Planteamiento del Problema


Pig latin es un juego de idioma usado principalmente en inglés. El Pig Latin lo usan los niños para divertirse o
para conversar secretamente sobre adultos u otros niños. Recíprocamente, adultos a veces lo usan para hablar
de temas sensibles que quieren que los niños no entiendan.

Los turistas anglohablantes a veces usan el Pig Latin para disimular sus conversaciones cuando viajan por
países donde el inglés es el segundo idioma.

REGLAS

Las reglas comunes de cambiar una palabra a Pig Latin son:

1. Para palabras que comienzan con sonidos de consonantes, mueva todas las consonantes que inician la
palabra al final y agrega la silaba "e" ("ay" en inglés). Ejemplos:
o mesa → esa-me
o padre → adre-pe
o chico → ico-che
o quemar → uemar-qe
2. Para palabras que comienzan con sonidos de vocales (incluyendo consonantes mudas), se agrega "e" al
final de la palabra.
o alma → alma-e
o hablar→ hablar-e

Obtenido de "http://es.wikipedia.org/wiki/Pig_Latin"

Capítulo 9 | Página 37
Ejemplo 9.12: Análisis y Diseño de la función rev_vocales (cadena[ ])

Descripción:
Dada una cadena, identifica el número total de vocales.

Parámetros de entrada:
 cadena[ ]: arreglo de caracteres que contiene una palabra

Valor de retorno:
 n_vocals: lleva el número de vocales encontradas en la palabra, variable de tipo entera.

Método:
Mediante un ciclo se recorrerán todas las letras que conforman el texto hasta encontrar el fin de
cadena y se comparara cada letra de la cadena con las vocales (a,e,i,o,u), cuando exista coincidencia se
incrementara en uno el valor de la variable n_vocals, esta variable almacenara el número total de vocales
encontradas .

Módulos:
No utiliza

Variables locales:
i: variable entera que permite llevar la posición de la cadena durante el proceso

Además se utilizan las variables ya mencionadas en los parámetros de entrada y valor de retorno.

Capítulo 9 | Página 38
Algoritmo (Pseudocódigo)

Inicio rev_vocales ( char cadena[ ] )


n_vocals←0
i← 0
Mientras cadena[i]≠'\0’ hacer
Si cadena[i]='a' ˅ cadena[i]='e' ˅ cadena[i]='i'
˅ cadena[i]='o' ˅ cadena[i]='u' entonces

n_vocals←n_vocals+1
i← i +1

Fin si
Fin mientras
Regresa n_vocals
Fin

Ejemplo 9.12: Análisis y Diseño de la función codec (cadena[])

Descripción:
Dada una cadena, codifica el texto en Pig Latin aplicando la regla de las palabras que comienzan con
sonido de consonante.

Parámetros de entrada:
 cadena[ ]: arreglo de caracteres que contiene una palabra

Valor de retorno:
No tiene

Método:
A todas las cadenas que comienzan con sonidos de consonantes ( las palabras del español tienen 1 o 2
consonantes máximo al inicio de una palabra),se les aplica lo siguiente:

 Se realiza en un arreglo temporal una copia de la primera consonante que se encuentre al inicio
de la cadena
 Se verifica la segunda letra de la cadena, si es una consonante “l” , “r” o “h”, se copia esta
consonante en la segunda posición del arreglo temporal y se agrega el fin de cadena, en caso

Capítulo 9 | Página 39
contrario se agrega el fin de cadena en la segunda posición del arreglo temporal.
 Se obtiene el tamaño del arreglo temporal (el cual solo puede tener dos valores 1 o 2), el valor
obtenido es la posición inicial impresa en pantalla del arreglo cadena.
 Se imprime un guion, el arreglo temporal y la letra e (letra para distinguir una palabra en piglatin)

Módulos:
No utiliza

Variables locales:

i: variable entera que permite verificar la posición de las consonantes que se encuentran al inicio de
una palabra

j: variable entera que permite llevar la posición de la cadena durante el proceso

tempcons[]: arreglo de caracteres que guarda las consonantes encontradas al inicio de una palabra

Además se utilizan las variables ya mencionadas en los parámetros de entrada y valor de retorno.

Algoritmo (Pseudocódigo)

Inicio codec ( char cadena[ ] )

i←0
j←0
tempcons[i]←cadena[i]
i←i+1
Si cadena[i]='l' ˅ cadena[i]='r' ˅ cadena[i]='h' entonces
tempcons[i]=cadena[i]
Fin si
i←i+1
tempcons[i]='\0';
imprimir “Piglatin: “
j←total de caracteres (tempcons)
Mientras cadena[j]≠'\0’ hacer
Imprimir cadena[j]
Imprimir “-“, tempcons,”e”
j← j +1
Fin mientras
Fin

Nota: el total de caracteres de una cadena se obtiene en lenguaje C, con la función strlen

Capítulo 9 | Página 40
Análisis y Diseño del módulo principal

Datos de entrada:
chain[20]: Arreglo de caracteres para el texto de entrada.

flag: variable que permite saber si el texto introducido es una palabra o solo una letra

exit: variable carácter que permite identificar con una “c”, si se desea continuar verificando como se
escriben palabras en pig latin o una “t” para terminar y salir del programa.

Datos de salida:

Módulos:
entero rev_vocales(cadena[])

codev (cadena[])

códec (cadena[])

Variables:
Sólo se utilizan las variables ya mencionadas en los parámetros de entrada y valor de retorno

Capítulo 9 | Página 41
Algoritmo (Pseudocódigo)

Inicio
Hacer
Imprime “Bienvenid@ al programa Pig Latin Traslator! v1.0”
Imprime “Ingresar la palabra:”
Leer chain
/* convierte la cadena ingresada a minúsculas*/
Si rev_vocales(chain)=0 entonces
Imprime “No es una palabra, faltan vocales”
flag←1
Fin_Si
Si total de caracteres (chain)=rev_vocales(chain) entonces
Imprime “No es una palabra, faltan consonantes”
flag←1
Fin_Si
Si flag=0 entonces
Si cadena[i]='a' ˅ cadena[i]='e' ˅ cadena[i]='i'
˅ cadena[i]='o' ˅ cadena[i]='u' entonces
codev(chain)
sino
códec(chain)
Fin_Si_sino
Fin_Si
Imprime “[t] End [c] Continue Opcion:”
Leer exit
Mientras exit≠'t'
Fin_Hacer_Mientras
Fin

Nota: el total de caracteres de una cadena se obtiene en lenguaje C, con la función strlen

Finalmente se deja como ejercicio el análisis y diseño de la función más sencilla al usuario, es decir, la función
que se encargará de codificar el texto en Pig Latin aplicando la regla de las palabras que comienzan con
sonido de vocal.

Capítulo 9 | Página 42
Ejecución

Capítulo 9 | Página 43
PRÁCTICA DE LABORATORIO No.9

SECCIÓN I.

Contesta falso o verdadero para los siguientes enunciados:


a. El %c es el especificador de formato que me permite imprimir o leer todos los elementos de
un arreglo de tipo carácter( )
b. Para guardar una cadena en memoria se necesita un arreglo de caracteres ( )
c. La cadena “Hola” es igual a la cadena “hola” ( )
d. El carácter ‘\0’ es el carácter que se utiliza como fin de cadena ( )
e. La declaración char x[]=”azul”; declara un arreglo de tipo carácter de tamaño 5 ( )
f. La función gets me permite leer una cadena de más de una palabra ( )
g. Para declarar un arreglo que me permita guardar una cadena es necesario conocer el tamaño de la
cadena ( )

SECCIÓN II.
I. Declara un arreglo de tamaño adecuado para guardar cada uno de los siguientes datos de
entrada

Dato de entrada Declaración del arreglo

La placa de un automóvil char placa[5];

EL nombre de un día de la semana

La CURP de una persona

II. Escribe en las casillas correspondientes el contenido que tendría el arreglo con las siguientes
declaraciones:

a) char nom[10]=”ANA”;

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]

Capítulo 9 | Página 44
[0] [1] [2] [3] [4] [5] [6] [7]
b) char clave[8]={‘K’};

c) char CURP[18]=”JHLI751212MDFNZR09”;

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]

I. Indica cuales son los errores que se encuentran en el siguiente código y como corregirlos.

Código Error Corrección

#include <stdio.h>
#include <stdlib.h>
#include <string>
main(){
char a[5];
char b[]=”1258”;
char c[15]=0;
int i;
a=”letras”;
printf(“Ingresa una
cadena:”);
scanf(“%s”,c[]);
for(i=0;i<strlen(b);i++)
if(isdigit(b[i]))
j++;
printf(“La cadena leida
es:%c\n”,c);
printf(“Se encontraron %d
digitos en %s”,j);
}

II. Implementa una función que devuelva el índice de la primera ocurrencia de un carácter en una
cadena.
III. Prueba el siguiente código e indica si la salida corresponde a la que se muestra en la figura, si no
es así modifica el código para que así sea.
IV. Realiza un programa que pida tu nombre y lo muestre escrito al revés, implementar una función
para invertir la cadena.
V. Realizar un programa que codifique cada una de las palabras en un mensaje invirtiendo, e
indique al final el total de palabras invertidas.

Capítulo 9 | Página 45
Capítulo

10
Arreglos bidimensionales
“Ley de Alzheimer de la programación: si lees un código que escribiste hace más de dos
semanas es como si lo vieras por primera vez”.

Dan Hurvitz

OBJETIVOS:

Al finalizar esta práctica el estudiante:


o Conocerá el concepto de arreglos bidimensionales y lo aplicará en la resolución de
problemas que involucren el procesamiento de conjuntos de datos homogéneos.
o Aprenderá a declarar un arreglo bidimensional y cómo manipularlo secuencialmente
o utilizando ciclos anidados.
o Utilizará arreglos bidimensionales como parámetros de entrada de funciones.
o Conocerá las operaciones básicas que se pueden realizar sobre un arreglo
bidimensional e implementará funciones en lenguaje C para cada una de ellas.

10.1 INTRODUCCIÓN
Los arreglos bidimensionales son estructuras de datos estáticas que permiten agrupar
elementos del mismo tipo y almacenarlos en un en un bloque de memoria juntos, uno
después del otro. A este grupo de elementos se les identifica usando el mismo nombre y dos
índices (también llamados posiciones o subíndices), el primero indica la fila del elemento y
el segundo la columna del elemento.

Al desarrollar un programa y almacenar información dentro de las estructuras estáticas


llamadas arreglos bidimensionales, resulta sencillo acceder a sus elementos indicando la
posición de la columna y la fila del arreglo, sin embargo existen cuatro patrones de
comportamiento que comúnmente se presentan para procesar los datos contenidos en la
matriz y se describen a continuación:

1. Acceso Aleatorio. En este caso el usuario ingresa la fila y columna para ubicar
exactamente, el elemento del arreglo al que se desea acceder.
2. Acceso por Columna. En este caso se accede a todos y cada uno de los elementos que
forman parte de la columna del arreglo bidimensional.
3. Acceso por Fila. En este caso se accede a todos y cada uno de los elementos que forman
parte de la fila del arreglo bidimensional.
4. Acceso por Entero. En este caso se accede a todos los elementos que forman parte del
arreglo bidimensional.
Algunos ejemplos de diversas situaciones en las cuales se requiere procesar datos
relacionados entre sí, utilizando los patrones de comportamiento antes descritos son los
siguientes problemas:

Imaginemos que utilizamos un arreglo de 4 x 7 para almacenar el registro de la temperatura


de un mes en la Ciudad de México. Las filas representarían las temperaturas registradas en
la semana, mientras que las columnas representarían un día de la semana específico
domingo, lunes, martes, miércoles, jueves, viernes o sábado.
Problema 1.- Si requerimos un programa que obtenga el promedio de las temperaturas
por semana, tendremos un procesamiento de datos con comportamiento “acceso por
fila”, ya que tendremos que sumar todos los elementos de la fila y dividirlos entre
siete para obtener el promedio de cada semana.

Problema 2.- Si necesitamos un programa que obtenga el promedio de las temperaturas


por un día de la semana, por ejemplo el sábado, tendremos que realizar un
procesamiento de datos con comportamiento “acceso por columna”, ya que
tendremos que sumar todos los elementos de la columna y dividirlos entre cuatro,
que son las cuatro semanas de datos de un mes.

Problema 3.- Si solicitamos un programa que obtenga el promedio de la temperatura de


ese mes, tendremos que realizar un procesamiento de datos con comportamiento
“acceso por entero”, ya que tendremos que sumar todos los elementos del arreglo y
dividirlos entre el tamaño del arreglo, el cual para este caso se obtiene del producto
de multiplicar las filas y columnas, para este caso será 28.

Problema 4.- Si es necesario desarrollar un programa que obtenga la temperatura del


tercer día de la semana uno, tendremos que realizar un procesamiento de datos con
comportamiento “acceso aleatorio”, ya que tendremos que dar el nombre del
arreglo y la posiciones de la fila y la columna, para el caso propuesto haríamos
referencia temperaturasCDMX [ 0 ][ 2 ].

10.2 DEFINICIÓN DE ARREGLO BIDIMENSIONAL

Los arreglos bidimensionales, al igual que los unidimensionales son una colección finita,
homogénea y ordenada de datos, son también conocidos en programación como tabla,
matriz o como un vector de vectores.

Técnicamente, es una estructura de datos estática y almacena datos de un mismo tipo, es


decir, es un espacio de memoria que almacena una colección de elementos del mismo
aunque no se organiza linealmente, sino que se organiza utilizando dos subíndices, uno para
las filas y otro para las columnas.

Un arreglo bidimensional puede verse como una colección de variables de un mismo tipo
que se identifican bajo un mismo nombre, la forma en que se diferencian los elementos del
arreglo es a través de la posición que ocupan y se accede a ella como si fuera una
coordenada cuya primera parte hace referencia a la fila y la otra a la columna.
Tomando como base que en el capítulo 8, hemos visto que un arreglo unidimensinal de n
elementos puede ser visto como una lista de valores. Análogamente, un arreglo
bidimensional de m x n puede ser visto como una tabla de valores que tiene m filas y n
columnas.

Una matriz ROWS*COLS tiene filas y columnas; por lo anterior el número de elementos es
el producto de ROWS*COLS y se almacenaran en memoria de forma sucesiva.

Gráficamente se puede representar como una tabla de valores.

Nombre del arreglo Columnas (C) COLS=3

matriz [ 0 ] matriz [ 1 ] matriz [ 2 ]

matriz[ 0 ] matriz (0,0) matriz (0,1) matriz (0,2)


matriz[ 1 ] matriz (1,0) matriz (1,1) matriz (1,2)
matriz[ 2 ] matriz (2,0) matriz (2,1) matriz (2,2)
matriz[ 3 ] matriz (3,0) matriz (3,1) matriz (3,2)

Filas (F) ROWS=4

Valores almacenados en el arreglo bidimensional matriz

1 2 3
4 5 6
7 8 9
10 11 12
Figura 10.1. Representación gráfica de un arreglo bidimensional

La definición formal se presenta a continuación junto con los otros términos derivados de
los arreglos:

Definición 10.1: Un arreglo bidimensional representa un conjunto de datos del mismo tipo
dispuestos en memoria de forma consecutiva, a los que se accede a través de dos índices
(posiciones). A cada uno de los datos que componen el arreglo se les denomina elementos.

En lenguaje C el tipo de datos que se almacena en un arreglo puede ser cualquiera de los
tipos básicos de C, es decir: int, char, float, double.

Definición 10.2: La posición que ocupa un elemento dentro de una matriz se le denomina
formalmente índice de la fila e índice de la columna.
Definición 10.3: El tamaño o longitud de un arreglo bidimensional se define como el
producto del número de filas y columnas que lo constituyen. Se calcula multiplicando el
índice de la fila y el índice de la columna, esto significa que si tenemos una matriz [3][3], el
total de elementos que se pueden guardar son 9 elementos.

En lenguaje C la numeración de los arreglo empieza en cero, es decir, al primer elemento


del arreglo le corresponde la fila 0 y la columna 0, y al último le corresponde la fila
ROWS-1 y la columna COLS-1 (Ver Figura 10.1.).

En los arreglos bidimensionales, el tamaño del arreglo se calcula obteniendo el producto de


multiplicar el total de fila y columnas (ROWS*COLS)

10.3 DECLARACIÓN E INICIALIZACIÓN DE MATRICES

La declaración de un arreglo bidimensional o matriz consiste en reservar espacio de


memoria para el conjunto de datos homogéneos, para declarar una variable de tipo arreglo
se siguen las misma reglas que para las variables simples; con la única diferencia que
después del identificador se escribe el tamaño del arreglo encerrado entre dos pares de
corchetes cuadrados, uno para las filas y otro para la columnas “[ ROWS ]” “ [ COLS ]”.

Sintaxis de la declaración de una matriz en Lenguaje C:

<tipo> <nombre> [ <rows> ] [ <cols> ];

Figura 10.2. Declaración de una matriz

Como ya hemos visto, los identificadores de una variable de tipo matriz debe cumplir con
las mismas reglas que cualquier otro identificador de variable simple.

Para ejemplificar la declaración de un arreglo bidimensional regresemos al ejemplo de la


Figura 10.1, la instrucción en lenguaje C para declarar el arreglo matriz sería:

int matriz [4][3];

Esta instrucción de C, reserva un espacio de memoria continuo, el cual permite almacenar


hasta 12 elementos de tipo entero bajo el nombre de matriz.

Otro aspecto importante en lenguaje C, es que se pueden declarar e inicializar los valores de
un arreglo bidimensional en una misma instrucción, basta con asignar el conjunto de
elementos encerrado entre llaves “{ }”, separándolos por comas.

Es importante mencionar que en el interior de la lista, los componentes de cada línea del
array sean encerrados nuevamente entre llaves. Para hacer más clara la visibilidad de los
elementos del array, podemos indicarlos en varias líneas
Sintaxis de la declaración e inicialización de una matriz en Lenguaje C:

<tipo> <nombre> [<rows>] [<cols>] = { <elem1>, <elem2>, …


,<elemTAM-1> };

Donde TAM es el producto de rows*cols


Figura 10.3. Declaración e inicialización de una matriz

Para ejemplificar la declaración de un arreglo bidimensional regresemos al ejemplo de la


Figura 10.1, observa los valores asignados a la matriz.

La instrucción en lenguaje C para declarar el arreglo matriz con los valores de cada
elemento del arreglo sería:

int matriz [4][3]= ={{ 1,2,3},


{ 4,5,6},
{ 7,8,9},
{ 10,11,12}};

Cuando se inicializa un arreglo bidimensional al momento de su declaración no es


necesario especificar el tamaño del arreglo pues el compilador define por default el tamaño
del arreglo como el número de elementos con los cuales se está inicializando. La
instrucción anterior se puede reemplazar por la siguiente:

int matriz[][] = ={ { 1,2,3},


{ 4,5,6},
{ 7,8,9},
{ 10,11,12}};

Observa que no se especificó el tamaño del arreglo bidimensional.

Es importante recordar que la instrucción que inicializa todos los elementos de un arreglo
bidimensional, al mismo tiempo, sólo es válida cuando se declara el arreglo bidimensional;
si se intenta realizar en otro momento el compilador marcará un error.

Una buena práctica de programación consiste en definir el tamaño de un arreglo


bidimensional por medio de una constante, de esta manera cuando se requiera cambiar el
tamaño del arreglo bidimensional bastará con hacerlo en la definición de la constante como
se verá más adelante.

Otro aspecto importante a ser considerado dentro de la inicialización de los arreglos


bidimensionales es que al inicializar un arreglo bidimensional al momento de su
declaración con al menos un valor, este valor se asigna a la posición (0,0) del arreglo y
todos los demás elementos quedan inicializados con 0. Así, por ejemplo:

int matriz[4][3] = {5};


Asigna un 5 a la posición (0,0) del arreglo bidimensional matriz, pero las posiciones
(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2),(3,0),(3,1) y (3,2) tendrán asignado por default
el valor 0.

10.4 ACCESO A LOS ELEMENTOS DE UN ARREGLO


BIDIMENSIONAL

Como mencionamos anteriormente, la forma de referirse a un elemento específico de una


matriz es por medio del nombre del arreglo bidimensional, seguido del índice de fila y el
índice de la columna del elemento, ambos índices encerrados entre corchetes.

Sintaxis para acceder a un elemento de una matriz:

<nombre de la matriz> [ <índice de la fila del


elemento> ] [ <índice de la columna del
elemento> ]

Figura 10.4. Acceso a un elemento de un arreglo bidimensional

Partiendo de la premisa que el número de índices determina la dimensionalidad del arreglo.


Por ejemplo, x[i] referencia a un elemento del arreglo unidimensional x. Análogamente,
x[i][j] referencia a un elemento del arreglo bidimensional x, esto significa que un arreglo
bidimensional se puede pensar como una tabla, donde x[i][j] es el elemento j de la fila [i].

La manipulación por separado de los elementos de un arreglo bidimensional se hace de


igual forma que para las variables simples. Revisemos los siguientes ejemplos basados en el
arreglo bidimensional matriz de la Figura 10.1.

a) Asignación de un valor a un elemento de un arreglo bidimensional.

Acción Instrucción en pseudocódigo Instrucción en código C


Asignar 8 al cuarto elemento del
matriz[1][0] ← 8 matriz[1][0] = 8;
arreglo bidimensional matriz.

b) Impresión del elemento de un arreglo bidimensional desde el dispositivo estándar de


salida.

Acción Instrucción en pseudocódigo Instrucción en código C


Imprime el último elemento
Imprimir printf(“%d”,matriz[3]
del arreglo bidimensional matriz[3][2] [2]);
matriz.
c) Lectura de un elemento del arreglo bidimensional desde el dispositivo estándar de
entrada.

Acción Instrucción en pseudocódigo Instrucción en código C


Lee un entero en la primera
scanf(“%d”,&matriz[0]
posición del arreglo Leer matriz[0][0]
[0]);
matriz.

Observa que al igual que las variables simples, cuando se lee un valor en un elemento de un
arreglo bidimensional también se utiliza el símbolo “&”.

d) Realizar operaciones con los elementos de un arreglo.

Acción Instrucción en pseudocódigo Instrucción en código C


Asigna a la variable x la
raíz cuadrada del segundo x ← (matriz[0]
x= sqrt(matriz[0][1]);
elemento del arreglo [1])1/2
bidimensional matriz
Incrementar en uno el
matriz[0]
tercer elemento del arreglo matriz[0][2]++;
[2]←matriz[0][2]+1
bidimensional matriz

e) Paso de parámetro de un elemento de un arreglo a una función definida por el


programador.

Acción Instrucción en pseudocódigo Instrucción en código C


Llamada a la función
duplica con el
primer elemento del
arreglo. Suponiendo
que la función x= duplica(matriz[0]
x ← duplica(matriz[0][0])
duplica recibe [0]);
como parámetro de
entrada un entero y
devuelve el doble del
mismo.

De acuerdo con el último ejemplo, concluimos que, los elementos de un arreglo


bidimensional se pueden pasar como parámetros a cualquier función (cuyos parámetros
correspondan al tipo de datos) de igual forma que las variables simples; tal y como se hizo
en los ejemplos anteriores con la función duplica, printf, scanf y sqrt.

En el siguiente programa se ilustra con mayor detalle la manipulación de los elementos de


un arreglo bidimensional en lenguaje C. El ejercicio consiste en llenar secuencialmente los
datos de una matriz de 4x5 elementos y sumar fila por fila y columna por columna,
guardando los resultados en dos vectores, uno vertical para las filas, y otro horizontal para
las columnas, al final se imprime la matriz ingresada y el resultado de sumar las filas y las
columnas de la matriz, resultados guardados en el vector vertical y el vector horizontal.

Programa 10.1. :calculaMatrizV1.c


Calcula la suma de las filas y columnas de una matriz de 4x5 elementos proporcionada por
el usuario
/* Autor: Nombre del Programador
Nombre Programa: calculaMatrizV1.c
Descripción: Calcula la suma de las filas y las columnas de una
matriz de 4x5 elementos proporcionada por el usuario.
Fecha de elaboración: Día/Mes/Año
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

main()
{
/*Definimos la matriz de 4x5*/
int matriz[4][5];
/*Definimos el vector horizontal*/
int horizontal[5];
/*Definimos el vector vertical*/
int vertical[4];
/*Definimos variables auxiliares*/
int fila,columna;
int suma=0;
int i;

printf
("\n****************************************************************
******************");
printf ("\n*El siguiente programa ingresa un matriz de 4x5
elementos,suma filas y columnas");
printf
("\n****************************************************************
******************\n\n");

/*Intrucciones que registran los datos que llenan la matriz 4x5*/


printf("Introduce un numero para la posicion [0,0]: ");
scanf("%d",&matriz[0][0]);
printf("Introduce un numero para la posicion [0,1]: ");
scanf("%d",&matriz[0][1]);
printf("Introduce un numero para la posicion [0,2]: ");
scanf("%d",&matriz[0][2]);
printf("Introduce un numero para la posicion [0,3]: ");
scanf("%d",&matriz[0][3]);
printf("Introduce un numero para la posicion [0,4]: ");
scanf("%d",&matriz[0][4]);
printf("Introduce un numero para la posicion [1,0]: ");
scanf("%d",&matriz[1][0]);
printf("Introduce un numero para la posicion [1,1]: ");
scanf("%d",&matriz[1][1]);
printf("Introduce un numero para la posicion [1,2]: ");
scanf("%d",&matriz[1][2]);
printf("Introduce un numero para la posicion [1,3]: ");
scanf("%d",&matriz[1][3]);
printf("Introduce un numero para la posicion [1,4]: ");
scanf("%d",&matriz[1][4]);
printf("Introduce un numero para la posicion [2,0]: ");
scanf("%d",&matriz[2][0]);
printf("Introduce un numero para la posicion [2,1]: ");
scanf("%d",&matriz[2][1]);
printf("Introduce un numero para la posicion [2,2]: ");
scanf("%d",&matriz[2][2]);
printf("Introduce un numero para la posicion [2,3]: ");
scanf("%d",&matriz[2][3]);
printf("Introduce un numero para la posicion [2,4]: ");
scanf("%d",&matriz[2][4]);
printf("Introduce un numero para la posicion [3,0]: ");
scanf("%d",&matriz[3][0]);
printf("Introduce un numero para la posicion [3,1]: ");
scanf("%d",&matriz[3][1]);
printf("Introduce un numero para la posicion [3,2]: ");
scanf("%d",&matriz[3][2]);
printf("Introduce un numero para la posicion [3,3]: ");
scanf("%d",&matriz[3][3]);
printf("Introduce un numero para la posicion [3,4]: ");
scanf("%d",&matriz[3][4]);

/*Instrucciones que suman las filas y registran el resultado en el


vector vertical*/
vertical[0]=matriz[0][0]+matriz[0][1]+matriz[0][2]+matriz[0][3]+matriz[0][4];
vertical[1]=matriz[1][0]+matriz[1][1]+matriz[1][2]+matriz[1][3]+matriz[1][4];
vertical[2]=matriz[2][0]+matriz[2][1]+matriz[2][2]+matriz[2][3]+matriz[2][4];
vertical[3]=matriz[3][0]+matriz[3][1]+matriz[3][2]+matriz[3][3]+matriz[3][4];

/*Instrucciones que suman las columnas y registran el resultado en


el vector horizontal*/
horizontal[0]=matriz[0][0]+matriz[1][0]+matriz[2][0]+matriz[3][0]+matriz[4][0];
horizontal[1]=matriz[0][1]+matriz[1][1]+matriz[2][1]+matriz[3][1]+matriz[4][1];
horizontal[2]=matriz[0][2]+matriz[1][2]+matriz[2][2]+matriz[3][2]+matriz[4][2];
horizontal[3]=matriz[0][3]+matriz[1][3]+matriz[2][3]+matriz[3][3]+matriz[4][3];

/*Impresión de la Matriz Ingresada*/


printf("\nMatriz Ingesada \n");
printf("\n %i %i %i %i %i\n",
matriz[0][0],matriz[0][1],matriz[0][2],matriz[0][3],matriz[0][4]);
printf("\n %i %i %i %i %i\n",
matriz[1][0],matriz[1][1],matriz[1][2],matriz[1][3],matriz[1][4]);
printf("\n %i %i %i %i %i\n",
matriz[2][0],matriz[2][1],matriz[2][2],matriz[2][3],matriz[2][4]);
printf("\n %i %i %i %i %i\n",
matriz[3][0],matriz[3][1],matriz[3][2],matriz[3][3],matriz[3][4]);

/*Recorre el vector vertical y muestra los resultados*/


printf("\nResultado de Sumar Filas\n");
for(i=0;i<4;i++)
{
printf(" %d\n",vertical[i]);
}

/*Recorre el vector horizontal y muestra los resultados*/


printf("\nResultado de Sumar Columnas \n");
for(i=0;i<5;i++)
{
printf("%d ",horizontal[i]);
}
printf ("\n ");
system("PAUSE");
}

La ejecución del Programa 10.1 se muestra en el siguiente cuadro.

Ejecución Programa 10.1.: calculaMatrizV1.c


A diferencia de otros lenguajes de programación, el lenguaje C no verifica que los índices
del arreglo bidimensional estén en el rango definido, por lo que se debe evitar sobrepasar el
índice máximo de la fila y la columna, ya que de lo contrario puedes tener resultados
impredecibles. Es responsabilidad del programador asegurar que nunca se intente acceder a
un elemento de la matriz inexistente, por lo cual se debe cuidar que los índices siempre
sean mayores o iguales que cero y menores que el tamaño del arreglo.

10.5 PROCESAMIENTO DE ARREGLOS CON CICLOS


Los arreglos y los ciclos están estrechamente relacionados, podríamos afirmar que en
cualquier programa que se emplee un arreglo bidimensional éste será manipulado por
medio de una estructura repetitiva anidada. Anteriormente mencionamos que un arreglo se
utiliza cuando queremos almacenar y manipular datos relacionados entre sí y,
generalmente, cuando tenemos un arreglo bidimensional debemos ejecutar el mismo
conjunto de operaciones sobre cada uno de sus elementos, tal y cual lo hicimos en el
Programa 10.1 donde se leyó e imprimió secuencialmente (uno por uno) cada uno de los
elementos de la matriz; éstas operaciones pueden realizarse utilizando un ciclo anidado. En
el siguiente cuadro se muestra las instrucciones secuenciales para imprimir los elementos
del arreglo bidimensional de 4 x 5 elementos llamado matriz en el programa 10.1.

/*Impresión de la Matriz Ingresada*/

printf("\nMatriz Ingesada \n");


printf("\n %i %i %i %i %i\n",
matriz[0][0],matriz[0][1],matriz[0][2],matriz[0][3],matriz[0][4]);
printf("\n %i %i %i %i %i\n",
matriz[1][0],matriz[1][1],matriz[1][2],matriz[1][3],matriz[1][4]);
printf("\n %i %i %i %i %i\n",
matriz[2][0],matriz[2][1],matriz[2][2],matriz[2][3],matriz[2][4]);
printf("\n %i %i %i %i %i\n",
matriz[3][0],matriz[3][1],matriz[3][2],matriz[3][3],matriz[3][4]);

Figura 10.5. Impresión secuencial de un arreglo bidimensional

Observemos que lo único que cambia en cada una de las instrucciones son los índices del
elemento que se imprime. Observe cuidadosamente las instrucciones de la figura 10.5 y el
primer printf, permite que se impriman todos y cada uno de los elementos de la Fila 0,
posteriormente el segundo printf, permite que se impriman todos y cada uno de los
elementos de la Fila 1 hasta llegar a la Fila 3. De lo anterior, podemos deducir que es
posible utilizar un ciclo anidado para imprimir las filas y columnas, es decir todos los
elementos de un arreglo bidimensional, sustituyendo los índices de los elementos por dos
contadores como lo muestra en la siguiente figura.
/*Impresión de la matriz ingresada*/

printf ("\nMatriz Ingresada\n");


for (fila=0;fila<4;fila++){
for (columna=0;columna<5;columna++){
printf ("%i ",matriz[fila][columna]);
}
printf ("\n");
}
Figura 10.6. Impresión de un arreglo bidimensional por medio de un ciclo

Observemos que el número de iteraciones que se ejecutan es 20, en la primera el valor de


fila es 0 y la columna es 0, en la segunda fila es 0 y columna 1, en la tercera fila es 0 y
columna 2, en la cuarta fila 0 y columna 3, en quinta fila 0 y columna 4, hasta este punto el
hemos terminado de recorrer todas las columnas de la fila 0, posteriormente recorreremos
todas las columnas de la fila 1, todas las columnas de la fila 2, hasta llegar a recorrer todas
las columnas de la fila 3, de tal manera que se ejecutan las 20 iteraciones que despliegan el
total de elementos de la matriz.

Matriz 4 x 5

Columna 0 Columna 1 Columna 2 Columna 3 Columna 4


Fila 0 Matriz [0][0] Matriz [0][1] Matriz [0][2] Matriz [0][3] Matriz [0][4]
Fila 1 Matriz [1][0] Matriz [1][1] Matriz [1][2] Matriz [1][3] Matriz [1][4]
Fila 2 Matriz [2][0] Matriz [2][1] Matriz [2][2] Matriz [2][3] Matriz [2][4]
Fila 3 Matriz [3][0] Matriz [3][1] Matriz [3][2] Matriz [3][3] Matriz [3][4]
Figura 10.7. Impresión del arreglo bidimensional, matriz [4][ 5]

Una de las principales ventajas de utilizar ciclos para manipular arreglos es la facilidad que
existe para modificar un programa, en el cual se requiere cambiar el tamaño del arreglo. Por
ejemplo, si se desea imprimir un arreglo de 4 x 2 elementos; en la versión secuencial sería
necesario agregar 8 instrucciones más, mientras que en la versión con ciclos basta con
cambiar la condición del ciclo por filas < 4 y columnas < 2.

Es importante mencionar que con el fin de facilitar la comprensión de la figura 10.6, los
contadores de los ciclos se llaman filas y columnas, sin embargo encontraremos en internet,
libros de programación, programas que pudiesen utilizar como nombres de contadores i
para representar las filas y j para representar las columnas, esto es tener un arreglo
bidimensional, matriz[i][j].

Anteriormente hemos visto que todas las estructuras repetitivas son equivalentes, así que
usaremos el ciclo for para manipular arreglos, sin embargo también se podría utilizar
cualquiera de las otras estructuras repetitivas; utilizaremos el for principalmente, por la
claridad con la que podemos representar el procesamiento del arreglo: el ciclo externo
recorre las filas y el ciclo más interno las columnas, ambas estructuras repetitivas tendrán
un contador que se inicia en cero que es el índice de la fila y de la columna del primer
elemento, el incremento del contador es de uno para recorrer todo el arreglo elemento por
elemento y la condición debe ser que el contador sea “menor estricto” que el tamaño de las
filas y columnas del arreglo, o bien, menor o igual al tamaño de las filas y columnas del
arreglo menos uno (dado que los índices válidos para un arreglo de tamaño TAM son:
0,1,2, …, TAM-1).

La forma general de procesar un arreglo por medio de un ciclo se muestra en el siguiente


cuadro.

Sintaxis general del procesamiento de arreglos mediante un ciclo for

Algoritmo 10.1.: Procesamiento de un arreglo bidimensional mediante un ciclo for


Pseudocódigo Lenguaje C
Desde i ← 0 Mientras i < TAMFil, i ← i + 1 for (i=0;i<TAMFil;i++){
Desde j ← 0 Mientras j < TAMCol, j ← j + 1 for (j=0;j<TAMCol;j++){
Procesar matriz[i][j]
Procesar Matriz[ i ] [ j ] }/*Fin ciclo columnas*/
printf ("\n");
Fin Desde }/*Fin ciclo filas*/
Fin_Desde
Lenguaje C

i←0

F
i < TAMFil
V

j←0

F
j < TAMCol

Procesar Matriz [ i ] [ j ]

j←j+1

i←i+1

Algoritmo 10.1. Procesamiento de un arreglo utilizando un ciclo anidado (Pseudocódigo y Diagrama de


Flujo)
Para ejemplificar lo anterior revisemos el código del Programa 10.2 que al igual que el Programa
10.1 consiste en llenar secuencialmente los datos de una matriz de 4x5 elementos y sumar fila
por fila y columna por columna, guardando los resultados en dos vectores, uno vertical para las
filas, y otro horizontal para las columnas, al final se imprime la matriz ingresada y el resultado
de sumar las filas y las columnas de la matriz, resultados guardados en el vector vertical y el
vector horizontal, pero ahora, con un ciclo anidado.

Programa 10.2.: calculaMatrizV2.c (Versión con ciclo anidado)


Calcula la suma de las filas y columnas de una matriz de 4x5 elementos proporcionada por
el usuario.
/* Autor: Nombre del Programador
Nombre Programa: calculaMatrizV2.c
Descripción: Calcula la suma de las filas y las columnas de una
matriz de 4x5 elementos proporcionada por el usuario.
Fecha de elaboración: Día/Mes/Año
*/

/*Directivas de preprocesador*/
include <stdio.h>
#include <stdlib.h>

#define TAMFil 4
#define TAMCol 5

int main()
{

int matriz[TAMFil][TAMCol];
int horizontal[TAMFil];
int vertical[TAMCol];
int fila,columna;
int suma=0;

/*Bucle que registra los numeros introducidos de teclado*/


for(fila=0;fila<TAMFil;fila++)
{
for(columna=0;columna<TAMCol;columna++)
{
printf("Introduce un numero para la posicion %d,%d: ",
fila,columna);
scanf("%d",&matriz[fila][columna]);
}
}

/*Bucle que suma las filas y registra el resultado en el vector


vertical*/
for(fila=0;fila<TAMFil;fila++)
{
suma=0;
for(columna=0;columna<TAMCol;columna++)
{
suma+=matriz[fila][columna];
}
vertical[fila]=suma;
}

/*Bucle que suma las columnas y registra el resultado en el vector


horizontal*/
for(columna=0;columna<TAMCol;columna++)
{
suma=0;
for(fila=0;fila<TAMFil;fila++)
{
suma+=matriz[fila][columna];
}
horizontal[columna]=suma;
}

/*Impresión de la matriz ingresada*/


printf ("\nMatriz Ingresada\n");
for (fila=0;fila<TAMFil;fila++){
for (columna=0;columna<TAMCol;columna++){
printf ("%i ",matriz[fila][columna]);
}
printf ("\n");
}

/*Recorre el vector vertical y muestra los resultados*/


printf("\nResultado Filas\n");
for(fila=0;fila<TAMFil;fila++)
{
printf(" %d\n",vertical[fila]);
}

/*Recorre el vector horizontal y muestra los resultados*/


printf("\nResultado Columnas \n");
for(columna=0;columna<TAMCol;columna++)
{
printf("%d ",horizontal[columna]);
}
printf ("\n");
system("PAUSE");
}

La ventaja de esta versión es que si se desea modificar el programa para que obtenga la
suma de las filas y columnas de una matriz de tamaño N x M proporcionada por el usuario
sólo basta cambiar el valor de la constante TAMFil y TAMCol.
10.6 ARREGLOS BIDIMENSIONALES COMO
PARÁMETROS DE FUNCIONES Y OPERACIONES
BÁSICAS DE MATRICES.
En el lenguaje C se pueden definir funciones que reciban como parámetro arreglos
bidimensionales, con la peculiaridad de que todos los arreglos en lenguaje C se pasan por
referencia. Recordemos que cuando una variable, en este caso de tipo arreglo, se pasa por
referencia significa que se está pasando la dirección de memoria asignado a la variable, lo
cual tiene como efecto que si la función modifica el valor de la variable, el cambio se hace
en la dirección de memoria correspondiente a la variable original, por lo tanto, se verá
reflejado en la función desde la cual se hizo la llamada. La razón fundamental de pasar los
arreglos por referencia es no desperdiciar memoria, si un arreglo se pasara por valor cada
vez que se hace una llamada a una función se realizaría una copia del arreglo, lo cual
implicaría un uso excesivo de memoria cuando pasáramos arreglos de gran tamaño.

La sintaxis general para definir una función que reciba como parámetro de entrada un
arreglo bidimensional, tabla o matriz es la misma que la de cualquier otra función, con la
diferencia de que cuando se define o declara la función en la lista de parámetros se utilizan
dos pares de corchetes cuadrados después del identificador del arreglo para indicar que se
trata de un arreglo bidimensional (igual que en la declaración del arreglo bidimensional).

Sintaxis general para definir una función que recibe un arreglo:

<tipoFuncion> <nombreFuncion> (<tipoArreglo> <nombreArreglo> [][])

{
/*Cuerpo de la función*/
}

Figura 10.8 Declaración de funciones que reciben arreglos bidimensionales

En cambio, cuando se llama una función sólo se escribe el nombre de la variable de tipo
arreglo sin emplear los corchetes cuadrados.

Sintaxis general para llamar una función que recibe un arreglo.

<nombreFuncion> ( <nombreArreglo> );

Figura 10.9. Paso de parámetros de tipo arreglo

Para ejemplificar lo anterior, en las siguientes secciones definiremos funciones que


implementan algunas de las operaciones básicas de los arreglos bidimensionales como son:
lectura, escritura, inicialización y búsqueda.

Es recomendable, para cumplir con los principios de modularidad, pasar como parámetro
de entrada el tamaño del arreglo, de esta manera la función puede utilizarse para arreglos de
cualquier tamaño, por tanto en cada función también se pasará como parámetro el tamaño
del arreglo.

Debido a lo anterior, es importante mencionar que si se trata de trabajar con una matriz
cuadrada (ampliamente utilizada en algebra) bastará con solo pasar como parámetros la
matriz y la variable TAM, ya que una matriz n por m elementos, es una matriz cuadrada si
el número de filas es igual al número de columnas, pero la función requerirá de un
parámetro más si la matriz no es cuadrada y varían las filas respecto a las columnas.

Para la implementación en Lenguaje C de los algoritmos, supondremos que los arreglos


bidimensionales son de tipo entero, considerando que el algoritmo es el mismo para
cualquier tipo de datos; para arreglos de otro tipo sólo se debe cambiar el tipo de datos
correspondiente.

Es importante considerar para los siguientes ejemplos, que cuando uno de los parámetros de
entrada de una función es un arreglo de dos dimensiones, matriz o tabla, si debemos indicar
el tamaño de la segunda dimensión del arreglo (columna).

Ejemplo:

int MiFuncion(int mi_arreglo[][5], int num_elementos);

10.7 LECTURA DE UN ARREGLO


La función LeeMatriz lee de la entrada estándar cada uno de los elementos del arreglo
bidimensional, recibiendo como parámetros de entrada el arreglo. No regresa ningún
resultado pero cambia el estado de la matriz o tabla según los datos de entrada.

Algoritmo 10.2.: Lectura de un arreglo bidimensional


Pseudocódigo Lenguaje C
void LeeMatriz(int Matriz[][TAMCol])
LeeMatriz(Matriz[ ][ TAMCol]) {
int fila, columna;
for (fila=0; fila<TAMFil; fila++) {
Inicio for (columna=0; columna<TAMCol;
columna++){
Desde fila ← 0 Mientras fila < TAMFil, fila ← fila + 1
printf(“\nProporciona el
Desde columna ← 0 Mientras columna < TAMCol, elemento %d”, fila,columna);
columna ← columna + 1 scanf(“%d”,
&Matriz[fila][columna]);
Imprimir “Proporciona el elemento”, fila, columna
Leer Matriz[fila][columna] }/*fin for columna*/
}/*fin for fila*/
}/*fin LeeArreglo*/
Fin_Desde_Columnas
Fin_Desde_Filas
Fin
Diagrama de Flujo

LeeMatriz(Matriz[ ][TAMCol ])

fila ← 0

F
fila < TAMFil
V

columna ← 0

F
columna < TAMCol
V

Imrprimir “Proporciona elemento”,fila, columna


col

Leer Matriz[fila][columna]

columna ← columna + 1

fila ← fila + 1

Fin LeeMatriz

Algoritmo 10.2. Lectura de un arreglo (Pseudocódigo y Diagrama de Flujo)

Como mencionamos anteriormente, en la llamada a una función que recibe un arreglo


nunca deben escribirse los pares de corchetes para indicar que es un arreglo
bidimensional, al igual que todas las funciones sólo se escribe el nombre de la variable que
se pasa como parámetro. Por ejemplo, la instrucción para llamar a la función LeeArreglo
con el arreglo Matriz definido en los Programas 10.1 y 10.2 es:
LeeMatriz(Matriz).

Revisemos la prueba de escritorio de la llamada anterior considerando que el arreglo


contiene ceros cuando se realiza la llamada y que los valores proporcionados por el usuario
son: 9, 10, 8, 5, 7 y 9.
Prueba de escritorio correspondiente a la llamada LeeMatriz(Matriz)

Estado del arreglo lista antes de la llamada a la función LeeMatriz(Matriz)

[0] [1] [2]


Fi la 0 0 0 0 Matriz [0][columna]
[0] [1] [2]
Fila 1 0 0 0 Matriz[1][columna]

Proceso:

Instrucción Entrada Salida Estado de las variables locales

TAMFil TAMCol
2 3
Iteración 0

fila = 0 [0] [1] [2]


columna=0 0 0 0 fila columna
0 0 0
0 0

¿columna < TAMCol?


¿0<3?
VERDADERO TAMFil TAMCol
Imprimir “Proporciona [0] [1] [2] 2 3
Iteración 1

Proporciona el
el elemento”, 9 0 0
elemento 0,0
fila,columna 0 0 0
fila columna
Leer
Matriz[fila][columna] 9 0 1
Leer Matriz[0][0]
columna =columna+1
columna= 0+1 = 1
¿columna < TAMCol?
¿1<3?
TAMFil TAMCol
VERDADERO
Imprimir “Proporciona [0] [1] [2] 2 3
Iteración 2

Proporciona el
el elemento”, 9 10 0
elemento 0,1
fila,columna 0 0 0 fila columna
Leer
Matriz[fila][columna] 10 0 2
Leer Matriz[0][1]
columna =columna+1
columna= 1+1 = 2
¿columna < TAMCol?
¿2<3?
VERDADERO [0] [1] [2] TAMFil TAMCol
Imprimir “Proporciona 9 10 8 2
Proporciona el 3
el elemento”,
Iteración 3

0 0 0
elemento 0,2
fila,columna
fila columna
Leer
Matriz[fila][columna] 8 0 3
Leer Matriz[0][2]
columna =columna+1
columna= 2+1 = 3
¿columna < TAMCol?
¿3<3? TAMFil TAMCol
FALSO [0] [1] [2] 2 3
Iteración 4
9 10 8
fila =fila+1 0 0 0
fila columna
fila= 0+1 = 1
1 0
columna=0

¿columna < TAMCol?


¿0<3?
TAMFil TAMCol
VERDADERO [0] [1] [2]
Imprimir “Proporciona 9 10 8 2 3
Iteración 1

Proporciona el
el elemento”, 5 0 0
elemento 1,0
fila,columna fila columna
Leer
Matriz[fila][columna] 5 1 1
Leer Matriz[1][0]
columna =columna+1
columna= 0+1 = 1
¿columna < TAMCol?
¿1<3? [0] [1] [2]
TAMFil TAMCol
VERDADERO 9 10 8
Imprimir “Proporciona 5 7 0 2 3
Iteración 2

Proporciona el
el elemento”,
elemento 1,1
fila,columna fila columna
Leer
Matriz[fila][columna] 7 1 2
Leer Matriz[1][1]
columna =columna+1
columna= 1+1 = 2
¿columna < TAMCol?
¿2<3? [0] [1] [2]
TAMFil TAMCol
VERDADERO 9 10 8
Imprimir “Proporciona 5 7 9 2 3
Iteración 3

Proporciona el
el elemento”,
elemento 1,2
fila,columna fila columna
Leer
Matriz[fila][columna] 9 1 3
Leer Matriz[1][2]
columna =columna+1
columna= 2+1 = 3
TAMFil TAMCol
[0] [1] [2] 2 3
Iteración 24

9 10 8
¿columna < TAMCol? 5 7 9
fila columna
¿3<3?
FALSO 1 3

Parámetros de salida y valor de retorno: ninguno


Estado del arreglo lista después de la llamada a la función LeeMatriz(Matriz,2,3)

[0] [1] [2]


Matriz 9 10 8
5 7 9

Figura 10.9. Prueba de escritorio de LeeMatriz


10.8 IMPRESIÓN DE UN ARREGLO BIDIMENSIONAL
La función ImprimeMatriz imprime en la salida estándar cada uno de los elementos del
arreglo. Los parámetros de entrada de la función son el arreglo. No regresa ningún
resultado y tampoco altera el estado del arreglo.

Algoritmo 10.3.: Impresión de un Arreglo


Pseudocódigo Lenguaje C
ImprimeMatriz(Matriz[ ][TAMCol ])
void ImprimerMatriz(int Matriz[][TAMCol])
{
Inicio
int fila, columna;
for (fila=0; fila<TAMFil; fila++) {
Desde fila ← 0 Mientras fila < TAMFil, fila ← fila + 1
for (columna=0; columna<TAMCol;
columna++){
Desde columna ← 0 Mientras columna < TAMCol,
columna ← columna + 1 printf(“ %d”, Matriz[fila][columna]);
Imprimir Matriz[fila][columna] }/*fin for columna*/
printf ("\n");
Fin_Desde_Columnas
Fin_Desde_Filas }/*fin for fila*/
Fin }/*fin LeeArreglo*/
Diagrama de Flujo

ImprimeMatriz(Matriz[ ] [TAMCol ])

fila ← 0

F
fila < TAMFil
V

columna ← 0

F
columna < TAMCol
V

Imrprimir Matriz [ fila ] [ columna ]

columna ← columna + 1

fila ← fila + 1

Fin ImprimeMatriz
10.9 INICIALIZACIÓN DE UN ARREGLO
La función InicializaMatriz asigna el mismo valor a todas los elementos del arreglo
(elemento por elemento ya que no es posible asignarle un valor a todo el arreglo de manera
simultánea). Los parámetros de entrada de la función son: el arreglo y el valor que se
asignará a los elementos. No regresa ningún resultado pero altera el estado del arreglo.

Algoritmo 10.4.: Inicialización de un arreglo


Pseudocódigo Lenguaje C
InicializaMatriz(Matriz[ ][TAMCol, valor) void InicializaMatriz(int
Matriz[][TAMCol],valor)
{
Inicio
int fila, columna;
for (fila=0; fila<TAMFil; fila++) {
Desde fila ← 0 Mientras fila < TAMFil, fila ← fila + 1
for (columna=0; columna<TAMCol;
columna++){
Desde columna ← 0 Mientras columna < TAMCol,
columna ← columna + 1
Matriz[fila][columna]=valor;
Matriz[fila][columna] ← valor }/*fin for columna*/
printf ("\n");
Fin_Desde_Columnas
Fin_Desde_Filas }/*fin for fila*/
Fin }/*fin LeeArreglo*/
Diagrama de Flujo
InicializaMatriz(Matriz[ ] [TAMCol ],valor)

fila ← 0

F
fila < TAMFil
V

columna ← 0

F
columna < TAMCol
V

Matriz [ fila ] [ columna ] ← valor

columna ← columna + 1

fila ← fila + 1

Fin InicializaMatriz
La prueba de escritorio de la función ImprimeMatriz e InicializaMatriz se deja como ejercicio al
lector.

10.10 BÚSQUEDA EN ARREGLO BIDIMENSIONAL


Una búsqueda es el proceso mediante el cual es posible verificar si un elemento pertenece a
un arreglo. Terminamos con éxito cuanto el elemento es encontrado, devolviendo la
posición en la cual está almacenado, o bien, regresando -1 cuando no se encontró el
elemento.

El método más sencillo para buscar un elemento en un arreglo se llama Búsqueda


Secuencial y consiste en iniciar la búsqueda en el primer elemento del arreglo avanzando
de uno en uno hasta encontrar el elemento indicado o hasta llegar al final del arreglo. El
algoritmo se presenta a continuación en pseudocódigo y se deja como ejercicio al lector la
implementación en lenguaje C y la prueba de escritorio.

Algoritmo 10.5.: Búsqueda secuencial en una Matriz


Pseudocódigo del algoritmo Búsqueda Secuencial

BúsquedaSecuencial(Matriz[ ][TAMCol ],elemento)

Inicio

encontrado ← 0

Desde fila ← 0 Mientras (fila< TAMFil) , fila ← fila + 1

Desde columna ← 0 Mientras (columna< TAMCol) , columna ← columna + 1

Si elemento = Matriz[fila][columna] entonces


imprimir " El”, elemento, ”ha sido encontrado en matriz[“,fila,”][“,columna,”]”
encontrado←1
Fin_Si

Si (enc=0 AND fila=TAMF-1 AND columna=TAMC-1)

imprimir " El”, elemento, ”NO ha sido encontrado en matriz”


Fin_Si

Fin_Desde
Fin_Desde

Fin

En el algoritmo se utiliza la variable llamada encontrado como bandera, el valor inicial es


0 indicando que el elemento aún no se ha localizado; mientras se ejecuta el ciclo, el valor
de la variable encontrado cambia sólo si el elemento indicado se localiza. Por tanto, si el
elemento nunca se encontró el valor que regresará será 0.
Existen otros algoritmos de búsqueda más eficientes, sin embargo, tienen como
precondición que el arreglo esté ordenado.

8.11 Proyecto Ejemplo de Arreglos Bidimensionales


En varias situaciones se requiere trabajar los arreglos bidimensionales o tablas como
matrices, retomando conceptos básicos matemáticos, como suma, resta, multiplicación,
división, sacar la inversa de una matriz dada, la suma de su diagonal, diagonal inversa, y
muchos otros aspectos relacionados.

Tomando como base lo anterior y las explicaciones teóricas realizadas anteriormente, se


solicita:

Escriba un programa que mediante la utilización de una matriz cuadrada de enteros permita
determinar:

La suma de los elementos de la matriz, suma de los valores ubicados en las esquinas de la
matriz, buscar un elemento de la matriz y sumar la diagonal principal y secundaria.

En esta sección presentaremos una solución al Problema planteado.

Proyecto 10.1: Descripción del problema


Un profesor de matemáticas de la Universidad Autónoma de la Ciudad de México ha
solicitado a sus estudiantes de ingeniería, realizar un programa que mediante la utilización
de una matriz cuadrada de enteros permita determinar:

La suma de los elementos de la matriz, sumar de los valores ubicados en las esquinas de la
matriz, buscar un elemento de la matriz y sumar la diagonal principal y secundaria.

Analizando el problema podemos percatarnos de que es necesario recurrir a un arreglo


bidimensional, matriz o tabla para almacenar la información y realizar algunas operaciones
básicas relacionadas con las matrices. En el siguiente cuadro se presenta un análisis de los
elementos más importantes del problema así como un bosquejo del algoritmo.
Análisis del problema
Datos de entada: Salida: Restricciones:

El arreglo - La matriz cuadrada con valores ingresados Ninguna


bidimensional, por el usuario.
matriz o tabla. - Sumar las esquinas de la matriz cuadrada. Constantes:
- Sumar todos y cada uno de los elementos TAMFil Tamaño
de la matriz cuadrada. de la fila de la
- Suma la diagonal principal de la matriz matriz.
cuadrada. TAMCol Tamaño
- Suma la diagonal secundaria de la matriz de la columna de
cuadrada. la matriz.
- Realizar la búsqueda de un elemento en la
matriz y muestra la posición en la que se
encuentra guardado.
Método: Para resolver el problema realizaremos los siguientes pasos:
1. Leer todos y cada uno de los elementos de la matriz.
2. Imprimir la matriz.
3. Calcular la suma de las esquinas de la matriz cuadrada.
4. Calcular la suma de todos y cada uno de los elementos de la matriz cuadrada.
5. Calcular la suma de la diagonal principal de la matriz cuadrada.
6. Calcular la suma de la diagonal secundaria de la matriz cuadrada.
7. Realizar la búsqueda de un elemento en la matriz y mostrar la posición que ocupa.

Del análisis anterior concluimos que necesitamos definir los siguientes módulos:
Diseño modular

Principal

Imprime Lee Suma Suma Suma Suma Busqueda


Matriz Matriz Esquinas Elementos Diagonal Diagonal Secuencial
Prin Sec

Los módulos ImprimeMatriz, LeeMatriz y BusquedaSecuencial ya han sido descrito


anteriormente, sólo falta diseñar e implementar los módulos: SumaEsquinas,
SumaElementos, SumaDiagonalPrin y SumaDiagonalSec. En esta parte sólo incluimos el
análisis, diseño e implementación dejando al lector la tarea de realizar las pruebas de
escritorio. Empecemos a desarrollar la solución a nivel de algoritmo del módulo
SumaElementos:
Algoritmo 10.7.: Análisis y Diseño de la función
SumaElementos(Matriz[ ][TamCol])
Descripción:
Dada la matriz cuadrada de enteros devuelve la suma de todos los elementos, es decir, implementa
la siguiente ecuación matemática:

𝑇𝐴𝑀𝐹𝑖𝑙−1 𝑇𝐴𝑀𝐶𝑜𝑙−1

𝑠𝑢𝑚𝑎 = ∑ ∑ 𝐴𝑟𝑟𝑒𝑔𝑙𝑜[𝑓𝑖𝑙𝑎][𝑐𝑜𝑙𝑢𝑚𝑛𝑎]
𝑓𝑖𝑙𝑎=0 𝑐𝑜𝑙𝑢𝑚𝑛𝑎=0

Parámetros de entrada:
• Arreglo[ ][TAMCol]: matriz cuadrada de enteros que se va a sumar

Valor de retorno:
• suma: resultado de la suma de todos los elementos del arreglo.

Método:
Utilizaremos un ciclo anidado para recorrer el arreglo y una variable (acumulador) para almacenar la
suma acumulativa.

Módulos:
No requiere de ningún otro módulo.

Variables locales:
fila: filas de la matriz cuadrada.
columna: columnas de la matriz cuadrada.
Algoritmo 10.7: Suma todos los elementos de una matriz
Pseudocódigo Lenguaje C
void SumaElementos(int matriz[][TAMCol]){
SumaElementos(Matriz[ ][TAMCol])
int suma=0,fila,columna;
Inicio for (fila=0;fila<TAMFil;fila++)
for (columna=0;columna<TAMCol;columna++)
suma ← 0 {
suma=suma+matriz[fila][columna];
Desde fila ← 0 Mientras fila < TAMFil, fila ← fila+1 }
printf ("\nLa suma de los elementos de
Desde columna ← 0 Mientras columna < TAMCol, la Matriz[%i][%i]=%i",fila,columna,suma);
columna ← columna+1 }

suma ← suma + Matriz[fila] [columna]

Fin_Desde

Fin_Desde

Imprimir “La suma de los elementos de la


Matriz [“,fila

Fin
Diagrama de Flujo

SumaArreglo(Arreglo[ ],TAM)

suma ← 0
pos ← 0

F
pos < TAM

suma ← suma + Arreglo[pos]

pos ← pos + 1

Regresa suma

Fin InicializaArreglo
El código completo se presenta en la siguiente tabla, se realizaron pequeñas modificaciones
a las funciones definidas para adecuar las impresiones en pantalla al problema planteado.
Programa 10.3: operaMatriz
Muestra como funcionan algunas operaciones básicas de una matriz 3x3
/* Autor: Nombre del Programador
* Nombre Programa: operaMatriz.c
* Descripción: El siguiente programa ejemplifica
* cómo se pueden realizar algunas operaciones básicas con
* una matriz de NxM, asimismo se muestra cómo utilizar las matrices
* como parámetros de entrada de las funciones
* Fecha de elaboración: dd/mm/aaaa
*/

/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>

/*Constantes
* TAMFil Tamaño de la Filas de la Matriz
* TAMCol Tamaño de las Columnas de la Matriz*/

#define TAMFil 5
#define TAMCol 5

/*Prototipos de las funciones a ser utilizadas en el programa*/


void LeeMatriz(int matriz[][TAMCol]);
void ImprimeMatriz(int matriz[][TAMCol]);
void SumaEsquinas(int matriz[][TAMCol]);
void SumaElementos(int matriz[][TAMCol]);
void SumaDiagonalPrin(int matriz[][TAMCol]);
void SumaDiagonalSec(int matriz[][TAMCol]);
void BusquedaSecuencial(int matriz[][TAMCol], int elemento);

/*Función Principal*/
main(){

int matriz[TAMFil][TAMCol], elemento;


LeeMatriz(matriz); /*Invocación de la función LeeMatriz*/
ImprimeMatriz(matriz); /*Invocación de la función ImprimeMatriz*/
SumaEsquinas(matriz); /*Invocación de la función SumaEsquinas*/
SumaElementos(matriz); /*Invocación de la función SumaElementos*/
SumaDiagonalPrin(matriz); /*Invocación de la función SumaDiagonal*/
SumaDiagonalSec(matriz); /*Invocación de la función SumaDiagonalSec*/
printf ("\nIngresa el elemento a buscar en la matriz:");
scanf ("%i", &elemento);
BusquedaSecuencial (matriz,elemento); /*Invocación de la función
BusquedaSecuencial*/
printf ("\n");
system ("pause");
}/*Fin de la función Principal*/
/* LeeMatriz: lee los valores desde el teclado para llenar la matriz
* parámetros: Matriz, la matriz a ser leída
* regresa: nada
*/
void LeeMatriz(int matriz[][TAMCol]) {
int fila=0,columna=0;
// clrscr();
for (fila=0;fila<TAMFil;fila++)
for (columna=0;columna<TAMCol;columna++) {//clrscr();
printf ("Digite un numero M[%i][%i]:", fila,columna);
scanf ("%d",&matriz[fila][columna]);
}
}

/* ImprimeMatriz: imprime los elementos de una matriz


* parámetros: Matriz, el arreglo que va a imprimir
* regresa: nada
*/
void ImprimeMatriz(int matriz[][TAMCol])
{
int fila=0,columna=0;
//clrscr();
printf ("La matriz contiene:\n");
for (fila=0;fila<TAMFil;fila++)
for (columna=0;columna<TAMCol;columna++){
printf ("%d ",matriz[fila][columna]);
if (columna==TAMCol-1)
printf ("\n");
}

/* SumaEsquinas: suma las esquinas de una matriz 3x3imprime los


elementos de una matriz
* parámetros: Matriz, el arreglo que va a imprimir
* regresa: nada
*/
void SumaEsquinas(int matriz[][TAMCol]){
int suma;
suma=matriz[0][0]+matriz[0][TAMCol-1]+matriz[TAMFil-1][0]+matriz[TAMFil-1][TAMCol-1];
printf ("\nLa suma de las esquinas de Matriz[%i][%i]=%i",
TAMFil,TAMCol,suma);
}

/* SumaElementos: suma todos y cada uno de los elementos ingresados en


* la matriz
* parámetros: Matriz, el arreglo con todos los elementos a sumar
* regresa: nada
*/
void SumaElementos(int matriz[][TAMCol]){
int suma=0,fila,columna;
for (fila=0;fila<TAMFil;fila++)
for (columna=0;columna<TAMCol;columna++) {
suma=suma+matriz[fila][columna];
}
printf ("\nLa suma de los elementos de la
Matriz[%i][%i]=%i",fila,columna,suma);
}

/* SumaDiagonalPrin: suma la diagonal de todos los elementos de la


matriz
* parámetros: Matriz, el arreglo con todos los elementos de la matriz
* regresa: nada
*/
void SumaDiagonalPrin (int matriz[][TAMCol]){
int suma=0,fila,columna;
for (fila=0;fila<TAMFil;fila++)
for (columna=0;columna<TAMCol;columna++) {
if (fila==columna) {
suma=suma+matriz[fila][columna];
printf ("\nLa suma de la diagonal de la Matriz[%i][%i]=%i",fila,columna,suma);
}/*Fin del Si*/
}/*Fin del For*/
}/*Fin de SumaDiagonalPrin*/

/* SumaDiagonalSec: suma la diagonal secundaria de todos los elementos


* de la matriz
* parámetros: Matriz, el arreglo con todos los elementos de la matriz
* regresa: nada
*/
void SumaDiagonalSec (int matriz[][TAMCol]){
int suma=0,fila=TAMFil,columna;
for(columna=fila;columna>=1;columna--) {
suma=suma+matriz[fila-columna][columna-1];
printf ("\nLa suma de la diagonal secundaria de la
Matriz[%i][%i]=%i",fila-columna,columna-1,suma);
}/*Fin del For*/
}/*Fin de SumaDiagonalSec*/

/* Busqueda Secuencial: búsqueda de un elemento de la matriz


* parámetros: Matriz, el arreglo con todos los elementos de la matriz
* elemento, el elemento a ser buscado dentro de la matriz
* regresa: nada
*/
void BusquedaSecuencial(int matriz[][TAMCol],int elemento){
int fila,columna;
int encontrado=0;
for (fila=0;fila<TAMFil;fila++)
for (columna=0;columna<TAMCol;columna++){
if (elemento==matriz[fila][columna]){
printf ("\n El %i, ha sido encontrado en
matriz[%i][%i]",elemento,fila,columna);
encontrado=1;}
if (encontrado==0 && fila==TAMFil-1 &&
columna==TAMCol-1) {
printf ("\n El %i, no ha sido encontrado en la
matriz",elemento);
printf ("\n");
}
}
}

La ejecución del programa anterior se muestra en la siguiente figura:

Ejecución Programa 10.3: operaMatriz.c


PROYECTO EJEMPLO
En esta sección presentaremos una solución al Problema 3 planteado al inicio de este
capítulo.

Proyecto 8.1: Descripción del problema


La Secretaria de Comunicación y Transporte (SCT) requiere un programa que registre el
número de accidentes automovilísticos en la autopista del sol a lo largo de un año y que a
partir de ellos emita un informe con los siguientes datos: el número de accidentes en cada
uno de los meses, el total de accidentes ocurridos a lo largo de un año, el promedio de
accidentes anual y los meses en los cuales se reportó un número mayor de accidentes que el
promedio.

Analizando el problema podemos percatarnos de que es necesario recurrir a un arreglo para


almacenar el número de accidentes mensuales, ya que es necesario obtener primero el
promedio y después verificar cuáles de los meses reportaron un número mayor de
accidentes al promedio anual. En el siguiente cuadro se presenta un análisis de los
elementos más importantes del problema así como un bosquejo del algoritmo.

Análisis del problema


Datos de entada: Salida: Restricciones:

Número de - Accidentes mensuales Ninguna


accidentes de - Total de accidentes anuales
cada uno de los - Promedio de accidentes anuales Constantes:
meses del año. - Meses que superaron el promedio anual de Número de meses
accidentes en un año (que
corresponderá al
tamaño del
arreglo)
Método: Para resolver el problema realizaremos los siguientes pasos:
8. Leer el número de accidentes ocurridos en cada uno de los meses
9. Realizar la suma de todos los accidentes
10. Calcular el promedio anual
11. Imprimir los accidentes mensuales, el total de accidentes anuales y el promedio
anual de accidentes.
12. Determinar e imprimir en que meses se reportaron un número mayor de accidentes
que el promedio.
Del análisis anterior concluimos que necesitamos definir los siguientes módulos:
Diseño modular

Principal

Imprimir Leer Suma Promedio Imprime


Arreglo Arreglo Arreglo Arreglo Mayores
Promedio

Los módulos Imprimir Arreglo y Leer Arreglo ya han sido descrito anteriormente, sólo falta
diseñar e implementar los módulos: Suma Arreglo, Promedio Arreglo e Imprime Mayores
Promedio. En esta parte sólo incluimos el análisis, diseño e implementación dejando al
lector la tarea de realizar las pruebas de escritorio. Empecemos con el método Suma
Arreglo:
Algoritmo 8.7.: Análisis y Diseño de la función
SumaArreglo(Arreglo[ ],TAM)
Descripción:
Dado un arreglo de enteros devuelve la suma de todos los elementos, es decir, implementa la
siguiente ecuación matemática:
TAM -1
suma =  Arreglo[i]
i=0

Parámetros de entrada:
• Arreglo[ ]: arreglo de enteros que se va a sumar
• TAM: tamaño del arreglo (entero)

Valor de retorno:
• suma: resultado de la suma de todos los elementos del arreglo.

Método:
Utilizaremos un ciclo para recorrer el arreglo y una variable (acumular) para almacenar la suma
acumulativa.

Módulos:
No requiere de ningún otro módulo

Variables locales:
Sólo se utilizan las variables ya mencionadas, por lo tanto, no se requiere de ninguna variable extra.
Pseudocódigo Lenguaje C

int SumaArreglo(
SumaArreglo(Arreglo[ ],TAM) int Arreglo[],
int TAM
Inicio
)
suma ← 0 {
int pos, suma = 0;
Desde pos ← 0 Mientras pos < TAM, pos ← pos+1
for (pos=0; pos<TAM; pos++)
suma ← suma + Arreglo[pos] suma += Arreglo[pos];

Fin_Desde return suma;


Regresa suma }/*fin SumaArreglo*/
Fin

Diagrama de Flujo

SumaArreglo(Arreglo[ ],TAM)

suma ← 0
pos ← 0

F
pos < TAM

suma ← suma + Arreglo[pos]

pos ← pos + 1

Regresa suma

Fin InicializaArreglo

El método PromedioArreglo se vuelve trivial una vez que hemos desarrollado la función
SumaPromedio(int[],int). El análisis, diseño e implementación se muestran en la siguiente
tabla.
Algoritmo 8.8.: Análisis y Diseño de la función
PromedioArreglo(Arreglo[ ],TAM)
Descripción:
Dado un arreglo de enteros devuelve el promedio de sus elementos, es decir, implementa la siguiente
fórmula:
T AM-1

 Arreglo[i]
prom = i =0

TAM

Parámetros de entrada:
• Arreglo[ ]: arreglo de enteros del cual se requiere el promedio
• TAM: tamaño del arreglo (entero)

Valor de retorno:
• prom: resultado del promedio de los elementos del arreglo (flotante)

Método:
Calcularemos la suma utilizando el módulo SumaArreglo(int[],int) y a partir de ella el promedio. No
se requiere ningún ciclo.

Módulos:
SumaArreglo(int[], int)

Variables locales:
Además de las variables mencionadas se utiliza la variable suma de tipo entero para almacenar el
resultado de la llamada a la función SumaArreglo(int[],int), con el fin de que el algoritmo sea más claro.
Pseudocódigo Lenguaje C

float PromedioArreglo(
int Arreglo[],
int TAM
PromedioArreglo(Arreglo[ ],TAM)
)
Inicio {
int prom, suma;
suma  SumaArreglo( Arreglo, TAM )
suma=SumaArreglo(Arreglo,TAM);
suma
prom  prom = (float)suma/TAM;
TAM
Regresa prom return prom;
Fin }/*fin PromedioArreglo*/
Diagrama de Flujo

PromedioArreglo(Arreglo[ ],TAM)

suma ← SumaArreglo(Arreglo,TAM)

suma
prom 
TAM

Regresa prom

Fin PromedioArreglo

Ahora, sólo falta diseñar el módulo que imprime en la pantalla los meses en los cuáles
ocurrieron más accidentes que el promedio anual.
Algoritmo8.9.: Análisis y Diseño de la función
ImpMayoresProm (Arreglo[ ],TAM)
Descripción:
A partir de un arreglo dado y el promedio del mismo la función imprime los meses (en
representación numérica) en los que el número de accidentes sobrepaso el promedio.

Parámetros de entrada:
• Arreglo[ ]: arreglo de enteros que se va a sumar
• TAM: tamaño del arreglo (entero)
• prom: promedio del arreglo (flotante)

Valor de retorno:
• ninguno, la función imprime en pantalla los resultados

Método:
Utilizaremos un ciclo para recorrer el arreglo y determinar cuáles de los elementos son mayores que
prom.
Módulos:
No requiere de ningún otro módulo

Variables locales:
Sólo se utilizan las variables ya mencionadas, por lo tanto, no se requiere de ninguna variable extra.
Pseudocódigo Lenguaje C

int ImpMayoresProm(
ImpMayoresProm(Arreglo[ ],TAM,prom)
int Arreglo[],
Inicio int TAM,
float prom
Desde pos ← 0 Mientras pos< TAM, pos←pos+1 )
{
Si Arreglo[pos] > prom entonces int pos;
Imprimir “ Mes”, pos+1.
Fin_Si for (pos=0; pos<TAM; pos++)
if(Arreglo[pos] > prom)
Fin_Desde
printf(“\n Mes %d”,
Fin pos+1);
}//fin ImpMayoresProm

Diagrama de Flujo

ImpMayoresProm(Arreglo[ ],TAM,prom)

pos ← 0

F
pos < TAM

Arreglo[pos] > prom

V
F
Imprimir “ Meses “, pos+1

pos ← pos + 1

Fin ImpMayoresProm
En el diseño del módulo anterior se decidió pasar como parámetro el promedio del arreglo
para hacer más eficiente el programa, ya que en el algoritmo principal se calcula; sin
embargo, podría omitirse y llamar a la función correspondiente dentro del módulo.
Para finalizar con el ejemplo construyamos el módulo principal.
Algoritmo 8.10: Análisis y Diseño del método principal del programa que
genera el reporte de los accidentes anuales en la Autopista del Sol

Datos de entrada:
• Número de accidentes registrados en cada uno de los meses del año (meses[ ], arreglo de
enteros)
Salida:
• meses[]: número de accidentes registrados mensualmente.
• totalAnual: total de accidentes registrados a lo largo de un año
• promAnual: promedio anual de accidentes registrados mensualmente.
• Meses en los cuales se registro un número mayor al promedio.

Módulos:
ImprimeArreglo(int[], int)
LeerArreglo(int[], int)
SumaArreglo(int[], int)
PromedioArreglo(int[], int,int)
ImpMayorArreglo(int[], int,int)

Variables locales:
Sólo las antes mencionadas.
Algoritmo del módulo principal
Inicio

TAM ← 12

Imprimir “Proporciona el número de accidentes registrados”

LeeArreglo(meses,TAM)

totalAnual ← SumaArreglo(meses,TAM)

promAnual ← PromedioArreglo(meses,TAM)

Imprimir “Los accidentes reportados en el año fueron:”

ImprimeArreglo(meses,TAM)

Imprimir “El total de accidentes ocurridos durante el año es ”, totalAnual


Imprimir “El promedio anual de accidentes es ”, promAnual

Imprimir “ Los meses en los cuales se reportó un número mayor de accidentes”

ImpMayoresProm(meses,TAM,promAnual)

Fin

El código completo se presenta en la siguiente tabla, se realizaron pequeñas modificaciones


a las funciones definidas para adecuar las impresiones en pantalla al problema planteado.

Programa 8.11: reporteAccidentes.c


Genera un reporte con las estadísticas de los accidentes registrados en la Autopista del Sol
/* Autor: Juan Pérez
* Nombre Programa: reporteAccidentes.c
* Descripción: el programa registra el número de
* accidentes automovilísticos en la autopista del sol
* a lo largo de un año e imprime en pantalla un informe
* con los siguientes datos:
* el número de accidentes en cada uno de los meses,
* el total de accidentes ocurridos a lo largo de un año,
* el promedio de accidentes anual y,
* los meses en los cuales se reportó un número mayor
* de accidentes que el promedio
* Fecha de elaboración: 16/09/2008 */

/*Directivas de preprocesador*/
#include<stdio.h>
#include<stdlib.h>

/* ImprimeArreglo: imprime en pantalla los elementos de


* un arreglo, en este caso el número de accidente mensuales
* parámetros: Arreglo[], el arreglo que va a imprimir
* TAM, tamaño del arreglo
* regresa: nada*/
void ImprimeArreglo(int Arreglo[], int TAM)
{
int pos;
for (pos=0; pos<TAM; pos++)
printf("\nMes %d: %d accidentes",pos+1,Arreglo[pos]);

}/*fin ImprimeArreglo*/
/* LeeArreglo: lee los elementos de un arreglo, en este caso
* el número de accidentes mensuales
* parámetros: Arreglo[], donde almacenará los datos
* TAM, tamaño del arreglo
* regresa: nada*/
void LeeArreglo(int Arreglo[],
int TAM)
{
int pos;
for (pos=0; pos<TAM; pos++)
{
printf("Mes %d: ", pos+1);
scanf("%d",&Arreglo[pos]);

}/*fin for*/
}/*fin LeeArreglo*/

/* SumaArreglo: suma los elementos del arreglo


* parámetros: Arreglo[], el arreglo que va a sumar
* TAM, tamaño del arreglo
* regresa: suma*/
int SumaArreglo(int Arreglo[], int TAM )
{
int pos, suma = 0;

for (pos=0; pos<TAM; pos++)


suma += Arreglo[pos];

return suma;

}/*fin SumaArreglo*/

/* PromedioArreglo: calcula el promedio de los elementos


* de Arreglo[]
* parámetros: Arreglo[], el arreglo que se va a sumar
* TAM, tamaño del arreglo
* regresa: prom*/
float PromedioArreglo( int Arreglo[], int TAM )
{
int prom, suma;

suma=SumaArreglo(Arreglo,TAM);

prom = (float)suma/TAM;

return prom;

}/*fin PromedioArreglo*/
/* ImpMayoresProm: imprime los meses en los que se reportó
* un número mayor de accidentes que el promedio
* parámetros: Arreglo[], el arreglo que se va a sumar
* TAM, tamaño del arreglo
* prom, promedio del arreglo
* regresa: nada
*/
int ImpMayoresProm(int Arreglo[], int TAM, float prom)
{
int pos;

for (pos=0; pos<TAM; pos++)


if(Arreglo[pos] > prom)
printf("\n Mes %d: %d accidentes", pos+1, Arreglo[pos]);

}//fin ImpMayoresProm

/* Función principal */

main(){

// Declaración de variables

const int TAM = 12;


int meses[TAM], totalAnual;
float promAnual;

printf("***********************************************\n");
printf("* El siguiente programa lee el número de accidentes
que *\n");
printf("* ocurrieron a lo largo de un año en la autopista
del *\n");
printf("* Sol y realiza un reporte con el promedio anual,
el to- *\n");
printf("* tal de accidentes y los meses en los que se
reportó un *\n");
printf("* número mayor al promedio *\n");
printf("************************************************\n");

//Lectura de los datos


printf("\n\nProporciona el número de accidentes registrados
mensualmente\n");
LeeArreglo(meses,TAM);

//Calculos requeridos
totalAnual = SumaArreglo(meses,TAM);
promAnual = PromedioArreglo(meses,TAM);
//Impresión del reporte
system("cls"); //limpiamos la pantalla

printf("*************************************************\n");
printf("* REPORTE DE ACCIDENTES AUTOPISTA DEL SOL *\n");
printf("*************************************************");

printf("\n\nLos accidentes reportados en el año fueron:\n");

ImprimeArreglo(meses,TAM);

printf("\n\nTotal de accidentes ocurridos durante el año =


%d", totalAnual);

printf("\n\nPromedio anual de accidentes = %.0f", promAnual);

printf("\n\nMeses que se registraron más accidentes que el


promedio:\n");

ImpMayoresProm(meses,TAM,promAnual);

printf("\n\n\t\t");
system("pause");
}

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define FILA 5
#define COLUMNA 10

void vaciarSala (int mat[][COLUMNA],int fila,int asiento);


void visualizarAsientos(int mat[][COLUMNA],int fila,int asiento);
void reservaAsiento(int mat[][COLUMNA],int fila,int asiento);

int main(){
srand(time(NULL));
int opcion=1, fila, asiento;
int mat[FILA][COLUMNA]={0};

while (opcion != 0){


//system("cls");
printf("1.- Vaciar sala\n");
printf("2.- Visualizar asientos disponibles\n");
printf("3.- Reservar asientos\n");
printf("0.- Salir\n");
printf("\nEscoge una opcion: ");
fflush(stdin);
scanf("%d", &opcion);
switch (opcion){
case 1: // Vaciar sala
vaciarSala(mat,fila,asiento);
break;

case 2: // Visualizar asientos disponibles


visualizarAsientos(mat,fila,asiento);
break;

case 3: // Reservar asientos


reservaAsiento(mat,fila,asiento);
break;

case 0:
// system("cls");
printf("Has decidido salir, hasta luego");
system ("pause");
return 0;
break;

} // Fin switch
} // Fin while

system ("pause");

void vaciarSala (int mat[][COLUMNA],int fila,int asiento){


// system("cls");
for (fila=0; fila<FILA; fila++) {
for (asiento=0; asiento<COLUMNA; asiento++) {
mat[fila][asiento] = 0;
}
}
printf("\nLa sala ha sido vaciada correctamente\n");
system ("pause");
}

void visualizarAsientos(int mat[][COLUMNA],int fila,int asiento) {


// system("cls");
printf ("\n\t\t\t --Asientos-- \n\t\t0 1 2 3 4 5 6 7
8 9 \n");
for (fila=0; fila<FILA; fila++) {
printf("|| Fila %i ||",fila);
for (asiento=0; asiento<COLUMNA; asiento++) {
printf(" %d ", mat[fila][asiento]);
}
printf("\n");
}
system("pause");
}

void reservaAsiento(int mat[][COLUMNA],int fila,int asiento){


//system("cls");
printf("\tReserva de asientos");
printf("\n------------------------------\n");
printf("\nEscribe la fila: ");
scanf("%d", &fila);
printf("\nSelecciona el asiento: ");
scanf("%d", &asiento);

if (mat[fila][asiento] == 0)
mat[fila][asiento] = 1;
else printf("Asiento ocupado");
system("pause");
}

Ejecución Programa 7.11: reporteAccidentes.c

Visualizar asientos disponibles:


Vaciar sala:

Reservar asientos
Los otros problemas planteados al inicio de este capítulo se resuelven de manera similar al
anterior y se dejan como ejercicio al lector. Con este ejemplo finalizamos el capítulo de
Arreglos Unidimensionales, en el siguiente capítulo revisaremos cómo se pueden manipular
los arreglos de caracteres cuando éstos representan cadenas.

PRÁCTICA DE LABORATORIO.

EJERCICIO I.

1. Contesta las siguientes preguntas

a) ¿Qué es un arreglo bidimensional?

b) ¿Qué diferencia hay entre un arreglo y un arreglo bidimensional?

c) ¿En qué casos se utiliza un arreglo bidimensional?

d) ¿Cuáles son las dimensiones de un arreglo bidimensional?

e) ¿Cuántas filas y columnas tiene un arreglo de 7x4?

f) ¿Cuántos elementos tiene un arreglo cuyo índices son Matriz [6][5] ?

g) ¿Cómo localizas el primero y último elemento del arreglo bidimensional declarado


como Matriz [6][5] ?

h) ¿Qué ventajas tiene definir el tamaño de las filas y columnas de un arreglo cómo
una constante?

i) Pueden almacenarse los siguientes datos en un arreglo bidimensional: [34, 90.5,


“hola”]. Explica tu respuesta.

j) Puede almacenarse los siguientes datos en un arreglo bidimensional: [“hola”,


“estudiantes”, “Uacemitas”]. Explica tu respuesta.

k) Cuando una función en lenguaje C recibe como parámetro un arreglo bidimensional,


éste se pasa por referencia o por valor. Explica.

2. Determina en cuáles de los siguientes casos los datos se pueden almacenar en variables simples y en
cuales en arreglo; declara en lenguaje C las variables. Por ejemplo:

Ejemplo 1: Almacenar el valor de las 10 resistencias conectadas en serie en un circuito electrónico.


Respuesta: Arreglo de 10 enteros (int resistencias[10]) DUDA: El valor de las resistencias es
entero

Ejemplo 2: Resistencia equivalente a 20 resistencias conectadas en serie.

Respuesta: variable entera (int totalResistencia)

i. Puntaje de los 20 clavadistas que se inscribieron a la competencia de clavados en la


Quebrada.

ii. Record mundial en 100 metros planos.

iii. Temperaturas registradas durante cada uno de los días del año.

iv. Ganancias mensuales de la compañía de circuitos electrónicos QinetiQ.

v. Placa de un automóvil.

vi. Máxima temperatura registrada en un mes.

vii. Número de lados de un polígono.

viii. Registro de la producción mensual de automóviles a lo largo de un año.

ix. Número de participantes inscritos a la competencia de clavados.

x. Registrar el consumo diario de luz para determinar la cuota mensual.

3. Realiza la prueba de escritorio para el modula SumaArreglo(int[ ], int ) con la siguiente llamada:

SumaArreglo( [4,7,6,-5,8], 5)
4. Modifica el Programa ¿? para que el reporte también incluya el número mínimo y máximo de
accidentes mensuales reportados. (Diseña una función que regrese el valor mínimo de un arreglo y
otra que devuelva el valor máximo)

5. Realiza el análisis, diseño e implementación de los Problemas 1 y 2 presentados al inicio de este


capítulo.

6. Construye programas en lenguaje C para cada uno de los siguientes problemas planteados. No
olvides que debes realizar el análisis y diseño del algoritmo antes de implementarlo.

i. Dado el vector a=(a1,a2,a3,a4,a5,a6), calcula la norma (denotada por |a|) del vector
utilizando la siguiente fórmula:

| a |= a1 + a2 + a3 + a4 + a5 + a6
2 2 2 2 2 2

ii. Escribe un programa que calcule la varianza de una lista de N números enteros (N<50).
La fórmula para calcular la varianza es
N −1
varianza =  ( xi − x) 2
i =0

donde x es el promedio de todos los números xi.

iii. Escribe un programa que calcule el centro de masa de un sistema con n masas
puntuales. El usuario debe proporcionar para cada punto i: su coordenada xi, su
coordenada yi y su masa mi. El programa debe imprimir las coordenadas del centro de
masa de los puntos que se introdujeron, denotas por (xg,yg), las cuales se calculan
utilizando las siguientes fórmulas:
n n

 mi xi m y i i
xg = i =1
n
yg = i =1
n

m
i =1
i m
i =1
i

(Pista: puedes utilizar tres arreglos, uno para las coordenadas en x, otro para las
coordenadas en y y el último para las masas)

iv. Un polinomio puede representarse por medio de un arreglo pensando que los índices
del arreglo representan el grado del término mientras que los elementos almacenados
representan el coeficiente del término. Por ejemplo, el polinomio
5x 4 + 8x 2 + 1 = 0
Puede representar en un arreglo de tamaño 5 como:

1 0 8 0 5

0 1 2 3 4

En la posición 0 se encuentra el coeficiente del término con grado 0, en la posición 1 el


coeficiente del término con grado 1 y así sucesivamente, cuando en el polinomio no
aparece un término de grado k, entonces en la posición k del arreglo se almacena 0.
Tomando en cuenta la información anterior realiza un programa que calcule la suma de
dos polinomios, cuyo grado no sea mayor a 5.

También podría gustarte