Manual Programacion C
Manual Programacion C
www.monografias.com
1. Presentación
2. Conceptos básicos
3. Lenguaje de Programación Estructurado C
4. Operadores, Expresiones y Estructuras
5. Ciclos
6. Funciones en C
7. Estructuras de Datos
8. Memoria dinámica
9. Recursión
10. Estructuras y Uniones
11. Tipos Abstractos de Datos (TAD´s) y Pilas
12. Colas
13. Listas Enlazadas
14. Nota Final
15. Bibliografía
Presentación
El Presente documento ha sido elaborado con la finalidad, de proveer a los estudiantes, de un recurso
ágil y fácil de comprender por aquellos que inician su estudio en la programación del lenguaje C.
¿Por qué decidí escribir este manual?
Sencillo, cuando en la universidad empezamos a estudiar C, nos pidieron que, si podíamos,
compráramos un libro texto para apoyar las clases, o buscáramos un manual en la Internet... yo opté por
la segunda opción, ya que como todos sabrán los libros de programación son muy caros (al menos aquí
en El Salvador), pero me encontré con dos grandes problemas:
1. Los Manuales que bajaba estaban incompletos, es decir; las explicaciones, los ejemplos y la
información en general era muy pobre, por no decir escasa.
2. si al dado caso, encontraba algún manual que estuviera completo, tenía muchos errores, en sus
ejemplo(errores de sintaxis, de ejecución y hasta de lógica) o en la explicación que se daba. Lo
cual al yo cometer esos ejemplos, mis profesores me lo hacían saber.
Por esas razones decidí escribir mi propio manual, para evitar que, otros estudiantes les suceda lo mismo
que a mí, y así les evitaré gastos en impresiones innecesarios.
Cabe recalcar que, no soy un experto programador... todavía sigo estudiando, pero quiero colaborar de
una manera desinteresada, a la formación de las personas.
Espero que, este pequeño manual les sirva de mucho y sobre todo, le saquen mucho provecho.
PARTE I :
CONCEPTOS BÁSICOS
CAPITULO I "MARCO INTRODUCTORIO"
Marco Conceptual
Se muy bien, que usted, querido lector; está muy ansioso por comenzar a programar, pero considero que
es importante; conocer un poco del lenguaje C, tanto de sus orígenes como de sus ventajas, pero no se
preocupen, seré breve en esta introducción teórica. Además que es importante conocer o recordar
algunos conceptos que, son importantes al momento de programar.
• Computadora
Componentes de Una Computadora
Hemos definido una, computadora como una máquina que recibe datos y ordenes, que al ejecutarlas
produce cierta información; dicha información se presenta en un “idioma” codificado; por que ha de
saberse que las computadoras no entienden nuestro idioma, o cualquier otro en el mundo. Dicho “idioma”
está compuesto únicamente por dos elementos los ceros y los unos. Mejor conocido como código
Binario, con el cual se representan los datos, que arroja la computadora.
En una forma más general, y creo que más sencilla, una computadora se comprende por dos grandes
grupo: El Hardware y el Software.
Hardware
El Hardware de un computador es un conjunto de elementos físicos, que la componen.
Veámoslo gráficamente:
Podríamos entrar en detalle de cada una de las partes descritas anteriormente, pero ese, no es el
objetivo de estas insignificantes páginas; sino que esto es una mera introducción teórica, por ello sólo
daré una breve explicación.
En la Unidad Central de Proceso (o CPU, por sus siglas en ingles –Central Proccessing Unit-) se
contiene la Unidad de Control, que su función es organizar y clasificar las instrucciones recibidas;
mientras que la Unidad Aritmética y Lógica, Se encarga de ejecutar dichas instrucciones. Los Buses,
son los mecanismos de interconexión en el CPU.
La memoria Principal, Es el lugar donde se cargan todas las instrucciones, programas, etc que se están
ejecutando.
Software
Debemos entender el software como la parte lógica de la computadora... ¿un poco difícil de comprender,
verdad?; es decir, que el software, es lo que dota a los componentes físicos de poder realizar tareas
determinadas. Ejemplo, para poder utilizar una computadora, esta debe tener instalado un sistemas
operativo. Para poder imprimir algún trabajo, aparte de poseer un impresor, en la computadora, debo
tener un software que me permita imprimir dicha acción (generalmente las impresoras traen un cd, son su
respectivo software de instalación).
Aburrido?...
Es necesario, que empiece con tanta palabrería; por que es necesario, para el lector tener en claro estos
conceptos.
Ahora vamos a hablar de algo un poco más interesante, como lo es el lenguaje de programación
Un lenguaje de Programación Es un conjuntos de palabras, reglas, con las cuales se le indica a la
computadora las funciones que debe realizar. Un lenguaje de programación puede ser:
1. Lenguajes Máquinas: se trata de lenguaje cuyas instrucciones son directamente comprendidas
por el ordenador o computador en el que se ejecuta el programa.
2. Lenguaje de Bajo Nivel: este tipo de lenguajes, al igual que sucede con los lenguajes máquinas,
existe una gran dependencia con el equipo en el que se va a ejecutar. No obstante son algo más
fáciles de escribir, quedando ubicados por tanto, según su grado de complejidad; en un nivel
intermedio entre el lenguaje máquina y el de alto nivel.
3. Lenguaje de Alto Nivel: Disponen de una sintaxis en lenguaje más natural, y un amplio conjunto
de funciones internas, que ayudan al programador en distintas situaciones, así como un número
determinado de utilidades y asistentes que ahorran tiempo y trabajo al programador. Dentro de
estos lenguajes tenemos: Visual Foxpro, Visual Basic. NET.
Programa:
Es un conjunto de instrucciones que se le dan a la computadora, para que ésta realice una determinada
tarea.
Lenguaje C
El lenguaje C, fue diseñado por Dennies Ritchie en 1970, en los laboratorios Bell de Estados Unidos.
Este lenguaje presenta varias características, entre las cuales están:
1. Lenguaje de programación de propósitos generales
2. Permite la Programación Estructurada
3. Abundancia de Operadores y Tipos de Datos
4. No está asociado a ningún sistema operativo ni a ninguna máquina
5. Popular y Eficaz
6. Permite el desarrollo de Sistemas Operativos y programas de aplicación
7. Portabilidad
8. Existen las librerías en las bibliotecas
9. tiene sólo 32 palabras reservadas
*bibliotecas: es el archivo que contiene código objeto de una colección de rutinas o funciones que
realizan tareas determinadas y se pueden utilizar en los programas.
*Enlazador: Programa que convierte las funciones de la biblioteca estándar de C, con el código que ha
traducido el compilador .
Estructura de Un programa en C
Ya estamos apunto de entrar a lo más interesante, a la programación en sí; pero es necesario, primero;
mencionar algunos de los errores típicos al programar, para que el lector sepa como identificarlos y así
los pueda corregir.
1. ERROR DE SINTAXIS: Estos errores son producidos, cuando se hace mal uso de las reglas del
lenguaje de programación, y se violan las normas de sintaxis, de ese lenguaje (en nuestro caso
C); estos errores son fáciles de detectar por que generalmente es el compilador, que los identifica
(Y hasta muestra la línea donde se encuentra dicho error, pero eso depende de la versión del
compilador que estemos usando). En este curso he usado Turbo C, en su versión 2 y 3.
2. ERRORES DE EJECUCIÓN: Estos errores se producen , cuando le indicamos a la
computadora, realizar una determinada acción, y esta la comprende, pero no puede ejecutarla.
Por ejemplo, indicarle a la computadora una división entre cero, sumar dos variables a las cuales
no se les ha signado valor alguno, etc.
3. ERRORES DE LÓGICA: Muchas veces, cuando estamos programando, el compilador no nos
indica errores de sintaxis, ni de lógica; pero el resultado de nuestro programa, esta fuera del
rango esperado, esto es producto de un error de lógica en el código de nuestro programa. Este
tipo de errores son muy difíciles de identificar y por supuesto de corregir, ya que generalmente
hay que revisar línea por línea de nuestro programa. Ejemplo: El sueldo negativo de un
empleado, etc.
La estructura de un programa en C, consta de algunas partes esenciales: las cuales son uno o más
módulos llamadas funciones, siendo main() la primera función que es llamada cuando empieza la
ejecución del programa .
Cada función debe contener :
>Directivas de pre-procesador (instrucciones que se le dan al compilador
#include antes de compilar)
#define
ejemplo:
#include <stdio.h>
Lo que se le esta indicando, es que de las librerías, “Incluya” en nuestro programa la directiva stdio.h, la
cual contiene las funciones de entrada y salida de datos (standar input output, en inglés). Si necesitamos
las funciones matemáticas, debemos especificarlo con la declaratoria:
#include <math.h>
Si necesitamos las funciones de cadenas:
#inlcude <stlib.h>
Es necesario aclarar que esto se hace al inicio del programa, y las declaratorias deben llevar el símbolo
de numeral (#) seguido de la sentencia “include”, y entre signos de mayor y menor que (<>) el nombre de
la directiva.
>Declaraciones Globales
pueden ser:
*Prototipos de Funciones: También llamadas declaraciones de funciones, lo cual se tratará más adelante
*Declaraciones de Variables
cabe destacar, que esto se hace seguido de los #include y los #define.
>Función Principal main()
Esta es la función principal de nuestro programa, su cuerpo, por ello NUNCA debe faltar, ya que en ella
van contenidas todas las instrucciones de nuestro programa.
la función main() va al inicio, luego abrimos llaves y dentro de ellas van las declaraciones de variables, las
sentencias de lectura, cálculos, asignaciones e impresiones, y con la última llave ( } ), le indicamos el
final del programa.
Ejemplo 1.1
Programa que a partir del radio, calcula el área de un circulo
Explicación:
Le indicamos al compilador, que usaremos las bibliotecas <stdio.h> y <conio.h>, ¿por qué <conio.h>?,
por que esta biblioteca, contiene las funciones getche(), getch(), etc, y de una de ellas hacemos uso en
este pequeño ejemplo.
Luego, le indicamos a nuestro programa el incio de nuestro programa (función main() ).
Declaramos, como valores reales, las variables radio y area (de esto se hablará más adelante). Luego,
con la instrucción printf(), mostramos en pantalla el mensaje (Radio=) y scanf se encarga de leer el valor
digitado por el usuario. Posteriormente area, es igual al la multiplicación de pi (3.14159), el radio al
cuadrado. Se muestra en pantalla ese resultado, luego el programa espera que se presiones cualquier
tecla (getch() ) y no retorna ningún valor (return 0).
Ejercicios
Defina los siguientes conceptos:
1. Programa:_____________________________________________________________________
_______________________________________________________
2. CPU:_________________________________________________________________________
________________________________________________________
3. Software:______________________________________________________________________
_______________________________________________________
4. Memoria
Principal:______________________________________________________________________
_______________________________________________________
5. Lenguaje de
Programación:__________________________________________________________________
______________________________________________________
Indique que tipo de error (Error de sintaxis, error de ejecución o error lógico), en cada uno de los
siguientes enunciados
1. Utilizar una variable antes de asignarle un valor:____________________
2. asignarle un valor real a una variable declarada como entero:______
3. al cometer este error, los resultados arrojados por el programa no son los que se
esperaban:______________________________________________
4. Un programa no puede ser ejecutado por el computador, mientras tenga este tipo de
errores:__________________________________________
( Incorrectos)
2c
2c
>No es lo mismo una minúscula que una mayúscula, ya que c distingue de entre ellas. Ejemplo: BETA ≠
Beta ≠ beta ≠ BeTa
>No son válidos los identificadores de palabras reservadas. En un inicio hablamos que c posee 32
palabras reservadas, entre ellas están:
float char while
int else return
Estas palabras no pueden ser utilizadas para identificar variables, constantes, funciones etc
b) Comentarios
En todo programa que estemos diseñando en C (o en cualquier otro lenguaje de programación); es
necesario insertar ciertos comentarios en el código, para que en posteriores modificaciones y cuando se
realice el mantenimiento, podamos recordar cosas importantes ya que, en los comentarios, podemos
incluir aspectos importantes del programas, explicaciones del funcionamiento de las sentencias, etc.
El formato de los comentarios en C, es el siguiente:
c) La Directiva #include
Permite que, el pre-procesador, incluya funciones proporcionadas por el fabricante, a nuestro programa.
Ejemplo:
#include <stdio.h> /* le decimos al compilador que incluya la librería
stdio.h */
d) la directiva #define
permite definir constantes simbólicas. Pero hasta ahora ha sido poco lo que hemos hablado acerca de las
constantes, es por ello que en aprovechando, este especio; dedicaré unas cuantas líneas para aclarar
ello.
Las variables pueden cambiar de valor, durante la ejecución del programa, por eso es que se llaman
variables. Y las constantes como su nombre lo indica, son valores que permanecen constantes durante
toda la ejecución del programa, un ejemplo de ello, es el valor de π (pi) que equivale a 3.14159....
En C existen diferentes tipos de variables, entre ellas tenemos:
1. Constates Numéricas:
Son valores numéricos, enteros o de reales (de punto flotante). Se permiten también constantes octales y
hexadecimales.
2. Constantes Simbólicas:
las constantes simbólicas tiene un nombre (identificador), y en esto se parecen las variables. Sin
embargo, no pueden cambiar de valor a lo largo de la ejecución del programa. En C, se pueden definir
mediante el preprocesador.
(Tomado del Manual “Aprenda Lenguaje ANSI C como si estuviera en Primero” Escuela superior de
Ingenieros Industriales. Universidad de Navarra. Febrero de 1998).
Ejemplo:
Esta directiva (#define) va, inmediatamente después de los #include. Se escribe la directiva, se deja un
espacio y se escribe el identificador de la constante, otro espacio y su valor.
e) Signos de Puntuación y de Separación
/ ! % ^ & * () - + {} [] \ ; : <> ¿ .
f) Todas las Instrucciones o sentencias del programa terminan con un punto y coma
(;)
Al momento de programar en C, esta es una regla de oro, y la causa por la cual nuestro programa puede
darnos muchos errores de sintaxis, cuando se omite, al final de cada sentencia un punto y coma (;). Ya
que con ello le indicamos al compilador que ha finalizado una sentencia.
NOTA: el lector no debe confundirse, las directivas: #include, #define. Main(), no llevan punto y coma, por
que no son sentencias.
Recordemos el ejemplo 1.1, y vea que al final de cada sentencia lleva su correspondiente punto y coma:
h) En una línea se pueden escribir más de una instrucción separada por un punto y
coma
Esto es posibles, por que con el punto y coma, le estamos indicando al compilador el fin de una sentencia
o instrucción.
Ejemplo:
Tipos de Datos en C
Un tipo de dato, se define como un conjunto de valores que puede tener una variables, junto con ciertas
operaciones que se pueden realizar con ellas.
Declaración de Variables
Una Variable, como su nombre lo indica, es capaz de almacenar diferentes valores durante la ejecución
del programa, su valor varía. Es un lugar en la memoria el cual, posee un nombre (identificador), y un
valor asociado.
La declaración de variables en C, se hace en minúsculas.
Formato:
Tipo_de_dato nombre_de_la_variable;
Ejemplos:
*Declare una variable de tipo entero y otra de tipo real, una con el nombre de “x” y otra con el identificador
“y”:
int x;
float y;
*Declare una variable de tipo entero llamada moon, e inicialícela con un valor de 20
int x = 20;
*Declare una variable de tipo real, llamada Pi, e inicialícela con una valor de 3.1415
float pi=3.1415;
Explicación:
En el apartado anterior, se explicó, que C, no tiene el tipo de dato llamado string, o mejor conocido como
cadenas de texto, pero nosotros podemos hacer uso de ellas, por medio de un arreglo, (de lo cual
hablaremos con más detalle, posteriormente); pero para declarar este tipo de datos colocamos el tipo de
datos, es decir la palabra reservada char luego el nombre, e inmediatamente abrimos, entre corchetes, va
el número de letras, que contendrá dicha variable. Es muy importante que al momento de declarar el
tamaño, sea un número mayor, al verdadero número de letras; por ejemplo, la palabra “Manuel”, solo
tiene 6 letras, pero debemos declararlo para 7 letras ¿Por qué?.
Veámoslo gráficamente, en la memoria, C crea un variable llammada nombre y esta posee la palabra
Manuel, así:
en realidad, hay 7 espacios, pero la cuanta llega hasta 6, por que c, toma la primera posición como la
posición cero, y para indicar el final de la cadena lo hace con un espacio en blanco.
Declaración de Constantes
Las constantes, como su nombre lo indica, son valores que se mantiene invariables durante la ejecución
del programa.
Su formato es el siguiente:
const tipo_de_dato nombre= valor;
donde const, es una palabra reservada, para indicarle al compilador que se esta declarando una
constante.
Ejemplo:
const int dia=7;
const float pi=3.14159;
const char caracter= ‘m’;
const char fecha[]=”25 de diciembre”;
#include <stdio.h>
Las Funciones de E / S más simples son getchar() que lee un carácter del teclado, espera un retorno de
carro (↵), es decir un enter y el eco aparece. Es decir la tecla presionada.
*putchar(): Imprime un carácter en la pantalla, en la posición actual del cursor.
Algunas variaciones:
*getche(): Aparece el Eco
*getch(): No aparece el eco
estas instrucciones se encuentran en la biblioteca conio.h
Veamos un ejemplo:
Programa que espera que se presiona una tecla, la muestra en pantalla, y además muestra el carácter
siguiente:
Ejemplo 2.1:
#include <stdio.h>
#include <conio.h>
main()
{
char car;
clrscr(); /*Se encarga de borrar la pantalla por eso se llama claer screen*/
car=getchar();
putchar(car+1);
getch();
return 0;
}
Ejemplo 2.2:
#include <stdio.h>
#include <conio.h>
main()
{
char x; /*Declaramos x como caracter*/
printf("Para Finalizar Presione cualquier Tecla:");
x= getchar();/*Captura y muestra el caracter presionado*/
getch();/*Espera a que se presione cualquier otra tecla para finalizar*/
return 0;
}
#include <stdio.h>
#include <conio.h>
main()
{
char nombre[40];
puts("digite su nombre:");
gets(nombre);
puts("BIENVENIDO:");
puts(nombre);
getch();
return 0;
}
NOTA: No haré mucho énfasis en estas instrucciones, ya que más adelante, veremos las instrucciones
scanf() y printf(), que son mucho más completas.
Entrada / Salida Por Consola con Formato
Las funciones gets, puts, getch, etc; son utilizadas, en una forma un poco rudimentaria, sin embargo; C
posee otra serie de funciones, que son más completas, las cuales nos permiten leer e imprimir (en
pantalla), datos con un formato determinado, el cual ha sido definido por el programador.
Salida Hacia Pantalla [printf()]
Se utiliza para imprimir en pantalla cadenas de texto solas, o mandar a pantalla el valor de alguna
variable, o constante, o una combinación de las anteriores. Su formato es el siguiente:
Printf(“cadena de control”, nombre_de_variables);
En donde:
Cadena de control: contiene códigos de formato que se asocian con los tipos de datos contenidos en las
variables.
Código Formato
%d Un entero
%i Un entero
%c Una caracter
%s Una cadena
%f Un real
%ld Entero largo
%u Decimal sin signo
%lf Doble posición
%h Entero corto
%o Octal
%x Hexadecimal
%e Notación Científica
%p Puntero
%% Imprime Porcentaje
TABLA 2.2
Ejemplo:
Int suma=10;
Printf(“La suma es %d”, suma);
Explicación:
Declaramos primero la variable como entero, con un valor de 10, luego la función printf, el mensaje va
entre comillas dobles, luego en el lugar que queremos que aparezca el valor, colocamos el formato de la
variable, cerramos comillas, luego una coma y el nombre de la variable. Es importante recalcar, que en la
posición que coloquemos el formato es donde aparecerá el valor de la variable en este caso, 10.
Ejemplo:
Char nombre[7]=”Manuel”;
printf(“%s es en creador de este manual”, nombre);
NOTA: el número de argumentos que tendrá la función printf() es indefinido, por lo que se puede
transmitir cuantos datos sean necesarios.
Ejemplo:
Int x=12, y=15;
char z=’D’;
float v=10.2563;
printf(“Estos son números %d %d %f; y esta es una letra %c”, x,y,v,z);
También podemos hacer algunos arreglos, al formato de salida, por ejemplo, si deseamos imprimir un
número real justificado a la izquierda podemos colocar:
printf(“%-f”, z);
Secuencias de Escapes
Indica que debe ejecutar algo extraordinario.
Carácter de Explicación
Escape
\n Simula un Enter. Se utiliza para dejar una línea de por medio
\t Tabulador horizontal. Mueve el cursor al próximo tabulador
\v Tabulador vertical.
\a Hace sonar la alarma del sistema
\\ Imprime un carácter de diagonal invertida
\? Imprime el carácter del signo de interrogación
\” Imprime una doble comilla
TABLA 2.3
Ejemplos:
1) printf(“Manuel \n Antonio \n Ortez\n\n);
2) int x=15;
printf(“El Valor de la variable es %d\n\n”, x);
Explicación:
Primero, iniciamos con las directivas del preprocesador:
#include <stdio.h>
#include <conio.h>
Con la cual le indicamos al compilador, que de su librería añada a nuestro programa las funciones
estándar de entrada y salida; así como las entradas y salidas por consola (stadio.h y conio.h,
respectivamente).
Luego declaramos la variables, que contendrán las notas como reales (o de punto flotante:
float n1, n2, n3;
Ya que, las notas pueden ser deciamales, por ejemplo 9.6, 8.5; etc.
Luego declaramos las variables, que contendrán las notas, caba aclarar que al momento de las
declaraciones las podemos hacer en el orden que deseemos, pueden ser primeros los tipo char y luego
los float, o viceversa, pero teniendo el cuidado que las variables que contendrán las nombres lleven la
longitud máxima entre corchetes, para nuestro caso, 10. ( [10] ).
Posteriormente, mostramos en pantalla, un mensaje con el cual le indicamos al usuario que introduzca
los datos respectivos:
printf("Introduzca el Nombre del Primer alumno:\n");
A continuación, va la función scanf, primero y entre comillas el tipo de dato que va a leer:
scanf("%s", nom1);
como puede notarse, va a leer la cadena de texto que contendrá la variable nom1. cabe aclarar, que
cuando se van a leer cadenas de texto, no es necesario colocar la dirección (&), lo cual no sucede con los
otros tipos de datos:
scanf("%f", &n1);
Después de haber leído los datos, espera a que se presiones cualquier tecla para finalizar la ejecución
del programa.
Ejemplo 2.5
Programa que imprime dos veces, la cadena de texto que se ha introducido:
#include <stdio.h>
#include <conio.h>
main()
{
char cadena[15];
printf("Digite la cadena:\n\n");
scanf("%s", cadena);
printf("\n\t LA CADENA ES LA SIGUIENTE:\n\n");
printf("***********************************************\n");
printf("%s\n", cadena);
printf("%s\n", cadena);
printf("***********************************************\n");
getch();
return 0;
}
Es importante, que el lector, intente correr, en su máquina estos ejemplos, para que comprenda con
mayor facilidad.
NOTA: Cuando la entrada, es una cadena de carácter, no es necesario el operador direccional (&). El
nombre de la cadena contiene la dirección.
Ejemplo:
Char cadena[]=”Manuel”;
Casos Especiales
*JUEGO DE INSPECCIÓN: Define Un conjunto de caracteres que puede leerse utilizando scanf().
Así:
%[ABC]s: A, B y C son los únicos caracteres que puede leer al encontrar uno diferente, finaliza con un
valor nulo.
%[ A-Z ]s: También pueden ser rangos de carácter en este caso sólo acepta mayúsculas.
*JUEGO INVERSO: Aquí se declaran que caracteres NO puede tomar, la función scanf(), se utiliza el
circunflejo (^), que acepta cualquiera menos...
Ejemplo:
%[^\n]s: Acepta cualquier carácter menos un salto de línea.
Ejemplo:
Scanf(“%[0-9]s”, &edad);
Cuestionario
1. Mencione y Explique que es la lógica
estructurada:_______________________________________________________________
_________________________________________________________________________
________________________________________
2. Para que sirven las funciones getch() y
putchar():_________________________________________________________________
_________________________________________________________________________
_________________________________________
3. Menciones las diferencias fundamentales entre las funciones de entrada y salida por
consola, con las funciones de entrada y salida por consola con
formato:__________________________________________________________________
_________________________________________________________________________
_________________________________________________________________________
_________________________________________________________________________
______________________
4. Escriba algunas restricciones que deben cumplir los
Identificadores:_____________________________________________________________
_________________________________________________________________________
________________________________________
Ejercicios:
1. Haciendo uso de las funciones gets y puts, diseñe un programa en C, que se lea el nombre del
usuario y lo muestre en pantalla junto con un saludo.
2. Diseñe un programa en C, que lea y muestre en pantalla el valor de tres variables de tipo Entero.
3. Diseñe un programa que muestre, los diferentes tipos de datos, usados en C. Primero, debe
indicársele al usuario que introduzca un valor, de un tipo dado; luego y después de haber
introducido valores en todas las variables, debe imprimirse el contenido de ellas, junto con un
mensaje que indique, el tipo de dato:
2x10=20
6. Realice el ejercicio 2.5, tal como se muestra, luego ejecútalo, nuevamente, pero quitándole al
código las sentencias: getch() y return 0. ¿Qué observas? Realiza tus propias conclusiones de ello y
de la importancia de estas dos funciones.
Operadores Aritméticos
Operador Propósito
+ Suma
- Resta
* Multiplicación
/ División
% Resto de la división entera
TABLA 3.1
Todos estos operadores se pueden aplicar a constantes, variables y expresiones. El resultado es el que
se obtiene de aplicar la operación correspondiente entre los dos operandos. (Tomado de “Aprenda
Lenguaje ANSII C, como si estuviera en primero”. Pag. 25).
Los operandos sobre los que actúan los operadores aritméticos deben ser valores Numéricos, es decir
datos enteros, punto flotante o de carácter (Int, float y char, respectivamente).
Una aclaración especial, merece el operador “%”, que indica el resto de la división entera. Veámoslo con
un ejemplo:
Si dividimos 30/3, su cociente es 10, y su residuo es 0. Si dividimos 25/3, su cociente es 8, y tiene un
residuo de 1. Entonces de lo que se encarga, este operador, es de devolvernos el valor del residuo de
una división. Cabe aclarar que los datos deben de ser tipo entero, y su sintaxis es la siguiente:
25%3
NOTA: Este Operador, NO puede aplicarse a los datos de tipo float.
Una Expresión, Es un conjunto de variable, constantes y otras expresiones más sencillas, relacionadas
por algún tipo de operador. De las cuales hablaremos con más detalle, posteriormente.
Operadores de Relaciónales, Lógicos y Unarios
Estos Operadores, los podemos dividir, en varios tipos, entre los cuales están:
1. OPERADORES UNARIOS: C, incluye una clase de operadores que actúan sobre un solo
operador para producir un nuevo valor. Por eso el nombre de unarios, por que para poder
funcionar solo necesitan de un operador.
Operador Propósito
disminuya en uno.
TABLE 3.2
Ejemplo:
Int i=1, x=5;
Printf(“%d”, ++i);
Printf(“%d”, - -i);
Estos operadores, el incremento y el decremento, pueden utilizarse de dos maneras, eso depende del
orden de aparición de los mismos:
-Si el operador precede al operando el valor del operando se modifica antes de ser utilizado.
-Si el operador aparece después del operando, este se modifica después de ser utilizado.
Ejemplo 3.1:
Utilizando los operadores Unarios:
#include <stdio.h>
#include <conio.h>
main()
{
int x=5;
printf("\tPRIMERO OBSERVAREMOS EL RESULTADO DE ++X\n\n");
printf("%d\n", ++x);
printf("%d\n", ++x);
printf("%d\n", ++x);
printf("\tAHORA OBSERVAREMOS EL RESULTADO DE --X\n\n");
printf("%d\n", --x);
printf("%d\n", --x);
printf("%d\n", --x);
printf("\tEL RESULTADO DE X++ ES:\n\n");
printf("%d\n", x++);
printf("%d\n", x++);
printf("\tY EL DE X-- ES:\n\n");
printf("%d\n", x--);
printf("%d\n", x--);
getch();
return 0;
}
Estos Operadores se encuentran dentro del mismo grupo de procedencia, que es menor que la de los
Operadores Unarios y aritméticos.
La Asociatividad de éstos es de izquierda a derecha. Cabe mencionar la diferencia entre los operadores =
y ==, el primero (=), se utiliza para asignaciones de valores, mientras que el otro (==), se usa para
comparaciones. Ejemplo: Si x>5, entonces x==6.
3. OPERADORES LÓGICOS: Estos son los que nos permiten unir varias comparaciones: 10>5 y 6==6.
Los operadores lógicos son: AND (&&), OR (||), NOT(!).
Operador && (AND, en castellano Y): Devuelve un 1 si se cumplen dos condiciones.
Operador || (OR, en castellano O): Devuelve un 1 si se cumple una de las dos condiciones.
Operador ! (NOT, negación): Si la condición se cumple NOT hace que no se cumpla y viceversa.
Ver el capítulo Sentencias, sección Notas sobre las condiciones para más información. (Tomado de
“Curso de C” por Gorka Urrutia).
Operadores de Asignación
Los Operadores de Asignación, como su nombre lo indica, se encargan de atribuirle, asignarle, confinarle,
etc a una variable, el resultado de una expresión o el valor de otra variable.
Se utilizan en forma de expresiones de asignación en los que se asigna en el valor de una expresión a un
identificador. El operador de asignación más utilizado es “=” y su formato es:
identificador = expresión;
Donde el identificador representa por lo general una variable y una constante, una variable o una
expresión más compleja.
Si los dos operandos de la expresión de asignación son de tipo de datos diferentes el valor de la
expresión de la derecha se convertirá automáticamente al tipo de identificador de la izquierda de ésta
forma la expresión de asignación será del mismo tipo de datos.
Ejemplo:
*Un valor en coma flotante puede ser truncado, se asigna a un identificador entero.
*Un valor de doble precisión puede ser redondeado si se asigna a un identificador de coma flotante.
En C, están permitidas las asignaciones múltiples, así:
Identificador1 = identificador2 = identificador3.....= identificadorn=expresión
Los Operadores de asignación tiene menos procedencia que el resto de los operadores y tienen
asociatividad de izquierda a derecha.
Ejemplo 3.2
Programa que calcula el valor de la expresión X^2+X+1
#include <stdio.h>
#include <conio.h>
main()
{
float x, y, z;
clrscr();
Jerarquía de Operadores
Categoría del Operador Operador
1. Operadores Unarios -, ++, --, !
2.Operadores Aritméticos:
2.1 Multiplicación, *, /, %
división y Resto
entero +,-
2.2 Suma y Resta
3. Operadores Relacionales <, <=, >, >=
4. Operadores de Igualdad ==, !=
5. Operadores Lógicos && (Y Lógico), || (NO Lógico)
6. Operadores de Asignación =, +=, -=, *=, /?, %=,
TABLA 3.5
REGLAS DE JERARQUÍA:
1. Se ejecuta primero el operador de más alta jerarquía
2. Operadores que tienen igual jerarquía se evalúan de izquierda a derecha
3. si existen expresiones encerradas entre paréntesis, estas se evalúan primero.
4. si existen paréntesis anidados se evalúan primero los paréntesis más internos.
EXPRESIONES
(Tomado de “Aprenda ANSII C como si estuviera en Primero”, Universidad de Navarra. 1998).
Ya han aparecido algunos ejemplos del lenguaje C en las secciones precedentes. Una Expresión es una
combinación de variables y/o constantes, y operadores. La expresión es equivalente al resultado que
proporciona al aplicar sus operadores a sus operandos. Por ejemplo 1 + 5 es una expresión formada por
dos operandos (1 y 5)y el operador (el +); esta expresión es equivalente al valor 6, por lo cual quiere decir
que allí donde esta expresión aparece en el programa, en el momento de la ejecución es evaluada y
sustituida por su resultado. Una expresión puede estar formada por otras expresiones más sencillas, y
puede contener paréntesis de varios niveles agrupando distintos términos. En C, existen diferentes tipos
de expresiones. El cual depende del tipo de operadores que se estén utilizando. Por ejemplo:
Expresiones lógicas, aritméticas, etc
Se debe hacer hincapié en que, si existen algunas expresiones encerradas entre paréntesis, estas se
evalúan primero. Ejemplo:
9*(8+5)
primero sumamos 8+5, cuyo resultado es 13, y este lo multiplicamos por nueve, con lo que la expresión
anterior, da cómo resultado: 117.
Si existen expresiones en paréntesis anidadas, es decir, que uno se encuentra dentro de otros
paréntesis, se evalúan los más internos. Ejemplo:
2*((20/(12-2))+5)
se evalúa la operación 12-2, que da como resultado 10, luego se divide 20, entre el resultado anterior, es
decir 10. el resultado es 2, y a este número se le suma 5, obteniendo 7. ahora se multiplica por dos, para
determinar así que la expresión anterior es igual a 14.
Estructuras
Estructuras Secuenciales
Se les denomina así, por que; son estructuras en un programa, que después de ejecutar una instrucción
o sentencia, continúan con la otra, hasta llegar al final del programa. Los ejemplos que hemos visto
anteriormente, son ejemplos de estructuras secuenciales. Veamos otros ejemplos:
Ejemplo 3.3
Diseñe un programa que calcula el cuadrado y el cubo de tres números introducidos por el usuario.
#include <stdio.h>
#include <conio.h>
main()
{
int x, x1, x2, y, y1, y2, z, z1, z2;
clrscr();
printf("\tPROGRAMA QUE CALCULA EL CUADRADO Y EL CUBO DE 3 NUMEROS\n\n");
printf("Introduzaca el primer n£mero:\n");
scanf("%d", &x);
printf("Ahora ingrese el siguiente n£mero:\n");
scanf("%d", &y);
printf("Y el tercer n£mero es:\n");
scanf("%d", &z);
x1=x*x;
x2=x*x*x;
y1=y*y;
y2=y*y*y;
z1=z*z;
z2=z*z*z;
printf("*********************************\n");
printf("**Numero****Cuadrado*****Cubo****\n");
printf("**%d **** %d ***** %d ****\n", x, x1, x2);
printf("**%d **** %d ***** %d ****\n", y, y1, y2);
printf("**%d **** %d ***** %d ****\n", z, z1, z2);
printf("*********************************\n");
getch();
return 0;
}
Ejemplo 3.4
Una empresa necesita conocer el sueldo neto a pagar a un empleado. Teniendo como entrada el salario
produzca una salida de sueldo neto. Los descuentos a aplicar son: ISSS 5%, AFP 7% y Renta 10%, estos
descuentos son sobre el salario, y es sueldo neto es la diferencia entre el salario y el total de las
retenciones:
#include <stdio.h>
#include <conio.h>
main()
{
float sueldo, afp, isss, renta, sn;
char nombre[50];
clrscr();
Ejemplo 3.5
Diseñe un programa que calcule el promedio y la suma de tres números ingresados por el usuario:
#include <stdio.h>
#include <conio.h>
main()
{
float x, y, z, sum, prom;
clrscr();
printf("El Primer n£mero es:\n");
scanf("%f", &x);
printf("Ahora el segundo n£mero:\n");
scanf("%f", &y);
printf("El Ultimo numero es:\n");
scanf("%f", &z);
sum=x+y+z;
prom=sum/3;
printf("*****************************************\n");
printf("**La suma es %.2f y el promedio es %.2f*\n", sum, prom);
printf("*****************************************\n");
getch();
return 0;
}
Estructuras Selectivas
Los pequeños programas que hemos diseñada hasta el momento, han sido del tipo secuencial, es decir,
una sentencia se ejecuta después de otra, hasta el final del programa.
Pero en la vida diaria muchas veces debemos elegir entre un camino y otro para llegar a nuestro destino.
Lo mismo pasa en programación, al realizar alguna actividad, nuestro programa debe ser capaz de elegir
uno u otro camino, a seguir dependiendo del valor de alguna condición evaluada.
Para ello C, dispone de tres tipos de 3 tipos de estructuras selectivas, la cuales son:
• Estructura Selectiva Simple
• Estructura Selectiva Doble
• Estructura Selectiva Múltiple
• ESTRUCTURA SELECTIVA SIMPLE
Funciona de la siguiente manera: se evalúa una condición, de ser cierta efectúa una acción, de lo
contrario, continúa con la ejecución normal del programa.
Su sintaxis es la siguiente:
If(condición) Acción;
O también:
If(Condición)
Acción;
Donde:
Condición: Es una expresión lógica que es evaluada por el compilador
Acción: es la Acción o Acciones que realizará el programa de resultar cierta la condición
NOTA: En C, no existe la sentencia “End If”, como en otros lenguajes de programación para indicar que
ha terminado el bloque de selección, sino que este se especifica con el punto y coma al final. Además
que, después de la condición NO se escribe un punto y coma. Si son varias acciones, estas deben ir
dentro de llaves {}, para indicarle al compilador que son un solo bloque de acciones que deben
ejecutarse.
Ejemplo 3.6
En una tienda se venden artículos de primera necesidad, a los cuales se les aplica un descuento del
20%, de la compra total, si esta es igual o mayor a $50. Diseñe un programa en C, que a partir del
importe total de la compra muestre lo que debe pagar el cliente.
#include <stdio.h>
#include <conio.h>
main()
{
float compra;
clrscr();
printf("Introduzca el valor de la compra:\n");
scanf("%f", &compra);
if(compra>=50)
compra=compra*0.8;
int num;
printf("Ingrese el n£mero:\n");
scanf("%d", &num);
if(num%2==0)
printf("ES PAR\n\n");
else
printf("ES IMPAR\n\n");
getch();
return 0;
}
Ejemplo 3.8
Diseñe un programa, que dada la nota de alumno, imprima en la pantalla un comentario sobre esa nota.
El criterio para los comentarios es el siguiente:
Si nota es mayor o igual a 9 “Excelente”
Si nota es mayor o igual a 8 “Muy Bueno”
Si nota es mayor o igual a 7 “Bueno”
Si nota es mayor o igual a 6 “Regular”
Si nota es menor que 6 “Necesita Mejorar”
#include <stdio.h>
#include <conio.h>
main()
{
float nota;
printf("Digite la nota, porfavor:\n");
scanf("%f", ¬a);
if(nota >= 9.0)
printf("EXCELENTE\n\n");
else
if(nota >= 8.0)
printf("MUY BUENO\n\n");
else
if(nota >= 7.0)
printf("BUENO\n\n");
else
if(nota >=6.0)
printf("REGULAR\n\n");
else
printf("NECESITA MEJORAR\n\n");
getch();
return 0;
}
Este ejemplo, muestra que C, permite hacer anidamientos, es decir, una selección dentro de otra, ya sea
del lado del cierto, o del falso o de ambos.
El lector, puede tratar de hacer sus propias conclusiones, además de buscar otras posibles solucione
para este mismo problema. Por ejemplo, ¿que pasaría si iniciamos con la condición del 6.0?. ¿Qué
pasaría si el usuario digita una neta negativa? ¿Cómo podrías darle solución a este problema?. Como
programadores, debemos hacernos muchas preguntas al momento de diseñar nuestros programas, ya
que estos No serán usados por nosotros, sino por otras personas.
Ejemplo 3.9
Dada el peso, la altura y el sexo, de unos estudiantes. Determinar la cantidad de vitaminas que deben
consumir estos estudiantes, en base al siguiente criterio:
>> Si son varones, y su estatura es mayor a 1.60, y su peso es mayor o igual a 150 lb, su dosis, serán:
20% de la estatura y 80% de su peso. De lo contrario, la dosis será la siguiente: 30% de la estatura y
70% de su peso.
>> Si son mujeres, y su estatura es mayor de a 1.50 m y su peso es mayor o igual a 130 lb, su dosis
será: 25% de la estatura y 75% de su peso. De lo contrario, la dosis será: 35% de la estatura y 65% de su
peso. La dosis debe ser expresada en gramos.
#include <stdio.h>
#include <conio.h>
main()
{
float peso, estatura, dosis;
char sexo;
printf("Introduzca el sexo del alumno(a)<H/M>:\n");
scanf("%c", &sexo);
printf("Peso:\n");
scanf("%f", &peso);
printf("La estatura es de:\n");
scanf("%f", &estatura);
if(sexo=='H' || sexo=='h')
{
if(estatura>1.60 && peso >=150)
{
dosis=(0.20*estatura)+(0.8*peso);
printf("La dosis de este alumno ser : %.2f gramos\n\n", dosis);
}
else
{
dosis=(0.3*estatura)+(0.7*peso);
printf("La dosis de este alumno sera %.2f gramos\n\n", dosis);
}
}
else
{
if(estatura>1.50 && peso >=130)
{
dosis=(0.25*estatura)+(0.75*peso);
printf("La dosis de esta alumna debe ser de %.2f gramos\n\n", dosis);
}
else
{
dosis=(0.35*estatura)+(0.65*peso);
printf("La dosis de esta alumna debe ser de %.2f gramos\n\n", dosis);
}
}
getch();
return 0;
}
• SELECCIÓN MÚLTIPLE
Como su nombre lo indica, permite seleccionar entre varios caminos para llegar al final. En este caso se
pueden elegir un camino o acción a ejecutar de entre varios posibles que se debe de evaluar, llamada
selector. Sintaxis:
switch(selector)
{
case Etiqueta A:
Acción A;
break;
case Etiqueta B:
Acción B;
break;
case Etiqueta n:
Acción n;
break;
default:
Excepción;
break;
}
En donde:
Selector: Variables, expresiones simples de tipo ordinal, (enteros y caracteres –int y char-)
Etiqueta: Tiene que ser del mismo tipo de datos de selecto. Estas deber ser constantes únicas y
diferentes de otras.
Excepción: Es opcional.
Ejemplo 3.10
Diseñe un programa en C, que dado un número del 1 al 3, muestre en pantalla y en letras, el mismo
número:
#include <stdio.h>
#include <conio.h>
main()
{
int n;
clrscr();
printf("El N£mero es:\n");
scanf("%d", &n);
switch(n)
{
case 0: puts("Cero");
break;
case 1: puts("Uno");
break;
case 2: puts("Dos");
break;
case 3: puts("Tres");
break;
default: puts("Dato No valido");
break;
}
getch();
return 0;
}
Cuestionario
Ejercicios:
1. Diseñe un programa que dados tres números indique cual es el mayor de ellos.
2. Diseñe un programa que dados tres números indique cual de ellos es el menor.
3. En un cine se exhiben, películas para mayores de edad, diseñe un programa que dada la edad,
indique si la persona puede o no ver la película.
4. En un supermercado, se realizan descuentos por las compras a partir de unas bolitas de colores.
Si el cliente saca una bolita color azul, tiene un descuento del 20%, si la bolita es roja, se aplica
un descuento del 30% y si saca una bolita color blanca, no se aplica ningún descuento. Diseñe
un programa que a partir del importe de la compra y el color de la bolita, muestre lo que debe
pagar dicho cliente.
5. Se procesan las notas de 5, alumnos, de las cuales se desea saber cual es el promedio de esas
5 notas, y cual fue la nota mayor y menor, además de imprimir al final el nombre y la nota de
cada alumno en forma de tabla.
6. un estudiante desea saber cuál fue su promedio en matemática I, para ello dispone de la
siguiente información: tiene 3 exámenes, con una ponderación del 20% cada uno y 2 laboratorios
con una ponderación del 30% cada uno. Diseñe un programa que dadas las notas calcule el
promedio del alumno y muestre en pantalla si el alumno esta reprobado o no (para aprobar esta
materia se requiere de una nota mayor o igual a 6.00).
7. En un estacionamiento, se cobra de la siguiente manera: los primeros 10 minutos son gratis, los
siguientes 30 minutos tiene un valor de $0.30 y la hora $0.60. diseñe un programa que reciba
tanto minutos como horas y muestre lo que debe cancelar el cliente. Tomando en cuenta que si
es Martes y Sábado se hace un descuento del 12.56% sobre el monto total.
8. Diseñe un programa que al introducir un dígito del 0 a 9, muestre como se lee.
9. Diseñe un pequeña calculadora que, al digitar un código realice una operación específica: si el
código es 1, la operación es la suma, si es 2, Resta. 3, multiplicación y 4 división. Si el usuario a
escrito otro código inválido, mostrar un mensaje de error.
10. Construya un programa que dado el salario de un empleado, permita aplicarle un aumento de
10% si el salario es inferior a $500, si es mayor se le aumentará un 8%. Luego debe aplicar una
retención del 0.96% en concepto de Renta a ambos casos.
11. Se desea calcular el sueldo de un trabajador, a partir de las horas trabajadas en la semana y la
clase a la que pertenece: Trabajadores Clase “A”, se les paga $7 por hora. Trabajadores clase
“B”, se paga $5 por hora. Trabajadores clase “C”, se les paga $4 por hora y los de clase “D”, $3.5
por hora.
12. Un comerciante se dedica a la venta de sillas únicamente. Vende tres tipos de sillas: tipo A, tipo B
y Tipo C los precios son $5.00, $7.00 y $10.00 respectivamente. Por cada cinco sillas compradas
del tipo A, del tipo B o del tipo C los clientes reciben un descuento de 3%, 5% y 7%, las demás se
cobran a precio normal. Diseñe un programa que imprima en forma de factura, con el nombre,
precio unitario, precio total, nombre de la tienda, etc lo que debe cancelar cada cliente en
concepto de la compra.
Descubre donde está el error.
El siguiente código, es de un programa que a partir de una nota determina si un alumno esta o no
reprobado, y este puede presentar algunos errores de lógica, de sintaxis o de ejecución. ¿Puedes
descubrirlos y modificarlos?
#Include <stdio.h>
#incluide <conio.h>
main()
{
float nota;
printf(“Digite la nota:\n”)
scanf(“%f”, nota);
if(nota>=6.00)
printf(“Aprobado\n\n);
else
printf(Reprobado\n\n);
getch();
return 0;
}
Capítulo IV "Ciclos"
Introducción**
Es muy común encontrar en los programas operaciones que se deben ejecutar un número repetido de
veces en períodos más o menos espaciados. Si bien las instrucciones son las mismas, los datos sobre
los que operan varían. A nuestro alrededor, encontramos problemas que presentan esas características,
por ejemplo: el cálculo de la nota final de los estudiantes de Programación I, se realizará tantas veces
como alumnos hayan inscritos en dicha asignatura, el cálculo del salario de los empleados de una
empresa, etc. En estos casos la solución que se diseñe para un solo grupo de datos se debe repetir
tantas veces como sea necesario (de acuerdo al número de estudiantes y de empleados para los
ejemplos anteriores).
Los cálculos simples o la manipulación de pequeños conjuntos de datos se pueden realizar fácilmente a
mano, pero las tareas grandes o repetitivas son realizadas con mayor eficacia por una computadora, ya
que estas están especialmente preparadas para ello.
Para repetir varias veces un proceso determinado haremos uso de los ciclos repetitivos, a los cuales se
les conoce con el nombre de estructura repetitiva, estructura iterativa, lazo o bucle.
(Tomado de Los guiones de clase de Introducción a la Informática. Universidad de El Salvador. Año
2005)
En C, podemos encontrar tres tipos de ciclos:
• Entrada Asegurada (while)
• Ciclo Controlado Por Contador (for)
• Hacer Mientras (do.. while)
Este ultimo, no está lógicamente estructurado, por tanto no haremos mucho hincapié en él.
Conceptos Generales
Funcionamiento de Un Ciclo
Un ciclo, funciona de la siguiente manera: Evalúa una condición de resultar cierta, realiza una acción o
bloque de acciones, luego vuelve a evaluar la condición y si nuevamente resulta cierta, realiza la (s)
acción (es). Cuando la condición de cómo resultado falso, se sale del ciclo y continúa con la ejecución
normal del programa.
Acumulador:
Es una variable, que , como su nombre lo indica se encarga de acumular valores. Esto se vuelve muy útil,
por ejemplo, cuando queremos encontrar la suma de los números del 0 al 9, en el acumulador, vamos
guardando los valores de dichas cifras. Puede ser tanto real como entera. Su valor inicial, en la mayoría
de los casos es cero.
Contador:
Es una variable de tipo entero, que nos ayuda, en el programa a contabilizar el número de ejecuciones de
una misma acción, de un grupo de alumnos etc. Un acumulador tiene tres valores distintos:
• Valor Inicial: es el valor con el cual iniciamos nuestro contador. Generalmente es cero. Esta
asignación puede hacerse cuando se declara la variable.
• Valor Final: después de la ejecución del ciclo, el valor del contador, será distinto a su valor inicial,
este puede ser mayo o menor que el mismo, todo depende si fue una cuenta creciente o
decreciente.
• Valor de Cambio: Es el valor Constante, en el cual se irá incrementando nuestro contador, este
puede ser positivo o negativo; es decir, si la cuanta se realiza de manera ascendente o
descendente.
NOTA: el lector no debe confundirse entre las variables tipo acumulador y tipo contador, estas se
diferencian unas de otras en que: los contadores, su valor de cambio es una constante, ya que aumenta y
disminuyen en el mismo valor, mientras que los acumuladores su valor de cambio no es constante. Un
acumulador necesariamente lo inicializamos con cero (o al menos en la mayoría de los casos). Un
contador puede iniciar con cualquier valor.
Bandera:
Las variables tipo bandera son aquellas que sólo admiten dos valores: cierto o falso, true o false, hombre
o mujer... etc
Ciclo de Entrada Asegurada
La sintaxis es la siguiente:
while(condición)
Acción;
Funciona de la siguiente manera: primero evalúa la condición, si da como resultado cierta realiza la
acción, luego vuelve a evaluar la condición, si su resultado es falso, se sale del ciclo y continúa con la
ejecución del programa.
Hay que tener mucho cuidado, cuando trabajamos con ciclos, ya que podemos caer en un ciclo infinito,
es decir que nunca se sale de él. Lo cual no es un error de sintaxis sino de lógica. Por lo cual en las
acciones debemos siempre colocar algo que haga que se modifique el resultado de la condición, lo cual
puede ser una bandera, un contador o un acumulador.
Ejemplo 4.1
Diseñe un Programa que imprima los primeros 10 números.
#include <stdio.h>
#include <conio.h>
main()
{
int i=1; /*Declaramos nuestro contador con su Valor Inicial*/
while(i<=10) /*Mientras i sea menor o igual a 10:*/
{
printf("%d\t", i);/*Imprimir el valor de i*/
i+=1;/*Aumentar el contador en 1*/
}
getch();
return 0;
}
Ejemplo 4.2
Se desea conocer el promedio de los números mayores que cero, en una serie de números ingresados
por el usuario. De los cuales no se sabe la cantidad, haciendo uso de una bandera, diseñe un programa
en el cual el usuario ingrese los números que desee.
#include <stdio.h>
#include <conio.h>
main()
{
int i=0, sum=0, ban=1, n;
float prom;
while(ban==1)
{
printf("Ingrese un n£mero por Favor:\n");
scanf("%d", &n);
if(n>0)
{
i=i+1;
sum+=n;
}
printf("Desea Ingresar Otro N£mero? (Si=1 y No=0)\n");
scanf("%d", &ban);
}
prom=sum/i;
printf("************************************************************\n");
printf("*** El Promedio de los numeros mayores que cero es: %.2f ***\n", prom);
printf("************************************************************\n");
getch();
return 0;
}
Ejercicio 4.3
En un salón se tienen las notas de 14, alumnos; de los cuales se desea saber cual fue el promedio de
todas las notas, cual fue la nota mayor y la nota menor. Así como la cantidad de aprobados en el curso
(Para Aprobar la asignatura se requiere de una nota mayor o igual a 6.0)
#include <stdio.h>
#include <conio.h>
main()
{
if(nota<menor)
menor=nota;
i=i+1;
suma=suma+nota;
}
prom=suma/14;
printf("El Promedio es %.2f\n\n", prom);
printf("El total de Aprobados es %d\n", j);
printf("La Mayor nota fue %.2f\n", mayor);
printf("%.2f corresponde a la nota menor\n", menor);
getch();
return 0;
}
ejemplo4.5
Diseñe un programa en C, que calcule las compras totales, realizadas por un grupo de 20 amas de casa.
Luego con esa información obtenga la media.
#include <stdio.h>
#include <conio.h>
main()
{
int i;
float compra, desvia, prom, varinza, sum=0;
for(i=1; i<=10; i++)
{
printf("Ingrese la cantidad que gast¢ la ama de casa %d:\n", i);
scanf("%f", &compra);
while(compra<0)
{
Cabe, mencionar que, en el ciclo for, podemos hacer cuentas decrecientes, es decir asignarle un valor
grande a nuestra variable de control y luego irla disminuyendo hasta un valor determinado.
Ejemplo 4.6
En un cine, se tienen 3 diferentes clases de boletos. Se pide que diseñe un programa en el cual:
a) se lea el precio de las 3 clase de boletos
b) Se lea el numero de boletos vendidos de cada tipo
c) Calcular cual boleto es el que se vendió menos
d) El total recaudado en taquilla
Además se sabe que durante el día se realizaron un total de n ventas.
#include <stdio.h>
#include <conio.h>
main()
{
float preciob1, preciob2, preciob3, sum=0, sum1=0, sum2=0, sum3=0;
int n, i, boletos1, boletos2, boletos3, boleto;
clrscr();
printf("\t\tBIENVENIDO(A)\n\n\n");
printf("Ingrese el precio de los boletos 1:\n");
scanf("%f", &preciob1);
while(preciob1<0)
{
printf("ERROR\n");
scanf("%f", &preciob1);
}
printf("Ingrese el precio de los boletos 2:\n");
scanf("%f",&preciob2);
while(preciob2<0)
{
printf("ERROR\n");
scanf("%f", &preciob2);
}
printf("Ingrese el precio de los boletos 3:\n");
scanf("%f",&preciob3);
while(preciob3<0)
{
printf("ERROR\n");
scanf("%f", &preciob3);
}
printf("¨Cu ntas ventas se realizaron este d¡a?:\n");
scanf("%d", &n);
while(n<0)
{
printf("ERROR\n");
scanf("%d", &n);
}
for(i=1; i<=n; i++)
{
printf("Ingrese el Boleto:\n");
scanf("%d", &boleto);
switch(boleto)
{
case 1: printf("Ingrese la cantidad de boletos vendidos:\n");
scanf("%d", &boletos1);
sum1+=boletos1;
sum=sum+(boletos1*preciob1);
break;
case 2: printf("Ingrese la cantidad de boletos vendidos:\n");
scanf("%d", &boletos2);
sum2+=boletos2;
sum=sum+(boletos2*preciob2);
break;
case 3: printf("Ingrese la cantidad de boletos vendidos:\n");
scanf("%d", &boletos3);
sum3+=boletos3;
sum=sum+(boletos3*preciob3);
break;
default: printf("ERROR, Vuelva a intentarlo\n\n");
break;
}
}
clrscr();
if(sum3<sum2 && sum3<sum1)
printf("Los Boletos que se vendieron menos fueron los boletos numero UNO\n\n");
if(sum2<sum3 && sum2<sum1)
printf("Los Boletos que se vendieron menos fueron los boletos numero DOS\n\n");
if(sum1<sum2 && sum1<sum3)
printf("Los Boletos que se vendieron menos fueron los boletos numero TRES\n\n");
printf("El total recaudado en taquilla, durante este dia fue: %.2f\n\n", sum);
getch();
return 0;
}
La diferencia fundamental, entre el ciclo while y do...while, es que en este ultimo, las sentencias se
realizarán por lo menos una vez, en cambio, con while, solo se cumplirán mientras se cumpla la
condición, lo cual puede ser nunca.
Ejemplo 4.7
Programa que determina si un año es bisiesto o no. Y un año es bisiesto si es múltiplo de cuatro, pero
excluyendo aquellos que son múltiplos de 100 pero no de 400
#include <stdio.h>
#include <conio.h>
void main()
{
int anio;
char respuesta;
printf("\n\n\nINICIO DEL PROGRAMA\n\n\n");
printf("\n\nEl programa te pide un anio y te dice exactamente si es bisiesto o no");
do
{
/*ENTRADA DE DATOS*/
printf("\n\nIntroduzca un anio determinado ");
scanf("%d",&anio);
/*PROCESO Y SALIDA DE DATOS*/
if ((anio%4==0 && anio%100!=0)||(anio%400==0)) printf("\n\nEl anio es bisiesto");
else printf("\n\nEl anio no es bisiesto");
printf("\n\nDesea introducir mas datos\n\n");
respuesta=getch();
} while(respuesta=='S' || respuesta=='s');
printf("\n\n\nFIN DEL PROGRAMA\n\n\n");
}
NOTA: este código ha sido tomado de “Practicas de Programación en C”, de Fernando Muñoz Ledesma.
Practica 3, ejercicio 5.
Cuestionario
1. ¿qué es y cómo funciona un ciclo?
_______________________________________________________________________
_____________________________________________
2. Cuál es la diferencia entre un contador y un
acumulador:_____________________________________________________________
________________________________________________
3. ¿cuál es la mejor manera de validar
datos?:_________________________________________________________________
________________________________________________
4. ¿cómo se evita un ciclo
infinito?:________________________________________________________________
__________________________________________________
5. ¿Qué diferencia existe entre un ciclo de entrada asegurada y el do...
while?:______________________________________________________
Descubre donde está el error.
El siguiente código muestra la serie:
1^2+2^2+3^2....n^2
en el cual hay errores de lógica, de sintaxis o hasta de ejecución, puedes descubrirlos y corregirlos?
#include <stdio.h>
#include <conio.h>
main()
{
int n i, x, sum=0;
printf("Inrtroduzca el valor de n:\n");
scanf("%d", &n);
while(n<0)
{
printf("Error, vuelva a digitar el valor de n:\n");
scanf("%d", n);
}
for(i=1; i<=n, i++)
x=i*i;
sum+=n;
printf("El valor de la suma es:%d\n\n", sum)
getch();
return 0;
}
Ejercicios
1. Se desea conocer la suma de los números enteros, positivos menores que n, el cual es un dato
dado por el usuario.
2. Muestre un programa en c, que imprima en pantalla los números desde un valor inicial, hasta un
valor final, ingresados por el usuario, tanto en forma descendente como ascendente.
3. Diseñe un programa que imprima la serie de Fugonacci, así: 0 1 1 2 3 5 8 13.... hasta un número
n dado por el usuario.
4. Calcule el promedio de edades de un grupo de estudiantes, de los cuales no se conoce la
cantidad.
5. Diseñe un programa que obtenga, la calificación mayor y la calificación menor, de un grupo de 40
estudiantes, además de los nombres de dichos alumnos.
6. En un país hubieron elecciones para elegir al presidente. El país consta de 7 provincias o
regiones, de las cuales se han levantado actas que contiene el total de votos obtenidos por los 4
partidos políticos en dicha región. Diseñe un programa en c, que lea las actas de las 7 provincias,
muestre que partido ganó las elecciones y en caso de empate, lo especifique con un mensaje.
7. en un supermercado, hay 3 departamentos (de ropa, comestibles y perfumería), en lo cuales se
realizan un descuento de 5%, 3.5% y 8% respectivamente, por las compras totales mayores de
$100.00. diseñe un programa que dado el monto de la compra, realice los descuentos
pertinentes por departamento, le indique al usuario a cuanto asciende su nuevo monto e indique,
cuanto fue lo recaudado al final del día.
8. La Empresa, el porvenir s.a de c.v desea conocer lo que debe pagar en concepto de horas extras
aun grupo de n empleados. Se sabe que una hora extra diurna, se paga el doble que una hora
normal. Y una hora extra nocturna se paga el doble de una hora normal más el 25%. Además que
todos los empleados tiene sueldos diferentes, muestre el nuevo sueldo de cada uno de ellos y lo
que tendrá que pagar la empresa en concepto de horas extra.
9. Una compañía de teléfonos, cobra $0.03 por minuto la llamada nacional local, $0.06 por la
llamada de larga distancia nacional y $0.10 la llamada de larga distancia internacional. Diseñe un
programa que calcule las facturas mensuales de los clientes, sabiendo que, si las llamadas
fueron realizadas por la mañana tienen un doble valor, y si los 10 primeros minutos de llamadas
locales son gratis, en cualquier horario.
Capitulo V: Funciones en C
La modularización, es una técnica usada por los programadores para hacer sus códigos más cortos, ya
que consiste en reducir un gran problema complejo, en pequeños problemitas más sencillos,
concentrándose en la solución por separado, de cada uno de ellos.
En C, se conocen como funciones aquellos trozos de códigos utilizados para dividir un programa con el
objetivo que, cada bloque realice una tarea determinada.
En las funciones juegan un papel muy importe las variables, ya que como se ha dicho estas pueden ser
locales o globales.
Variables Globales: Estas se crean durante toda la ejecución del programa, y son globales, ya que
pueden ser llamadas, leídas, modificadas, etc; desde cualquier función. Se definen antes del main().
Variables Locales: Estas, pueden ser utilizadas únicamente en la función que hayan sido declaradas.
La sintaxis de una función es la siguiente:
Tipo_de_datos nombre_de_la_funcion(tipo y nombre de argumentos)
{
acciones
}
donde:
• Tipo_de_datos: Es el tipo de dato que devolverá esa función, que puede ser real, entera, o tipo
void(es decir que no devolverá ningún valor).
• Nombre_de_la_funcion: Es el identificador que le damos a nuestra función, la cual debe cumplir
las reglas que definimos en un principio para los identificadores.
• Tipo y nombre de argumentos: son los parámetros que recibe la función. Los argumentos de
una función no son más que variables locales que reciben un valor. Este valor se lo enviamos al
hacer la llamada a la función. Pueden existir funciones que no reciban argumentos.
• Acciones: Constituye el conjunto de acciones, de sentencias que cumplirá la función, cuando
sea ejecutada. Entre ellas están:
1. Asignaciones
2. Lecturas
3. Impresiones
4. Cálculos, etc
Una función, termina con la llave de cerrar, pero antes de esta llave, debemos colocarle la instrucción
return, con la cual devolverá un valor específico. Es necesario recalcar que si la función no devuelve
ningún valor, es decir, es tipo void, no tiene que ir la sentencia return, ya que de lo contrario, nos dará un
error.
Pero, es válido que nos hagamos la siguiente pregunta:
¿Cómo es que funcionan los Subprogramas?
A menudo, utilizamos el adjetivo de “Subprogramas”, para referirnos a las funciones, así que, el lector
debe familiarizarse también con este término.
Los subprogramas se comunican con el programa principal, que es el que contiene a las funciones,
mediante parámetros, que estos pueden ser: Parámetros Formales y Parámetros Actuales.
Cuando se da la comunicación los parámetros actuales son utilizados en lugar de los parámetros
formales.
Paso de Parámetros
Existen dos formas de pasar parámetros, las cuales son:
Paso por Valor
También conocido como parámetros valor. Los valores se proporcionan en el orden de cálculos de
entrada.
Los parámetros se tratan como variables locales y los valores iniciales se proporcionan copiando los
valores de correspondientes argumentos.
Los parámetros formales-Locales de una función reciben como inicilaes los valores de los parámetros
actuales y con ellos se ejecutan las acciones descritas en el subprograma.
Ejemplo:
A=5;
B=7;
C=proc1(A, 18, B*3+4);
Proc1(X, Y, Z)
Explicación:
Donde, se encuentra c, se está llamando la función, denominada proc1, en la cual se están enviando
como parámetros el valor de A, que es cinco; el cual es recibido por la variable X, en la definición de la
función proc1; en la misma función, Y tendrá el valor de 18; por que ese es el valor del parámetro formal,
mientras que Z, tendrá un valor inicial de 25, ya que ese es el resultado del tercer parámetro que resulta
ser una expresión aritmética.
Funciones Definidas Por El Usuario en C
Una función, como ya se ha dicho, es un bloque de código dentro del programa que se encarga de
realizar una tarea determinada. Por lo tanto un programa en c debe constar de una o más funciones, y
por su puesto no puede faltar la función principal main().
Un viejo adagio dice: Separa y vencerás, lo cual se acopla perfectamente cuando tenemos un programa
que es bastante grande; podemos separarlos en pequeños subprogramas (funciones), y concentrarnos
en la solución por separados de cada uno de ellos y así resolver un gran problemas, en unos cuantos
problemitas más pequeños.
Si un programa, está constituido por más de una función, las llamadas a la misma, pueden realizarse
desde cualquier parte del programa, y la definición de ellas debe ser independiente unas de otras.
Por lo tanto sería un grave error el tratar de definir una función dentro de otra.
Una función puede ser llamada desde cualquier parte del programa no sólo una vez, y cuando es
llamada, empieza a ejecutar las acciones que están escritas en código.
Para mayor comodidad del lector vamos a ver varios ejemplos, del uso de funciones y a medida que
vayamos avanzando se volverán más complejos.
El orden será el siguiente:
1. Funciones que no devuelven ningún valor
2. Funciones que devuelven un valor entero
3. Funciones que devuelven un valor Real
4. Funciones combinadas
5. Funciones en las que usamos Menú.
{
int a, b;
printf("Ingrese el valor de a:\n");
scanf("%d", &a);
printf("Ingrese el valor de b:\n");
scanf("%d", &b);
suma(a,b); /*Llamado de la funci¢n*/
mayor(a,b); /*Unicamente el nombre de la funci¢n y de los par metros*/
getch();
return 0;
}
void suma(int a, int b) /*Definici¢n de la funci¢n*/
{ /*Abrimos llaves al inicio de la definici¢n*/
int sum; /*Declaraci¢n de las variables locales*/
sum=a+b;
printf("El valor de la suma es %d:\n\n", sum);
} /*Fin de la funci¢n suma*/
void mayor(int a, int b)
{
if(a==b)
printf("Son iguales\n\n");
else
{
if(a>b)
printf("El valor de a es mayor que el de b\n\n");
else
printf("El valor de b es mayor que el de a\n\n");
}
}
Definición de la Función
La función ha sido declarada, ha sido llamada y por lo tanto deber haber sido definida. Lo cual consta de
dos partes, las cuales son:
1. La Primera Línea
Que como su nombre lo indica, es la primera línea de la definición de la función y con ella le indicamos al
compilador que está en presencia de una función. Su formato es el siguiente:
Tipo_de_dato nombre_de_la_función (tipo y nombre de los argumentos)
2. Cuerpo de la función
Se inicia con una llave “{“, y en ella, se pueden realizar asignaciones, cálculos, impresiones, así como la
declaración de las variables locales. Puede estar constituidas por estructuras secuenciales, selectivas,
iterativas, anidamientos, se pueden llamar otras funciones, etc; finaliza con “}”. Puede devolver uno o
ningún valor.
Ejemplo 5.2
Diseñe un Programa en C, que Dado un número entero y mayor que cero, Determine si es o no un
número Primo. Ojo, los números primos sólo son divisibles por el mismo y por la unidad (1).
#include <stdio.h>
#include <conio.h>
void primo (int numero);
main()
{
int numero, ban=1;
clrscr();
while(ban==1)
{
printf("Introduzca el n£mero por favor:\n");
scanf("%d", &numero);
while(numero<0)
{
printf("ERROR, el valor del n£mero debe ser mayor que cero\n");
scanf("%d", &numero);
primo(numero);
Si no es así, entonces la función se debe declarar. La declaración simplemente maneja el tipo de dato
que la
función regresa y el tipo de parámetros usados por la función.
Es una práctica usual y conveniente escribir el prototipo de todas las funciones al principio del programa,
sin
embargo esto no es estrictamente necesario.
Para declarar un prototipo de una función se indicara el tipo de dato que regresará la función, el nombre
de la
función y entre paréntesis la lista del tipo de los parámetros de acuerdo al orden que aparecen en la
definición de la
función. Por ejemplo:
int longcad(int n); Lo anterior declara una función llamada longcad que regresa un valor entero y acepta
otro valor entero como parámetro.
(Tomado de “Manual de C” de Héctor Tejada Villela)
Ejemplo 5.3
Diseñe un programa, que dado un número entero y mayor que cero, muestre su factorial. (El factorial de 5
es 120; 5x4x3x2x1=120)
#include <stdio.h>
#include <conio.h>
int factorial (int num);
main()
{
int num, ban=1;
clrscr();
while(ban==1)
{
printf("Ingrese el valor del n£mero por favor:\n");
scanf("%d", &num);
while(num<0)
{
printf("ERROR, el valor del n£mero debe ser mayor que cero:\n");
scanf("%d", &num);
}
printf("El valor del factorial es %d\n\n", factorial (num));
printf("¨Desea Realizar otro calculo?Si=1 y No=0\n");
scanf("%d", &ban);
}
getch();
return 0;
}
int factorial (int num)
{
int sum=1, i;
for(i=2; i<=num; i++)
{
sum=sum*i;
}
return (sum);
}
Explicación:
Quizá, lo único nuevo, e importante de explicar, radica en la llamada y la definición de la función. Cuando
una función nos devolverá un valor entero, al identificador de dicha función debe precederle el tipo de
dato. En el lugar, donde llamamos la función, es que aparecerá el valor que nos devuelva, como valor de
retorno. En nuestro ejemplo, en una impresión. Y al momento de definirla, no se nos debe olvidar,
colocarle la sentencia return(); ya que, mediante esta declaratoria, está retornando el valor calculado.
Pero, que sucede cuando se está trabajando, con valores bastante grandes, al utilizar solamente el int, se
producirá un error lógico; ya que como valor de retorno podría ser un cero o una cifra negativa. Por tanto
debemos usar el tipo de dato “long int”.
Ejemplo 5.4
Diseñe un programa, que dada una cifra entera y mayor que cero, sea elevada a una potencia introducida
por el usuario, la cual. (Ejemplo: 5^2=25).
#include <stdio.h>
#include <conio.h>
long int potencia (int base, int exponente);
main()
{
int base, exponente;
clrscr();
printf("La Base es:\n");
scanf("%d", &base);
while (base<0)
{
printf("ERROR, el dato debe ser mayor que cero:\n");
scanf("%d", &base);
}
printf("El Exponente es:\n");
scanf("%d", &exponente);
printf("%d ^ %d es %ld\n\n", base, exponente, potencia(base,exponente));
getch();
return 0;
}
long int potencia (int base, int exponente)
{
long int sum=0, i,x;
for(i=1; i<exponente; i++)
{
x=base*base;
sum=sum+x;
}
return (sum);
}
Este método es un poco complejo y puede realizarse de manera más fácil, haciendo uso de las funciones
predefinidas en C, de las cuales hablaremos a continuación.
El lenguaje c, cuenta con una serie de funciones de bibliotecas que realizan operaciones y cálculos de
uso frecuente.
Para acceder a una función, se realiza mediante el nombre seguido de los argumentos que le servirán a
la función a realizar la tarea específica.
Nombre(arg1, arg2,...argn);
*Funciones Matemáticas
Para acceder a ellas, se debe colocar la directiva #include <math.h> en el encabezado del programa.
Función (Sintaxis) Tipo de Dato Propósito
acos(d) double Devuelve el arco coseno de d
asin(d) double Devuelve el arco seno de d
atan(d) double Devuelve el arco tangente de d
atan(d1, d2) double Devuelve el arco tangente de d1/d2
ceil(d) double Devuelve el valor redondeado por exceso, al
siguiente entero mayor
cos(d) double Devuelve el coseno de d
cosh(d) double Devuelve coseno hiperbólico de d
exp(d) double Eleva a la potencia d
fabs(d) double Devuelve el valor absoluto de d
floor(d) double Devuelve el valor redondeado por defecto al
entero menor más cercano
log(d) double Devuelve el logaritmo natural de d
log10(d) double Devuelve el lo. (base10) de d
pow(d1, d2) double Devuelve d1 elevado a la potencia d2
sin(d) Double Devuelve el seno de d
sinh(d) double Seno hiperbólico de d
sqrt(d) double Raíz cuadrada de d
Tan(d) double Devuelve la tangente de d
tanh(d) double Devuelve la tangente hiperbólica de d
Hay muchas otras funciones, pero para ahondar más, debes saber cuál es la versión de C, instalada en
tu máquina y así verificar cuáles funcionan correctamente; pero por lo general, estas funciones son muy
estándar para la mayoría de compiladores.
A continuación, pasaremos a desarrollar una serie de ejercicios, en los cuales haremos uso de la
funciones predefinidas en c, así como la modularización, es decir; el uso de funciones definidas por el
usuario.
Ejemplo 5.5
Se desea conocer el resultado de las siguientes operaciones:
1. √a+b
2. |a-b|
3. ab
Las variables a y b, son de tipo real, y pueden ser positivas o negativas.
#include <stdio.h>
#include <conio.h>
#include <math.h>
double raiz(float a, float b);
double valor_absoluto(float a, float b);
double exponente (float a, float b);
main()
{
float a, b;
clrscr();
printf("\t\tBIENVENIDO\n\n");
printf("Ingrese el valor de a, por favor:\n");
scanf("%f", &a);
printf("Ahora el valor de b:\n");
scanf("%f", &b);
printf("El resultado de la ra¡z cuadrada de %.2f + %.2f es %.2f\n\n", a,b,raiz(a,b));
printf("|%.2f-%.2f| es igual a %.2f\n\n", a,b,valor_absoluto(a,b));
printf("%.2f^%.2f es igual a %f\n\n", a,b,exponente(a,b));
getch();
return 0;
}
double raiz(float a, float b)
{
float x;
double y;
x=a+b;
y=sqrt(x);
return (y);
}
double valor_absoluto(float a, float b)
{
float x;
double y;
x=a-b;
y=fabs(x);
return (y);
}
double exponente (float a, float b)
{
double x;
x=pow(a,b);
return (x);
}
Supongo que, este ejemplo no requiere mayor explicación. Pero me gustaría que el lector, comprenda la
gran cantidad de usos que podemos darle, a aquellas funciones matemáticas, junto con las funciones
definidas por el usuario, esta es una gran ayuda, ya que ¿se imaginan la cantidad de código que
deberíamos colocar, para determinar cosas tan elementales como el valor absoluto?; con estas funciones
matemáticas, C, nos ahorra mucho trabajo y código.
4. Funciones Combinadas
A continuación veremos un ejemplo de un programa en el cual utilizamos dos funciones de diferente tipo
de dato.
Ejemplo 5.5
El valor del número e se puede aproximar sumando n términos de la serie: e = 1 + 1/1! + 1/2! + 1/3! + ...
Escribir un programa que solicite el número de términos de la serie a sumar e informe del valor
aproximado de e. Téngase en cuenta que el termino i de la anterior serie se obtiene dividiendo por (i-
1). (La exclamación es el factorial).
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main(void)
{
int numero;
char respuesta;
do {
do {
printf("\n\nIntroduzca un numero de terminos de la serie: ");
scanf("%d",&numero);
} while (numero<0);
calculodatos(numero);
for (i=1;i<=numero;i++) {
e=e+1./factorial(i);
}
printf("\n\nEl valor de e para %d terminos es %f.",numero,e);
}
clrscr();
while(ban==1)
{
printf("\t\tBIENVENIDO/A\n\n");
printf("Introduzca el valor del angulo, por favor:\n");
scanf("%f", &angulo);
printf("¨Que desea hacer?:\n\n");
printf("********************************************\n");
printf("**** 1. seno del angulo ****\n");
printf("**** 2. coseno del angulo ****\n");
printf("**** 3. tangente del angulo ****\n");
printf("********************************************\n");
scanf("%d", &opcion);
while(opcion<0 || opcion>3)
{
printf("ERROR, la opcion debe estar entre 0 y 3:\n");
scanf("%d", &opcion);
}
clrscr();
switch(opcion)
{
case 1: seno (angulo);
break;
case 2: coseno (angulo);
break;
case 3: tangente (angulo);
break;
}
printf("¨Hay mas datos? (si=1 y no=0)\n");
scanf("%d",&ban);
}
getch();
return 0;
}
void seno (float angulo)
{
float y;
y=sin (angulo);
printf("El seno de %f es %f\n\n", angulo, y);
}
void coseno (float angulo)
{
float y;
y=cos(angulo);
printf("El coseno de %f es %f\n\n", angulo, y);
}
void tangente (float angulo)
{
float y;
y=tan(angulo);
printf("La tangente de %f es %f\n\n", angulo, y);
getch();
}
Cuestionario
1. Mencione y explique, las parte en las que se componen las funciones definidas por el usuario en
C:____________________________________________________________________________
______________________________________________________________________________
_______________________________________________________________
2. ¿Cuál es la diferencia entre las funciones predefinidas en c y las funciones definidas por el
usuario?
______________________________________________________________________________
______________________________________________________________________________
_____________________________________
3. ¿En que consiste el paso de
parámetros?:___________________________________________________________________
______________________________________________________________________________
___________________________________________
4. ¿Cuál es la diferencia entre parámetros formales y
actuales?:_____________________________________________________________________
______________________________________________________________________________
______________________________________________________________________________
_________________________________
5. En que se diferencias las variables locales a las
globales:______________________________________________________________________
______________________________________________________________________________
______________________________________________________________________________
_________________________________
Ejercicios
1. Realice una pequeña calculadora, utilizando funciones
2. Diseñe un programa que permita calcular la serie ½ + 1/3 + ¼+ ... 1/n.
3. Diseñe un programa, que muestre el mayor y el menor de tres introducidos por el usuario.
4. Se desea conocer el logaritmo natural y el logaritmo base 10 de una serie de números. Así como
la suma de dichos valores
5. Se desea conocer la permutación de dos números distintos. Usando funciones. Diseñe un
programa que resuelva dicho problema. (NOTA: 5P3=5!/(5-3)!)
6. Se desea conocer la equivalencia de dólares a colones (un dólar = 8.75 de colón), la equivalencia
de un kilogramos a libras (1kg=2.2lb) y la conversión de kilómetros a millas (1km=0.62millas).
realice esta solución mediante un menú.
7. Calcule lo que debe pagar cada cliente en un almacén; si por cada compra el cliente tiene
derecho a sacar un papelito, y dependiendo del color, se efectúan diferentes descuentos. Si el
color es blanco, se realiza un descuento del 2.63% sobre la cuenta, si es verde, un descuento de
4.85% y si es rojo, un descuento de 5.02%. se sabe además que si es día lunes o viernes, el
porcentaje de descuento es el doble.
8. El seno de un ángulo, puede aproximarse, de la siguiente manera: sin(x) = x - x^3/3! + x^5/5! -
x^7/7! + ..., determine este valor, y usando la función sin(d), luego muestre la diferencia entre
estos valores.
9. En una empresa de electricidad, se cobrar las facturas correspondientes al consumo de kwh, de
la siguiente manera: los primeros 100 kwh, se cobran $2.5, lo siguientes 200 kwh, son a $5.00,
los 300kwh, siguientes, son cobrados a razón de $7.5, los kwh siguientes se cobran a $7.00.
diseñe un programa que permita determinar lo que debe pagar un grupo de clientes al final del
mes.
10. En una empresa de repuestos de automóvil, poseen 10 tipos de repuestos identificados con los
números de 1 al 10. durante la semana se realizan diferentes ventas de los repuestos. Se desea
saber la cantidad de repuestos que se deben comprar, para actualizar el inventario. El cual se
realiza cada cinco días, y se procede de la siguiente manera: cada día se contabilizan el total de
facturas, en las cuales se muestran la cantidad de artículos vendidos así como el total de la
venta. Con esos datos, indique al usuario cuantos y de que tipo, son los repuestos que se deben
comprar así como la ganancia.
El tipo puede ser cualquiera de los ya conocidos y el tamaño indica el número de elementos del vector
(se debe indicar entre corchetes [ ]). En el ejemplo puedes observar que la variable i es utilizada como
índice, el primer for sirve para rellenar el vector y el segundo para visualizarlo. Como ves, las posiciones
van de 0 a 9 (total 10 elementos).
(Tomado de “Introducción al lenguaje de programación de C/C++”. Sergio Pacho)
Ejemplo:
Constante Simbólica
Hace más sencillo o más fácil modificar un programa que utiliza arreglos. Ya que todas las referencias al
tamaño del arreglo pueden ser alteradas, cambiando el valor de la constante simbólica.
Ejemplo 6.1
Diseñe un programa que lea un vector de 10 posiciones, luego determine si la quinta posición es positiva,
si la primera posición es negativa y si la ultima posición es cero.
#include <stdio.h>
#include <conio.h>
#define N 10
main()
{
float x[N];
int i;
for(i=0; i<N; i++)
{
printf("Ingrese el valor %d:\n", i);
scanf("%f", &x[i]);
}
if(x[4]>0)
{
printf("La quinta Posici¢n es Positiva\n\n");
}
if(x[0]<0)
{
getch();
return 0;
}
Explicación
En este ejemplo estamos haciendo uso de la constante simbólica, de cuyos beneficios ya se habló. La
definimos con 10 posiciones, recordando que C, empieza a contar desde cero. Luego definimos el vector
llamado x, como punto flotante, y ojo, que éste va acompañado de su dimensión. Luego haciendo uso de
un for, empezamos a llenar el vector. Luego preguntamos si la posición cuatro es positiva. El lector se
preguntará el por que, la instrucción es x[4] y no x[5], ya que; lo que queremos es averiguar, si la posición
cinco es la positiva. Pues bien, la posición identificada con el número cuatro, es en efecto la que contiene
el quinto número. Así:
Esta es una versión gráfica, de lo que sucedería al llenar nuestro vector con los valores indicados. Como
podemos ver, C empieza a enumerar las casillas desde el cero, hasta el 9. totalizando de esa manera 10
posiciones. Así que, x[4]=8.
Es por ello, que el for, lo inicializamos con cero, hasta un valor menor que el de la constante, ya que de lo
contrario nos daría un error.
Una particularidad con los vectores de tipo char (cadena de caracteres), es que deberemos indicar en
que elemento se encuentra el fin de la cadena mediante el carácter nulo (\0). Esto no lo controla el
compilador, y tendremos que ser nosotros los que insertemos este carácter al final de la cadena. Por
tanto, en un vector de 10 elementos de tipo char podremos rellenar un máximo de 9, es decir, hasta
vector[8].Si solo rellenamos los 5 primeros, hasta vector[4], debemos asignar el carácter nulo a
vector[5]. Es muy sencillo: vector[5]='\0';
Ahora veremos un ejemplo de como se rellena un vector de tipo char. Podemos ver que en el for se
encuentran dos condiciones:
1.-Que no se hayan rellenado todos los elementos (i<19).
2.-Que el usuario no haya pulsado la tecla ENTER, cuyo código ASCII es 13.
(cadena[x-i]!=13).
El lector, debe preguntarse, que pasaría si existen dos valores exactamente iguales que sean los valores
máximos y que por ende, estén en diferentes posiciones, que solución le darías como programador?...
este tipo de preguntas debe hacerse siempre que ha finalizado un programa, y nunca dejar nada sin
resolver, por que recordemos que los programas que diseñamos son para que otras personas los usen.
Matrices
Las matrices se declaran de forma análoga, con corchetes independientes para cada
subíndice. La forma general de la declaración es:
tipo nombre[numero_filas][numero_columnas];
donde tanto las filas como las columnas se numeran también a partir de 0. La forma de
acceder a los elementos de la matriz es utilizando su nombre, seguido de las expresiones
enteras correspondientes a los dos subíndices, entre corchetes.
En C tanto los vectores como las matrices admiten los tipos de las variables escalares
(char, int, long, float, double, etc.),
Las matrices en C se almacenan por filas, en posiciones consecutivas de memoria. En
cierta forma, una matriz se puede ver como un vector de vectores-fila. Si una matriz tiene N
filas (numeradas de 0 a N-1) y M columnas (numeradas de 0 a la M-1), el elemento (i, j)
ocupa el lugar:
posición_elemento(0, 0) + i * M + j
A esta fórmula se le llama fórmula de direccionamiento de la matriz.
(Tomado de “Aprenda Lenguaje ANSI C como si estuviera en Primero”. Universidad de Navarra).
Ejemplo 6.3
Diseñe un programa que lea un matriz de 6*6 y luego determine la suma de cada una de las filas y la
almacene en un vector llamado suma.
#include <stdio.h>
#include <conio.h>
#define F 6
#define C 6
main()
{
int matriz[F][C], i,j, vector [F]={0,0,0,0,0,0};
for(i=0; i<F; i++)
for(j=0; j<C; j++)
{
printf("Ingrese el elemento F=%d y Columna=%d de la matriz:\n", i,j);
scanf("%d", &matriz[i][j]);
vector[i]=vector[i]+matriz[i][j];
}
printf("La Matriz generada es:\n\n");
for(i=0; i<F; i++)
{
for(j=0; j<C; j++)
{
printf("*%d*", matriz[i][j]);
}
printf("\n");
}
printf("Y el vector suma de las filas es:\n\n");
for(i=0; i<F; i++)
printf("%d\t", vector[i]);
getch();
return 0;
}
Creo que no hay mucho por explicar, el uso de una matriz en C, es bastante parecido al de un vector,
pero con las diferencias que en un vector tenemos únicamente una dimensión y en las matrices tenemos
dos.
A continuación desarrollaremos un ejemplo, el cual es bastante significativo para mi, ya que fue mi primer
proyecto, cuando cursé la Materia de Programación I en la Universidad, espero que les guste:
Ejemplo 6.4
Escriba un programa que visualice un cuadro mágico de orden impar N, comprendido entre 3 y 11; el
usuario debe elegir el valor de N. Un cuadro mágico se compone de números enteros entre 1 y N, la
suma de los números que figuran en cada fila, columna y diagonal son iguales.
Ejemplo:
8 1 6
3 5 7
4 9 2
}
if(f<0)
f=dim-1;
if(c==dim)
c=0;
if(cuadrado[f][c]!=0)
{
c--;
f=f+2;
}
cuadrado[f][c]=i;
}
printf("La constante m gica es: %d\n\n", comprueba (cuadrado, dim));
impresion(cuadrado, dim);
getch();
return 0;
}
int comprueba (int cuadrado [N][M], int dim)
{
int magic=1, f,c, consmagic, sum=0, i, j=-1;
consmagic=((dim*dim*dim)+dim)/2;
for(f=0; f<dim; f++)
{
sum=0;
for(c=0; c<dim; c++)
sum=sum+cuadrado[f][c];
if(sum!=consmagic)
magic=0;
}
for(c=0; c<dim; c++)
{
sum=0;
for(f=0; f<dim; f++)
sum=sum+cuadrado[f][c];
if(sum!=consmagic)
magic=0;
}
sum=0;
for(i=0; i<dim; i++)
sum=sum+cuadrado[i][i];
if(sum!=consmagic)
magic=0;
sum=0;
for((i=dim-1); i>=0; i--)
{
j=j+1;
sum=sum+cuadrado[i][j];
}
if(sum!=consmagic)
magic=0;
if(magic==0)
consmagic=0;
return (consmagic);
}
void impresion (int cuadrado[N][M], int dim)
{
int f, c;
printf("\tEL CUADRO GENERADO ES:\n\n");
for(f=0; f<dim; f++)
{
for(c=0; c<dim; c++)
printf("*%d*", cuadrado[f][c]);
printf("\n");
}
}
Cuestionario
1. ¿Qué es una array o arreglo?
___________________________________________________________________
___________________________________________________________________
___________________________________________________________________
__________
2. ¿Cuál es la diferencia entre un vector y una matriz?
___________________________________________________________________
___________________________________________________________________
_________________________________
3. ¿Cómo se define y se declara una función cuyos parámetros son vectores o
matrices?
___________________________________________________________________
___________________________________________________________________
______________________________
4. ¿Cuáles son los tipos de datos admitidos para los
arreglos?:___________________________________________________________
___________________________________________________________________
___________________________________________________________________
_____________________________
5. ¿Cuáles son las diferencias fundamentales entre un arreglo y una variable simple?
___________________________________________________________________
___________________________________________________________________
___________________________________________________________________
_______________________
Ejercicios
1. En una escuela se tiene el listado de 30 alumnos con sus respectivas notos, diseñe un programa
que muestre las notas de los alumnos que tuvieron una nota mayor que el promedio.
2. Diseñe un programa que dado un vector de magnitud X, busque y muestre la posición en la que
se encuentra un valor N, dentro del vector
3. Se tiene dos vectores A y B, diseñe una solución, en la cual, en un tercer vector se guarde la
multiplicación de los vectores A y B, y luego se impriman los tres vectores, uno a la par del otro,
en forma vertical
4. Diseñe un programa en C, en el cual guarde un vector de 100 posiciones, determine la media y la
desviación estándar.
Este informe debe ser seguido de un segundo en el cual se proporcione un análisis de las
infracciones por cuadrante. Para cada uno de los 4 cuadrantes mencionados, debe darse el numero
de infracciones y la multa promedio.
PARTE II:
MEMORIA DINÁMICA
Capítulo VII: Memoria Principal y Aritmética de Punteros
Al iniciar ésta segunda parte, vamos a comentar algunos aspectos que son de suma importancia y que el
lector debe conocer y manejar, como lo es el uso y funcionamiento de la memoria, dentro de la
computadora.
Debemos iniciar diciendo que, La Memoria Principal; se compone de un conjunto de celdas básicas
dotadas de una determinada organización.
Ya que, la Memoria, es el lugar donde se guardan datos y programas.
Tipos de Memoria
La Memoria RAM
Es aquella memoria que ‘se volatiliza’ al apagar el equipo. A mayor cantidad de RAM, más ventanas se
pueden abrir, más programas funcionando simultáneamente y menos bloqueos de la PC. Existen varios
tipos de RAM, según su forma de encapsulado.
MÓDULOS DIP (Dual Inline Package): eran chips de memoria de forma rectangular y chata. Presentaban
dos líneas de pines en sus laterales. Una muesca o punto sobre el chip indicaban cuál es la pata nº 1
para evitar colocar el chip al revés en el zócalo de la mother. Hoy no se utilizan memorias RAM en
formato DIP, pero sí todavía como caché en motherboards u otras tarjetas.
MÓDULOS SIP (Single Inline Package): se trataba de módulos de memoria RAM cuyos chips de
memoria se encontraban soldados sobre una pequeña placa de circuito impreso que hacía contacto con
la motherboard con una sola hilera de pines soldados en uno de sus bordes. Los pines calzaban en un
zócalo colocado en la mother.
MÓDULOS SIMM (Single Inline Memory Module): son módulos de memoria que también tienen una sola
hilera de pines. Una pequeña placa de circuito tiene soldada en una o ambas caras varios chips de
memoria. Estos módulos de memoria se presentan en dos versiones. Existen:
-SIMM de 30 pines: organizan la cantidad total de memoria en renglones de a 8 bits. (Mother 486)
-SIMM de 72 pines: organizan la cantidad total de memoria en renglones de a 32 bits. (Mother 486 o
Pentium)
MÓDULOS DIMM (Double Inline Memory Module): similares a los SIMM, aunque poseen 168 pines y
organizan la memoria en renglones de a 64 bits. Hay módulos DIMM de 168 pines para 16, 32, 64, 128,
256 y hasta 512 MBytes. (Mother Pentium o Pentium II en adelante).
MÓDULOS DDR (Double Data Rate Synchronous DRAM): esta tecnología transmite al doble de la
velocidad del bus del sistema. Estas memorias se presentan en forma de módulos de 184 contactos o
pines.
Zócalos y Bancos
Un banco es un conjunto de zócalos para insertar chips individuales (como los DIP, o SIP), o módulos de
memoria RAM (SIMM de 30, SIMM de 72 o DIMM de 128 pines).
Una motherboard posee más de un banco de memoria para agregar más memoria a la máquina sin tener
que retirar la que estaba instalada. Cada banco de memoria puede poseer 1, 2 ó 4 zócalos.
Un banco organiza la cantidad total de memoria en renglones sucesivos según el ancho del bus de datos
del microprocesador. Por ejemplo, en un Intel 486 (bus de datos de 32 bits), para colocar memorias en
los bancos deben respetarse las siguientes reglas:
1.- Un banco de memoria debe tener en todos sus zócalos la misma cantidad de módulos.
2.- Debe llenarse primero el banco 0, luego el banco 1, y así sucesivamente (excepto si la motherboard
posee autobanking).
3.- Un banco debe tener módulos de la misma velocidad. No se puede colocar una memoria SIMM de 60
nanosegundos junto con otra de distinta velocidad.
Memoria Caché
Estas memorias son de tipo estáticas. Son muy veloces (10 ns) y también caras, ya que su proceso de
fabricación es mucho más complejo. Con una memoria caché el micro lee una dirección de memoria y
mientras procesa la información el caché lee las restantes posiciones de memoria principal consecutivas.
Cuando el micro necesite leer la próxima dirección de memoria, su contenido se encontrará en caché. De
esta manera, se acelera mucho la velocidad de procesamiento.
Una declaración de variable como:
int var;
produce una asociación entre el nombre 'var' y un espacio de almacenamiento en memoria. Por lo tanto
hay dos elementos relacionados con el nombre 'var': un valor que se puede almacenar alli y una
dirección de memoria para la variable, algunos autores se refieren a estos dos aspectos como el
"rvalue" y "lvalue" de la variable.
Además del identificador "var", tenemos la palabra "int" que nos indica el TIPO (type) de la variable. El
tipo nos indica:
1-CUANTAS CELDAS DE MEMORIA (bytes) se asocian a ese nombre de variable.
2-DE QUE MODO SERAN INTERPRETADOS los datos que se encuentren en tal localidad de
memoria,
1-Un byte es la menor unidad de información que pueden direccional la mayoría de las computadoras.
En la mayoría de las arquitecturas el tipo char ocupa un solo byte, por lo tanto es la unidad mínima. Un
bool admite solo dos valores diferentes, pero es almacenado como un byte. El tipo integer ocupa
generalmente 2 bytes, un long 4, double 8, y asi con el resto de los tipos.
2-El otro punto es la relación entre LO QUE HAY en una celda de memoria y COMO ES
Que es esto? Depende en gran parte del TIPO (type) que hayamos asociado a esa celda (y suponiendo
que exista tal asociación). Ese valor interpretado como un Hexadecimal es 0x61, en decimal es 97, y si
fue asociada al tipo char representara la letra 'a', cuyo ASCII es igual a 97. En ninguna localidad de
memoria hay algo como la letra 'a', lo que encontramos son valores binarios que en caso de estar
asociados a char y en caso de que lo saquemos en pantalla como char hara que veamos encendidos
ciertos pixeles de pantalla, en los cuales reconoceremos una representación de la letra 'a'.
La representación binaria de datos ocupa demasiado espacio, por ese motivo es preferible utilizar el
sistema Hexadecimal, ademas de ser muy fácil de traducir a binario es mas económico que este o el
decimal. Observar los bytes de un sector de memoria de un programa facilita la comprensión sobre el
modo en que cada tipo (type) se asocia a direcciones de memoria.
Supongamos un programa que declara, define e inicializa las siguientes variables:
Apuntadores
Una de las cosas más difíciles que encuentran los principiantes en C es entender el concepto de
apuntadores. Me he encontrado a menudo que la principal razón por la que los principiantes tienen
problemas con los apuntadores es que tienen una muy pobre o mínima concepción de las variables, (del
modo en que C hace uso de ellas). Así que comencemos con una discusión sobre las variables de C en
general. Una variable en un programa es algo con un nombre, que contiene un valor que puede variar. El
modo en que el compilador y el enlazador (linker) manejan esto es que asignan un bloque específico de
la memoria dentro de la computadora para guardar el valor de una variable. El tamaño de este bloque
depende del rango en que a
esta variable le es permitido variar. Por ejemplo, en PC’s de 32 bits, el tamaño de una variable de tipo
entero (int) es de 4 bytes, en una máquina antigua de 16 bits los enteros tienen un tamaño de 2 bytes. En
C el tamaño de un tipo de variable como una de tipo entero no tiene porqué ser el mismo en todos los
tipos de máquinas. Es más en C disponemos de diferentes tipos de variables enteras, están los enteros
largos (long int) y los enteros cortos (short int) sobre los que puedes averiguar en cualquier texto básico
sobre C. El presente documento asume que se está usando un sistema de 32 bits con enteros de 4 bytes.
Y es a qui donde radica la importancia, de lo punteros, por que como ya se dijo, los Punteros o
Apuntadores, son variables que contienen la dirección de otra variable.
En forma gráfica, la podemos representarlo, como se muestra en la figura de arriba. P, es apuntador
hacia el entero identificado como “a”, en el cual, se reservan dos bytes, para guardar el entero. 3B, es la
dirección de la variable. (NOTA: “3B”, es sólo por decir un dato, para que el lector tenga la idea de una
dirección de memoria).
Cabe mencionar que, un puntero, es una variable q termina con la dirección con la que comienza la
variable a la que apunta:
Los usos principales, que tienen, los punteros, son los siguientes:
->Nos ayuda, para que una función devuelva más de un valor. Por ejemplo, una función que devuelva un
vector de enteros, en dicha función mandamos la dirección del primer elemento a la función principal, y a
partir de ella, imprimimos todos los valores contenidos en el vector.
->Mejor uso de la memoria dinámica. Esto es lo que más nos tiene cuenta, el lector debe tener presente
que, el uso de punteros, ayuda a ahorrar memoria y por consiguiente, hace más efectivo el uso y
administración de la misma.
La forma de declarar un apuntador, es la siguiente:
Int *p;
Int->indica que, es un puntero hacia un entero.
*->indica al compilador que esa variable, es un puntero
p-> Es el identificador del puntero.
Otros ejemplos:
float *q; /*apuntador hacia un flotante*/
char *z; /*puntero que contiene la dirección de una variable que guarda un carácter */
Para referirnos a un valor a través de un apuntador, lo hacemos mediante un proceso llamado
indirección.
Por ejemplo, para mandar a impresión el valor entero, hacia el cual a punta “p”, sería así:
printf(“%d”, *p);
Ahora bien, para guardar la dirección de alguna variable, en un puntero, debemos conocer un nuevo
amigo:
&->Operador de Dirección.
P1=&a;
Con esa simple declaración, estamos indicándole al compilador que p1, contendrá le dirección de
memoria de a.
int a;
/*Declaraci¢n de un puntero a un entero */
int *p;
clrscr();
printf("Ingrese un valor ENTERO para la variable:\n");
scanf("%d", &a);
while(a<0)
{
printf("ERROR, el valor debe ser mayor que cero:\n");
scanf("%d", &a);
}
clrscr();
/*Limpiamos la pantalla */
printf("a=%d\n", a); /*Imprimo el valor de a*/
printf("La direcci¢n de a es %p\n", &a);
printf("*p=%p\n", p); /*Imprimo la direcci¢n que guarda p*/
/*imprimo el valor guardado en la direccion a la que apunta p*/
printf("a=%d\n", *p);
printf("El tama¤o de *p es %d\n", sizeof(p));
getch();
return 0;
}
Explicación:
Como ya se ha explicado, la forma de declaración de un puntero consiste en colocar al tipo de dato al
cual apunta, el operador asterisco (*), seguido del identificador del mismo. Debo hacer notar que, para
mandar a impresión una dirección de memoria, debo hacerlo usando la expresión “%p”, para lo cual es
indistinto si mando directamente la dirección de memoria, haciendo uso del operador & (ejemplo: &a), o si
le mando, a impresión el contenido de la variable apuntador. (ejemplo: "*p=%p\n", p).
La función sizeof como su nombre lo indica, se utiliza para determinar el tamaño (en bytes) de alguna
variable en específico.
Ejemplo 7.2
Diseñe un programa, que sume dos variables de tipo entero, por medio de apuntadores.
#include <stdio.h>
#include <conio.h>
main()
{
int a, b, c;
int *p1, *p2, *p3; /*declaracion de los punteros */
printf("Ingrese el valor de a:\n");
scanf("%d", &a);
printf("Ahora el valor de b:\n");
scanf("%d", &b);
c=a+b;
printf("a+b=%d\n", c);
/*asiganamos las direcciones a los punteros correspondientes/
p1=&a;
p2=&b;
/*ahora desreferenciamos el valor de los punteros para sumarlos */
printf("*p1 + *p2=%d\n", *p1+*p2);
p3=&c;
printf(" Direcci¢n de a es %p\n Direccion de b es %p\n Y la de c es %p\n\n", p1, p2, p3);
getch();
return 0;
}
con esta instrucción: *p1+*p2, estamos invocando el valor guardado en la dirección a la que apunta p1 y
p2, por tanto se obtiene el mismo resultado, que si sumáramos directamente las dos variable (a+b).
Ejemplo 7.3
Programa que, asigna valores a punteros y juega un poco con las direcciones y las desrefenciaciones.
#include <stdio.h>
#include <conio.h>
main()
{
int a, b,c, *p1, *p2;
void* p; /*puntero que apunta a void*/
p1=&a;/*guarda la direcci¢n de a*/
*p1=1; /* a donde apunta p1, guarda el valor de uno */
p2=&b;
*p2=2;
p1=p2; /*En p1, guarda la direccion a la que apunta p2*/
*p1=0;
p2=&c;
*p2=3;/*a donde apunta p2, le asigno 3*/
printf("a =%d\n b =%d\n c =%d\n\n", a,b,c);
p=&p1; /*p contiene la direccion de p1*/
p=p2;
*p1=1;/*es como asignarle a C, el valor de 1*/
/*con los cambios realizados imprimimos el nuevo
valor de las variables */
printf("a =%d\n b =%d\n c =%d\n\n", a,b,c);
getch();
return 0;
}
Punteros y Arreglos
Si mat es un arreglo unidimensional, la dirección del primer elemento puede ser expresada tanto como
&mat[0] o simplemente como mat.
La dirección del elemento (i+1) se puede expresar como (mat+i), donde mat, representa la rireción del
arreglo e “i”, la posición específica a la cual nos referimos.
Si &mat[i] y (mat +i) representan la dirección del i-ésimo elemento de mat, mat[i] y *(mat+i), representa el
contenido de esa dirección.
Por ejemplo, supongamos que tenemos el siguiente arreglo:
Int mat[]={2, 16, -4, 29, 234, 12, 0, 3}
Tenemos un arreglo de 8 posiciones, de las cuales ya hemos aprendido que, para referirnos a cada uno
de esos valores, lo podemos hacer usando subíndices. Así:
Mat[0]=2;
Mat[1]=16;
...
pero podemos acceder a ellos de un modo alternativo, usando punteros de la siguiente manera:
int *ptr;
ptr=&mat[0];
y de esa manera, podemos imprimir los valores de nuestro arreglo, ya sea usando la notación de arreglos
o desreferenciando nuestro puntero.
Ejemplo 7.4
Utilice un puntero para imprimir los valores de un puntero de 6 posiciones.
#include <stdio.h>
#include <conio.h>
main()
{
int mat[]={2,3,5,9,10,4};/*declaracion del arreglo*/
int i;
int *ptr;
ptr=&mat[0];/*al puntero ptr, le asignamos la direccion del primer
elemento del arreglo*/
clrscr();
printf("\t\t\tElementos del arreglo:\n\n\n");
for(i=0; i<6; i++)
{
printf("mat[%d]=%d\n", i, mat[i]); /*notacion de arreglos*/
printf("ptr+%d=%d\n", i, *(ptr+i));/*notacion de punteros*/
}
getch();
return 0;
}
ahora, supongamos que, lo que tenemos es un arreglo multidimensional, entonces, podemos acceder a el
mediante la siguiente sintaxis:
multi[renglon][columna]
ó
*(*(multi + renglon) + columna)
Para entender mejor lo que sucede, reemplacemos *(multi + renglon) con una X, tal que la expresión nos
quede como *(X + columna) Ahora vemos que esta X es como un apuntador ya que la expresión se
encuentra desreferenciada y sabemos que col es un entero. Aquí la aritmética a utilizar es de un tipo
especial llamada “aritmética de punteros”. Eso
significa que, ya que hablamos de un arreglo de enteros, la dirección a ser apuntada por (el valor de) X +
columna + 1 debe ser mayor que la dirección de X + columna por una cantidad que es igual a
sizeof(int) (el
tamaño del tipo de dato entero). Ya que sabemos la estructura de memoria para arreglos
bidimensionales, podemos determinar que en la
expresión multi + renglon como se hizo arriba, multi + renglon + 1 hace que esto se incremente por un
valor igual al necesario para “apuntar a” el siguiente renglón, lo cual sería entonces COLUMNAS * sizeof
(int).
Esto nos dice que si la expresión *(*(multi + renglones) + columnas) va a ser evaluada correctamente
en tiempo de ejecución, el compilador debe generar código que tome en consideración el valor de
COLUMNAS, es decir, la segunda dimensión. Debido a la equivalencia entre las dos formas de
expresión, esto se hace cierto ya sea que usemos la sintaxis de punteros o la de arreglos multi[renglon]
[columna].
Así que para evaluar cualquiera de estas dos expresiones, se deben conocer 5 valores:
1. La dirección del primer elemento del arreglo, la cual es conocida por la expresión multi, es decir, el
nombre del arreglo.
2. El tamaño y el tipo de los elementos que conforman el arreglo, en este caso sizeof(int).
3. La segunda dimensión del arreglo.
4. El valor específico del índice para la primera dimensión, renglon en este caso.
5. El valor específico del índice para la segunda dimensión, columna en este caso.
Una vez que conocemos esto, consideremos el problema de diseñar una función que manipula los
elementos de un arreglo previamente declarado. Por ejemplo, uno que establecería un valor de 1 todos
los elementos del
arreglo multi.
void set_value(int m_arreglo[][COLUMNAS])
{
int renglon, columna;
for (renglon = 0; renglon < RENGLONES; renglon++)
{
for (columna = 0; columna < COLUMNAS; columna++)
{
m_arreglo[renglon][columna] = 1;
}
}
}
Y para llamar a esta función usaríamos entonces:
set_value(multi);
Dentro de esta función, hemos usado los valores establecidos por #define en RENGLONES y
COLUMNAS, los
cuales establecen los límites para los ciclos. Pero dentro de lo que le concierne al compilador, estas son
simples constantes, es decir, no hay nada que les relacione directamente con el tamaño del arreglo
dentro de la
función. renglon y columna son variables locales, por supuesto. La definición formal del parámetro le
permite
al compilador determinar las características del valor del puntero que será pasado en tiempo de
ejecución.
Realmente no necesitamos la primera dimensión y, como veremos más adelante, habrá ocasiones en las
que
preferiremos no declararla en la definición de los parámetros de una función, no quiere decir que eso se
vuelva un hábito o algo con lo que haya que ser consistente. Pero la segunda dimensión debe ser usada
como se ha mostrado en la expresión del parámetro. La razón por la que la necesitamos es por la forma
de la evaluación de m_arreglo[renglon][columna]. Mientras que el parámetro define el tipo de datos (int
en este caso) y las variables automáticas para renglón y
columna son definidas en los ciclos for, sólo un valor puede ser pasado usando un único parámetro. En
este caso, es el valor de multi, como lo pasamos en la llamada a la función, es decir, la dirección de su
primer
elemento, más bien referido como un apuntador al arreglo. Así que la única manera que tenemos de
informar al compilador de la segunda dimensión es incluirlo explícitamente en la definición del parámetro
de la función. De hecho y por lo general todas las dimensiones de orden mayor que uno, necesitan de
arreglos multidimensionales. Esto significa que si hablamos de arreglos de 3 dimensiones, la segunda y
tercera dimensiones deben estar especificadas en la definición del parámetro.
Ejemplo 7.5
/*Programa que lee un arreglo y una matriz usando
aritm‚tica de punteros */
#include <stdio.h>
#include <conio.h>
#define M 3
#define N 3
main()
{
int x[3][3], y[3];
int f=0,c=0;
clrscr();
for(f=0; f< M; f++)
{
for(c=0; c<N; c++)
{
printf("*(x+ %d)+%d)=", f,c);
scanf("%d", *(x+f)+c);
}
printf("Elemento %d del vector:\n", f);
scanf("%d", &y[f]);
}
printf("IMPRESIONES:\n");
printf("*** MATRIZ ***\n");
for(f=0; f<M; f++)
for(c=0; c<N; c++)
printf("%d", *(*(x+f)+c));
printf("\n*** VECTOR ***\n");
for(f=0; f<M; f++)
printf("%d", *(y+f));
getch();
return 0;
}
Ejemplo 7.6
Uso de funciones
#include <stdio.h>
#include <conio.h>
void generacion(int a[3][3], int b[3][3]);
main()
{
int a[3][3]={1,2,3,4,5,6,7,8,9}; /*generamos dos matrices de igual tamaño*/
int b[3][3]={1,2,3,4,5,6,7,8,9};
int f,c;
printf("LAS MATRICES GENERADAS SON:\n\n");
printf("Matriz a:\n");
for(f=0; f<3; f++)
{
for(c=0; c<3; c++)
printf("%d", a[f][c]);
printf("\n");
}
printf("Matriz b:\n");
for(f=0; f<3; f++)
{
for(c=0; c<3; c++)
printf("%d", b[f][c]);
printf("\n");
}
generacion(a,b); /*la función recibe como parámetros, las dos matrices*/
getch();
return 0;
}
void generacion (int a[3][3], int b[3][3])
{
int d[3][3],f,c;
for(f=0; f<3; f++)
for(c=0; c<3; c++)
d[f][c]=a[f][c]*b[f][c];
Ejemplo 7.7
Programa que dado un vector de enteros, aumenta en una unidad el valor de los miembros del arreglo y
envía la dirección del primer elemento como retorno a la función principal. (Note, como hacer para
devolver más de un valor)
#include <stdio.h>
#include <conio.h>
/*funcion que devuelve una direcion de memoria*/
int *valores (int a[]);
main()
{
int a[]={2, 5, 9, 7, 10};
int *p, i;
clrscr();
printf("Los valores del puntero, aumentado en uno, son:\n");
p=valores(a);
for(i=0; i<5; i++)
printf("* %d *", *(p+i));
getch();
return 0;
}
int *valores (int a[])
{
int i, *p;
for(i=0; i<5; i++)
a[i]=a[i]+1;
p=&a[0];
return (p);
}
Cuestionario
1. ¿Cuál es la diferencia entre memoria principal y memoria caché?
______________________________________________________________________________
______________________________________________________________________________
_______________________________________________________
2. 8 bytes es equivalente a:________ bits
3. ¿Qués es un
registro?:______________________________________________________________________
_______________________________________________________
4. ¿Para que nos sirven los
puneteros?:____________________________________________________________________
_______________________________________________________
5. ¿cuál es la diferencia entre desreferenciación e indirección?
______________________________________________________________________________
____________________________________________
Ejercicios:
1. Diseñe un programa en C, que lea un arreglo de 20, lea los valores, luego los imprime y muestre
la suma de sus elementos, usando notación de punteros
2. Diseñe un programa, que imprima, las direcciones de memoria de los elementos de una arreglo
de 5X5, elementos
3. Se desea conocer el número de elementos de una cadena de caracteres, ingresada por el
usuario. (NOTA: el final de una cadena es representada por un elemento nulo ‘\0’. Utilice
aritmética de punteros para determinar este valor)
4. Escriba un programa en C, que le permita al usuario, ingresar una cadena de caracteres (no
mayor a 100 elementos) y luego, la imprima, en forma inversa, usando para ello, notación de
punteros.
5. Un programa en C, contiene la siguiente declaración:
Char *mat[5]={“Yo”, “Tu”, “El”, “Nosotros”, “Ellos”};
a)¿Cuál es el significado de mat?_________________________________
b)¿Qué significa (mat+2)?_________________________________________
c)¿Qué significa *(mat+2)?_________________________________________
en éste capítulo, trataremos de darle mayor prioridad a los ejemplos, es por ello, que, iniciaremos con el
ejemplo más emblemático de una función recursiva, como es el caso del factorial.
Ejemplo 8.1
Realice una función recursiva que muestre el factorial de un número ingresado por el usuario.
Un número factorial, se define como:
n!=nx(n-1)X(n-2)x....x1
lo cual puede representarse así:
0!=1
1!=1
2!=2X1
3!=3x2x1
y la forma recursiva para ello sería:
2!=2x1!
3!=3x2!
...
n!=nx(n-1)!
#include <stdio.h>
#include <conio.h>
int factorial (int n); /*declaración de la función*/
main()
{
int n, r;
printf("Ingrese el Valor del factorial que desea Calcular:\n");
scanf("%d", &n);
while(n<0)
{
printf("Ingrese el Valor del factorial que desea Calcular:\n");
scanf("%d", &n);
}
r=factorial(n); /*llamado de la función*/
printf("El valor de %d!= %d\n", n, r);
getch();
return 0;
}
int factorial (int n)
{
int r;
if(n==0)/*CASO BASE: al resultar cierta se sale de la recursión*/
r=1;
else
r=n*factorial(n-1); /*llamado recursivo de la función */
return r;
}
Explicación:
El programa, lo que hace, es mandarle el parámetro a la función factorial (supongamos que n=2).
Entonces pregunta si n=0, al evaluar, resulta falsa, por tanto nos vamos por el lado del else, y asigna a r
el valor de 3, por la llamada recursiva de n-1, (es decir 2. r=2*factorial(1)). Y vuelve a evaluar la
condición, resulta falsa y vuelve a llamar la función (r=1*factorial(0)). Por tanto, al evaluar la condición
esta vez, resultará cierta, y le asigna el valor de 1, el cual lo retorna a la llamada anterio (r=1*1), este
resultado, lo devuelve a la llamada que se hizo antes de ésta (r=2*1). Y este resultado, se devuelve a la
llamada anterior. Ésta es la que se hizo en la función original, y luego se imprime ese valor
Antes de continuar, debemos hacer hincapié en algo muy importante, como lo es: la forma en la cual,
dejaríamos de ejecutar las llamadas iterativas, ya que debe existir alguna condición que, al resultar cierta,
ya no haga más llamadas recursivas y por el contrario, empiece a retornar los valores de la función. A
esto se le llama Caso Base.
Un Caso Base, son los casos del problema que se resuelven sin recursión, para nuestro primer ejemplo
es 0, por que el factorial de cero, está definido como 1 (uno).
Si el problema es lo suficientemente complejo, debemos hacer uso de Los casos generales, que son
definidos como la solución de uno o más problemas o un conjunto de pasos adicionales. Sin embargo,
debemos tener en cuenta que, los casos generales, deben avanzar hacia un caso base, de lo contrario
tendremos una recursión infinita.
Ejemplo 8.2
Realice una función recursiva que calcule la suma de dos enteros.
Solución:
Si tenemos por ejemplo a+b, la suma sería “a”, siempre y cuando “b” sea cero, de lo contrario,
recursivamente, sería: 1+suma(a, b-1).
#include <stdio.h>
#include <conio.h>
int suma (int a, int b);
main()
{
int a, b, r;
clrscr();
printf("Ingrese el primer sumando:\n");
scanf("%d", &a);
printf("Ingrese el segundo sumando:\n");
scanf("%d", &b);
r=suma(a,b);
printf("%d + %d = %d\n", a, b,r);
getch();
return 0;
}
int suma (int a, int b)
{
int r;
if(b==0)/*caso base*/
r=a;
else
r=1+suma(a, b-1);/*llamada recursiva, disminuimos b, para que en un
return r;} momento dado, sea igual a cero*/
Ejemplo 8.3
Diseñe un programa que multiplique, dos valores enteros, recursivamente.
Sabemos muy bien que, a*b, es igual a sumar a+a+a+a+a+......b veces.
Pero si b=1, entonces, el producto es igual a “a”.
#include <stdio.h>
#include <conio.h>
int multi (int a, int b);
main()
{
int a, b, r;
clrscr();
printf("Ingrese el valor del multiplicando:\n");
scanf("%d", &a);
printf("Ahora el multiplicador:\n");
scanf("%d", &b);
r=multi(a, b);
printf("%d * %d= %d", a, b, r);
getch();
return 0;
}
int multi (int a, int b)
{
int r;
if(b==1)/*elemento neutro de la multiplicación, que es nuestro caso base*/
r=a;
else
r=a+multi(a, b-1);/*llamada recursiva de la función, disminuimos b, para que llegue un momento en el
que sea igual a 1, y le sumamos a, por que así está definida la multiplicación, como la suma de “a”, b
veces*/
return r;
}
Ejemplo 8.4
Calcule, recursivamente la expresión: Xn
#include <stdio.h>
#include <conio.h>
int exp (int base, int p);
main()
{
int base, p, r;
clrscr();
printf("Ingrese la Base:\n");
scanf("%d", &base);
printf("Exponente es:\n");
scanf("%d", &p);
r=exp(base, p);
printf("%d^%d=%d\n\n", base, p, r);
getch();
return 0;
}
int exp (int base, int p)
{
int r;
if(p==0)
r=1;
else
r=base*exp(base, p-1);
return r;
}
Ejemplo 8.5
Obtenga, recursivamente el Máximo Común Divisor, mcd(m,n), de dos números, sabiendo que:
-es n si n<m y n divide a m
- es mcd(n,m) si m<n
- es mcd(n, residuo de m/n), en caso contrario
#include <stdio.h>
#include <conio.h>
int mcd (int m, int n);
main()
{
int r, m, n;
clrscr();
printf("PROGRAMA QUE CALCULA EL M.C.D.\n");
printf("Ingrese un N£mero:\n");
scanf("%d", &m);
printf("El otro es:\n");
scanf("%d", &n);
r=mcd(m,n);
printf("el mcd de %d y %d es %d\n", m,n, r);
getch();
return 0;
}
int mcd (int m, int n)
{
int r;
if(n<=m && m%n==0)
r=n;
else
if(m<n)
r=mcd(m,n);
else
r=mcd(n, m%n);
return r;
}
Ejemplo 8.6:
Programa que calcula la secuencia de Fubonacci, recursivamente:
#include <stdio.h>
#include <conio.h>
int fubonacci (int n);
main()
{
int n, r;
printf("PROGRAMA QUE IMPRIME LA SECUENCIA DE FOBONACCI\n");
printf("Ingrese el valor de n:\n");
scanf("%d", &n);
printf("** %d **", r);
getch();
return 0;
}
int fobonacci (int n)
{
int r;
if(n==0 || n==1)
r=n;
else
r=fobonacci(n-1)+fobonacci(n-2);
return r;
}
Ejemplo 8.7:
/*Programa que calcula la suma de los primeros
n numeros enteros, positivos */
#include <stdio.h>
#include <conio.h>
int func (int n);
main()
{
int n, r;
clrscr();
printf("A partir de cero... ¨Hasta que n£emro desea sumar?\n");
scanf("%d", &n);
r=func(n);
printf("La suma de los primeros %n numeros eneteros es: %d\n", n, r);
getch();
return 0;
}
int func (int n)
{
int r;
if(n==0)
r=0;
else
r=n+func(n-1);
return r;
}
Este programa, lo que hace es sumar desde cero, hasta el n, número entero.
Ejemplo 8.8:
Programa que calcula, la suma de los primeros n elementos de un vector:
#include <stdio.h>
#include <conio.h>
r=sumav(v, n);
printf("El valor de la suma es %d\n", r);
getch();
return 0;
}
int sumav(int v[], int n)
{
int r;
if(n==0)
r=v[0];
else
r=v[n]+sumav(v, n-1);
return r;
}
Ejemplo 8. 9:
#include <stdio.h>
#include <conio.h>
/*programa que lee e imprime un arreglo de enteros
y despues hace una busqueda lineal recursiva de ciertos
valores digitados por el usuario */
int busquedalineal(int *V, int n, int b);
void main()
{
int arreglo[12];
int pos, i, num, bus, buscar=1;
pos=0;
printf("Digite 12 valores para guardarlos en el arreglo:\n");
for(i=0; i<12; i++)
{
printf("Digite el elemento de la posici¢n %d del vector:\n", i+1);
scanf("%d", &arreglo[i]);
}
printf("Los elementos del vector son:\n");
for(i=0; i<12; i++)
{
printf("%d", arreglo[i]);
}
printf("\n");
while(buscar)
{
printf("Digite un n£mero a buscar:");
scanf("%d", &num);
pos=busquedalineal(arreglo, 11, num);
if(pos)
printf("El numero %d se encuentra en el arreglo\n", num);
else
printf("El numero %d NO se encuentra en el arreglo\n", num);
printf("¨Desea Buscar otro numero? (S/N)\n");
bus=getch();
if(bus=='s' || bus=='S')
buscar=1;
else
buscar=0;
}//fin del while
getch();
}//fin del main
/*Funci¢n REcursiva de Busqueda Lineal */
int busquedalineal(int *V, int n, int b)
{
if(n==0)
return (V[0]==b);
else
if(V[n]==b)
return 1;
else
return busquedalineal (V, n-1, b);
}
Ejemplo 8.10:
/* Pograma que dado un vector de enteros
calcule:
1. el elemento maximo del arreglo
2. el elemento minimo del arreglo
3. la suma de los elementos del arreglo
4. el producto de los elementos del arreglo
5. el promedio de los elementos del arreglo */
#include <stdio.h>
#include <conio.h>
#define N 10
int max (int a[], int n);
int min (int a[], int n);
int sum (int a[], int n);
int multi (int a[], int n);
int prom (int a[], int n);
main()
{
int a[N], i=0, r, op, n=10, ban=1;
clrscr();
printf("\t\tBIENVENIDO (A)\n\n");
printf("Ingrese los valores del vector:\n");
for(i=0; i<N; i++)
{
printf("Ingrese el elemento %d del arreglo:\n", i);
scanf("%d", &a[i]);
}
clrscr();
printf("Los valores del arreglo son:\n");
for(i=0; i<N; i++)
printf("** %d **", a[i]);
getch();
while(ban==1)
{
clrscr();
printf("*****************************************\n");
printf("** ¨Que desea hacer? **\n");
printf("** 1. El maximo elemento **\n");
printf("** 2. El minimo elemento **\n");
printf("** 3. La suma de sus elementos **\n");
printf("** 4. Su producto **\n");
printf("** 5. Su promedio **\n");
printf("** 6. Sair **\n");
printf("*****************************************\n");
scanf("%d", &op);
switch(op)
{
case 1: clrscr();
r= max(a, n);
printf("el maximo elemto del vector es %d\n", r);
break;
case 2: clrscr();
r=min(a, n);
printf("El elemnto minimo es %d\n", r);
break;
case 3: clrscr();
r= sum(a, n);
printf("La suma de los elementos del vector es: %d \n", r);
break;
case 4: clrscr();
r=multi (a, n);
printf("Al multiplicar todos los elementos del vector resulta %d \n", r);
break;
case 5: clrscr();
r=prom(a, n);
printf("El promedio de los valores son: %d \n", r);
break;
case 6: return 0;
default:
printf("ERROR, comando no valido!!\n");
}
printf("¨Desea Seguir? (1/2)\n");
scanf("%d", &ban);
}
getch();
return 0;
}
int max (int a[], int n)
{
int r, aux;
if(n==0)
r=a[0];
else
{
aux=max(a, n-1);
if(a[n]>aux)
r=a[n];
else
r=aux;
}
return r;
}
int min (int a[], int n)
{
int r, aux;
if(n==0)
r=a[0];
else
{
aux=min(a, n-1);
if(a[n]<aux)
r=a[n];
else
r=aux;
}
return r;
}
int sum (int a[], int n)
{
int r;
if(n==0)
r=0;
else
r=a[n-1]+sum(a, n-1);
return r;
}
int multi (int a[], int n)
{
int r;
if(n==0)
r=1;
else
r=a[n-1] * multi(a, n-1);
return r;
}
int prom (int a[], int n)
{
int r;
r=sum(a, n)/N;
return r;
}
Hasta el momento, hemos visto algunos ejemplo del uso de la recursión, pero hay que tener en cuenta,
algunos aspectos importantes, para decidir entre recursión o iteración:
1. la relación, tiempo-espacio de la memoria, para una llamada recursiva es mayor que para una
iterativa, por eso, se debe hacer uso de la recursión sólo cuando, una solución iterativa no sea
posible.
2. si son posibles las dos soluciones (iterativa y recursiva), entonces es mejor hacer uso de la
solución iterativa, puesto que una llamada recursiva consume mayor tiempo y espacio en
memoria.
3. hay por el contrario, muchos problemas que se resuelven exclusivamente con recursión, ya que
son demasiado extensos y complejos, por tanto una solución recursiva, presentará una imagen
más fiable, comprensible y elegante.
Recursión Infinita
Cuando hablábamos de los ciclos (bucles), decíamos que, dentro del ciclo debe existir algo que modifique
la condición, para que, en un momento dado, se pueda salir del ciclo. Lo mismo pasa con la iteración.
Una Recursión Infita, implica que cada llamada recursiva produce otra, y ésta a su vez produce otra
llamada, y éta también produce otra llamada, y así sucesivamente para SIEMPRE, o al menos hasta que
se termine la memoria de la computadora, para evitar esto, hay que tener en cuenta, y muy bien
definidos, los casos baes y los casos generales del problema.
Un algoritmo divide y vencerás, puede ser llamado recursivamente, a partir de un subconjunto (más
pequeño) de datos. La condición que deja de hacer las llamadas, es lo que hemos denominado como
caso base.
Torres de Hanoi
Quizá el siguiente ejemplo, sea uno de los más complejos de la recursión, por que consite en tres varillas,
torres o postes, paralelos; del cual el primero, posee tres donas, discos... en orden de la más grande a la
más pequeña, y se desean mover de la varilla 1 a la varilla 3, pero bajo ciertas condiciones:
1. los movimientos deben ser de uno en uno (es decir, no se pueden mover dos o tres donas al
mismo tiempo)
2. en una varilla, no puede quedar una dona pequeña, bajo una grande.
La siguiente imagen, representa, la forma más adecuada, para realizar los movimientos:
Parece bastante complejo verdad?, en lo personal, me tomó un poco de tiempo, entender y buscar la
forma de mover las donas (debo confesar que no soy muy bueno para ese tipo de jueguitos).
El ejemplo anterior constaba, unicamente de tres discos (donas), pero puede generalizarse para n discos
y tres varilas.
El algoritmo sería el siguiente:
If(n==1)
-Mover e disco 1 de la varilla inicial(1) a la varilla final(3)
Else
-Mover n-1 discos desde varilla inicial(1) hasta varilla la varilla temporal(2), utilizando la varilla 3,
como auxiliar.
-mover n-1 discos desde la varilla auxiliar central a varilla final (3) utilizando como auxiliar a la
varilla número uno.
Ejemplo 8.11:
#include <stdio.h>
#include <conio.h>
void hanoi (int n, int inic, int temp, int fin);
main()
{
int n; /*numero de discos a mover*/
clrscr();
printf("Numero de discos:\n");
scanf("%d", &n);
/*mover n discos desde 1 a 3, usando a 2 como auxiliar*/
hanoi(n, 1, 2, 3);
getch();
return 0;
}
void hanoi (int n, int inic, int temp, int final)
{
if(n>0)
{
/*mover n-1 discos de inic a temp*/
hanoi(n-1, inic, final, temp);
/*mover el que quede en inic a final*/
printf("Del poste %d al %d\n", inic, final);
/*mover n-1 discos de temp a final*/
/*el temporal es inic*/
hanoi(n-1, temp, inic, final);
}
}
Cuestionario
1. ¿Qué es
recursión?:___________________________________________________________________________
_____________________________________________________________
3. ¿Qué aspectos, son los que debo tomar en cuenta, cuando tengo que decidir entre iteración o
recursión?
______________________________________________________________________________
______________________________________________________________________________
_____________________________________________________
4. ¿Cuándo se produce una recursión infinita?
______________________________________________________________________________
_________________________________________________
5. ¿Para que sirven los casos base?¿y los casos generales?
______________________________________________________________________________
______________________________________________________________________________
__________________________________
6. ¿En que consiste la técnica “divide y vencerás”?
______________________________________________________________________________
______________________________________________________________________________
__________________________________
Descubre el error:
El siguiente código, presenta algunos errores de lógica, de sintaxis y de ejecución, ¿Puedes descubrirlos
y corregirlos?
/* Programa que calcula la suma de dos vectores */
#include <stdio.h>
int suma (int a[], int b[], int n);
void main()
{
int a[5]={1,2,3,4,5};
int b[5]={1,2,3,4,5};
int n=5, r;
clrscr();
printf("el valor de la suma es:\n");
r=suma(a,b, n);
printf("%d", r);
getch();
}
int suma (int a[], int b[], int n)
{
if(n!=0)
r=0;
else
{
r=a[n-1]+b[n-1]+suma(a, b, n-1);
}
return r;
}
Ejercicios:
1. Diseñe un programa que sume los primeros n múltiplos de tres, en forma recursiva
2. Diseñe un función (o programa completo) que, dado un vector, determine el producto de todos
sus elementos en forma recursiva.
3. el valor de e (una expresión matemática, puede representarse de la siguiente manera: e=1/1!
+1/2!+...+1/n!. Diseñe una función (o programa) que muestre el resultado recursivo de ésta
sucesión.
4. Dado un vector, escriba un programa que busque un elemento ingresado por el usuario, en el
vector y con un mensaje en pantalla, indique si ese valor se encuentra o no.
5. Escribir un programa, que comntenga una función recursiva, para calcular la función de
Ackermann, definida de la siguiente forma:
->A(m,n)=n+1, si m=0
->A(m,n)=A(m-1, 1) si n=0
->A(m,n)=A(m-1, A(m, n-1)) si m>0 y n>0
6. El combinatorio de dos números, está definido, por la expresión siguiente: C(m, n)=m!/(n!x(m-n)!),
escribir un programa en C, que calcule el combinatorio, donde n! Es el factorial de n.
7. Dados dos vectores de enteros e igual tamaño, calcular la suma de los productos de sus
elementos: C=Σ (AiXBi).
8. Escriba un programa que transforme cualquier núemro base 10, a otra base b, que puede ser de
8 a 16, utilizar para ello recursividad.
9. Diseñe un programa que muetre cual es el valor mayor contenido en un vector unidimensional.
10. Se tiene un conjunto de pesos p1, p2, p3...pn. y se desea estudiar, como objetivo si existe una
selección de dichos objetos que totalicen exactamente un peso v, dado. Por ejemplo, si el
objetivo es V=12y los pesos son: 4, 3, 6, 2, 1; se pueden elegir el primero, el tercero y el cuarto,
ya que: 4+6+2=12. El algotimo para solucionar este problema tiene como tarea básica añadir un
nuevo peso , probar si con ese peso se logra el objetivo o se avanza en el sentido de alcanzar la
solución.
Estructuras
Los arrays, son un ejemplo de estructuras (de tipo homogéneo), sin embargo, éste tipo de estructuras
posee una gran limitante, puesto que, sólo admite datos del mismo tipo (entero, flotante, carácter); las
estructuras que vamos a estudiar, estan compuestas por un grupo de variables, no necesariamente del
mismo tipo, en las cuales, podemos almacenar diferente información (en cuanto a tipo), pero referente a
un mismo tópico.
Por ejemplo, en nuestro carnét de identidad, del colegio, de la universidad, aparecen muchos datops
acerca de nosotros.
Nombres, apellidos, edad, dirección, fecha de nacimiento, grado, sección, ciclo...
Estos datos, son de diferente tipo, pero, en C, podemos almacenarlos utilizando un tipo de dato registro al
que llamamos Estructura.
Por tanto, una estructura es una calección de una o más tipos de elementos denominados miembros,
cada uno de los cuales puede ser de un tipo de dato diferente.
La figura anterior, muestra, los datos (y el tipo) para cierto registro, pero supongamos que dichos datos,
son para alumnos de alguna universidad, la cual cuenta con 500 alumnos... la cantidad de variables, sería
impresionante, por ejemplo tendríamos que declarar 500 variables para los nombres, 500 para los
apellidos, 500 para las edades, y así sucesivamente.
Es ello que radica la importncia de las estructuras, por que nos ahora tiempo además que las estructuras
son una herramienta importante para la creación de programas portentes y bases de datos.
Declaración de una Estructura
Como toda variable, en C, debemos declarar una estructura para poder hacer uso de ella, y la forma de
hacerlo es la siguiente:
Por ejemplo, para los datos anteriores, la forma de declararla sería la siguiente:
struct datos
{
char nombre[30];
char apellido[20];
int edad;
char dirección[100];
char fecha_nac[8];
};
Ejemplo:
1. struct datos
{
char nombre[30];
char apellido[20];
int edad;
char dirección[100];
char fecha_nac[8];
} alumno1, alumno2, alumno3;
Si por algún caso, los datos de las tres variables, fuesen los mismos, podemos asignar los valores de
ésta forma:
Alumno1=alumno2;
Alumno3=alumno2;
O también:
Alumno1=alumno3=alumno2;
Ya que, al contener los mismos miembros, es como si se tratacen de datos tipo int, float o char, por
consiguiente podemos hacer las asignaciones anteriores.
o también así:
struct datos alumno2={“Carolina”,
“Pelayo”,
20,
“San Salvador, El Salvador”,
“14/05/86”,
};
El tamaño de una estructura es determinado de forma muy simple, consiste en sumar, el tamaño de todos
los miembros, para nuestro caso particular, el tamaño (en bytes) de la estructura datos sería:
Sizeof(datos);
Cuyo resultado será: 160.
Para utilizar el operador flecha, debemos hacer uso de punteros (y creíste que ya nos habíamos olvidado
de los punteros verdad?).
Por ejemplo, si tenemos un puntero a una estructura:
Estructuras Anidadas
Al igual que los ciclos, las decisiones, las expreciones, etc, las estructuras también pueden estar dentro
de otras, a esto es que se le llama estructuras anidadas.
Supongamos que tenemos dos estructuras siguientes:
stuct empleado
{
char nom[30];
char puesto[10];
int edad;
float sueldo;
char municipio[20];
char ciudad[10];
char dirección[50];
};
struct cliente
{
char nom[30];
char fecha_deuda[8];
float saldo;
char municipio[20];
char ciudad[10];
char dirección[50];
};
observamos que, en ambas estructuras, hay fatos que se repiten, los cuales los podríamos ubicar en otra
structura, así:
struct direc
{
char municipio[20];
char ciudad[10];
char dirección[50];
};
struct cliente
{
char nom[30];
char fecha_deuda[8];
float saldo;
struct direc direc_cliente;
};
en C, podemos definir una estructura dentro de otra estructura, claro siempre y cuando la declaración de
ésta, haya sido previo.
Ejemplo 9.2
Se desea diseñar una estructura que contenga la información de operaciones financieras. Esta estructura
debe contar con un número de cuenta, una cantidad de dinero, el tipo de operación (deposito=0, retiro de
fondos=1, puesta al dia=2 o estado de cuenta=3) y la fecha y hora en que la operaciópn se ha realizado.
#include <stdio.h>
#include <conio.h>
#include <dos.h>
/*declaracion de las estructuras*/
struct fecha
{
unsigned int mes, dia, anyo;
};
struct tiempo
{
unsigned int horas, minutos;
};
struct registro_operacion
{
long numero_cuenta;
float cantidad;
int tipo_operacion;
struct fecha f;
struct tiempo t;
};
struct registro_operacion entrada();
main()
{
struct registro_operacion w;
w=entrada();
printf("\n\n operacion realizada\n");
printf("\t%ld\n", w.numero_cuenta);
/*ATENCION: note, la forma en la que llamamos a los miembros
de una estructura anidada*/
printf("\t%d-%d-%d\n", w.f.dia, w.f.mes, w.f.anyo);
printf("\t%d:%d\n", w.t.horas, w.t.minutos);
getch();
return 0;
}
struct registro_operacion entrada()
{
struct time t;
struct date d;
struct registro_operacion una;
printf("\nNumero de cuenta:\n");
scanf("%ld", &una.numero_cuenta);
puts("\nTipo de Operacion:\n");
puts("Deposito (0)");
puts("Retirada de Fondos (1)");
puts("Puesta al Dia (2)");
puts("Estado de Cuenta (3)");
scanf("%d", &una.tipo_operacion);
/*fecha y tiempo del sistema*/
gettime(&t);
una.t.horas=t.ti_hour;
una.t.minutos=t.ti_min;
getdate(&d);
una.f.anyo=d.da_year;
una.f.mes=d.da_mon;
una.f.dia=d.da_day;
return una;
}
Explicación
A fin de realizar el acceso correcto a los campos día, mes y año, así como el tiempo (la hora y los
minutos) en que se efectuó la operación, se define una estructura fecha y una estructura tiempo. La
estructura registro_operación tiene como miembro una variable (un campo) de tipo fecha, otra variable de
tipo tiempo y otras variables para representar los otros campos. La estructura de tipo operación se hace
con una variable entera. A continuación se declara tipos, se escribe una función que lee una operación
financiera y devuelve la operación leída. La fecha y hora es capturada del sistema. (ejemplo tomado de
“Algoritmos y Estructuras de datos, una perspectiva en C”, Luis Joyanes Aguilar)
Arrays de Estructuras
Los ejemplos de estructuras que hemos visto hasta el momento, sólo podemos manejar un conjunto de
datos a la vez, o podemos declarar varias variables para manejar, por ejemplo, los datos de 5 alumnos de
una institución.
Sin embargo, al igual que los tipos int, float... en c, podemos crear arrays con estructuras.
Por ejemplo, supongamos que ya tenemos declarada una estructura llamada datos, que contiene los
datos personasles de alumnos de cierta institución, y queremos guardar los datos de 100 alumnos, no
necesitamos de crear 100 variables de tipo estructura, sino que hacemos esta declaratoria:
strcut datos alumnos[100];
Ejemplo 9.3
Una biblioteca, desea tener un registro de los libros que en ella se encuentran, se sabe que existe un
número no mayor a 100 libros, y que los datos que se necesitan registrar son: el nombre del autor, el
título del libro y las existencias del mismo. Diseñe una estructura que sea capaz de guardar esa
información y luego mandarla a impresión.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define N 100
struct info_libro
{
char autor[20];
char titulo[30];
int num_ejemplares;
};
main()
{
struct info_libro libro[N];
int i=0;
clrscr();
printf("\t\tPROGRAMA QUE CONTROLA UN INVENTARIO DE LIBROS\n\n\n");
Lo más resaltante que, debo rescatar es que, a diferencia de los vectores y las matrices, las estructuras
no identifican su primer elemento con cero, sino con el número uno, es por ello que nuestro contador,
inicia con ese número, además que, para diferenciar un registro por otro, debemos incluir siempre el
subíndice, [i].
Estructuras y Funciones
Cuando hacemos uso de funciones y de estructuras, debemos tener en cuenta la forma correcta, de
cómo enviarle a la función el parámetro deseado, y con las estructuras, no es la excepción, por ejemplo:
/*declaración de la estructura*/
struct info_libro
{
char autor[20];
char titulo[30];
int num_ejemplares;
};
/*definición de la estructura*/
a partir de la declaració hecha por el typedef se puede hacer uso del nuevo sinónimo del tipo de dato
para definir variables, en general donde se utilizan los tipos de datos.
Veamos un ejemplo donde matemos dos pájaros de un solo tiro, es decir donde se muestren el uso de
funciones y el typedef.
Ejemplo 9.4
Un médico almacena la siguiente información de sus paciente en un array de registros (como mucho
habrá 100 pacientes): Nombre, teléfono, dirección, y si tiene alergias. Escribir un programa con las
siguientes opciones (todas ellas diben realizarse con funciones):
a) Introducir los datos interactivamente. Para saber que ya no se van a introducir más pacientes, el
usuario introducirá un 0 al solicitarle el número de teléfono del paciente.
b) Imprimir por pantalla toda la información.
c) Listar todos los pacientes con alergias.
d) Crear una lista con el índice de todos los pacientes que sean de la Comunidad de Madrid (su teléfono
comienza por el prefijo 91).
e) Salir.
#include <stdlib.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#define TAM_NOM 31
#define TAM_DIR 41
#define TAM_ALE 71
#define TAM_PAC 100
typedef struct {
char nombre[TAM_NOM];
double telefono;
char direccion[TAM_DIR];
char alergias[TAM_ALE];
} reg_paciente;
void tecla();
char menu();
void leerdatos(reg_paciente *pacientes, int *tamano);
void hayalergias(reg_paciente *pacientes, int tamano);
void imprimirtodos(reg_paciente *pacientes, int tamano);
void telefono91(reg_paciente *pacientes, int tamano);
void main()
{
char opcion;
int tamano;
reg_paciente pacientes[TAM_PAC];
system("color f0");
printf("\n\n\n\n\n\n\n\n\n\t\t\tINICIO DEL PROGRAMA\n\n\n\n\n\n\n\n");
tecla();
leerdatos(pacientes,&tamano);
do {
opcion=menu();
switch(opcion) {
case '1':
imprimirtodos(pacientes,tamano);
break;
case '2':
hayalergias(pacientes,tamano);
break;
case '3':
telefono91(pacientes,tamano);
break;
case '4':
leerdatos(pacientes,&tamano);
break;
case '0':
printf("\nSaliendo...");
break;
}
tecla();
} while (opcion!='0');
printf("\n\n\n\n\n\n\n\t\t\tFIN DEL PROGRAMA\n\n\n\n\n\n\n");
tecla();
}
void tecla()
{
printf("\nPresiona cualquier tecla para continuar ");
getch();
clrscr();
return;
}
for (i=0;i<TAM_PAC;i++) {
printf("\n\n\n****PACIENTE NUMERO %2d****",i+1);
printf("\nIntroduzca el nombre (no m…s de %d caracteres)\n\t",TAM_NOM-1);
do {
gets(pacientes[i].nombre);
} while (strlen(pacientes[i].nombre)==0);
printf("\nIntroduzca su tel‚fono (o 0 o cualquier letra para no introducir m…s datos)\n\t");
do {
gets(buffer);
} while (strlen(buffer)==0);
pacientes[i].telefono=atof(buffer);
if (pacientes[i].telefono==0) {
*tamano=i;
break;
}
printf("\nIntroduzca la direccion (no m…s de %d caracteres)\n\t",TAM_DIR-1);
do {
gets(pacientes[i].direccion);
} while (strlen(pacientes[i].direccion)==0);
printf("\nIntroduzca la descripcion de la alergias (no m…s de %d caracteres)\n",TAM_ALE-1);
printf("\n****Escriba \"NO\" si el paciente no tiene ninguna alergia****\n\t");
do {
gets(pacientes[i].alergias);
} while (strlen(pacientes[i].alergias)==0);
}
return;
}
char menu()
{
char respuesta;
printf("\n\n\n\n\tMENU DEL PROGRAMA");
printf("\n1.- Imprimir en la pantalla toda la informacion");
printf("\n2.- Listar los pacientes con alergias");
printf("\n3.- Crear una lista de los pacientes con el telefono 91-XXXXXXX");
printf("\n4.- Crear una lista nueva de pacientes");
printf("\n0.- Salir del programa.\n");
do {
respuesta=getch();
} while (respuesta<'0' || respuesta>'9');
return respuesta;
}
getch();
}
}
}
if (contimpresos==0) printf("\nÛÛ\t No hay ningun dato que mostrar en la pantalla.\t ÛÛ");
printf("\n\tÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
Û");
return;
}
}
}
}
if (contimpresos==0) printf("\nÛÛ\t No hay ningun dato que mostrar en la pantalla.\t ÛÛ");
printf("\n\tÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
Û");
return;
}
(tomado de “Practicas de C” /*Fernando Muñoz Ledesma .Practica 10.Ejercicio 3*/
Le recuerdo al lector que, el uso del do...while, en programación estructurada, está prohibido, sin
embargo, he decidido, no modificar este ejemplo, pues por respeto al escritor y si decidí colocarlo como
ejemoplo es por que considero que es un buen código, muy bien desarrollado, además que, para el
lector podría ser un reto, el pasarlo a estructurado.
Uniones
Hemos visto que las estructuras toman una parte de la memoria y se la reparten entre sus miembros.
Cada miembro tiene reservado un espacio para él solo. El tamaño total que ocupa una estructura en
memoria es la suma del tamaño que ocupa cada uno de sus miembros.
Las uniones tienen un aspecto similar en cuanto a cómo se definen, pero tienen una diferencia
fundamental con respecto a las estructuras: los miembros comparten el mismo trozo de memoria. El
espacio que ocupa en memoria una unión es el espacio que ocupa el campo más grande. Para
entenderlo mejor vamos a ver un ejemplo:
Primero vamos a ver cómo se define una unión:
union nombre_de_la_union
{
miembros de la union;
};
El tamaño de la unión corresponde, a la variable de mayor tamaño, por ejemplo, si en una unión tenemos
una variable tipo int (2 bytes), uno de tipo float (4 bytes) y un arreglo de 10 elementos (10 bytes),
entonces el tamaño de la unión será de 10 bytes.
Ejemplo 9.5
Diseñe un programa que lea el nombre y la inicial de una persona y luego lo imprima:
#include <stdio.h>
#include <conio.h>
union _persona
{
char nombre[10];
char inicial;
} pers;
main()
{
printf("Escribe tu nombre: ");
gets(pers.nombre);
printf("\nTu nombre es: %s\n", pers.nombre);
printf("Tu inicial es: %c\n", pers.inicial);
getch();
return 0;
}
Cuestionario
1. ¿Cómo se define una esructura?
_________________________________________________________________
_________________________________________________________________
______
2. ¿Cuál es la diferencia entre una declaración y una definición de la estructura?
_________________________________________________________________
_________________________________________________________________
________________________
3. ¿Cómo se determina el tamaño de una estructura? ¿y el de una unión?
_________________________________________________________________
_________________________________________________________________
_____________________________
4. ¿En que se diferencia una estructura de una unión?
_________________________________________________________________
_________________________________________________________________
_____________________________
5. ¿Para que sirve el typedef?
_________________________________________________________________
_________________________________________________________________
___________________________
2. struct nombre[10];
3. struct nombre *ptr;
ptr=&b;
printf(“El nombre es %s\n”, ptr.nom);
Ejercicios
1. Diseñe un programa que lea e imprima una la fecha del sistema. Declare la fecha como una
estructura.
2. Defina una estructura que contenga los elementos de un número racional (numerador y
denominador), y permira sumar, restar, multiplicar y dividir, dos números racionales.
3. En una tienda de perfumes, se desea crear un programa que controle las facturas de las ventas,
dichas facturas deben contener los siguientes datos: número correlativo, nombre del cliente,
unidades solicitadas, monto total, estado (morosos, atrasados, pagado). El sistema debe generar
el listado de los morosos así como también el total de clientes atrasados y en monto por los
pagos de los clientes.
4. un punto en el plano cartesiano, necesita únicamente una coordenada “x” y otra “Y”, definir un
programa que muestre:
-la distancia entre los dos puntos (D=sqrt((x2-x1)^2+(y2-y1)^2))
-La ecuación general del plano
-el punto medio entre los dos puntos (Xm=(x1+x2)/2; Ym=(y1+y2)/2).
5. Se desea controlar los registros de personas que tienen seguros de auto, casa y de vida, escriba
un programa en C, que permita leer los datos indicados y luego los muestre en pantalla,
indicando, lo siguiente:
Abstracción
Es la capacidad de manejar un objeto, (tema o idea) como un concepto general sin considerar la enorme
cantidad de detallesque pueden estar asociados con dicho objetoel proceso de abstracción presenta dos
aspectos importantes:
1. Destacar los aspectos relevantes
2. Ignorar aspectos irrelevantes.
La abtracción de datos es la técnica que permite inventar o definir nuevos tipos de datos (tipos de datos
definidos por el usuario), adecuados a la aplicación que se desea realizar.
TAD en C
Las características del lenguaje C que va a permitir la implementación de un TAD son principalmente, las
estructuras o registros para representación de los datos , y las funciones para representar las
operaciones especificadas. Además en un archivo de inclusión o cabecera se guarda la representación
de los datos y el interfaz, prototipos de funciones, etc.
Ejemplo:
Se desea especificar el un TAD que manipule números complejos (los números cmplejos están
compuestos por una parte real y otra imaginaria a+bi)
.tupedef struct
{
float a;
int b;
}complejo;
La anterior declaración puede hacerse en un archvo .h, y en programa principal, debe espécificarse esa
inclusión de la siguiente manera:
#include “nuevotad.h”
y con ello, ya podemos hacer uso de nuestro TAD, haciendo declaraciones como la siguiente:
complejo p1;
Código
/*archivo camplejo.h*/
typedef struct
{
float a;
int b;
}complejo;
/*archivo complejo.c*/
#include <stdio.h>
#include <conio.h>
#include "complejo.h"
main()
{
complejo p, q,r;
clrscr();
p.a=5.6;
q.a=6.5;
p.b=3;
q.b=8;
r.a=p.a+q.a;
r.b=p.b+q.b;
printf("El n£emero complejo es %.1f + %di\n", r.a, r.b);
getch();
return 0;
}
Explicación
El lector debe comprender la importanci, de lo que acabamos de hacer, y es que con la declaración
anterior, en cualquier otro de nuestros programas deseamos usar el tipo de dato, complejo, lo único que
debemos hacer es incluir la cabecera complejo. H¸que definimos en un archivo .h, algunos escritores,
indican que en este tipo de archivos (.h), podemos declarar las funciones que vamos a usar en nuetro
programa, lo cual, no está errado, sino que; personalmente considero que, para mantener de una manera
fidedigna, el concepto de TAD, debemos únicamente declararlo en el archivo *.h.
También hay que apuntar que la cabecvera complejo.h, se distingue de las demás por que va entre
comillas (“”) y no entre signos (<>), y es que, si una cabecera va entre signos mayor y menor que, le
estamos indicando al compilador que lo busque en la carpeta de los includes (por lo menos para turbo C),
pero si va entre comillas(“”), es como decirle al compilador que busque el archivo *.h en la misma carpeta
en la que está guardado el archivo *.c que estamos compilando.
Lo siguiente de lo que hablaremos es, en sencia, la médula central de ésta segunda parte del curso, por
tanto, si para el lector, no han quedado claros algunos conceptos de punteros, estructuras, etc; le
recomiendo que vuelva a leer los capítulos correspondientes, ya que de lo contrario, le será un poco difícil
la comprensión de lo que sigue, puesto que, continuamos con: La Gestión de Memoria Dinámica.
Para ello, iniciremos hablando de una estrictura muy importante como lo son las:
Estas características implican un comportamiento de lista LIFO (Last In First Out), el último en entrar
es el primero en salir.
El símil del que deriva el nombre de la estructura es una pila de platos. Sólo es posible añadir platos
en la parte superior de la pila, y sólo pueden tomarse del mismo extremo. También una rimera de
libros, en la que no se puede acceder a un determinado libro, sino se mueven primero los libros que
están encima de él.
Las pilas se pueden implementar de dos formas:
->Como Arreglos
->Usando Memoria Dinámica.
Operaciones con Pilas
push-> Se utiliza para introducir elementos, ésta función necesita como argumento a la pila y el
elemento a introducir.
pop-> se utiliza para sacar elementos de la pila, Como argumentos, necesita, unicamente a la pila.
Hemos llenado nuestra pila, llamada “s”, con los valores que le insicamos en la función push.
Ahora si queremos sacar el elemento del tope, basta indicar con la siguiente sentencia:
Pop(s);
Pop(s);
Y la pila quedaría:
Se debe tener en cuenta que, dentro de las funciones pop y push, puden estar otras funciones inmersas,
por ejemplo, si queremos introducir más datos, debemos tener otra función que verifique si la pila, no está
llena. Por el contrario si queremos sacar datos, debemos sersiorarnos que la pila no esté vacía, es ahí
donde nacen estas otras funciones que deben estar presentes en nuestros programas:
->empty, que verifica si la pila está vacía
->stacktop->retorna el elemento superior de la pila.
Representación de Pilas usando arreglos
Para ésta abstracción consideraremos que nuestra pila, está compuesta por un arreglo de n elementos y
una variable que controla el subíndice de la cima de la pila.
De declaración puede ser la siguiente:
Typedef struct {
char elementos[100];
int top;
}Pila;
Por ejemplo:
s.top=-1; /*inicializamos la pila*/
push(&s, x);
s.top++; /*ahora top vale 0*/
push (&s, y);
s.top++; /*top vale 1*/
push(&s, k);
s.top++; /*ahora top vale 2*/
pop(&s);
s.top-- /*s.top=1*/
claro que éste aumento (o decremento) debe hacerse dentro de la función push (o pop), según
corresponda.
Muchas veces un programa puede llevar otras funciones, pero digamos que eso ya es valor agregado por
parte del programador, las funciones más importantes son pop y push, y son esas las que vamos a
describir a continuación:
Algoritmo de la función push (para arreglos)
1. Verificar si la pila no está llena
2. incrementar en 1 el puntero índice de la pila
3. almacenar el elemento en la posición del puntero de la pila
Algoritomo de la función pop (para arreglos)
1. si la pila está vacía imprimir un mensaje
2. sino está vacía, leer el elemento de la posición del puntero de la pila
3. decrementar en uno el puntero de la pila.
Ejemplo 10.2
Se desea guardar un aproximado de 100 números enteros en una estructura tipo pila, la cual permita
añadir elementos, sacar los elemtos e imprimir los datos contenidos en la pila.
/*pila en forma de arreglo*/
#include <stdio.h>
#include <conio.h>
/*declaracion de la pila*/
typedef struct{
int datos[100];
int top;
}Pila;
/*Programa Pincipal*/
main()
{
Pila pila; /*definicion de la variable pila*/
int x, opc=5, i, k=0;
pila.top=-1;
clrscr();
while(opc!=4)
{
printf("\t\t\t MENU PRINCIPAL\n\n\n");
printf("\t1. Introducir datos\n");
printf("\t2. Sacar datos\n");
printf("\t3.Imprimir la pila\n");
printf("\t4.Salir\n");
scanf("%d", &opc);
switch(opc)
{
case 1: if(pila.top==99)
printf("ERROR, pila llena\a\n");
else
{
printf("Ingrese el dato\n");
scanf("%d", &x);
push(&pila, x);
k++;
}
break;
case 2: printf("El elemento sacado es %d\n\n", pop(&pila));
k--;
getch();
break;
case 3: if(pila.top>=0)
{
getch();
}
else
printf("No hay datos en la pila\a\n");
break;
}
clrscr();
}
printf("Fin del programa\n\n");
getch();
return 0;
}
/*funcin que agregaun dato a la pila*/
void push (Pila *ps, int x)
{
ps->datos[++(ps->top)]=x;
}
/*funcin que elimina y devuelve un elemento de la pila*/
int pop(Pila *ps)
{
int r=NULL;
if(empty(ps)==1)
printf("No hay elementos para sacar\a\n");
else
r=ps->datos[(ps->top)--];
return r;
}
/*funcion que verifica si la pila esta vac¡a*/
int empty (Pila *ps)
{
int r;
if(ps->top==-1)
r=1;
else
r=0;
return r;
}
Explicación
El programa enterio, muestra, en forma sencilla, el uso de las pilas, en forma estática, (usando arreglos),
con funciones muy sencillas como pop, push y empty, pero el lector debe recordar que él puede
modificar, crear otras funciones (o estas mismas) según sea el problema que se esté resolviendo. Para el
caso, en este ejemplo, he usado sintaxis como la siguiente:
r=ps->datos[(ps->top)--];
lo cual, podría haberse hecho en más pasos, de la siguiente forma:
i=ps->top--;
r=ps->datos[i];
y así sucesivamente, por tanto se debe encasillar a que la sintaxis anterior es la única solución viable a
éste problema. Las instrucciones pueden cambiar, pero lo que debe permanecer siempre invariable, es el
algoritmo correspondiente a las funciones anteriores (pop y push).
También el lector se preguntará, el por que hemos usado una variable auxiliar, identificada como k. Pues
bien la respuesta es muy simple, esta variable sirve para controlar el número de impresiones que se
harán en la función pertinente es por ello que esa variable crece (en la función push) y decrece (en la
función pop).
Pilas Implementadas con Memoria Dinámica
Las pilas, que hasta ahora hemos tratado, han sido usando únicamente memoria estática, es decir
definimos ciertos espacios, dentro de los cuales guardamos nuestros datos. Pero, que pasaría, si los
datos superan el espacio reservado en memoria para ellos?.
Es aquí donde surge, la importancia que la pila crezca y decrezca dinámicamente, donde el único límite
existente es la memoria misma de la pc.
La forma de declararlo es la siguiente:
typedef struct _nodo {
int dato;
struct _nodo *siguiente;
} tipoNodo;
Así que sigue siendo muy importante que nuestro programa nunca pierda el valor del puntero al primer
elemento, igual que pasa con las listas abiertas.
Teniendo en cuenta que las inserciones y borrados en una pila se hacen siempre en un extremo, lo que
consideramos como el primer elemento de la lista es en realidad el último elemento de la pila.
Las operaciones básicas siguen siendo las mismas, aunque claro con sus respectivas variantes:
-push, para igresar nuevos valores en la pila
-pop, para eliminar lementos de la pila
Función push (para memoria dinámica)
1. Supondremos que ya se creó un nodo nuevo (hablaremos de eso después), si la pila está vacía,
es decir el puntero que apunta a la pila, está apuntando a NULL.
2. lo que debemos hacer es que, nodo en su parte de siguiente, apunte a NULL
3. Y el puntero que apunta a la pila, apunte ahora al nuevo nodo.
Un nodo, está compuesto del dato (que puede ser entero, real, carácter u otra estructura) y un puntero,
que enlazará éste nodo con el siguiente.
El puntero Pila, apunta a la cima de la pila, cuando no hay datos está inicializada a NULL.
Función Pop
Partiremos del supuesto que exiten más de un nodo en la pila, el algoritmo, sería el siguiente:
Función push
void push(Pila *pila, int v)
{
pNodo *nuevo;
nuevo=(pNodo)malloc(sizeof(tiponodo));
nuevo->valor=v;
nuevo->siguiente=*pila;
*pila=nuevo;
}
en la función anterior, observamos que, no devuelve ningún valor, que recibe como argumentos, la
dirección de la pila y el valor (entero, en éste caso) que vamos a guardar en la pila.
Posteriomente, declara un nuevo puntero que tendrá la dirección del nodo que vamos a crear.
nuevo=(pNodo)malloc(sizeof(tiponodo))
quizá, la línea anterior es una de las más potentes e interesantes que podemos encontrar y es que con
esa línea le estamos diciendo al compilador que vamos a crear un nuevo nodo, del tamaño de tiponodo,
para ello usamos la instrucción malloc, que sive para pedirle al sistema memoria, sizeof, lo que hace es
determinar el tamaño (en bytes), de la variable que le pasamos como argumento y ese valor, es lo que
malloc le pide al sistema, y como éste devuelve un puntero del tipo void, por eso es necesario hacer el
casting: (pNodo)malloc.
Función Pop:
int pop (Pila *pila)
{
pNodo nuevo;
int v;
nodo=*pila;
if(!nodo)
*pila=nodo->siguiente;
v=nodo->valor;
free(nodo);
return (v);
}
Note que ésta función lo único que recibe es el puntero a la pila. La función free(), sirve para liberar ese
espacio de memoria.
Ejemplo 10.3
Código completo para el uso de las pilas
#include <stdio.h>
#include <conio.h>
typedef struct nodo{
int valor;
struct nodo *siguiente;
}tiponodo;
typedef tiponodo *pNodo;
typedef tiponodo *Pila;
void push(Pila *pila, int v);
int pop(Pila *pila);
main()
{
Pila pila=NULL;
push(&pila, 20);
push(&pila, 30);
push(&pila, 40);
printf("%d", pop(&pila));
printf"%d", pop(&pila));
getch();
return 0;
}
void push(Pila *pila, int v)
{
pNodo *nuevo;
nuevo=(pNodo)malloc(sizeof(tiponodo));
nuevo->valor=v;
nuevo->siguiente=*pila;
*pila=nuevo;
}
int pop (Pila *pila)
{
pNodo nuevo;
int v;
nodo=*pila;
if(!nodo)
*pila=nodo->siguiente;
v=nodo->valor;
free(nodo);
return (v);
}
Explicación.
La salida, de éste programa no es muy atractiva, pero hagamos un esfuerzo por comprenser que es lo
que hemos hecho.
Y aunque lo que veamos en pantalla, son sólo unos números, que a lo mejor al principio, no tengamos ni
idea de donde aparecieron, (igual que yo, la primera vez), debemos estar seguros que éste programita
cumple con todas las especificaciones de una pila.
Por ejemplo, las invocaciones de push, mandamos a la pila, los valores de 20, 30, 40. de los cuales el
número 40, es el que se encuentra en la cima de la pila, por eso, al llamar a pop, manda a impresión el
número 40. ahora quien está en la cima es 30, y al volver a invocar a pop, es ese valor el que se
imprime. El lector puede modificar este programa colocando otros valores en push, agregando más
llamadas a pop, etc.
A pesar que el programa anterior es muy apropiado para exponer el funcionamiento de las pilas, hasta
cierto punto, está incompleto, ya que no interactúa mucho que digamos con el usuario.
Por ello mostramos a continucación otro código que es un poco más completo.
En el cual no pasamos parámetros a las funciones, por que las variables son de tipo global, éste es un
método, para hacer las cosas un poco más fáciles.
Ejemplo 10.4
Se desean guadar en una pila, cierta cantidad de nombres, a la vez que el usuario puede eliminar e
imprimir el estado de la pila, según lo desee. Diseñe un programa en C, que de soporte a dicho problema,
usando pilas implementadas con memoria dinámica.
/* Ejemplo de una pila. */
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <alloc.h>
void push(void);
void pop(void);
void visualizar(void);
struct pila
{
char nombre[20];
struct pila *ant;
}*CAB=NULL,*AUX=NULL;
printf("\t\t1.- Insertar\n");
printf("\t\t2.- Extraer\n");
printf("\t\t4.- Salir\n\n");
opc=getch( );
switch(opc)
{
case '1':
push( );
break;
case '2':
pop( );
break;
case '3':
visualizar( );
}
}
getch();
}
void push(void)
{
AUX=(struct pila *)malloc(sizeof(struct pila));
clrscr( );
printf("Nombre: ");
gets(AUX->nombre);
if (CAB==NULL)
{
CAB=AUX;
AUX->ant=NULL;
}
else
{
AUX->ant=CAB;
CAB=AUX;
}
}
void pop(void)
{
if (CAB==NULL) return;
AUX=CAB;
CAB=CAB->ant;
free(AUX);
}
void visualizar(void)
{
if (CAB==NULL) return;
clrscr( );
AUX=CAB;
while (AUX!=NULL)
{
printf("Nombre: %s\n",AUX->nombre);
AUX=AUX->ant;
}
getch( );
}
Explicación
Éste ejemplo es un poco más completo, ya que le permite al usuario, interactuar, al poder ingresar los
datos cómo él los desee, además que, note, que en las funciones (push, pop e insertar) no mandamos
parámetros. Ya que éstos son declarados fuera del main(), por tanto se pueden acceder a ellas, por
cualquier función dentro del programa. Lo cual puede ser muy útil, cuando estamos trabajando, por
ejemplo, con cadenas de caracteres, como en éste caso.
Evaluciones de Expresiones
Las expresiones aritméticas que hasta ahora hemos usado, las encontramos en forma infija, es decir
usando (), *,/,-,+...
Ejemplo:
X=z+(b+c)^(q-m)-(b*c)
A la expresión anterior también algunos autores la llaman, interfija. Sin embargo las expresiones tienen
otras formas de poderlas representar, por ejemplo: la notación posfija o también conocida como polaca
inversa (en honor al matemático de origen polaco, que la propuso), y ésta consiste en colocar el operador
a continuación de los operandos. Por ejemplo:
A*b/(c-d) /*infija*/
A*b/c-d /*quitamos paréntesis*/
Ab*/cd-/*signo delante de operador*/
Ab*cd-/ /*posfija*/
Otro ejemplo:
A+(B*C)
A+B*C
A+BC*
ABC*+
Ahora bien, lo anterior podría parecer bastante complejo, y ciertamente lo es, por lo cuala continuación
mostramos el algoritmo para pasar una expresión de infija a posfija:
1. leer la cadena de caracteres, y repetir el paso 2 al 4 por cada carácter
2. si es un operando, pasarlo a expresión posfija
3. si es un operador:
3.1 si la pila está vacía, meterlo en la pila. Repetir desde 1
3.2 si la pila no está vacía:
-si la prioridad del operador leído es mayor que la prioridad del operador cima de la pila,
meterlo en la pila y repetir dede 1
-si la prioridad del operador es menor o igual que la prioridad del operador de la cima de
la pila, sacar operador cima de la pila y pasarlo a la expresión posfija, volver a 3
4. si es paréntesis derecho:
4.1 sacar operador cima de la pila y pasarlo a la expresión posfija
4.2 si nueva cima es paréntesis izquierdo suprimir elemento cima
4.3 si cima no es paréntesis izquierdo, volver a 4.1
4.4 volver a partir de 1
5. si quedan elementos en la pila pasarlos a la expresión posfija
6. fin del algoritmo
(Algoritmo tomado de: “Algoritmos y estructuras de datos, una perspectiva en C”. Joyanes Aguilar, Luis.
Madris 2004. Pág. 329)
el código sería el siguiente:
Ejemplo 10.5
#include<stdio.h>
#include<string.h>
#define n 60
}
else{ // comparando prioridad de operadores del tope y el entrante
comparador=ver(p);
//comparador tiene el valor de la cima
if(auxiliar.prioridad > comparador.prioridad)
{ if(auxiliar.prioridad==6) //si es operador de apertura
auxiliar.prioridad=0; //en la pila su prioridad es 0
push(p,auxiliar);
bandera=1;
}
else
{ //si operado entrante es menor o igula que el de la pila
auxiliar=pop(p);//sacar operandor de la cima de la pila 4
j+=1;
expos[j]=auxiliar.caracter; //agregarlo a expresion posfija
bandera=0; //volver a evaluar evaluar operador entrante regreso a
}
}
}while(bandera==0);
}//cierra 4
}
}//cierra 1
while(empty(p)!=1)//sacar todos los operadores sobrantes en pila
{
auxiliar=pop(p);
j+=1;
expos[j]=auxiliar.caracter;
}
//impresion de resultado
marco();
printf("\n\nLA EXPRESION EN NOTACION INFIJA ES :\n ");
printf("%s",expin);
printf("\n\nLA EXPRESION EN NOTACION POSFIJA ES :\n");
for(i=0;i<=j;i++) {
printf("%c",expos[i]);}
break;
case '*': auxiliar.prioridad=4;
auxiliar.caracter='*';
break;
case '/': auxiliar.prioridad=3 ;
auxiliar.caracter='/';
break;
case '+': auxiliar.prioridad=2 ;
auxiliar.caracter='+';
break;
case '-': auxiliar.prioridad=1 ;
auxiliar.caracter='-';
break;
case '(': auxiliar.prioridad=6;
auxiliar.caracter='(';
break;
case '[': auxiliar.prioridad=6 ;
auxiliar.caracter='[';
break;
case '{': auxiliar.prioridad=6 ;
auxiliar.caracter='{';
break; }
return auxiliar;
}
bandera=0;
if (((expin[i-1]>=65 )&&(expin[i-1]<=90)) || ((expin[i-1]>=97 )&&
(expin[i-1]<=122)) || (expin[i-1]==41 )||
(expin[i-1]==93) || (expin[i-1]==125 ))
bandera=0;
}
}
for(i=0;i<=(strlen(expin)-1);i++)
{
switch(expin[i])
{
case '(':parentesis+=1;
numero[0]+=1;
break;
case '[':parentesis+=2;
numero[1]+= 2;
break;
case '{':parentesis+=3;
numero[2]+=3;
break;
case ')':parentesis-=1;
numero[0]-=1;
if(parentesis<0) bandera=0;
break;
case ']':parentesis-=2;
numero[1]-=2;
if(parentesis<0) bandera=0;
break;
case '}':parentesis-=3;
numero[2]-=3;
if(parentesis<0) bandera=0;
break;
}
}
return bandera;
}//terminacion de función validación expresión
int full (struct pila *p) //funcion full devuelve 1 si la pila esta llena
{
if (p->tope==4)
return 1;
else
return 0;
}
p->tope=-1;
}
int empty(struct pila *p)// funcion empty devulve 1 si la pila esta vacia
{
if (p->tope==-1)
return 1;
else
return 0;
}
struct operador pop(struct pila *p) //funcion pop quita el dato que
{ //se encuntre en la cima de la pila
struct operador auxiliar;
if (empty(p)==1)
{
auxiliar.caracter='0';
}
else
{
auxiliar= p->vect[p->tope];
p->tope--;
};
return auxiliar;
};
}
void error()
{
printf("Posibles razones:\n");
printf("1-hay dos operadores juntos\n");
printf("2-hay dos operandos juntos\n");
printf("3-la expresion comienza con operador\n");
printf("4-la expresion termina con operador\n");
NOTA: el código anterior fue elaborado por : Nelson Eduardo Najarro Alvarez, para la Asignatura de
Programación II, de la Carrera de Ingeniería de Sistemas Informáticos de la Universidad de El Salvador.
El ejemplo anterior es baste complejo y muy largo, pero decidí colocarlo, por que en muchos libros sólo
colocan las funciones o trozos del código, pero como aquí queremos hacer las cosas bien, hemos
decidido colocar el código completo.
Además que en muchas universidades o colegios, dejan como tarea éste código, así creo que te servirá
mucho.
Cuestionario
1. ¿Qué es un TAD?
__________________________________________________________________________
___________________________________________________________________
2. ¿Cuál es la importancia del TAD?
__________________________________________________________________________
_________________
3. ¿Para que se usan las pilas?
__________________________________________________________________________
________________________________________________________________
4. ¿Cómo funcionan las pilas?
__________________________________________________________________________
________________________________________________________________
5. Mencione y explique las implementaciones de Pilas en
C:_________________________________________________________________________
_____________________
ptrTemp = *ptrCima;
valorElim = ( *ptrCima )->dato;
*ptrCima = ( *ptrCima )->ptrSiguiente;
free( ptrCima);
return valorElim;
Ejercicios
1. Un vector en física, puede representarse mediante sus componentes x, y, z; de la siguiente
forma: V=2i + 4j –6k. Cuando una de las componente está ausente, simplemente no se escribe
(o la podemos representar con un cero: V= 0i + 4j + 6k.) Cree un TAD que lea un vector y luego
calcule:
-Su magnitud (||V||=sqrt(a^2+b^2+c^2)). Donde a,b y son los coeficientes
-Su vector unitario: U=a/||V|| + b/||V|| + c/||V||
2. los números radicalesse componen de una base y un signo radical, diseñe un programa que sea
capaz de implementar un TAD llamado radical y permita:
-sumarlos, restarlos o dividirlos; según lo desee el usuario.
3. Disñe un TAD que represente un tipo cadena (string), y que permita las siguientes operaciones:
leer la cadena, imprimirla, copiarla, determinar el tamaño de la cadena y buscar un carácter
específico en la cadena.
4. se desea transformar un número base10, a binario; utilizando una pila, en la cual se guarden los
residuos de las divisiones. A partir de las impresiones generadas por la pila, muestre el número
binario equivalente.
5. A partir de un párrafo ingresado por el usuario, diseñe un programa que, determine el número de
consonantes y vocales que están presentes en dicho párrafo.
6. Diseñe un programa que lea una cadena de caracteres y luego la imprima en forma inversa
7. Se desea crear una pila en c, que lea cierta cantidad de números enteros y luego muestre:
-La suma de todos los números y el producto de ellos, cuántos son mayores que cero.
8. una palabra es polindromo si se lee igual por la derecha que por la izquierda, diseñe un programa
que lea una palabra, luego imprim esa misma palabra al revés y con un mensaje indique si es o
no palíndromo.
9. En una tienda de repuestos llevan su control de inventarios, mediante el sistema UEPS (últimos
en entrar, primeros en salir), el cual puede ser controlado mediante una estructura del tipo Pila;
en ésta tiendo necesitan que se registren sus productos, los cuales tendrán los siguientes datos:
código, descripción, precio unitario, fecha de ingreso (del sistema). Además de registrar las
órdenes de salida y mostrar los siguientes reportes: ventas por fecha, ventas por producto
(ingresando el código), cantidad de productos que exceden un precio determinado. El cual será
ingresado por el usuario.
Concepto de Cola
Una cola, es una estructura en la cual se almacenan elementos (en orden de llegada), es decir que, se
ingresan los elementos por la parte final de la estructura y se eliminan (o se sirven) por la parte del frente.
Como puede observarse, ésta estructura cuenta con dos apuntadores, uno que apunta al último elemento
y otro que apunta hacia el primer elemeto (o el elemento del frente).
Se debe tener en cuenta que, una cola, almacena los datos en ella, manteniendo cierto orden, ya que sus
elementos se añaden por el final de la cola y se extraen o se eliminan por la parte de frente.
El lector dispernsará el por que la insistencia de ello, pero cuando uno inicia este estudio, al momento de
programar, se cunfe fácilmente, por que las sentencias de las funciones son muy parecidas a la de una
pila, y en algunas ocaciones me pasaba que empezaba programando una cola, pero terminaba
funcionando como pila.
Las operaciones con las colas son:
-insert (q, x) Inserta el elemento x en la parte posterior de la cola q
Las colas pueden implementarse, al igual que las pilas, mediante dos formas:
Arrays
Memoria Dinámica
La figuara de arriba, muestra la forma de implementar una cola, como arreglo, en la que cada casilla,
representa una estructura compuesta por el tipo de dato a guardar (o bien otra estructura).
Las variables q.rear y q.front, se van modificando cada vez que añadimos o eliminamos datos de nuestra
cola.
Para determinar la cantidad de elementos en cualquier momento es:
Cant=q.rear-q.front+1
Ahora vemos un ejemplo del funcionamiento de las colas, implementadas como arreglos:
Supongamos que en una cola vamos a almacenar elementos de tipo carácter.
Insert(&q, ‘A’);
q.rear=0, q.front=0
insert (&q, ‘B’)
q.rear=1
q.front=0
q.rear=2
q.front=0
remove(&q);
q.rear=2
q.front=1
remove(&q);
q.rear=2
q.front=2
insert(&q, ‘D’);
C D
0 1 2 3 4
q.rear=3
q.front=2
insert(&q, ‘E’);
q.rear=4
q.front=2
Como se puede ver, al manejar una cola en forma de arreglolineal, resulta complicado y poco eficiente,
por que al eliminar elementos quedan nodos vacíos y cuando se llega al último elemento de la cola,
aparecerá un mensaje de error, (o al menos debería aparecer), que indioque que ya no hay más
espacios, sin embargo, eso sería una total mentira, ya que tenemos elementos sin utilizar; una solución
para ello podría ser que cada vez que se eliminen un elemento mover todos los datos hacia la izquierda,
pero, te imaginas la cantidad de código para realizar esa acción???... eso es muy complejo (y no resuelve
el problema de la eficiencia), entonces es donde surge, el considerar la cola, como un arreglo circular.
Pero como puede verse, el manero de los subíndices, resulta bastante complejo, poco accesible y,
tampoco se resuelve el problema de la eficiencia; es decir que ésta solución tampoco resulta eficiente
para el problema que se intenta resolver, por esas consideraciones no trataremos acerca de esta
estructura, en forme de arreglo, por que no nos resulta viable, nos concentraremos en la implementación,
usando memoria dinámica, la cual, si resulta más eficiente y hasta cierto punto menos compleja.
Colas implementadas con Memoria Dinámica
Para hacer uso de las colas, debemos declarar el nodo y los apuntadores de la cola, así:
typedef struct _nodo {
int dato;
struct _nodo *siguiente;
} tipoNodo;
Donde, es evidente que, tipoNodo es el tipo de los nodos que compondrán la cola.
pNodo es el tipo para declarar punteros a un nodo.
Cola es el tipo para declarar colas
Ejemplo 11.1
Se desea almacenar cierto número de enteros en una estructura de tipo Cola, diseñe una solución que
permita, leer, eliminar datos de la cola.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
/*declaracion de la cola*/
struct nodo
{
int elemento;
struct nodo *siguiente;
};
typedef struct nodo Nodo;
typedef struct
{
Nodo *frente;
Nodo *final;
}Cola;
main()
{
int x, opc=8, j=0;
Cola cola;
CrearCola(&cola);
clrscr();
while(opc!=3)
{
printf("\t\t\tMENU PRINCIPAL\n\n\n");
printf("\t 1. Insertar\n");
printf("\t 2. Eliminar\n");
printf("\t 3. Salir\n");
scanf("%d", &opc);
switch(opc)
{
case 1: printf("Ingrese el numero a introducir:\n");
scanf("%d", &x);
insert(&cola, x);
++j;
break;
case 2: printf("%d fue eliminado de la cola\n", remover(&cola));
--j;
getch();
break;
}
clrscr();
}
getch();
return 0;
}
if(empty(*cola))
{
cola->frente=nuevo;
}
else
cola->final->siguiente=nuevo;
cola->final=nuevo;
}
/*elimina el elemento que esta aL frente de la cola*/
int remover (Cola *cola)
{
int temp=NULL;
if(!empty(*cola))
{
Nodo *nuevo;
nuevo=cola->frente;
temp=cola->frente->elemento;
cola->frente=cola->frente->siguiente;
free(nuevo);
}
else
printf("ERROR, cola vacia, se puede eliminar\a\n");
return (temp);
}
Explicación
Como puede notarse, hemos implementado, de una manera muy sencilla, los algoritmos para ingresar y
eliminar datos en una cola, note que hemos declarado dos estructuras para la cola, es decir que, una de
ellas contiene la parte entera de los datos que vamos a guardar y el apuntador hacia el siguiente nodo;
mientras que la otra estructura, cuenta con los apuntadores del frente y del final.
Algo importante que hay que resaltar es el hecho que, en la zona de declaración de variables, debemos
declarar la varible de tipo Cola que es el parámetro que le pasaremos a las diferentes funciones; muchas
veces se nos olvida hacer eso y por lo cual, intentamos, erradamente, mandarle la dirección del tipo de
dato, es decir; que en vez de mandarle la dirección de la variable cola (&cola), le mandamos la dirección
del TAD Cola (&Cola), lo cual no sería correcto.
Apartir de ello, creamos la cola, como lo explicamos, haciendo que cola->frente=cola->final=NULL; con lo
cual nos aseguramos que la cola esté vacía, y lista para agregar valores. Las demás funciones, sólo son
la implementación rigurosa de los algoritmos planteados con anterioridad.
No es importante aprenderse al pié de la letra, cada una de las funciones, ya sea para la pila o para las
colas, si no que, es importante, aprenderse lo que hace cada función, tener presente el algoritmo e
implementarlo según convenga al problema que estamos resolviendo.
Ejemplo 11.2
Cree una cola en C, que permita introducir y realizar un suceso por el usuario, el cual debe ser una
arreglo unidimensional; sin embargo, la cola debe ser implementada dinámicamente.
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct datos elemento;
struct datos {
char suceso[81];
elemento *siguiente;
};
void error (void)
{
printf("Error, insufuciente espacio en memoria\n\a");
exit(1);
}
elemento *nuevoelemento (void)
{
elemento *q=(elemento*)malloc(sizeof(elemento));
if(!q)error();
return q;
}
void menu (void);
void introducir (elemento**, elemento**, char[]);
char *realizar (elemento**, elemento**);
main()
{
elemento *principio, *final;
char opcion, suceso[81];
principio=final=NULL;
while(1){
do{
clrscr();
menu();
opcion=toupper(getche());
}while(opcion!='I' && opcion !='R' && opcion != 'S');
clrscr();
switch(opcion){
case 'I': printf("\nIntroduzca un suceso:\n");
gets(suceso);
introducir(&principio, &final, suceso);
break;
case 'R': strcpy(suceso, realizar(&principio, &final));
if(*suceso)
printf("\realizando el suceso %s", suceso);
printf("Pulse una tecla para continuar:\n");
getch();
break;
case 'S': exit(0);
}
}
}
void menu(void)
{
printf("\n\tIntroducir suceso");
Cuestionario
1. ¿Por qué las Colas se consideran estructuras del tipo FIFO?
____________________________________________________________________________
_________________________________________________________________
Ejercicios
1. Escribir un programa en C, que lea cierca cantidad de enteros y luego determine: Cuál es el valor
mayor, el valor menor y el promedio de todos los datos.
2. Diseñe un programa que sea capaz de leer dos colas y mediante un mensaje indicar si son
iguales. Nota: los elementos constitutivos de las colas son caracteres.
3. En un supermercado, se tiene sólo una caja habilitada para que los clientes puedan cancelar sus
compras, se pide que, el sistema muestren la cantidad de productos comprados, el monto total de
la venta.
4. Una tienda dispone de 10 repartidores, para las entregas a domicilio, genere un programa que
simule: el despacho de cada repartidor, sabiendo que si la entrega se realiza después de 30
minutos de realizada la orden, al cliente se le aplica un 30% sobre la compra. El programa debe
mostrar: el total de entregas por repartidor, el monto de la ganancia, y las pérdidas, en concepto
de entregas tardías.
5. En la empresa “La Bodeguita”, se lleva el control de los inventarios, por el método PEPS,
(Primeros en entrar, Primeros en Salir). Y se desea mecanizar este proceso, para ello, se deben
ingresar los productos, y para registrar la venta, se necesitan los siguientes datos: Código,
correlativo de orden, descripción, cantidad y precio. El sistema debe generar: el informe de todas
la ventas, el artículo que más se ha vendido, y el que menos, así como la ganancia de la
empresa.
Identifique, y corriga, los posibles errores que están presentes en las siguientes funciones:
int empty(Cola cola)
{
return (cola.frente);
}
Nodo *CrearNodo(int x)
{
aux=(Nodo*)malloc(sizeof(Cola));
aux->elemento=x;
aux->siguiente=NULL;
return aux;
}
En muchos cursos, libros y Manuales, inician el estudio de las Estructuras de Datos Dinámicas, hablando
acerca de las Listas, sin embargo, éste manual ha sido la excepción, ya que considero que, el estudio de
las listas, posterior al conocimiento y manejo de las Pilas y Colas, se hace mucho más comprensible.
Como se observa en la imagen, los nodos de las listas al igual que las colas y pilas, está compuesta por
una parte de información (que pude ser datos enteros, flotantes, caracteres, estructuras..) y el puntero
que mantiene el enlace entre un nodo y otro.
Existen varios tipos de Listas, pero para efectos de comprensión y sintetización, hablaremos de cutro
tipos esenciales de listas:
Tipos De Listas
1. Lista simplemente enlazada: Cada nodo, contiene un único apuntador hacia el siguiente nodo,
por lo cual hace de él una estructura muy eficiente, ya que el último de la lista apunta hacia null,
por ello, es fácil hacer recorridos directos.
2. Listas Doblemente enlazada: Esta lista se caracteriza por que sus nodos contienen dos punteros,
uno hacia el nodo siguiente y otro hacia el nodo anterior.
3. Listas Circulares: Este tipo de lista, es sólo una extención de las lista simplemente enlazada, con
la diferencia que el último elemento se enlaza al primer elemento de la lista, lo cual permite el
recorrido en forma de anillo
4. Lista Circular Doblemente enlazada: Quizá este tipo de lista, sea la más compleja, ya que es la
combinación de las lista circular y las doblemente enlazadas, ya que es una lista doblemente
enlazada donde el primer elemento se conecta con el último y viceversa.
Ahora el lector comprende, el por que, si hablamos de estos tópicos al inicio, a lomejor, hubiera
desistido de leer éste manual, y es que, al tener la experiencia de haber trabajado con estructuras
como pilas y colas, antes de listas, hace que uno comprenda mucho mejor, los conceptos y
algoritmos de éste tipo de estructuras.
El TAD Lista
En una lista podemos almacenar datos del mismo tipo, con la característica que puede contener un
número indeterminado de elementos y que, mantienen un orden explícito, por que cada elemento, se
une a otro mediante un puntero, como ya se ha dicho anteriormente, los elementos constitutivos de
las listas se denominan nodos.
Las listas son estructuras de datos dinámicos, por tanto, pueden cambiar de tamaño durante la
ejecución del programa, aumentando o disminuyendo el número de nodos.
Un aspecto importante de las listas es que las inserciones, las podemos hacer por el frente, al final,
en medio, después de..., etc, etc, etc; es decir que, no existen reglamentos que nos restringan añadir
datos a una lista, en la posición que nosotros querramos.
De igual manera, para las eliminaciones de nodos, podemos hacerlo como nosotros lo querramos, si
embagargo, se acostumbra ingresando el campo de información o dato que se desea eliminar.
Operaciones con las listas
P: puntero a un nodo
L: puntero a la lista
ListaVacia(L): Iniciliza la lista L, como lista vacía
Insertar(L, x, p): Inserta al dato x, en un nuevo nodo de la lista L, después del nodo apuntado por p
Se puede decir que, estas son las operaciones básicas para una lista; sin embargo, como ya se ha
insistido, eso dependerá del programador y de la complejidad del problema que se está resolviendo,
además del tipo de lista que se haya elegido.
Para ello, acontinuación hablaremos, por separado, de cada uno de los tipos de listas.
Una estructura como ésta, requiere, que se tengan en cuenta, las operaciones básicas que, se
realizarán:
Estructura del Nodo
Por ejemplo, la podemos definir así:
struct nodo{
int x;
struct nodo *sig;
};
p=getnodo();
Función getnodo()
Esta función, se utiliza para pedirle memoria a la computadora, lo cual puede realizarse en las
misma función de insertar, pero para tener un mekor orden, es mejor hacerlo por aparte.
Por tanto, es evidente que, ésta función lo que devuelve es una dirección de memoria.
Lista getnodo()
{
Lista p;
p=(Lista)malloc(sizeof(struct nodo));
return p;
}
Algoritmo:
1. Crear un nodo auxiliar apuntado por q.
2. si p, apunta a nullo p->sig apunta a NULL, imprima mensaje de error
3. sino; q, en suparte de siguiente, debe tener la dirección a la que apuntaba, p->sig.
4. p->sig debe apuntar a q en su parte de siguiente.
5. Liberar de memoria el nodo apuntado por q.
void delafter(Lista p, char *px)
{
Lista q;
If(p==NULL || p->sig==NULL)
Printf(“ERROR, lista vacía\a\n”);
Else
{
q->sig=p->sig;
p->sig=q->sig;
free(q);
}
}
Con ésta función, lo que hacemos es inicializar la lista a NULL, por lo que se pierden los elementos que
habíamos guardado en los nodos. Pero Ojo, eso no significa que hayamos liberado memoria que
ocuparon, los nodos, esa memoria será liberada, cuando se deje de ejecutar el programa, o si
hubiésemos, utilizado la función free(), para cada nodo.
Función Buscar
Ésta función, nos devuelve, la dirección de memoria de un valor que deseamos buscar en la lista.
Ejemplo 12.1
Se pide que, cree una agenda, donde pueda almacenar el nombre, teléfono y correo electrónicode sus
amigos; haciéndo uso de una lista enlazada. Dicha agenda, debe permitirle: añadir un nuevo registro,
eliminar y Mostrar la lista de todos los registros.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
struct nodo{
int corre;
char nom[80];
char tel[9];
char email[50];
struct nodo *sig;
};
typedef struct nodo *Lista;
Lista p, cabeza;
Lista getnodo();
void insafter(Lista p, char nom[80], char tel[9], char email[50], int i);
void eliminar (Lista p, int k);
void imprimir(Lista p);
main()
{
char nom[80], tel[9], email[50];
int k, opc=8, i=0;
clrscr();
p=getnodo();
cabeza=p;
while(opc!=4)
{
printf("\t\t\nMENU PRINCIPAL\n\n\n");
printf("\t\t1. Registrar Nuevos Datos\n");
printf("\t\t2. Imprime todos los registros\n");
printf("\t\t3. Eliminar Datos\n");
printf("\t\t4.Salir\n");
scanf("%d", &opc);
switch(opc)
{
case 1: printf("Ingrese el Nombre:");
scanf("%s", &nom);
printf("Telefono:");
scanf("%s", &tel);
printf("e-mail:");
scanf("%s", email);
i++;
insafter(&p, nom, tel, email, i);
break;
case 2: printf("Listado de todos los registros\n\n");
imprimir(&p);
break;
case 3: printf("¨A quien desea eliminar?(ingrese el correlativo)\n");
scanf("%d", &k);
eliminar(&p, k);
break;
}
clrscr();
}
return 0;
}
Lista getnodo()
{
Lista p;
p=(Lista)malloc(sizeof(struct nodo));
if(p==NULL)
printf("Memoria Insuficiente\a\n");
return p;
}
if(p==NULL)
printf("ERROR, lista vac¡a\n\a");
else
{
q=getnodo();
strcpy(q->nom, nom);
strcpy(q->tel, tel);
strcpy(q->email, email);
q->corre=i;
q->sig=p->sig;
p->sig=q;
p=p->sig;
}
}
void imprimir(Lista p)
{
Lista dir;
p=p->sig;
for(dir=p; dir!=NULL; dir=dir->sig)
{
printf("\n\t***********************************\n");
printf("\t correlativo: %d\n", dir->corre);
printf("\t Nombre %s\n", dir->nom);
printf("\t Telefono: %s\n", dir->tel);
printf("\t e-mail: %s\n", dir->email);
printf("\n\t***********************************\n");
getch();
}
}
void eliminar(Lista p, int k)
{
Lista indice;
cabeza=p;
if(indice->corre==k)
{
cabeza=cabeza->sig;
printf("%s est hiciendo eliminado\n", indice->nom);
getch();
if(p==NULL || p->sig==NULL)
printf("ERROR, ya no hay m s datos\n");
else
{
cabeza->sig=indice->sig;
free(indice);
}
}
}
}
Hata ahora, los recorridos que hemos realizado en las listas; han sido en sentido directo, pero existen
muchos casos en los que es necesario acceder a los elementos de las estructuras en ambos sentidos
(adelante y por detrás), de ahí es donde se deriva la importancia de esta estructura.
La declaración de la estructura puede ser:
Las operaciones básicas, siguen siendo las mismas; aunque clara con sus variantes, por que recordemos
que, en éste tipo de estructuras estamos manejando dos punteros, en un solo nodo.
Insertar
Para insertar en un lista, existen algunos mecanismos, casos, formas, etc.
Por ejemplo:
INSERTAR AL FRENTE DE LA LISTA
q->sig=p;
}
Función getnodo()
tLista getnodo()
{
tLista nuevo;
nuevo=(tLista)malloc(sizeof(Lista));
if(nuevo==NULL)
printf(“Memoria insuficiente\a\n”);
nuevo->sig=nuevo->ant=NULL;
return nuevo;
}
Función Eliminar
Esta función, se encarga de borrar, el nodo apuntado por “p”, encontrado con la función posición y
considere eliminación al inicio, en medio y al final.
Algoritmo:
1. Busque el nodo que contiene el dato que desea eliminar , teniendo el cuidado de guardar la
dirección del nodo a eliminar y la dirección del nodo anterior a éste.
2. la parte siguiente del nodo anterior debe apuntar al puntero sifguiente del nodo a eliminar.
3. la parte anterior del nodo siguiente a eliminar debe apuntar a la parte a lo que apuntaba la parte
anterior del nodo a eliminar.
4. en caso de que el nodo a eliminar sea el primero en la lista, se modifica la cabeza para que tenga
la dirección del nodo siguiente.
5. finalmente liberamos la memoria ocupada.
else
actual->ant->sig=NULL;
free(actual);
}
}
Ejemplo 12.2
/*******************************************************************
* LISTA.C *
* Objetivo del programa: Realizar un programa que de de altas, *
* busque y liste una lista de tipo estructura dinamica *
* doblenmente ligada *
* Versi›n: 2.1 *
* Autor: JOSE LUIS SANCHEZ FERRUSCA *
* Fecha: 6/04/2005 *
*******************************************************************
void main()
{
op=1;
nuevo=NULL; /* El apuntador esta vacio */
primero=NULL; /* El apuntador esta vacio */
recorre=NULL; /* El apuntador esta vacio */
do
{
/* clrscr(); */
switch(opcion)
{
case 1: altas();
break;
/*case 2: busqueda();
break;*/
case 3: listado();
break;
case 4: baja();
break;
}while (opcion!=5);
scanf ("\n");
}
else
{
do
{
if (primero==recorre)
{
if (nuevo->numero<primero->numero)
{
primero=nuevo;
primero->sig=recorre;
recorre->ant=nuevo;
}
else
{
primero->sig=recorre;
recorre->ant=primero;
}
}
else if (recorre->sig!=NULL)
{
if (nuevo->numero<recorre->numero)
{
anterior=recorre->ant;
nuevo->ant=anterior;
nuevo->sig=recorre;
anterior->sig=nuevo;
recorre->ant=nuevo;
}
}
else
{
recorre->sig=nuevo;
nuevo->ant=recorre;
}
}while(recorre!=NULL);
}
}
/********************************************************************
* Funci¢n: Busqueda *
* Argumentos: void - No recibe argumentos *
* - * *
* Valor de Retorno: Void -> No regresa valores *
* Comentario: Esta función va a hacer una busqueda en la lista *
* *
********************************************************************/
void busqueda()
{
int bus;
recorre=primero;
printf ("\nINGRESE EL NUMERO A BUSCAR\n");
scanf ("%d",&bus);
do
{
if (bus==recorre->numero) /* El dato a buscar se encuentra en recorre en su campo
numero ?*/
{
printf ("\n SE HA ENCONTRADO EL NUMERO %d\n ",bus);
recorre=recorre->sig;
}
else
recorre=recorre->sig;
}while (recorre!=NULL);
}
/********************************************************************
* Funci¢n: listado *
* Argumentos: void - No recibe argumentos *
* - * *
* Valor de Retorno: Void -> No regresa valores *
* Comentario: Esta función va a imprimir la lista *
* *
********************************************************************/
void listado()
{
recorre=primero;
while (recorre!=NULL)
{
printf ("\n NUMERO--> %d\n",recorre->numero);
recorre=recorre->sig;
}
}
/********************************************************************
* Funci¢n:Baja *
* Argumentos: void - No recibe argumentos *
* - * *
* Valor de Retorno: Void -> No regresa valores *
* Comentario: Esta función va a dar de baja los datos en la lista *
* *
********************************************************************/
void baja()
{
recorre=primero;
anterior=primero;
printf ("\nINGRESE EL NUMERO A BUSCAR\n");
scanf ("%d",&bus);
do
{
if (bus==recorre->numero) /* El dato a buscar se encuentra en recorre en su campo
numero ?*/
{
if (recorre==nuevo)
{
nuevo=nuevo->ant;
free(recorre);
nuevo->sig=NULL;
}
}
else
{
anterior->sig=recorre->sig;
recorre=anterior->sig;
free(primero);
primero=recorre;
anterior=primero;
}
}
else
if (recorre==primero)
recorre=recorre->sig;
else
{
recorre=recorre->sig;
anterior=anterior->sig;
}
}while (recorre!=NULL);
}
NOTA: Este código ha sido elaborado por JOSE LUIS SANCHEZ FERRUSCA, aunque, por razones de
didáctica, he hecho algunas modificaciones.
Listas Circulares
Se puede afirmar que, las listas circulares, son un caso especial de las listas simples, con la variante que
el último nodo, en su parte de siguiente, apunta al primer nodo de la lista.
La parte de dato, de la lista circular, puede estar compuesta de enteros, punto flotante, caracteres,
arreglos o estructuras.
Declaración de la estructura
Typedef struct elemento{
Int dato;
Struct nodo*sig;
}nodo;
lc *Lista=NULL;
Función getnodo()
lc getnodo()
{
lc nuevo;
nuevo=(lc)malloc(sizeof(nodo));
nuevo->dato=x;
nuevo->sig=nuevo;
/* para que sea circular debe apuntar a sí mismo*/
return nuevo;
}
Función Ingresar
Void insertar (lc *Lista, int x)
{
lc p;
p=getnodo(x);
if(*Lista==NULL) /*si hay elementos en la lista*/
{
p->sig=(*Lista)->sig;
(*Lista)->sig=p;
}
*Lista=p;
}
Función Eliminar
Éste algoritmo es muy parecido al de una lista simple, ya que ésta estructura, como ya se ha dicho, es un
caso especial de la lista lineal, sin embargo, existen unas pequeñas consideraciones a tener en cuenta.
Algotitmo
1. Se debe buscar el nodo que contiene el dato que desea eliminar
2. luego se debe enlazar el nodo anterior con el siguiente
3. si el nodo a eliminar está apuntado por “Lista”, es decir el puntero de acceso, se modifica “Lista”
para que contenga la dirección del nodo anterior a éste.
4. finalmente se libera el nodo de la memoria
void eliminar (lc *Lista, int x)
{
lc *actual;
int encontrado=0;
if((*Lista)==NULL)
printf(“No hay elementos en la lista\n”);
else
{
actual=*Lista;
/*empezamos a buscar*/
while((actual->sig!=Lista) && (!encontrado))
{
encontrado=(actual->sig->dato==x);
if(!encontrado)
actual=actual->sig;
encontrado=(actual->sig->dato==x);
/*enlazamos el nodo abterior con el nodo siguiente*/
if(encontrado)
{
lc=p;
p=actual->sig; /*nodo a eliminar*/
if(*Lista==(*Lista)->sig)
*Lista=NULL;
else
{
if(p==*Lista)
*Lista=actual;
actual->sig=p->sig;
}
free(p);
}
}
}
Ejemplo 12.3
Las reglas de la divisibilidad indican que, si un número termina en cero o cifra par, es divisible por dos; y
si la suma de sus elementos es tres o múltiplo de tres, entonces, ese número es divisible por tres.
Además que, si un núero es divisible por dos y por tres al mismo tiempo, entonces es divisible por seis.
(ejemplo 12. Termina en cifra par, 2+1=3, es divisible por 2 y por tres, entonces también es divisible por
6), diseñe un programa que, que almacene en una lista circular, y muestre por cual de esos tres números
(2 , 3 y 6) es divisible.
lc *Lista;
int k=0;
while(i<=k)
{
sum=sum+aux->x;
aux=aux->sig;
i++;
}
if(sum%3==0)
{
div3=1;
printf("El numero es divisible entre tres\n");
aux=(*Lista);
if(aux->x%2==0)
{
div2=1;
printf("El numero es divisible entre Dos\n");
}
if(div2==1 && div3==1)
printf("Tambien es divisible entre 6\n");
getch();
}
Explicación
Lo primero que hacemos, es almacenar el número en un arreglo, auxiliar, ya que cada elemento del
número lo guardamos en una casilla del arreglo, luego, vamos pasando cada número como parámetro a
la función insert(), ya que en ella creamos los nodos y en sí, la lista circular. Una vez que, hemos llenado
la lista, pasamos el puntero de acceso a la función divi(), en la cual determinamos los núemeros por los
cuales es divisible.
Éste tipo de listas, es una combinación de las listar cicular y la lista doblemente enlazada, puesto que
cada nodo está conectado con el siguiente nodo y el anterior a él, además que el primer nodo está
conectado al último, y el último al primero.
Es por esa razón que, particularmente consideto que, ésta estructura es una de las más complejas de
manejar, por las consideraciones que debemos tener.
Declaración de la estructura
Typedef struct celda{
Int elemento;
Struct nodo*sig, ant;
}tipocelda;
Función getnodo()
tLista getnodo()
{
tLista L;
L=(tLista)malloc(sizeof(tipocelda));
If(L==NULL)
Printf(“ERROR: Memoria Insuficiente\a\n”);
L->sig=L->ant=L;
Return L;
}
Función Insertar
Algoritmo:
1. Crear el nuevo nodo
Función Buscar
Esta función recibe como argumento un dato a buscar, dentro de la lista y devuelve el nodo que contenga
dicho dato.
tPosicion buscar (int x, tLista L)
{
tPosicion p;
int ban=0;
p=L->sig;
while((p!=L) && (!ban))
if(p->elemento==x)
ban=1;
else
p=p->sig;
return p;
}
Ejemplo 12.4
Diseñe una lista circular doblemente enlazada, que gaurde enteros, y luego permita determinar cuantas
veces se encuentra un número ingresado por el usuario.
#include <stdio.h>
#include <conio.h>
#include <string.h>
/*Version Circular doblememente enlazada*/
typedef struct tipoNodo{
int x;
struct tipoNodo *adelante;
struct tipoNodo *atras;
}Nodo;
/*Declaracion de los sinonimos para referirnos al tipo de dato*/
typedef Nodo *tLista;
getch();
return 0;
}
/*definici¢n de las funciones*/
void insertarPrim(tPosicion cabeza, int entrada)
{
tPosicion nuevo;
/*creamos un nuevo nodo y le asignamos la direccion de memoria*/
nuevo=(tPosicion)malloc(sizeof(Nodo));
if(nuevo==NULL)
printf("ERROR\n");
nuevo->x=entrada;
/*la parte de adelante del nuevo nodo, apunta al primer nodo*/
nuevo->adelante=cabeza;
nuevo->atras=cabeza->atras;
cabeza->atras->adelante=nuevo;
cabeza->atras=nuevo;
}
tLista CrearNodo()
{
/*creamos un nuevo nodo, el cual sera la "cabeza" de la lista*/
tLista L;
L=(tLista)malloc(sizeof(Nodo));
if(L==NULL);
printf("Error, memoria Insuciente\n");
L->adelante=L->atras=L;
return L;
}
void ImprimeLista(Nodo *ptr)
{
Nodo *p;
int k=0;
if(ptr!=NULL)
{
printf("Lista de N£meros Guardados:\n");
p=ptr->adelante;
do{
k++;
if(k<=cont)
printf("\t\t\t* %d *\n", p->x);
p=p->adelante;
}while(p!=ptr->adelante);
}
else
{
printf("No Hay elementos en la Lista\n");
}
}
int buscar(int busca, Nodo *cabeza, Nodo *ptr)
{
int k=0;
if(ptr!=NULL)
{
cabeza=ptr->adelante;
do{
if(cabeza->x==busca)
k++;
cabeza=cabeza->adelante;
}while(cabeza!=ptr->adelante);
}
else
{
printf("No Hay elementos en la Lista\n");
}
return k;
Cuestionario
1. ¿Qué son y para que sirven las listas?
_________________________________________________________________________
_________________________________________________________________________
_____________________________________
2. ¿Cuál es la diferencia entre una lista circular y una doblemente enlazada?
_________________________________________________________________________
_________________________________________________________________________
________________________________
3. Una lista, ¿Puede comportarse como una cola?
_________________________________________________________________________
_________________________________________________________________________
_____________________________________
4. ¿Por qué se afirma que una lista circular, no tiene ni primer y último elemento?
_________________________________________________________________________
_________________________________________________________________________
________________________________
5. La función getnodo() e insertar(), se diferencian
en:______________________________________________________________________
____________________________________________________
Ejercicios
1. En una lista simple, que almacena enteros, mostrar cual es el dato mayor y cual es el dato
menor.
2. Diseñe un registro para n alumnos de una Universidad, con sus respectivas notas de
Programacion II y Estructuras de Datos, dichos datos, se deben guardar en una Lista lineal. Se
sabe que, en ésta universidad, existe la política que si, un alumno ha reprodado estas dos
materias, es dado de baja en la universidad. (Nota mínima 6.00)
3. Se desea guardar cierta cantidad de caracteres en una lista doble, y luego imprimir los
caracteres de izquierda a derecha y viceversa.
4. Diseñe un programa que, le permita al usuario, almacenar en una lista doblemete enlazada, los
registros de las personas que han adquirido un seguro de vida, además que permita eliminar
registros, y adicionar nuevos datos.
5. En una lista circular se desean guardar, cadenas de caracteres, y luego imprimir la cadena de
mayor longitud.
6. Diseñe un programa queopere con números complejos (tienen parte real e imaginaria), y
permita, sumarlos, restarlos, multiplicarlos, y determinar la magnitud de cada uno de ellos.
7. Escribir un programa en C, que apartir de una lista doble circular, ordene alfabéticamente, los
caracteres contenidos en ella y luego los imprima.
8. Diseñe un programa en C, que contenga una lista circular, cuyos elementos sean enteros
largos, luego imprimir todos los elementos y la suma de ellos.
9. El aeropuerto internacional de El Salvador, desea controlar el flujo de pasajeros, y de aerolíneas
que circulan por él. Diseñe un programa que de soporte a las salidas y entradas de los aviones,
mediante una lista doblemente enlazada cuya información sería la siguiente: Destino, compañía,
hora de salida y pasajeros. Luego, y apartir de ese último dato, es que se eliminarán los datos
de la lista de pasajeros.
10. Un punto en el espacio, está compuesto por coordenadas x, y, z. Diseñe un programa que
apartir de una lista circular doble, determine la distancia entre el primer punto y el último. (Nota:
D2=(x1-x2)2+(y1-y2)2+(z1-z2)2).
Nota Final
Del lenguaje C, hace falta por hablar mucho, con estas insignificantes páginas, no he agotado el
estudio de éste interesante y útil, lenguaje de programación. Sin embargo, yo concluyo hasta aquí, por
que algunas cosas ya no me compete, hablarlas a mí.
No me queda mas que, desearte suerte, en ésta etapa como programador.
Y ánimo!!! Sigue siempre adelante....
Recuerda que puedes hacer cualquier comentario, sugerencia, observación, etc, a mi correo electrónico:
[email protected]
Bibliografía
-"Aprenda Lenguaje ANSI C Como si estuviera en Primero". De jalón de la Fuente, Javier García.
Rodriguez Garrido, José Ignacio. Escuela Superior de Ingenieros Industriales, Universidad de Navarra.
1998
-"Curso de C". Urrutia, Gorka. http://www.elrincondelc.com
-"Introducción al Lenguaje de Programación C/C++". Pacho, Sergio.
-"Ejercicios de Practicas de C". Ledesma Muñoz, Fernando. http://ledesma.f2o.org
-"Curso de armado y reparación de PC en 10 clases. Primera Parte". Boselli, Gustavo.
[email protected]
-"Tutorial sobre apuntadores y arreglos en C". Jensen, Ted. Año 2000. http://www.
netcom.com/~tjensen/ptr/cpoint.htm
-"Algoritmos y Estructuras de Datos, una perspectiva en C". Joyanes Aguilar, Luis. Zahonero Martínez,
Ignacio. Mc Graw Hill, año 2004. Madrid, España.
-"Estructuras dinámicas de datos algoritmos, acceso, propiedades, ejemplos". Pozo, Salvador. Julio de
2001. http://www.conclase.net/c/edd/
-"Guines de Clase: Introducción a la Informática, programación I y Programación II". González, César.
Castillo, Milagro. Vázquez, Rodrigo, (Respectivamente). Universidad de El Salvador, Facultad de
Ingeniería y Arquitectura, Escuela de Sistemas Informáticos.