Fundamentos de Programacion Fundamentos
Fundamentos de Programacion Fundamentos
Fundamentos de Programacion Fundamentos
Programa Desarrollado
Cuatrimestre dos
Programa de la asignatura:
Fundamentos de Programación
Clave:
150910206
ESAD
Agosto, 2010
Índice
b. Descripción ........................................................................................................................................................ 3
c. Propósito ............................................................................................................................................................ 4
III. Temario 6
IV. Metodología de trabajo 8
V. Evaluación 10
VI. Materiales de apoyo 11
VII. Desarrollo de contenidos por unidad 12
Unidad 1: Introducción a la computadora y desarrollo de software ........................................................... 12
a. Ficha de identificación
Horas contempladas: 72
b. Descripción
La asignatura de Fundamentos de Programación tiene como meta principal que desarrolles las
competencias necesarias para construir programas que resuelvan problemas reales. Constituye un pilar
primordial para aquellos alumnos que, como tú, están estudiando una carrera relacionada con
computación -como Desarrollo de Software y Telemática- pues los conocimientos y habilidades que
desarrollen en este curso serán indispensables para la varias de las asignaturas subsecuentes del plan
de estudios de ambas ingenierías, tal es el caso de bases de datos, programación orientada a objetos,
programación web, estructura de datos, entre otras.
El lenguaje de alto nivel que se ha elegido para implementar los programas es C, ya que es uno de los
lenguajes, estructurados y modulares, utilizados actualmente; además cuenta con entornos de trabajo
de software libre, como el que utilizaremos llamado Dev-C.
Para facilitar que desarrolles las competencias establecidas, los contenidos del curso se han dividido de
forma estratégica en seis unidades. En la primera se delinea el concepto de computadora y su
funcionamiento, para ello se describen los elementos que la integran; posteriormente, se hace una
aproximación a los lenguajes de programación y los paradigmas que existen en la actualidad,
resaltando la programación modular y estructurada. En la siguiente unidad se expone la metodología de
la programación y se introduce el concepto de algoritmo, que es la solución sistemática de problemas,
así que también se presentan dos formas de representación: el pseudocódigo y los diagramas de flujo.
Asimismo, se ilustran las estructuras secuenciales, selectivas y repetitivas. A continuación, en la unidad
tres, se presentan los elementos básicos para construir un programa simple en lenguaje C: datos,
contantes, variables y las operaciones básicas de entradas/salidas para describir el uso de los
c. Propósito
El curso tiene como finalidad proporcionarte las bases para que comiences a desarrollar programas
estructurados que solucionen problemas simples, a través del desarrollo de algoritmos en pseudocódigo
y diagrama de flujo y su codificación en lenguaje C, además se te describe el proceso de compilación de
un programa y su ejecución identificando los elementos de la computadora que intervienen en cada
paso.
Competencia general:
Competencias específicas:
III. Temario
1. Introducción a la computadora y desarrollo de software
1.1. ¿Qué es una computadora?
1.2. Estructura y funcionamiento de una computadora
1.2.1. Modelo de Von Neumann
1.2.2. Ejecución de programas en la computadora
1.2.3. Almacenamientos de programas y datos
1.3. Lenguajes de programación
1.3.1. Evolución de los lenguajes de programación
1.3.2. Paradigmas de los lenguajes de programación
1.4. Ciclo de vida del software
2. Diseño de algoritmos
2.1. Concepto de algoritmo y características
2.2. Representaciones de algoritmos
2.2.1. Pseudocódigo
2.2.2. Diagrama de flujo
2.3. Estructuras de control
2.3.1. Secuenciales
2.3.2. Selectivas
2.3.3. Repetitivas
3. Introducción al lenguaje C
3.1. Componentes de un programa
3.1.1. Instrucciones
3.1.2. Comentarios
3.1.3. Palabras reservadas
3.1.4. Estructura general de un programa
3.2. Tipos de datos
3.3. Variables y constantes
3.3.1. Identificadores
3.3.2. Declaración e inicialización de variables
3.3.3. Tipos de constantes
3.3.4. Declaración de constantes
3.4. Expresiones matemáticas
3.4.1. Tipos de operadores
3.4.2. Evaluación de expresiones
3.5. Bibliotecas y funciones
3.5.1. Funciones matemáticas
3.5.2. Funciones de entrada y salida
3.6. Codificación de algoritmos
4. Estructuras de control
4.1. Estructuras selectivas
4.1.1. Estructura selectivasimple (if)
4.1.2. Estructura selectiva doble (if-else)
4.1.3. Estructura selectiva múltiple (switch-case)
4.2. Estructuras repetitivas
4.2.1. Estructura Mientras (while)
4.2.2. Estructura Desde-mientras (for)
4.2.3. Estructura Hacer-mientras (do-while)
4.3. Estructuras anidadas
5. Estructuras de datos
5.1. Arreglos
5.1.1. Definición y tipos
5.1.2. Declaración e inicialización
5.1.3. Acceso a los elementos de un arreglo
5.1.4. Ciclos y arreglos
5.1.5. Cadenas
5.2. Estructuras
5.2.1. Definición, declaración e inicialización
5.2.2. Acceso a los elementos
6. Funciones
6.1. Diseño descendente
6.2. Definición, declaración e invocación de funciones en C
6.3. Alcance de las variables
6.4. Paso de parámetros
6.4.1. Por valor
6.4.2. Por referencia
Ahora bien, para que puedas resolver un problema mediante un programa de computadora, primero
tienes que tener claro el funcionamiento principal de la misma, tanto para almacenar la información
como para llevar a cabo el conjunto de instrucciones que integran la solución de un problema, es decir,
el programa. Por tal motivo, en la primera unidad se expone la arquitectura tradicional de las
computadoras conocida como el Modelo de Von Neumann a través de la cual se te explica cómo se
almacenan los programas y los datos de un problema en una computadora y qué elementos intervienen
en la ejecución de las instrucciones. Lo anterior se expone utilizando un programa que se encarga de
calcular el área de un rectángulo, de esta manera podrás aprender los contenidos mediante un ejemplo
simple. Posteriormente, se describen los pasos que llevan a la construcción de un programa,
nuevamente, utilizando un ejemplo simple. La evaluación de esta unidad se realizará mediante una
prueba de opción múltiple en la que se incluyen los temas expuestos.
Para exponer los temas relacionados con la elaboración de los algoritmos, se recurre al mundo de la
ardilla, un ejemplo en el cual se supone que una ardilla ha sido entrenada para realizar un conjunto
limitado de instrucciones precisas, de tal manera que para solucionar un problema únicamente se puede
recurrir a este conjunto.
De esta forma se pretende que reconozcas intuitivamente que la computadora –al igual que la ardilla–
sólo es capaz de realizar un conjunto limitado de instrucciones precisas, por lo que la solución de un
problema sólo puede incluir instrucciones reconocidas por ésta. Así mismo, mediante este ejemplo, se
te muestra qué es un algoritmo, cuáles son sus características y sus formas de representación;
posteriormente se te presentan las estructuras de control básicas de la programación estructurada
ejemplificando su uso a través de la construcción de algoritmos que dirigen a la ardilla en la realización
de una tarea específica. Así se te introduce en el diseño de soluciones estructuradas de problemas
simples representadas de manera formal, ya sea con un diagrama de flujo o con un pseudocódigo. Es
importante mencionar que la ventaja de presentar el tema desde un ejemplo tan simple y limitado como
el mundo de la ardilla, facilita que puedas proponer soluciones estructuradas en un lenguaje específico,
que es justo de lo que se trata la programación.
En general, en las actividades formativas deberás realizar algunas modificaciones a los algoritmos o
programas presentados en los contenidos, o bien, los escribirás en lenguaje C para que puedas ver su
ejecución, además debes ingresar al foro de cada unidad para realizar los comentarios que tengas al
respecto. Es importante que realices cada una de las actividades propuestas por que a través de ellas
se complementan los temas expuestos. De igual manera, para poner en práctica los conocimientos
adquiridos y evaluar que se haya alcanzado la competencia específica a lo largo del curso desarrollarás
un proyecto integrador que consiste en el planteamiento, análisis, diseño de la solución e
implementación de un programa que involucre el uso de todas las estructuras de control y de datos que
se estudiarán. En este caso, tú serás el responsable de plantear el problema que deseas solucionar y
junto con tu facilitador delimitarás las fases y/o versiones que realizarás en cada unidad. El papel que
juega tu facilitador (a) en el proceso de aprendizaje es fundamental, pues su tarea es guiarte en la
solución de problemas a partir del análisis que tú realices y las ideas o dudas que te surjan con el
mismo. El facilitador por ningún motivo debe darte la solución completa de un problema, en lugar de ello
debe propiciar el intercambio de ideas y experiencias de los estudiantes mediante foros de discusión,
favoreciendo el aprendizaje colaborativo; si después de esto siguen existiendo dudas se recomienda
que resuelva problemas similares para que tanto tú como tus compañeros refuercen los conocimientos y
habilidades de una manera inductiva y así logren dar solución al problema en cuestión. Por lo anterior,
existe un foro de discusión general en el cual cualquiera puede exponer sus dudas respecto a un tema
o problema para que sean retroalimentados por tu facilitador y los compañeros de grupo. Es importante
que el facilitador promueva un clima de respeto mutuo y libertad de expresión para que tanto tú como
tus compañeros se sientan cómodos al externar sus dudas y propuestas, permitiendo la disensión de
manera fundamentada y deferente.
V. Evaluación
Por lo anterior, para aprobar la asignatura, se espera la participación responsable y activa del
estudiante así como una comunicación estrecha con su facilitador para que pueda evaluar
objetivamente su desempeño. Para lo cual es necesaria la recolección de evidencias que permitan
apreciar el proceso de aprendizaje de contenidos: declarativos, procedimentales y actitudinales.
ESQUEMA DE EVALUACIÓN
Foros y base de datos 10%
Actividades formativas 30%
Evidencias 40%
E-portafolio. 50%
Autorreflexiones 10%
Examen final 10%
CALIFICACIÓN FINAL 100%
Cabe señalar que para aprobar la asignatura, se debe de obtener la calificación mínima indicada por la
ESAD.
Bibliografía básica:
Böhm, C., & Jacopini, G. (1966). Flow diagrams, Turing machines, and languages only with two
formation rules". Communications of the ACM, 9 (5), 366-371.
Joyanes, L., & Zohanero, I. (2005). Programación en C. Metodología, algoritmos y estructuras de datos.
España: Mc Graw Hill.
Reyes, A., & Cruz, D. (2009). Notas de clase: Introducción a la programación. México, D.F.: UACM.
Viso, E., & Pelaez, C. (2007). Introducción a las ciencias de la computación con Java. México, D.F.: La
prensas de ciencias, Facultad de Ciencias, UNAM.
Propósitos de la unidad
En esta unidad:
Identificarás los conceptos básicos relacionados con la computadora y los lenguajes de
programación.
Distinguirás los elementos de una computadora que intervienen en la ejecución de un programa
a través del modelo de Von Neumann.
Distinguirás los paradigmas de programación y los lenguajes asociados a éstos.
Reconocerás las fases que se siguen para solucionar un problema mediante la computadora.
Competencia específica
Introducción
Bienvenido a la primera unidad del curso fundamentos de programación, en esta unidad estudiaremos
qué son las computadoras y cómo pueden ayudarnos para resolver problemas. Lo primero que tienes
que tener claro es que las computadoras no poseen inteligencia alguna, ya que por sí solas no son
capaces de resolver ningún problema, su importancia está en la capacidad de datos que pueden
almacenar y manipular; de tal manera que para lograr nuestro fin– resolver problemas mediante la
computadora – es necesario desarrollar programas escritos en un lenguaje de programación para que
puedan ser ejecutados por una computadora.
Los orígenes de las computadoras se remontan a dispositivos mecánicos como el ábaco que sirve para
contar y ya se utilizaba en el año 2000 a.C, seguido de la Pascalina que fue la primera calculadora
mecánica del mundo inventada por el francés Blaise Pascal en el año de 1642, y que después fue
perfeccionada por Gottfried Leibniz. Dos siglos después, en el año de 1834, Charles Babbage anticipó
la estructura de la computadora electrónica moderna, sin lograr su objetivo dadas las limitaciones de
ingeniería del siglo XIX. Fue hasta la segunda guerra mundial (1936) que se cumplieron sus
expectativas, cuando Alan Turing desarrolló la primera computadora electromecánica: el Colossus,
encargada de descifrar los mensajes alemanes escritos en código Enigma.1 Una década después
1 Se conoce como código Enigma al lenguaje de codificación de la máquina con el mismo nombre, utilizada en Europa a inicios de 1920, para
cifrar y descifrar mensaje.
Por lo anterior, en esta unidad se estudiarán los siguientes temas: el Modelo de Von Neumann, los
pasos para realizar un programa y los principales paradigmas y lenguajes de programación utilizados
actualmente.
Para facilitar el estudio de la asignatura, hemos creado un foro de discusión general, a través del cual
podrás comentar cualquier asunto relacionado con Fundamentos de programación. Accede al foro
desde el aula virtual.
Para fines de este curso entenderemos que una computadora es una máquina electrónica que recibe
datos de entrada y los procesa de acuerdo al conjunto de instrucciones, llamado programa, para
obtener nuevos datos que son el resultado del proceso, tal como se ilustra en la siguiente figura.
Figura 1.1:Computadora
Lo anterior nos lleva a clasificar los componentes de una computadora en dos clases: hardware y
software. Los recursos de hardware son todos aquellos elementos de la computadora que se pueden
palpar, como por ejemplo: el monitor, el teclado, el disco duro, la memoria, entre otros. En cambio, los
recursos de software son aquellos elementos intangibles sin los cuales la computadora no funcionaría,
esto es, el soporte lógico: programas y datos, entre los cuales se encuentran los sistemas operativos,
editores de texto, compiladores, bases de datos, videojuegos, entre otros.
El Modelo de Von Neumann propone que tanto el programa como los datos sean almacenados en la
memoria, de esta forma la computadora no tendría que reconstruirse, pues para programarla
únicamente debe introducirse el programa por el dispositivo indicado, y posteriormente alimentar con los
datos de entrada para que calcule la salida correspondiente. Los elementos que componen esta
arquitectura son: la unidad central de procesamiento integrada por la unidad aritmética-lógica y la
unidad de control, la memoria y los dispositivos de entrada/salida. A continuación se describe
brevemente la función de cada uno de los elementos que integran el Modelo de Von Neumann:
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.
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) y las cintas magnéticas, entre otras, son ejemplos de dispositivos de almacenamiento
secundario.
Es así que todas las unidades de la computadora se comunican a través del sistema de buses que son
cables mediante los cuales se envían señales y dependiendo de la información que transmiten se
clasifican en:
Ahora ya sabemos cómo está estructurada internamente la computadoras, qué elementos la integran y
cuál es la función de cada uno de ellos, el siguiente paso es descubrir cómo colaboran para llevar a
cabo la ejecución de un programa, en seguida lo explicamos: los datos de entrada que requiere un
programa se introducen a la computadora, a través de los dispositivos de entrada; posteriormente se
almacenan en la memoria RAM, para que la CPU pueda procesarlos, conforme a las instrucciones del
programa, hasta obtener el resultado deseado, mismo que envía al usuario por medio de los
dispositivos de salida. Todas estas acciones son coordinadas por la unidad de control que envía las
señales y datos a cada uno de los dispositivos de la computadora involucrados en la ejecución de las
instrucciones del programa a través del sistema de buses. En la siguiente sección se describe con
mayor detalle este proceso.
Para que entender mejor lo que sucede en el interior de la CPU al ejecutar cualquier programa, a
continuación se describen de manera general los pasos que se realizan, una vez que el programa y los
datos fueron almacenados en la memoria principal:
Al período en el que se ejecuta una instrucción se le conoce como ciclo de instrucción o ciclo fetch.
Con el fin de ilustrar este procedimiento, analizaremos la ejecución del siguiente programa escrito en un
lenguaje de programación ficticio.
Antes de definir paso a paso la ejecución de este programa describiremos la función de cada una de las
instrucciones que lo integran.
Instrucción Descripción
Imprime en el dispositivo de salida estándar los <Datos>
Imprimir <Dato> indicados en la instrucción, que pueden ser un mensaje de texto
o el valor de una variable.
Lee por medio del teclado un dato, lo almacena en la variable
Leer <X>
<X> indicado y lo almacena en la memoria RAM.
La flecha representa una asignación, esta acción actualiza la
<X> ← <Dato>
dirección de memoria asignada a <X> con el valor <Dato>.
Tabla 1.1: Lista de instrucciones en lenguaje de programación ficticio
Cabe señalar que en los lenguajes de programación, las direcciones de memoria se representan por
medio de variables, para hacerlos más legibles. De tal manera que <X> representa una variable y
<Dato> puede ser un mensaje o cualquier valor.
Ahora sí, de acuerdo con la información anterior, en la siguiente tabla se describen paso a paso las
acciones que realiza la unidad de control junto con las otras unidades de la computadora involucradas
en la ejecución de cada una de las instrucciones del programa.
La computadora sólo entiende señales binarias: ceros y unos, encendido y apagado, ya que todos los
dispositivos de una computadora trabajan con dos únicos estados: hay corriente eléctrica y no hay
corriente, respectivamente. Por tal motivo, los datos y programas almacenados en la memoria están
codificados como cadenas de 1´s y 0´s para que la unidad de control pueda interpretarlos. A esta
codificación se le llama lenguaje de máquina.
Cabe mencionar que la memoria está dividida en varias celdas, en cada una de ellas se puede
almacenar únicamente 0s ó 1s, a estos valores se les denomina valores binarios o BIT´s (BInary DigiT).
Las celdas se agrupan para formar registros (también llamados palabras), a cada uno le corresponde
una dirección de memoria, así cuando se desea escribir o leer de la memoria un dato o una instrucción
se debe especificar la dirección dónde se encuentra.
Como podrás imaginar, para un ser humano resultaría sumamente complicado escribir los programas
en lenguaje de máquina, es por eso que los programas se escriben en lenguaje de programación
entendibles para los seres humanos y después se traducen mediante un software especial –que pueden
ser un compilador o un traductor– a cadenas de 0´s y 1´s. De tal manera que a cada instrucción le
corresponde un código binario específico y para cada dato también existe una codificación única. Por
ejemplo, la palabra “Hola” se representa como “0100 1000 0110 1111 0110 1100 0110 0000”, ya que a
cada letra le corresponde una codificación:
H O L A
0100 1000 0110 1111 0110 1100 0110 0000
El número 80 se puede representar como “0101 0000” y la instrucción “MOV R2, R7” se codifica de la
siguiente manera “0010 0000 1000 0100.”
Los lenguajes de programación sirven para escribir programas de computadora orientados a resolver
algún problema o necesidad. Cada lenguaje de programación se define a partir de un conjunto de
símbolos básicos, llamado alfabeto; un conjunto de reglas, llamado sintaxis, que definen la forma de
manipularlos o combinarlos para representar instrucciones; y las reglas que especifican los efectos de
dichas instrucciones cuando son ejecutadas por la computadora, conocidas como semántica. De esta
manera tenemos que:
� ó = + � + á
Por otro lado, dependiendo de su legibilidad para el ser humano los lenguajes de programación se
clasifican en 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, como son:
sumar, restar, guardar en memoria, etc. En cambio, las instrucciones de los lenguajes de alto nivel son
más parecidas a un lenguaje humano, por lo regular inglés. Por otro lado, los programas escritos en
bajo nivel describen a detalle lo que sucede a nivel de hardware, mientras que los programas escritos
en un lenguaje de alto nivel lo ocultan, teniendo como ventaja que son más fáciles de entender para las
personas.
Con las primeras computadoras surgió el primer lenguaje de programación que –como es de
imaginarse– fue el lenguaje de máquina, el cual es considerado el lenguaje de primera generación. Las
instrucciones en lenguaje de máquina dependían de las características de cada equipo, por lo que dada
la dificultad de desarrollar programas en unos y ceros, los investigadores de la época desarrollaron el
lenguaje ensamblador, cuyo conjunto de instrucciones consta de palabras nemotécnicas que
corresponden a las operaciones básicas que una computadora puede ejecutar.
Lo cual es ilegible para el ser humano, en cambio en lenguaje ensamblador esta instrucción se puede
representar de la siguiente forma:
MOV R2, R7
2 Este ejemplo es una adaptación de la versión original que aparece en (Joyanes & Zohanero, 2005, pág. 32) (Joyanes & Zohanero, 2005, pág. 32)
Aunque sigue estando en clave, es más amigable que las cadenas de ceros y unos. Otra característica
del lenguaje ensamblador es que las instrucciones dependían de las características físicas
(arquitectura) de la computadora.
Al igual que el lenguaje ensamblador, los programas escritos en un lenguaje de alto nivel deben ser
codificados a lenguaje de máquina, así que junto con ellos se desarrollaron programas traductores, que
de acuerdo con la forma en que trabajan se dividen en dos tipos: compiladores e intérpretes.
Los compiladores traducen todo el programa escrito en un lenguaje de alto nivel, llamado
programa fuente, generando un nuevo programa objeto que está escrito en lenguaje de máquina
y a partir de este se genera un programa ejecutado, el cual puede ejecutarse cada vez que se
desee sin tener que compilar el programa fuente de nueva cuenta. Además, como parte del
proceso de traducción el compilador detecta los errores que hay en el código fuente,
informándole al programador para que los corrija, pues un programa sólo se compila si no tiene
errores.
En cambio, un intérprete revisa una a una cada línea de código, la analiza y enseguida la
ejecuta, sin revisar todo el código y sin generar un programa objeto, así que cada vez que se
quiere ejecutar el programa se vuelve a traducir el programa fuente línea por línea.
Por lo anterior, los compiladores requieren una fase extra antes de poder generar un programa
ejecutable, y aunque esto pareciera menos eficiente en cuanto a tiempo, los programas creados con
compiladores se ejecutan mucho más rápido que un mismo programa ejecutado con un intérprete.
Además, cuando un programa ya ha sido compilado puede ejecutarse nuevamente sin tener que
compilarse, mientras que los programas que son interpretados, cada vez que se ejecutan se deben
volver a traducir.
Conforme han ido evolucionando las computadoras también lo han hecho las estrategias para
solucionar problemas, generando nuevos programas programación con diferentes filosofías, llamadas
paradigmas de programación, de esto hablaremos a continuación.
Un paradigma de programación representa un enfoque particular o filosofía para diseñar soluciones. Los
paradigmas difieren unos de otros en los conceptos y la forma de abstraer los elementos involucrados
en un problema, así como en los pasos que integran su solución del problema, en otras palabras, el
cómputo.
� ( , ) = ∗
De tal manera que para calcular el área de un rectángulo de base igual a 5 unidades y altura
igual a 10 unidades, se ejecuta la función con los parámetros 5,10, es decir, areaRectángulo
(5,10), la cual devuelve como resultado 50.
Los lenguajes de programación más representativos del paradigma funcional son: Lisp, ML y
Haskell.
Esta regla dice que el valor de la variable area corresponde al área del rectángulo con base b y
altura h sólo si area es el resultado de multiplicar b por h. Estamos suponiendo que se ha
definido el predicado multiplicación(a, b, c).
En este caso para calcular el resultado se utiliza el principio de razonamiento lógico para
responder a las preguntas planteadas, por ejemplo si se desea calcular el área del mismo
rectángulo, la pregunta sería la siguiente:
? � (5,10, �)
Y después de que se realicen los cálculos (en este caso llamadas inferencias) el resultado que
arrojaría sería:
� = 50
Independientemente del paradigma que se siga y del lenguaje que se utilice para programar, existe un
conjunto de fases que deben seguirse para realizar un programa de computadora, al cual se le conoce
como ciclo de vida del software, en la siguiente figura se lista cada una de ellas.
Documentación
Pruebas y
delprograma
validación
En las siguientes secciones aprenderemos en qué consiste cada una de las fases, para lo cual nos
apoyaremos en la resolución del siguiente problema con la intención de que se entienda mejor:
Problema 1.1: Se requiere construir un programa que calcule el área de un rectángulo, con base
b y altura h.
En esta fase se determina ¿qué hace el programa? Por lo cual debe definirse de manera clara y concisa
el problema en cuestión, se debe establecer el ámbito del problema, las características, limitaciones y
modelos de lo que se desea resolver. Este paso debe conducir a una especificación completa del
problema en donde se describa cuáles son los datos requeridos para resolverlo (datos de entrada) y
cuál es el resultado deseado (salida).
1.4.2Diseño de la solución
Es en esta fase se define ¿cómo el programa resuelve el problema? Para ello, se describe paso a paso
la solución del mismo, lo cual se conoce como algoritmo. Cuando el problema es grande se recomienda
dividirlo en subproblemas más pequeños y resolver por separado cada uno de ellos. A esta metodología
se le conoce como diseño descendente (top-down) o modular. Existen diferentes formas de representar
un algoritmo algunas formales, como una fórmula matemática, o informales, como es el caso del
lenguaje natural.
En la siguiente unidad estudiaremos a mayor profundidad los algoritmos y su representación, pero para
seguir con el desarrollo de nuestro programa ejemplo, plantearemos la solución como una secuencia de
pasos en español.
El programa 1.1es otra forma de representar la solución de este problema, se conoce como
pseudocódigo.
El algoritmo no puede ser ejecutado por una computadora por ello debe traducirse a un lenguaje de
programación (como por ejemplo C) para obtener un programa fuente que se traduzca a lenguaje de
máquina para que sea ejecutado por la computadora.
En el siguiente cuadro se muestra la codificación en lenguaje C del algoritmo, por ahora no es necesario
que lo comprendas puesto que esto lo podrás hacer conforme vayas aprendiendo a programar, por lo
pronto solamente se muestra con fines ilustrativos.
#include<stdio.h>
#include<stdlib.h>
main()
{
int b,h, area;
printf("Ingresa la base y altura:");
scanf("%d %d", &b,&h);
area = b * h;
printf("Area = %d", area);
}
Programa 1.2: Programa en C que calcula área de un rectángulo
Esta fase debe hacerse una vez que se ha diseñado el algoritmo y después de que se codifica, sirve
para verificar que son correctos. Existen diferentes formas de probar que la solución es correcta,
algunas de ellas formales y otras informales: las primera se utilizan para garantizar que el programa o
algoritmo siempre calcula el resultado deseado para cualquier conjunto de datos de entrada; en cambio,
en las segundas sólo se prueba que funciona correctamente para algunos datos de entrada, tratando de
encontrar posibles errores, en este caso no se puede garantizar el programa o algoritmo calcule la
salida correcta para cualquier conjunto de datos. En cualquiera de los dos casos, si se encuentra alguna
falla se debe corregir y volver a realizar pruebas. En este curso utilizaremos las pruebas de escritorio,
las cuales se explicarán en la unidad 2.
1.4.5 Documentación
Cualquier proyecto de software por la complejidad que tiene requiere tanto las ideas principales como
el desarrollo de principio a fin sea documentado, con el fin de que cualquiera puedan entender la lógica
del programa y de ser necesario pueda modificarlos sin tantas complicaciones. Es común que si se
desea modificar un programa y no se tiene información acerca de cómo fue construido sea más fácil
volverlo a hacer que intentar entenderlo. Uno de los mejores ejemplos de la importancia de la
documentación es el software libre, en el cual colaboran diversos desarrolladores para su elaboración,
los cuales se encuentran en diferentes puntos geográficos de globo terráqueo, así que la forma de
entender que está haciendo cada uno y bajo que método es la documentación. Además de que se debe
tomar en cuenta que se llama software libre porque está disponible el código fuente para que cualquier
persona pueda modificarlo a su conveniencia.
Como parte de la documentación también deben incluirse manuales de usuario y las normas de
mantenimiento para que se haga un buen uso del software.
1.4.6 Mantenimiento
Esta fase tiene sentido una vez que fue terminada una primera versión del programa y ya está siendo
utilizado. Ya que en ésta se actualiza y modifica para corregir errores no detectados o para cambiar y/o
agregar una nueva función. Por ejemplo, se puede extender el programa 1.1, que calcula el área de un
rectángulo para que también calcule su perímetro.
#include<stdio.h>
#include<stdlib.h>
main()
{
int b,h, area, perimetro;
printf("Ingresa la base y altura:");
scanf("%d %d", &b,&h);
perimetro = 2*b + 2*h;
area = b * h;
printf("Perimetro = %d", perimetro);
printf("Area = %d", area);
}
En el programa se resaltan las instrucciones que se añadieron al programa para calcular el perímetro.
Actividad de Autoevaluación
Ingresa al aula virtual para realizar las dos actividades de autoevaluación, la primera trata de un
pequeño juego tipo maratón en el que pondrás a prueba tus conocimientos, la segunda consiste en un
cuestionario de opción múltiple.
Para alcanzar los objetivos de esta unidad se ha propuesto una actividad formativa en la cual revises la
ejecución de un problema apoyándote en una animación que muestra paso a paso la ejecución del
programa 1.1 que se presentó en esta unidad, en dicha animación se especifica cada uno de los
elementos que están involucrados en la ejecución de las instrucciones.
Referencias:
Reyes, A., & Cruz, D. (2009). Notas de clase: Introducción a la programación. México, D.F.:
UACM.
Viso, E., & Pelaez, C. (2007). Introducción a las ciencias de la computación con Java. México,
D.F.: La prensas de ciencias, Facultad de Ciencias, UNAM.
Propósitos
En esta unidad:
Identificar los datos de entrada y la salida de un algoritmo
Diseñar un algoritmo que solucione un problema.
Representar el algoritmo en diagrama de flujo y pseudocódigo
Verificar que el algoritmo calcule el resultado correcto
Competencia específica
Introducción
En la unidad anterior se describieron los pasos que se requieren para construir un software, sin lugar a
dudas, la fase más importante es el diseño de la solución, ya que es aquí donde se debe crear un
modelo que corresponde a los pasos que llevan a la solución del problema en cuestión, y se conoce
como algoritmo. Para construir esta solución se requiere no sólo de inteligencia, sino también de
creatividad, ya que el programador sólo cuenta con la especificación del problema y su experiencia en
resolver problemas de una forma estructurada.
Para logar nuestro objetivo se introduce “el mundo de la ardilla”, en el cual se deben solucionar
problemas mediante un conjunto de instrucciones específicas que puede ejecutar una ardilla sobre un
tablero.
La palabra algoritmo proviene del nombre de un matemático persa conocido como Mohammad Al-
KhoWârizmi, nacido alrededor del 780 d.c. en KhoWârizm, de ahí el su seudónimo. Se considera como
el padre de la algoritmia porque definió las reglas paso a paso para sumar, restar, multiplicar y dividir
números decimales. La traducción al latín de Al-Khwārizmī es algoritmi, que da origen a la palabra
algoritmo (Joyanes & Zohanero, 2005).
Formalmente, un algoritmo se define como una secuencia finita de instrucciones precisas y eficaces
para resolver un problema, que trabaja a partir de cero o más datos (entrada) y devuelve un resultado
(salida).4
Para ilustrar este concepto se presenta el siguiente escenario ficticio, que hemos llamado:
El mundo de la ardilla
Supongamos que una ardilla ha sido entrenada para realizar las instrucciones que se muestran en la
tabla 2.1, sobre un tablero.
Los paréntesis al final de cada instrucción sirven para identificar que se trata de una orden que puede
ejecutar la ardilla. Si observas la lista de instrucciones podrás darte cuenta 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, podría
realizar ambas tareas si se le dan las instrucciones precisas en términos de las acciones que sabe
hacer. Por ejemplo, para que la ardilla gire a la derecha tendríamos que ordenarle tres veces que girará
a la izquierda, es decir, la secuencia de instrucciones que debe ejecutar es:
giraIzquierda()
giraIzquierda()
giraIzquierda()
4 Esta definición es una adaptación de la que aparecen en (Viso & Pelaez, 2007, pág. 3)
5La ardilla poseen una bolsa donde almacena cualquier cantidad de bellotas.
Estos pasos constituyen un algoritmo, el cual soluciona el problema de hacer que la ardilla gire a la
derecha.
Una de las características principales de los algoritmos es que cada paso debe estar definido de forma
clara y precisa, sin ambigüedades, de tal manera que pueda ejecutarse de manera inequívoca, por
ejemplo, en el mundo de la ardilla, la instrucción gira() sería una instrucción ambigua, ya que la ardilla
no sabría si debe girar a la derecha o a la izquierda.
Otra característica de los algoritmos es que siempre terminan, por lo que no puede ser una lista infinita
de pasos. Y tampoco puede contener pasos que sean irrealizables o cuya ejecución sea infinita, pues
en este caso no sería posible calcular el resultado deseado, si una instrucción está bien definida y es
eficaz se puede asegurar que su ejecución termina con éxito, sin embargo, esto no garantiza, de
ninguna manera, que el algoritmo también termine.
Por lo anterior, al diseñar un algoritmo se debe garantizar que dada cualquier entrada siempre termine y
calcule la respuesta correcta. De tal manera que todo algoritmo debe tener las siguientes
características:
1.Entrada.
2. Salida.
3. Definido.
4. Eficaz.
5. Terminación.
Una vez que se ha diseñado un algoritmo, se recomienda realizar una prueba de escritorio para verificar
si funciona correctamente, ésta consiste en ejecutar el algoritmo utilizando papel y lápiz, se propone
datos de entrada específicos y se realiza cada una de las instrucciones en el orden establecido,
registrando los cambios que se producen después de la ejecución de cada instrucción. De esta manera,
se valida que el resultado obtenido en la prueba de escritorio corresponda al resultado deseado (el
correcto).
Existen diversas formas de representar un algoritmo, en la unidad anterior expusimos diversas formas
de representar la solución del problema de calcular el área de un rectángulo, por ejemplo, en el
programa 1.1 se expresa la solución en pseudocódigo, después en el algoritmo 1.1 se representa en
lenguaje natural (español) y en el programa 1.2 se utiliza el lenguaje de programación C, o se puede
expresar mediante la fórmula matemática:
á = ×
Todas estas representaciones, excepto el lenguaje natural, se consideran formales, y cabe mencionar
que existen más, sin embargo, las representaciones más comunes son el pseudocódigo y los diagramas
de flujo. La primera, generalmente se utilizar por su parecido con el lenguaje natural (español, inglés,
francés o cualquier otro) y porque su codificación en un lenguaje de programación estructurado y
modular, como C, es directa. En cambio, los diagramas de flujo son totalmente gráficos, lo que hace
más fácil seguir el orden en que se ejecutan las instrucciones. Es importante mencionar que se puede
utilizar cualquiera de las dos representaciones para diseñar un algoritmo, pues en cualquiera de los dos
se puede expresar cualquier algoritmo estructurado, de tal manera que la más conveniente depende de
cada programador. En las siguientes secciones se presenta cada uno de ellos y así podrás decidir cuál
prefieres.
2.2.1. Pseudocódigo
Problema 2.1: En la figura 2.1.a. se muestra el estado inicial de un tablero, el cual contiene en la primer
casilla (de izquierda a derecha) una bellota, representada por un asterisco (*), y a la ardilla,
representada por una flecha que apunta hacia la dirección que está mirando. El problema consiste en
diseñar un algoritmo que la ardilla pueda ejecutar para llegar al estado meta representado en la figura
2.1.b., que implica que la ardilla lleve la bellota a la última casilla. Para resolverlo se tiene la siguiente
información:
a) El mundo es conocido, es decir, se sabe de antemano 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.
c) En la primera casilla hay una bellota.
* *
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 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 2.1. Primer mundo de la ardilla
Los diagramas de flujo son una representación gráfica de un algoritmo que utiliza símbolos para
representar las instrucciones y flechas para unirlas e indicar el orden en que deben ejecutarse -llamadas
líneas de flujo. 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 siguiente tabla.
Símbolo Descripción
Terminal. Representa el inicio y el final de un algoritmo.
Terminal
Problema 2.2: Ahora la tarea de la ardilla es que cambie las bellotas que están en la primera fila (ver
figura 2.2.a) a la segunda y viceversa, dejándolas en la misma columna (ver figura 2.2.b).
d) En este punto los científicos ya entrenaron a la ardilla para ejecutar la orden giraDerecha(), por
lo tanto, ya puede ser usada en el algoritmo.
* * * * * *
* * * * * *
Análisis: De acuerdo con la figura 2.2, inciso a, 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.
En esta actividad, reflexionarás sobre el uso de los diagramas de flujo y pseudocódigos a partir de
varias preguntas. Posteriormente, ingresarás un comentario al respecto en el Foro Representación de
algoritmos.
Como primera evidencia de aprendizaje para esta unidad deberás elegir un problema que se pueda
solucionar mediante un programa de computadora y realizarás la descripción precisa del mismo.
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, esto tiene el gran inconveniente que cuando se hacía una
modificación en el programa, era necesario 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 diseñarse e
implementar utilizando únicamente tres tipos de estructuras de control: secuenciales, condicionales y
repetitivas; esto es, sin utilizar GOTO”(Böhm & Jacopini, 1966), 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 podían 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,
ésta incluye estructuras secuenciales, selectivas y repetitivas, conocidas como estructuras de control.
Las estructuras secuenciales son un bloque de instrucciones que se ejecutan una tras otra, en el mismo
orden en el que están escritas.
Un ejemplo de este tipo de instrucciones son todas las que se utilizaron en el algoritmo 2.1. Veamos
otro ejemplo.
Problema 2.3: Ahora la ardilla se enfrenta a un nuevo mundo (ver figura 2.3) en el que su tarea consiste
en recoger las dos bellotas colocadas en la posiciones indicadas por la figura 2.3.a y llevarlas a la
última casilla de la primera fila, como se muestra en la figura 2.3.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.
* **
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 llegar a la última
casilla de la prime fila. Otra posible opción es que recoja la primera bellota, la lleve a la primera casilla,
regrese por la segunda bellota y también la lleve a la primera casilla. Esta última opción requiere más
esfuerzo por parte 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()
giraDerecha()
avanza()
avanza()
recogeBellota()
avanza()
dejaBellota()
dejaBellota()
Fin
Las instrucciones selectivas, más usuales, que una computadora es capaz de realizar son: Imprimir,
Leer y Asignar. La representación en diagrama de flujo de estas instrucciones se ilustra en la siguiente
tabla, en cuanto que la representación en diagrama de flujo se utilizan los mismos verbos y símbolos
pero encerrados entre un símbolo de proceso.
En esencia, las estructuras selectivas se utilizan cuando la solución de un problema conlleva tomar una
decisión, ya que se ejecuta un conjunto determinado de instrucciones dependiendo de si se cumple o no
una condición en un momento determinado. Por ejemplo, la ardilla solamente puede avanzar si se no
hay pared, en este caso la condición es no hayPared() y la acción que se realiza es avanza().
Revisemos el siguiente ejemplo:
Problema 2.4: Nuevamente la ardilla está en el mundo lineal que se ilustra en la figura 2.4.a, tiene que
recoger una bellota y llevarla a la última casilla como se muestra en la figura 2.4.b, sólo que ahora no
sabe con precisión en que casilla está la bellota y la única información con la que cuenta es la siguiente:
a) En el tablero hay una sola bellota. Las casillas donde puede estar son la tercera o la quinta, lo
cual se representa con un círculo en la figura 2.4.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 esta no contiene
nada pues la ardilla no sabrá que hacer.
d) La ardilla ya ha sido entrenada para decir si hay bellota.
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. Ya que 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 lo 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 éste 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
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
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
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.
Por otro lado, observa que en los algoritmos 2.4 y 2.5 se definieron instrucciones para el caso que se
cumple la condición (Si) y para el caso que no (Sino); en cambio, en este último algoritmo sólo se ha
definido un conjunto de instrucciones que se ejecuta si la condición se cumple, de no ser así no hay
instrucciones específicas y la ardilla continuará realizando las siguientes instrucciones del algoritmo. Es
importante destacar que ambas estructuras son equivalentes, es decir, que los problemas que se
solucionan con una también es posible hacerlo con la otra.
Existen tres tipos de estructuras selectivas que se clasifican de acuerdo al número de alternativas:
a) Simples (Si): involucra un único bloque de instrucciones que se ejecuta sólo si una condición se
cumple.
b) Dobles (Si-Sino): abarca dos bloques de instrucciones, uno se ejecuta cuando la condición es
verdadera y el otro cuando es falsa.
c) Múltiples (Seleccionar): tiene 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.
Si la condición es
Estructuras Si <condición> entonces
verdadera, se ejecuta el
selectivas <instrucciones>
conjunto de instrucciones.
simples Fin_Si
Seleccionar <expresión>
caso <valor1>:
Compara el valor de
<instrucciones1>
expresión con cada uno
caso <valor2>:
los valores
Estructuras <Instrucciones2>
correspondientes a cada
selectivas .
caso, es decir cada valor
múltiples .
K y sólo si son iguales
.
realiza las instrucciones
otro:
correspondientes.
<instruccionesOtro>
Fin_Seleccionar
Tabla 2.4 Estructuras selectivas
Diseña un algoritmo, en pseudocódigo o diagrama de flujo, usando estructuras selectivas para resolver
el problema que te proporcionará tu facilitador(a).
Las estructuras repetitivas, también llamadas ciclos, permiten ejecutar varias veces un bloque de
instrucciones en función de una condición. Para ilustrar esto, volvamos al problema 2.1 del subtema
2.2.1; en este mundo la ardilla debe llevar una bellota desde la primera casilla hasta la última en un
mundo lineal (ver figura 2.1). Observa que una vez que la ardilla recoge la bellota y está viendo de
frente, debe avanzar una y otra vez mientras no se tope con la pared, esto se puede modelar con un
ciclo de la siguiente manera.
Inicio
recogeBellota()
giraIzquierda()
giraIzquierda()
giraIzquierda()
Mientras no (hayPared()) hacer avanza()
Fin Mientras
dejaBellota()
Fin
Generalmente, un ciclo se utiliza cuando descubrimos un patrón, tal como se hizo en el análisis del
problema 2.2. Si observas el algoritmo 2.2, verás que al final hay una flecha que regresa a la primera
instrucción, representado con ello un ciclo. La presentación en pseudocódigo de este algoritmo sería la
siguiente:
Inicio
Mientras no(hayPared()) hacer
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
La clave para utilizar un ciclo es identificar el conjunto de instrucciones que se deben repetir y la
condición para que se ejecuten. 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. Éstas son:
a) Mientras-hacer: en este ciclo primero se verifica que la condición sea verdadera y en tal caso se
ejecuta el bloque de instrucciones y se repite nuevamente el ciclo.
b) Hacer-Mientras: en esta estructura primero se realizan las instrucciones y después se verifica la
condición, si se cumple se repite el ciclo.
c) Desde-mientras: funciona igual que Mientras pero tiene asociada una variable que sirve como
contador para controlar el número de veces que se repite un ciclo, de tal manera que la
condición involucra al contador.
Desde
<inicialización>mientras Inicializa el valor del
<condición>, contador, verifica si la
<incremento/decremento condición se cumple y en
Ciclo Desde-
> tal caso ejecuta las
mientras
instrucciones,
(for)
<instrucciones> posteriormente incrementa
o decrementa la variable
Fin_Desde contador.
Diseña un algoritmo, en pseudocódigo o diagrama de flujo, que resuelva el problema que te presentará
tu facilitador(a) utilizando ciclos.
Actividad de Autoevaluación
Como evidencias de la unidad se proponen dos problemas en el mundo de la ardilla, el primer algoritmo
tendrás que realizarlo utilizando pseudocódigo en tanto que el segundo tendrás que representarlo en
diagrama de flujo. Además de las instrucciones que aparecen en la tabla 2.1 puedes utilizar la
instrucción giraDerecha() que hace que la ardilla de vuelta a la derecha.
Referencias:
Böhm, C., & Jacopini, G. (1966). Flow diagrams, Turing machines, and languages only with two
formation rules". Communications of the ACM, 9 (5), 366-371.
Reyes, A., & Cruz, D. (2009). Notas de clase: Introducción a la programación. México, D.F.:
UACM.
Viso, E., & Pelaez, C. (2007). Introducción a las ciencias de la computación con Java. México,
D.F.: La prensas de ciencias, Facultad de Ciencias, UNAM.
Propósitos
En esta unidad:
Competencia específica
Introducción
En la unidad anterior se presentó un conjunto de instrucciones que una ardilla (ficticia) podría realizar y
se mostró cómo, a través de éstas, la ardilla podía resolver varios problemas siempre y cuando se
definiera un algoritmo. Ahora veamos qué relación hay entre el mundo de la ardilla y las computadoras:
la ardilla representa la computadora, que sólo puede ejecutar un conjunto de instrucciones definidas;
además, al igual que la ardilla, la computadora por sí misma es incapaz de resolver ningún problema,
para hacerlo necesita un programa que pueda seguir (ejecutar) para obtener el resultado deseado, así
como la ardilla necesitaba un algoritmo que le indicara cómo realizar una tarea específica.
Por lo anterior, en esta unidad se mostrará la forma en que se crean programas en el lenguaje de
programación C, desarrollado por Dennis M. Ritche en el año de 1972, en los laboratorios Bell de la
AT&T, y que posteriormente fue estandarizado por un comité del ANSI (por sus siglas en inglés
American National Standard Institute) dando lugar al ANSI C, a través del cual se garantiza que
cualquier programa creado bajo este estándar pueda ser ejecutado en cualquier computadora
(Kernighan & Ritchie, 1991).
A lo largo de la unidad se introducen los elementos básicos del lenguaje de programación C: tipos de
datos, variables y constantes, expresiones aritméticas, funciones matemáticas y funciones de entrada y
salida, con el fin de empezar a crear programas secuenciales que resuelvan problemas simples. Como
primer tema analizaremos la estructura del programa “hola mundo”, pues como ya es costumbre en
programación es el primer programa que se realiza cuando se aprende un nuevo lenguaje de
programación. A partir de este ejemplo se revisa la estructura de un programa y los elementos que lo
integran.
En general, “un programa es una secuencia de instrucciones mediante las cuales se ejecutan diferentes
acciones de acuerdo con los datos que se estén procesando (López, 2005)”. En la unidad 1 se explicó
que un programa sólo puede ser ejecutado por una computadora solamente si está escrito en lenguaje
de máquina, pero existen lenguajes de programación, que son fáciles de entender para los seres
humanos, mediante los cuales se pueden escribir programas más legibles conocidos como programas
fuentes –en C los programas fuente tiene extensión .c–, que son traducidos a lenguaje de máquina
mediante compiladores o traductores. En el caso de C es un lenguaje compilado, por lo que se genera
un programa ejecutable con extensión .exe, que puede ser ejecutado cada vez que se desee sin
necesidad de volver a compilar el programa fuente, a menos que se realice algún cambio.
De acuerdo con su creador un programa fuente en C, cualquiera que sea su tamaño, consta de
funciones y variables. Una función contiene un conjunto de instrucciones, también llamadas
preposiciones o enunciados, que especifican las operaciones que la computadora debe realizar; en
tanto, las variables son los espacios de memoria donde se almacenan los valores utilizados en dichas
operaciones(Kernighan & Ritchie, 1991, pág. 6).
Como se ha mencionado, el primer programa que se debe escribir cuando se aprende un nuevo
lenguaje es “hola mundo”, así que para no perder la tradición a continuación se muestra cómo se
implementa este programa en lenguaje C.
/*Directivas de preprocesador*/
#include<stdio.h>
El programa hola.c solo tiene una función: main; generalmente se puede dar cualquier nombre a las
funciones que se definan en un programa, sin embargo, main es una función especial que siempre
debe aparecer en algún lugar del programa, ya que es el punto desde el cual se inicia la ejecución
(equivale a la instrucción de inicio de un algoritmo). Los paréntesis que aparecen después de la palabra
main indican que se trata de una función; las instrucciones que forman parte de ésta, llamadas cuerpo
de la función, van encerradas entre llaves “{ }”, señalando el inicio y fin de la misma, respectivamente.
Las instrucciones que comienzan con “/*” y terminan con “*/”, se llaman comentarios e indican que
todo lo que está escrito entre esos símbolos no son instrucciones que la computadora debe ejecutar
sino información de interés para el programador; por ejemplo la primera línea de código:
/*Directivas de preprocesador*/
Otro tipo de instrucciones especiales son las directivas del preprocesador, que son instrucciones que se
realizan antes de la compilación del programa, un ejemplo es:
#include<stdio.h>
Se distinguen porque inician con el carácter gato “#”, en este caso esta instrucción le indica al
compilador que debe incluir la información que está definida en el archivo de biblioteca stdio.h, en el
cual se encuentran todas las funciones de salida y entrada, como printf.
Los compiladores de lenguaje C ofrecen distintas directivas, sin embargo las que utilizaremos son:
En el cuerpo de la función main, del programa 3.1, sólo aparece una instrucción que es la invocación a
la función printf con el argumento “Hola mundo… \n”.
Invocamos o llamamos una función cuando requerimos que se ejecute con un conjunto de datos
específicos, a los cuales llamamos argumentos. Una función se invoca o llama al nombrarla, es decir,
escribiendo su nombre seguido de la lista de argumentos, separados por comas y encerrados entre
paréntesis. En otras palabras, los argumentos son los valores que una función necesita para realizar la
tarea que tiene encomendada, por ejemplo, la función printf tiene como fin imprimir la cadena de
caracteres que recibe como parámetro, en este caso particular imprimirá la frase “Hola mundo…”
seguida de un salto de línea, que es lo que representa la secuencia de caracteres “\n”.
Como sabes, la intención de esta asignatura es que aprendas a diseñar programas en C que te sean de
utilidad para resolver problemas por medio de la computadora. Para lograrlo, es necesario que
perseveres y practiques constantemente hasta habituarte en el manejo de los elementos de C; con ese
propósito, durante toda esta Unidad y las que siguen, te presentaremos varios ejercicios que deberás
realizar como complemento de las demás Actividades y Evidencias de Aprendizaje. Para conocer los
detalles ingresa al aula virtual.
El primer ejercicio consiste en la elaboración del programa hola_mundo.c. Para conocer más detalles
ingresa al aula virtual.
3.1.1. Instrucciones
Una instrucción o enunciado en lenguaje C se puede definir como una expresión que tiene alguna
consecuencia, generalmente la consecuencia se ve reflejada en el cambio del valor que está
almacenado en las variables. De acuerdo con su estructura sintáctica se pueden clasificar en dos tipos:
simples y compuestas.
Las instrucciones simples se distinguen porque terminan con punto y como “;”.
La sintaxis es:
<instrucción>;
int x;
x = 2*y;
printf(“Hola”);
En cambio las instrucciones compuestas son un conjunto de instrucciones que se escriben entre llaves
“{…}”, para formar, lo que conocemos como, un bloque de instrucciones.
La sintaxis es
{
<instrucción>;
<instrucción>;
…
<instrucción>;
}
Un ejemplo de este tipo de instrucciones es el cuerpo de la función main, del programa 3.1.
3.1.2. Comentarios
Los comentarios son textos que sirven como información al programador y no son procesados por el
compilador, es decir, no son instrucciones que debe realizar la computadora y por lo tanto no son
traducidos a lenguaje de máquina. Para que un texto sea comentario debe estar entre los símbolos /*
(marca el comienzo) y*/ (marca el final de comentario).Pueden ir en cualquier parte del programa.
Un buen programador debe comentar sus programas para que otras personas puedan entender la
lógica del programa, e incluso, los comentarios también le sirven al programador cuando en un tiempo
futuro requiera realizar cambios. También es recomendable incluir al inicio del programa: el nombre del
programa, el nombre del programador, una breve descripción de la tarea que realiza, las fechas de
creación y de la última modificación, todo esto encerrado entre comentarios. Por ejemplo, al inicio del
programa 3.1 sería conveniente incluir el siguiente comentario:
/*
Programa: hola.c
Programador: Pedro Pérez
Descripción: El programa imprime la cadena “hola mundo”
Fecha de creación: Agosto, 2010
Última modificación: Septiembre, 2010
*/
Los comentarios también se pueden incluir al lado de una instrucción para describir de qué se trata, por
ejemplo:
Las palabras reservadas de cualquier lenguaje de programación, se llaman así porque tienen un
significado especial para el compilador, el lenguaje C tiene 32 palabras reservadas (27 fueron definidas
en la versión original y cinco añadidas por el comité del ANSI: enum, const, signed, void y
volatile), todas ellas escritas con minúsculas.6
666 En este punto es importante destacar que el lenguaje C distingue entre mayúsculas y minúsculas.
Una vez que se han descrito los diferentes elementos que integran un programa de lenguaje C, en la
siguiente sección se describe la estructura general de un programa en C.
Y como se ha mencionado, se pueden incluir comentarios en cualquier parte del código. A continuación
se presenta un programa que calcula el área de una circunferencia dada la medida de su radio.
/* Declaraciones globales */
const float PI = 3.1416;
/* Función Principal */
main( )
{
/* Declaraciones de variables locales la función main*/
float radio, area;
Por el momento, basta con que observes la estructura del programa, que se hace evidente con los
comentarios, el significado de cada una de las líneas de código se irá definiendo en las siguientes
secciones. Para cerrar esta sección en la siguiente figura se muestra la ejecución del programa con la
entrada radio=3.
En esta ocasión, lo que tienes que hacer es escribir y compilar el programa en la computadora
utilizando DevC++, posteriormente ejecútalo con diferentes valores para el radio e ingresa un
comentario al respecto al Foro Introducción al lenguaje C.
En la Unidad 1 se mencionó que todos los datos que son procesados por una computadora se
almacenan en la memoria principal y, no importa de qué tipo sean, se representan por medio de unos y
ceros, sin embargo, no todos se pueden representar con el mismo número de bits7, esto sí depende del
tipo que se trate. Por ejemplo, los caracteres como: „a‟, „@‟,‟Z‟, entre otros, se representan utilizando 8
bits, en cambio para representar un número decimal se utilizan, al menos, 32 bits; por lo que cuando se
crea un espacio de memoria es necesario indicar qué tipo de dato se desea almacenar ahí, para que se
7 Recuerda que un bit se definió como la unidad mínima de información, esto es, 1 ó 0.
reserve la cantidad de celdas de memoria que se necesitan y las operaciones que se pueden realizar
con ellos.
Enteros
Numéricos
Decimales
Lógicos (verdadero o falso)
Simples
Alfanuméricos (caracteres)
Arreglos
Unidimensionales
Estructurados Multidimensionales
Estructuras o registros
Tabla 3.2: Tipos de datos
Por el momento, sólo nos enfocaremos en los tipos de datos simples definidos en el estándar de C, en
la Unidad 5 se estudiarán los datos estructurados. En la siguiente tabla se muestran los tipos de datos
simples en C:
Una vez que conocemos lo tipos de datos que se pueden representar en lenguaje C, es conveniente
saber cómo se reservan espacios de memoria donde son almacenados, esto es justo lo que se explica
en el siguiente tema.
Las variables y las constantes en lenguaje C se utilizan para almacenar valores, la diferencia que existe
entre ellas es que el valor almacenado en una variable puede ser modificado en cualquier instante del
programa en tanto que las constantes no pueden modificarse.
Formalmente, una variable es un espacio de memoria que tiene asignado un nombre (también llamado
identificador) y se utiliza para almacenar un valor que puede ser modificado durante la ejecución de un
programa, a este valor que se encuentra almacenado en una variable en un momento dado se le llama
estado de la variable. Por lo contrario, una constante es un dato cuyo valor se establecen en tiempo de
compilación y no pueden cambiar durante la ejecución del programa. Existen dos tipos de constantes:
literales y simbólicas. Las segundas, al igual que las variables, también tienen asignado un nombre.
A lo largo de esta sección descubrirás cómo puedes construir variables y constantes en lenguaje C, así
que lo primero será listar las reglas que debes seguir para nombrarlas.
3.3.1. Identificadores
En lenguaje C hay una serie de restricciones en cuanto a los nombres o identificadores, ya sea de
variables, constantes o funciones. Éstas son:
a) Los identificadores se integran por letras y dígitos pero es necesario que el nombre siempre
comience con una letra, por ejemplo: enteroA, arco3, S184.
b) No puede contener caracteres especiales, por ejemplo, acentos (á,í), la letra eñe (Ñ), gato (#),
guión (-). El carácter de subrayado “_”es el único carácter especial que puede utilizarse,
generalmente se usa para darle una mejor legibilidad al nombre de una variable. Por ejemplo:
entero_A, area_Circulo, i_elemento.
c) El lenguaje C distingue entre letras mayúsculas y minúsculas, esto significa que los siguientes
identificadores son distintos: area, Area, AREA.
d) No pueden contener espacios en blanco.
e) No pueden ser palabras reservadas.
Aunque no es una regla, se recomienda que los identificadores sean significativos, es decir, que el
nombre indique qué dato se está almacenando ahí.
Ejemplo 3.1:
2) Se requiere un identificador para una variable que almacene el promedio de ventas anuales de
una tienda departamental.
Opciones sugeridas: promedioVentas, prom_Ventas, promAnual.
Una vez definidas las reglas de cómo escribir los nombres de las variables y los tipos básicos que
pueden tener, estás listo para descubrir cómo se crean las variables en lenguaje C. A esta acción se le
denomina declaración de variables.
<tipo><identificador>;
Donde <tipo> se refiere un tipo básico de C y el <identificador> se refiere al nombre con el cual
se identificará el espacio de memoria reservado, puede ser cualquier nombre siempre y cuando se
respeten las reglas vistas previamente.Veamos los siguientes ejemplos:
También es posible declarar una lista de variables de un mismo tipo separando los nombres con comas
“,”
<tipo><identificador1>, … , <identificadorN>;
Podemos ver un ejemplo de este tipo de declaración en el programa que calcula el área de una
circunferencia (programa 3.2), cuando declaramos las variables locales a la función main
Siempre es posible asignarles un valor inicial a las variables cuando las declaramos, a esta operación
se conoce como inicialización. La sintaxis es:
<tipo><identificador>=<valor>;
asignada a una variable también se escribe su identificador, en contenido se muestra el dato que está
almacenado en dicha dirección. El estado de una variable (memoria) hace referencia al valor que hay
almacenado en un momento determinado.
MEMORIA
Las constantes son expresiones con un significado invariable, en un programa puede haber dos tipos de
constantes: literales y simbólicas.
Las constantes literales son valores de un determinado tipo escritos directamente en un programa y
pueden ser de los siguientes tipos:
a) Constantes numéricas, que son números representados en sistema decimal, algunas veces se
estila escribir una letra que indique el tamaño o tipo de la constante numérica:
Enteros: Por ejemplo 123, 2006, -38…
Enteros Largos: Se consideran números enteros que superan un entero (int) generalmente
valores mayores a 32,000; se le agrega los sufijos l ó L por ejemplo: 123456789L.
Enteros sin Signo: Aquí se define que el valor de la constante se va a considerar como un
número positivo o mayor a cero, en estos casos se agrega el sufijo U o u así por ejemplo
podemos escribir: 123456789LU.
Flotantes: Los valores numéricos que impliquen precisión decimal se pueden escribir de dos
formas, la primera sería: 14324.983 o 3.1416. También se puede representar en representación
exponencial (e-n), por ejemplo: 1.1434E2 o 1.1432e-5. También se ocupan los sufijos f o F y l
o L para especificar los tipos double.
Como puedes observar, las constantes reales con representación exponencial, tienen como
valor una parte entera o real en forma decimal, seguida por la letra E, seguida de una constante
entera, que se interpreta como exponente de la base 10.
También existen constantes carácter que aparentan ser compuestas pero sólo son
representaciones de caracteres de formato o caracteres especiales, y en C se les conoce como
secuencias de escape.
c) Constantes cadena, que son una secuencia de caracteres entre comillas, incluyendo secuencias
de escape, por ejemplo: “hola”, “hola mundo \n”.
Por otro lado, las constantes simbólicas representan datos permanentes que nunca cambian.
Ahondaremos en este tema en la siguiente sección.
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, por lo que no podemos decir que se declara, sólo se define. La sintaxis general es la
siguiente:
#define<identificador><valor_constante>
Por ejemplo:
#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 almacena el valor 3.1416 en ningún espacio de memoria
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 que tenga la
restricción de sólo lectura, para impedir que el valor sea cambiado, en este caso si la declaramos. La
sintaxis general es similar a la forma de declarar una variable sólo que se antepone al tipo la palabra
reservada const y es obligatorio asignar el valor.
const<tipo><identificador> = <valor_constante>;
Por ejemplo:
La directiva #define debe ser al principio del programa antes del main, en cambio, la declaración de
una constante mediante const puede ser dentro o fuera de las funciones al igual que las
declaraciones de variables.
Se recomienda escribir el nombre de una constante con letras mayúsculas para diferenciarlas de las
variables, pero las reglas son exactamente las mismas que para los identificadores de las variables.
En la unidad anterior describiste el problema que resolverás a lo largo del curso, como evidencia de
esta unidad debes realizar el análisis del problema, definiendo claramente los datos de entrada y la
salida del programa junto con su tipo de datos, además debes considerar que los identificadores
respeten las reglas de C.
Una expresión matemática puede ser un número, una variable, una constante o la combinación de
operadores y todas las anteriores. Toda expresión al ser evaluada produce un valor. Se dividen en dos
tipos de acuerdo al tipo de datos que devuelven cuando son evaluadas: expresiones aritméticas cuando
el resultado de la evaluación es un número y expresiones booleanas cuando el resultado de la
evaluación es un valor booleano (verdadero o falso). En este punto es importante destacar que el modo
en que el lenguaje C maneja los valores booleanos es por medio de valores enteros: cero equivale a
falso y cualquier entero distinto de cero representa verdadero.
Las expresiones matemáticas permiten modelar situaciones reales, por ejemplo, mediante las
expresiones aritméticas podemos modelar la forma de calcular el área de cualquier figura, también
podemos representar la forma de calcular las raíces de un polinomio de segundo grado, o calcular el
monto de una venta, etc. En cambio las expresiones booleanas son la base para construir programas
que pueden tomar decisiones. Veamos los siguientes ejemplos:
Ejemplo 3.2:
Expresión aritmética: ← 2+ 2
Codificación en C: c =sqrt(a*a + b*b);
b) ¿x es un número par?
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, que devuelve el residuo de
una división.
Expresión booleana: � 2 = 0
Codificación en C: x % 2 == 0
En el siguiente subtema se presentan los operadores básicos del lenguaje C, tanto aritméticos como
booleanos.
Los operadores son palabras o símbolos que hacen que permiten realizar operaciones con los datos de
un programa, para cada tipo de datos hay una serie de operadores definidos.
Entre todos los operadores se distingue el operador de asignación “=”, que se puede leer como “guarda
un valor en la variable indicada”, el valor puede ser una constante literal o el resultado de una expresión.
Cabe señalar que este operador en pseudocódigo o diagrama de flujo lo hemos representado con una
flecha apuntado hacia la izquierda ←.
Los operadores aritméticos definidos en C son: “+” (suma), “-” (resta), “*” (multiplicación),“/” (división) y
“%”(módulo). Este último representa el residuo de dividir dos números enteros, por ejemplo si realizamos
En C también existen los operadores de incremento “++” y decremento “--“, éstos tienen el efecto de
aumentar o decrementar en una unidad el valor de una variable, supongamos que estamos trabajando
con la variable x:
x++; → 6
x--; → 4
Observa que el operador de igualdad se escribe con dos símbolos de igualdad seguidos (==). El error
más común es escribir una comparación con un sólo símbolo de igualdad, recuerda que (=) es el
operador de asignación y su significado es totalmente distinto. En cuanto a los operadores booleanos,
su significado es el siguiente:
Conjunción“&&”, es un operador binario que se evalúa como verdadero sólo cuando las dos
expresiones involucradas son verdaderas, en caso contrario devuelve falso. Por ejemplo, si
evaluamos las siguientes expresiones en el estado x=2, y=3, z=5.
Disyunción“||”, también es un operador binario que devuelve únicamente devuelve falso si los
dos operadores son falsos, en caso contrario devuelve verdadero. Nuevamente, tomemos el
mismo estado de las variables x=2, y=3, z=5.
En C existen otro tipo de operadores, sin embargo, su estudio supera los objetivos de este curso por lo
que no los revisaremos, si deseas saber más puedes consultar (Joyanes & Zohanero, 2005).
La evaluación de las expresiones depende de tres cosas, principalmente, el estado de las variables que
aparecen en la expresión, el significado de los operadores y su precedencia. Esta última se refiere a la
prioridad de los operadores, es decir, el orden en el que se evalúan, eliminando con esto la ambigüedad
de las expresiones, por ejemplo, si tenemos la expresión:
2+3*5
Podríamos evaluarla de dos diferentes formas: La primera 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. Otra manera sería
realizar primero la multiplicación 3*5 (=15) y sumar el resultado a 2, obteniendo 17 como resultado final.
Pero sabemos que en matemáticas primero se realiza la multiplicación y después la suma, en otras
palabras, tiene mayor prioridad la multiplicación que la suma. Por lo tanto, el resultado correcto de la
expresión 2 + 3 * 5 es 17. En la siguiente tabla se muestra la precedencia de operadores de lenguaje
C que se han presentado.
Operadores Prioridad
( ) Mayor
-(unario), ++, --
*, /, %
+, -
<, >, >= , <=
==, !=
&&, ||
= Menor
Tabla 3.6: Prioridad de operadores
Ejemplo 3.3: Dada la siguiente expresión matemática para convertir grados centígrados (C) a su
equivalente Fahrenheit (F),
9
� = + 32
5
Su codificación en C es:
F= (9.0/5.0)*C +32;
Se escribe 9.0 y 5.0 para que la división devuelva un número flotante, de lo contrario la división será
entera. En este caso las variables F y C, deben ser declaradas como float.
Observa que el único operador que cambia el estado de una variable es el de asignación “=”.
(x % 2 == 0)
Para esta actividad deberás ingresar al aula a resolver un cuestionario de opción múltiple que te
ayudará a identificar qué tanto dominio tienes sobre los temas que hasta ahora has estudiado.
Las funciones que más se utilizan están agrupadas en bibliotecas estándar, declaradas como archivos
de cabecera, de tal manera que para utilizarlas se debe incluir en el archivo utilizando la directiva
#include seguida del nombre del archivo encerrado entre “<>”
8Si deseas más información se te recomienda consultar (Joyanes & Zohanero, 2005).
Con esta información es posible codificar algoritmos que requieran este tipo de operaciones. Por
ejemplo, la fórmula para calcular el área de una circunferencia, que se aparece en el programa 3.2.
9 En (Kernighan & Ritchie, 1991) puedes consultar las funciones que están definidas en cada una de las bibliotecas estándar.
area = PI * pow(radio,2);
En los programas que hemos visto aparece la función de salida estándar printf, que se encarga de
imprimir un mensaje en la pantalla. La sintaxis general es:
printf(<cadena_de_control>, <lista_de_identificadores>);
Las secuencias de escape se mostraron en la tabla 3.5. Los especificadores de conversión se utilizan
para imprimir valores dentro de la cadena de control especificados por una variable, una constante o
una expresión. En la siguiente tabla se muestran los que más se usan.
Especificador Acción
%d Insertar un entero (int)
%i Insertar un entero tipo (int)
%ld Insertar un entero tipo (long)
Insertar un número flotante
%f
tipo (float)
Insertar un número de tipo
%lf
(double)
%c Insertar un caracter (char)
Observa que se imprime el texto tal cual pero en vez de imprimir el especificador de conversión %d se
imprime el valor de la primera variable que es radio y en el lugar del especificador %f se imprime el
valor del siguiente argumento que es la variable area.
La salida sería:
El perimetro es 18.85
En este caso en el lugar del convertidor %.2f se imprime el resultado de evaluar la expresión
PI*2*radio que es el segundo argumento, el número .2 que aparece en el convertidor indica que
sólo deben imprimirse dos decimales.
En lenguaje C la lectura de datos por medio del teclado se realiza con la función scanf, en la cual se
deben de especificar de ante mano los tipos de datos que se desea recibir, además de los
identificadores de las variables donde se desean almacenar.
scanf(<cadena_de_control>,<lista_de_direcciones_de_variables>);
Donde <cadena_de_control> es una cadena con los códigos que controlarán la forma como se
recibirán los datos desde teclado y la <lista_de_direcciones_de_variables> es una lista con
las localidades de memoria de las variables donde se almacenarán los datos que el usuario del
programa introduzca a través del teclado.
Ejemplo 3.6: Suponiendo que se desea leer la base y la altura de un rectángulo y guardarlas en las
variables de tipo int llamadas basen y altura, de tal manera que el usuario ingrese los valores
separados por una coma, digamos “5,2” entonces la instrucción sería:
Nota: Es frecuente que las personas olviden escribir el & antes del identificador de una variable, al
utilizar la función scanf, cuestión que no es supervisada por el compilador y genera un error en el
momento que se ejecuta el programa.
Esta actividad consiste en diseñar un programa en C para resolver un problema dado, para conocer el
problema y los detalles de la actividad, ingresa al aula virtual.
Para cerrar esta unidad desarrollemos un programa en C que resuelva el siguiente problema:
Análisis: Los datos de entrada son el número de boletos (nboletos) y el monto del pago (pago), la
salida del programa es el monto que se debe pagar por los boletos (total) y el monto del cambio
(cambio), indicando el tipo y número de cada uno de los billetes o monedas que se devolverán.
Notemos que el precio de los boletos siempre es el mismo, así que se puede declarar como una
constante, llamémosla PRECIO. Así que para calcular el monto que el cliente debe pagar tenemos la
siguiente fórmula:
Para calcular cuántos billetes o monedas se tienen que devolver, se utilizarán los operadores de módulo
y división. Por ejemplo, si el cambio es 360 se puede calcular el número de billetes de 100 dividiendo
360 entre 100, lo cual resulta ser 3 que corresponde al número de billetes, el resto del cambio es igual
a 360 módulo 100, en este caso es 60.
Por último, los billetes sólo pueden ser de $500, $200, $100, $50 y $20 y las monedas sólo son de
$10, $5, $2 y $1. Por el momento supondremos que el usuario siempre ingresa datos correctos.
Diseño del algoritmo: En la siguiente tabla se muestra el algoritmo que resuelve el problema.
Constantes:
PRECIO = 45
Variables:
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.
Inicio
Imprimir “ Proporciona el número de boletos”
Leer nboletos
total = nBoletos * PRECIO
Imprimir “Proporciona tu pago”
Leer pago
cambio = pago – total
Imprimir “Tu cambio es”, cambio
Imprimir “El número de billetes de 200 pesos es”,
cambio/200
cambio = cambio módulo 200
Imprimir “El número de billetes de 100 pesos es”,
cambio/100
cambio = cambio módulo 100
Imprimir “El número de billetes de 50 pesos es”,
cambio/50
cambio = cambio módulo 50
Imprimir “El número de monedas de 10 pesos es”,
cambio/10
cambio = cambio módulo 10
Imprimir “El número de monedas de 5 pesos es”,
cambio/5
cambio = cambio módulo 5
Imprimir “El número de monedas de 2 pesos es”,
cambio/2
cambio = cambio módulo 2
Imprimir “El número de monedas de 1 peso es”, cambio
Fin
Algoritmo 3.1: ventaBoletos (pseudocódigo)
Nota: Observa que no es necesario utilizar variables para el número de billetes o monedas de las
diferentes denominaciones, pues sólo se utiliza una vez el resultado del cálculo así que se puede
imprimir directamente el resultado del mismo.
Para verificar que el algoritmo funciona, en la siguiente tabla se realiza una prueba de escritorio
considerando que los datos de entrada son 5 boletos y el monto del pago son 500 pesos. En la primera
columna aparece la instrucción que se ejecuta, en la siguiente el dato que suponemos se ingresa,
después están las operaciones que se realizan en la ALU, en la cuarta columna se muestra los valores
de las variables después de que se ha realizado la instrucción y en la última columna se indica el
mensaje que se imprimirá en la pantalla, cuando sea el caso. Por otro lado, incluimos en la tabla de
estado de la memoria la constante PRECIO sombreando el espacio correspondiente para indicar que no
puede ser modificado.
Dato
Estado de la memoria Dato de
Instrucción de Operaciones
(variables y constantes) salida
entrada
PRECIO nBoletos total pago cambio
Inicio - - -
45 - - - -
Imprimir
Proporciona
“Proporciona el PRECIO nBoletos total pago cambio
- - el número
número de 45 - - - -
de boletos
boletos”.
Leer nBoletos PRECIO nBoletos total pago cambio
5 - -
45 5 - - -
total =
total = 5*45 PRECIO nBoletos total pago cambio
nBoletos*PRECIO - -
=225 45 5 225 - -
Imprimir
PRECIO nBoletos total pago cambio Proporciona
“Proporciona tu - -
45 5 225 - - tu pago
pago”
Leer nBoletos PRECIO nBoletos total pago cambio
500 - -
45 5 225 500 -
cambio =
cambio = pago - PRECIO nBoletos total pago cambio
- 500-225 = -
total 45 5 225 500 275
275
Imprimir “Tu
PRECIO nBoletos total pago Cambio Tu cambio
cambio es”, - -
45 5 225 500 275 es 275
cambio.
Imprimir “El El número
número de PRECIO nBoletos total pago Cambio de billetes
- 275/200 = 1
billetes de $200 45 5 225 500 275 de $200 es
es”, cambio/200 1
cambio =
cambio = cambio PRECIO nBoletos total pago Cambio
- 275 mod 200 -
módulo 200 45 5 225 500 75
=75
Imprimir “El El número
número de PRECIO nBoletos total pago Cambio de billetes
- 75/100 = 0
billetes de $100 45 5 225 500 75 de $100 es
es”, cambio/100 0
cambio =
cambio = cambio PRECIO nBoletos total pago Cambio
- 275 mod 100 -
módulo 100 45 5 225 500 75
=75
Imprimir “El
El número
número de PRECIO nBoletos total pago Cambio
- 75/50= 1 de billetes
billetes de $50 45 5 225 500 75
de $50 es 1
es”, cambio/50
cambio = 75
cambio = cambio PRECIO nBoletos total pago Cambio
- mod 50 -
módulo 50 45 5 225 500 25
=25
Imprimir “El
El número
número de PRECIO nBoletos total pago Cambio
- 25/50= 0 de billetes
billetes de $20 45 5 225 500 25
de $50 es 0
es”, cambio/20
cambio =
cambio = cambio PRECIO nBoletos total pago Cambio
- 25 mod 20 -
módulo 20 45 5 225 500 5
=5
Imprimir “El El número
número de PRECIO nBoletos total pago Cambio de
- 5/10= 0
monedas de $10 45 5 225 500 25 monedas
es”, cambio/10 de $10 es 0
cambio = 5
cambio = cambio PRECIO nBoletos total pago Cambio
- mod 10 -
módulo 10 45 5 225 500 5
=5
Imprimir “El El número
número de PRECIO nBoletos total pago Cambio de
- 5/5= 1
monedas de $5 45 5 225 500 5 monedas
es”, cambio/5 de $5 es 1
cambio = 5
cambio = cambio PRECIO nBoletos total pago Cambio
- mod 5 -
módulo 5 45 5 225 500 0
=0
Imprimir “El El número
número de PRECIO nBoletos total pago Cambio de
- 0/2= 0
monedas de $2 45 5 225 500 0 monedas
es”, cambio/2 de $2 es 0
PRECIO nBoletos total pago Cambio
cambio = 0
cambio = cambio 45 5 225 500 0
- mod 2 -
módulo 2
=0
/* Directivas al procesador */
#include<stdio.h>/* Funciones de entrada y salida */
#include<stdlib.h>/* Funciones del sistema */
/* Función Principal */
main( )
{
/*Declaración de variables y constantes */
constint precio = 45;
intnBoletos, total, pago, cambio;
/*Mensaje de bienvenida*/
printf("********** Venta de boletos CineESAD************\n\n");
/*Datos de entrada*/
printf("Proporcione el numero de boletos que desea comprar:\t");
scanf("%d",&nBoletos);
El resultado de la ejecución del programa utilizando los mismos datos de entrada de la prueba de
escritorio es:
Escribe, compila y ejecuta el programa ventaBoletos.c que acabamos estudiar e ingresa al foro para
exponer tus comentarios al respecto. Consulta el aula para más detalles.
Referencias:
Böhm, C., & Jacopini, G. (1966). Flow diagrams, Turing machines, and languages only with two
formation rules". Communications of the ACM, 9 (5), 366-371.
Reyes, A., & Cruz, D. (2009). Notas de clase: Introducción a la programación. México, D.F.:
UACM.
Propósitos
En esta unidad:
Construirás expresiones booleanas para modelar situaciones reales.
Diseñarás algoritmos para resolver problemas que impliquen la toma de decisiones, utilizando
estructuras selectivas.
Diseñarás algoritmos para resolver problemas que realicen una misma tarea varias veces
usando estructuras repetitivas.
Codificarás en lenguaje C algoritmos estructurados.
Competencia específica
Utilizar estructuras de control selectivas y repetitivas para resolver problemas simples a través del
desarrollo de programas en lenguaje C.
Introducción
En la segunda unidad, mediante el mundo de la ardilla, aprendiste que cualquier algoritmo puede ser
escrito utilizando únicamente tres tipos de instrucciones, conocidas como estructuras de control:
secuenciales (cuando se ejecutan una tras otra), selectivas (cuando se ejecutan dependiendo de una
condición) y repetitivas (que se ejecutan varias veces en función de una condición); su objetivo es
controlar el flujo de ejecución de un programa, es decir, el orden en que se ejecutan las instrucciones.
Considerando que en la unidad anterior diseñaste algoritmos secuenciales y los codificaste en lenguaje
C, para obtener el programa deseado que diera solución al problema en cuestión, podemos decir que
solamente te falta saber cómo funcionan y cómo se codifican en lenguaje C las otras dos estructuras
para poder diseñar algoritmos estructurados. Así que esto es justamente el tema de esta unidad, aquí
estudiarás con más detalle los tipos y funcionamiento de las estructuras selectivas y repetitivas,
introducidas en la unidad 2.
Para su mejor comprensión, esta unidad está dividida en dos partes: en la primera revisarás algunos
problemas donde la solución implica tener que elegir el camino que se debe seguir para llegar al
resultado deseado, los cuales se solucionan utilizando estructuras selectivas, por lo cual se analizarás a
mayor profundidad el significado (semántica) de cada estructura y verás la forma de codificarla (sintaxis)
en lenguaje C. La segunda parte está dedicada a las estructuras repetitivas, para las cuales se sigue la
misma estrategia, verás cómo se pueden solucionar problemas utilizando este tipo de estructuras y
también analizarás su semántica y aprenderás su sintaxis en lenguaje C. De esta manera, al finalizar la
unidad podrás construir programas que incluyan cualquier tipo de estructura de control.
Para diseñar programas capaces de tomar decisiones se requiere de las estructuras de control
selectivas, también llamadas condicionales. Éstas llevan a cabo su función (controlar el flujo del
programa) mediante una condición que se representa utilizando expresiones booleanas, de tal manera
que cuando la condición se cumple (es verdadera) se ejecuta un conjunto de instrucciones definidas
para este caso y, dependiendo del tipo de estructura, es posible que se ejecute otro conjunto de
instrucciones distinto para el caso contrario (cuando la condición es falsa); e incluso, es posible definir
diferentes conjuntos de instrucciones para valores distintos que pudiera tomar una variable. Es así que
dependiendo de su estructura se han definido tres tipos: simples, dobles y múltiples.
Para el estudio de cada estructura selectiva, a continuación, se dedican tres sub-secciones, una para
cada una, en las cuales entenderás cómo funcionan y la forman en que se codifican en lenguaje C.
La estructura de decisión simple, como su nombre lo indica, permite decidir entre ejecutar o no un
bloque de acciones; en pseudocódigo se propuso la palabra reservada Si para su representación y en
lenguaje C esta estructura se codifica mediante la sentencia de control if, tal como se muestra en la
siguiente tabla.
if(<condición>)
Si <condición>
entonces <instrucciones>
<instrucciones>
Fin Si
La <condición> puede ser cualquier expresión booleana y las <instrucciones>, llamadas cuerpo del Si
(if), bien puede ser una sola instrucción o un bloque de instrucciones en cuyo caso van entre llaves
{}.
La manera en la que se ejecuta una instrucción Si (if) es la siguiente: se evalúa la condición que
aparece entre paréntesis y si es verdadera (tiene un valor diferente de cero) entonces se ejecutan las
instrucciones del cuerpo del Si (if), en caso de no serlo no se ejecuta y continúa el flujo de ejecución.
NOTA: En lenguaje C, cuando el cuerpo de una estructura tiene más de una instrucción éstas deben ir
encerradas entre llaves.
Problema 4.1: Se requiere un programa que lea un valor entre 0 y 360 y determine el tipo de ángulo,
considerando que:
Salida: Método:
Mensaje 1: “Agudo” Realizar comparaciones
Datos de entada:
Mensaje 2: “Recto” utilizando la estructura de
ángulo
Mensaje 3: “Obtuso” selección simple para
Mensaje 4: “Llano” determinar el tipo de ángulo,
Mensaje 5: “Cóncavo” se requiere una por cada tipo
Lo primero que se requiere es leer el valor del ángulo, posteriormente, verificar de qué tipo es para
imprimir el mensaje indicado. A continuación se muestra el algoritmo, tanto en pseudocódigo como en
diagrama de flujo:
Inicio
Imprimir "Ingrese la medida del angulo (grados): "
Leer angulo
Si angulo=90 entonces
Imprimir "El ángulo es recto"
Fin_Si
Fin
Algoritmo 4.1.a: Tipo de ángulo - pseudocódigo
Observa que, para hacer más legible el algoritmo en pseudocódigo, se han dejado sangrías para indicar
qué instrucciones forman el cuerpo de cada una de las estructuras Si y se han encerrado con un
rectángulo, esto se adoptará para cualquier bloque de instrucciones que corresponda al cuerpo de una
estructura.
Al ejecutar paso a paso el algoritmo la única condición que satisface el estado de la memoria es la que
sombreamos (angulo>90 AND angulo<180), por lo tanto, la única instrucción que se toma en cuenta es
la del cuerpo del Si correspondiente.
El último paso es la codificación. Observa que el cuerpo de cada una de las estructuras consta de una
instrucción por lo tanto no es necesario encerrarla entre llaves {}.
/* Programa: tipoAngulo.c
Descripción: Determina el tipo de angulo (agudo, recto, obtuso, llano
o cóncavo)
*/
#include<stdio.h>
#include<stdlib.h>
/* Función Principal*/
main ()
{ /*Inicio de la función Principal*/
/*Declaración de variables */
intangulo;
/*Mensaje de bienvenida*/
printf ("\nEste programa determina de que tipo es el angulo dado.");
/*Instrucciones */
printf ("\n\nIngrese la medida del angulo (grados): ");
scanf ("%d",&angulo);
if (angulo<=0 || angulo>=360)
printf ("\n No tiene clasificación");
if (angulo>0 &&angulo<90)
printf ("\n El angulo es agudo");
if (angulo==90)
printf ("\n El angulo es recto");
if (angulo>90 &&angulo<180)
printf ("\nElangulo es obtuso");
if (angulo ==180)
printf ("\n El angulo es llano");
if (angulo>180 &&angulo<360)
printf ("\nElangulo es concavo");
printf ("\n\n\t");
system ("pause");
Al igual que en la Unidad anterior, en esta ocasión, contarás con un foro para publicar y comentar tus
actividades, las cuales te servirán de práctica para mejorar en el desarrollo de programas en C.
Recuerda que estas actividades no necesitas enviarlas al (a la) facilitador(a), pero sí debes subirlas al
Foro y comentar los aportes de tus compañeros(as).
Para iniciar el trabajo en el foro realiza una prueba de escritorio del algoritmo que resuelve el problema
4.1 presentado en esta sección (algoritmo 4.1.a y algoritmo 4.1.b) considerando que el valor del ángulo
es 98 grados. Posteriormente escribe, compila y ejecuta el programa tipoAngulo.c (programa 4.1) en la
computadora con el mismo valor del ángulo. Finalmente, ingresa un comentario respecto a los
resultados obtenidos.
Las estructuras selectivas dobles nos permiten elegir alguna de dos posibles acciones a realizar
dependiendo de la condición. En pseudocódigo se propone usar las palabras reservadas Si-Sino y en C
se codifican mediante la sentencia if-else, tal como se muestra en la siguiente tabla.
Si (<condición>)
if(<condición>)
entonces
<instruccionesV>
<instruccionesV> else
sino
<instruccionesF>
<instruccionesF>
Fin Si-Sino
Al igual que en la estructura selectiva simple, la <condición> representa una expresión booleana y, las
<instruccionesV> y <instruccionesF> puede ser una o varias, a las primeras se les llama cuerpo del Si
(if) y las segundas son el cuerpo del Sino (else).
Esta estructura de control ejecuta sólo uno de los dos cuerpos de instrucciones: cuando la condición es
verdadera se ejecutan las <instrucciuonesV> y en caso contrario se ejecutan las <instruccionesF>.
Problema 4.2: Se requiere un programa que simule el cálculo y muestre las raíces reales para una
ecuación de segundo grado de la forma:
� 2 + � + = 0.
− ± 2 −4
�=
2
Por lo que los datos que requerimos son los coeficientes de la ecuación (a, b y c) y el resultado deseado
serán las raíces. También se debe considerar que un polinomio tenga raíces reales se debe cumplir la
condición:
2
4
De lo contrario el resultado de la raíz cuadrada sería un número imaginario. Para esto se propone una
estructura selectiva doble para verificar que existan raíces reales.
Inicio
Imprimir “Ingresa los coeficientes a, b y c:”
Leer a, b, c
det← 2 − 4
Si (det 0 ) entonces
x1←
x2 ←
Imprimir “Las raíces son: ”, x1 , x2
Si no
Imprimir “No existen raíces reales”
Fin_Si-Sino
Fin
Algoritmo 4.2.a: Ecuación de segundo grado - pseudocódigo
Observa que en el algoritmo se utiliza la variable auxiliar det, la razón es porque el cálculo del
determinante ( 2 − 4 ) se emplea en más de un lugar del algoritmo, por lo tanto, se recomienda
guardar este resultado para no volverlo a calcular.
Para la codificación en lenguaje C se debe notar que el cuerpo del Si (if) tiene un bloque de tres
instrucciones, por lo que deberán ir encerradas entre llaves {}, en cambio el cuerpo del Sino (else)
sólo tiene una instrucción por lo que no son necesarias.
/* Programa: ecuacion.c
Descripción: Solución de ecuaciones de segundo grado utilizando
*/
/* 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");
/* Datos de entrada */
printf ("\nIntroduzca los coeficientes de a,b y c:");
scanf ("%lf,%lf,%lf",&a,&b,&c);
det = pow (b,2)-4*a*c;
La ejecución del programa con los mismos datos de entrada de la prueba de escritorio es la siguiente.
Realiza una prueba de escritorio del algoritmo que resuelve el problema 4.2 considerando la ecuación
2� 2 + 5� + 3 = 0. Enseguida escribe y compila el programa ecuacion.c (programa 4.2) en la
computadora y ejecútalo con la misma ecuación para verificar los resultados. Ingresa un comentario
respecto a los resultados obtenidos en el Foro Estructuras de control y discute si es posible resolver
este problema utilizando estructuras selectivas simples.
Las estructuras selectivas múltiples permiten escoger uno de varios caminos posibles. Para la
estructura condicional múltiple se proponen las palabras clave Seleccionar-caso en pseudocódigo,
misma que se implementa en lenguaje C utilizando las palabras reservadas switch-case. Esta
secuencia 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.
Pseudocódigo Lenguaje
switch (<expresión>)
Casos para<expresión>
{
caso<valor1>:
case<valor1>:
<instruccionesCaso1>
<instrucciones1>;
caso<valor2>:
break;
<instruccionesCaso2>
case<valor2>:
…
<instrucciones2>;
otros casos:
break;
<instruccionesOtros>
default:
Fin_Casos
<instruccionesOtras>
}
Diagrama de Flujo
En este caso la <expresión> no es booleana sino aritmética y de tipo entero, así cada caso corresponde
a un valor que puede resultar de su evaluación. De esta forma, el flujo de control que sigue la ejecución
de una instrucción Seleccionar-casos (switch-case) es el siguiente: se evalúa la <expresión> y si el
valor corresponde al valor de un caso, es decir a un <valori>, se ejecuta el bloque de <instruccionesi>
hasta encontrar el final de la instrucción, que en el caso de C está representado por la palabra
reservada break, terminando ahí la ejecución de la instrucción. Cuando el valor no corresponde a
ningún caso se ejecuta el bloque de instrucciones correspondiente a otros casos (default). El
conjunto de todos los casos, incluyendo el default, conforman el cuerpo de la estructura Seleccionar-
casos (switch-case).
Problema 4.3: Se requiere un programa que dada una calificación con número despliegue un mensaje,
de acuerdo con la siguiente información:
0-6: Reprobado
7: Suficiente, Aprobado
8: Bien, Aprobado
9: Notable, Aprobado
10: Sobresaliente, Aprobado
En este caso es conveniente utilizar una estructura selectiva múltiple, en donde la expresión que se
evalúe sea la calificación del estudiante y se defina un caso por cada una de las calificaciones posibles.
Es claro, que la entrada únicamente es la calificación y la salida es el mensaje correspondiente. De lo
anterior el algoritmo en pseudocódigo y diagrama de flujo quedaría de la siguiente forma.
Inicio
Imprimir “ Inserte una calificación: "
Leer nota
Seleccionar (nota)
caso 0: caso 1: caso2: caso 3: caso 4: caso 5: caso 6: Imprimir “Reprobado"
caso 7: Imprimir "Suficiente, Aprobado"
caso 8: Imprimir "Bien, Aprobado"
caso 9: Imprimir “Notable, Aprobado”
caso 10: Imprimir “Sobresaliente, Aprobado”
otros casos: Imprimir "Esa nota es incorrecta"
Fin_Casos
Fin
Algoritmo 4.3.a: Conversión de calificación numérica a letra - pseudocódigo
Observa que tanto en el diagrama de flujo como en el algoritmo, hay siete casos (0 6) en los
que la instrucción a ejecutar es la misma, por lo que se agrupan, más no se puede poner una condición
que los contenga, como se haría en las estructuras vistas en las subsecciones anteriores, así que en el
pseudocódigo se especifica cada valor antecedido por la palabra caso pero sólo se escribe una vez la
instrucción y, de manera similar, se listan todos los valores. Para aclarar el funcionamiento de esta
estructura y verificar si el algoritmo funciona adecuadamente, en la siguiente tabla se muestra una
prueba de escritorio cuando la nota es igual a 8.
Es importante señalar que, a diferencia de las estructuras anteriores, el cuerpo de una estructura
selectiva múltiple siempre debe ir encerrado entre llaves {} cuando se codifica en C, más no así las
instrucciones que se definen para cada caso, ya que estás se acotan por las palabra reservadas case y
break, por tal motivo no debes olvidar escribir el break al final de cada caso de lo contrario también se
ejecutarán las instrucciones de los casos que aparezcan después.
/* Programa: calificacion.c
* Descripción: Dada una calificación con número despliega un mensaje
* 0,1,2,3,4,5,6 - Reprobado
* 7 - Suficiente, Aprobado
* 8 - Bien, Aprobado
* 9 - Notable, Aprobado
* 10 - Sobresaliente, Aprobado*/
#include<stdio.h>
#include<stdlib.h>
/*Función principal*/
main()
{
/*Declaración de variables*/
int nota;
/*Mensaje de bienvenida */
printf("\nEl siguiente programa lee una calificacion con número,
\ndetermina que tipo de calificacion es\n");
/*Datos de entrada*/
printf("\nInserte una calificacion numérica: ");
scanf("%d",¬a);
/*Comparación*/
switch(nota)
{
case 0: case 1: case 2: case 3: case 4: case 5:
case 6: printf("\n\n\t\"Reprobado\"");
break;
case 7: printf("\n\n\t\"Suficiente, Aprobado\"");
break;
case 8: printf("\n\n\t\"Bien, Aprobado\"");
break;
case 9: printf("\n\n\t\"Notable, Aprobado\"");
break;
case 10: printf("\n\n\t\"Sobresaliente, Aprobado\"");
break;
default: printf("\n\n\t\"Esa nota es incorrecta\"");
}
printf ("\n\n\t\t");
system ("pause");
}
Programa 4.3: calificacion.c
En la siguiente figura se muestra la ejecución de este programa con el valor de entrada igual a 8.
A lo largo de esta sección has estudiado los tres tipos de estructuras selectivas y por medio de los
ejemplos presentados te has dado cuenta de la importancia y utilidad de estas estructuras, sin ellas
sería imposible construir programas que implicaran la toma de decisiones. Sin embargo, todavía existen
problemas que requieren de otro tipo de estructuras que permitan repetir una tarea un número
determinado de veces, la siguiente sección está dedicada a este tema.
Realiza una prueba de escritorio del algoritmo que resuelve el problema 4.3 presentado en esta sección
(algoritmo 4.3.a y algoritmo 4.3.b) considerando que el valor de entrada es igual a 10. Después escribe
y compila el programa calificacion.c (programa 4.3) en la computadora y ejecútalo con el mismo valor
para comparar el resultado. Finalmente, reflexiona si es posible resolver el problema planteado
utilizando estructuras selectivas simples o dobles e ingresa tu comentario en el Foro Estructuras de
control
En la mayor parte del diseño o implementación de las soluciones que se plantea a problemas
específicos nos encontramos con instrucciones que deben ejecutarse un número determinado de veces,
si hacemos un análisis más profundo de estas situaciones, en la mayorías de las ocasiones nos
encontramos que las instrucciones son las mismas, pero que los datos varían, esto se hace posible
utilizando las estructuras repetitivas, generalmente llamadas ciclos.
Existen varias estructuras de repetición implementadas por los diferentes lenguajes de programación,
todas con la misma idea: repetir un conjunto de instrucciones, llamadas cuerpo del ciclo, dependiendo
de condición. En la mayoría de los ciclos el cuerpo se repiten siempre y cuando la condición se cumpla,
sin embargo, también existe una estructura repetitiva que se repite en tanto que no se cumple la
condición. En esta sección sólo nos enfocaremos en las primeras que son las que están definidas en el
lenguaje C y en la mayoría de los lenguajes estructurados y orientados a objetos actuales. Cabe
mencionar que a cada una de las veces que se repite el ciclo se le conoce como iteración.
Cuando se utilizan ciclos dentro de un programa, te puedes enfrentar a dos posibles situaciones:
Que conozcas desde el diseño cuántas veces deben repetirse las instrucciones (repetición
definida),
Que el número de veces que se deban repetir las instrucciones dependa de un valor que se
conoce hasta el momento de la ejecución del ciclo (repetición indefinida).
En el primer caso se necesitará una variable que funja como un contador, en la cual se registre el
número de iteraciones que se vayan ejecutando. En cambio, en las repeticiones indefinidas
generalmente se controlan mediante interruptores o banderas, o bien, con valores centinela.
Con los anterior puedes darte cuenta que para las estructuras de control repetitivas es muy importante
el uso de variables auxiliares y 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 e
interruptores.
Un contador es una variable comúnmente de tipo entero destinada a almacenar un valor que se irá
incrementando o decrementando en una cantidad constante. Se suelen utilizar mucho en procesos
repetitivos definidos, para contabilizar el número de veces que se repite un conjunto de acciones o
eventos, es decir en los cuerpos de las instrucciones repetitivas.
Sobre una variable contadora se realizan dos operaciones básicas: inicialización e incremento o
decremento, según sea el caso. Todo contador se debe inicializar con un valor inicial (0, 1...)
contador = Valor_Inicial
Cada vez que aparezca el evento a contar se ha de incrementar o decrementar en una cantidad fija (I, D
respectivamente) el valor del contador.
contador = contador+ I;
contador = contador- D;
Los contadores más utilizados tienen incrementos o decrementos de uno en uno, es por ello que la
simplificación de dichas expresiones es:
En contraste, un interruptor o bandera es una variable que puede tomar dos posibles valores a lo largo
de la ejecución del programa, éstos son: 1 (encendido/abierto) y 0 (apagado/cerrado), de ahí su
nombre. Y se utilizan principalmente para registrar la ocurrencia o no de un suceso.
Por último, un acumulador es una variable cuyo objetivo es acumular cantidades sucesivas obtenidas al
realizar la misma operación. El uso más habitual de un acumulador es obtener sumas y productos. Al
igual que con los contadores, para poder utilizar un acumulador hay que realizar sobre ellos dos
operaciones básicas: inicialización e incremento.
En el caso de obtener sumas el acumulador se inicializa en cero y en el caso de los productos en uno,
para no afectar el resultado.
SumaTotal=0;
ProductoFinal=1;
Una vez obtenido y almacenado en una variable la cantidad a acumular la añadimos a la variable
acumulador:
En resumen, los contadores permiten llevar la cuenta del número de iteraciones que se realizan en un
ciclo (definido o indefinido), y en el caso específico de ciclos definidos son el medio por el cual se
controla el fin del ciclo. Por otro lado los acumuladores nos permiten guardar resultados parciales de
operaciones que se realizan como parte del cuerpo de un ciclo (puede ser definido o indefinido). En este
punto, es importante señalar que en lenguaje C hay tres diferentes estructuras repetitivas: while
(Mientras-hacer), for (Desde-mientras) y do-while (Hacer-mientras), con todas ellas es posible
modelar ciclos definidos o indefinidos, pues las tres son equivalente, es decir, cualquiera de ellas se
puede expresar en términos de las otras. En los siguientes subtemas estudiarás a profundidad cada una
de ellas y verás su equivalencia.
La estructura repetitiva Mientras, codificada en lenguaje C con la palabra reservada while, controla las
repeticiones a partir de una condición que se evalúa al inicio del ciclo, de esta manera en cada iteración
primero se evaluará la condición y mientras resulte verdadera se repetirá el ciclo. En la siguiente tabla
se muestran las representaciones del ciclo Mientras (while).
Para ejemplificar cómo se construye un ciclo indefinido utilizando un valor centinela, se propone el
siguiente problema.
Problema 4.4: Se requiere un programa que calcule el promedio de temperaturas que registra una
ciudad, las temperaturas se introducirán en grados Farenheit °F y no se conoce de antemano el número
de temperaturas que el usuario introducirá.
Para resolver el problema planteado se podría pedir el número de temperaturas que se desean registrar
para calcular el promedio, pero esto equivale a una estructura de repetición definida, si decidiéramos
dejar abierto este dato hasta el momento de la ejecución del programa, tendríamos que construir una
condición que haga que el ciclo se repita mientras que el usuario desea ingresar temperaturas. Pero
¿cómo se puede resolver esto? En casos como este se propone utilizar un valor centinela que indique el
fin de la captura de datos. Claramente el valor centinela debe ser seleccionado de tal forma que no se
confunda con algún valor de entrada aceptable, por ejemplo podríamos considerar que dado que existe
un límite mínimo de temperaturas en grados Farenheit, a saber -460°F, el valor centinela sería
cualquier número inferior a éste, es claro que no existe una temperatura más baja, sin embargo el límite
máximo es difícil de definir ya que en forma experimental se obtienen en los laboratorios temperaturas
de miles de grados, mientras que en una explosión atómica se alcanzan temperaturas de millones de
grados. Se supone que la temperatura en el Sol alcanza los mil millones de grados (Pérez, 1992, pág.
325).
Así que en este caso se usará un ciclo que vaya leyendo una a una las temperaturas (almacenándolas
en la variable �) y acumulando la suma en la variable �, estas acciones se repetirán hasta
que el usuario introduzca un número menor a -460. De esta manera, la condición de término es:
� −460; por lo que antes de iniciar el ciclo se debe pedir la primera temperatura, para que se
compare con la condición y si es mayor a -460 se acumule en la suma. Además, se utilizará un contador
para registrar el número de temperaturas que se lean. Finalmente, cuando se termina el ciclo se divide
el resultado de la suma de las temperaturas entre el valor del contador. Lo anterior se expresa en el
siguiente pseudocódigo.
Inicio
c←0, sumaF←0
Imprimir "Ingrese la primer temperatura registrada en grados Fahrenheit:"
Leer tempF
Mientras (tempF≥-460)
c←c+1
sumaF=sumaF+tempF
Imprimir "Ingrese la siguiente temperatura en grados Fahrenheit (un número mayor
a -460)
para calcular el promedio "
Leer tempF
Fin Mientras
promF←sumaF/c
Imprimir “El promedio de las temperaturas es” promF
Fin
Algoritmo 4.4.a: Promedio temperaturas - pseudocódigo
En la siguiente tabla se muestra una prueba de escritorio para las temperaturas 75, 78, 79 y para
concluir el ciclo -2000.
En la tabla 4.9 se puede observar como el mismo conjunto de instrucciones se repite tres veces (3
iteraciones), en cada una se valida la condición y el ciclo termina sólo cuando ésta no se satisface por
el estado de las variables implicadas en la expresión booleana. Una vez que se ha ilustrado el
funcionamiento del ciclo Mientras y verificado que si funciona, el siguiente paso es la codificación, para
la cual se determinó utilizar una variable para representar el valor centinela que controla el ciclo.
/* Programa: promTemp.c
Descripción: Calcula el promedio de las temperaturas que el usuario
ingresa.
*/
#include<stdio.h>
#include<stdlib.h>
#define centinela -460
/* Función principal */
main () {
/*Declaración de acumuladores y contadores*/
float tempF,promF, sumaF=0;
int c=0;
scanf ("%f",&tempF);
Por último, en la siguiente figura se muestra la ejecución del programa con los mismos datos que se
utilizaron en la prueba de escritorio.
Realiza una prueba de escritorio del algoritmo que resuelve el problema 4.4 presentado en esta sección
(algoritmo 4.4.a y algoritmo 4.4.b) considerando que los valores de entrada son 80, 100, 250, -1000.
Después escribe y compila el programa promTemp.c (programa 4.4) en la computadora y ejecútalo con
los mismos valores para comparar el resultado. Posteriormente reflexiona si es posible resolver este
problema utilizando únicamente estructuras selectivas e ingresa tus comentarios en el Foro Estructuras
de control.
El ciclo Desde-mientras, en inglés y lenguaje C for, evaluará una condición y mientras ésta sea
verdadera se ejecutará el conjunto de instrucciones definidas en el cuerpo de la estructura,
generalmente las repeticiones se controlan por un contador, ya que como parte de su sintaxis tiene la
opción de inicializar una variable (el contador) e incrementarlo o decrementarlo. Este tipo de estructura
es conveniente utilizarla cuando se conoce de antemano el número de veces que se debe repetir el
ciclo (ciclos definidos). Sus representaciones se muestran en la siguiente tabla.
Desde
<inicialización>Mientras<condición>,
<incr/decr>
<Instrucciones>
Fin desde
Lenguaje C
for (<inicialización>;<condición>; <inc/dec>)
<instrucciones>
Es importante señalar que si te centras en la representación en diagrama de flujo de este ciclo podrás
darte cuenta que se parece al diagrama del ciclo while, salvo por la inicialización y el incremento (o
decremento); de hecho si partes del diagrama de flujo para la codificación puedes utilizar un while. De
igual manera, un ciclo while se puede representar con un ciclo for cuya <inicialización> e
<incremento/decremento> son vacíos, sólo se define la condición. Con lo anterior se muestra la
equivalencia de las dos estructuras.
Problema 4.5: Se requiere un programa que calcule el total de la nómina de los trabajadores de una
empresa.
El problema es similar al que se presentó en la sección anterior, se debe leer el pago de cada trabajador
y realizar la suma de cada uno de éstos, para lo cual se puede utilizar un acumulador. La diferencia es
que en este caso no se utilizará un valor centinela para terminar la lectura de los pagos, pues se
preguntará al usuario al inicio del programa cuántos trabajadores hay, así el número de iteraciones
quedará determinado antes de iniciar el ciclo. De lo anterior tenemos que si el número de empleados es
n entonces el ciclo debe repetirse n-veces, para lo cual se utilizará un contador c que debe tomar los
valores 1,2,…, n, así que el ciclo debe repetirse siempre que c ≤ n. En cuanto a la suma de los pagos,
se utilizará un acumulador, al cual llamaremos nom, que se inicializará en cero dado que se trata de una
suma. Observa la solución del problema.
Inicio
Imprimir "Ingrese el total de empleados: "
Leer n
Desde c=1 , nom=0, Mientras (c<=n), c=c+1
Imprimir “Ingresa el salario del trabajador”, c
Leer sal
nom=nom+sal
Fin desde
Imprimir “La nómina a pagar es en total $”, nom
Fin
Algoritmo 4.5.a: Nómina - pseudocódigo
En la siguiente tabla se muestra la ejecución paso a paso del ciclo suponiendo que se quiere calcular la
nómina de tres empleados y ya se ha leído el dato y registrado en la variable n, la cual no cambia su
valor a lo largo de la ejecución del ciclo.
Por lo tanto, la salida del algoritmo es: “La nómina a pagar es $45”. La codificación sería la siguiente.
/* Programa: nomina.c
Descripción: calcula la nómina a pagar de n trabajadores
*/
/*directivas de preprocesador*/
#include<stdio.h>
#include<stdlib.h>
/*Función Principal*/
main ()
{
/* Declaración de las variables */
int n,c;
float nom,sal;
/*Acumulador de salários*/
nom=nom+sal;
}
printf(“\n La nomina a pagar es $&.2f”, nom);
}
Programa 4.5: nomina.c
En la siguiente figura se muestra la ejecución del programa con los mismos datos de entrada que se
utilizaron en la prueba de escritorio.
Realiza una prueba de escritorio del algoritmo que resuelve el problema 4.5 presentado en esta sección
(algoritmo 4.5.a y algoritmo 4.5.b) para calcular la nómina de 4 trabajadores con salarios igual a:
$2000.00, $5000.00, $3000.00 y $1000.00. Posteriormente, escribe y compila el programa nomina.c
(programa 4.4) en la computadora y ejecútalo con los mismos valores para comparar el resultado.
Analiza cómo podrías resolver este problema utilizando la estructura Mientras (while) e ingresa tus
comentarios al respecto en el Foro Estructuras de control.
A diferencia de las estructuras repetitivas anteriores, en las cuales las condiciones se evalúan al
principio del ciclo, por lo que las instrucciones que se repiten se ejecuten de 0 hasta N veces, en la
estructura Hacer-mientras (do-while) la evaluación se lleva acabo al final, esto implica que el conjunto
de instrucciones que se repite se ejecuta al menos una vez.
Hacer do
<instrucciones> <instrucciones>;
while(<condición>);
Mientras <condición>Fin
Observa que en el código en C, la única estructura de control, de todas las que hemos visto, que tiene
punto y coma después de la expresión o condición es el do-while.
Por el funcionamiento de la estructura, el caso típico del uso del do-while son los menús. Para
ejemplificar lo anterior se propone el siguiente problema.
Problema 4.6: Se requiere un programa que imprima un menú con las siguientes opciones, el cual se
repita en tanto no se elige la opción d (Salir).
a. Calcular la fuerza
b. Calcular la aceleración
c. Calcular la masa
d. Salir
Además, dependiendo de la opción que elija el usuario se deberá realizar la tarea indicada utilizando la
segunda ley de Newton que dicta: “La aceleración que un cuerpo adquiere es directamente proporcional
a la resultante de las fuerzas que actúan en él, y tiene la misma dirección en el sentido que en dicha
resultante”
� = ∗ ó
En este caso, para resolver la parte del menú se utilizará un switch-case, en el que cada opción del
menú corresponda a un caso, así las instrucciones que lo forman deben ser: la lectura de los datos
correspondientes y la operación apropiada (que se define despejando la variable en cuestión de la
fórmula dada). Para que el menú se repita se plantea un ciclo while que se ejecute mientras la opción
sea distinta de 4 (Salir). De esta forma el algoritmo se presenta a continuación en sus dos
representaciones.
Inicio
Hacer
Imprimir "Realiza Cálculos trabajando la 2a. Ley de Newton"
Imprimir " ------------------------------------------------"
Imprimir " a. Fuerza."
Imprimir " b. Aceleración."
Imprimir " c. Masa."
Imprimir " d. Salir."
Imprimir " Elegir una Opción: "
Leeropc
Selecciona (opc)
Caso 1: Imprimir "Ingresa La masa:”
Leer m
Imprimir “Ingresa la aceleración:”
Leer a
f = m*a
Imprimir “Fuerza = ”, f
Caso 2: Imprimir "Ingresa la fuerza:”
Leer f
Imprimir “Ingresa la masa:”
Leer m
a = f/m
Imprimir “Aceleración = ”, a
Caso 3:Imprimir "Ingresa la fuerza:”
Leer f
Imprimir “Ingresa la aceleración:”
Leer a
m = f/a
Imprimir “Masa = ”, m
A continuación se presenta el diagrama de flujo, salvo que únicamente se ha dejado indicado en donde
van las instrucciones de los tres primeros casos, ya definidas en el pseudocódigo.
En el diagrama de flujo se puede observar claramente que el ciclo se ejecutará mientras el usuario no
elija la opción d, que corresponde a salir. Por lo que no se deja como ejercicio al lector la validación del
algoritmo.
/* Programa: newton.c
Descripción: Muestra un menú para calcular la aceleración,
fuerza o masa, conforme a la segunda ley de Newton */
/*directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
/*Función Principal*/
main ()
{ /*Declaración de variables*/
char opc;
float f,m,a;
a=f/m;
printf("\nLaaceleracion es %.2f\n\n\t",f);
system ("pause");
break;
case 'c': printf ("\n\nIngresa la fuerza: ");
scanf("%f",&f);
printf ("\nIngresa la aceleración: ");
scanf("%f",&m);
m=f/a;
printf("\nLa masa es %.2f\n\n\t",f);
system ("pause");
break;
case 'd': printf ("\n\nAdios\n");
system ("pause");
break;
default: printf ("\n\n Opcion Invalida");
}/*Fin dela Selección Múltiple*/
}while (opc!='d');
}/*Fin*/
Programa 4.5: newton.c
En la siguiente figura se muestra la ejecución de una iteración del ciclo, en la cual la opción elegida es
la primera.
Observa que dentro del cuerpo del ciclo se definió una estructura selectiva, es decir, que las
instrucciones del cuerpo de cualquier estructura compuesta, sea repetitiva o selectiva, puede contener a
otras. Uno de los casos más utilizados es el anidamientos de los if´s, de lo cual hablaremos en la
siguiente sección. Pero antes de dar por terminada esta sección se propone la siguiente actividad.
Finalmente, reflexiona si las estructuras de control repetitivas presentadas en esta unidad son
equivalente, es decir, cualquier de ellas se puede representar con las otras e ingresa tus conclusiones
en el Foro Estructuras de control.
Las estructuras de control selectivas y repetitivas se consideran compuestas ya que se forman a partir
de otras instrucciones que son las que se ejecutaran de acuerdo a una condición dada. Es importante
remarcar que las instrucciones que forman el cuerpo de una estructura pueden ser también estructuras
compuestas, como se demostró en la solución del último problema visto (ver algoritmo 4.6 y programa
4.6), en el cual un switch está dentro de un while. Así que es posible anidar cualquier tipo de
estructura, sin embargo, lo más común es anidar instrucciones if, pues se utilizan cuando se tienen
varios casos, por ejemplo, si revisamos nuevamente el problema 4.1, donde se quiere determinar el tipo
de ángulo, es mejor solución utilizar if-anidados para resolverlo porque así no se evalúan condiciones
que, una vez que se ha definido el tipo de ángulo, son innecesarias.
Inicio
Imprimir "Ingrese la medida del angulo (grados): "
Leer angulo
Si realizas la prueba de escritorio con el ángulo igual a 90 grados, podrás darte cuenta que a diferencia
de la primera versión del algoritmo donde se evalúan todas las condiciones, aquí sólo se evalúan las
tres primeras, en los dos primeros Si es falsa y por lo tanto se ejecutan las instrucciones del Sino
correspondiente, pero en el tercer Si anidado la condición es verdadera y se imprime el tipo de ángulo,
posteriormente se acaba el anidamiento.
El programa en C es el siguiente:
main ()
{
/*Declaración de variables */
intangulo;
/*Mensaje de bienvenida*/
printf ("\nEste programa determina de que tipo es el angulo
dado.");
/*Instrucciones */
printf ("\n\nIngrese la medida del angulo (grados): ");
scanf ("%d",&angulo);
if (angulo<=0 || angulo>=360)
printf ("\n No tiene clasificación");
else if (angulo<90)
printf ("\n El angulo es agudo");
else if (angulo==90)
printf ("\n El angulo es recto");
else if (angulo<180)
printf ("\nElanguloesobtuso");
else
printf ("\nElangulo es concavo");
printf ("\n\n\t");
system ("pause");
}
Programa 4.7: tipoAngulo2.c
Con este ejemplo se da por terminada esta unidad, ahora ya conoces todas las estructuras y has visto
cómo funcionan y qué tipo de situaciones se puede modelar con ellas. Aunque cabe destacar que para
solucionar cualquier problema basta con que sepas utilizar el ciclo while y la estructura selectiva if-
else, pues ya se mencionó que todos los ciclos son equivalentes y con la estructura if-else, puedes
modelar un switch-case anidando if´s.
Realiza una prueba de escritorio del algoritmo que resuelve el problema 4.1, en su versión de if´s
anidados, presentado en esta sección (algoritmo 4.7), considerando que el valor del ángulo es 90
grados. Posteriormente escribe, compila y ejecuta el programa tipoAngulo2.c (programa 4.7) en la
computadora con el mismo valor del ángulo para verificar tu resultado.
Con ayuda de tu facilitador deberás acotar el problema que describiste en la evidencia de la unidad 2,
de tal manera que la solución sólo implique el uso de estructuras de control. Para conocer los detalles
de la actividad, ingresa al aula virtual.
Para cumplir con los objetivos de esta unidad te recomendamos que realices las actividades señaladas
como opcionales de cada subtema, las cuales consisten en pruebas de escritorio y codificación,
compilación y ejecución de los programas que se realizaron a lo largo de esta unidad.
Referencias:
Pérez, H. (1992). Física General (Primera Edición ed.). México: Publicaciones Cultura.
Propósitos
En esta unidad:
Determinarás las estructuras de datos involucradas en la solución de un problema.
Diseñarás soluciones empleando arreglos y estructuras (registros).
Utilizarás arreglos y estructuras (registros) en programas escritos en lenguaje C.
Competencia específica
Utilizar estructuras de datos para almacenar y manipular los datos de un programa por medio del
desarrollo de programas en lenguaje C.
Introducción
En muchas ocasiones nos vemos en la necesidad de procesar datos que están relacionados entre sí, a
este tipo de datos se le conoce como estructurados, ya que están compuestos de un conjuntos de datos
básicos (recuerda la clasificación de datos presentada en la unidad 3 en la tabla 3.2 Tipo de datos). Por
ejemplo pensemos en el nombre completo de una persona, que está compuesto nombre, apellido
paterno y apellido materno, o bien, en una dirección, formada por nombre de la calle, número y código
postal, en este último caso no sólo está formada por varios datos simples sino que además podemos
considerarlos de diferentes tipos (Figura 5.3).
Con este tipo de datos será útil poder hacer referencia a ellos bajo un mismo identificador, y así tratarlos
como una unidad. Una estructura de datos es un mecanismo de agrupación de datos que facilitan el
manejo de datos estructurados y que se caracteriza por la forma en que se acede a sus elementos.
Pensemos en otro ejemplo en el cual se tienen datos relacionados, supongamos que nos enfrentamos
al siguiente problema:
Problema 5.1: Se requiere un programa para llevar el registro de calificaciones de un grupo de diez
estudiantes y generar reportes que incluyan datos como el promedio del grupo, la calificación máxima,
el número de estudiantes que tienen una calificación superior al promedio del grupo, entre otros.
En este caso, a diferencia de los ejemplos anteriores, es claro que las calificaciones de cada estudiante
se puede tratar como un dato simple e independiente de los otros, sin embargo las operaciones que se
desean realizar serán las mismas para todo el conjunto de calificaciones, de tal forma que habría que
escribir una serie de instrucciones secuenciales para ingresar cada dato y procesarlo. Por ejemplo, para
ingresar los datos se requiere leer una por una cada calificación, para obtener el promedio se tendría
que hacer la suma de todas y después dividirlas entre 10, hasta aquí no se ha complicado mucho, pero
imagina todas las comparaciones que debes hacer para identificar cuál es la calificación mayor. Es claro
que 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 60 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.
En cambio si consideramos a todos las calificaciones como un dato estructurado podemos hacer uso de
una estructura de dato que nos facilite su manipulación.
Existen diferentes tipos de estructuras de datos, cada una caracterizada por la forma de acceso a sus
elementos, y el tipo que estos pueden tener, así tenemos arreglos, listas, colas, tablas, pilas, entre
otros. No obstante, para esta unidad nos centraremos sólo en las estructuras de datos que implementa
el lenguaje C de forma directa: los arreglos y las estructuras.
5.1. Arreglos
El uso de arreglos facilita y hace 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 las calificaciones en el
Problema1, ya que todas las calificaciones se pueden considerar como valores enteros.
“Un arreglo se define como una colección finita, homogénea y ordenada de elementos. Finita ya
que para todo arreglo debe especificarse el número máximo de elementos que podrá contener;
la homogeneidad se refiere a que todos los elementos deben ser del mismo tipo, y ordenada
porque es posible determinar cuál es el primer elemento, cual el segundo, y así hasta el enésimo
elemento”(Cairo Osvaldo, Guardati Buemo Silvia, 1993).
La posición que ocupa un elemento dentro de un arreglo se le denomina formalmente índice y siempre
es un número entero. El tamaño o longitud de un arreglo se define como el número de elementos que lo
constituyen. La dimensión de un arreglo está relacionada con el número de índices necesarios para
especificar a una elemento en particular.
Los arreglos unidimensionales(también llamados lineales) reciben su nombre debido a que cualquier
elemento es referenciado por un único índice, por ejemplo retomando el caso de las calificaciones del
problema 5.1, éstas pueden ser almacenadas en un arreglo unidimensional como el que se muestra en
la Figura 5.4, en donde el nombre del arreglo es lista y los nombres de las variables donde se
almacenan las calificaciones son: 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 para cada elemento es el número que le corresponde a
cada variable según la posición que ocupa en la lista. Observa que un solo índice es suficiente para
diferenciar a un elemento de otro.
Por otro lado los arreglos multidimensionales son aquellos para los cuales un solo índice no es
suficiente para poder referenciar a un elemento individual, los arreglos bidimensionales son el caso más
comúnmente utilizado de arreglos multidimensionales y por tanto los únicos que presentaremos.
de los elementos del arreglo de arreglos. Es claro que con un solo índice no podríamos identificar a un
único elemento ya que solo podríamos ubicar toda una columna o todo un renglón, en cambio la
combinación de renglón-columna si nos identifica a un elemento en particular.
En lenguaje C los índices de los arreglo siempre empiezan 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.
El tipo de dato para los arreglos puede ser cualquier tipo básico, es decir entero, flotante o carácter (en
C int, float, double o char ). De todos ellos los arreglos de tipo carácter (char) tienen un
tratamiento especial, ya que un arreglo de este tipo se considerara una cadena. Debido a la importancia
que tienen las cadenas en la programación más adelante los trataremos de manera particular.
Al igual que las variables simples, un arreglo puede inicializarse al momento de ser declarado, para ello
se utiliza el operador asignación “=”, pero como un arreglo almacena a un conjunto de datos, es
necesario inicializarlo con un conjunto de valores, los cuales se indican mediante llaves, separando por
comas cada elemento del conjunto de valores iniciales, la sintaxis se muestra a continuación:
<tipo><nombre>[<tamaño>]={<valor0>,<valor1>,…,<valorTAM-1>};
La asignación de cada valor inicial se hace consecutivamente desde el elemento 0, por tanto no es
posible asignar valores a elementos salteados.
Veamos como ejemplo la declaración del arreglo unidimensional lista (Figura 5.4) planteado para las
calificaciones del problema1. Inicializando sus elementos en la declaración queda como:
<tipo><nombre>[<tamaño1>][<tamaño2>]={
{<valor00>,<valor01>,…,<valor0(TAM21)>},
{<valro10>,<valor11>,…,<valor1(TAM21-1)>},…,
{<valor(TAM1-1)0>,<valor (TAM2-1)1>,…,<elem(TAM1-1)(TAM2-1)>}
};
Veamos ahora como queda la declaración del arreglo bidimensional tabla mostrado en la Figura I.5,
inicializando su valores:
int tabla[5][3]={{9,10,8},{5,9,6},{7,9,4},{8,9,6},{7,9,4}};
int tabla[5][3]={9,10,8,5,9,6,7,9,4,8,9,6,7,9,4};
Esta es debido a que como ya se dijo antes un arreglo bidimensional se pude ver como un arreglo de
arreglos.
Por otro lado, en lenguaje C siempre es necesario especificar el tamaño del arreglo al momento de
declararlo, sin embargo esto se puede hacer de forma explícita o implícita.
Observa que en este caso no se escribe el tamaño dentro de los corchetes, pero como hay 10
elementos en el conjunto de valores iniciales, el compilador de C asume un tamaño 10 para el arreglo.
Para los arreglos bidimensionales, sólo es posible especificar una dimensión de forma implícita, el
tamaño de renglones siempre debe hacerse de forma explícita.
Es importante señalar que cuando se desea inicializar el arreglo al declararlo, es posible inicializar sólo
algunos de sus elementos, pero en este caso se tendría que especificar explícitamente el tamaño,
además se debe recordar que la asignación de valores iniciales es consecutiva desde el elemento 0.
Los elementos para los cuales no se indique un valor inicial, automáticamente se inicializan en cero. Por
ejemplo la declaración
int lista[10] = {5};
Reservará espacio en memoria para los 10 elementos del arreglo de los cuales al primer elemento se le
asignará un 5 y al resto se les asignará un cero.
En el caso de los arreglos bidimensionales es posible declara sólo algunos elementos por renglón,
siempre y cuando los elementos sean consecutivos, como en el caso de los unidimensionales. Por
ejemplo la siguiente declaración para el arreglo tabla:
int tabla[5][3]={{9,10},{5},{7,9,4},{8,9,}};
int tabla[5][3]={9,10,5,7,9,4,8,9,};
Para referirse a un elemento del arreglo es necesario indicar el nombre del arreglo seguido del índice o
índices correspondientes al elemento que deseamos acceder. Para ello se debe seguir la siguiente
sintaxis.
Cada elemento del arreglo se puede tratar igual que a cualquier otra variable, es decir, podemos
asignarle un valor, incluir en una expresión algebraica o lógica, imprimir en pantalla su valor, asignarle
desde el teclado un valor, etc.
Los arreglos y los ciclos están estrechamente relacionados, podríamos afirmar que en cualquier
programa que se emplee un arreglo éste será manipulado por medio de una estructura repetitiva.
Anteriormente mencionamos que un arreglo se utiliza cuando queremos almacenar y manipular datos
relacionados entre sí y, generalmente, cuando tenemos un arreglo debemos ejecutar el mismo conjunto
de operaciones sobre cada uno de sus elementos.
En lenguaje C el índice del elemento se puede especificar mediante una expresión, es decir una
variable, una constante o como el resultado de una operación, lo que hace posible ir cambiando el
índice de un elemento dentro de un ciclo sin tener que escribir una serie de instrucciones secuenciales
para realizar la misma operación sobre cada uno de los elementos del arreglo.
La forma general de procesar un arreglo unidimensional por medio de un ciclo se muestra en la Figura
I.6, observa cómo la variable contador pos del ciclo, también se utiliza como índice para el elemento del
arreglo, de tal forma que en cada iteración se haga referencia a un elemento diferente del arreglo.
No es difícil intuir que para el caso de los arreglos bidimensionales será necesario no solo un ciclo sino
un par de ciclos anidados, ya que tenemos dos dimensiones, cada uno de los ciclos se encargará de
variar a un índice, la estructura general para estos ciclos se muestra en la Figura I.7. Dos ciclos están
anidados cuando está uno dentro de otro, de tal forma que por cada iteración del externo, el interno
completa todo un ciclo. Considerando esto, observa que en la Figura I.7 se toma como contador a la
variable i para el ciclo externo, la cual también se utiliza como índice de renglón. Asimismo, se utiliza a
la variable j como contador para el ciclo interno, además de índice de columna, de esta manera se está
recorriendo el arreglo por renglón.
Si analizamos el diagrama de flujo podemos observa que para la primera iteración del ciclo externo, la
variable i tiene el valor de 0 mientras que j toma los valores desde 0 hasta TAMC-1 (TAMC es el número
total de columnas) de tal forma que el renglón se mantiene fijo mientras nos movemos en todas las
columnas, en la siguiente iteración cambia el renglón ya que i toma el valor de 1 y recorremos
nuevamente todas las columnas, este proceso se repite hasta el valor final de i que es TAMF-1 (TAMF
es el número de renglones).
Ejemplo 5.1: Veamos cómo utilizar arreglos en la solución de un problema, resolviendo como ejemplo
parte del problema 1, enfocándonos únicamente en la lectura de las 10 calificaciones y el cálculo del
promedio.
Para almacenar las calificaciones se puede utilizar un arreglo unidimensional de tipo entero, será
necesario pedir al usuario que ingrese las 10 calificaciones para poder realizar las operaciones
necesarias para el cálculo del promedio, es decir, la suma de las misma y la división entre el total de
calificaciones, para finalmente imprimir en pantalla el promedio, adicionalmente se imprimirá también la
lista de calificaciones ingresadas. No existe ninguna restricción que sea necesaria considerar.
Cada uno de los procesos que se van a realizar sobre las calificaciones, es decir leerlas, sumarlas e
imprimirlas en pantalla se pueden implementar mediante ciclos, en vez de tener que escribir 10 veces la
misma expresión para cada una de las 10 calificaciones. La lectura y la suma de las calificaciones se
pueden implementar dentro del mismo ciclo. De esta manera podemos resumir el análisis del problema
de la siguiente forma:
9
[]
prom = =0
10
Inicio
suma ← 0
Desde i ← 0 mientras i<10, i ← i+1
Imprimir “Ingresa la calificación” i
Leer calif[i]
suma← suma+calif[i]
Fin Desde
prom ← prom/10
Imprimir “Las calificaciones ingresadas fueron:”
Desde i ← 0 mientras i<10, i ← i+1
Imprimir “Calificación” i “:” calif[i]
Fin Desde
Imprimir “Calificación promedio = ” prom
Fin
Algoritmo 5.2. Promedio de calificaciones
/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
/* Definimos como constante simbólica el tamaño del arreglo*/
#define TAM 10
/* Definición de función principal */
main( )
{
/*Declaración del arreglo calificaciones*/
int calif[TAM];
double prom = 0;
int i;
printf("*******************************************\n”);
printf(“* El siguiente programa calcula el promedio de *\n");
printf(“* un grupo de diez estudiantes *\n”);
printf("********************************************\n”);
}
/*Cálculo e impresión del promedio*/
prom = prom/TAM;
/*Impresión de las calificaciones*/
printf("\nLas calificaciones ingresadas fueron: \n");
for(i=0; i < TAM; i++)
printf("\nCalificacion %d: %d",i+1, calif[i]);
Observa que el tamaño del arreglo se especifica por medio de una constante simbólica, utilizando la
directiva #define, esto facilita el cambiar el tamaño del arreglo sin tener que hacer cambios en todo el
código.
Reflexiona cómo solucionarías el problema si no contaras con los arreglos y qué pasaría si en vez de
calcular el promedio de 10 calificaciones tuvieras que hacerlo para 50, qué se debería modificar en cada
el programa que se presentó en esta sección y qué tendrías que modificar sino contaras con los
arreglos. Ingresa tus conclusiones al Foro Estructuras de Datos
Ejemplo 5.2: Se requiere un programa que calcule el determinante de una matriz de 2x2. Considerando
la siguiente información.
Análisis del problema: Para este problema se puede utilizar un arreglo bidimensional de 2 renglones y
2 columnas para almacenar los valores de la matriz, los cuales se pueden solicitar al usuario utilizando
la estructura de ciclos anidados presentada en la Figura I.7, nuestro dato de salida será el valor del
determinante y adicionalmente también se mostrará en pantalla la matriz ingresada.
Datos de entada: Elementos de la matriz (A[ ][ ])
Salida: Valor del determinante (det)
Método:
= [0][0] ∗ [1][1] − [0][1] ∗ [1][0]
La codificación del algoritmo se deja como ejercicio al lector, sin embargo, en la siguiente figura se
muestra un ejemplo de ejecución del programa.
/* Directivas al preprocesador */
#include <stdlib.h>
#include <stdio.h>
/* Función principal */
main()
{
int i, j;
float det;
float A[TAM][TAM]; /*declaración de la matriz*/
/* Mensaje de bienvenida */
printf("***************************************\n");
printf("* Determinante de una matriz A de 2x2 *\n");
printf("***************************************\n");
/* Lectura de la matriz A */
for(i=0; i<TAM; i++)
for(j=0; j<TAM; j++){
printf("\nProporciona el elemento A[%d][%d]: ", i,j);
scanf("%f", &A[i][j]);
}
det = A[0][0]*A[1][1] - A[0][1]*A[1][0];
printf("\nA: \n\t");
/* Impresión de la matriz A */
for(i=0; i<TAM; i++){
for(j=0; j<TAM; j++)
printf("%8.2f", A[i][j]);
printf("\n\t");
}
Realiza una prueba de escritorio con los datos que se muestran en la ejecución del programa
determinante.c (figura 5.7) y posteriormente escribe, compila y ejecuta el programa en la
computadora. Comparte tu experiencia en el Foro.
5.1.5 Cadenas
Una cadena es una serie de caracteres tratados como una sola unidad. Una cadena puede incluir
letras, dígitos y varios caracteres especiales(Deitel H. M., Deitel P. J., 1995).
En algunos lenguajes de programación existe un tipo de dato específico para definir a las cadenas, sin
embargo, en lenguaje C las cadenas se implementan por medio de arreglos de tipo carácter ya que no
existe un tipo específico para ellas, pero existe todo un conjunto de funciones estándar definidas en la
biblioteca string.h mediante las cuales es posible realizar las operaciones más comunes, por
ejemplo: copiarlas, concatenarlas, compararlas, entre otras. Además es posible imprimirlas y leerlas de
forma similar que un dato simple.
En lenguaje C toda constaten de tipo cadena se indica entre comillas dobles, por ejemplo:
“Calle 2 #135”
Una cadena en C termina siempre con el carácter nulo „\0‟ (cuyo valor ascii es cero) que representa el
fin de cadena.
Al declarar arreglos de tipo char que sean una cadena se pueden inicializar directamente con una
constante cadena de la siguiente forma:
char cad[50]=”saludo”;
Al inicializar una variable cadena de esta manera, se agrega automáticamente el símbolo de carácter
nulo para indicar el fin de cadena, es decir, en la posición 6 del arreglo cad se encuentra almacenado el
fin de cadena „\0‟. De forma general, es importante señalar que en un arreglo de tamaño N es posible
almacenar correctamente una cadena de máximo N-1 caracteres. De tal forma que en el arreglo cad se
puede almacenar una cadena de máximo 49 caracteres.
Las cadenas en C pueden ser asignadas a un arreglo de tipo char utilizando la función scanf
mediante el especificador de formato %s, por ejemplo la línea de código:
scanf(“%s”,cad);
De esta manera se lee desde el teclado una cadena y se guarda en el arreglo cad, sólo que en este
caso no se incluye el operador & antes del nombre del arreglo, pues el identificador del arreglo
almacena la dirección del primer elemento del mismo.
La función gets() también nos permite leer del teclado una cadena y asignarla a un arreglo de tipo
char, por ejemplo la instrucción:
gets(cad);
Una diferencia importante entre usar scanf y gets es que con la primera la lectura de la cadena se da
por terminada cuando el usuario presiona [Enter] o Espacio, mientras que la segunda termina la lectura
de la cadena únicamente cuando el usuario presiona [Enter], tal como se muestra en los siguientes
ejemplos:
Cadena ingresada Instrucción Contenido del arreglo cad[]
“Calle 2 #135” scanf(“%s”,c cad[]:
ad); C a l l e / / … /
0 0 0
“Calle 2 #135” gets(cad); cad[]:
C a l l e 2 # 1 3 5 / … /
0 0
Similarmente, para imprimir en pantalla una cadena se puede utilizar la función printf con el
especificador de formato %s, o bien, la función puts, nuevamente ambas funciones tienen un
comportamiento similar con la única diferencia de que puts incluye siempre un salto de línea al final,
esto se ilustra a continuación.
Código c Ejecución
Impresión de cadena con printf
#include <stdio.h>
#include <stdlib.h>
main(){
char mensaje[30]=”Mar
profundo ”;
printf(“%s”,mensaje);
system(“pause”);
}
Las funciones que nos permiten el manejo de cadenas se encuentran en la biblioteca estándar
string.h, para ilustrar algunas se muestra el siguiente programa en C.
Ejemplo 5.3: El programa siguiente verifica si una clave (password) de 8 caracteres alfanuméricos
ingresado por el usuario es correcta.
/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* se incluye la biblioteca de
cadenas */
main( )
{
/* Declaración de variables */
char pwscorrecto[9]=”jk278la0”; /* Clave correcta */
if (!strcmp(pwscorrecto,pwsingresado)){ /* comparación
de claves, si la función strmp regresa 0 son i
iguales */
printf(“pasword correcto \n”);
strcat(mensaje,nombre); /* pega al final de
mensaje el nombre del usuario*/
puts(mensaje); /* impresión de la cadena con salto
de línea*/
}
else {
strcpy(mensaje, “Acceso denegado”); /* copia la
cadena acceso denegado al mensaje */
puts(mensaje); /* imprime la cadena*/
}
system("pause");
}
Programa 5.3: password.c
Con esto se concluye la sección de arreglos, recuerda que un arreglo es un conjunto de dato del mismo
tipo. En la siguiente sección verás cómo puedes agrupar datos de diferente tipo.
5.2. Estructuras
Las estructuras en C al igual que los arreglos nos permiten tratar a un conjunto de datos bajo un mismo
identificador pero a diferencia de los arreglos las estructuras son conjuntos de datos contiguos en
memoriano homogéneos de tal forma que una estructura puede estar formada por datos de diferentes
tipos.
Una de las aplicaciones para las estructuras es para la manipulación de registros que se recuperan o se
almacenan en una base de datos.
“Una estructura es una colección de una o más variables, de tipos posiblemente diferentes,
agrupadas bajo un nombre para manejo conveniente (en algunos lenguajes también se conocen
como registros). Las estructuras permiten tratar a un grupo de variables relacionadas como una
unidad, en vez de que se traten en forma separada (Kernighan & Ritchie, 1991, pág. 141)”.
La definición de una estructura en C inicia con la palabra reservada struct seguida del identificador
para el tipo de estructura y de un bloque de definiciones de variable que constituyen el conjunto de
elementos que forman parte de ella, la sintaxis para definir la estructura es la siguiente:
struct<identificadorEstructura>{
<tipo1><identificadorE1>;
<tipo2><identificadorE2>;
<tipoN><identificadorEN>;
}
Observa que se utiliza la palabra reservada struct para iniciar la definición y que las declaraciones
para los elementos de la estructura se encierran entre llaves. Una estructura puede contener a N
elementos de diferentes tipos, de cualquiera de los tipos básicos, o incluso un arreglo, veamos un
ejemplo:
struct paciente {
int nss; /* número de seguro social */
char apellido[50];
char nombre[20];
int edad;
float estatura;
char sexo;
}
En este ejemplo se está definiendo la estructura paciente que tiene seis elementos: dos enteros (nss y
edad), dos cadenas (apellido y nombre), un flotante (estatura) y un carácter (sexo). Sin embargo la
definición anterior no reserva espacio en memoria para la estructura, más bien define un tipo de dato,
por lo tanto para poder utilizar la estructura, es necesario declarar una variable de este tipo, es aquí
cuando se reserva espacio en memoria. La sintaxis para hacer esta declaración es la siguiente:
struct<identificadorEstructura><identificador_var>;
Declara a las variables paciente1 y paciente2, las cuales son del tipo paciente y por tanto para cada una
de ellas se reserva espacio en memoria suficiente para cada uno de sus seis elementos.
Otra forma válida de hacer la declaración es haciéndola seguida a la definición de la estructura, para el
ejemplo anterior puede escribirse como sigue:
struct paciente {
int nss;
char apellido[50];
char nombre[20];
int edad;
float estatura;
char sexo;
} paciente1, paciente2;
En este caso el identificador para el tipo de estructura puede omitirse, pero entonces la única forma de
declarar variables para ese tipo de estructura es en su definición, y no en una declaración por separado,
de tal forma que nuestro ejemplo puede quedar como sigue:
struct {
int nss;
char apellido[50];
char nombre[20];
int edad;
float estatura;
char sexo;
} paciente1, paciente2;
Por otro lado, al igual que los arreglos, también se pueden inicializar los elementos de una estructura en
el momento de la declaración de una variable del tipo de la estructura en cuestión, éstos deben estar
encerrados entre llaves y separados por comas. La sintaxis general es la siguiente:
struct<identificadorEstructura><identificador_var> =
{ <valorE1>,<valor2>, ,<valorN> };
Por ejemplo :
Sólo en el momento de la declaración es posible asignar todos los valores de una estructura (al igual
que con los arreglos), así que si después se quiere modificar tendrán que hacerse las modificaciones de
forma separada en cada uno de sus elementos, como se muestra en el siguiente subtema.
Retoma el programa que has trabajado para las evidencias de aprendizaje anteriores y realiza una
versión preliminar del mismo pero ahora incorporando lo que has aprendido en esta unidad. Tu
facilitador(a) te orientará si tienes dudas. Con esta actividad podrás prepararte para la entrega de tu
evidencia de aprendizaje de la unidad.
Para referenciar un elemento de la estructura se utiliza el operador punto “.” con la siguiente sintaxis:
<idenficador_var>.<idenficadorEi>
Este operador seguido del identificador del elemento, referencia el elemento indicado, de tal forma que
la sentencia para asignar al paciente1 un nss de 23442145 será:
paciente1.nss = 23442145;
paciente1.nombre = “Pablo”;
Del mismo modo se realiza la impresión o lectura del elemento de la estructura, sin perder de vista su
tipo. Para ilustrar esto se propone el siguiente ejemplo.
Ejemplo 5.4: En el siguiente programa se declara una estructura de tipo perro, que tiene los siguientes
elementos:
Elemento Tipo
Raza char[]
Edad int
Peso float
Posteriormente se declaran dos variables de éste tipo, una se inicializa en la declaración y a la otra se le
asignan valores desde el teclado, ambos se muestran al final en pantalla.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main(){
/* Declaración de la estructura perro*/
struct perro{
char raza[20];
int edad;
float peso;
} fido, pluto = {"labrador", 7, 20} ; /* inicialización de la
variable pluto*/
printf("***********************************");
printf("\n* Comparando perros *");
printf("\n***********************************");
getch();
}
Nota: Observa que en este caso, para poder imprimir la letra ñ se utilizó su código Ascii (164) para lo
cual en la cadena de control del printf se escribió %c en donde se desea imprimir la ñ. Este mismo
truco se hizo para acentuar la letra a.
Escribe, compila y ejecuta en la computadora el programa perros.c. Posteriormente, analiza cuáles son
las principales diferencia entre las estructuras y los arreglos e ingresa tus comentarios en el Foro
Estructuras de Datos.
Para concluir con este capítulo veamos el siguiente ejemplo donde se utilizan estructuras y arreglos:
Ejemplo 5.5:Se requiere un programa que permita registrar los datos de los perros que ingresan a
refugio para perros, y poder desplegar los datos de alguno de los perros, a cada perro se le asigna una
clave numérica consecutiva. Los datos que se registran del perro son:
la fecha de ingreso (cadena)
nombre (cadena)
raza (cadena)
color (cadena)
edad (entero)
peso (flotante)
Para la solución del problema se puede plantear un menú que permita registrar a un perro, o bien,
desplegar los datos del perro solicitados. En este caso tenemos como datos de entrada la opción del
menú que elija el usuario. En el caso de que sea un registro tendremos también se tienen como datos
de entrada los datos del perro. Para la opción de despliegue se tendrá que dar como datos de entrada
la clave del perro y la salida serán los datos correspondientes a la clave.
Para desplegar y repetir el menú se requiere un ciclo y una estructura condicional que maneje las
opciones del menú. Para almacenar los datos del perro, se puede utilizar una estructura similar al
ejemplo anterior. Mediante un arreglo de estas estructuras estaremos en capacidad para manipular los
datos de varios perros, el tamaño de este arreglo tendrá que ser igual a la capacidad del refugio (100
perros), de tal forma que el índice del arreglo que toca a cada perro corresponderá con su clave.
Una restricción que hay que tomar en cuenta es que se debe verificar que no se sobre pase la
capacidad de los 100 peros, tanto al ingresar datos como al recuperarlos, para ello se llevará un
contador (c) que actualice el número de perros ingresados.
Inicio
c←0
Hacer
Imprimir “Refugio para perros -Ladrido Feliz- ”
Imprimir “1) Registrar un perro ”
Imprimir “2) Buscar un perro ”
Imprimir “3) Salir ”
Imprimir “Elige una opción: ”
Leer op
Casos para op
Caso 1: Si c≥100 entonces
La prueba de escritorio se deja como ejercicio al lector. En cuanto a la codificación del algoritmo se
muestra a continuación.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main(){
/* Declaración del arreglo de tipo estructura perro */
struct perro{
char fecha[10];
char raza[30];
char color[50];
char nombre[30];
int edad;
float peso;
} perros[100];
scanf("%d",&op);
switch (op){
case 1: /*Opción Registrar perro */
printf( "\n------------------------------\n");
if(c>=100) /* Verifica si hay espacio */
printf("El refugio esta lleno\n");
else{
/*Si hay espacio pide los datos del perro y
Y los guarda en el registro c del arreglo */
printf( "Ingresa los datos del perro:");
printf( "Clave:%.3d\n", c);
printf( "fecha de ingreso[dd/mm/aa]: ");
scanf( "%s", perros[c].fecha);
printf( "nombre: ");
fflush(stdin);
gets( perros[c].nombre);
printf( "color: ");
gets( perros[c].color);
printf( "raza: ");
gets( perros[c].raza);
printf( "edad: ");
scanf("%d" ,&perros[c].edad);
printf( "peso: ");
scanf("%f" ,&perros[c].peso);
c++;
}
break;
case 2: /* Opción buscar perro */
printf( "\n-------------------------------\n");
printf( "Clave: ");
scanf("%d",&clave);
/* verifica que la clave sea válida */
while(clave>=100 || clave <0){
printf("La calve no es válida, ingresa
nuevamente la clave:");
scanf("%d",&clave);
}
/* Imprime los datos del perro correspondiente
a la clave */
printf("nombre:%s\n",perros[clave].nombre);
printf( "fecha de ingreso: %s\n",
perros[clave].fecha);
printf( "color: %s\n", perros[clave].color);
printf( "raza: %s\n", perros[clave].raza);
printf( "edad: %d a%cos\n",
perros[clave]edad,164);
printf( "peso: %.2f kilos\n",
perros[clave].peso);
break;
case 3: /* Caso salir, no hace nada */
break;
default: /* Caso opción inválida */
printf( "Opcion no valida\n");
}
}while (op!=3); /* El ciclo do-while se repite mientras la
opción no sea salir (3) */
}
Para alcanzar los objetivos de esta unidad te recomendamos que escribas y compiles los programas
que se desarrollaron a lo largo de la misma y realices una prueba de escritorio de cada uno de ellos, tal
como se sugiere en las actividades opcionales.
Referencias:
Cairo Osvaldo, Guardati Buemo Silvia. (1993). Estructura de Datos. México: McGraw-Hill.
Deitel H. M., Deitel P. J. (1995). Cómo programar en C/C++. México: Prentice Hall.
Unidad 6: Funciones
Propósitos
En esta unidad:
Identificarás los sub-problemas en que puede dividirse un problema.
Diseñarás algoritmos modulares para solucionar un problema
Construirás funciones en lenguaje C que realicen tareas específicas
Competencia específica
Implementar funciones para resolver problemas a través del desarrollo de programas modulares
escritos en lenguaje C.
Introducción
Hasta esta etapa del curso, has aprendido a utilizar las estructuras de control para diseñar soluciones
de problemas simples. También has conocido diferentes formas de representar los datos involucrados
en un problema, desde simples hasta estructurados (como arreglos, cadenas y estructuras). Sin
embargo, todas las soluciones propuestas constan únicamente de un módulo (función), llamado
principal (en C, main); lo cual no es conveniente cuando se trata de problemas complejos que requieran
de una serie de tareas para lograr su solución, pues ésta sería muy grande y, por lo tanto, difícil de
corregir o modificar. En estos casos lo recomendable es crear módulos independientes que realicen
cada una de las tareas específicas y que en conjunto implementen la solución del problema. A esta
metodología se le llama diseño modular, y está inspirado en la estrategia “divide y vencerás”.
En esta unidad se explicarán las funciones, que son el medio por el cual se implementan los módulos en
lenguaje C. Se presentará la sintaxis para crear una función y la forma en la que se utilizan. Además, se
resolverá un problema utilizando el diseño modular para ejemplificar el tema.
La metodología del diseño descendente consiste en efectuar una relación entre las etapas de
estructuración de manera que se relacionen entre sí a través de entradas y salidas de datos. Es decir,
Con lo anterior podemos decir que: un módulo se caracteriza por realizar una tarea específica, posee
sus propios datos de entrada – llamados parámetros de entrada – y su resultado – llamado salida o
valor de retorno –. El diseño de cada módulo debe ser independiente de los otros; si es necesario que
exista comunicación entre ellos, ésta únicamente podrá realizarse a través de los parámetros de
entrada y del valor de retorno. En este sentido, puede ser visto, por otros módulos, como una caja negra
que hacia el exterior sólo muestra qué hace y qué necesita, pero no cómo lo hace.
La creación de un modulo conlleva dos partes: la definición del módulo y la llamada o invocación
(ejecución). La primera consta de tres partes:
Definir los parámetros de entrada;
Definir el valor de retorno;
Escribir todas las instrucciones que le permitirán llevar a cabo la tarea, es decir, un algoritmo.
No existe un método para saber en cuánto módulos se debe dividir un problema, sin embargo es
importante tener en cuenta los siguientes principios del diseño modular:
Las partes altamente relacionadas deben pertenecer a un mismo módulo.
Las partes no relacionadas deben residir en módulos diferentes.
Para ejemplificar esta forma de resolver problemas y cómo implementarla, se presenta la siguiente
situación:
Problema 6.1: Realiza el análisis y diseño de un programa que lea las temperaturas promedio
mensuales registradas en una ciudad a lo largo de un año y calcule el promedio anual. Además, debe
convertir las temperaturas mensuales dadas en grados Celsius a grados Fahrenheit al igual que el
promedio.
Empecemos por hacer un bosquejo de los pasos que se tienen que realizar para llegar al resultado
deseado.
1. Leer las doce temperaturas promedio mensuales
2. Calcular el promedio anual de las temperaturas
3. Convertir las temperaturas promedio mensuales de Celsius a Fahrenheit
4. Convertir el promedio anual de temperaturas a Fahrenheit
5. Imprimir las temperaturas mensuales en grados Fahrenheit y el promedio anual de las
temperaturas, en Celsius y Fahrenheit.
Para registrar las temperaturas mensuales proponemos utilizar un arreglo de tamaño 12. Y de acuerdo
con lo anterior, proponemos tres módulos: El primero encargado de leer las temperaturas mensuales
dadas en grados Celsius, este módulo necesita el nombre del arreglo donde va a almacenar los datos.
Otro módulo encargado de calcular el promedio de las temperaturas, que recibe como parámetros de
entrada el arreglo con las temperaturas mensuales y devuelve el promedio en grados Celsius. El último
módulo sólo convierte grados Celsius a grados Fahrenheit. Esta información se resume en el diagrama
modular que se muestra a continuación.
Es mediante un diagrama modular como se representan de manera gráfica los módulos que integran la
solución del problema. Y una vez que se han definido, el siguiente paso es diseñar el algoritmo de cada
uno de ellos.
Módulo leerTemps(temp[ ])
Inicio
Desde mes ← 1 mientras mes ≤ 12,
mes ← mes + 1
Imprimir “Proporciona la temperatura
del
mes”, mes
Leer temps[mes-1]
Fin_Desde
Fin
Observa que el ciclo del módulo leeTemps se inicia con mes igual a 1 y termina cuando mes es 13, se
propone así porque se pide la temperatura de los mes 1, 2, 3,.. 12, así que la variable mes guardara el
número de mes correspondiente a cada lectura, sin embargo para almacenar la temperatura en la
posición del arreglo indicada se resta uno (temps[mes-1]), así se guarda desde la posición 0 y hasta la
posición 11.
Módulo promTemps(temp[])
Inicio
suma ← 0
Desde mes ← 0 mientras mes ≤ 11,
mes ← mes + 1
suma ← suma + temps[mes]
Fin_Desde
Regresa (suma/12)
Fin
En contraste con el ciclo del módulo leerTemp, en este módulo el contador del ciclo se inicia en 0 y
termina en 11 (aunque el valor que tiene al finalizar el ciclo es 12) pues se utiliza para ir sumando cada
uno de los elementos del arreglo, así que la variable mes indicará cada una de las posiciones del
arreglo.
Módulo aFahrenheit(tempC)
Inicio
tempF = 9 5 tempC + 32
Regresa tempF
Fin
Por esta ocasión se deja como ejercicio al lector que realice las representaciones en diagrama de flujo
del módulo principal, considerando que el símbolo que se utiliza para llamar a módulos que no
devuelven ningún valor, como es el caso se leerTemps se muestra a continuación:
leerTemps(temp[])
Esto no es caso, del resto de los módulos secundarios que sí devuelven un valor, así que se utiliza el
símbolo de proceso (rectángulo).
Con el ejemplo anterior resaltan indudablemente varias ventajas que proporciona un diseño modular:
1. Es posible reutilizar código, ésta indudablemente es 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 aFahrenheit se utiliza 13 veces (12 en el ciclo y una para convertir el
promedio) y sólo basto 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 existiera 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 involucrados.
3. Fácil modificación o extensión, si se requiere modificar una tarea del problema o agregar una
nueva, no será necesario rediseñar todo el algoritmo, basta con hacer las modificaciones en los
módulos pertinente.
Una vez que se ha ilustrado como realizar una solución modular, lo siguiente es explicar cómo se
codifica cada uno de los módulos, tema que se abordará en las siguientes secciones. }
En esta actividad reflexionarás sobre las ventajas del diseño descendente y las funciones en Lenguaje
C a partir de la solución modular del problema que planteaste en las unidades anteriores. Conforme
avances en el diseño modular del mismo ingresa tus comentarios al respecto en el Foro Funciones.
En el caso particular de C, un módulo se implementa como una función, recuerda que en la unidad 3 se
explico que un programa en C está integrado de varias funciones, donde una función se define como
una porción de código (un conjunto de instrucciones agrupadas por separado) enfocado a realizar una
tarea en específico, resaltando la función principal main.
Al igual que la función principal, cada una de las funciones existe de manera independiente, tiene sus
propias variables, instrucciones y un nombre que las distingue de las otras. Además de los parámetros
de entrada (también llamados argumentos) que recibe y el tipo de dato que regresa.
El <tipo de dato retorno> indica el tipo de dato que la función devuelve (puede ser cualquiera de
los tipos básicos), para lo cual se utiliza la palabra reservada return. Cuando la función no va a
devolver ningún dato se especifica mediante el tipo void y no debe incluir la palabra reservada
return. El <identificador de la función> es el nombre que le vamos a dar a la función y mediante
el cual vamos a hacer referencia a ella. Se deben seguir las mismas reglas que los identificadores de
las variables y se recomienda que sea nemónicos. Enseguida del nombre y entre paréntesis va la lista
de parámetros, que consiste de una lista de declaraciones de variables locales que van a contener los
datos de entrada para la función, se debe especificar explícitamente el tipo y nombre para cada uno,
separados por comas, aún cuando sean del mismo tipo: tipo1 param1, tipo2 param2,…, tipoN paramN.
Por último, las instrucciones del cuerpo de la función van entre llaves.
La primera línea de la definición de una función se conoce como encabezado de la función (en inglés
header).
Para ilustrar esto, a continuación se muestra la codificación del módulo aFahrenheit definido en la
sección anterior.
La llamada o invocación a una función se realiza cuando se requiere que se ejecuten las instrucciones
del cuerpo con valores de entrada determinados. Para invocar a una función se tiene que escribir el
nombre seguido de los valores que deben coincidir con el orden, número y tipo de los parámetros de
entrada dados en la definición de la función y deben estar encerrados entre paréntesis.
La sintaxis general para invocar una función ya sea predefinida o definida por el programador, es la
siguiente:
Los parámetros de entrada de una función son valores, por lo que pueden estar determinados por:
valores constantes (8,‟a‟, 5.2, “cadena constante”) o una variable (lo que realmente se pasa a la función
es el valor almacenado) o una expresión (el parámetro de entrada real es el resultado de la evaluación).
Por ejemplo, la llamada a la función que definimos se remarcar en la siguiente instrucción:
promF = aFahrenheit(promC);
Al igual que las variables una función debe de ser declarada antes de utilizarse, es decir, antes de
invocarla. La declaración de una función se realiza escribiendo el prototipo de la función. El prototipo de
una función coincide con el encabezado de la misma terminando con punto y como (;) El prototipo de
una función sólo indica al compilador que existe y cómo es, más no lo que hace por lo tanto se debe
definir después. Por ejemplo, el prototipo de la función antes definida sería:
Cabe señalar que en el prototipo de las funciones se puede omitir los identificadores de los parámetros,
más no los tipos.
Para ilustrar todo lo anterior, a continuación se muestra la codificación del algoritmo modular diseñado
en la sección anterior.
/* Programa: promedioTemp.c
* Descripción: Calcula el promedio de las temperaturas promedio
* mensuales registrada a lo largo de un año*/
/* Biblioteca */
#include<stdio.h>
#include<stdlib.h>
/* Variables globales */
int meses = 12;
/* Función principal */
main()
{
/* Declaración de variables locales a main */
float temps[12], tempsF[12], promF, promC;
int mes;
system(“pause”);
} /* fin main */
/* Definición de funciones */
return (suma/12);
} /* fin leerTemps */
Actividad 2. Funciones
En esta actividad presentarás una versión preliminar del programa que has trabajado como parte de las
evidencias de aprendizaje anteriores, el cual, ahora debe incorporar módulos para hacer más eficiente
su funcionamiento. En el aula virtual encontrarás más detalles.
El alcance de las variables es la parte del programa dentro de la cual se pueden utilizar. Cuando son
declaradas fuera del cuerpo de cualquier función se denominan variables globales y pueden ser
utilizadas en cualquier punto del programa a partir del lugar donde fueron declaradas, en cambio
cuando son declaradas dentro del cuerpo de alguna función se denominan variables locales a ésta, es
decir sólo dentro de esa función pueden ser utilizadas. Las variables locales que tienen en el mismo
nombre pero fueron declaradas en diferentes funciones, no tienen relación, son espacios de memoria
totalmente independientes uno de otro. Podemos decir que, son como dos personas diferentes que
tienen el mismo nombre. Por otro lado las variables que se ponen como argumentos en la declaración
de una función se consideran locales a estas. Para ejemplificar lo anterior, se muestra el siguiente
programa, en el cual se distinguen con diferentes colores el alcance de las variables.
#include<stdio.h>
#include<stdlib.h>
Variable global
int TAM = 5;
Variable local a
inicializaA
void inicializaA(int A[])
{ Variable local Referencia a una
int i;
variable global
for (i=0; i<TAM; i++)
A [i] = 0;
}
main()
{ Declaración de
int i; variables locales a main
int A[] = {1,1,1,1,1};
printf(“Arreglo antes de la llamada a inicializaA: A = [”);
for (i=0; i<TAM; i++)
{ if(i< TAM -1)
printf(“%d ,”,A[i]);
else
printf(“%d ]\n\n\t”,A[i]);
}
inicializaA(A);
printf(“Arreglo despues de la llamada a inicializaA: A = [”);
for (i=0; i<TAM; i++)
{ if(i< TAM -1)
printf(“%d ,”,A[i]);
else
printf(“%d ]\n\n\t”,A[i]);
}
system(“pause”);
}
Programa 6.2: porReferencia.c
Al utilizar variables globales todas las funciones pueden manipularlas, sus valores permanecen mientras
el programa está en ejecución. Sin embargo su uso puede promover errores de tipo lógico, ya que al
modificar el valor de una variable dentro de una función puede afectar el resultado de otra. Por ejemplo,
supongamos que la función inicializaA() modifica el valor de la variable TAM que almacena el número de
elementos del arreglo A, este cambio repercutirá en los ciclos de la función main, los cuales imprimen el
arreglo A. En este caso se producirá un error en la ejecución, pues si el valor es menor a cinco no se
imprimirán todos los valores y se es mayor entonces habrá elementos indefinidos. Detectar y corregir
este tipo de errores puede ser una tarea nada fácil, por lo que no se recomienda el uso de variables
globales, lo cual no ocurre si son constantes.
Las variables locales por otra parte favorecen mucho la reutilización de código y la modularidad, ya que
cada función declara y manipula sus propias variables sin depender de lo que ocurra en otras funciones,
esto no significa que al utilizar solamente variables locales no sea posible compartir datos entre las
diferentes funcione, esto se hace mediante sus datos de entrada y retorno, una posible desventaja es
que el valor de las variables locales se pierde cada vez que la función termina.
EL paso de parámetros se refiere a la forma en la que se transfieren como parámetro una variable a una
función, esencialmente, si se le otorgan o no permisos para modificar los valores originales. Cuando no
se le otorga permisos para que la modifique se dice que es paso de parámetros por valor, pues en este
caso sólo se transfiere el valor de la variable, el cual se almacena en una variable local de la función
que se está llamando. En cambio, cuando la función puede modificar el valor de la variable se dice que
es un paso de parámetro por referencia, pues en este caso no se pasa sólo el valor sino la dirección de
memoria que le corresponde a la variable.
En los siguiente subtemas se explica más a detalle los dos tipos de paso de parámetro.
Cuando se realiza una llamada a una función por valor y en ésta aparece una variable como uno de los
argumentos, en realidad no se está pasando la variable sino una copia del valor que ésta contiene, lo
cual implica que si dentro de la función se modifica el argumento esto no se ve reflejado en el programa
desde el cual se hizo la llamada, pues son localidades de memoria diferentes (recuerda que en cada
llamada a una función se crean nuevas variables y se destruyen una vez finaliza la ejecución).
#include<stdio.h>
#include<stdlib.h>
void inicializa(int a)
{
a = 0;
main()
{
int a=10;
system("pause");
}
Programa 6.3: porValor.c
En la ejecución puedes ver que la variable local a main no se modifica, esto es porque se pasa una
copia del valor que almacena cuando se realiza la llamada a la función inicializa(a). Este valor se guarda
en un espacio de memoria, también llamado a, que es una variable local a la función y que existe
mientras ésta se ejecuta. Observa que el cambio si se realiza en la variable local de la función inicializa.
La llamada a una función por referencia sí modifica el valor de la variable, pues lo que realmente se está
pasando es la dirección de memoria asignada a la variable para que la función pueda modificar el valor.
En C los arreglos siempre se pasan por referencia, ya que el nombre del arreglo en realidad almacena
la dirección de memoria donde se encuentra almacenado el primer elemento del arreglo. De esta
manera cuando se realiza una llamada a una función y se escribe el identificador de un arreglo como
parámetro, se está pasando la dirección. Para ejemplificar lo anterior se muestra la ejecución del
programa pasoReferencia.c que se presentó en un subtema anterior.
En la ejecución del programa se observa que después de la llamada a la función cambia el estado del
arreglo A.
Finalmente, cabe mencionar que para realizar la llamada por referencia de una variable de tipo básico
en lenguaje C es necesario pasar la dirección de la variable para lo cual se utiliza el operador & seguido
del nombre de la variable (&nombre), como se hace en la función scanf, este operador regresa la
dirección de memoria que le corresponde a la variable indicada. Por otra parte, para almacenar la
dirección de memoria de una variable se utiliza una variable de tipo apuntador. Una variable apuntador
se encarga de almacenar una dirección de memoria, la declaración de una variable apuntador es similar
a la de cualquier otra variable, con la diferencia que se debe escribir un asterisco entre el tipo de la
variable y el identificador. El tema de los apuntadores es muy interesante, sin embargo, no es uno de
los objetivos de este curso.
Para que apliques lo aprendido en esta unidad y a lo largo del curso se propone la siguiente evidencia
de aprendizaje.
Evidencia de aprendizaje: Programa en C. Entrega final del programa modular que soluciona el
problema planteado
Esta es la última evidencia, deberás realizar el diseño modular del problema original que describiste en
la evidencia de la unidad dos, así como la codificación de tu solución. Para conocer los detalles ingresa
al aula virtual.
Te sugerimos que escribas, compiles y ejecutes los programas que se desarrollaron a lo largo de esta
unidad para que comprendas mejor los contenidos y te apropies de ellos.
Referencias
Deitel H, M., & Deitel P, J. Cómo programar en C/C++. México: Prentice Hall.
Pérez, H. (1992). Física General (Primera Edición ed.). México: Publicaciones Cultura.