Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Primer Semestre
Programa de la asignatura:
Fundamentos de programación
Índice
Hasta esta etapa del curso, has aprendido a utilizar las estructuras de control
para diseñar soluciones de
problemas simples. También has
conocido diferentes formas de
representar los datos involucrados en
un problema, desde simples hasta
estructurados (como arreglos,
cadenas y estructuras). Sin embargo,
todas las soluciones propuestas
constan únicamente de un módulo
(función), llamado principal (en C,
main); lo cual no es conveniente
cuando se trata de problemas complejos que requieran de una serie de tareas
para lograr su solución, pues éste sería muy grande y, por lo tanto, difícil de
corregir o modificar.
En esta unidad se explicarán las funciones, que son el medio por el cual se
implementan los módulos en lenguaje C. Se presentará la sintaxis para crear
una función y la forma en la que se utilizan. Además, se resolverá un problema
utilizando el diseño modular para ejemplificar el tema.
Así mismo y con el fin de poder dar solución a problemas cada vez más
complejos, conoceremos las estructuras que nos permiten procesar datos
relacionados entre sí y que están compuestas de conjuntos de datos básicos.
Propósitos
En esta unidad:
Competencia específica
Dentro del diseño estructurado existen varias “escuelas”, una de las cuales
parte de los módulos de más alto nivel, delegando responsabilidades a los de
más abajo cuando así convenga, y sin tener que esperar a que éstos estén
terminados (Levine, G. 2001 p.234). A esta metodología se le conoce como
diseño descendente (top-down).
Con lo anterior podemos decir que: un módulo se caracteriza por realizar una
tarea específica, posee sus propios datos de entrada – llamados parámetros de
entrada – y su resultado – llamado salida o valor de retorno –. El diseño de cada
módulo debe ser independiente de los otros; si es necesario que exista
comunicación entre ellos, ésta únicamente podrá realizarse a través de los
parámetros de entrada y del valor de retorno. En este sentido, puede ser visto,
por otros módulos, como una caja negra que hacia el exterior sólo muestra
qué hace y qué necesita, pero no cómo lo hace.
Unidad 3. Funciones y estructuras de datos
Empecemos por hacer un bosquejo de los pasos que se tienen que realizar
para llegar al resultado deseado.
1. Leer las doce temperaturas promedio mensuales
2. Calcular el promedio anual de las temperaturas
3. Convertir las temperaturas promedio mensuales de Celsius a Fahrenheit
4. Convertir el promedio anual de temperaturas a Fahrenheit
5. Imprimir las temperaturas mensuales en grados Fahrenheit y el
promedio anual de las temperaturas, en Celsius y Fahrenheit.
Módulo leerTemps(temp[ ])
Inicio
Desde mes ← 1 mientras
mes ≤ 12, mes ← mes + 1
Imprimir
“Proporciona la
temperatura del
mes”, mes
Leer temps[mes-
1]
Fin_Desde
Fin
Observa que el ciclo del módulo leerTemps se inicia con mes igual a 1 y termina
cuando mes es 13, se propone así porque se pide la temperatura de los meses
1, 2, 3,.. 12, así que la variable mes guardará el número de mes correspondiente
a cada lectura, sin embargo, para almacenar la temperatura en la posición del
arreglo indicada se resta uno (temps[mes-1]), así se guarda desde la posición 0
y hasta la posición 11.
es 12) pues se utiliza para ir sumando cada uno de los elementos del arreglo,
así que la variable mes indicará cada una de las posiciones del arreglo.
Módulo promTemps(temp[])
Inicio
suma ← 0
Desde mes ← 0 mientras
mes ≤ 11, mes ← mes + 1
suma ← suma +
temps[mes]
Fin_Desde
Regresa (suma/12)
Fin
Módulo Principal
Inicio
/* Lectura de las temperaturas, invocando al módulo leerTemps */
Imprimir “Ingresa los promedios de temperaturas mensuales”
leerTemps(temps[])
leerTemps(temp[])
Una vez que se ha ilustrado cómo realizar una solución modular, lo siguiente
es explicar cómo se codifica cada uno de los módulos, tema que se abordará
en las siguientes secciones.
Unidad 3. Funciones y estructuras de datos
Al igual que la función principal, cada una de las funciones existe de manera
independiente, tiene sus propias variables, instrucciones y un nombre que las
distingue de las otras, además de los parámetros de entrada (también
llamados argumentos) que recibe y el tipo de dato que regresa.
cuando sean del mismo tipo. Ejemplo tipo1 param1, tipo2 param2, …, tipoN
paramN. Finalmente, las instrucciones del cuerpo de la función van entre
llaves.
La sintaxis general para invocar una función ya sea predefinida o definida por
el programador, es la siguiente:
promF = aFahrenheit(promC);
Al igual que las variables, una función debe ser declarada antes de utilizarse, es
decir, antes de invocarla. La declaración de una función se realiza escribiendo
el prototipo de la función. El prototipo de una función coincide con el
encabezado de la misma terminando con punto y coma (;) El prototipo de una
función sólo indica al compilador que existe y cómo es, más no lo que hace, por
lo tanto, la función debe definirse después. Por ejemplo, el prototipo de la
función anterior es:
float aFahrenheit(float tempC);
/* Biblioteca */
#include<stdio.h>
#include<stdlib.h>
/* Variables globales */
int meses = 12;
/* Función principal */
main()
{
/* Declaración de variables locales a main */
float temps[12], tempsF[12], promF, promC;
int mes;
system(“pause”);
} /* fin main */
/* Definición de funciones */
{
/* Definición de variables locales a leerTemps */
int mes;
return (suma/12);
} /* fin leerTemps */
Las variables locales que tienen el mismo nombre, pero fueron declaradas en
diferentes funciones, no tienen relación, son espacios de memoria totalmente
independientes uno de otro. Podemos decir que, son como dos personas
diferentes que tienen el mismo nombre. Por otro lado, las variables que se
ponen como argumentos en la declaración de una función se consideran
locales a estas. Para ejemplificar lo anterior, se muestra el siguiente programa,
en el cual se distinguen con diferentes colores el alcance de las variables.
Unidad 3. Funciones y estructuras de datos
/asterisco porReferencia.casterisco/
#include<stdio.h>
#include<stdlib.h>
Variable global
int TAM = 5;
Variable local A
inicializaA
void inicializaA(int A[])
{ Variable local i
int i;
for (i=0; i<TAM; i++)
A [i] = 0; Referencia a una
} variable global
main()
{ Declaración de
int i; variables locales a main
int A[] = {1,1,1,1,1};
printf(“Arreglo antes de la llamada a inicializaA: A = [”);
for (i=0; i<TAM; i++)
{ if(i< TAM -1)
printf(“%d ,”,A[i]);
else
printf(“%d ]\n\n\t”,A[i]);
}
inicializaA(A);
printf(“Arreglo después de la llamada a inicializaA: A = [”);
for (i=0; i<TAM; i++)
{ if(i< TAM -1)
printf(“%d ,”,A[i]);
else
printf(“%d ]\n\n\t”,A[i]);
}
system(“pause”);
}
Programa 3.3: porReferencia.c
Las variables locales por otra parte favorecen mucho la reutilización de código
y la modularidad, ya que cada función declara y manipula sus propias variables
sin depender de lo que ocurra en otras funciones, esto no significa que al
utilizar solamente variables locales no sea posible compartir datos entre las
diferentes funciones, esto se hace mediante sus datos de entrada y retorno,
una posible desventaja es que el valor de las variables locales se pierde cada
vez que la función termina.
Cuando se realiza una llamada a una función por valor y en ésta aparece una
variable como uno de los argumentos, en realidad no se está pasando la
variable sino una copia del valor que ésta contiene, lo cual implica que si dentro
de la función se modifica el argumento esto no se ve reflejado en el programa
desde el cual se hizo la llamada, pues son localidades de memoria diferentes
(recuerda que en cada llamada a una función se crean nuevas variables y se
destruyen una vez finalizada la ejecución).
/*porValor.c*/
#include<stdio.h>
#include<stdlib.h>
void inicializa(int a)
{
a = 0;
printf("\nEl valor de la variable local \"a\" es %d\n\n",a);
}
main()
{
int a=10;
system("pause");
}
Programa 3.4: porValor.c
Finalmente, cabe mencionar que para realizar la llamada por referencia de una
variable de tipo básico en lenguaje C es necesario pasar la dirección de la
variable para lo cual se utiliza el operador & seguido del nombre de la variable
(&nombre), como se hace en la función scanf, este operador regresa la dirección
Con este tipo de datos será útil poder hacer referencia a ellos bajo un mismo
identificador, y así tratarlos como una unidad. Una estructura de datos es un
mecanismo de agrupación de datos que facilitan el manejo de datos
estructurados y que se caracteriza por la forma en que se acede a sus
elementos.
En este caso, es claro que las calificaciones de cada estudiante se pueden tratar
como un dato simple e independiente de los otros, sin embargo, las
operaciones que se desean realizar serán las mismas para todo el conjunto de
calificaciones, de tal forma que habría que escribir una serie de instrucciones
secuenciales para ingresar cada dato y procesarlo.
Por ejemplo, para ingresar los datos se requiere leer una por una cada
calificación, para obtener el promedio se tendría que hacer la suma de todas
las calificaciones y después dividirlas entre 10, hasta aquí no se ha complicado
mucho, pero imagina todas las comparaciones que debes hacer para
identificar cuál es la calificación mayor.
3.5.1. Arreglos
En donde el nombre del arreglo es lista y los nombres de las variables donde
se almacenan las calificaciones son: lista[0], lista[1], lista[2], lista[3], lista[4],
…, lista[9].
En este caso el nombre en común es lista y lo único que cambia para cada
elemento es el número que le corresponde a cada variable según la posición
que ocupa en la lista.
Por otro lado, los arreglos multidimensionales son aquellos para los cuales un
solo índice no es suficiente para poder referenciar a un elemento individual, los
arreglos bidimensionales son el caso más comúnmente utilizado de arreglos
multidimensionales y por tanto los únicos que presentaremos.
“Un arreglo bidimensional es un conjunto de datos homogéneos, finito
y ordenado, donde se hace referencia a cada elemento por medio de dos
índices. El primero de los cuales generalmente se utiliza para indicar
Unidad 3. Funciones y estructuras de datos
Declaración e inicialización
En lenguaje C los índices de los arreglos siempre empiezan en cero, es decir, al
primer elemento del arreglo le corresponde la posición 0, al segundo la
posición 1, al tercero la posición 2 y así sucesivamente hasta llegar al elemento
TAM-1, donde TAM corresponde al tamaño del arreglo, en el ejemplo antes
mencionado.
Unidad 3. Funciones y estructuras de datos
El tipo de dato para los arreglos puede ser de cualquier tipo básico, es decir
entero, flotante o caracter (en C int, float, double o char ). De todos ellos, los
arreglos de tipo caracter (char) tienen un tratamiento especial, ya que un
arreglo de este tipo se considera una cadena.
int tabla[5][3]={{9,10,8},{5,9,6},{7,9,4},{8,9,6},{7,9,4}};
Unidad 3. Funciones y estructuras de datos
int tabla[5][3]={9,10,8,5,9,6,7,9,4,8,9,6,7,9,4};
Observa que en este caso no se escribe el tamaño dentro de los corchetes, pero
como hay 10 elementos en el conjunto de valores iniciales, el compilador de C
asume un tamaño 10 para el arreglo.
Reservará espacio en memoria para los 10 elementos del arreglo de los cuales
al primer elemento se le asignará un 5 y al resto se les asignará un cero.
Cada elemento del arreglo se puede tratar igual que a cualquier otra variable,
es decir, podemos asignarle un valor, incluir en una expresión algebraica o
lógica, imprimir en pantalla su valor, asignarle desde el teclado un valor, etc.
Unidad 3. Funciones y estructuras de datos
Ciclos y arreglos
Los arreglos y los ciclos están estrechamente relacionados, podríamos afirmar
que en cualquier programa que se emplee un arreglo, éste será manipulado
por medio de una estructura repetitiva. Anteriormente mencionamos que un
arreglo se utiliza cuando queremos almacenar y manipular datos relacionados
entre sí y, generalmente, cuando tenemos un arreglo debemos ejecutar el
mismo conjunto de operaciones sobre cada uno de sus elementos.
Observa como la variable contador pos del ciclo, también se utiliza como índice
para el elemento del arreglo, de tal forma que en cada iteración se haga
referencia a un elemento diferente del arreglo.
Como se puede apreciar dos ciclos están anidados cuando uno está dentro
del otro, de tal forma que, por cada iteración del externo, el interno completa
todo un ciclo.
Cada uno de los procesos que se van a realizar sobre las calificaciones, es decir
leerlas, sumarlas e imprimirlas en pantalla se pueden implementar mediante
ciclos, en vez de tener que escribir 10 veces la misma expresión para cada una
de las 10 calificaciones. La lectura y la suma de las calificaciones se pueden
implementar dentro del mismo ciclo. De esta manera podemos resumir el
análisis del problema de la siguiente forma:
Unidad 3. Funciones y estructuras de datos
∑9𝑖=0 𝑐𝑎𝑙𝑖𝑓[𝑖]
prom =
10
Inicio
suma ← 0
Desde i ← 0 mientras i<10, i ← i+1
Imprimir “Ingresa la calificación” i
Leer calif[i]
suma← suma+calif[i]
Fin Desde
prom ← prom/10
Imprimir “Las calificaciones ingresadas fueron:”
Desde i ← 0 mientras i<10, i ← i+1
Imprimir “Calificación” i “:” calif[i]
Fin Desde
Imprimir “Calificación promedio = ” prom
Fin
Algoritmo Promedio de calificaciones
{
/*Declaración del arreglo calificaciones*/
int calif[TAM];
double prom = 0;
int i;
printf("*******************************************\n”);
printf(“* El siguiente programa calcula el promedio de *\n");
printf(“* un grupo de diez estudiantes *\n”);
printf("********************************************\n”);
/*Lectura y suma de las calificaciones*/
for(i=0; i < TAM; i++)
{
printf("Proporciona la calificación %d: ",i+1);
scanf(“%d”, &calif[i]);
prom = prom + calif[i];
}
/*Cálculo e impresión del promedio*/
prom = prom/TAM;
/*Impresión de las calificaciones*/
printf("\nLas calificaciones ingresadas fueron: \n");
for(i=0; i < TAM; i++)
printf("\nCalificacion %d: %d",i+1, calif[i]);
printf("\n\n\tPromedio = %.2f\n\n", prom);
system("pause");
}
Codificación promCalificaciones.c
Observa que el tamaño del arreglo se especifica por medio de una constante
simbólica, utilizando la directiva #define, esto facilita el cambiar el tamaño del
arreglo sin tener que hacer cambios en todo el código.
A continuación, se presenta otro ejemplo para ilustrar el uso de arreglos
bidimensionales.
Método:
𝒅𝒆𝒕 = 𝑨[𝟎][𝟎] ∗ 𝑨[𝟏][𝟏] − 𝑨[𝟎][𝟏] ∗ 𝑨[𝟏][𝟎]
Puedes llevar a cabo la codificación del algoritmo como ejercicio, misma que
se muestra a continuación:
/* Directivas al preprocesador */
#include <stdlib.h>
#include <stdio.h>
/* Constantes con el tamaño de la matriz */
#define TAM 2
/* Función principal */
main()
{
int i, j;
float det;
float A[TAM][TAM]; /*declaración de la matriz*/
/* Mensaje de bienvenida */
printf("***************************************\n");
printf("* Determinante de una matriz A de 2x2 *\n");
printf("***************************************\n");
/* Lectura de la matriz A */
for(i=0; i<TAM; i++)
for(j=0; j<TAM; j++){
printf("\nProporciona el elemento A[%d][%d]: ", i,j);
scanf("%f", &A[i][j]);
}
det = A[0][0]*A[1][1] - A[0][1]*A[1][0];
printf("\nA: \n\t");
/* Impresión de la matriz A */
for(i=0; i<TAM; i++){
for(j=0; j<TAM; j++)
printf("%8.2f", A[i][j]);
printf("\n\t");
}
Programa determinantes.c
3.5.2. Cadenas
Una cadena es una serie de caracteres tratados como una sola unidad. Una
cadena puede incluir letras, dígitos y varios caracteres especiales (Deitel H. M.,
Deitel P. J., 1995).
Una cadena en lenguaje C termina siempre con el caracter nulo ‘\0’ (cuyo valor
ascii es cero) que representa el fin de cadena.
Al declarar arreglos de tipo char que sean una cadena se pueden inicializar
directamente con una constante cadena de la siguiente forma:
char cad[50]=”saludo”;
La función gets() también nos permite leer del teclado una cadena y asignarla
a un arreglo de tipo char, por ejemplo la instrucción:
gets(cad);
Una diferencia importante entre usar scanf y gets es que con la primera
función la lectura de la cadena se da por terminada cuando el usuario presiona
la tecla [Enter] o Espacio, mientras que la segunda termina la lectura de la
cadena únicamente cuando el usuario presiona la tecla [Enter], tal como se
muestra en los siguientes ejemplos:
#include <stdio.h>
#include <stdlib.h>
main(){
char mensaje[30]=”Mar
profundo ”;
printf(“%s”,mensaje);
system(“pause”);
}
char mensaje[30]=”Mar
profundo ”;
puts(mensaje);
system(“pause”);
}
/*Directivas de preprocesador*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* se incluye la biblioteca de cadenas */
main( )
{
/* Declaración de variables */
char pwscorrecto[9]=”jk278la0”; /* Clave correcta */
char pwsingresado[9]; /* para leer la clave que
ingrese el usuario */
char nombre[10]; /* Para leer el nombre del usuario */
char mensaje[50]=”Bienvenido ”;
/* Lectura de datos */
printf(“Nombre: ");
gets(nombre); /* Lectura de una cadena con espacios */
printf(“pasword: ");
scanf(“%s”,pwsingresado); /* Lectura de una cadena sin
espacios*/
if (!strcmp(pwscorrecto,pwsingresado)){ /* comparación
de claves, si la función strmp regresa 0 son iguales */
printf(“pasword correcto \n”);
strcat(mensaje,nombre); /* pega al final de
mensaje el nombre del usuario*/
puts(mensaje); /* impresión de la cadena con salto
de línea*/
Unidad 3. Funciones y estructuras de datos
}
else {
strcpy(mensaje, “Acceso denegado”); /* copia la
cadena acceso denegado al mensaje */
puts(mensaje); /* imprime la cadena*/
}
system("pause");
}
Programa password.c
3.5.3. Estructuras
Las estructuras en lenguaje C al igual que los arreglos, nos permiten tratar a
un conjunto de datos bajo un mismo identificador, pero a diferencia de los
arreglos, las estructuras son conjuntos de datos contiguos en memoria no
homogéneos de tal forma que una estructura puede estar formada por datos
de diferentes tipos.
Unidad 3. Funciones y estructuras de datos
struct<identificadorEstructura> {
<tipo1><identificadorE1>;
<tipo2><identificadorE2>;
<tipoN><identificadorEN>;
}
Observa que se utiliza la palabra reservada struct para iniciar la definición y que
las declaraciones para los elementos de la estructura se encierran entre llaves.
Una estructura puede contener a N elementos de diferentes tipos, de
cualquiera de los tipos básicos, o incluso un arreglo. Veamos un ejemplo:
struct paciente {
int nss; /* número de seguro social */
char apellido[50];
char nombre[20];
Unidad 3. Funciones y estructuras de datos
int edad;
float estatura;
char sexo;
}
struct<identificadorEstructura><identificador_var>;
Declara a las variables paciente1 y paciente2, las cuales son del tipo paciente y
por tanto para cada una de ellas se reserva espacio en memoria suficiente para
cada uno de sus seis elementos.
} paciente1, paciente2;
struct {
int nss;
char apellido[50];
char nombre[20];
int edad;
float estatura;
char sexo;
} paciente1, paciente2;
Por otro lado, al igual que los arreglos, también se pueden inicializar los
elementos de una estructura en el momento de la declaración de una variable
del tipo de la estructura en cuestión, éstos deben estar encerrados entre llaves
y separados por comas. La sintaxis general es la siguiente:
struct<identificadorEstructura><identificador_var> =
{ <valorE1>,<valor2>, ,<valorN> };
Por ejemplo:
paciente1.nss = 23442145;
paciente1.nombre = “Pablo”;
En el siguiente programa se declara una estructura de tipo perro, que tiene los
siguientes elementos:
Elemento Tipo
Raza char[]
Edad int
Peso float
#include <stdio.h>
#include <stdlib.h>
Unidad 3. Funciones y estructuras de datos
#include <conio.h>
main(){
/* Declaración de la estructura perro*/
struct perro{
char raza[20];
int edad;
float peso;
} fido, pluto = {"labrador", 7, 20} ; /* inicialización de la
variable pluto*/
printf("***********************************");
printf("\n* Comparando perros *");
printf("\n***********************************");
getch();
}
Nota: Observa que, en este caso, para poder imprimir la letra ñ se utilizó su
código Ascii (164) para lo cual en la cadena de control del printf se escribió %c
en donde se desea imprimir la ñ. Este mismo procedimiento se llevó a cabo
para acentuar la letra a.
Para concluir con esta sección veamos el siguiente ejemplo donde se utilizan
estructuras y arreglos:
Se requiere un programa que permita registrar los datos de los perros que
ingresan a refugio canino, y poder desplegar los datos de alguno de los perros,
a cada perro se le asigna una clave numérica consecutiva. Los datos que se
registran del perro son:
la fecha de ingreso (cadena)
nombre (cadena)
raza (cadena)
color (cadena)
edad (entero)
Unidad 3. Funciones y estructuras de datos
peso (flotante)
Una restricción que hay que tomar en cuenta es que se debe verificar que no
se sobrepase la capacidad de atención del albergue (100 perros), tanto al
ingresar datos como al recuperarlos, para ello se llevará un contador (c) que
actualice el número de perros ingresados.
Inicio
c ← 0
Hacer
Imprimir “Refugio para perros -Ladrido Feliz-”
Imprimir “1) Registrar un perro”
Imprimir “2) Buscar un perro”
Imprimir “3) Salir”
Imprimir “Elige una opción:”
Unidad 3. Funciones y estructuras de datos
Leer op
Casos para op
Caso 1: Si c≥100 entonces
Imprimir “El refugio está lleno”
Si no
Imprimir “Ingresa los datos del perro:”
Imprimir “Clave:” c
Imprimir “fecha de ingreso[dd/mm/aa]:”
Leer perros[c].fecha
Imprimir “color:”
Leer perros[c].color
Imprimir “nombre:”
Leer perros[c].nombre
Imprimir “raza:”
Leer perros[c].raza
Imprimir “edad:”
Leer perros[c].edad
Imprimir “peso:”
Leer perros[c].peso
c ← c+1
Fin si-si no
Caso2: Imprimir “Clave:”
Leer clave
Mientras clave≥100 v clave <0 hacer
Imprimir “La clave no es válida, ingresa
nuevamente la clave:”
Leer clave
Fin mientras
Imprimir “nombre:”, perros[clave].nombre
Imprimir “fecha de ingreso:”, perros[clave].fecha
Imprimir “color: ”, perros[clave].color
Imprimir “raza: ”, perros[clave].raza
Imprimir “edad: ”, perros[clave].edad
Imprimir “peso: ”, perros[clave].peso
Caso 3:
Otro caso: Imprimir ”Opción no válida”
Fin casos
Mientras op≠3
Fin Hacer-mientras
Fin
Algoritmo Registro perros
Unidad 3. Funciones y estructuras de datos
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main(){
/* Declaración del arreglo de tipo estructura perro */
struct perro{
char fecha[10];
char raza[30];
char color[50];
char nombre[30];
int edad;
float peso;
} perros[100];
scanf("%d",&op);
switch (op){
case 1: /*Opción Registrar perro */
printf( "\n------------------------------\n");
if(c>=100) /* Verifica si hay espacio */
printf("El refugio esta lleno\n");
else{
/*Si hay espacio pide los datos del perro y
Y los guarda en el registro c del arreglo */
printf( "Ingresa los datos del perro:");
printf( "Clave:%.3d\n", c);
printf( "fecha de ingreso[dd/mm/aa]: ");
scanf( "%s", perros[c].fecha);
Unidad 3. Funciones y estructuras de datos
Programa registroPerros.c
Cierre de la unidad
Fuentes de consulta