Sei sulla pagina 1di 72

NOTAS DE CLASE

PROGRAMACION
Y
METODOS NUMERICOS

Juan Gabriel Triana Laverde

Universidad Nacional de Colombia

1
Índice general

I. Generalidades 1
I.1. ¿Como creo un programa? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
I.2. Trabajando con DEV C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
I.3. ¿Como escribo un programa? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
I.4. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
I.5. Comandos útiles de la librerı́a stdio.h . . . . . . . . . . . . . . . . . . . . . . . . . 6
I.5.1. printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
I.5.2. scanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
I.6. Algunos comandos importantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
I.6.1. Tabla de códigos útiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
I.6.2. // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
I.6.3. /* */ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

II. ESTRUCTURAS DE CONTROL 11


II.1. CONDICIONAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
II.1.1. if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
II.1.2. Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
II.2. BUCLES O CICLOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
II.2.1. for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
II.2.2. while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

III.FUNCIONES 18
III.1. Declaración de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
III.1.1. Tipos de función . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
III.2. Construcción de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
III.3. Parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
III.3.1. Paso por valor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
III.3.2. Paso por referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
III.4. Distibución de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
III.5. Funciones Recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2
ÍNDICE GENERAL ÍNDICE GENERAL

IV.AGRUPAMIENTO DE VARIABLES 27
IV.1. Arreglos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
IV.2. Cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
IV.3. apuntadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
IV.3.1. Asignación de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
IV.3.2. Liberación de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
IV.4. Arreglos en funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
IV.5. Vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
IV.6. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
IV.6.1. Arreglos bidimensionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
IV.6.2. Matrices con apuntador . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

V. ESTRUCTURAS 51
V.0.3. Sistemas numéricos en estructuras . . . . . . . . . . . . . . . . . . . . . . 52
V.1. arreglos de estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

VI.ARCHIVOS 60
VI.1. Comandos importantes para el manejo de archivos . . . . . . . . . . . . . . . . . 61
VI.1.1. fopen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
VI.1.2. fclose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
VI.1.3. fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
VI.1.4. fprintf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
VI.2. Archivos como parámetros de funciones . . . . . . . . . . . . . . . . . . . . . . . 64
VI.2.1. Inconvenientes con los datos . . . . . . . . . . . . . . . . . . . . . . . . . . 67

3
INTRODUCCIÓN

En 1970 nace un programa conocido como B, diseñado en 1970 por Ken Thompson (AT&T),
inspirado en el programa BCP L. En 1972 es Dennis Ritchie diseña finalmente C a partir del
modelo aportado por el programa B, la novedad que ofrecı́a C era la eficacia superior gracias a
su diseño basado en tipos y estructuras de datos.
Este nuevo programa nos permite realizar con mayor eficacia una programación estructurada
(división del programa en funciones independientes que se unifican)1 , debido a la variedad de
opciones que nos ofrece, esto sin tener en cuenta la optimización de los recursos del ordenador,
lo cual nos ofrece programas mas veloces. La principal ventaja de C es su versatilidad, ya que
no es un programa diseñado con orientación hacia algún area en especifico.

En 1980 surge C++2 de la mano de Bjarne Stroustrup (AT&T), este nuevo lenguaje es una
extensión de C, ya que dota de nuevas caracterı́sticas entre las que destacan la gran cantidad
de funciones y expresiones, además de la oportunidad de declarar variables en cualquier lugar
del programa. Entre las ventajas mas destacadas por los programadores de este nuevo lenguaje
es la mejora en el concepto de herencia, lo cual permite llevar la programación al nivel de la
programación orientada a objetos (POO)3 , de esta forma se conquista el terreno de la simulación
de eventos.
El éxito de C++ fue tal que el concepto de prototipos de funciones fue incluido en los diseños
posteriores de C. No obstante, muchas compañı́as a través de los años buscaron diseñar su propia
versión de C, lo cual dificultó el intercambio de información debido a las discrepancias que
pudieron surgir entre cada versión; esto llevo a la intervención de la ANSI (American National
Standars Institute) en 1983 para crear una estandarización del lenguaje C.

En el año 2000, Microsoft presenta .NET con el lanzamiento de una nueva extensión C#4
creado por Anders Hejlsberg, que servirá de lenguaje principal de la plataforma .NET. Este
nuevo programa resulta de combinar aspectos de C++ con Java, lo cual le permite llevar la
programación orientada a objetos a un nivel muy superior, ya que facilita la manipulación de
clases permitiendo que el programa en sı́ sea parte de una clase5 .
1
Este es el tipo de programación que se maneja en el curso
2
El nombre es debido a que la linea C++ en código denota un incremento en el valor asignado a la variable C
3
Este tema no es abarcado en el curso
4
C# es una extensión de C + +, de allı́ su nombre que es una abreviación de C++++
5
Este tema será tratado en el curso, veremos que una clase es un caso particular de los objetos

I
CAPÍTULO I

Generalidades

I.1. ¿Como creo un programa?


Para poder crear un programa en lenguaje C o C++ , debemos pensar primero en donde lo
guardaremos. Debido a que el sistema operativo que estamos trabajando es Ubuntu, tendremos
algunas “molestiasrespecto a lo que acostumbramos en Windows, no obstante es solo cuestión
de costumbre, ya que son mas las ventajas que las dificultades que nos ofrece Ubuntu. Enumer-
aremos los pasos a seguir para evitar confusiones.

1. Lo primero es crear una carpeta en la cual guardar los programas, por ello crearemos
la carpeta “programas”, de este modo no tendremos problemas si no trabajamos todo el
tiempo en el mismo ordenador.
Para crear la carpeta iremos a la terminal (Aplicaciones→Accesorios→Terminal), donde
escribimos:

mkdir programas

De este modo hemos creado la carpeta programas en la carpeta personal(ubicada en la


sección Lugares).

2. Ahora nos dirigimos a la carpeta programas (lugares→carpeta personal→programas), una


vez en ella creamos un nuevo documento de texto.
Opcionalmente, desde la terminal podemos escribir gedit nombre lo cual permitira crear
un documento nuevo llamado nombre.

3. En el documento de texto, escribimos las lı́neas de código pertinentes y guardamos el


documento con el nombre deseado (sin espacios ni acentuaciones) y la extensión .c o .cpp
dependiendo del caso, por ejemplo, si nuestro programa se llama prog1 la forma correcta de
guardarlo es prog1.c o prog1.cpp. La extensión .c quiere decir que las lı́neas de texto están

1
I.1. ¿COMO CREO UN PROGRAMA? CAPÍTULO I. GENERALIDADES

escritas en el lenguaje C, mientras la extensión .cpp corresponde a un texto en lenguaje


de C++. Por ejemplo escribiremos el siguiente programa que se guardara bajo el nombre
prog1.cpp1 .

#include <stdio.h>
int main()
{
printf(" hola ");
return 0;
}

4. Una vez guardado el programa podemos compilarlo2 desde la terminal. Si en la terminal


vemos que la parte inicial del texto no muestra la carpeta “programas”, debemos ingresar
a ella, por ello introducimos

cd programas

Esto nos permite ingresar a la carpeta programas, de este modo podemos trabajar sobre los
documentos en ella guardados. Para compilar introducimos alguna de las siguiente lı́neas.

g++ prog1.cpp -o prog1

g++ -o prog1 -Wall prog1.cpp

Esto si nuestro programa se llama prog1.cpp, nótese que después de la instrucción “ -o.el
nombre del programa es colocado sin la extension (.cpp), esto es porque -o es la parte
del compilador encargada de nombrar el archivo ejecutable de nuestro programa, (por
supuesto el ejecutable puede tener cualquier nombre, por cuestiones de orden se sugiere
dejar el mismo nombre que tiene el documento).

Después de compilar correctamente prog1.cpp en nuestra carpeta de archivos es creado el


ejecutable prog1, este ejecutable realizará las operaciones indicadas en el programa.
La primer palabra de la linea del compilado es g++, indicando el compilador que estamos
usando3 ; se escogió el compilador g++ por su compatibilidad con varias funciones de las
librerı́as básicas.
1
Más adelante analizaremos concretamente cada lı́nea del programa
2
Entiéndase por compilar, el proceso que realiza el ordenador para revisar si las lı́neas de código introducidas
son correctas en el lenguaje que estamos trabajando
3
Existen diversos compiladores, por ejemplo gcc.

2
I.2. TRABAJANDO CON DEV C++ CAPÍTULO I. GENERALIDADES

5. Después de compilado, debemos ejecutar el programa, de este modo el usuario podrá ver
la tarea que realiza el programa, recordemos que en Ubuntu lo ejecutables no se ejecutan
directamente, para ello en la terminal escribimos.

./prog1

Nota Si modificamos el código del programa, debemos guardar los cambios en el docu-
mento, luego ir a la terminal para compilar el programa, de este modo el sistema operativo
modifica el archivo ejecutable, y después ejecutamos el programa.

I.2. Trabajando con DEV C++


Supongamos que nos encontramos en el sistema operativo Windows, y que estamos trabajando
con el software libre DEV C++, con el cual se escribira el mismo código estudiado anteriormente.

#include <stdio.h>
int main()
{
printf(" hola ");
return 0;
}

Una vez guardado, compilado y ejecutado, el programa parece no hacer nada, no obstante el
programa se ha ejecutado es solo que la ventana de ejecución se ha cerrado automaticamente.
Para evitar este inconveniente, debemos agregar la librerı́a stdlib.h la cual contiene el comando
system(”PAUSE”), que permite al programa hacer una pausa antes de cerrar automaticamente
el resultado del ejecutable. Luego, nuestro código se convierte en:

#include <stdio.h>
#include <stdlib.h>
int main()
{
printf(" hola ");
system("PAUSE");
return 0;
}

Cuando trabajamos en el sistema operativo UBUNTU, la orden system(”PAUSE”); no es nece-


saria ya que el compilador que utilizamos (g++) no cierra automaticamente la pantalla de
ejecución.

3
I.3. ¿COMO ESCRIBO UN PROGRAMA? CAPÍTULO I. GENERALIDADES

I.3. ¿Como escribo un programa?


Como se prometio en la sección anterior, analizaremos el programa

#include <stdio.h>

int main()
{
printf(" hola ");
return 0;
}

La primera lı́nea es quizás la mas rara de todas, pero no debemos preocuparnos es solo
la declaración de una librerı́a. Las librerı́as o bibliotecas son archivos (de extensión .h)
que contienen un conjunto de funciones que pueden ser aplicadas en la construcción de un
programa.

En este caso se utiliza la librerı́a stdio.h ya que contiene el comando printf que será utilizado
durante el programa; esta librerı́a es conocida como la librerı́a estándar y será utilizada a
lo largo del curso. Si el programa anterior no incluye la declaración de la librerı́a stdio.h,
mostrará un error de compilación ya que se intenta acceder al comando printf sin haber
enunciado la librerı́a que le contiene, en pocas palabras la librerı́a es un baúl lleno de
funciones a las cuales podemos acceder solo si el baúl se encuentra abierto.

Después vemos la aparición de int main(), formalmente es la declaración de una función


de naturaleza entero, pero eso sera revisado con mas detalle a medida que avance el curso.
Por lo pronto la función main es el cuerpo del programa, es decir, se hará todo lo que se
encuentre en su interior (desde {, hasta }). Los sı́mbolos {} muestran la apertura y cierre
de alguna estructura especial, en este caso es la apertura y cierre de la función main().

printf es un comando especial que nos permite emitir mensajes en la pantalla del programa.

Debido a la naturaleza de la función int main() debemos regresarle algún numero entero,
en particular le regresamos 0, por ello aparece la lı́nea return 0;

I.4. Variables
Los lenguajes de programación, en particular C, nos permiten utilizar parte de la memoria del
computador para almacenar datos, una variable será una porcion de memoria que se identifica
con un nombre en particular; cabe destacar que el valor de la variable (valor almacenado en la
memoria) puede modificarse a lo largo del programa.
Por supuesto, es de esperar que existan diversos tipos de variable que se pueden utilizar para cada
tarea a realizar. A continuación se presentan algunos tipos de variable y sus correspondientes
extensiones.

4
I.4. VARIABLES CAPÍTULO I. GENERALIDADES

TIPO DE DATO NOMBRE MAXIMO ASIGNACION


ENTERO PEQUEÑO short int 128 % hd
ENTERO int 32767 %d
ENTERO GRANDE long int 2.147.483.647 % ld
REAL PEQUEÑO float 2126 %f
REAL double 21023 % lf
REAL GRANDE long double 2 1023 (puede variar) % Lf
CARACTER char %c
Para declarar una variable utilizamos el nombre del tipo de dato deseado. Por ejemplo:

int a,b;
double c;
float z;

Nota El lenguaje permite declarar varias variables del mismo tipo en la misma lı́nea.
Los nombres de las variables deben ser escogidos de tal manera que permitan determinar fácil-
mente su utilidad en el programa; por ejemplo, si hablamos de un multiplicador de Lagrange
conviene más un nombre de variable lambda y no algo como a o b. Además es recomendable no
utilizar nombres reservados para una variable, por ejemplo:

int int;
double double;
float float;

En este caso el nombre de la variable es el mismo nombre del tipo de variable, lo cual puede
generar algo de confusion en el compilador. Por ejemplo, nombres como

int printf;
double scanf;

No son recomendables ya que son nombres de comandos de la librerı́a stdio.h.


Si deseamos asignar un valor a una variable, es necesario que la variable se encuentre declarada,
ası́:

int x;
x=5;

Será la forma correcta de hacer que la variable x tome el valor 5. Mientras la instrucción

x=5;

por si sola producirá un error ya que, al no haber declarado la variable x, no se ha creado el


espacio en memoria para almacenar el valor de la variable.

También podemos utilizar operaciones aritméticas para dar valores a nuevas variables a partir
de valores dados. Por ejemplo, la secuencia de instrucciones

5
I.5. COMANDOS ÚTILES DE LA LIBRERÍA STDIO.H CAPÍTULO I. GENERALIDADES

int x,y,suma;
x=5;
y=6;
suma=x+y;

Asigna espacio para 3 variables enteras, dos de las cuales (x,y) toman valores, la tercera toma
valor a partir de las demás. Ası́ la asignación de valores x = 5, y = 6 nos permite obtener una
nueva variable en la cual se almacena la adición de los valores anteriores, dicha variable se llama
suma. La siguiente secuencia de instrucciones es equivalente

int x=5,y=6,suma;
suma=x+y;

Pues nuestro lenguaje de programación nos permite asignar valores a las variables incluso cuando
se están declarando. No obstante, la siguiente secuencia es erronea:

int x=5,y,suma;
suma=x+y;

Ya que si bien hemos almacenado espacio en memoria para todas las variables a utilizar, la
asignación de valor sobre la variable suma depende de los valores almacenados en x,y, sin
embargo la variable y no posee un valor almacenado, razón por la cual nuestro programa no
producira los resultados deseados.

Hasta el momento, los valores de las variables han sido asignados dentro del programa, pero
no es la única manera de hacerlo, de hecho el objetivo es que el usuario del programa pueda
modificar los valores de las variables a utilizar, para ello hablaremos un poco de las ventajas del
uso de la librerı́a stdio.h.

I.5. Comandos útiles de la librerı́a stdio.h


I.5.1. printf
Este comando de la librerı́a stdio.h es quizás el mas usado junto a su compañero scanf. La
semántica de este comando es:

printf("LO QUE ESTA ENTRE COMILLAS ES VISTO POR EL USUARIO");

El comando printf nos permite publicar el valor asignado a alguna variable, esto se hace usando
la siguiente lı́nea de código.

Ejemplo 1 Publicando un valor al usuario

printf("El valor de la variable a es: \%d",a);

6
I.5. COMANDOS ÚTILES DE LA LIBRERÍA STDIO.H CAPÍTULO I. GENERALIDADES

En este caso la variable a es entero, si fuese double debemos cambiar la asignación por %lf.
Debemos tener muy en cuenta las asignaciones para int y double, ya que son las que mas
trabajaremos a lo largo del curso. Podemos publicar varias variables en el mismo printf.

Ejemplo 2 Publicando varios valores de distinto tipo al usuario

printf("El radio de la circunferencia es r=%d, su area es A=%lf %d",r,A);

Cabe destacar que la primer asignación de extensión de variable que aparece corresponde a la
primera variable en aparecer después de las comillas, el segundo código de extensión corresponde
a la segunda variable y ası́ sucesivamente. En este caso %d corresponde a la variable r por tanto
el radio es entero, mientras %lf corresponde al area representado por la variable A, de donde se
concluye que el area es un número real.

Comandos especiales en el printf

CODIGO FUNCION
\n cambio de interlinea
\t Tabulación horizontal
\v Tabulación vertical
\’ comilla
\a Alerta de error

I.5.2. scanf
Este comando de la librerı́a stdio.h permite reconocer variables y asignarle un valor en especifico,
por ejemplo, si le pedimos al usuario digitar un número, scanf lo identificara y asignara este
numero a una variable, para ası́ poder ser trabajado en el programa, la semántica es:

scanf(“Tipo de Variable”,&Variable);

Un ejemplo serı́a:

scanf("%d",&a);

En la primera parte, entre las comillas vemos el código de asignación %d, esto permite que
de antemano sepamos que la variable es tipo entero, sin embargo vemos que el nombre de la
variable es acompañado del signo &; esto se conoce como paso por referencia, en pocas palabras,
el compilador asigna el valor numérico a la variable, en este caso a.

Ejemplo 3 Como le digo al usuario que digite un número entero para que este sea usado en el
programa?

7
I.5. COMANDOS ÚTILES DE LA LIBRERÍA STDIO.H CAPÍTULO I. GENERALIDADES

int a;
printf("Digite un numero entero");
scanf("%d",&a);

Esta lı́nea hace que al usuario en pantalla le aparezca el letrero “ digite un numero ”, el programa
se detiene hasta que el usuario introduzca un numero y luego pulse Enter.
Si pedimos dos números al usuario, tendremos que escribir dos veces el scanf asignándolo a
variables distintas, claro esta solo por buena presentación del programa.
Nota En el ejemplo anterior se le dijo al usuario que digite un numero entero, sin embargo
nada garantiza que el nos digite un número de otra naturaleza, más adelante veremos como
evitar problemas de este tipo.

Ejemplo 4 Como le digo al usuario que digite un número entero para que este sea usado en el
programa, y además mostrarle al usuario cual fue el número que el nos dio para verificarlo?

int a;
printf("Digite un numero entero");
scanf("%d",&a);
printf("El numero digitado fue %d",a);

Nota No sobra destacar que en el printf la variable no va acompañada de &, ya que estamos
publicando su valor pero no lo estamos modificando

Ejemplo 5 Como le digo al usuario que digite dos números enteros para que estos sean usados
en el programa, y además mostrarle al usuario los números que el digito para verificarlos?

Para este fin tenemos dos opciones:

OPCION 1

int a,b;
printf("Digite dos numeros enteros");
scanf("%d%d",&a,&b);
printf("Los numeros digitados fueron. %d \t %d",a,b);

OPCION 2

int a,b;
printf("Digite el primer numero");
scanf("%d",&a);
printf("Digite el segundo numero");
scanf("%d",&a);
printf("Los numeros digitados fueron. %d \t %d",a,b);

8
I.6. ALGUNOS COMANDOS IMPORTANTES CAPÍTULO I. GENERALIDADES

Los dos programas realizan la misma labor, e incluso muestran el mismo resultado al usuario.
La diferencia radica en las necesidades del programador. Si el programador desea escribir lo
menos posible usará la opción 1, sin embargo si el programador desea trabajar de manera mas
ordenada y estructurada utilizará la opción 2.
Nota Personalmente, considero mejor la opcion 2 ya que es mucho más clara, además
permite reutilizar el código como será visto más adelante.

I.6. Algunos comandos importantes

I.6.1. Tabla de códigos útiles


COMANDO UTILIDAD
++ Incrementa el valor numérico asignado a una variable
– Disminuye el valor numérico asignado a una variable
+,-,*,/ Operaciones ariméticas usuales
= Operador de asignación igual
a% b Denota el residuo de la division a/b
&& Operador lógico Y, permite hacer conjunción
|| Operador lógico O, permite hacer disyunción
== Operador de comparación de igualdad
<=,>= Relación menor igual y mayor igual, respectivamente
!= Relación de no ser igual

Además contamos con ciertas abreviaturas:

x∗ = i equivale a x=x∗i
x+ = i equivale a x=x+i
x/ = i equivale a x = x/i
i++ equivale a i=i+1
i−− equivale a i=i−1

I.6.2. //
Este comando permite escribir un comentario de una lı́nea, los comentarios son ignorados por
el compilador, pero están a la vista en el código del programa, son útiles si queremos colocar
nuestro nombre en cada programa, o para recordar la utilidad de las variables, etc.

Ejemplo 6 Veamos el programa prog1.c

9
I.6. ALGUNOS COMANDOS IMPORTANTES CAPÍTULO I. GENERALIDADES

#include <stdio.h> // Declaración de la librerı́a


int main() // Declaración de la función main
{ // Apertura de la función main
printf(" hola ");// Letrero de saludo al usuario
return 0; // Regreso de un entero a la función main
} // Cierre de la función main

I.6.3. /* */
Los comandos /* , */ hacen que todo lo que se encuentre en su interior sea un comentario, de
este modo podemos ignorar un conjunto de lı́neas completa, su uso mas común es para que el
programador introduzca una explicación detallada acerca del funcionamiento del programa.

Ejemplo 7 Como usar comentarios largos en un segmento de programa

#include<stdio.h>
#include<math.h>
/*
Esta linea de comandos nos permite hallar el area y la longitud de una circunferencia,
el usuario debe digitar el radio para que el programa trabaje.
Cabe destacar que pi es una constante que el programa C reconoce ya que se encuentra
definida en la librerı́a math.h
*/
int main()
{
double l,a,r;
printf("Digite el valor del radio de la circunferencia ");
scanf("%lf",&r);
l=2*pi*r; // calcula la longitud de la circunferencia
a=pi*(r*r);
printf("\n La longitud de la circunferencia es: %lf \n",l);
printf(" El area de la circunferencia es: %lf \n \n",a);
return 0;
}

10
CAPÍTULO II

ESTRUCTURAS DE CONTROL

Las sentencias de un programa en C se ejecutan secuencialmente, es decir, cada una a contin-


uación de la anterior empezando por la primera en aparecer. Para poder modificar un poco el
orden para satisfacer nuestras necesidades contamos con las estructuras de control. Las primeras
son las condicionales que analizaremos son aquellas que permiten el cumplimiento de ciertas
condiciones previas, las segundas son los bucles o ciclos los cuales permiten trabajar un grupo
de instrucciones un numero dado de ocasiones mientras se tiene la posibilidad de modificar
algunos valores.

II.1. CONDICIONAL
II.1.1. if
Las sentencias del tipo condicional son las mismas que se han analizado en los cursos preliminares,
como el de fundamentos. Por ejemplo si llueve no saldré de casa; esta sentencia tiene como
condición la aparación de la lluvia, la consecuencia serı́a la no salida de casa.
Nota Cabe destacar que en la frase del ejemplo, si no salgo de la casa no implica que este
lloviendo.

Este tipo de sentencias se manejan con el operador condicional if. la semántica de este operador
es:
if (condición)
{
sentencia 1;
sentencia 2;
..
.
sentencia n;
}

11
II.1. CONDICIONAL CAPÍTULO II. ESTRUCTURAS DE CONTROL

Funciona de la siguiente manera, si la condición se satisface (es decir, es verdadera) se ingresa


en el if y se siguen las sentencias que en él se encuentran.
En caso de que la condición no se satisfaga, el compilador salta hasta la lı́nea de cierre del
operador condicional (es decir, el compilador pasa a la lı́nea siguiente al } correspondiente al if).

Ejemplo 8 Como funciona el operador if

#include <stdio.h>

int main()
{
int a;
a=0;

if(a<0)
{
printf("a es negativo");
}
if(a>0)
{
printf("a es positivo");
}
return 0;
}

Debido a que en el programa a = 0, las condiciones a < 0 y a > 0 no se satisfacen, por esta
razón el compilador pasa automáticamente a la lı́nea return 0;.

• if else

Es útil en ocasiones contar con una ejecución en caso de que la condición inicial no se satisfaga.
La semántica es la siguiente

if (condición)
{
sentencia 1;
..
.
sentencia n;
}
else
{
sentencia n + 1;
..
.

12
II.1. CONDICIONAL CAPÍTULO II. ESTRUCTURAS DE CONTROL

sentencia n + m;
}

Si la condición propuesta en el if es cierta, se ejecutan las n sentencias propuestas, y se ignora


lo relacionado al else, es decir, pasa a la lı́nea siguiente a la sentencia n + m.
Si la condición del if es falsa, se ingresa a las sentencias propuestas en el else, en este caso se
ejecutarı́an las sentencias n + 1, · · · , n + m

• if anidados
Las sentencias if pueden incluir otros if dentro de sus sentencias, por esta razón se les conoce
como anidados. Un ejemplo concreto serı́an las lı́neas

if (a >= b)
{
if (b != 0)
c = a/b;
}

Vemos que la primer condición dice a ≥ b, para poder efectuar una division de tal modo que el
resultado sea mayor que 1, sin embargo para poder dividir se requiere que el denominador sea
distinto de cero, es decir b 6= 0.
Nota Si una estructura de control cuenta con una sola sentencia en su interior no es
necesario colocar los corchetes { , } ya que el compilador ejecutara únicamente la lı́nea inmedi-
atamente siguiente a la aparición de la estructura de control.
Claramente esta linea de comandos se puede escribir mucho mas fácilmente como:

if (a >= b && b!=0)


c = a/b;

Podemos también decir que avise al usuario cuando b sea nulo, para ello colocamos

if (b==0)
printf("b es negativo, por ello no se puede efectuar la division");

Nota Nótese que en la condición del if anterior aparece b == 0, en lugar de b = 0. ¿porque?

13
II.1. CONDICIONAL CAPÍTULO II. ESTRUCTURAS DE CONTROL

II.1.2. Switch
La familia de las condicionales se extiende con la sentencia switch, ya que nos permite múltiples
ramificaciones manipuladas desde una única estructura. La semántica de la sentencia switch es
la siguiente:

switch (expresión)
{
case 1:
sentencia 1;
..
.
sentencia n;
break;
case 2:
sentencia 12 ;
..
.
sentencia p2 ;
break;
case 3:
..
.
case m:
sentencia 1m ;
..
.
sentencia rm ;
break;
}

El funcionamiento es similar a las sentencias if else, se ingresa en la expresión del switch (nor-
malmente es una variable), se mira si toma los valores descritos en cada caso, si toma el valor
asignado en algún caso entonces entra a realizar la familia de sentencias en el consignadas.
Nota El comando break hace que se salga del case, con lo cual la estructura condicional
se cumple.
Esto queda mucho mas claro en un ejemplo.

Ejemplo 9 El usuario tiene un programa que le determina el numero de dias que tiene cada
mes, para ello el programador le diseño un programa de tal modo que a cada mes le corresponde
un numero asignado por el orden de aparición en el año

switch (mes)
{
case 1: //Enero
No-dias=31;
break;

14
II.2. BUCLES O CICLOS CAPÍTULO II. ESTRUCTURAS DE CONTROL

case 2: //Febrero
No-dias=28 //año no bisiesto
break;
..
.
case 12: //Diciembre
No-dias=31;
break;
default:
printf(“El año tiene 12 meses en el calendario que manejamos”);
break;
}

Nota El comando default considera todos los valores posibles no consignados en los valores
constantes que toma cada case.
Nota En ausencia del comando default, el programa funciona similar salvo que en caso
de digitar un numero no correspondiente a algún mes, el programa continua en la lı́nea siguiente
al } que concluye el switch.

II.2. BUCLES O CICLOS


II.2.1. for
For es el “rey”de los bucles ya que es el más fácil de trabajar, convirtiéndolo en el mas utilizado
en muchos de los lenguajes de programación, en particular en C. Su semántica es:

for (inicialización; condición de control; modificación )

Esto se explica mejor con un ejemplo.

int i;
for(i=1; i<=5; i++)
{
printf("El valor de i es %d \n",i);
}

Esta lı́nea de comandos produce el resultado:

El valor de i es 1
El valor de i es 2
El valor de i es 3
El valor de i es 4
El valor de i es 5

15
II.2. BUCLES O CICLOS CAPÍTULO II. ESTRUCTURAS DE CONTROL

El bucle for funciona de la siguiente manera:


• La inicialización marca el numero que toma el contador (en este caso i) al iniciar el ciclo.
• La condición de control marca el lı́mite hasta el cual llega el contador
• La modificación i + +, hace que el valor del contador varı́e, de este modo se da mayor control
sobre la cantidad de ingresos al ciclo que se va a realizar.

Sin embargo, aun ası́ es posible tener ciclos infinitos.

Ejemplo 10 Encabezado de un ciclo infinito usando for:

for (i = 1; 1 <= n; i + +)

Es fácil darnos cuenta que el ciclo serı́a infinito ya que i toma los valores 1, 2, · · · , n, · · · pero la
condición de control 1 <= n siempre sera cierta, por tanto el ciclo no finalizara.

II.2.2. while
Esta sentencia es muy versátil ya que permite ejecutar repetidamente un bloque de sentencias,
mientras se satisfaga una condición definida en su encabezado. La semántica es la siguiente:

while(condición de control)
{
instruccion 1;
instruccion 2;
..
.
instruccion n;
}

Funciona de la siguiente manera:


Si la condición de control se satisface, entra en el ciclo y ejecuta las n sentencias, al culminar
llega al }, pero en lugar de continuar en la siguiente lı́nea, regresa a la condición de control, si
esta se satisface vuelve a ejecutar las n sentencias, y ası́ sucesivamente.
Si en algún momento del ciclo la condición de control ya no se satisface, el while termina las n
sentencias, verifica la sentencia de control, y dado que ya no se cumple, pasa a la lı́nea siguiente
al } que cierra el ciclo.
Si la condición de control no se satisface, el compilador ignora todo el ciclo, es decir, pasa a la
lı́nea siguiente al } que cierra el ciclo.

Este manejo de los ciclos es bastante útil para construir menus, ya que si el usuario digita
un numero que no corresponde al deseado, no es necesario sacarlo del programa para volverle
a mostrar las instrucciones, es cuestión de colocar en la condición los posibles numeros que
tenemos, si el numero digitado no coincide con ellos debemos mostrar el menu.
Sin embargo, se constituye en un arma de doble filo ya que si se coloca una expresión tautológica
en la condición de control, el ciclo no para de ejecutarse, por ejemplo un encabezado con una

16
II.2. BUCLES O CICLOS CAPÍTULO II. ESTRUCTURAS DE CONTROL

condición como while(1 < 2). Esto es debido a que 1 < 2 se cumple sin importar lo que
realicemos.
Otro problema es cuando dejamos la condición a cargo de una constante que no se modifica.

Ejemplo 11 Un ciclo que jamas termina

int i=1;
while(i<=3)
{
printf("El valor de i es %d \n",i);
printf("Veamos que sucede \n");
}

Es claro que i no modifica su valor, por tanto el ciclo se ejecutara siempre ya que la condición
de control será verdadera todo el tiempo. La forma correcta serı́a escribir

int i=1;
while(i<=3)
{
printf("El valor de i es %d \n",i);
printf("Veamos que sucede \n");
i++
}

De este modo al mostrar los dos letreros al usuario el valor de i se incrementa, lo cual garantiza
que en algún momento i deje de ser menor o igual a 3, por tanto la condición de control será falsa
después de dos iteraciones.

Nota Los encabezados de las estructuras de control no llevan ; ya que son funciones espe-
ciales del programa que se ejecutan, mas no son sentencias a realizar.

17
CAPÍTULO III

FUNCIONES

Una función en lenguaje C puede ser entendida como un bloque de instrucciones encargados de
realizar un proceso. El uso de funciones nos permite convertir programas de gran tamaño, en
bloques de código más ordenados y manejables.
Dividir un programa en un conjunto de funciones nos permite además reducir el tiempo de
ejecución de los programas, ya que una función (o rutina) puede ser utilizada muchas veces en
un programa sin incrementar excesivamente el número de lı́neas de código, reduciendo ası́ la
probabilidad de cometer errores en los programas.
El manejo de un programa en el lenguaje C es realizado a partir de funciones, ya que el main
es también una función, en nuestro caso de naturaleza entera ya que se enuncia int main(). A
continuación se presenta la forma general de una función.

III.1. Declaración de funciones


Las funciones se componen de:

TipoDeFuncion NombreDeFuncion (lista de parámetros)


{
cuerpo de la función
}

En pocas palabras, deben ser definidas con: un tipo que nos permite identificar si la función
regresa un valor y si es ası́ de que tipo es dicho valor; un nombre que nos permite realizar
llamados en la función main; una lista de parámetros a los cuales daremos valores para realizar
el proceso modelado en el cuerpo de la función (el cuerpo es una cadena de instrucciones).

18
III.2. CONSTRUCCIÓN DE FUNCIONES CAPÍTULO III. FUNCIONES

III.1.1. Tipos de función


Las funciones que diseñemos pueden ser:

1. Funciones que regresan un valor al main

2. Funciones que realizan un proceso que no requiere retornar valores

Entre las funciones del primer tipo (que retornan valor) tendremos un tipo de función por cada
tipo de variable existente. En particular tendremos:

int Funciones que al final retornan un valor entero

float Funciones que al final retornan un valor float

double Funciones que al final retornan un valor double

También existiran funciones long int, char, . . . . Lo importante a tener en cuenta es que el tipo
de función debe coincidir con el tipo de variable que la función retorna.

Nota int main() es una función de tipo int de nombre main y sin parámetros. Por ser de
naturaleza entera, requiere que se retorne un valor entero, de allı́ la razón por la cual se ubica la
lı́nea de código return 0 (personalmente uso 0 o 1 para retornar, pero puede ser cualquier valor
entero).

Las funciones del segundo tipo (que no retornan valor) son conocidas como funciones vacı́as,
como no tienen tipo de variable asignado, son conocidas bajo el tipo de función void.

Nota Si las funciones son del primer tipo, requieren al final la instrucción return. Si son
del segundo tipo (void) no se debe ubicar return al final de la función.

III.2. Construcción de funciones


Para construir una función debemos identificar:

1. El proceso que deseamos definir en la función

2. Un nombre sencillo que permita identificar fácilmente la labor que desempeña la función

3. Las variables que en el proceso se convertirán en parámetros

4. Las variables, que no son parámetros, que en el proceso serán utilizadas

5. La necesidad de retornar valores en la función (es decir, si los resultados se requieren en


el main).

Ejemplo 12 Construir una función que permita saludar al usuario un número de veces deter-
minado.

19
III.2. CONSTRUCCIÓN DE FUNCIONES CAPÍTULO III. FUNCIONES

Esta función solo debe enviar un número de saludos que, posiblemente, sea digitado por el
usuario, por esa razón sabemos que el número de saludos es un parámetro, ya que no tenemos
un número fijo. Como la función solo envia saludos, no es necesario regresar ningún valor a la
función principal (main), por tanto la función es de tipo void

void saludo(int n)
{ //inicio de funcion
int i;
for (i=1;i<=n;i++)
{
printf("\nHOLA");
}
printf("\n\n");
} //Fin de la funcion

Ejemplo 13 Construir una función que permita calcular n!, para cualquier valor n.

Esta función tendrá como parámetro la variable n ya que el valor no es fijo y además puede ser
dado por el usuario. Posiblemente el resultado n! se requiera para ser utilizado en el programa,
por ello es mejor retornarlo; debido a que la función factorial funciona con números enteros,
podemos concluir que el tipo de función correspondiente será int (por supuesto, también podrı́a
ser long int)

int factorial(int n)
{
// funcion que calcula el factorial
int i,prod=1;

for (i=1;i<=n;i++)
{
prod=prod*i;
}
printf("\n\n (%d)!=%d\n\n",n,prod);
return prod;
}

20
III.3. PARÁMETROS CAPÍTULO III. FUNCIONES

III.3. Parámetros
Las funciones, hasta el momento nos han sido de utilidad para distribuir de una manera eficiente
nuestros programas. Para definir una funcion, debemos tener en cuenta las variables que se
requieren para que la funcion realice el proceso para el cual fue diseñada, entre ellas distinguiamos
algunas variables que se consideraban como parámetros. Sin embargo, durante el desarrollo de
la función, es posible que los parámetros modifiquen su valor, en dicho caso es de esperar que
al trabajar la función en el main, las variables utilizadas como parámetro hallan modificado su
valor debido a que han sido utilizadas en la función, pero esto no siempre ocurre.

Para ello consideremos el siguiente programa:

#include<stdio.h>

void lee(double x);


void lee2(double &x);

int main()
{
double a,b;
lee(a);
lee2(b);
printf("a= %lf \t b=%lf \n\n",a,b);
return 0;
}

void lee(double x)
{
printf("Digite un valor\n");
scanf("%lf",&x);
}

//---------------------

void lee2(double &x)


{
printf("Digite un valor\n");
scanf("%lf",&x);
}

Aparentemente las funciones lee y lee2 son equivalentes, sin embargo solo una de ellas realiza la
lectura del valor digitado por el usuario, la razón es que mientras una de las funciones considera
la variable que le es dada como parámetro, la otra considera una copia del parámetro.

21
III.4. DISTIBUCIÓN DE UN PROGRAMA CAPÍTULO III. FUNCIONES

III.3.1. Paso por valor


La función lee:

void lee(double x)
{
printf("Digite un valor\n");
scanf("%lf",&x);
}

Tiene un parámetro que dentro de la función es identificado como x, el cual es pasado por valor,
es decir el compilador crea una copia del valor dado como parámetro y trabaja con ella. En el
caso de la función lee, que fue llamada en el main como lee(a), el valor del parámetro no se
modifica ya que se trabaja con una copia de la variable a (la original fue creada en el main()),
es decir una copia de la variable a es ingresada a la función y recibe el valor dado por el usuario,
mientras la variable a original no se usa.
Esto hace que al publicar el valor asignado a la variable a, no coincida con el valor dado por el
usuario, razón por la cual la lectura, pese a estar bien en términos semánticos, no se realiza.

III.3.2. Paso por referencia


La función lee2

void lee2(double &x)


{
printf("Digite un valor\n");
scanf("%lf",&x);
}

Tiene un parámetro que dentro de la función es identificado como x, el cual es pasado por
referencia, es decir el compilador trabaja con el parámetro original. En el caso de la función
lee2, que fue llamada en el main como lee2(b), el valor del parámetro se modifica ya que se
trabaja con la variable b (creada en el main()), es decir la función se aplica sobre la variable b
que es ingresada a la función y recibe el valor dado por el usuario.
Esto hace que al publicar el valor asignado a la variable b, coincida con el valor dado por el
usuario.

Nota En C + + el paso por referencia es aplicado utilizando el sı́mbolo & sobre el parámetro.
Si se desea modificar el valor de algún parámetro, se le aplica paso por referencia; si no deseamos
modificar el valor de los parámetros entonces hacemos paso por valor.

III.4. Distibución de un programa


Debido a la introducción de las funciones en el programa, nuestro código queda ordenado de
una manera mas eficiente. Supongamos que tenemos un programa compuesto de las funciones

22
III.4. DISTIBUCIÓN DE UN PROGRAMA CAPÍTULO III. FUNCIONES

factorial y saludo que construimos anteriormente, el cual puede ser escrito de dos maneras: El
código puede ser descrito ası́: :

MANERA I

→ Llamado de librerı́as

→ Prototipos de funciones1

→ funcion int main()

→ Declaración de las demás funciones de trabajo

#include<stdio.h>
#include<math.h>

void saludo(int n); //PROTOTIPO DE LA FUNCION


int factorial(int n); //PROTOTIPO DE LA FUNCION

//---------------------------
int main()
{
int x,y;
saludo(5); // llama a la funcion saludo con parametro 5
printf("\n\n");
x=factorial(5); // Calcula 5 factorial, y lo almacena en x.
y=factorial(6); // Calcula 6 factorial, y lo almacena en y.
return 0;
}
//----------------------------

// Funcion que calcula n!


int factorial(int n)
{
// funcion que calcula el factorial
int i,prod=1;

for (i=1;i<=n;i++)
{
prod=prod*i;
}
1
El prototipo de una función es una instrucción que pemite al compilador saber que dicha función será creada.
Para construir el prototipo, solo debemos colocar el encabezado de la funcion(tipo,nombre y parámetros ) seguidos
de ;

23
III.4. DISTIBUCIÓN DE UN PROGRAMA CAPÍTULO III. FUNCIONES

printf("\n\n (%d)!=%d\n\n",n,prod);
return prod;
}
//----------------------------
// Funcion que imprime n letreros de saludo

void saludo(int n)
{ //inicio de funcion
int i;
for (i=1;i<=n;i++)
{
printf("\nHOLA");
}
} //Fin de funcion

MANERA II

→ Llamado de librerı́as

→ Declaración de las demás funciones de trabajo

→ funcion int main()

#include<stdio.h>
#include<math.h>

// Funcion que calcula n!


int factorial(int n)
{
// funcion que calcula el factorial
int i,prod=1;

for (i=1;i<=n;i++)
{
prod=prod*i;
}
printf("\n\n (%d)!=%d\n\n",n,prod);
return prod;
}
//----------------------------
// Funcion que imprime n letreros de saludo

void saludo(int n)
{ //inicio de funcion

24
III.5. FUNCIONES RECURSIVAS CAPÍTULO III. FUNCIONES

int i;
for (i=1;i<=n;i++)
{
printf("\nHOLA");
}
} //Fin de funcion

//---------------------------
int main()
{
int x,y;
saludo(5); // llama a la funcion saludo con parametro 5
printf("\n\n");
x=factorial(5); // Calcula 5 factorial, y lo almacena en x.
y=factorial(6); // Calcula 6 factorial, y lo almacena en y.
return 0;
}
//----------------------------

Las dos formas de trabajar son equivalentes, no obstante la primera manera es más cómoda
para los programadores que vean nuestro código, ya que de antemano les permite conocer las
funciones que van a ser construidas, y ademas permiten tener a la vista la función main.

III.5. Funciones Recursivas


Una función se dice recursiva si se llama a si misma, es decir, la función se define en terminos de
si misma. Sin embargo, pese a la facilidad a la hora de generar el código, una función recursiva
puede ser peligrosa para los fines del programador, ya que es muy probable que la función no
trabaje según lo planeado.
La naturaleza de las funciones recursivas las hace una salida poco eficiente, en parte debido
a la sobrecarga ocasionada por los llamados de la función, además del proceso de renovación
constante de los parámetros de la función.

Ejemplo 14 Diseñar una función recursiva que permita obtener la sumatoria desde 1 hasta un
valor entero n dado como parámetro.

int suma(int n)
{
if(n<0)
{
printf("\n\nEl valor dado no es conveniente.\n\n");
return 0;
}
if(n==0) return n;

25
III.5. FUNCIONES RECURSIVAS CAPÍTULO III. FUNCIONES

printf("El valor es %d\n",n);


return n+suma(n-1);
}

El código anterior calcula la sumatoria desde 1 hasta n de manera recursiva. Funciona de la


siguiente manera:

Si n < 0 retorna el valor 0, ya que no es posible calcular la sumatoria

Si n vale 0 retorna el valor n = 0.

Si n es mayor que cero, la función trabaja de la siguiente manera:

suma(n) = n + suma(n − 1)
= n + [n − 1 + suma(n − 2)]
= n + (n − 1) + [n − 2 + suma(n − 3)]
= n + (n − 1) + · · · + 2 + suma(1)
= n + (n − 1) + · · · + 2 + 1 + suma(0)

El valor de suma(0) es 0, luego:

suma(n) = n + (n − 1) + · · · + 2 + 1 + 0
n
X
suma(n) = i
i=1

Ejemplo 15 Calcular n!, para un valor n dado por el usuario

int factorial(int n)
{
if(n<0)
{
printf("\n\nEl valor dado no es conveniente.\n\n");
return 0;
}
if(n==0 || n==1) return 1;
return n*factorial(n-1);
}

Funciona analogamente a la sumatoria, la diferencia radica en que la función factorial acumula


productos en lugar de sumas.

26
CAPÍTULO IV

AGRUPAMIENTO DE VARIABLES

IV.1. Arreglos
Los arreglos son un conjunto de variables, del mismo tipo, que se identifican bajo el mismo
nombre, para acceder a cada uno de los valores de las variables se utilizan ı́ndices consecutivos;
el ı́ndice más bajo corresponde al primer valor del arreglo y el ı́ndice más alto corresponde al
último ı́ndice del arreglo. Los arreglos son de gran utilidad cuando se desean agrupar variables
del mismo tipo.
Matemáticamente los arreglos pueden ser vistos como vectores, es decir, n-uplas de valores a los
cuales se accede por un indice. Por ejemplo:

X = (1, 12, 15, 6)

es un arreglo de dimensión 4, en el cual podemos acceder a cada valor de la siguiente manera:

X[0] = 1
X[1] = 12
X[2] = 15
X[3] = 6

Nota En C++ los ı́ndices inician desde 0 ya que este ı́ndice denota la posición inicial en
memoria del arreglo.

Cabe destacar que los arreglos, pese a componerse de variables del mismo tipo, deben declararse
de una manera especial ya que el compilador debe asignar el espacio correspondiente en memoria;
para realizar la declaración, debemos tener en cuenta:

El tipo de las variables del arreglo

27
IV.2. CADENAS DE CARACTERES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

El nombre que se desee para el arreglo

El tamaño del arreglo

La semántica es la siguiente

Tipo nombre[tamaño]

Ejemplo 16 Declarar en memoria espacio para 50 variables tipo double

La siguiente instrucción realiza la asignación de memoria:

double vector[50];

El arreglo anterior contiene 50 variables tipo double, y las identifica bajo el nombre vector. Para
darle valor a las componentes del arreglo, se hace accediendo a los ı́ndices de la siguiente manera:

vector[0] = 10
vector[1] = 10
vector[2] = 20
vector[3] = 30
..
.vector[49] = 319

Nota No sobra recordar que los ı́ndices del arreglo para el usuario van de 1 hasta n, mientras
para el programa los ı́ndices van de 0 hasta n − 1.

IV.2. Cadenas de caracteres


Las cadenas de caracteres son arreglos de tipo char, debemos tener en cuenta que las palabras se
forman al unir un conjunto de caracteres, de esta manera podemos leer palabras. Por ejemplo,
si tenemos un arreglo N de tamaño 5, podemos ingresar la siguiente información.

N[0]&=’M’\\
N[1]&=’a’\\
N[2]&=’r’\\
N[3]&=’i’
N[4]&=’a’

28
IV.2. CADENAS DE CARACTERES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

De donde se concluye que el arreglo N contiene el nombre Maria.

Debemos tener en cuenta que para poder utilizar de manera adecuada las cadenas de caracteres,
debemos limitar el tamaño del arreglo que la contiene ya que de no hacerlo desbordariamos la
memoria del computador. En el caso que requiramos el nombre del usuario, serı́a pésima idea
pedirle que digite el tamaño del arreglo (en este caso el número de letras que componen su
nombre), por esta razón es mejor crear un arreglo de dimensión fija, pero lo suficientemente
grande para que el usuario no utilice más caracteres de los esperados por el arreglo.

Ejemplo 17 Construir un programa que reconozca el nombre del usuario.

char c[80]; //Declaracion inicial del arreglo


printf("Digite su nombre: \n\n"); //Reconoce mayusculas,minusculas,espacios
gets(c); //reconocimiento de cadenas de caracteres
printf("\nHola sr. %s\n",c);
printf("\n Este programa es un ejemplo del uso de cadenas de caracteres \n\n");
printf("Gracias por su atencion sr %s .\n\n",c);

La función gets() se encarga de reconocer la cadena de caracteres dada por el usuario, por
supuesto puede ocurrir que el nombre dado tenga menos de 80 caracteres, no obstante el com-
pilador al guardar la cadena en el arreglo solo ocupara las casillas necesarias, las demás tendrán
un señalador que indica al compilador que estas no contienen información razón por la cual, no
se muestran al publicar la cadena.
Nota Un char tiene extensión %c, una cadena de caracteres tiene extensió %s (tipo string).

Si el usuario digita el nombre “Carlos”, resultado del programa anterior será:

c[0]=’C’ c[1]=’a’ c[2]=’r’\\


c[3]=’l’ c[4]=’o’ c[5]=’s’

Sin embargo, las demás casillas c[6], · · · ,c[79] tendrán el valor ’\0’, el cual indica el final de la
cadena. En pocas palabras, como c[6]=’\0’, tenemos que la cadena finaliza en c[5].

Nota No confundir el fin de cadena ’\0’ con un espacio en blanco ’ ’.

Ejemplo 18 Construir una funcion que permita saber si el nombre del usuario requiere mas de
n caracteres (n dado).

void nombre(char X[],int n)


{
int i;
for (i=0;i<=n;i++)
{
if(X[i]==’\0’) //Si x[i] es fin de cadena.
{

29
IV.3. APUNTADORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

printf("El nombre tiene %d caracteres",i);


break; //salida de las estructuras
}
}
if(i>n) printf("El nombre tiene mas de %d caracteres",n); //si no se ingresa al if.
}

IV.3. apuntadores
Los apuntadores son variables en las cuales se almacena la dirección en memoria que ocupa
alguna variable. Para poder visualizar con mayor facilidad la relación entre el apuntador y el
arreglo, debemos decir que el nombre del arreglo es en realidad un apuntador que apunta a la
primera componente del arreglo. Para ello tomemos la siguiente declaración:

double X[10];

Si extraigo el valor que esta contenido en la dirección en la cual se ubica el arreglo X, es decir
utilizo la instrucción

double p=*X;

Obtengo, que p tiene el valor almacenado en X[0].


Siendo más directos, si asignamos valores al vector X, podemos utilizar el comando & que nos
permite conocer la dirección en memoria, y también el operador ∗ que nos permite conocer el
valor que almacena dicha dirección.

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

int main()
{
double X[3];
X[0]=5;
X[1]=7;
X[2]=11;

printf("\nX[0] almacena el valor %lf, en la direcci\’on %p",*X,&X[0]);


printf("\nX[1] almacena el valor %lf, en la direcci\’on %p",*(X+1),&X[1]);
printf("\nX[2] almacena el valor %lf, en la direcci\’on %p",*(X+2),&X[2]);
system("PAUSE");
return 0;
}

30
IV.3. APUNTADORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

Nota Recuerde que la dirección en memoria esta en formato hexadecimal, o base 16 (la
extensión %p especial para apuntadores, permite visualizarlo)

Ahora que sabemos que el arreglo es un puntero a la primera posición, se puede decir que el
identificador (nombre del arreglo) nos permite indexar componentes en el apuntador de manera
analoga a como se hace en el arreglo. Es decir, solo debemos cambiar la declaración de arreglo,
a declaración de apuntador en nuestros programas.
En este momento, cabe una pregunta, ¿como limito el tamaño de un vector si lo declaro como
apuntador?.
Para dar respuesta a este interrogante debemos hablar de asignación de memoria.

IV.3.1. Asignación de memoria


En las declaraciones de vectores que hemos visto hasta el momento, se puede observar que:

Podemos utilizar el numero exacto de componentes del arreglo

Podemos dejar componentes del arreglo en blanco

No obstante, solo una de ellas es la forma correcta de trabajar, y es desde luego la segunda.
Cuando creamos un vector y no utilizamos toda su capacidad, estamos desperdiciando la memo-
ria del computador ya que se ocupa en algo que no será de utilidad, por supuesto, esta mala
práctica nos lleva a los errores de desbordamiento1 .

Con el fin de optimizar los recursos de memoria el lenguaje C++ nos permite asignar la memoria
estrictamente necesaria para realizar nuestras labores, para ello nos brinda el comando new con
el cual declaramos el número de variables que contendrá nuestro arreglo. La declaración de un
arreglo via apuntador se hará de la siguiente manera:

double *X;
X=new double[10];

La primera lı́nea nos permite apuntar a X, mientras la segunda lı́nea nos permite definir el
número de componentes de los cuales dispondremos en nuestro arreglo. En este caso, como el
número de componentes del arreglo es fijo, diremos que se realiza asignación estática de
memoria.

Si la declaración se realiza de la siguiente manera:

double *X;
X=new double[n];

Donde n es dado por el usuario, diremos que se realiza asignación dinámica de memoria,
ya que la asignación de memoria se realiza durante la ejecucuión del programa.
1
Como el lector habrá observado, son errores muy frecuentes al iniciarse en el mundo de los arreglos, además
que no son fáciles de localizar en el programa

31
IV.4. ARREGLOS EN FUNCIONES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

IV.3.2. Liberación de memoria


El lenguaje C++, además de permitirnos asignar memoria de manera eficiente, nos permite
liberarla una vez no necesitemos la información almacenada, para ello nos ofrece el comando
delete el cual es inverso al comando new.2 .
Ası́, si deseamos borrar el espacio asignado para el vector X de tamaño n, utilizamos la in-
strucción.

delete X;

La combinación new, delete, nos permite controlar de manera eficiente la memoria, ya que
utilizamos la cantidad estricta y la liberamos una vez no sea necesaria.

IV.4. Arreglos en funciones


Para poder utilizar un arreglo como parámetro de una función, debemos anunciarle al compilador
que el parámetro es, en efecto, un arreglo; existen diversas maneras de hacerlo, en particular
la más común es aplicando el concepto de apuntador3 , con el cual obtenemos la dirección del
arreglo en la memoria del computador.

A continuación se presenta un programa (escrito en DEV-C++) que permite al usuario crear


un arreglo de dimensión n, que contendrá n valores tipo double.

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

void lectura(double X[],int n); //PROTOTIPO


void escritura(double X[],int n); //PROTOTIPO

int main()
{
int n;
printf("Digite el tama~
no del arreglo\n\n");
scanf("%d",&n);
double X[n];
lectura(X,n);
escritura(X,n);
system("PAUSE");
return 0;
}
//----------------------------

2
En términos generales, new es un constructor de objetos, mientras delete es un destructor
3
Estos serán estudiados más adelante

32
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

void lectura(double X[],int n)


{
for(int i=1;i<=n;i++)
{
printf("\nX[%d]= ",i);
scanf("%lf",&X[i-1]);
}
}
//----------------------------

void escritura(double X[],int n)


{
for(int j=1;j<=n;j++)
{
printf("\nX[%d]= %lf ",j,X[j-1]);

}
}

Las funciones lectura y escritura tienen como parámetro un arreglo ilimitado.


Si cambiamos los encabezados de las funciones (y los prototipos) por:

void lectura(double *X,int n)


void escritura(double *X,int n)

El resultado será el mismo, ya que en esta declaración indicamos que X corresponde a un


apuntador. Por supuesto la declaración en la función main debe realizarse via apuntador.

IV.5. Vectores
Los arreglos, matemáticamente, pueden ser vistos como vectores que almacenan información,
por esta razón es natural pensar en construir un programa que permita reconocer vectores y
aplicar operaciones básicas sobre ellos.

Ejemplo 19 Diseñar un programa que permita al usuario digitar dos vectores, y le permita
aplicar sobre ellos:

Suma

Resta

Producto Punto

Producto Vectorial

33
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

El programa siguiente fue escrito en DEV-C++4 , la razón es la inclusión de las pausas utilizando
el comando system. Este programa funciona también en Ubuntu.
El programa funciona con arreglos

//---------------DECLARACION DE LAS LIBRERIAS---------------------------------


#include<stdio.h>
#include<stdlib.h>
//-------------------PROTOTIPOS DE FUNCIONES-----------------------------------

void presentacion();
int menu();
void lectura(int n, double X[]);
void escritura(int n, double X[]);
void suma(int n, double X[],double Y[], double suma[]);
void resta(int n, double X[],double Y[], double resta[]);
double productopunto(int n, double X[],double Y[]);
void productovectorial(int n, double X[],double Y[],double cruz[]);
//-----------------------------------------------------------------------------

int main()
{
presentacion();
//---inicio del programa
int m,n,k;
printf("\nDigite la longitud del primer vector: ");
scanf("%d",&n);
printf("\nDigite la longitud del segundo vector: ");
scanf("%d",&m);
if(n!=m)
{
printf("\nLOS VECTORES DEBEN TENER LA MISMA LONGITUD\n");
system("PAUSE");
return 0;
}

double A[n],B[m];
printf("\nDigite los datos del primer vector: \n");
lectura(n,A); //Lectura de los datos del vector A
printf("\n\n El vector es:\n\n");
escritura(n,A);
4
Se programo en DEV-C++ para que el lector que trabaje bajo el sistema operativo Windows no tenga
problemas de cierre automático de pantalla

34
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

printf("\n\n");
printf("\nDigite los datos del segundo vector: \n");
lectura(m,B); //Lectura de los datos del vector B
printf("\n\n El vector es:\n\n");
escritura(m,B);
printf("\n\n");

k=menu();

if(k==1)
{
double producto;
producto=productopunto(n,A,B);
printf("< ");
escritura(n,A);
printf(" , ");
escritura(m,B);
printf(" >");
printf("=%lf",producto);
printf("\n\n");
system("PAUSE");
return 0;
}
if(k==2)
{
double sum[n];
suma(n,A,B,sum);
escritura(n,A);
printf(" + ");
escritura(n,B);
printf(" = ");
escritura(n,sum);
printf("\n\n");
system("PAUSE");
return 0;
}
if(k==3)
{
double res[n];
resta(n,A,B,res);
escritura(n,A);
printf(" - ");
escritura(n,B);
printf(" = ");

35
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

escritura(n,res);
printf("\n\n");
system("PAUSE");
return 0;
}

if(k==4)
{
if(m!=3 || n!=3)
{
printf("\nLos vectores deben tener dimension 3\n\n");
system("PAUSE");
return 0;
}
double Z[3];
productovectorial(n,A,B,Z);
escritura(n,A);
printf(" X ");
escritura(n,B);
printf(" = ");
escritura(n,Z);
}

printf("\n\n");
system("PAUSE");
return 1;
}

//============================================================
void presentacion()
{
printf("\n *****************************************************************\n");
printf("Este programa requiere dos vectores de la misma dimension.\n");
printf("Como resultado podemos obtener las operaciones basicas de vectores, \n");
printf("aplicadas sobre dichos vectores. Las posibles operaciones son:\n");
printf(" * Suma\n * Resta \n * Producto Punto\n * Producto Vectorial");
printf("\n *****************************************************************\n");
}
//============================================================
int menu()
{
int k=5;
double k2;
while(k!=1 && k!=2 && k!=3 && k!=4)

36
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

{
printf("\n------------------MENU------------------\n");
printf("\n -> Digite 1 si quiere calcular <A,B>");
printf("\n -> Digite 2 si quiere calcular A+B");
printf("\n -> Digite 3 si quiere calcular A-B");
printf("\n -> Digite 4 si quiere calcular A X B");
printf("\n----------------------------------------\n");
scanf("%lf",&k2);
k=(int)k2;
if(k!=k2) k=5; //Si el usuario no digita un numero entero, continua en el menu
}
return k;
}
//============================================================
void lectura(int n, double X[])
{
//Funcion que lee los datos dados por el usuario en la terminal
int i;
for (i=1;i<=n;i++)
{
printf("digite la componente [%d]",i);
scanf("%lf",&X[i-1]);
}
}
//============================================================
void escritura(int n, double X[])
{
// Funcion que escribe los datos dados por el usuario como vector.
int i;
printf("(");
for (i=1;i<n;i++)
{
printf("%lf,",X[i-1]);
}
printf("%lf)",X[n-1]);
}
//============================================================
void suma(int n, double X[],double Y[],double suma[])
{
//Suma de dos vectores X,Y. El resultado se almacena en un vector llamado suma
int i;
for (i=1;i<=n;i++)
{
suma[i-1]=X[i-1]+Y[i-1];

37
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

}
}
//============================================================
void resta(int n, double X[],double Y[],double diferencia[])
{
//Resta de dos vectores X,Y. El resultado se almacena en un vector llamado diferencia
int i;
for (i=1;i<=n;i++)
{
diferencia[i-1]=X[i-1]-Y[i-1];
}
}
//============================================================
double productopunto(int n, double X[],double Y[])
{
//Producto punto de X,Y (<X,Y>). El resultado es un escalar llamado Prod
int i;
double Prod=0;
for (i=1;i<=n;i++)
{
Prod+=X[i-1]*Y[i-1];
}
return Prod;
}
//============================================================
void productovectorial(int n, double X[],double Y[],double cruz[])
{
//Producto vectorial, permite construir un vector que es ortogonal a X, Y a la vez
//la construccion no es la mejor, pero de momento funciona.
//Esta definicion solo funciona para vectores de tama~
no 3.
cruz[0]= X[1]*Y[2]-X[2]*Y[1];
cruz[1]=-(X[0]*Y[2]-X[2]*Y[0]);
cruz[2]= X[0]*Y[1]-X[1]*Y[0];
}

// FIN DEL PROGRAMA

El programa anterior incluye algunos controles sobre el menu. No obstante, el lector como
ejercicio puede modificar el programa de tal manera que permita al usuario calcular nuevamente
operaciones sin tener que salir de la ventana de ejecución, claro esta permitiendo al usuario salir
de la ventana de ejecución, además de controlar que el usuario no digite caracteres cuando debe
digitar numeros.
La declaración de los vectores se hizo vı́a arreglos, el lector puede modificar el programa de tal
manera que la declaración se haga con apuntadores.

38
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

Se recomienda al usuario prestar atención a la manera en que se escribe el vector en la función


de escritura.

Ejemplo 20 Diseñar un programa que permita al usuario digitar un conjunto de datos, y que
le de la opcion de calcular:

Media

Varianza

Desviación estandar

El siguiente programa se escribe utilizando apuntadores.

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

void lectura(double *X,int m);


void escritura(double *X,int m);
double media(double *X,int n);
double varianza(double *X,int n);

int main()
{
int n;
printf("Digite el numero de datos");
scanf("%d",&n);
double a,desv,var;
//-----
double *X; //declaracion
X=new double[n]; //asignacion de memoria
if(X==NULL)
{
printf("\nLa memoria es insuficiente\n");
system("PAUSE");
return 0;
}
//-----
lectura(X,n);
escritura(X,n);
a=media(X,n);
printf("\n\n LA MEDIA ES: %lf\n\n",a);
var=varianza(X,n);

39
IV.5. VECTORES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

printf("\n LA VARIANZA ES: %lf\n\n",var);


printf("\n LA DESVIACION ES: %lf\n\n",sqrt(var));
system("PAUSE");
return 0;
}

//============================================================
double varianza(double *X,int n)
{
double suma=0.0;
double mid,d;
mid=media(X,n);
for(int i=1;i<=n;i++) suma+=(mid -X[i-1])*(mid -X[i-1]);
d=suma/n;

return d;
}
//============================================================
double media(double X[],int n)
{
double suma,mid;
suma=0.0;
for(int i=1;i<=n;i++)
{
suma+=X[i-1];
}
mid=suma/n;
return mid;
}
//============================================================
void lectura(double *X,int m)
{
for (int i=1;i<=m;i++)
{
printf("\nX[%d]= ",i);
scanf("%lf",&X[i-1]); //Almacenamiento en cada posicion.
}
}
//============================================================
void escritura(double *X,int m)
{
for (int i=1;i<=m;i++)
{
printf("\nX[%d]= %lf ",i,X[i-1]);

40
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

}
}

El programa anterior funciona, pero esta escrito de una manera poco agradable. Queda como
ejercicio al lector modificar el programa de tal manera que de todas las comodidades al usuario,
además de controlar que el usuario digite datos que no se puedan reconocer.
Nota En la función main se observa la comparación X==NULL, esto indica si el apuntador
es nulo, en cuyo caso no habrá espacio disponible en memoria para almacenar el arreglo

IV.6. Matrices
IV.6.1. Arreglos bidimensionales
En términos de programación, las matrices pueden ser vistas como arreglos bidimensionales, es
decir arreglos cuyas componentes a su vez son arreglos.

El concepto de arreglo bidimensional nos permite poder movernos en dos direcciones indepen-
dientemente (filas y columnas) lo cual resulta ventajoso a la hora de escribir los programas, ya
que se pueden analizar con mayor facilidad. En el caso de declaración via arreglos el manejo es
similar, solo se debe tener en cuenta el número de componentes del arreglo de arreglos, es decir,
el número de filas y columnas que debe tener nuestra matriz; veamos la siguiente declaración:

double X[5][5];

Esta instrucción reserva espacio en memoria para recibir 5 × 5 elementos tipo double, de este
modo hemos podemos construir la siguiente matriz:

X[0][0] X[0][1] X[0][2] X[0][3] X[0][4]


X[1][0] X[1][1] X[1][2] X[1][3] X[1][4]
X[2][0] X[2][1] X[2][2] X[2][3] X[2][4]
X[3][0] X[3][1] X[3][2] X[3][3] X[3][4]
X[4][0] X[4][1] X[4][2] X[4][3] X[4][4]
La instrucción

X[2][4]=5;

asigna el valor 5 a la componente (3,5) de la matriz que es para el compilador la componente


(2,4) (recordemos que para el compilador los indices inician desde 0).

De este modo podemos dar valores a cada una de las componentes de la matriz, no obstante, lo
ideal serı́a darle al usuario la opcion de digitar los valores de cada término de la matriz, para
ello se procede como lo hemos hecho antes, solo que esta vez se trabaja de esta forma

scanf("%lf",&X[i][j]);

41
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

Puesto que X[i][j] es una variable tipo double, claro esta, este proceso se debe hacer para 0 ≤
i, j ≤ 4.

Sin embargo, para el trabajo desde funciones se recomienda el uso de los apuntadores ya que
la declaración ilimitada double X[][] no puede ser utilizada como parámetro en una función, el
paso como parámetro requiere que sea de una manera restringida, es decir con un número de
columnas fijo.

Ejemplo 21 Construir una función de lectura y una de escritura para matrices con 10 colum-
nas.

void lecturaMatriz(double X[][10], int m, int n)


{
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
{
printf("X[%d][%d] = ",i+1,j+1);
scanf("%lf", &X[i][j]);
}
}
}

//----------------------------------------------------------
void escrituraMatriz(double X[][10], int m, int n)
{
for(int i =0;i<m; i++)
{
for(int j=0; j<n; j++) printf("%lf\t",X[i][j]);
printf("\n");
}
printf("\n\n");
}

Claro esta, solo podemos trabajar matrices que tengan 10 columnas, si quisieramos trabajar
matrices con 20 columnas tendrı́amos que diseñar una funciones especı́ficas para matrices de 20
columnas; este inconveniente hace que los arreglos bidimensionales sean la peor opcion a la hora
de trabajar con matrices, ya que no ofrecen al usuario la oportunidad de decidir el tamaño que
tendrán las matrices que desea construir. La mejor opcion para dicha tarea son los apuntadores.

IV.6.2. Matrices con apuntador


Como vimos anteriormente, un apuntador me es útil para definir arreglos, por ello si queremos
un arreglo cuyas componentes son arreglos (una matriz), lo más natural serı́a pensar en un
apuntador sobre un apuntador.

42
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

Esta idea, desde el punto de vista matemático es muy razonable, no obstante desde el punto
de vista de ingenierı́a no es tan simple. Recordemos que un apuntador contiene la dirección de
la variable a la que apunta, mientras que en un apuntador a apuntador, el primer apuntador
contiene la dirección del segundo apuntador el cual apunta a la variable que tiene el valor que
buscamos.

El concepto de apuntador, es una de las herramientas más poderosas a la hora de diseñar un


programa, sin embargo su alto nivel conceptual lo convierten en un arma de doble filo, por
ello se recomienda visualizar el apuntador momentaneamente como una manera equivalente de
construir arreglos.

Debemos recordar que la construcción de arreglos via apuntador no nos permite inicializar
mientras declaramos, es decir, no podemos definir el tamaño del arreglo en el momento de
declarar el apuntador, requerimos del comando new para poder realizar dicha labor ya que la
asignación de memoria se debe realizar de manera dinámica, de otro modo el usuario no podrı́a
decidir el tamaño de la matriz que desea trabajar

Ejemplo 22 Construir una función que permita declarar una matriz de tamaño m × n

double **creaMatriz(int m,int n)


{
// ASIGNACION DINAMICA DE MEMORIA PARA UNA MATRIZ
// DEVUELVE LA MATRIZ CON SUS VALORES ALMACENADOS
double **p;
int i;
p = new double *[m] ;
if( p == NULL ) return( NULL );
for( i=0; i<m; i++) {
p[i] = new double[n];
if( p[i] == NULL ) return( NULL );
}
return p;
}

Analicemos la función descrita:

La condición p==NULL, indica que la dirección de la variable no puede ser nula.

El nombre de la función tipo double tiene antepuestos dos asteriscos.

I Sin asteriscos indica que retorna una variable double


I Un asterisco indica que retorna un apuntador (vector)
I Dos asteriscos indica que retorna un apuntador a apuntador (matriz)

Por supuesto, la función se puede construir tipo void, sin embargo se opta por trabajar con
retorno para ası́ mostrar un ejemplo de la manera en que se retornan variables puntero.

43
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

Ejemplo 23 Construir una función que permita construir la matriz identidad de cualquier
tamaño (la matriz identidad es una matriz de ceros con unos en la diagonal)

void identidad(double **X, int m, int n)


{
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==j) X[i][j]=1; // 1 en la diagonal
if(i!=j) X[i][j]=0; // 0 fuera de la diagonal
}
}
}

La matriz identidad normalmente se define como matriz cuadrada.

Ejemplo 24 Diseñar una función que retorne 0 si dos matrices dadas son distintas, 1 si las
dos matrices dadas son iguales

int igualdad(double **X,double **Y,int m, int n)


{
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(X[i][j]!=Y[i][j]) return 0;
}
}
return 1;

cabe destacar que dos matrices son iguales si y solo si todas sus componentes son iguales. Por
esta razón basta con que difieran en solo un término para que concluyamos que las dos matrices
son diferentes.

Ejemplo 25 Construir un programa que permita al usuario:

Sumar matrices

Restar matrices

Multiplicar matrices

Hallar la traspuesta de matrices

44
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

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

void presentacion(int &opcion);


void leetamano(int &m,int &n);
double **creaMatriz( int m, int n);
void liberaMemoria( double **X, int m);
double **construyeMatriz(int &m,int &n);
void lecturaMatriz(double **X, int m, int n);
void escrituraMatriz(double **X, int m, int n);
void sumaMatriz(double **X,double **Y,double **S,int m, int n);
void restaMatriz(double **X,double **Y,double **R,int m, int n);
void productoMatriz(double **X,double **Y,double **P,int m, int n,int p);
void traspuesta(double **X,double **Xt,int m,int n);

int main()
{
int opcion=0;
while(opcion<1 || opcion>4)
{
presentacion(opcion); //menu que funciona solo con opciones entre 1 y 4
}
if(opcion==1)
{
double **A,**B,**SUM;
int m,n,p,q;
A=construyeMatriz(m,n);
B=construyeMatriz(p,q);
if(m!=p || n!=q)
{
printf("\n ERROR:La dimension de las matrices impide la operacion\n\n");
system("PAUSE");
return 0;
}
SUM=creaMatriz(m,n);
sumaMatriz(A,B,SUM,m,n);
}
if(opcion==2)
{
double **A,**B,**RES;
int m,n,p,q;
A=construyeMatriz(m,n);
B=construyeMatriz(p,q);
if(m!=p || n!=q)

45
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

{
printf("\n ERROR:La dimension de las matrices impide la operacion\n\n");
system("PAUSE");
return 0;
}
RES=creaMatriz(m,n);
restaMatriz(A,B,RES,m,n);
}
if(opcion==3)
{
double **A,**B,**PROD;
int m,n,p,q;
A=construyeMatriz(m,n);
B=construyeMatriz(p,q);
if(n!=p)
{
printf("\n ERROR:La dimension de las matrices impide la operacion\n\n");
system("PAUSE");
return 0;
}
PROD=creaMatriz(m,q);
productoMatriz(A,B,PROD,m,n,q);
}
if(opcion==4)
{
double **A,**At;
int m,n;
A=construyeMatriz(m,n);
At=creaMatriz(n,m);
traspuesta(A,At,m,n);
}

system("PAUSE");
return 0;
}

//====================================================
void presentacion(int &opcion)
{
printf("\n**************************************************\n");
printf(" PROGRAMA PARA TRABAJAR MATRICES\n");
printf(" \nEl programa permite al usuario trabajar las operaciones \n");
printf("Suma, Resta, Producto, Traspuesta\n");
printf("\n->Digite 1 para sumar dos matrices");

46
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

printf("\n->Digite 2 para restar dos matrices");


printf("\n->Digite 3 para multiplicar dos matrices");
printf("\n->Digite 4 para determinar la traspuesta de una matriz");
printf("\n************************************************\n");
printf("\nDigite la opcion que desea ");
scanf("%d",&opcion);
}
//===========================================================
void leetamano(int &m,int &n)
{
//Funcion para leer el numero de filas y columnas de una matriz
printf("Digite el numero de filas ");
scanf("%d",&m);
printf("Digite el numero de columnas ");
scanf("%d",&n);
}
//===========================================================
double **creaMatriz( int m, int n)
{
// ASIGNACION DINAMICA DE MEMORIA PARA UNA MATRIZ
// DEVUELVE LA MATRIZ CON SUS VALORES ALMACENADOS
double **p;
int i;
p = new double *[m] ;
if( p == NULL ) return( NULL );

for( i=0; i<m; i++) {


p[i] = new double[n];
if( p[i] == NULL ) return( NULL );
}
return p;
}
//===========================================================
void liberaMemoria( double **X, int m)
{
/* LIBERA LA MEMORIA ASIGNADA A LA MATRIZ
Notese que toma el numero de filas (recuerden como
se iba creando la matriz cuando usabamos el comando new)
*/

int i;
for( i = 0; i < m; i++) delete X[i];
delete X; //Delete libera la memoria, piensese como el puesto de new
}

47
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

//===========================================================
void lecturaMatriz(double **X, int m, int n)
{
// lectura de un matriz de tama~no mXn
int i, j;
for( i=0; i<m; i++)
{
for( j=0; j<n; j++)
{
printf("X[%d][%d] = ",i+1,j+1);
scanf("%lf", &X[i][j]);
}
}
}
//===========================================================
void escrituraMatriz(double **X, int m, int n)
{
// escritura de un matriz de tama~no mXn
printf("\n\n");
int i, j;
for( i =0;i<m; i++)
{
for( j=0; j<n; j++) printf("%lf\t",X[i][j]);
printf("\n");
}
}
//===========================================================
double **construyeMatriz(int &m,int &n)
{
//Funcion para construir una matriz, utiliza las funciones creadas anteriormente
//Se usa para reducir el numero de instrucciones en la funcion main
printf("\nA continuacion se solicitara informacion de una matriz\n");
leetamano(m,n);
double **X;
X=creaMatriz(m,n);
lecturaMatriz(X,m,n);
escrituraMatriz(X,m,n);
return X;
}
//===========================================================
void sumaMatriz(double **X,double **Y,double **S,int m, int n)
{
int i,j;
for(i=0;i<m;i++)

48
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

{
for(j=0;j<n;j++)
{
S[i][j]=X[i][j]+Y[i][j];
}
}
printf("\n\nEl resultado de la suma es: ");
escrituraMatriz(S,m,n);
}
//===========================================================
void restaMatriz(double **X,double **Y,double **R,int m, int n)
{
int i,j;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
R[i][j]=X[i][j]-Y[i][j];
}
}
printf("\n\nEl resultado de la resta es: ");
escrituraMatriz(R,m,n);
}
//===========================================================
void productoMatriz(double **X,double **Y,double **P,int m, int n,int q)
{
//Recordemos que el producto de matrices se define como:
//P[i][j]=<A_i,B^j> donde A_i es fila i-esima de A, B^j es columna j-esima de B
// <,> indica el producto punto de dos vectores
for(int i=0;i<m;i++)
{
for(int j=0;j<q;j++)
{
P[i][j]=0;
for(int k=0;k<n;k++)
{
P[i][j]+=X[i][k]*Y[k][j];
}
}
}
printf("\n\nEl resultado del producto es: ");
escrituraMatriz(P,m,q);
}
//===========================================================

49
IV.6. MATRICES CAPÍTULO IV. AGRUPAMIENTO DE VARIABLES

void traspuesta(double **X,double **Xt,int m,int n)


{
//Recordemos que si X es detama~
no mXn, su traspuesta es de tama~
no nXm
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
Xt[j][i]=X[i][j]; //X_{m*n} Xt_{n*m}
}
}
printf("\n\nLa matriz traspuesta es: ");
escrituraMatriz(Xt,n,m);
}

50
CAPÍTULO V

ESTRUCTURAS

Las estructuras son entes que permiten agrupar variables bajo un mismo identificador, a difer-
encia de los arreglos, se permite el agrupamiento de variables de distinto tipo. El agrupamiento
bajo una estructura permite que las variables que lo componen sean tratadas juntas.

La semántica para estructuras es la siguiente:

struct NOMBRE{
tipo1 variable1 ;
..
.
tipon variablen ;
};

Cabe destacar que las variables de las cuales se compone la estructura no necesariamente son del
mismo tipo. El llamado o referenciado de una estructura se realiza por su nombre, pero además
de eso, podemos acceder a cada uno de los elementos que lo componen, para ello basta colocar
el nombre de la estructura seguido de un punto el cual se sigue del nombre del elemento que
deseamos extraer (este . es un operador de selección).

Ejemplo 26 Definir una estructura que permita almacenar la siguiente información de un


alumno del curso de programación

Nombre completo

Número de documente

Código

Edad

51
CAPÍTULO V. ESTRUCTURAS

struct Alumno{
char Nombre[100];
long int documento;
int codigo;
int edad;
};

Nota La estructura puede ser visualizada como la construccion de un nuevo tipo de variable,
por esta razón debe ir un “;.al final de la declaración.

Esta estructura permite agrupar bajo un tipo de variable “Alumno”toda la información que el
usuario necesita (nombre,documento de identidad, código, edad).

Ejemplo 27 Construir una estructura que permita trabajar una pareja ordenada bajo un nombre
dado por un caracter.

struct Coordenada{
char nombre;
double coord1;
double coord2
};

En la funcion main(), si queremos darle valor a una pareja ordenada, procedemos de la siguiente
manera:

Coordenada X; //X es una pareja ordenada con un nombre.


X.nombre=’A’;
X.coord1=3;
X.coord2=4.5;

Las instrucciones anteriores nos permiten construir la pareja ordenada A=(3,4.5), la cual es
identificada en el programa bajo el nombre X. Este ejemplo nos permite observar el manejo que
se da al operador de selección (.), el cual nos permite dar a valor a cada uno de los elementos
que integra nuestra estructura.

V.0.3. Sistemas numéricos en estructuras


Las estructuras pueden ser utilizadas en matemáticas para definir conjuntos de números que se
definen a partir de conjuntos conocidos. Este es el caso de los conjuntos de números racionales
y complejos, definidos respectivamente como:
na o
Q= | a, b ∈ Z, b 6= 0
b
C = {a + bi | a, b ∈ R}

52
CAPÍTULO V. ESTRUCTURAS

Recordemos que en los complejos i es el número talque i2 = −1.

El uso de estructuras nos permite definir un tipo de variable asociado a complejos o fraccionarios,
con lo cual podremos agrupar datos de una manera mas sencilla y eficiente. Debemos tener en
cuenta que las estructuras se construyen a partir de tipos conocidos, en el caso de los fraccionarios
requerimos dos variables tipo int, mientras en el caso de los complejos serán dos variables tipo
double.

Por supuesto, al crear una estructura (por lo pronto pensemos en crear un nuevo tipo de variable)
podemos también definir operaciones entre elementos de la estructura, por ejemplo en el caso de
los números fraccionarios podemos definir operaciones tales como: suma, resta, multiplicacion,
división. Para ello debemos recordar como se definen estas matemáticamente, por ejemplo la
suma se define como:

a c ad + bc
+ =
b d bd
si consideramos dos variables x, y del tipo definido en la estructura de números fraccionarios
tales que x = ab , y = dc , tendremos lo siguiente en términos de las variables del tipo definido en
la estructura.

x.numerador y.numerador (x.numerador)(y.denominador) + (x.denominador)(y.numerador)


+ =
x.denominador y.denominador (x.denominador)(y.denominador)
De este modo, resulta claro el modo en el que se procede para deducir la forma en la que se
debe expresar el resultado de cada operación en términos de los elementos de la estructura;
repitiendo este proceso para las demás operaciones podemos crear un programa que permita
realizar operaciones con números fraccionarios.

Ejemplo 28 Construir una estructura que permita identificar numeros fraccionarios, ademas
de permitir realizar las operaciones:
Suma de fraccionarios

Resta de fraccionarios

Producto de fraccionarios

#include<stdio.h>

struct fraccionario{
int num;
int den;
};

void lectura(fraccionario &W);


void sumafrac(fraccionario X,fraccionario Y,fraccionario &SUMA);

53
CAPÍTULO V. ESTRUCTURAS

void restafrac(fraccionario X,fraccionario Y,fraccionario &SUMA);


void prodfrac(fraccionario X,fraccionario Y,fraccionario &PROD);

int main()
{

fraccionario Z,Y,S,R,P;
lectura(Z);
lectura(Y);
sumafrac(Z,Y,S);
restafrac(Z,Y,R);
prodfrac(Z,Y,P);
printf("\n (%d/%d) + (%d/%d) =(%d/%d)\n",Z.num,Z.den,Y.num,Y.den,S.num,S.den);
printf("\n (%d/%d) - (%d/%d) =(%d/%d)\n",Z.num,Z.den,Y.num,Y.den,R.num,R.den);
printf("\n (%d/%d) * (%d/%d) =(%d/%d)\n",Z.num,Z.den,Y.num,Y.den,P.num,P.den);

return 0;
}

//================================================================================
void lectura(fraccionario &W)
{
printf("\nDigite el fraccionario, primero el numerador, luego el denominador");
scanf("%d%d",&W.num,&W.den);
}
//================================================================================
void sumafrac(fraccionario X,fraccionario Y,fraccionario &SUMA)
{
SUMA.num=(X.num*Y.den+X.den*Y.num);
SUMA.den=X.den*Y.den;
}
//================================================================================
void restafrac(fraccionario X,fraccionario Y,fraccionario &RESTA)
{
RESTA.num=(X.num*Y.den-X.den*Y.num);
RESTA.den=X.den*Y.den;
}
//================================================================================
void prodfrac(fraccionario X,fraccionario Y,fraccionario &PROD)
{
PROD.num=(X.num*Y.num);
PROD.den=(X.den*Y.den);
}

54
CAPÍTULO V. ESTRUCTURAS

Se debe tener en cuenta:

I La estructura se define después de las librerias, y antes de las funciones

I La estructura se trata como un nuevo tipo de variable

I La estructura, al ser vista como variable, admite paso por valor y por referencia

I Para acceder a cada elemento de la estructura se usa el operador “.”

Las funciones que operan fraccionarios se construyen aplicando paso por referencia, sin embargo
podemos modificarlas levemente para trabajarlas como funciones con retorno, ası́ obtenemos:

//================================================================================
fraccionario sumafrac(fraccionario X,fraccionario Y)
{
fraccionario SUMA;
SUMA.num=(X.num*Y.den+X.den*Y.num);
SUMA.den=X.den*Y.den;
return SUMA;
}
//================================================================================
fraccionario restafrac(fraccionario X,fraccionario Y)
{
fraccionario RESTA;
RESTA.num=(X.num*Y.den-X.den*Y.num);
RESTA.den=X.den*Y.den;
return RESTA;
}
//================================================================================
fraccionario prodfrac(fraccionario X,fraccionario Y)
{
fraccionario PROD;
PROD.num=(X.num*Y.num);
PROD.den=(X.den*Y.den);
return PROD;
}

Un error frecuente a la hora de diseñar programas con estructuras es colocar las funciones
antes de la definición de la estructura. Por ejemplo, usando el ejemplo anterior, serı́a tener un
encabezado

#include<stdio.h>

void lectura(fraccionario &W);


void sumafrac(fraccionario X,fraccionario Y,fraccionario &SUMA);

55
CAPÍTULO V. ESTRUCTURAS

void restafrac(fraccionario X,fraccionario Y,fraccionario &SUMA);


void prodfrac(fraccionario X,fraccionario Y,fraccionario &PROD);

struct fraccionario{
int num;
int den;
};

Como el compilador lee de izquierda a derecha y de arriba hacia abajo, tendremos un inconve-
niente respecto al tipo de variable fraccionario, ya que cuando es utilizado en las funciones, el
compilador no le reconoce como un tipo. La declaración de la estructura se suele hacer antes
de las funciones para que el programador pueda disponer de la estructura como parámetro de
función.

Queda al usuario como ejercicio definir la función cociente de fraccionarios, además de expo-
nenciación de fraccionarios, radicación de fraccionarios, entre otras operaciones posibles. Por
supuesto, mejorar la escritura de los resultados en el programa ya que la presentación no es
agradable cuando se digitan fraccionarios negativos.

Ejemplo 29 Construir una estructura que permita manipular numeros complejos, y además
diseñar funciones que permitan realizar las siguientes operaciones:

Lectura de números complejos

Escritura de números complejos

Suma de números complejos

Resta de números complejos

Producto de números complejos

Conjugación de números complejos

void lectura(complejo &W)


{
printf("\nDigite la parte real del numero complejo ");
scanf("%lf",&W.preal);
printf("\nDigite la parte imaginaria del numero complejo ");
scanf("%lf",&W.pimag);
}
//=====================================================================
int escritura(complejo W)
{
//Esta funcion permite escribir el numero complejo de una manera presentable
if(W.preal!=0 && W.pimag<0)
{

56
CAPÍTULO V. ESTRUCTURAS

printf("%lf - %lf i\n\n",W.preal,-W.pimag);


return 0;
}
if(W.pimag==0 )
{
printf("%lf \n\n",W.preal);
return 0;
}
if(W.preal==0 && W.pimag!=0)
{
printf("%lf i\n\n",W.pimag);
return 0;
}
if(W.pimag>0)
{
printf("%lf + %lf i\n\n",W.preal,W.pimag);
return 0;
}
}
//=====================================================================
void sumacomplejo(complejo Z,complejo W,complejo &SUMA)
{
//El parametro SUMA almacena el resultado de la operacion, por ello
//se trabaja con paso por referencia
SUMA.preal=Z.preal+W.preal;
SUMA.pimag=Z.pimag+W.pimag;
}
//=====================================================================
void restacomplejo(complejo Z,complejo W,complejo &RESTA)
{
//El parametro RESTA almacena el resultado de la operacion, por ello
//se trabaja con paso por referencia
RESTA.preal=Z.preal-W.preal;
RESTA.pimag=Z.pimag-W.pimag;
}
//=====================================================================
void prodcomplejo(complejo Z,complejo W,complejo &PROD)
{
//El parametro PROD almacena el resultado de la operacion, por ello
//se trabaja con paso por referencia
PROD.preal=(Z.preal)*(W.preal)-(Z.pimag)*(W.pimag);
PROD.pimag=(Z.preal)*(W.pimag)-(Z.pimag)*(W.preal);
}
//=====================================================================

57
V.1. ARREGLOS DE ESTRUCTURAS CAPÍTULO V. ESTRUCTURAS

void conjugado(complejo Z,complejo &Zconj)


{
//El parametro Zconj almacena el resultado de la operacion, por ello
//se trabaja con paso por referencia
Zconj.preal=(Z.preal);
Zconj.pimag=-(Z.pimag);
}

Se deja como ejercicio al usuario construir un programa en el cual se utilicen estas funciones,
además de construir otras funciones que permitan la utilizacion de complejos, como por ejemplo
el cociente de dos complejos, la norma de un complejo, entre otras.

V.1. arreglos de estructuras


Las estructuras son vistas como un nuevo tipo de variable, por ello es razonable pensar en
agrupar variables del tipo diseñado en la estructura, en pocas palabras, podemos pensar en
arreglos de estructuras. Para poder visualizar este concepto, revisemos el siguiente ejemplo:

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

struct complejo{ //objeto tipo struct llamado complejo


double preal; // parte real
double pimag; // parte imaginaria
}; // El fin de estructura va con ;

void lectura(complejo &W);


int escritura(complejo W);

int main()
{
complejo *Z;
int n,p;
printf("\nDigite el tama~
no del arreglo\n");
scanf("%d",&n);
Z=new complejo[n];

for(int i=0;i<n;i++)
{
lectura(Z[i]);
p=escritura(Z[i]);
}
system("PAUSE");
return 0;

58
V.1. ARREGLOS DE ESTRUCTURAS CAPÍTULO V. ESTRUCTURAS

}
}
//==============================================================
void lectura(complejo &W)
{
printf("\nDigite la parte real del numero complejo");
scanf("%lf",&W.preal);
printf("\nDigite la parte real del numero complejo");
scanf("%lf",&W.pimag);
}
//==============================================================
int escritura(complejo W)
{

if(W.preal!=0 && W.pimag<0)


{
printf("%lf - %lf i\n\n",W.preal,-W.pimag);
return 0;
}
if(W.pimag==0 )
{
printf("%lf \n\n",W.preal);
return 0;
}
if(W.preal==0 && W.pimag!=0)
{
printf("%lf i\n\n",W.pimag);
return 0;
}
if(W.pimag>0)
{
printf("%lf + %lf i\n\n",W.preal,W.pimag);
return 0;
}
}

Ası́ como podemos tomar arreglos compuestos de variables de tipo complejo, también podemos
crear estructuras que se compongan de elementos tipo complejo, no obstante anidar estructuras
no es una manera eficiente de trabajar.

59
CAPÍTULO VI

ARCHIVOS

La inclusión de datos en nuestros procesos, hasta el momento, se ha realizado via teclado por el
usuario durante la ejecución del programa, este modo de trabajo puede ser un inconveniente ya
que, si el usuario desea trabajar una matriz de tamaño 10 × 10 forzosamente debera digitarla
cada vez que ejecute el programa.
El manejo de información desde archivos nos permite almacenar los datos en un archivo, el cual
reconoceremos en el programa para poder leer los datos de trabajo, incluso el resultado del
programa puede ser entregado al usuario en otro archivo, lo cual mejora la presentacion de los
resultados.

Al igual que el procesamiento del programa por parte del compilador, la lectura de datos desde
archivos se realiza iniciando de izquierda a derecha y de arriba hacia abajo en el documento. Es
decir, para el compilador las siguientes presentaciones de datos son iguales:

--------------

2 3

1 2 3
4 5 6
--------------

--------------
2

3 1 2 3
4
5 6
--------------

60
VI.1. COMANDOS IMPORTANTES PARA EL MANEJO DE ARCHIVOS
CAPÍTULO VI. ARCHIVOS

--------------
2
3
1
2
3
4
5
6
--------------

Nota los ——- no se escriben en el programa, son usados para indicar el inicio y fin del
archivo
La lectura de datos se realiza de manera secuencial, es decir, no se omiten carácteres.

Para poder procesar un archivo dentro de un programa, debemos establecer el protocolo a seguir
para que nuestro procesamiento sea exitoso. Para ello debemos cuestionarnos:

¿Como podemos abrir un archivo en un programa?

¿Como podemos cargar un archivo en un programa?

¿Como podemos manipular un archivo en un programa?

¿Como podemos extraer informacion de un archivo en un programa?

Cada una de las preguntas anteriores tiene su respuesta en un comando de la libreria stdio.h1 ,
además de contar con el tipo de variable FILE.

VI.1. Comandos importantes para el manejo de archivos


VI.1.1. fopen
El comando fopen nos permite abrir el archivo, es decir, podemos accesar a él, no obstante aún
no lo tendrı́amos cargado en el programa. Para ello debemos tener en cuenta que tipo de archivo
deseamos cargar, ya que los archivos pueden ser de diversos tipos.

r: Modo lectura: El archivo solo permite leer la información, pero no modificarla

w: Modo escritura: El archivo solo permite modificar la información, pero no leerla

a: Modo escritura-al final: El archivo permite escribir resultados sin perder la información an-
terior
1
Estos comandos de la librerı́a son, en su mayorı́a, muy parecidos a los comandos usados hasta el momento
salvo que se ante pone la letra f, con lo cual se indica que el trabajo es desde archivos.

61
VI.1. COMANDOS IMPORTANTES PARA EL MANEJO DE ARCHIVOS
CAPÍTULO VI. ARCHIVOS

r+: Modo lecto-escritura: El archivo permite leer la información, y modificarla

w+: Modo lecto-escritura: El archivo permite leer la información, y modificarla. Predominan


los atributos del modo w

a+: Modo lecto-escritura al final: El archivo permite leer y escribir resultados sin perder la
información anterior

Dependiendo del tipo de tarea que deseemos realizar, escogemos el modo en el cual abrimos
nuestro archivo. Claro esta, debemos tener en cuenta las propiedades de cada modo, por ejemplo
un modo r nos permitirá utilizar los datos, pero no modificar el archivo, el modo w nos permitira
modificarlo pero no leer información.
Se sugiere no trabajar los tipos que combinan el modo de los archivos (los modos que incluyen
+ en su declaración) ya que al admitir lectura y escritura, es muy probable que el usuario al
trabajar genere pérdida de información.

La semántica del comando fopen es la siguiente:

FILE *A;
A=fopen("datos.txt","r");

La instrucción FILE *A, es en realidad un apuntador a la variable A, la cual es un variable tipo


archivo, en pocas palabras apuntamos a la información del archivo. Los parámetros de fopen
son “datos.txt”que es el archivo en el cual se encuentran los datos, y el modo de trabajo del
archivo que en este caso es lectura “r”. Al hacer A=fopen(“datos.txt”,“r”); estamos diciendo
que la información del archivo de lectura datos.txt, en el programa se realizará cada vez que
trabajemos con la variable A.

VI.1.2. fclose
El comando fclose es el antipodal de fopen, ya que nos permite cerrar los archivos que han sido
abiertos. Normalmente el no cerrar un archivo no genera mayor problema para el compilador,
no obstante se recomienda hacerlo ya que no es recomendable no cerrar lo que hemos abierto,
prueba de ello es que al salir de nuestros hogares no dejamos la puerta abierta. La semántica es
la siguiente

FILE *A;
A=fopen("datos.txt","r");
..
.

fclose(A);

62
VI.1. COMANDOS IMPORTANTES PARA EL MANEJO DE ARCHIVOS
CAPÍTULO VI. ARCHIVOS

VI.1.3. fscanf
Este comando de lectura desde archivos, como es de esperarse, funciona solo con archivos de
lectura, ya que si se intenta utilizar sobre archivos de escritura nos presenta un error, puesto
que no contamos con los permisos para extraer información del archivo.

La manera de trabajo es muy similar a lo que se ha trabajado hasta el momento. La semántica


es la siguiente:

double x;
FILE *A;
A=fopen("datos.txt","r");
fscanf(A,"%lf",&x);

Como el archivo A que hemos abierto es de lectura, podemos realizar la lectura de un valor,
ası́ el comando

fscanf(A,"%lf",&x);

tiene sentido. Los parámetros son los mismos que cuando se trabajo con lectura desde teclado, no
obstante como la lectura se realiza desde archivo, debemos indicarle desde que archivo se realiza
la lectura(pues podemos cargar más de un archivo en un programa), de alli la aparición de la
dirección de la variable tipo file A. EL compilador busca el número que sigue en el indicador de
posición, una vez lo localiza lo lee y lo descarga en la variable indicada, luego el indicador de
posición del archivo pasa al lugar justo después del valor leı́do.

VI.1.4. fprintf
Este comando de escritura desde archivos, como es de esperarse, funciona solo con archivos de
escritura, ya que si se intenta utilizar sobre archivos de lectura nos presenta un error, puesto
que no contamos con los permisos para modificar la información del archivo.

La manera de trabajo es muy similar a lo que se ha trabajado hasta el momento. La semántica


es la siguiente:

double x;
FILE *A,*B;
A=fopen("datos.txt","r");
B=fopen("salida.txt","w");

if(A==NULL)
{
printf("\nERROR: El archivo de datos no existe\n\n");
system("PAUSE");
return 0;
}

63
VI.2. ARCHIVOS COMO PARÁMETROS DE FUNCIONES CAPÍTULO VI. ARCHIVOS

fscanf(A,"%lf",&x);
fprintf(B,"\nEl valor leido es: %lf \n",x);
Como el archivo A que hemos abierto es de lectura, podemos realizar la lectura de un valor.
Además el archivo B que hemos abierto es de escritura, por tanto podemos escribir en el archivo
los resultados, ası́ las instrucciones anteriores nos permiten leer un valor que se encuentra en el
archivo A y escribirlo en el archivo B.

No sobra destacar el gran parecido entre las funciones scanf, printf respecto a fscanf,fprintf, ya
que es una de las ventajas de trabajar la libreria estandar, pues la semántica en los comandos
de manejo desde teclado y archivos se mantiene.
Nota El control if(A==NULL) será de gran importancia, ya que permite salir del programa
en caso de que el archivo de datos no se localice.
¿Porque no se controla el archivo B?

VI.2. Archivos como parámetros de funciones


A continuación veremos como se utiliza el concepto de archivo en un programa.
Ejemplo 30 Construir un programa que permita calcular la media de un conjunto de datos
dados en un archivo. (El primer dato del archivo es el tamaño del vector).

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

void instrucciones();
void lectura(FILE *A,double *X,int n);
void escritura(FILE *B,double *X,int n);
void media(FILE *B,double *X,int n);

int main()
{
instrucciones();
FILE *A,*B;
double *X;
int n;

A=fopen("dat.txt","r"); //Apertura del archivo de lectura

if(A==NULL)
{
printf("\nERROR: El archivo de datos no existe\n\n");
system("PAUSE");
return 0;
}

64
VI.2. ARCHIVOS COMO PARÁMETROS DE FUNCIONES CAPÍTULO VI. ARCHIVOS

B=fopen("result.txt","w"); //Apertura de archivo de escritura


fscanf(A,"%d",&n); //se lee el primer valor del archivo
X=new double[n];
lectura(A,X,n);
escritura(B,X,n);
media(B,X,n);
system("PAUSE");
return 0;
}

//============================================================
void instrucciones()
{
printf("\n***********************************************\n");
printf("PROGRAMA QUE CALCULA LA MEDIA DE UN CONJUNTO DE DATOS\n\n");
printf("En la carpeta de compilacion podra encontrar el documento dat.txt");
printf("\neste documento debe contener los siguientes datos.\n\n");
printf("-> Tama~
no del arreglo\n");
printf("-> Valor de cada componente del arreglo\n");
printf("***********************************************\n\n");
}
//============================================================
void lectura(FILE *A,double *X,int n)
{
//Funcion que lee los datos dados por el usuario en el arcihvo dat.txt
int i;
for (i=1;i<=n;i++)
{
fscanf(A,"%lf",&X[i-1]);
}
}
//============================================================
void escritura(FILE *B,double *X,int n)
{
// Funcion que escribe los datos dados por el usuario como vector.
int i;
fprintf(B,"(");
for (i=1;i<n;i++)
{
fprintf(B,"%lf,",X[i-1]);
}
fprintf(B,"%lf)",X[n-1]);
}

65
VI.2. ARCHIVOS COMO PARÁMETROS DE FUNCIONES CAPÍTULO VI. ARCHIVOS

//============================================================
void media(FILE *B,double *X,int n)
{
double s=0.0,mid;
for(int i=1;i<=n;i++) s+=X[i-1];

mid=s/n;
fprintf(B,"\n\nLa media es %lf",mid);
}

Debemos tener en cuenta:

El archivo puede ser utilizarse como parámetro de función, ya que es tratado como apun-
tador a variable

El archivo de lectura tiene asociados los fscanf del programa

El archivo de escritura tiene asociados los fprintf del programa

El archivo de lectura OBLIGATORIAMENTE debe existir sino, el compilador tomara


datos basura

El archivo de escritura puede no existir, en cuyo caso al ejecutar el programa se crea


automaticamente

Si el archivo de escritura existe, al ejecutar se borra la información anterior y se almacena


la nueva.

Supongamos que corremos el programa con la siguiente información en el archivo dat.txt

---------------
5

1 2 3 4 5
---------------

En cuyo caso el archivo de salida result.txt

---------------
(1.000000,2.000000,3.000000,4.000000,5.000000)

La media es 3.000000
---------------

Supongamos que volvemos a ejecutar el programa con los siguientes datos

66
VI.2. ARCHIVOS COMO PARÁMETROS DE FUNCIONES CAPÍTULO VI. ARCHIVOS

---------------
6

1 2 3 4 5 9
---------------

El archivo result.txt tiene almacenada la información de la ejecución con el primer conjunto de


datos. Vamos a considerar dos casos.

Trabajando con modo w


Si consideramos la apertura del archivo result.txt en modo w, es decir

B=fopen("result.txt","w"); //Apertura de archivo de escritura

El resultado será el siguiente

---------------
(1.000000,2.000000,3.000000,4.000000,5.000000,9.000000)

La media es 4.000000
---------------

Trabajando con modo a


Si consideramos la apertura del archivo result.txt en modo a, es decir

B=fopen("result.txt","a"); //Apertura de archivo de escritura

El resultado será el siguiente

---------------
(1.000000,2.000000,3.000000,4.000000,5.000000)

La media es 3.000000

(1.000000,2.000000,3.000000,4.000000,5.000000,9.000000)

La media es 4.000000
---------------

VI.2.1. Inconvenientes con los datos


Continuando con el ejemplo anterior, supongamos que el archivo dat.txt tiene la siguiente infor-
mación:

67
VI.2. ARCHIVOS COMO PARÁMETROS DE FUNCIONES CAPÍTULO VI. ARCHIVOS

---------------
5

1 2 3 4 5 9
---------------

Pese a que hay 6 datos, el primer dato indica la existencia de solo 5, por ello el compilador solo
leera los 5 primeros datos. En este caso el resultado será el siguiente:

---------------
(1.000000,2.000000,3.000000,4.000000,5.000000)

La media es 3.000000
---------------

Ahora supongamos que el número de datos es mayor a los que hemos consignado, por ejemplo,
supongamos que el archivo de datos tiene la siguiente información

---------------
10

1 2 3 4 5 9
---------------

En este caso, la información de los últimos 4 datos será inexistente, por esta razón el compilador
se verá forzado a rellenarlo con información basura, la cual producirá resultados de poca utilidad.

Por esta razón se sugiere dar información detallada al usuario de la manera en la cual deben ser
ubicados los datos en el archivo de datos.

68

Potrebbero piacerti anche