You are on page 1of 107

Programación en C Tutorial Dr.

Eric Jeltsch
F.

PROGRAMACIÓN EN C
(Tutorial para la asignatura Programación
Estructurada de la carrera Ingeniería en
computación)

Dr. Eric Jeltsch F.


Académico
Departamento de Matemáticas
Universidad de La Serena

Prologo:

______________________________________________________________________________________ 1
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
El lenguaje de programación C es básico para entender el Sistema Operativo (SO) UNIX
y sus aplicaciones. Por otra parte C es la fuente del lenguaje de programación C++, el cual es
utilizado en el nuevo paradigma de la Programación Orientada a Objetos, y de Visual C++,
lenguaje utilizado para el desarrollo de aplicaciones Cliente/Servidor en Windows. De manera que
el aprendizaje de C tiene variadas y justificadas razones para ser uno de los lenguajes que un
Ingeniero en Computación debiera conocer y dominar al final de su carrera.

C es un lenguaje fácil de aprender, especialmente si se conoce un lenguaje procedural,


como por ejemplo, Pascal o Fortran. Para Uds. que conocen Pascal, podemos afirmar que todo
concepto de Pascal puede ser representado directamente en C. La “dificultad” de C radica en la
libertad que este lenguaje ofrece, el cual promueve la creación de nuevos conceptos a partir de los
ya conocidos. Partiremos de la base que Ud. posee como herramienta o entorno de desarrollo
(IDE) Dev C++, Borland C++ ó Turbo C/C++ en su computador, en caso contrario vaya a la
dirección Web, en donde podrá bajarlo en forma gratuita. http://www.bloodshed.net/devcpp.html

Al mismo tiempo, con este material se persigue apoyar los contenidos del curso de
Programación Estructurada en un lenguaje dinámico y versátil. Por diferentes razones se ha
escogido el lenguaje C. La forma en que se abordan los contenidos de este material es porque
nuestros alumnos ya tienen un curso de programación básica, en donde usan el lenguaje Pascal
como referencia. De manera que muchos conceptos propios de los fundamentos de la
programación se han dejado de lado, como por ejemplo los diagramas de flujos o algunas otras
herramientas didácticas. Lo que se pretende es que rapidamente el alumno pueda familiarizarse con
la sintaxis del lenguaje C y así crear su propio estilo. Para mayor información en este sentido, vea el
documento DevDotStar_Read_Principios.pfd.

Se pretende que este tutorial a través de ejemplos escogidos brinde una visión general de
las distintas propiedades y funciones que el lenguaje C posee para la construcción de programas
eficientes, robustos, según algunos criterios básicos de análisis de algoritmos.

Finalmente, la proyección inmediata de éste tutorial es preparar al lector para las


asignaturas de Estructuras de Datos, Diseño y Análisis de Algoritmos, como cursos
fundamentales para entender la Ingeniería de Software, área en donde los alumnos de nuestra
carrera deben demostrar sus competencias y habilidades, al término de su carrera.

Dr. Eric Jeltsch F.


Académico
Departamento de Matemática
Universidad de La Serena

Contenidos:

1 INTRODUCCION
2 ESTRUCTURAS DE CONTROL ( Entrada y Salida de Datos )
2 FUNCIONES ( Diseño Top-Down ó Modular )
3 RECURSIVIDAD
______________________________________________________________________________________ 2
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
4 ITERACION
5 COMPLEJIDAD DE ALGORITMOS
6 PREPROCESADORES en C
7 PROCESANDO TIPOS DE DATOS CHAR
8 PUNTEROS
9 ARRAY

Lectura y Escritura en Archivos Secuenciales

10 ESTRUCTURAS
APLICACIONES
11 ANEXOS

Literatura:
(1) Lenguaje C, Introducción a la Programación , Kelley-Pohl, Addison-Wesley Iberoamericana,
1987.
(2) El Lenguaje de Programación C, Brian W. Kernighan-Dennis Ritchie, Prentice-Hall
Hispanoamericana, 1991.
(3) Lenguaje C y Estructura de Datos, Juan García de Sola-Vicente Garcerán, McGraw-Hill
Interamericana, 1992.
(4) Turbo C/C++ 3.1, Manual de Referencia, Herbert Schild, McGraw Hill de Informática, 1994.
(5) Programación Estructurada en C, James L. Antonakos, Kenneth C. Mansfield Jr. Prentice-Hall,
1997.(Libro Guia, es decir nos orienta respecto de los conceptos que al final del curso deberíamos
dominar.)
(6) Toda la red Internet a disposición. Si Ud.le da a Google la palabra “programación en C” le
saldrán al menos 13.400.000 enlaces. Ahora si le da “Programming with C”, le saldrán al menos
19.400.000 enlaces. Sin embargo, es suficiente conocer y manejar los conceptos que se le brindan
en sesiones de clases y laboratorios. En resumen, no necesita una conexión a Internet para aprobar
la asignatura de “Programación Estructurada”, sólo basta que Ud. se comprometa a realizar todas y
cada una de las tareas que se le encomienden.

1. INTRODUCCION
La Ingeniería de Software (IS) afecta a la economía y a las sociedades de muchas
maneras.En los EEUU, el software contribuyó a 1/4 de todo el incremento del PIB durante los 90's
(alrededor de 90,000 millones de dólares por año), y 1/6 de todo el crecimiento de productividad
durante los últimos años de la década (alrededor de 33,000 millones de dólares por año). La
ingeniería de software contribuyó a $1 billón de crecimiento económico y productividad en esa
década. Alrededor del globo, el software contribuye al crecimiento económico en formas similares,
¿Cual es la realidad chilena?.
______________________________________________________________________________________ 3
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
La ingeniería de software cambia la cultura del mundo debido al extendido uso de la
computadora. El correo electrónico (E-mail), la WWW y la mensajería instantánea permiten a la
gente interactuar en nuevas formas. El software baja el costo y mejora la calidad de los servicios de
salud, las dependencias gubernamentales y otros servicios sociales. Los proyectos exitosos donde
se han usado métodos de ingeniería de software incluyen a Linux, el software del transbordador
espacial, los cajeros automáticos y muchos otros.
La ingeniería de software se puede considerar como la ingeniería aplicada al software,
esto es en base a herramientas preestablecidas, la aplicación de las mismas de la forma más eficiente
y óptima; objetivos que siempre busca la ingeniería. No es sólo de la resolución de problemas, sino
más bien teniendo en cuenta las diferentes soluciones, elegir la más apropiada.
Un objetivo de décadas ha sido el encontrar procesos o metodologías predecibles y
repetibles que mejoren la productividad y la calidad del software. La ingeniería de software
requiere llevar a cabo numerosas tareas, dentro de ellas están las siguientes:

- Análisis de requisitos
Es la tarea de extraer los requisitos de un producto de software, siendo ésta la
primera etapa para crearlo. Mientras que los clientes piensan que ellos saben lo que el software
tiene que hacer, se requiere de habilidad y experiencia en la ingeniería de software para reconocer
requisitos incompletos, ambiguos o contradictorios. El resultado del análisis de requisitos con el
cliente se plasma en el documento ERS, Especificación de Requerimientos del Sistema, cuya
estructura puede venir definida por varios estándares, tales como CMM-I u otros. Asimismo, se
define un diagrama de Entidad/Relación, en el que se plasman las principales entidades que
participarán en el desarrollo del software. La captura, análisis y especificación de requisitos (incluso
pruebas de ellos), es una parte crucial; de esta etapa depende en gran medida el logro de los
objetivos finales. La IEEE Std. 830-1998 normaliza la creación de las Especificaciones de
Requisitos Software (Software Requirements Specification).

- Especificación
Es la tarea de describir detalladamente el software a ser escrito, en una forma
matemáticamente rigurosa.

- Diseño y arquitectura
Se refiere a determinar como funcionará de forma general la propuesta, sin entrar en
detalles. Consiste en incorporar consideraciones de la implementación tecnológica, como el
hardware, la red, etc. Se definen los Casos de Uso para cubrir las funciones que realizará el
sistema, y se transforman las entidades definidas en el análisis de requisitos en clases de diseño,
obteniendo un modelo cercano a la programación orientada a objetos.

- Programación
Reducir un diseño a código puede ser la parte más obvia del trabajo de ingeniería de
software, pero no es necesariamente la porción más larga. La complejidad y la duración de esta
etapa está intimamente ligada al o a los lenguajes de programación utilizados.

______________________________________________________________________________________ 4
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
- Prueba
Consiste en comprobar que el software realice correctamente las tareas indicadas en la
especificación. Una técnica de prueba es probar por separado cada módulo del software, y luego
probarlo de forma integral, para asi llegar al objetivo. Se considera una buena practica el que las
pruebas sean efectuadas por alguien distinto al desarrollador que la programó, idealmente un area
de pruebas; sin perjuicio de lo anterior el programador debe hacer sus propias pruebas. En general
hay dos grandes formas de organizar un area de pruebas, la primera es que esté compuesta por
personal inexperto y que desconozca el tema de pruebas, de esta forma se evalúa que la
documentación entregada sea de calidad, que los procesos descritos son tan claros que cualquiera
puede entenderlos y el software hace las cosas tal y como estan descritas. El segundo enfoque es
tener un area de pruebas conformada por programadores con experiencia, personas que saben sin
mayores indicaciones en que condiciones puede fallar una aplicación y que pueden poner atención
en detalles que personal inexperto no consideraría.

- Documentación
Todo lo concerniente a la documentación del propio desarrollo del software y de la
gestión del proyecto, pasando por modelaciones (UML), diagramas, pruebas, manuales de usuario,
manuales técnicos, etc; todo con el propósito de eventuales correcciones, usabilidad,
mantenimiento futuro y ampliaciones al sistema.

- Mantenimiento
Mantener y mejorar el software para enfrentar errores descubiertos y nuevos
requisitos. Esto puede llevar más tiempo incluso que el desarrollo inicial del software. Alrededor de
2/3 de toda la ingeniería de software tiene que ver con dar mantenimiento. Una pequeña parte de
este trabajo consiste en arreglar errores, o bugs. La mayor parte consiste en extender el sistema
para hacer nuevas cosas.

Ahora, en el contexto de la asignatura de Programación Estructurada se supone que


Ud. se maneja en el traspaso de seudo códigos o diagrama de flujos,- que representan un esbozo
de la solución a un problema -, a un lenguaje de programación, por ejemplo, Pascal. Lo novedoso
de ahora es conocer la sintaxis y el lenguaje de programación C para poder realizar lo mismo de
antes pero usando el lenguaje de programación C. De manera que, a pesar de toda la rigurosidad
existente, en esta asignatura nos preocupamos solamente de:

a) Plantear una propuesta (o definición del problema), (técnicas de representación)


b) ¿Que algoritmos y estructuras de datos son las más apropiadas.?,
c) Conocer las herramientas del lenguaje C,
d) Codificación de la propuesta,
e) Implementar a) usando los conocimientos de b), para su ejecución.

Con el fin de llevar a cabo la implementación de las propuestas con relativo éxito se deben
considerar una serie de pasos, que en el futuro deberían ser realizadas en forma natural si Ud.
pretende ser Ingeniero en Computación de la ULS.

1. Definición del problema.


______________________________________________________________________________________ 5
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
a) La mayoría de los problemas que aparecen en los libros o se le presentan a los estudiantes
durante la carrera están bien definidos y son precisos. En la vida real, los problemas rara vez se
presentan sin ambigüedades. Como es el caso del siguiente enunciado:

“Dado un texto, clasificar las palabras según su largo y


calcular el porcentaje de cada tipo.”

b) Este tipo de enunciados nos obligan a interactuar con la persona que propuso el problema, para
producir un conjunto de especificaciones claras, no ambiguas que llevan a aclarar lo que deseamos
hacer. Estas especificaciones se conocen como especificaciones de entrada y salida, y
procesamiento especial, y se obtienen a traves de preguntas que se hacen a la persona que planteó
el problema.
c) Si vemos el plantemiento anterior para la entrada, algunas preguntas pertinentes serian: ¿los
valores de entrada son caracteres?, en ese caso, ¿qué caracteres podemos encontrar?, ¿cómo saber
cuando se acabó el texto?, ¿qué pasa con la puntuación?, etc. Lo mismo ocurre respecto de su
salida, la que debe o debería respetar un cierto formato.

2. Definición de algoritmo y Estructura de datos como fundamentales.


Un algoritmo (del latín, dixit algorithmus y éste a su vez del matemático persa al-Jwarizmi) es una
lista bien definida, ordenada y finita de operaciones que permite hallar la solución a un problema.
Dado un estado inicial y una entrada, a través de pasos sucesivos y bien definidos se llega a un
estado final, obteniendo una solución. Por otra parte, una estructura de datos es una forma de
organizar un conjunto de datos elementales con el objetivo de facilitar su manipulación. Un dato
elemental es la mínima información que se tiene en un sistema. En resumen los requerimientos
básicos.

2.1. Codificación de los algoritmos


Los programas deben estar escritos en un lenguaje comprensible por la máquina, ya sea:
a. Lenguaje máquina
b. Código comprensible por un intérprete (que traduce el programa a la máquina).
Para obtener el código que comprende la máquina (ceros y unos) es necesario someter al código
fuente, código comprensible por el ser humano, a un proceso de traducción:
a. Compilar: analizar sintaxis, conversión a código objeto.
b. Enlazado (o linkado): unir el código objeto a las librerías y datos necesarios para que el sistema
operativo sepa ejecutar el programa y convertirlo en un proceso (programa en ejecución). Ejemplo
de lenguajes habitualmente compilables: Pascal, C, C++, Java.

______________________________________________________________________________________ 6
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Otra forma, es que sin necesidad de un compilador el código sea comprensible por el ser humano,
esto se logra o es convertido en tiempo de ejecución (equivalente a una traducción simultánea) a
código comprensible por la máquina. Se diferencia del código compilado en que:

a. Es más rápido.
b. Es más fácil de mantener (modificar, corregir errores), ya que siempre es comprensible por el ser
humano.
c. Es más flexible ya que los lenguajes interpretados permiten mayores abstracciones que los
lenguajes compilados.
d. Es portable a cualquier plataforma que posea un intérprete. El programa compilado solo
funciona en la plataforma para la que fue compilado. Ejemplo de lenguaje habitualmente
interpretado: Perl, VBS, Basic, Javascript, PHP. Existen soluciones intermedias al código
compilado o interpretado. Ejemplo: Java, posee bytecodes que es código compilado intermedio que
requiere ser interpretado parcialmente.

______________________________________________________________________________________ 7
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

3. Técnicas de representación de Algoritmos ( visto en la signatura Introducción a la


Programación)
Existen diferentes técnicas de representación de algoritmos:
a. Pseudocódigo.
b. Diagrama de flujo de datos.

Dicho lo anterior, digamos que un programa propone una solución a un problema, a través de un
algoritmo que emplea una estructura de datos apropiada. Los algoritmos son independientes del
lenguaje de programación y de la máquina en la que se ejecuta.

4. Desarrollo de un programa o implementacion de una propuesta.

En resumen, la solución o propuesta de solución se puede dividir en estas 3 fases:


a. Análisis del problema
b. Diseño del algoritmo
c. Implementación.

UN EJEMPLO

______________________________________________________________________________________ 8
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Titulo de la Actividad Ejemplo: En nuestro caso lo llamamos Recreemos los Triángulos.

Problema
Consideremos el plano bidimensional y en él se dan, por ahora, tres puntos P1, P2 y P3, que
representan los vértices de un triángulo. En este primer proyecto se desea computar alguna de las
características de ellos, como por ejemplo, el área, el perímetro, el baricentro y también si se da un
cuarto punto, se pide computar la distancia de este punto al triángulo.

Descripción
La entrada de los datos al programa por lo general es a través de datos que se ingresan por teclado
o por archivos. En esta oportunidad el ingreso de datos se considera solamente por teclado. En
todo caso el usuario deberá ser preguntado si desea ingresar los datos por archivo o por teclado.
Ud. deberá seleccionar por teclado, sin embargo si elige por archivo el programa no se debería
“caer”.

Una vez que el usuario ya ha sido preguntado, y Ud. elegido su opción por teclado, el programa le
invita a ingresar tres pares de puntos, es decir P1(x1, y1), P2(x2, y2) y P3(x3, y3), que
corresponden a los vértices del triángulo. Luego el programa le pide al usuario las coordenadas de
un cuarto punto P4(x4, y4), el que deberá ser usado para computar la distancia de P4 al triángulo.

Con toda esta información las operaciones a realizar y desplegar son:


 Area del triángulo.
 Perimetro del triángulo.
 Centroide.
 Distancia del cuarto punto al triángulo. La distancia al triángulo desde el punto es
definida como la mínima distancia desde el punto a los tres segmentos de línea
definidos por los tres vértices del triángulo.

El programa debería entregar como salida el resultado de estas operaciones. Por ejemplo:
Para el triángulo definido por (15,20.5) (10,10.5) (20,10.5) tiene:

Un área de 50
Un perimetro de 32.361
Un centroide de (15,13.8)
La distancia entre el punto (15,-5) y el triángulo es 15.5

- Análisis al problema.
Por ejemplo, los puntos P1, P2, P3 y P4 definidos por: P1=(x1,y1), P2=(x2,y2), P3=(x3,y3) y
P4=(x4,y4). ¿Cómo computo el área?. Hallamos la base b, luego encontramos la altura h y
entonces: area = (b*h)/2

______________________________________________________________________________________ 9
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

l12
P P2
(x,y) (x2,y2)

P1
(x1,y1)

P3
(x3,y3)

Para hallar la base necesitamos computar la distancia entre dos puntos del triángulo, digamos
(x1,y1) y (x2,y2) para definir la base para el triángulo, entonces

b = distancia(P1,P2),donde
distancia(P1,P2) = sqrt( (x1-x2)2 + (y1-y2)2 )….etc.
Otro camino es :

______________________________________________________________________________________ 10
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
P2
(x2,y2)

A2
P1
A1
(x1,y1)

A
b

A3

P3
(x3,y3)

a
El área del triángulo es:
A= a*b – A1 – A2 – A3, donde, las áreas A1, A2 y A3 del triángulo son fáciles de encontrar.
¿Cómo computo el perímetro?

perímetro = distancia(P1,P2) + distancia(P1,P3) + distancia(P2,P3).

¿Cómo computo el Centroide?


P2
(x2,y2)
P12
(x12,y12)

P1
(x1,y1)
Pc
(xc,yc)
P23
(x23,y23)

P3
(x3,y3)

¿Cómo computo la distancia desde un cuarto punto al triángulo?


______________________________________________________________________________________ 11
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Caso 1:
P2
(x2,y2)

P1
(x1,y1)
P4
(x4,y4)

P3
(x3,y3)

Caso 2:
P2
(x2,y2)

P1
(x1,y1) P4
(x4,y4)

P3
(x3,y3)

-Requerimientos, basados en el análisis.


- Definir constantes, si existen.
- Definir el tipo de datos para la entrada de ellos:
int x1, y1;
int x2, y2;
______________________________________________________________________________________ 12
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
int x3, y3;
int x4, y4; // cuarto punto

Definir el tipo de datos para la salida de datos:


double area;
double perimetro;
double distancia;
double xc, yc; /* el centroide */

Variables:
double b; /*base del triángulo*/
double h; /*altura del triángulo*/

Formulas a utilizar, basado en el análisis:

- Diseño de la propuesta.
Algoritmo inicial, por ejemplo
Darle la opción para que el usuario diga si los datos los va a incorporar por archivos o por teclado.
Si es a través de un archivo, diga por esta vez NO!
Si la opción es por teclado, entonces invitar al usuario para que indique los cuatro puntos que se
requieren.
Computar el área, con ellos.
Computar el perímetro, con ellos.
Computar la distancia desde el cuarto punto al triángulo
Desplegar el área, perímetro y distancia.
Algoritmo Refinado, por ejemplo
Darle la opción (0 si es por teclado, 1 si es archivo).
Si la opcion = = 0
darle x1 y y1.
darle x2 y y2.
darle x3 y y3.
darle x4 y y4.
Si la opción = = 1
Decir que por ahora no hay archivo activo. Vuelva a preguntar.
Computar el área
Computar la base del triángulo:
b = distancia( x1, y1, x2, y2 ) = sqrt( (x1-y1)*( x1-y1) + (x2-y2)*( x2-y2) )
Computar la altura del triángulo
h = altura( x1, y1, x2, y2, x3, y3 )
(Todos estos cálculos se sustentan con el análisis.)
Computar el perimetro.
perimetro = distancia( x1, y1, x2, y2 ) + distancia( x1, y1, x3, y3 ) + distancia( x3, y3, x2, y2 )
Computar la distancia desde el cuarto punto al triángulo.
(Todos estos cálculos se sustentan con el análisis.)
Desplegar el área, perimetro y distancia.
- Implementación o código fuente.

______________________________________________________________________________________ 13
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
En este apartado recomiendo diferentes recursos LIBRES disponibles en la web. Por ejemplo, Dev-
C++, que es un entorno integrado de desarrollo (IDE) que emplea MinGW. Es una buena solución
si nos gustan los IDEs avanzados. Lo podemos descargar con MinGW incluido (e integrado).

/* Programa:
* Descripción:
* Autor:
* Curso:
* Profesor Laboratorio:
* Fecha:
*/

Un ejemplo más realista desde el punto de vista de los alumnos.

Con este ejemplo quiero mostrar lo que usualmente hacen los alumnos al saltarse el proceso de
análisis y diseño, pues de esta forma se llega a una solución funcional pero no robusta o capaz de
mantenerse en el tiempo, frente a otros requerimientos.

Análisis del problema y propuesta de una solución


Problema: Ordenar un conjunto de datos.
Propuesta: Queremos ordenar de menor a mayor un vector de tamaño N de números enteros. En
donde, No existen elementos repetidos en el vector.

Implementación

#include<stdio.h>

int
main ()
{
int j, i, tmp ;
int v[10]={2,4,5,3,1,6,7,8,10,9};

puts("El vector inicial: ");

for(i=0;i<10;i++)
printf("%i ",v[i]);

printf("\n");

for (i=0;i<10;i++) {
for (j=i+1;j<10;j++) {
if (v[i]>v[j]) {
tmp=v[i];
v[i]=v[j];
v[j]=tmp;
}
}
}

puts("El vector ordenado: ");

______________________________________________________________________________________ 14
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
for(i=0;i<10;i++)
printf("%i ",v[i]);

printf("\n");
getch();
return 0;

A continuación se muestran otras propuestas, que surgen a través de discutir o realizar el análisis y
el diseño de la propuesta. Por ejemplo, que algoritmo de ordenamiento utilizar y porque, se
consideran nº repetidos en la entrada, podrán ordenarse caracteres también. ….etc.

#include<stdio.h>

int
main ()
{
int j;int i;int tmp;int v[10]={2,4,5,3,1,6,7,8,10,9};

puts("el vector inicial: ");

i=0;
while (i<10) {
printf("%i ",v[i]);
i++;
}

printf("\n");

i=0;
while (i<10) {
j=i+1;
while (j<10) {
if (v[i]>v[j]) {
tmp=v[i];
v[i]=v[j];
v[j]=tmp;
}
j++;
}
i++;
}

puts("El vector ordenado: ");

______________________________________________________________________________________ 15
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
i=0;
while (i<10) {
printf("%i ",v[i]);
i++;
}

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

#include<stdio.h>

void ordenaVector(int dim, int v[]);


void muestraVector(int dim, int v[]);

/* 0 -> no hay duplicados, 1 -> si hay */


int buscaDuplicados(int dim, int v[]);

int main ()
{
int v[100];
int dim,ok;
int i, j, tmp, k;

do {
printf("Introduce el tama¤o del vector seguido de intro (2-100): ");
scanf("%i",&dim);

if (dim>100 || dim<2) {
puts("Error: Fuera de rango");
ok=0;
} else {
ok=1;
}
} while (!ok);

do {
for (i=0;i<dim;i++) {
printf("elemento %i: ",i+1);
scanf("%i",&v[i]);
}

if(ok=buscaDuplicados(dim,v))
puts("Error: no repitas digitos, vuelve a introducir.");

} while(ok);

printf("Vector introducido: ");


muestraVector(dim,v);
do {
printf("MENU\n");
printf("\t1.Ordenar el vector.\n");
printf("\t2.Buscar elemento en el vector.\n");
printf("\t0.Salir.\n");
______________________________________________________________________________________ 16
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
printf("opcion: ");
scanf("%i",&ok);

switch (ok) {
default:
puts("Error: opci¢n incorrecta.");
break;
case 1:
ordenaVector(dim,v);

printf ("El vector ordenado:");


muestraVector(dim,v);
break;
case 2:
printf ("introduce numero a buscar en el vector: ");
scanf ("%i",&k);

for (i=0;i<dim && k!=v[i];i++);


if (k==v[i])
printf("Numero encontrado en la posicion: %i\n",i+1);
else
printf ("El numero %i no se encuentra en el vector\n",k);
break;
case 0:
puts("chaolin!");
return 0;
break;
}
} while(1);
}

void ordenaVector(int dim, int v[])


{
int i,j,tmp;

for(i=0;i<dim;i++) {
for(j=i+1;j<dim;j++) {
if (v[i]>v[j]) {
tmp=v[i];
v[i]=v[j];
v[j]=tmp;
}
}
}

return;
}

int buscaDuplicados(int dim, int v[])


{
int i;
int j;
for (i=0;i<dim;i++) {
for (j=0;j<dim;j++) {
if (i!=j) {
if (v[i]==v[j]) {
______________________________________________________________________________________ 17
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
return 1;
}
}
}
}
return 0;
}

void muestraVector(int dim, int v[])


{
int i;
for (i=0;i<dim;i++) {
printf("%i",v[i]);
if (i<dim-1) {
printf(",");
}
}
printf(".\n");
return ;
}

______________________________________________________________________________________ 18
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

1 ESTRUCTURAS DE CONTROL ( Entrada y Salida de Datos )

Ejemplo 1 (Trasladando un algoritmo de Pascal a C )

{ programa para encontrar el factorial de 6}


program ejemplo;
const valor=6;
var i, j: integer;
begin
j:=1;
for i:=1 to valor do
j:= j*i;
writeln(‘ El factorial de 6 es ‘, valor, ‘ es ‘, j );
end.

En general, un programa escrito en un lenguaje de alto nivel, tal como Pascal ó C, posee dos tipos
de items: DATOS y PROCEDIMIENTOS.

 DATOS es la información que se almacena, y se hace referencia a ella a través de tipos de


datos.
 PROCEDIMIENTOS son descripciones de las etapas requeridas para procesar los datos.

En C todos los procedimientos son llamados FUNCIONES, mientras que Pascal, posee
procedure y function.

La traducción a un programa en C, está dado por el código siguiente:

/* file: ej0.c
Nombre del programa: factorial de 6
Fecha: 3.12.96.
Actualización: 11.07.2008
Propósito: Mostrar un primer programa en C
*/

#include<stdio.h>
#define VALOR 6
int i,j;
main( )
{ j=1;

______________________________________________________________________________________ 19
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
for (i=1; i<=VALOR; i++)
j=j*i;
printf( "el factorial de %d es %d\n", VALOR, j);
getch();
return(0);
}

Comentarios:
Ud. puede ver la correspondencia que existe entre Pascal y C, la diferencia es que C parte con el
código #include<stdio.h>, el cual incluye la librería estandar de Entrada/Salida para que Ud.
pueda leer y escribir valores, manejar archivos de texto, etc. C posee un gran número de librerías
las cuales son utilizadas dependiendo del problema, entre ellas se cuenta con stdio, string, time,
math y otras, las cuales a futuros veremos en que tipo de problemas se aplican.
Cuando Ud. ingresa este programa las líneas #include y #define parten al comienzo de la 1ª
columna, suponiendo la existencia de una barra virtual la cual clasifica el texto según casilleros ó
columnas. Para los usuarios de Borland C++ ó Turbo C/C++ el archivo debe tener la extensión
(.c) o bien (.cpp) y se compila, ejecuta, según los visores windows. La línea #define crea una
constante, este tipo de declaraciones son llamados preprocesadores. Dos variables globales son
declaradas usando la línea int i, j ; La línea main( ) declara la función principal. Todo programa en
C debe tener esta función principal. Cada programa en C es particionado en funciones que se
llaman entre sí. En C los paréntesis {, y } reemplazan el begin y end de Pascal. También, el
símbolo = remplaza la asignación := de Pascal. La estructura for de Pascal es semánticamente la
misma en C, salvo que C incorpora otros operadores para incrementar o decrementar las variables,
i++. La línea return(0); es utilizada para decirle al sistema operativo (SO) que el programa
termina y se sale ( status = 0 ), la declaración getch() se relaciona con el hecho de mantener en
pantalla la ejecución. En la declaración printf ( ) es extremadamente importante que el nombre del
operador esté en el formato correspondiente al tipo de la variable declarada.

Ejemplo 2 (Un algoritmo en C)

Dadas las horas trabajadas y el pago por hora, escribir un programa que compute el sueldo de una
persona con un número específico ID. El programa deberá mprimir los datos y el sueldo.

Solución: Imprimir título del programa; (especificando file, nombre del programa, fecha y
propósitos del mismo;
set datos: set de números ID, horas trabajadas, pago por hora;
set sueldo: el producto de las horas trabajadas y el pago por hora;
print : datos y resultados;

/* file: pe971.c
Nombre del programa: sueldo
Fecha: 3.12.96
Actualización: 11.07.2008

______________________________________________________________________________________ 20
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Propósito: Este programa calcula el sueldo para una persona, dadas las horas
trabajadas y el costo por hora
*/
#include<stdio.h>

main()
{ /* declaración*/
int id_numero; /* tipo entero */
float horas_trabajadas, costo_por_hora, sueldo; /* tipo punto flotante */

/* Imprimir el título */
printf("***cálculo del sueldo***\n\n"); /* saltando una línea */

/* inicializando variables*/
id_numero=123;
horas_trabajadas=20.0;
costo_por_hora=7.5;

/* cálculo de sueldo */
sueldo=horas_trabajadas*costo_por_hora;

/* imprime datos y resultado */


printf("id_numero = %d\n", id_numero);
printf("horas_trabajadas = %f, costo_por_hora = %f\n", horas_trabajadas,
costo_por_hora);

printf("Sueldo = %f\n", sueldo);


getch();
return(0);
}

Comentarios:
En cualquier programa los simbolos /* y */ sirven para expresar los comentarios de un programa ó
dejar inactiva algunas líneas de la ejecución del mismo. Cada línea de sentencia termina con ;
Declaración de las librerías que utilizará, en este caso, <stdio.h>, la que se incluye con la línea
#include<stdio.h>
El cuerpo de la función main( ) está contenido entre { y }.
Tomar en cuenta que sobre un PC solamente 16 bits ( 2 bytes ) son usados, de manera que el rango
es de 32767 a -32768. (El rango representable por k-bits es desde -2k-1 a +2k-1 - 1.
¿ Qué resulta si declara int zip; zip= 92126; y lo ejecuta ?.
C utiliza punto decimal para distinguir entre número punto flotante y número entero.
printf( ) es la función utilizada para imprimir los resultados la cual está incluída en la librería
<stdio.h>. El simbolo \n , indica fin de línea.
printf ( ) imprime según los formatos respectivos, %d es la conversión a entero y %f es la
conversión a punto flotante.
La línea return(0); es utilizada para decirle al SO que el programa se sale ( Status=0 );

El programa como es sabido debe ser compilado, es decir trasladado a un lenguaje de máquina
(programa objeto) usando un compilador. Al final de este proceso, en donde C utiliza sus librerías
de funciones estandares, resulta un programa ejecutable. El efecto es:

______________________________________________________________________________________ 21
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
*** cálculo del sueldo ***

id_numero= 123
horas_trabajadas= 20.000000, costo_por_hora=7.500000
sueldo= 150.000000

Comentarios:
Respecto a la salida de los datos, podemos decir que, el primer printf( ) imprime el valor de
id_numero en la posición donde %d está localizada. La forma interna del valor de id_numero es
convertida a formato entero decimal e impreso, la salida es: id_numero= 123
El siguiente printf( ) escribe los valores de horas_trabajadas a la posición de la primera %f, y el
valor de costo_por_hora a la posición del segundo %f. Las formas internas son convertidas a
números reales decimales ( puntos flotantes ) e imprime. La salida es
horas_trabajadas= 20.000000, costo_por_hora=7.500000
Los valores punto flotante son impresos, por defecto, con seis dígitos después del punto decimal.
La impresión final es : sueldo= 150.000000

Ejemplo 3 ( Entrada de Datos en C )

Utilizando el Ejemplo 2, construiremos un programa, pero que ahora los datos id_numero,
horas_trabajadas y costo_por_hora serán leídos desde el teclado.
Solución: Imprimir título del programa; (especificando file, nombre del programa, fecha y
propósitos del mismo;
leer los datos id_numero, horas_trabajadas y costo_por_hora ;
set sueldo: el producto de las horas trabajadas y el pago por hora;
print : datos y resultados;

/* file: pe972.cpp
Nombre del programa: sueldo
Fecha: 3.12.96
Propósito: Este programa calcula el sueldo para una persona, dadas las horas
trabajadas y el costo por hora, cuyos datos son ingresados por teclado
*/
#include<stdio.h>

main()
{ /* declaración*/
int id_numero; /* tipo entero */
float horas_trabajadas, costo_por_hora, sueldo; /* tipo punto flotante */
/* Imprimir el título */
printf("***cálculo del sueldo***\n\n"); /* saltando una línea */
/* leer datos en las variables */
printf(“tipo id_numero: “);
scanf(“%d”, &id_numero);
______________________________________________________________________________________ 22
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
printf(“horas_trabajadas : “);
scanf(“%f”, & horas_trabajadas);
printf(“costo_por_hora : “);
scanf(“%f”, & costo_por_hora);

/* cálculo de sueldo */
sueldo=horas_trabajadas*costo_por_hora;

/* imprime datos y resultado */


printf("\nid_numero = %d\n", id_numero);
printf("horas_trabajadas = %f, costo_por_hora = %f\n", horas_trabajadas,
costo_por_hora);

printf("Sueldo = %f\n", sueldo);


getch();
return(0);
}

Comentario:
Para que este programa lea el valor en vez de utilizar una constante como Ejemplo 1, se utilizó

/* printf es una función de la libreria stdio.h */


printf (“ Entre el valor:” );
scanf ( “ %d”, &valor );

/* lo que en Pascal es*/


write(‘ Entre el valor:’ );
readln(valor); */

El signo & delante de valor, representa la dirección del operador en C, lo que veremos en el tema
de punteros.
printf( ) es la función que facilita escribir por consola, similarmente existe la función scanf( ) para
leer datos desde el teclado y almacenarlos en algún objeto, esto último se realiza en C a través del
operador de dirección &.
Las distintas líneas pueden resumirse en
scanf(“%d %f %f, &id_numero, &horas_trabajadas, &costo_por_hora );
La salida es la misma a la que se obtuvo en el Ejemplo 2.

Ejemplo 4 ( Tomando decisiones )

Utilizando el Ejemplo 3, se le pide construir un programa que sea capaz de calcular el sueldo,
excepto que las horas de sobretiempo se pagan a 1.5 veces el valor normal, de otra manera se
calcula el sueldo en forma normal.
La propuesta esta orientada a utilizar las diversas estructuras de control que posee C. Es así como
el programa( en esta nueva versión) deba durante su ejecución realizar decisiones, de manera que
debe incorporarse una etapa de if--then.

if ( horas_trabajadas > 40.0 )


sueldo= 40.0 * costo_por_hora + 1.5 * costo_por_hora * ( horas_trabajadas - 40.0 );
else
sueldo= horas_trabajadas * costo_por_hora;
______________________________________________________________________________________ 23
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Trasladando a C queda:

if ( horas_trabajadas > 40.0 ) {


pago_regular= 40.0 * costo_por_hora ;
sobre_tiempo= 1.5 * costo_por_hora * ( horas_trabajadas - 40.0 );
}
else {
pago_regular = horas_trabajadas * costo_por_hora;
sobre_tiempo=0.0;
}
pago= pago_regular + sobre_tiempo;

Ejemplo 5: ( Decisiones basados sobre valores TRUE y FALSE )

Basado en el Ejemplo 4, la propuesta puede ser mejorada al considerar operadores lógicos.

if ( ( horas_trabajadas > LIMITE) AND (costo_por_hora < = MAX ) )


calcular pago_regular y sobre_tiempo
else
calcular solamente pago_regular, sin sobre_tiempo.

Trasladando a C queda:

if ( ( horas_trabajadas > LIMITE) && (costo_por_hora < = MAX ) ) {


pago_regular= LIMITE * costo_por_hora;
sobre_tiempo= FACTOR * costo_por_hora * ( horas_trabajadas - LIMITE );
}
else }
pago_regular = horas_trabajadas * costo_por_hora;
sobre_tiempo=0.0;
}

Observación:
Es útil recordar los operadores lógicos, precedencia y asociaciones, además ver la definición de
constantes para definir LIMITE y FACTOR ( preprocesadores #define...).

Ejemplo 6: ( Un simple...while )

Consideremos el Ejemplo 3 salvo que el programa ahora, lee los datos, computa el sueldo e
imprime los datos y el sueldo para un número desconocido de personas. Por tal motivo surge la
necesidad de usar otras estructuras de control, en esta oportunidad es el ciclo.

/* file: pe97c2.cpp
Nombre del programa: sueldo
Fecha: 3.12.96
Propósito: Este programa calcula el sueldo para un número arbitrario de
persona,
dada las horas trabajadas y el costo por hora
______________________________________________________________________________________ 24
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
*/
#include<stdio.h>
#define LIMITE 40.0 /* constantes */
#define FACTOR 1.5

main()
{ /* declaración*/
int id_numero, contador;
float horas_trabajadas, costo_por_hora, pago_regular, sobre_tiempo, sueldo;

/* Imprimir el título */
printf("***cálculo del sueldo***\n\n");

printf("número de personas: ");


scanf("%d", &contador);
while ( contador > 0 ) {

/* lee los datos en la variable */


printf("\nTipo id_numero: ");
scanf("%d", &id_numero);
printf("\nHoras trabajadas: ");
scanf("%f", &horas_trabajadas);
printf("\nCosto por hora: ");
scanf("%f", &costo_por_hora);

/* cálculo de resultados */
if (horas_trabajadas > LIMITE ) {
pago_regular= LIMITE * costo_por_hora;
sobre_tiempo= FACTOR * costo_por_hora * ( horas_trabajadas - LIMITE );
}
else {
pago_regular= horas_trabajadas * costo_por_hora;
sobre_tiempo= 0.0;
}
sueldo= pago_regular + sobre_tiempo;

/* imprime datos y resultado */


printf("\nid_numero = %d\n", id_numero);
printf("horas_trabajadas=%f,costo_por_hora=%f\n",horas_trabajadas,
costo_por_hora);

printf("Pago Regular = %f, Sobre_tiempo =%f\n", pago_regular, sobre_tiempo);


printf("Sueldo = %f\n", sueldo);

/* contador */
contador= contador - 1;
}
return(0);
}

Después de ejecutar una sesión de este programa se obtiene la siguiente salida:

***cálculo del sueldo***


número de personas: 2

______________________________________________________________________________________ 25
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Tipo id_numero: 123
Horas trabajadas: 20
Costo por hora: 7.5
id_numero=123
horas_trabajadas=20.000000 , costo_por_hora= 7.500000
Pago_regular= 150.000000, Sobre_tiempo= 0.000000
Sueldo= 150.000000

Tipo id_numero: 456


Horas trabajadas: 50
Costo por hora: 10

id_numero=456
horas_trabajadas=50.000000 , costo_por_hora= 10.000000
Pago_regular= 400.000000, Sobre_tiempo= 150.000000
Sueldo= 550.000000

Ejemplo 7 ( Controlando el término de un loop )

/* file: pe97c4.cpp
Nombre del programa: sueldo
Fecha: 10.12.96
Propósito: Este programa lee las horas trabajadas, costo por hora y calcula el
sueldo para un número arbitrario de persona. El programa también computa el
sueldo más alto, número de personas y el sueldo promedio.
*/
#include<stdio.h>
#define LIMITE 40.0
#define FACTOR 1.5

main()
{ /* declaración*/
int id_numero, contador;
float horas_trabajadas, costo_por_hora,
pago_regular, sobre_tiempo, sueldo, sueldo_alto, promedio;

/* Imprimir el título */
printf("***cálculo del sueldo***\n\n");

/* inicializa acumuladores */
contador=0;
sueldo_alto=0;

/* inicializa variables loop, 0 se activa cuando se ha ingresado al menos


un valor */
printf("Tipo id_numero, 0 para salir:");
scanf("%d", &id_numero);

while ( id_numero > 0 ) {


/* lee los datos en la variable */
printf("\nHoras trabajadas: ");
______________________________________________________________________________________ 26
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
scanf("%f", &horas_trabajadas);
printf("\nCosto por hora: ");
scanf("%f", &costo_por_hora);
/* cálculo de resultados */
if (horas_trabajadas > LIMITE ) {
pago_regular= LIMITE * costo_por_hora;
sobre_tiempo= FACTOR * costo_por_hora * ( horas_trabajadas - LIMITE );
}
else {
pago_regular= horas_trabajadas * costo_por_hora;
sobre_tiempo= 0.0;
}
sueldo= pago_regular + sobre_tiempo;

/* imprime datos y resultado */


printf("\nid_numero = %d\n", id_numero);
printf("horas_trabajadas = %f, costo_por_hora = $%f\n",
horas_trabajadas, costo_por_hora);
printf("Pago Regular = $%f, Sobre_tiempo = $%f\n", pago_regular,
sobre_tiempo);
printf("Sueldo = $%f\n", sueldo);

/* acumuladores activados */
contador= contador + 1;
sueldo_alto= sueldo_alto + sueldo;

/* depurar las sentencias de impresión */


printf("\ndebug:sueldo_alto=%f, contador=%d\n", sueldo_alto, contador);

/* update variable loop */


printf("\nTipo id_numero, 0 para salir:");
scanf("%d", &id_numero);
}
if (contador > 0) {
promedio= sueldo_alto/ (float)contador;
printf("\n ***Resumen*** \n");
printf("Número de personas=%d, sueldo más alto=$%f\n", contador,
sueldo_alto);
printf("Sueldo promedio=$%f\n", promedio);
}
return(0);
}

______________________________________________________________________________________ 27
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Aplicaciones Diversas (entendiendo el porque se producen ciertos Errores)

Programa que hace un primer saludo. El típico Hola mundo.

#include <stdio.h>

int main()
{
printf( "Hola mundo" );
getch();
return 0;
}

Observar que existen una serie de funciones en la biblioteca estándar de C, las funciones son
subprogramas que ya están compilados, es decir, junto a cualquier compilador de C se le deben
acompañar los códigos objeto de todas las funciones de su biblioteca estándar, pero no
necesariamente sus códigos fuente. Por tanto, aunque no sea posible modificar sus códigos fuente,
(en Java sí se puede) pero se pueden usar dichas funciones en cualquier programa. Por ejemplo, se
puede llamar a la función printf() para que muestre por pantalla el saludo "Hola mundo".
printf( "Hola mundo" );
______________________________________________________________________________________ 28
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Para utilizar una función (sea de la biblioteca estándar de C o no) en un programa, la función debe
ser declarada previamente, al igual que se tienen que declarar las variables y las constantes que usa
un programa. En este caso no fue necesario pues ella es encuentra en la librería stdio.h. (stdio,
Standard Input/Output). De allí la necesidad de informarle al compilador de la declaración de la
función printf, a través de la declaración en el encabezado #include <stdio.h> . Las funciones de la
biblioteca estándar de C están clasificadas en base a su funcionalidad, y sus declaraciones se
agrupan en archivos con extensión (.h), los cuales son llamados archivos de cabecera. Además
de stdio.h, algunos de los archivos de cabecera más utilizados en C son: math.h, string.h y stdlib.h.
En ellos están escritas, respectivamente, las declaraciones de las funciones matemáticas, funciones
de cadena y funciones de utilidad de la biblioteca estándar de C.

Toda función retorna un valor. En nuestro primer programa se ha escrito:

return 0;

Esto quiere decir que la función main devuelve el valor 0. Precediendo a main se ha escrito la
palabra reservada int, indicando así, que la función retornará un valor de tipo int entero).

int main()

En general, la instrucción return suele ser la última del bloque de instrucciónes de la función main.
Al retornar el valor 0, indica (informa al sistema operativo) que el programa finalizó correctamente,
es decir, sin producirse ningún error en su ejecución. Cuando la función main devuelva un valor
distinto de cero, esto siginificará que se ha producido algún error en la ejecución del programa, o
que ha ocurrido algo fuera de lo normal. Notar que se ha incluido en este programa la función
getch(), con el fin de mostrar la pantalla de MS-DOS y visualizar el resultado de su ejecución. La
instrucción return es una de las instrucciones de control que existen en C. Por tanto, es una
palabra reservada. Después del valor de retorno (que es una expresión) se debe escribir un punto y
coma (;).

#include <stdio.h>

int main()
{
int a, b;

printf( "Introduzca el primer numero: " );


scanf( "%d", &a );
printf( "Introduzca el segundo numero: " );
scanf( "%d", &b );
printf( "Los valores son: %d, %d ", a, b );
getch();
return 0;
}

por pantalla veremos algo parecido a:

______________________________________________________________________________________ 29
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Obsérvese que, cuando se ejecuta scanf(), por ejemplo, en la primera vez

scanf( "%d", &a );

el programa se detiene a la espera de que el usuario teclee el dato de entrada requerido, el cual es
almacenado, temporalmente, en el buffer (memoria intermedia) de la entrada estándar. Y cuando se
pulsa la tecla INTRO, es, en ese momento, cuando a la variable a se le asigna el valor introducido.
Pero, además, se produce un salto de línea automático, de forma que, después de introducir el
número 12, la siguiente instrucción se muestra una línea más abajo. De igual forma, después de la
instrucción
scanf( "%d", &b );

también se produce un salto de línea automático. En este ejemplo todo ha ido muy bien, sin
embargo, se tiene que tener especial cuidado si utilizados scanf ( ) para leer caracteres. Notar ahora
lo siguiente,

#include <stdio.h>

int main()
{
char a, b, c;

printf( "Introduzca primer caracter: " );


scanf( "%c", &a );
printf( "Introduzca segundo caracter: " );
scanf( "%c", &b );
printf( "Introduzca tercer caracter: " );
scanf( "%c", &c );
printf( "Los valores son: %c, %c, %c ", a, b, c );
getch();

return 0;
}

por pantalla se verá algo similar a:

______________________________________________________________________________________ 30
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

En esta ocasión, ¿por qué no se ejecuta, correctamente, la siguiente instrucción?

scanf( "%c", &b );

La razón es la siguiente: cuando se ejecuta la primera instrucción de entrada

scanf( "%c", &a );

después de asignar el carácter 'f' a la variable a, se produce un salto de línea automático, pero, en el
buffer del teclado también se ha quedado almacenada la secuencia de escape (\n), que es,
precisamente, un carácter. En consecuencia, cuando se ejecuta la instrucción

scanf( "%c", &b );

a la variable b se le asigna el salto de línea almacenado en el buffer de la entrada estándar, y la


ejecución del programa continua con la siguiente instrucción. Ahora, el buffer vuelve a estar vacío
y, por tanto, la entrada de la variable c sí que se ejecuta de forma correcta.

Para comprobar que esto es así, podemos sustituir la instrucción

printf( "Los valores son: %c, %c, %c ", a, b, c );

por esta otra:

printf( "Los valores son: %d, %d, %d ", a, b, c );

En pantalla veremos:

¿Y que fue lo que paso ahora….?. Como se puede comprobar en las tablas del ASCII, los números
102, 10 y 104 corresponden a los caracteres 'f', LF (Salto de Línea) y 'h', respectivamente. Veamos
a continuación este pequeño “detallito”.

El Código Estándar Americano para el Intercambio de Información (American Standard Code for
Information Interchange, ASCII) es, hoy en día, el código más utilizado en los equipos
computacionales. ASCII emplea grupos de 7 bits para codificar caracteres en binario, permitiendo
representar a 27 = 128 caracteres. Su tabla de correspondencias es la siguiente:

______________________________________________________________________________________ 31
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Figura. Tabla ASCII.

Los dígitos que rodean la tabla sirven para identificar al número decimal que corresponde a cada
carácter. De modo que, para un determinado carácter, el número decimal que le corresponde se
obtiene de agrupar los dígitos de su fila y de su columna.

Por ejemplo, al carácter H del ASCII le corresponde la agrupación de los dígitos (7) de su fila y (2)
de su columna, es decir, el carácter H se codifica con el código 7210 = 10010002.

Los primeros 32 caracteres del ASCII son de control. En la siguiente tabla se muestran sus
significados:

______________________________________________________________________________________ 32
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Figura. Significado de los 32 primeros caracteres del ASCII.

______________________________________________________________________________________ 33
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

El carácter 32 (SP) representa al Espacio en Blanco y el carácter 127 (DEL) a Borrar. El resto de
caracteres corresponden a las letras del alfabeto inglés (a, b, c, d, e,...), los dígitos del sistema
decimal (0, 1, 2, 3,...) y caracteres especiales (@, #, %,...). Por ejemplo,

//El siguiente programa muestra el número ASCII de cualquier carácter.


#include <stdio.h>
#include <conio.h>
main()
{
char ch;
printf ("Introduzca un caracter:");
ch=getche ();
printf ("\nEl codigo ASCII es:%d",ch);
getch();
}

______________________________________________________________________________________ 34
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

También existe un ASCII extendido de 8 bits con el que se puede representar a 28 = 256 caracteres.
En dicho código, los 128 primeros caracteres coinciden con el ASCII de 7 bits y, el resto,
corresponden a algunos caracteres alfabéticos no ingleses (ñ, Ñ, á, é,...), algunas letras griegas,
símbolos matemáticos y caracteres gráficos. Véase la siguiente figura:

Figura. Tabla de los caracteres (128-255) del ASCII extendido.

Otro ejemplo es:


#include <stdio.h>
/* Con este programa, se obtiene un número ASCII y su letra correspondiente;
¿Cuál es la salida.?. En este caso "97 a" en ambos printf */

main()
{
char car1 = 'A', car2 = 65, car3 = 0;
car3 = car1 + 'a' - 'A';
printf("%d %c\n", car3, car3);
car3 = car2 + 32;
printf("%d %c\n", car3, car3);
getch();
}

Comentario:
char car1 = 'A', car2 = 65, car3 = 0; // el valor ASCII de 'A' es 65
car3 = car1 + 'a' - 'A'; // car3 = 'A' + 'a' - 'A' = 'a'
printf("%d %c\n", car3, car3); // 'A' dista de 'a' 32 caracteres, luego:
______________________________________________________________________________________ 35
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
car3 = car2 + 32; // car3 = 'A' + 32 = 'a'
Este es el código ASCII extendido más utilizado por las computadoras digitales. Sin embargo, los
caracteres (128-255) pueden variar de unos sistemas operativos a otros, dependiendo de su
configuración. Así, en función de los idiomas que se hablan en distintas zonas geográficas del
mundo, la Organización Internacional de Estándares (International Standards Organization, ISO) ha
definido distintos estándares ASCII, tales como: ISO 8859-1 (usado para el castellano), ISO 8859-
2 Latín (utilizado en Europa central), ISO 8859-5 Cirílico (para lenguajes eslavos), ISO 8859-6
Árabe (para lenguajes arábigos), etc.

En un procesador de texto, los caracteres del ASCII más frecuentemente utilizados, tales como:
letras (a, b, c,...), dígitos (0, 1, 2,...) y signos de puntuación (?, ;, :,...) son fáciles de imprimir por
pantalla, pulsando directamente su tecla correspondiente. Sin embargo, para mostrar otros
caracteres, es necesario utilizar una combinación de teclas, por ejemplo, el símbolo almohadilla (#)
se imprime pulsando las teclas (Alt Gr + 3). No obstante, cualquier carácter del ASCII se puede
mostrar por pantalla tecleando su código decimal, al mismo tiempo que se presiona la tecla Alt. Por
ejemplo, en un procesador de texto, el carácter H del ASCII se puede imprimir por pantalla
pulsando las teclas (7) y (2) del Bloque Numérico del teclado, al mismo tiempo que se mantiene
presionada la tecla (Alt).

Por otra parte, la combinación de la tecla (Alt) con los 32 primeros números decimales (0-31),
también imprime por pantalla algunos caracteres gráficos.

Figura. Tabla de los caracteres "gráficos" (0-31) del ASCII.

Para resolver este problema, antes de leer un carácter con scanf(), hay que vaciar (limpiar) el buffer
del teclado. Para ello, se utiliza la función fflush().

#include <stdio.h>

int main()
{
char a, b, c;

printf( "Introduzca primer caracter: " );


scanf( "%c", &a );
printf( "Introduzca segundo caracter: " );
fflush( stdin );
scanf( "%c", &b );
printf( "Introduzca tercer caracter: " );
fflush( stdin );
scanf( "%c", &c );
printf( "Los valores son: %c, %c, %c ", a, b, c );
getch();
return 0;
}
______________________________________________________________________________________ 36
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

La primera vez que se ejecuta scanf(), el buffer del teclado está vacío, por tanto, no es preciso
utilizar fflush(), pero sí, en los dos casos posteriores. Obsérvese que, a fflush() hay que indicarle el
buffer a limpiar, Standar Input (stdin) en este caso. Como pueden ver esto nos introduce al hecho
que no todo puede funcionar como uno quisiera. Es así como en C existen diversos tipos de errores
(como en todo lenguaje de programación!!). Ellos son:

 Error de sintaxis:
En programación, un error de sintaxis se produce al escribir, incorrectamente, alguna parte del
código fuente de un programa. De forma que, dicho error impedirá, tanto al compilador como al
intérprete, traducir dicha instrucción, ya que, ninguno de los dos entenderá qué le está diciendo el
programador. Por ejemplo, si en lenguaje C, en vez de la instrucción

printf( "\n Introduzca el primer numero (entero): " );

un programador escribe

prrintf( "\n Introduzca el primer numero (entero): " );

cuando el compilador o el intérprete lean esta línea de código, ninguno de los dos entenderá qué es
prrintf y, por tanto, no sabrán traducir esta instrucción a código máquina, por lo que, ambos
pararán la traducción y avisarán al programador con un mensaje de error.

 Error de ejecución:
En programación, un error de ejecución se produce cuando la máquina no puede ejecutar alguna
instrucción de forma correcta. Por ejemplo, en lenguaje C, la instrucción

c = 5 / 0;

es correcta sintácticamente y será traducida a código binario. Sin embargo, cuando la computadora
intente realizar la división

5/0

se producirá un error de ejecución, ya que, matemáticamente, no se puede dividir por cero. Por
ejemplo,

#include <stdio.h>

int main()
{
int a;
float b;
______________________________________________________________________________________ 37
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
a = 0;
b = 6.4 / a;

printf( "%f", b );
getch();
return 0;
}

 Error de lógica:
En programación, los errores de lógica son los más difíciles de detectar. Cuando un programa no
tiene errores de sintaxis ni de ejecución, pero, aún así, no funciona bien, esto es debido a la
existencia de algún error lógico. De manera que, un error de lógica se produce cuando los
resultados obtenidos no son los esperados. Por ejemplo, en lenguaje C, si en vez de la instrucción

c = a + b;

un programador hubiera escrito

c = a * b;

hasta que no se mostrase por pantalla el resultado de la operación, el programador no podría darse
cuenta del error, siempre que ya supiese de antemano el resultado de la suma. En este caso, el
programador podría percatarse del error fácilmente, pero, cuando las operaciones son más
complejas, los errores de lógica pueden ser muy difíciles de detectar. Por ejemplo

#include <stdio.h>

int main()
{
float base, altura;

base = 6.3;
altura = 4.;

printf( "El area es: %f", base * altura / 3 );


getch();
return 0;
}

______________________________________________________________________________________ 38
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

aquí al programador se le olvidó que la altura de un triángulo es (base * altura) /2.

Formatos diversos.

Ejecuta el siguiente programa para aclarar las ideas sobre cómo funciona el operador división (/)
con distintos tipos de variables. En él se puede comprobar la diferencia entre la división entera y de
punto flotante. Guarde el programa como division.c.

# include <stdio.h >


main()
{
printf("división entera: 5/4 es %6d\n", 5/4);
printf("división entera: 6/3 es %6d\n", 6/3);
printf("división entera: 7/4 es %6d\n", 7/4);
printf("división flotante: 7./4. es %6.3f\n", 7./4.);
printf("división mixta: 7./4 es %6.3f\n", 7./4);
getch();
}

Comentario:
Es importante recordar que el tipo de formato debe estar de acuerdo con el tipo del argumento en
la función printf(). Para el formato de salida (%6.3f) se tendrán un total de 6 espacios de
salida, de los cuales 3 serán decimales. En el siguiente programa te presentamos un avance de las
"complicadas" operaciones que puede realizar C. Escribe el programa y almacénalo como carrera.c.
Compila el programa y ejecútalo; apuntando el resultado. Después modifica el programa
sustituyendo 1760.0 por 1760 en la línea que calcula el número de kilómetros. Vuelve a compilar y
a ejecutar. ¿Sale lo mismo que antes? ¿Qué ha podido pasar?

// Un maratón tiene 26 millas y 385 yardas.


// Calcula la distancia de la carrera en kilómetros.
// Una milla tiene 1760 yardas.
#include <stdio.h>
main()
{
int millas, yardas;
float kilometros;
millas = 26;

______________________________________________________________________________________ 39
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
yardas = 385;
kilometros = 1.609 * (millas + yardas / 1760.0);
printf("\nUn marathon tiene %f kilometros.\n\n", kilometros);
getch();
}

Comentario:
En C las constantes que incluyen un punto decimal son de tipo double. La variable yardas es de tipo
int. Si en el denominador se pone sólo 1760, el resultado de yardas/1760 es entero y por tanto
incorrecto. Basta poner 1760.0 para que yardas sea promovido a double y todas las operaciones
aritméticas de esa sentencia se realicen con precision double.

En este ejemplo se muestra la construcción de un programa, de manera que una vez ejecutado nos
pregunta el número de calzado, peso y color favorito. Así, el formato %6.2f: deberá mostrar peso
en 6 espacios, de los cuales 2 serán para los decimales y 1 para el punto decimal.

#include <stdio.h>
void main(void)
{
int calzado;
float peso;
char color[20];
printf("Confiesa tu calzado, peso y color favorito:\n");
printf("\n Calzado: ");
scanf("%d", &calzado);
printf("\n Peso: ");
scanf("%f", &peso);
printf("\nColor favorito: ");
scanf("%s", color);
printf("¡El %s!\n", color);
printf("¿Cómo puede gustarte el %s\n", color);
printf("Calzando un %d y pesando %6.2f Kg.?\n", calzado, peso);
getch();
}

Comentario:
En la función printf() hay que utilizar diferentes formatos de salida para las variables que
deseamos imprimir en pantalla. Así, el formato %6.2f: mostrará peso en 6 espacios, de los cuales
dos serán para los decimales y uno para el punto decimal. Observa cómo se pide el peso, calzado y
color favorito de forma que los dos puntos (:) queden alineados en la pantalla.

¿Cuál es el valor que se espera obtener con el siguiente programa?.

#include <stdio.h>

main ()
{
/* Definición de variables*/
int a, b;
int suma, resta, producto, division, modulo;
int postincremento, preincremento;
int postdecremento, predecremento;
printf("introduzca dos números a y b: ");
______________________________________________________________________________________ 40
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
scanf("%d %d", &a, &b);
suma=a+b;
resta=a-b;
producto=a*b;
division=a/b;
modulo=a%b;
postincremento=a++;
preincremento=++a;
postdecremento=b--;
predecremento=--b;
printf("Suma= %d\n", suma);
printf("Resta= %d\n", resta);
printf("Producto= %d\n", producto);
printf("Division= %d\n", division);
printf("Modulo= %d\n", modulo);
printf("Postincremento de %d= %d\n", a, postincremento);
printf("Preincremento de %d= %d\n", a, preincremento);
printf("Postincremento de %d= %d\n", b, postdecremento);
printf("Predecremento de %d= %d\n", b, predecremento);
getch();

______________________________________________________________________________________ 41
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

2 FUNCIONES ( Diseño Top-Down ó Modular )

Es fácil de pensar que las tareas de diseño de los algoritmos pueden subdividirse en
sub-tareas, las que pueden ser resueltas independientemente. En esta parte discutiremos el método
de diseño modular de algoritmos y los programas que él permite implementar.
Veremos como las funciones pueden ser usadas en un programa C, y como nuevas funciones
pueden ser definidas en el programa. Veremos las facilidades que ofrecen las “macros”, y como
ellas pueden ser implementadas para hacer los programas más fáciles de leer y actualizar.
La mayoría de los lenguajes de programación crean sus propios procedimientos o
funciones, y C no podía ser la excepción. De manera que esta sección mostrará como dividir sus
códigos en funciones, el objetivo radica en que es más fácil cambiar segmentos de programas por
otros, en vez de cambiar el programa completo, más aún, si C facilita la compilación separada, es
decir, permite partir un programa en archivos en donde cada uno sea compilado por separado. La
ventaja es muy natural, pues no requiere de una compilación del programa completo ( para los
impacientes!!).
Las funciones son un grupo de códigos usados en forma de unidades compactas que
pueden ser utilizadas repetidas veces. La función main() es un ejemplo de ellas, la cual es llamada
al comienzo del programa, todas las otras funciones son llamadas directa o indirectamente desde
main().
Las siguientes consideraciones deben ser respetadas para PROTOTIPO DE
FUNCIONES.

NOMBRE: Nombre de la función.


FECHA: Fecha de cuando se realizó.
DESCRIPCION: Descripción de lo que la función realiza.
PARAMETROS: Descripción de cada uno de los parámetros de la función.
RETURNS: Descripción de los valores a retornar , si es que quiere que retorne algún valor.

Ejemplo 8: ( Prototipo de una función en C )

/* Nombre: triangulo
Fecha: 12.12.96
Descripción: computa el Area de un Triángulo
Parámetros: base, altura
Returns: area
*/
float triangulo ( float base, float altura ) /* función prototipo */
{
float area; /* area del triángulo */
area = base * altura / 2.0 ; /* cálculo */
return(area);
}
______________________________________________________________________________________ 42
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Ejemplo 9: (Función dentro de un programa en C)

Consideremos el Ejemplo 7, pero ahora el cómputo del sueldo se hace a través de una función.

/* file: pe97c5.cpp
Nombre del programa: sueldo mediante una función
Fecha: 10.12.96
Propósito: Este programa calcula e imprime el sueldo para un número arbitrario
de persona. Una función separada es usada para calcular el sueldo total.
*/
#include<stdio.h>
#define LIMITE 40.0
#define FACTOR 1.5

main()
{ /* declaración*/
int id_numero;
float horas_trabajadas, costo_por_hora, sueldo_total;
float calculo_sueldo(float horas, float costo); /* prototipo función */

/* Imprimir el título */
printf("***cálculo del sueldo***\n\n");

/* inicializa variables loop, 0 para salir */


printf("\nTipo id_numero, 0 para salir:");
scanf("%d", &id_numero);

while ( id_numero > 0 ) {


/* lee los datos en la variable */

printf("\nHoras trabajadas: ");


scanf("%f", &horas_trabajadas);
printf("\nCosto por hora: ");
scanf("%f", &costo_por_hora);

/* cálculo de sueldos */

sueldo_total=calculo_sueldo(horas_trabajadas, costo_por_hora);

/* imprime datos y resultado */

printf("\nid_numero = %d\n", id_numero);


printf("horas_trabajadas = %f, costo_por_hora = $
%6.2f\n",horas_trabajadas,
costo_por_hora);

printf("sueldo total=$%10.2f\n", sueldo_total);


/* update variable loop */
______________________________________________________________________________________ 43
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
printf("\nTipo id_numero, 0 para salir:");
scanf("%d", &id_numero);
}
return(0);
}

/* archivo pe97c?.cpp continuación */


/* función calcula y retorna sueldo total */

float calculo_sueldo( float horas, float costo)


{
float regular, sobre, sueldo;
printf("\ndebug: entrada a calculo_sueldo(): horas=%f, costo=%f\n", horas,
costo);
if (horas > LIMITE ) {
regular= LIMITE * costo;
sobre= FACTOR * costo * ( horas - LIMITE );
}
else {
regular= horas * costo;
sobre= 0.0;
}
sueldo= regular + sobre;
printf("\ndebug: salida de calculo_sueldo(): %f\n", sueldo);
return(sueldo);
}

Después de ejecutar una sesión con este programa se tiene:

***cálculo del sueldo***

Tipo id_numero, 0 para salir : 123


Horas trabajadas: 20
Costo por hora: 7.5

debug: entrada a calculo_sueldo() : horas = 20.000000, costo = 7.500000


debug: salida de calculo_sueldo(): 150.000000

id_numero=123
horas_trabajadas=20.000000 , costo_por_hora= $7.50
sueldo= $150.00

Tipo id_numero, 0 para salir : 0

Comentarios:
Las sentencias debug muestran claramente los parámetros de entrada a calculo_sueldo() y el
retorno de sueldo. La utilidad de debug es detectar en la función cualquier anomalía

Llamadas por Valor

Cuando se invoca a una función, y se tenga solamente acceso a los valores de sus argumentos, y
NO a los argumentos mismos, se dirá que es una llamada por valor. En C todas las llamadas son

______________________________________________________________________________________ 44
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
por valor, es decir, es imposible que una función llamada tenga acceso directo a un objeto definido
en la función.

Ejemplo 10: ( Llamada por valor )

/* archivo pe97c6.cpp
12.12.96
programa demostrativo de una llamada por valor TRAZA
*/
#include<stdio.h>

main() x
{ int x; ?
int crece(int n);
printf("***llamada por valor***\n");
x=7; 7
printf("el valor original de x es %d\n", x); 7
printf("el valor de crece(x) es %d\n", crece(x)); 8
printf("el valor de x es ahora, ó siempre ha sido!! %d\n", x); 7
return 0;
}
/* función crece */ n
int crece(int n) 7
{
n= n+1; 8
return n; 8
}

La traza muestra que la variable x en main() tiene asignada a priori el valor 7 a la llamada crece(), la
cual incrementa su parámetro a 8 y retorna este valor. Después de la llamada a función, el valor de
x en main() es 7, el cual NO ha cambiado pues, SOLAMENTE EL VALOR de x es pasado a
crece(). Por lo tanto, hemos visto que una llamada a función NO puede directamente cambiar los
valores de un objeto definido en la función llamada.

Comentario: Compilando y ejecutando este programa se tiene:


*** llamada por valor***
el valor original de x es 7
el valor de crece(x) es 8
el valor de x es ahora, ó siempre ha sido!! 7

Amplitud de las Variables

Todas las variables y funciones de C poseen dos importantes atributos, ellos son: Amplitud y
Clase de Almacenamiento.

Amplitud de una variable es el área del programa en donde la variable es válida. Ellas pueden ser
variables globales, las cuales son válidas en todas partes ( de allí su nombre ), de manera que su
alcance es el programa mismo y variables locales las que se limitan al bloque donde ella está
declarada y no puede ser accesada fuera de este bloque, en donde bloque es una sección de
códigos encerrados en paréntesis { }.

______________________________________________________________________________________ 45
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Ejemplo 11: ( Diferencia entre variable local y global. )

int global; /* variable global */


main( )
{
int local; /* variable local */

global = 1; /* global puede ser usado aquí */


local = 2; /* también puede local ser usada aquí */

{ /* comienza un nuevo bloque */


int muy_local; /* esta variable es local al bloque */
muy_local = global + local;
} /* se acaba de cerrar el bloque */
/* muy_local NO puede ser usado */
}

EJEMPLO 12: ( Variables locales con el mismo nombre que las variables globales )

int total; /* número total de entradas */


int contador; /* contador de total de entradas */

main( )
{
total = 0;
contador = 0; /* conjunto global contador */

{ /* comienza un nuevo bloque */


int contador; /* contador local al bloque */
contador = 0;
while (1)
{
if ( contador > 10 )
break;
total + = contador;
contador ++;
} /* se acaba de cerrar el bloque */
/* muy_local NO puede ser usado */
}
contador++;
return(0);
}

Comentario:
Como se vé el campo de la variable contador (primera vez declarada) es válido en todo el
programa. La declaración de la segunda variable local contador toma precedencia sobre la
declaración global al interior del bloque más pequeño , que es donde fué declarada la variable
contador. En este bloque la variable global contador está escondida. Las estructuras de control aquí
utilizadas serán en los próximas páginas aclarados.

Almacenamiento

Existen 4 clases de almacenamientos, ellos son: automática, externa, registro y estática.


______________________________________________________________________________________ 46
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

AUTO: Las variables declaradas dentro del cuerpo de funciones son automáticas por omisión. De
la misma manera las variables definidas dentro de bloques son también automáticas, esto se declara
mediante la palabra reservada AUTO la que por lo general, se omite.

Ejemplo 13:
{ es equivalente a {
char c; auto char c;
int i, j, k; auto int i,j,k;
} }

Dentro de este bloque se consideran las variables locales a él, lo que significa que saliendo del
bloque el sistema deja de reservar memoria para las variables AUTO, por lo que se pierden sus
valores. Si se ingresa nuevamente al bloque se activa otra vez el proceso pero SIN los valores
anteriores. La amplitud de las variables AUTO son LOCALES al bloque, en el cual han sido
definidas.

EXTERN: estás variables constituyen un método para transmitir información entre bloques.
Cuando una variable es declarada fuera de una función se le asigna almacenamiento permanente , y
su clase es EXTERN. Este tipo de variables se consideran globales para todas las funciones
declaradas después de ella. Notar que al salir del bloque ó de la función , la variable externa
permanece.

Ejemplo 14: double x= 1.0, y= 2.0, z; /* variables globales */

main( )
{
double f( );
z= f( );
printf(“%.lf %.lf %lf\n”, x, y, z); /* 3.0 2.0 9.0 */
}
double f ( ) /* la función posee la clase externa de almacenamiento */
{
double y, z;/* y, z son locales, y, z “globales” están escondidas */
x = y = z = 3.0;
return ( x + y + z );
}

Las variables externas permanecen existiendo durante la ejecución del programa, la utilidad es para
transmitir valores entre funciones, ó para informar al cuerpo de la función que se espera una
variable de otro lugar ( incluso puede NO ser visible dentro de este archivo, sino provenir de un
archivo externo).

REGISTER: Esta clase de almacenamiento indica que las variables deberán almacenarse en
registros de memoria de alta velocidad, objetivo principal de esta declaración es intentar mejorar la
velocidad de ejecución. Se recomienda declarar REGISTER las variables que tengan acceso más
frecuente a un ciclo ó funciones.

Ejemplo 15:
______________________________________________________________________________________ 47
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
{
register int i; /* notar que se declaro lo más cerca de su lugar de uso */
for (i = 0; i < LIMITE; ++i )
{
................
}
} /* la salida del bloque dejará libre al registro */

STATIC: Tiene dos usos importantes, uno de ellos es permitir que una variable local retenga su
valor previo antes de entrar de nuevo en el bloque donde reside, esto es lo contrario a las variables
AUTO que perdían su valor al salir del bloque donde residen, requiriendo nueva asignación de
valores iniciales. Sencillamente una variable local STATIC es una variable local que retiene su valor
entre llamadas de funciones.

Ejemplo 16: A veces es útil saber cuantas veces ha sido invocada una función. Para tal efecto y a
manera de ver como trabaja la variable STATIC se define la función cuentas( ).

#include <stdio.h>
include<conio.h>
int cuenta(int i);

main(void )
{
do
{
cuenta(0);
}
while (!kbhit()); /*espera a que se pulse una tecla , la función kbhit()
devuelve verdad si se ha pulsado una tecla del teclado, en cualquier otro caso
devuelve 0*/
printf(“ se ha invocado a cuenta %d veces”, cuenta(1));
return 0;
}
cuenta(int i);
{
static int c=0;

if (i ) return c;
else c++;
return 0;
}

Comentario:
En este ejemplo si se llama a cuenta() con un valor 0 se incrementa la variable c. Si se llama a
cuenta() con cualquier otro valor devuelve el número de veces que ha sido invocada la función. La
utilidad de este programa, aparte de ejemplificar la declaración STATIC, es mostrar el número de
veces que una función ha sido llamada, que puede ser mucha utilidad para el estudio del Análisis y
Complejidad de Algoritmos.

Otro buen ejemplo es para generar series numéricas que produzcan un nuevo número basado en el
último generado, de allí la conveniencia de utilizar STATIC. Un segmento de lo dicho se expresa en
el siguiente segmento de programa
______________________________________________________________________________________ 48
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

series ( void)
{
static int num;

num = num+ 23;


return(num);
}

Comentario:
La variable num sigue existiendo entre las sucesivas llamadas a la función, en lugar de ir y venir
como lo haría una variable local normal, de manera que cada llamada a series() produce un nuevo
miembro de la serie basado en el número precedente. Notar que carece de inicialización, pues la
primera vez que se llama a la función, num tendrá el valor implícito de cero. Por otra parte las
clases de almacenamiento pueden ser ya sea permanentes o temporales.Las variables globales
son siempre permanentes, ellas son creadas e inicializadas antes de que el programa comience y
se quedan hasta que el programa termina.
Las variables temporales están localizadas en una sección de memoria llamada STACK al
comienzo del bloque, por tal motivo si Ud. trata de almacenar muchas variables temporales, le
saldrá un mensaje “Stack overflow”. Cada vez que el bloque es ingresado las variables temporales
son inicializadas.
El tamaño de este Stack depende del Sistema Operativo y del Compilador que se este usando.
En UNIX el programa está localizado automáticamente en el Stack más grande posible.
En Turbo C el espacio STACK es al menos 64,000 bytes.

______________________________________________________________________________________ 49
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

3 RECURSIVIDAD

Recursividad ocurre cuando una función es llamada a si misma directa ó


indirectamente. Este tipo de algoritmos, por lo general simplifican la estructura de muchos
programas. Sin embargo, en algunos lenguajes las llamadas a procedimientos tienen un costo mayor
que el de asignaciones, de ahí que la eliminación de la recursividad es un tema interesante de tratar.
Con esto NO se quiere decir que la recursividad es ineficaz, pues la sencillez estructural del
programa puede justificar la recursividad, sino más bien tenerlo como una consideración a futuro.
La función factorial es uno de los más típicos ejemplos. Veremos a continuación una
serie de ejemplos en los cuales la recursividad queda en evidencia.

Una función recursiva debe tener dos reglas básicas: Debe tener un punto FINAL y
debe SIMPLIFICAR el código del programa.

Ejemplo 17: ( Factorial: fact(0)= 1; fact(n)= n * fact(n-1); )

int fact(int numero)


{
if ( numero = = 0) /* condición de punto FINAL*/
return (1);
/* else*/
return ( numero * fact ( numero - 1)); /* SIMPLIFICA, pues fact
( numero - 1)) es
fact(numero)*/
}
Un programa en C que prueba este algoritmo es:
/* file: ej42.cpp*/
#include<stdio.h>
#define N 12
long int factorial(int n);/* factorial versión recursiva*/
void main(void)
{ /* prueba de la función*/
int n;
for(n=1; n<=N;++n)
printf("\n%ld", factorial(n));
printf("\n\n");
}
long int factorial(int n)
______________________________________________________________________________________ 50
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
{/* la función factorial, versión recursiva*/
if (n<=1)
return(1);
else
return(n * factorial(n-1));
}

Comentarios:
Se tiene en cuenta que una función es recursiva cuando se llama a sí misma, ya sea directa o
indirectamente.
Llamada de la función valor devuelto
factorial(1) 1
factorial(2) 2 * factorial(1)
factorial(3) 3 * factorial(2)
factorial(4) 4 * factorial(3)
En factorial(), la variable n se disminuyó en 1 cada vez, hasta llegar al caso
base con n igual a 1, esto significa que en el cálculo se realizan n-llamadas a la función.

Ejemplo 18: ( Función suma )

/* file: ej41.cpp*/
#include<stdio.h>
#define N 5
int suma(int n);
void main(void)
{ /* prueba de la función*/
int n;
for(n=1; n<=N;++n)
printf("\n%2d", suma(n));
printf("\n\n");
}
int suma(int n)
{
if (n<=1)
return(n);
else
return(n+ suma(n-1));
}

Comentarios:
La función suma( ) se desglosa según lo siguiente:
Llamada de la función valor devuelto
suma(1) 1
suma(2) 2 + suma(1)
suma(3) 3 + suma(2)
suma(4) 4 + suma(3)
suma(5) 5 + suma(4)
En suma( ), la variable n se disminuyó en 1 cada vez, hasta llegar al caso base con n igual a 1.

Ejemplo 19: ( La Sucesión de Fibonacci )


/*file: ej47.cpp */

______________________________________________________________________________________ 51
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
#include<stdio.h>
unsigned long fibo(unsigned long i)
{
if(i<=1L)
return 1L;
else
return(fibo(i-1L)+ fibo(i-2L));
}/* fibonacci como una función RECURSIVA*/

int main (void)


{
unsigned long i;
for(i=0L; i<1000L;i++)
fibo(23L);
printf("%ld\n", fibo(23L));
return(0);
} /* fin de testeador fibonacci RECURSIVO*/

Ejemplo 20: ( Búsqueda Binaria )


Para buscar un valor T dentro de un vector X(1),..., X(n) (ordenados de menor a mayor!!), se
podría aplicar el método de la Búsqueda Binaria (BB) quién es otro ejemplo de la recursividad.
En efecto,
* busca un T en un arreglo ordenado X[min..max] comparando T con el elemento ubicado en la
mitad del intervalo.
* Si el intervalo está vacío debido a que min > max, devuelve NO.
* Si el elemento de la mitad es T, devuelve SI.
* En cualquier otro caso, descarta la mitad del intervalo y se invoca a si mismo recursivamente
sobre un intervalo más pequeño.
* Tan pronto como se completa cualquiera de las invocaciones recursivas busqueda_binaria(min,
k-1) ó busqueda_binaria(k+1, max), el control regresa al invocador de busqueda_binaria(min,
max).

La función main( ) utiliza la función scanf( ) para leer un entero dentro del valor i de T.
Después invoca busqueda_binaria(1, N), la cual devuelve SI ó NO.

/* file ej50.cpp */
#include<stdio.h>
int si=1, no=0;
#define N 7
int X[]={0, 11, 22, 33, 44, 55, 66, 77};
int T;
int busqueda_binaria(int min, int max)
{
int k;
if ( min > max ) return no;
k=(min + max)/2;
if (T==X[k]) return si;
else
if(T<X[k]) return busqueda_binaria(min, k-1);
else
if(T>X[k]) return busqueda_binaria(k+1, max);
}
int main (void)
______________________________________________________________________________________ 52
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
{
scanf("%d", &T);
if (busqueda_binaria(1, N))
printf("encontrado\n");
else
printf("NO encontrado\n");
return 0;
}

4 ITERACION

iteración es un mecanismo para eliminar total ó por lo menos parcialmente la recursividad.


Presentamos a continuación una serie de ejemplos recursivos, pero implementados ahora desde el
punto de vista iterativos.

Ejemplo 22: ( Factorial Iterativo )


/* file: ej43.cpp */
#include<stdio.h>
long factorial(long ulI)

{/* la función factorial, versión iterativa*/

register long ulJ= 1L;

while(ulI > 1L)


ulJ*= ulI--;
return(ulJ);
}/*fin de la función factorial, versión iterativa*/

int main(void)
{
long ulvalor;
for(ulvalor=0L; ulvalor<=65000L;ulvalor++)
factorial(12L);
printf("%ld\n", factorial(12L));
return(0);
}/* fin de la función main()*/

Ejemplo 23: ( Fibonacci Iterativo )


/*file: ej46.cpp*/
#include<stdio.h>
unsigned long fibonacci(unsigned long i)
{
register unsigned long j=1,k=1,l;
unsigned long resultado;
______________________________________________________________________________________ 53
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
if(i<=1L)
return 1L;
for(l=1L;l<i;l++)
{
resultado=j+k;
j=k;
k=resultado;
}
return resultado;
}/* fin función fibonacci ITERATIVO */
int main (void)
{
unsigned long i;
for(i=0L; i<1000L;i++)
fibonacci(20L);
printf("%ld\n", fibonacci(20L));
return(0);
}/* fin de testeador de la función fibonacci ITERATIVO*/

Ejemplo 24: ( Fibonacci iterativo, sin función )


/*file: ej48.cpp*/
#include<stdio.h>
#define LIMITE 10
void main(void)
{ int f0=0, f1=1, n, aux;
/* imprimimos el encabezado y los casos*/
printf("\n%7s%19s%29s", "", "fibonacci", "fibonacci");
printf("\n%7s%19s%29s", "n", "número", "cuociente");
printf("\n%7d%19d", 0, 0 );
printf("\n%7d%19d", 1, 1 );
for (n=2; n < = LIMITE; ++n)
{
aux=f1;
f1+=f0;
f0=aux;
printf("\n%7d%19d%29.16f",n , f1, (double)f1/(double)f0);
}
printf("\n\n");
}/* fin fibonacci ITERATIVO, sin función */

Comentarios:
Los números de Fibonacci tienen muchos usos y propiedades importantes, una de ellas es el
cuociente de Fibonacci, formado por el cuociente F(n) / F(n-1), el cual es posible de demostrar que
converge al número real 1/2(1+sqrt(5)).

Ejemplo 25: ( Búsqueda Binaria no- recursiva )


/*file: ej51.cpp*/
#include<stdio.h>
int si=1, no=0;
#define N 7
int X[]={0, 11, 22, 33, 44, 55, 66, 77};
int T;
int busqueda_binaria(int min, int max)/* NO recursiva*/
{
______________________________________________________________________________________ 54
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
int k;
L: if ( min > max ) return no;
k=(min + max)/2;
if (T==X[k]) return si;
else
if (T<X[k]) max= k-1;
else
if (T>X[k]) min= k+1;
goto L;
}
int main (void)
{
scanf("%d", &T);
if (busqueda_binaria(1, N))
printf("encontrado\n");
else
printf("NO encontrado\n");
return 0;
}

5 COMPLEJIDAD DE ALGORITMOS

Sin embargo, después de todas estas observaciones sobre recursividad v/s no-recursivo, no
podría abandonar está parte sin entregarles al menos una “carta” respecto de la Complejidad de
Algoritmos, que es en definitiva quién nos dirá, según algunos mecanismos, que implementación
escoger.

* Tiempo de Ejecución de un programa:


Se refiere a lo que sucede mientras un programa es ejecutado. Es decir, cuando se resuelve un
problema, con frecuencia hay necesidad de elegir entre varios algoritmos
¿ Cómo se debe elegir ? Hay dos objetivos que por lo general se contradicen. Uno de ellos es

(1) Que el algoritmo sea fácil de entender, codificar y depurar, y el otro es


(2) Que el algoritmo use eficientemente los recursos del computador.

En el caso que se utilice una ó pocas veces conviene escoger según el objetivo (1), sin embargo
frente a un algoritmo que requiere un uso constante, entonces es más ventajoso utilizar el objetivo
(2), de lo que se infiere, que un buen programador no sólo debe preocuparse que el programa se
ejecute con “rapidez”, sino que también debe saber cuándo aplicar algunas técnicas de optimización
ó ignorarlas.

* Medición del tiempo de ejecución de un programa:


El tiempo de ejecución de un programa depende de factores , tales como:
(1) datos de entrada en el programa,
(2) la calidad del código generado por el compilador utilizado para crear el programa objeto,
______________________________________________________________________________________ 55
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
(3) la naturaleza de las instrucciones empleadas en la ejecución del programa,
(4) la complejidad de tiempo del algoritmo,
(5) del computador.

* Unidades de medida:
En general no vamos a utilizar unidades de tiempo dependientes de una máquina específica, sino de
parámetros tales como Nº de comparaciones, Nº de intercambios, Nº de llamadas a un
procedimiento, etc.

Ejemplo: Estimar la complejidad del siguiente algoritmo.

BEGIN
y := 1; p:= 1; {* la inicialización tiene un costo constante de dos
asignaciones *}
WHILE (y < n) DO {* el ciclo es ejecutado n-veces, y cada vez se
ejecutan 3 sentencias. La comparación y las dos asignaciones en el cuerpo del
WHILE-DO *}

BEGIN
p:= p * i
y:= y + 1
END
END
{* el tiempo de complejidad f(n) de este algoritmo sobre una entrada n es f(n)=3n + 2. *}
Ejemplo.

Cuántas veces se ejecuta

for (int i= 0; i < N; i++) {


for (int j= 0; j < N; j++) {
algo simple
}
}

tendremos N * N * O(1) = O(n2) .

for (int i= 0; i < N; i++) {


for (int j= 0; j < i; j++) {
algo_de_O(1)
}
}

el bucle exterior se realiza N veces, mientras que el interior se realiza 1, 2, 3, ... N veces
respectivamente. En total 1 + 2 + 3 + ... + N = N*(1+N)/2 , es decir O(n 2). A veces aparecen bucles
multiplicativos, donde la evolución de la variable de control no es lineal (como en los casos
anteriores)

c= 1;
while (c < N) {
algo_simple
c= 2*c;
______________________________________________________________________________________ 56
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
}

El valor incial de "c" es 1, siendo "2 k" al cabo de "k" iteraciones. El número de iteraciones es tal
que 2k ≥ N => k= eis (log2 (N)) [el entero inmediato superior] y, por tanto, la complejidad del bucle
es O(log n).

c= N;
while (c > 1) {
algo_de_O(1)
c= c / 2;
}

Un razonamiento análogo nos lleva a log2(N) iteraciones y, por tanto, a un orden O(log n) de
complejidad.

for (int i= 0; i < N; i++) {


c= i;
while (c > 0) {
algo_de_O(1)
c= c/2;
}
}

tenemos un bucle interno de orden O(log n) que se ejecuta N veces, luego el conjunto es de orden
O(n log n).

Los métodos y elementos para resolver recurrencias, analizar y optimizar algoritmos se verán en el
curso regular de DISEÑO Y ANALISIS DE ALGORITMOS.

Como ejemplos prácticos en C, se tienen los siguientes:


/* frecuencia pasos */
float rsuma(float lista[ ), int n) /* 0 0 */
/* función recursiva que suma los elementos de una lista,
esto significa para el compilador que el tiene los parametros, variables locales y dirección de
retorno
aseguradas*/
{
if (n) /* n + 1 n + 1 */
return rsuma(lista, n-1) + lista[n - 1); /* n n */
return 0; /* 1 1 */
}/* en total se tienen 2n + 2 */

A pesar que se tiene el conocimiento de una potente herramienta teórica para estimar el tiempo de
complejidad, no es demasiado temprano para preguntarnos si existe un método más práctico para
medir exactamente el costo de nuestro algoritmo en nuestro computador. La respuesta es SI
existen, y los elementos para llevarlos a cabo están en la librería TIME.H

Ejemplo 26: (Fibonacci recursivo v/s iterativo, quién gana ?? )

______________________________________________________________________________________ 57
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
/* begin fibonacci recursivo */
/*file: fibo.cpp*/
#include<stdio.h>
#include<time.h>
unsigned long fibo(unsigned long i)
{
if(i<=1L)
return 1L;
else
return(fibo(i-1L)+ fibo(i-2L));
}
int main (void)
{
clock_t end;
unsigned long i;
printf("\n\n\n este programa mide el tiempo..\n\n");
for(i=0L; i<1000L;i++)
fibo(20L);
printf("%ld\n", fibo(20L));
end=clock();
printf("%lf segundos\n", end / CLK_TCK);
return(0);
}
/*fin fibonacci recursivo */

/* begin fibonacci iterativo */


/*file: fibo1.cpp*/
#include<stdio.h>
#include<time.h>
unsigned long fibo(unsigned long i)
{
register unsigned long j=1,k=1,l;
unsigned long resultado;
if(i<=1L)
return 1L;
for(l=1L;l<i;l++)
{
resultado=j+k;
j=k;
k=resultado;
}
return resultado;
}
int main (void)
{
clock_t end;
unsigned long i;
printf("\n\n\n este programa mide el tiempo..\n\n");
for(i=0L; i<1000L;i++)
fibo(20L);
printf("%ld\n", fibo(20L));
end=clock();
printf("%lf segundos\n", end / CLK_TCK);
return(0);
}
/*fin fibonacci iterativo */
______________________________________________________________________________________ 58
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Comentario: Ejecute ambos programas por separado (por supuesto) y compare los resultado que
obtiene, los cuales dependerán evidentemente del computador que está utilizando.

Ejemplo 27: (Costo de un ciclo )


/*file: ej34.cpp*/
#include<time.h>
#include<stdio.h>
/* este programa mide el tiempo que le cuesta al ciclo for ir desde 0 a 500.000
*/
main(void)
{
clock_t start, stop; /* clock se encuentra en time.h*/
unsigned long t;

start = clock();
for ( t=0; t< 500000L; t++);
stop= clock();
printf(" el ciclo for requiere %f segundos\n", (stop - start) / CLK_TCK);
return 0;
}

Comentario:
La función clock() devuelve un valor aproximado del tiempo que se ha estado ejecutando el
programa de llamada a clock(). Devuelve el valor -1 si el tiempo no está disponible. Para
transformar este valor a segundos, basta dividirlo por la constante CLK_TCK*/

6 PREPROCESADORES en C

Para ampliar la capacidad de notación, el lenguaje C utiliza preprocesadores, que en concreto se


visualizan a través de los simbolos #, los más conocidos son #include, #define.
Por ejemplo una línea de control como #include <stdio.h>, significa que llama al preprocesador a
tomar el archivo stdio.h e insertarlo en el programa ( los paréntesis < > denota que son
estandares ), diferentes de aquellos que se declaran por ejemplo /* util.h*/ y en el encabezado del
programa se ve #include “util.h”. La expresión “util.h” puede ser un archivo cualquiera; o un
camino relativo “.../../util.h”; o un camino absoluto “/root/include/util.h”( en DOS se usa (\) en vez
de (/) como separador de directorios ).
Por otra parte #define se les conoce como MACROS.

Ejemplo 28: ( Declaración de constantes a través de macros )

Las siguientes son definiciones de macros.


#define PI 3.14159
#define TAM 1000
#define RCUAD radio* radio
#define AREA PI * RCUAD
Este tipo de macros se utilizan de la siguiente manera:
______________________________________________________________________________________ 59
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Codigo Original Código expandido después de los preprocesadotes

circun=2 * PI * radio; circun = 2 * 3.14159 * radio;


y = x + TAM; y = x + 1000;
printf(“ Tamaño= “, TAM ); printf (“ Tamaño= “, 1000);
AREA; 3.14159 * radio * radio;

Como Ud. puede ver el preprocesador reemplaza el nombre de la macro por el string especificado.
La amplitud de la definición de la macro es el código fuente después de la línea en donde se declaro
la #define. La definición puede ser removida utilizando #undef.

#define TAM 40 /* TAM es definido como un string de 40 caracteres */


........
#undef TAM /* Tamaño no está definido */
#define TAM 100 /* Tamaño está definido ahora en 100 */

Ejemplo 29: ( Macros con argumentos )

Es posible utilizar macros con argumentos para reemplazar la llamada a las funciones, por ejemplo
en vez de escribir la función cuadrado de un número bastará con definir la macro:

#define CUADRA(x) ( x * x ), haciendo uso de ella mediante y= CUADRA( radio );


printf (“ el cuadrado de %d es %d\n”, CUADRA(radio));

Sin embargo, deben recordar que las llamadas a macros son SUSTITUCIONES y los parámetros
de las macros NO son nunca evaluados o chequeados por algún tipo de dato consistente.

Un programa en C que utiliza una macro de este tipo es:

#include<stdio.h>
#define SQR(x) (x * x )/* después de ejecutarlo, diga que es lo que obtiene*/
main ( )
{
int contador; /* contador para el loop */
for ( contador = 0; contador < 5; contador ++ )
{
(void) printf (“x %d, x cuadrado %d\n”, contador + 1, SQR(contador + 1 ) );
}
return(0);
} /* Este programa trae un error, cómo lo mejora??*/

Comentario:
El problema surge porque SQR( contador + 1) es ( contador + 1 * contador + 1 ), luego la
solución al problema radica en cambiar la definición de #define SQR....por
#define SQR(x) ( (x) * (x) ).

Ejemplo 30: ( Cálculo de funciones a través de macros )

______________________________________________________________________________________ 60
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Son frecuentemente utilizadas las macros para sustituir a llamadas de funciones por codificación
en línea, lo cual es más eficiente, por ejemplo
#define MAX(x,y) ( ( (x) > (y) ) ? (x) : (y) ) que utiliza el operador ?

/***********************************************
* Utiliza macros para el cálculo de algunas funciones
* Propósito: mostrar macros, y la recursividad de las macros*/
#include<stdio.h>
#include<stdio.h>
#define CUADRADO(x) x*x
#define CUBO(x) CUADRADO(x)*x
#define ABS(x) ((x >=0) ? x : 0 - x )
#define PRODUCTO(x,y) x*y
#define IMPRIME(x) printf("\n"#x)

void main(void)
{
float z= -7., v=6.;

printf("\nEl cuadrado de %f es %f",z, CUADRADO(z));


printf("\nEl cubo de %f es %f",z, CUBO(z));
printf("\nEl valor absoluto de %f es %f",z, ABS(z));
printf("\nEl producto de %f por %f es %f", z, v, PRODUCTO(z,v));
IMPRIME(FIN DE LA MUESTRA MACROS !!);
}

Comentario:
Se definen 5 MACROS que son llamadas y ejecutadas desde main( ), la primera de ellas es el
cuadrado de un número, cuyo tipo NO se define en la macro, sino dentro del programa, la segunda
es una extensión de CUADRADO, la tercera utiliza la sentencia reducida de
IF-ELSE, y la última que imprime una cadena sin entrecomillarla.

Ejemplo 31: ( Intercambiar utilizando una macro )


/*file: ej36.cpp*/
#include<stdio.h>
#include<math.h>
#define MAX_SIZE 101
#define SWAP(x, y, t ) ((t)=(x), (x)=(y), (y)= (t))
void sort(int [], int);/* ordenar por selección*/
void main(void)
{
int i,n;
int list[MAX_SIZE];
printf("deme la entrada:");
scanf("%d",&n);
if (n<1 || n> MAX_SIZE) {
fprintf(stderr, "valor de n NO-válido\n");
}
for (i=0; i < n; i++) {
printf("%d ", list[i]);
}
sort(list,n);
printf("\n cuerpo de ordenamiento:\n");
______________________________________________________________________________________ 61
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
for (i=0; i < n; i++)/* imprime la lista ordenada*/
printf("%d ", list[i]);
printf("\n");
}
void sort(int list[], int n)
{
/*ordenar por seleccion*/
int i, j, min, temp;
for (i = 0; i < n - 1; i++) {
min = i;
for ( j=i+1; j < n; j++)
if ( list[j] < list[min])
min= j;
SWAP(list[i], list[min], temp);
}
}
Comentario: Las distintas funciones ó elementos desconocidos hasta el momento serán abordados
prontamente.

Ejemplo 32: ( Funciones que contienen macros)

/* pe97c7.cpp
nombre: dias calurosos
fecha : 12.12.96
propósito: contar el número de días calurosos de un conjunto de temperaturas
*/
#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define FRIO 80
#define CALOR 90
#define DCALOR(t) ((t) > CALOR )
#define DFRIO(t) ((t) < FRIO )
#define XDIAS(n,b) (((n)+(b))> 0)
int dias_bueno(int temp);
void impri_resul(int bueno, int nublados, int temp_sum);

main()
{ /* declaracion */
int temperatura, /* temperatura diaria */
total=0, /* inicializador del acumulador */
num_dias_bueno=0,
num_dias_nublados=0;

/* imprime titulo y prompt */


printf("***contando dias buenos***\n\n");
printf(" ingrese la temperatura del dia (0 para salir):");

/* lee la primera temperatura */


scanf("%d", &temperatura);
while ( temperatura !=0) {

/* procesar una temperatura */


if (dias_bueno(temperatura))
num_dias_bueno= num_dias_bueno + 1;
______________________________________________________________________________________ 62
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
else
num_dias_nublados=num_dias_nublados + 1;

/* acumula el número total de temperaturas */


total= total+ temperatura;

/* lee la siguiente temperatura */


scanf("%d", &temperatura);
}
impri_resul(num_dias_bueno, num_dias_nublados, total);
return 0;
}

/* función para testear los dias buenos, dadas las temperaturas */

int dias_bueno(int temp)


{
if (DFRIO(temp)) return FALSE;

if (DCALOR(temp)) return FALSE;

return TRUE;
}

/* función para imprimir, dado el número de dias_calor, dias_nubla y total


de temperaturas */

void impri_resul(int dias_bueno, int dias_nublados, int total)


{
float temp_promed;
printf("Existieron %d dias buenos y %d dias nublados\n",
dias_bueno, dias_nublados);

if XDIAS(dias_bueno, dias_nublados) {
temp_promed= (float) total/ (float)(dias_bueno + dias_nublados);
printf("El promedio de temperaturas para %d dias fué de %f\n",
dias_bueno + dias_nublados, temp_promed);
}
}
Comentario:
Ejecute el programa para una cantidad de datos.

Ejemplo 33: ( Archivo Estandar y EOF)

Una macro interesante que se encuentra en stdio.h es llamada EOF (End Of File)
De manera que es posible ahora escribir un programa que contenga un loop que TERMINE
cuando el FIN del archivo de entrada haya sido alcanzado.

#include<stdio.h>
......
semaforo=scanf( );
while ( semaforo != EOF ) {
........
______________________________________________________________________________________ 63
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
semaforo = scanf( );
}

Comentario:
El valor devuelto por scanf( ) es almacenado en la variable semaforo. El loop se repite hasta que
semaforo recibe (la luz verde ) EOF. El código anterior es portable a cualquiera implementación
ya que el valor está definido en stdio.h en todo implementación. Podemos entonces ahora escribir
un programa que use FIN de archivo para terminar la lectura de datos.

/* pe97c9.cpp
nombre: archivo EOF
fecha : 12.12.96
propósito: leer datos hasta fin de archivo
*/

#include <stdio.h>
int abs(int n);

main()
{ /* declaracion */
int largo=0, n, semaforo;
/* imprime titulo y prompt */
printf("***entero absoluto más grande***\n\n");
printf(" ingrese el entero, EOF para salir: "
"^Z para DOS, ^D para UNIX\n");
semaforo=scanf("%d", &n);

while ( semaforo!=EOF) {

if (abs(n)> largo)
largo= abs(n);
semaforo=scanf("%d", &n);
}
printf("El valor absoluto más grande es =%d\n",largo);
return 0;
}
/* función que retorna el valor absoluto de n */
int abs(int n)
{
if (n < 0 )
return -n;
else
return n;

}
Comentario:
La salida se ve, así

*** entero absoluto más grande***

ingrese el entero, EOF para salir: ^Z para DOS, ^D para UNIX


-20
0
30
______________________________________________________________________________________ 64
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
-60
^Z
El valor absoluto más grande es = 60

7 PROCESANDO TIPOS DE DATOS CHAR

Hasta el momento hemos considerado solamente tipos de dato numéricos, sin embargo en
programación no todos los datos tienen esta cualidad, pues la programación de una password
encriptada podría ser una buena aplicación de los tipo de elementos char ó determinar el número de
comas ó dígitos ó símbolos extraños que un texto posee, lo que en Teoría de Autómatas y
Lenguajes Formales se conoce como un “reconocedor de palabras” , tema que se abordará en esa
asignatura. Por tal motivo veremos una serie de ejemplos que nos darán los rudimentos para
abordar este tipo de problemas.
______________________________________________________________________________________ 65
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Ejemplo 39: (Procesador de textos)

/* pe97c11.cpp
nombre: char: ch
fecha : 12.12.96
propósito: leer un stream de caracteres, un caracter a la vez, y enviarla a la
salida hasta EOF
*/

#include <stdio.h>

main()
{ char ch; /* declaración para un objeto ch */
int bandera; /* bandera almacena el número de items leídos por
scanf()*/

/* imprime titulo */
printf("***Copia del Programa***\n\n");
printf("tipo de texto, termina con EOF\n");

bandera=scanf("%c", &ch);/* lee el primer caracter */


while (bandera !=EOF) { /* repite mientras no sea EOF */
printf("%c", ch); /* imprime el último caracter leído */
bandera=scanf("%c", &ch); /* lee el siguiente caracter y lo
adiciona a bandera*/
} /* bandera es EOF, ch no puede ser cambiado */
return 0;
}

Comentario:
La conversión específicada para formatear datos de tipo character es %c.
Los datos de tipo caracter están representados en el computador usando un código numérico
estandarizado, llamado código ASCII, el cual posee 128 caracteres con valores del 0 al 127.
Entonces 7 bits son suficientes para representar los caracteres en ASCII, aunque algunos
computadores reservan 1byte (8bits) para los caracteres ASCII.

Ejemplo 40 (código ASCII)

A propósito de la tabla de códigos ASCII, debemos hacer notar que ‘A’ tiene el código 65, el ‘B’
tiene el valor 66 y así sucesivamente. Es importante mencionar el hecho que los valores ASCII de
las letras ‘A’ hasta la ‘Z’ son contiguos en forma creciente desde el 65 hasta el 90, las minúsculas
en cambio comienzan del valor 97, con la misma particularidad que las mayúsculas. Similarmente
los dígitos del ‘0’ al ‘9’ , los cuales parten del valor 48. Cabe distinguir que los símbolos dígitos
son de tipo character, es decir NO confundir ‘0’ que tiene el valor 48 con el 0 como valor.

/* pe97c12.cpp
nombre: ascii
fecha : 12.12.96
propósito: leer una lista de caracteres hasta el fin del archivo, imprimiendo
los atributos reconocedores del código ASCII, entre otros.
*/

______________________________________________________________________________________ 66
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
#include <stdio.h>
void imp_reps(char ch);

main()
{ char ch; /* declaración para un objeto ch */
int bandera; /* bandera almacena el número de items leídos por
scanf()*/

/* imprime titulo */
printf("***Atributos***\n\n");
printf("tipo texto, termina con EOF\n");
bandera=scanf("%c", &ch);/* lee el primer caracter */

while (bandera !=EOF) { /* repite mientras no sea EOF */


if (ch >= 'a' && ch <= 'z') {/* caso letras minúsculas */
imp_reps(ch);
printf("letra minúscula\n");
}
else if (ch >='A' && ch <= 'Z') { /* caso letra mayúscula */
imp_reps(ch);
printf("letra mayúscula\n");
}
else if (ch >= '0' && ch <= '9') {/* caso dígitos */
imp_reps(ch);
printf("un digito\n");
}
else if( ch=='.'||ch ==','||ch==';'||ch==':'||ch=='?'||ch=='!'){
imp_reps(ch);
printf("un símbolo de puntuación\n");
}
else if (ch == ' ') {/* caso espacio */
imp_reps(ch);
printf("un caracter espacio\n");

else if (ch < 32 || ch ==127) { /* control de caracter */


imp_reps(ch);
printf("un caracter control\n");
}
else { /* caso letra mayúscula */
imp_reps(ch);
printf("un símbolo especial\n");
}
bandera=scanf("%c", &ch); /* lee el siguiente caracter */
}/* termina el while */
return 0;
}/* termina el programa*/
void imp_reps(char ch)
{
printf("%c, ASCII valor decimal %d,octal %o,hexagésimal %x:",ch, ch, ch, ch);
}

Ejemplo 41 ( usando las funciones getchar( ) y putchar( ) )

getchar( ) y putchar( ) son rutinas de la librería stdio.h, las cuales son más generales que las
librerías estandares de entrada y salida. La función getchar( ) lee un caracter simple desde la
______________________________________________________________________________________ 67
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
entrada estandar y retorna el valor del caracter como el valor de la función, el tipo del valor
retornado es int, aunque para evitar confusiones se considera signed char para almacenar los
valores. La rutina similar para salida de caracteres es putchar( ), la cual devuelve sus argumentos
como caracteres a la salida estandar. Es decir, devuelve los caracteres ASCII cuyos valores están
en c a la salida estandar, El argumento para putchar se espera que sea int, sin embargo la variable c
puede ser char (que corresponde a los valores enteros de ASCII). A continuación se escribe un
programa que usa esta nueva manera de entrada/salida en vez de las estandares scanf( ) y
printf( ).
Otra diferencia es que scanf( ) almacena los datos en un objeto cuya dirección está dada por sus
argumentos, mientras getchar( ) retorna el valor del caracter leído como su valor.

/* pe97c13.cpp
nombre: getchar() y putchar()
fecha : 12.12.96
propósito: copiar entrada estandar en salida estandar
*/
#include <stdio.h>
main()
{ signed char c; /* declaración para un objeto ch */
/* imprime titulo */
printf("***copia archivo***\n\n");
printf("tipo texto, se sale con EOF\n");
c=getchar();
while (c !=EOF) {
putchar(c);
c=getchar();
}
return 0;
}

Observación: En C no existe tipo dato primitivo para string; sin embargo constantes string
(también llamadas string literales) pueden ser escritas directamente en un programa usando
comillas. Por ejemplo string nulo es “ “, otra constante es “Juan Perez”. Entonces NOTAR la
diferencia entre ‘c’ que es una constante char y “c” que es una constante string de un caracter y el
caracter NULL, es decir, “c” se representa por ‘c’ \0’, “string” se representa por ‘s’ ‘t’ ‘r’ ‘i’ ‘n’ ‘g’
\0’, mientras que ‘c’ se representa por ‘c’

Ejemplo 42: ( Utilizando funciones para procesar textos )

Lo que resulta bastante usual en programación es confeccionar macros que realicen tareas como
pasar un texto de mayúscula a minúscula, reconocer palabras ó dígitos, etc. dada una cadena de
caracteres. Aquí se presentarán dos programas, que deberían darle la inspiración necesaria para
implementar otras tareas en este tema.

/* pe97c14.cpp
nombre: mayuscula()
fecha : 12.12.96
propósito: copiar archivo de entrada a la salida estandar, después de convertir
minúsculas a mayúsculas
*/
#include <stdio.h>

______________________________________________________________________________________ 68
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
#define es_min(c) ((c) > = 'a' && (c) < = 'z')
#define va_may(c) ((c) - 'a' + 'A')
char mayuscula(char ch);

main()
{ signed char ch; /* declaración para un objeto ch */
/* imprime titulo */
printf("***copia programa- caso mayúscula***\n\n");
printf("tipo texto, se sale con EOF\n");

while ((ch = getchar())!= EOF) {


putchar(mayuscula(ch));/* imprime valor de mayuscula(ch) */
}
return 0;
}
char mayuscula(char c) /* prototipo de la función*/
{
if ( es_min(c))
return va_may(c);/* convierte a mayuscula */
/* de otra manera */
return c;
}

Comentario:
En la expresión while ((ch = getchar())!= EOF) se ha combinado la operación de leer un caracter
y preguntar por EOF. En los paréntesis internos son evaluados primero, getchar() leyendo un
caracter y asignando el valor retornado a ch. El valor asignado a ch está entonces comparándolo
con EOF. Si no es EOF el loop se ejecuta, de otra manera el loop termina. La precedencia es
importante, de allí los paréntesis.

/* pe97c15.cpp
nombre: getint()
fecha : 12.12.96
propósito: convertir una sucesión de caracteres dígitos a enteros
*/
#include <stdio.h>
#define DEBUG
#define es_min(c) ((c) >= 'a' && (c) <= 'z')
#define es_dig(c) ((c) >= '0' && (c) <= '9')
#define ERROR -1

char ent_dig(int n);


int dig_ent(char ch);
int getint();

main()
{
printf("***secuencia de dígitos a enteros***\n\n");
printf("Tipee una secuencia de dígitos\n");
printf("Entero=%d\n", getint());
return 0;
}
int getint()
{ int n=0;

______________________________________________________________________________________ 69
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
signed char ch;
ch=getchar();
while (es_dig(ch)){
n= n*10 + dig_ent(ch); /* approach modular */

#ifdef DEBUG /* esta macro es llamada compilación condicional para testear la


implementación*/
printf("debug:getint: ch=%c\n", ch);
printf("debug:getint: n= %d\n", n);
#endif
ch=getchar();
}
return n;
}

int dig_ent(char ch) /* prototipo de la función*/


{ /* ingresa un caracter y retorna un entero, el valor ch -’0’ si ch es un
caracter dígito, sino lanza un mensaje de ERROR */
if ( es_dig(ch))
return ch - '0';
printf("ERROR:dig_ent: %c no es un dígito\n", ch);
return ERROR;
}

char ent_dig(int n) /* prototipo de la función*/


{
if (n>=0 && n<10)
return n + '0';
printf("ERROR:ent_dig: %d no está en el rango de 0 a 9\n", n);
return NULL;
}

______________________________________________________________________________________ 70
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

8 PUNTEROS
La mayoría de los programas que debemos implementar se sustentan en estructuras de
datos de longitud desconocida. Un ejemplo simple podría ser el de un programa que lee las líneas
de un archivo y las ordena. Por tanto, deberemos leer un número indeterminado de líneas, y tras
leer la última, ordenarlas. Una manera de manejar ese ``número indeterminado'', sería declarar una
constante, por ejemplo, MAX_LINEAS, darle un valor “grande” y, declarar un array de tamaño
MAX_LINEAS. Como se puede ver nuestro programa no sólo quedaría limitado por ese valor
máximo, sino que además gastaría esa enorme cantidad de memoria.
La propuesta para salvar esta situación es el de utilizar memoria dinámica. La memoria
dinámica es un espacio de almacenamiento que se solicita en tiempo de ejecución. De esa manera, a
medida que el proceso va necesitando espacio para más líneas, va solicitando más memoria al
sistema operativo para guardarlas. El medio para manejar la memoria que otorga el sistema
operativo, es el puntero, puesto que no podemos saber en tiempo de compilación dónde nos dará
espacios el sistema operativo. Hay un aspecto de las funciones en C que tienen singular importancia
para introducir el concepto de puntero. Por ejemplo,

#include<stdio.h>
#define PI 3.141596

float area(float);
main( )
{
float radio, superficie;
printf(“introduzca el radio\n”);
scanf(“%f”, radio);
superficie= area(radio);
printf(“\nRadio %f Superficie %f”, radio, superficie );
return0;
}
float area ( float rad)
{
float super;
super= PI * rad *rad;
return(super);
}

Comentario:
En este programa y en otros, los datos que pasamos a la función son copias de los datos, NO los
datos mismos, en tal caso, estamos frente a una llamada por valor.
En el ejemplo anterior, se copian 4 bytes de datos almacenados en la memoria reservada para el
nombre radio, a la zona de memoria llamada rad, que también posee 4 bytes. Las operaciones en
la función area( ) se realizan sobre esta última zona de memoria, no sobre radio, y el valor
resultante se almacena en super temporalmente, antes de copiarse en superficie. Las variables de
area( ) son locales de tipo automatic y quedan liberadas (su espacio es devuelto a la memoria
interna de la computadora) una vez que se ejecuta la función.
Para nuestros efectos, la variable radio de la función main( ) sigue invariable en tanto no tome un
nuevo valor en una nueva llamada a area( ), y el espacio de memoria y valores asociados a las
variables pertenecientes a area( ) desaparecen.
______________________________________________________________________________________ 71
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Otra transmisión de datos se refiere a llamada por referencia, en la cual NO se envía una copia de
los datos sino la dirección que estos ocupan en la memoria, de allí la posibilidad de utilizar
punteros.
Un puntero es la dirección de memoria de una variable. Las utilidades principales son:
* Poder proporcionar una rápida forma de referenciar los elementos de un array.
* Permitir a las funciones de C modificar los parámetros de llamada.

Operadores &y*

El operador & es un operador “monario” que entrega la dirección de memoria de un objeto. Por
ejemplo, m=&contador; /* asigna la dirección de contador a la variable m*/
& se aplica solamente a objetos que están en memoria
El operador *, el cual es también “monario”, devuelve el valor de la variable ubicada en la dirección
que se especifíca, por ejemplo q=*m; se refiere a “q recibe el valor de la dirección m”.
/* Deposita el valor 10 en la variable recibe */

main (void)
{
int recibe, fuente;
int *m;

fuente = 10;
m = &fuente; /* & es usada para obtener la dirección del objeto fuente */
recibe = *m; /* desreferencia a m, ó contenido del operador * */
return 0;
}

Ejemplo: ( dato v/s dirección )

#include<stdio.h>

main( )
{
int x;
int *iptr;

printf(“***Testeando variables puntero***\n”);


x=10;
iptr= &x; /* asigna la dirección de x a la variable iptr */
printf(“ dirección %d contiene el valor %d\n”, iptr, *iptr);
}

Comentario:
Este programa produce la salida

***Testeando variables puntero***


dirección 1000 contiene el valor 10

Sin embargo, observar que NO se está interesado en el valor de la variable puntero en sí, puede
incluso ser diferente cada vez que el programa se corra, lo realmente importante es saber el
contenido de la celda a la cual el puntero apunta. (Vea GRAF1)
______________________________________________________________________________________ 72
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Ejemplo (Ilustrar situaciones que se dan con punteros)

Consideremos el siguiente ejemplo utilizando la siguiente declaración:


int x, z;
float y;
char ch, *pch;
int *pi, *pi2
float *pf;

Cuando estas declaraciones son encontradas, las celdas de memoria para estas variables se ven así.
(Vea GRAF2)

También es posible inicializar variables de tipo puntero.


x=100;
y= 20.0;
z=50;
pi= &x;/* pi apunta a x */
pi2= &z; /* pi2 apunta a z */
pch= &ch; /* pch apunta a ch */
(Vea GRAF3)

Se muestran a continuación algunos ejemplos de declaraciones y como los datos y referencias


cambian.
1) pi2=pi; /* pi2 apunta a donde pi apunta, es decir pi2 apunta a x (Vea GRAF4)
2) pi=&z; /* pi apunta ahora a z, pi2 permanece apuntando a x, es decir pi apunta a z, pi2
apunta a x*/ (Vea GRAF5)
3) *pi=*pi2; /* z=x, es decir z=100 */ (Vea GRAF6)
4) *pi= 200; /* z=200, x no es alterado*/ (Vea GRAF7)
5) *pi2= *pi2 + 200; /* x=300, z no es alterado*/ (Vea GRAF8)

Pasando Punteros a Funciones

Como se ha discutido en C los argumentos son pasados a la función por valor. Sin embargo, para
implementar la llamada por referencia que Pascal posee, se utilizan los punteros ( en realidad, NO
solamente para esto). A propósito las funciones en C pueden retornar valores simples como valor
de la función, sin embargo a través del acceso indirecto la llamada a una función puede retornar
varios valores. Solamente un valor es actualmente retornado como el valor de la función, todos los
otros valores pueden ser indirectamente almacenados en objetos en la función llamada. Este uso de
las variables punteros es el más común en C.

Ejemplo ( Indirectamente incrementando una variable )

/* pe97c16.cpp
nombre: incremento
fecha : 19.12.96
propósito: ilustrar el acceso indirecto de x por una función indire_incre(),
función incrementa x en 1
______________________________________________________________________________________ 73
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
*/
#include <stdio.h>
void indire_incre(int *p);

main()
{
int x;
printf("***acceso indirecto***\n\n");
x=7;
printf("el valor original de x es %d\n", x);
indire_incre(&x);
printf("el valor de x es ahora %d\n", x);
return 0;
}
void indire_incre(int *p)
{
*p=*p + 1;
} /*Vea GRAF 9*/

/* pe97c17.cpp
nombre: retornar por acceso indirecto
fecha : 19.12.96
propósito: usar una función que retorne el cuadrado de un argumento
e indirectamente almacena el cubo
*/
#include <stdio.h>
double scubo(double x, double *pcubo);

main()
{
double x, square, cubo;
printf("***directa e indirectamente retorna valores***\n\n");
x=3;
square=scubo(x, &cubo);
printf("x=%f, square=%f, cubo=%f\n", x, square, cubo);
return 0;
}
double scubo(double x, double *pcubo)
{
*pcubo=x * x * x;
return(x * x);
} /*Vea GRAF 10*/

/* pe97c18.cpp
nombre: intercambio
fecha : 19.12.96
propósito: usar una función que intercambia valores a través de punteros
*/
#include <stdio.h>
void swap(int *p1, int *p2);

main()
{ int dat1=100,dat2=200;
printf("antes de la llamada a swap dat1=%d, dat2= %d\n", dat1,dat2);
swap(&dat1, &dat2);
printf("después de la llamada a swap dat1=%d,dat2=%d\n", dat1, dat2);
______________________________________________________________________________________ 74
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
return 0;
}
void swap(int *ptr1,int *ptr2)
{ int temp
temp=*ptr1;
*ptr1=*ptr2;
*ptr2=temp;
} /* Vea GRAF 11*/

______________________________________________________________________________________ 75
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

9 ARRAY

Un array al igual que en Pascal es un conjunto de localización de memoria consecutiva usada para
almacenar datos, cada item en array es llamado elemento y el número de elementos en un array es
llamado dimensión del array.

Arreglo Unidimensional:
Todos los array tienen el 0 como índice de su primer elemento, luego por ejemplo, int p[10] ;
declara un arreglo de enteros que tiene diez elementos, desde p[0] hasta p[9].
/*Cuenta el número de elementos no-cero, parando cuando encuentra el cero*/

#include<stdio.h>

int array[10]={4,5,8,9,8,1,0,1,9,3};
int indice;

main(void)
{
indice=0;

while (array[indice]!=0)
indice++;
(void)printf("El número de elementos antes de leer el cero fueron %d\n",
indice);
return(0);
}

/*Cuenta el número de elementos no-cero, parando cuando encuentra el cero, pero versión
punteros*/

#include<stdio.h>

int array[10]={4,5,8,9,8,1,0,1,9,3};
int *arreglo_ptr;

main(void)
{
arreglo_ptr=array;

while ((*arreglo_ptr)!=0)
arreglo_ptr++;
(void)printf("El número de elementos antes de leer el cero fueron %d\n",
arreglo_ptr - array);
return(0);
}

Arreglo de Caracteres:
Strings son array de caracteres. El caracter especial ´\0´ (cero ) es usado para indicar el fin del
string. Por ejemplo, este segmento crea un array de longitud cuatro
______________________________________________________________________________________ 76
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

char nombre[4] ;
main()
{
name[0] = ´S ;
name[1] = ´O´;
name[2] = ´S;
name[3] = ´\0´;
return(0);
}
En todo programa la línea #include<string.h> es necesaria para informar a C que esta usando la
función libreria string. La función (void) indica que no está usando el valor de retorno de
strcpy( ). Otras funciones relacionadas con el manejo de cadenas son: strcat( ), strlen( ),
strcmp( ).
/* Ilustrar el uso de las funciones strcpy(), strcat(), strlen() y strcmp()*/

#include<stdio.h>
#include<string.h>

main(void)
{
char cadena1[80], cadena2[80];

gets(cadena1); gets(cadena2);/*asegurárse de que las cadenas sean


suficientemente largos para aceptar la mayor entrada */

printf("longitudes:%d %d\n",strlen(cadena1), strlen(cadena2));


if (!strcmp(cadena1, cadena2))
printf("Las cadenas son iguales\n");
strcat(cadena1, cadena2);
printf("%s\n", cadena1);
return 0;
}
Comentarios:
Todas estas funciones utilizan la librería "string.h"
strcpy() copia la cadena apuntada por c2 en la cadena apuntada por c1. Devolviéndo c1. strcat()
concatena la cadena apuntada por c2 en la apuntada por c1, también devuelve c1.
strlen() devuelve la longitud de la cadena apuntada por c1.
strcmp() compara c1 con c2, devolviéndo 0 si las dos cadenas son iguales, mayor que 0 si la
cadena apuntada por c1 es mayor que la apuntada por c2 y menor que 0 si la cadena apuntada por
c1 es menor que la apuntada por c2.

Arreglo Bidimensional:
Los array bidimensionales de Pascal se declaraban [0..5, 0..7]. Mientras que en C por cada nueva
dimensión debe agregarse un paréntesis. Por ejemplo
/*matriz tipica*/
int matriz[tamaño1] [tamaño2];
Para accesar el 10 en el lugar (1,2) de la matriz usamos matriz[1] [2] = 10.

______________________________________________________________________________________ 77
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

ARCHIVOS (Lectura y Escritura en Archivos Secuenciales)


Un aspecto importante de cualquier programa, es su habilidad de comunicarse con el mundo
exterior. La lectura de archivos con datos de entrada, y la escritura de archivos de salida con los
resultados, consituye una manera simple pero efectiva de comunicación.

Funciones de Libreria para la Entrada y Salida (E/S) de Archivos


Existen rutinas para abrir y cerrar archivos, y para leer y escribir en ellos utilizando datos
formateados, de manera similar a como se hace el formato de datos para entrada y salida en
pantalla. La función fopen se utiliza para abrir un archivo, y asociarle una variable apuntador a
archivo con la cual el programa puede referirse a la fuente del archivo.

El prototipo de fopen es:

FILE *fopen(const char *filename, const char *modo);

fopen retorna un apundador al archivo si la llamada fue exitosa, y sino, retorna NULL. El apuntador
al archivo se utiliza para indentificar la fuente del mismo, se pasa como parámetro a las rutinas de
escritura, lectura y manejo del archivo. El filename y modo son cadenas de caracteres.

Los modos válidos son:


Modo Uso
r Abre para lectura
w Abre o crea para escritura. Descarta (destruye) cualquier contenido previo
Abre o crea para escritura. Agrega a (escribe despues de) cualquier contenido
a
previo
r+ Abre para modificaciones (lectura y escritura)
w+ Abre o crea para modificacion. Descarta (destruye) cualquier contenido previo
a+ Abre o crea para modificacion. Agrega a cualquier contenido previo

Para declarar una variable de tipo apuntador a archivo:


#include <stdlib.h>
FILE *fp;

Ejemplo de apertura de un archivo:


fp = fopen("datos.txt","w");

La funcion fflush se usa para físicamente escribir (bajar) en el archivo cualquier data almacenada en
el "buffer" intermedio. Para hacer los programas mas eficientes y reducir las llamadas al sistema, los
archivos mantienen un buffer (memoria) intermedia, una vez que el buffer esta lleno, los datos se

______________________________________________________________________________________ 78
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
escriben en el archivo. La rutina fflush fuerza a que el buffer sea escrito en el archivo asociado. Su
prototipo es:

int fflush(FILE *fp);


Recibe un apuntador a archivo como parámetro. Retorna cero si la llamada fue exitosa y EOF si no
lo fue. EOF es una constante especial que denota que se ha llegado al final del archivo.

Para cerrar los archivos se utiliza la funcion fclose. Su prototipo es:


int fclose(FILE *fp);
fclose retorna cero si fue exitosa y EOF si hubo alguna falla.

Para escribir datos en un archivo se utiliza la fprintf. Esta función es muy similar a printf, que ya
sabemos manejar bien. La única diferencia es que fprintf tiene un argumento adicional, el
apundador a archivo que especifica la fuente donde queremos escribir. Su prototipo es:
int fprintf(FILE *fp, const char* format, ....);
fprintf retorna el número de caracteres que fueron escritos exitosamente o un número negativo en
caso de falla.

Para leer datos de un archivo se utiliza la función fscanf. Esta función es muy similar a scanf, que
ya sabemos manejar bien. La única diferencia es que fscanf tiene un argumento adicional, el
apundador a archivo que especifica la fuente de donde queremos leer. Recuerden que los
argumentos para guardar datos deben ser apuntadores (i.e, llevan & en caso de ser variables
simples, y solo el nombre en caso de ser arreglos de caracteres). Su prototipo es:
int fscanf(FILE *stream, const char* format, ....);

Ejemplo
El siguiente programa abre un archivo que contiene un nombre y cedula de indentidad. Luego abre
un archivo de salida y escribe estos datos (pero en orden invertido). El archivo de entrada fue
creado con un editor de texto.

Archivo de entrada: entrada.txt


Pedro 18878798

#include <stdio.h>
int main()
{
char ifilename[] = "entrada.txt";
char ofilename[] = "salida.txt";
char name[30];
int idNum;

FILE *ofp, *ifp; /* Declara apuntadores a archivo */


ifp = fopen(ifilename,"r"); /* Abre archivo de entrada */
fscanf(ifp,"%s %d",name,&idNum); /* Lee datos de entrada */
ofp = fopen(ofilename,"w"); /* Abre archivo de salida */
fprintf(ofp,"%d %s\n",idNum, name); /* Escribe los datos */
fclose(ifp); fclose(ofp); /* Cierra los archivos */
return 0;
}

Archivo de salida: salida.txt


______________________________________________________________________________________ 79
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
18878798 Pedro

Ejercicio de Practica
Extender el ejercicio anterior para que sea capaz de leer y escribir múltiples pares de nombres y
cédulas. Sugerencia: Crear un ciclo y chequear cuando la rutina scanf retorna EOF.

Solucion:
#include <stdio.h>
int main(){
char ifilename[] = "c:/entrada.txt";
char ofilename[] = "c:/salida.txt";
char name[30];
int idNum;
FILE *ofp, *ifp;
ifp = fopen(ifilename,"r"); /* Abrir archivo entrada */
ofp = fopen(ofilename,"w"); /* Abrir archivo salida */
while (fscanf(ifp,"%s %d",name,&idNum) != EOF) {/*Leer datos */
fprintf(ofp,"%d %s\n",idNum, name); /* Escribir datos */
}
fclose(ifp); fclose(ofp); /* Cerrar archivos */
return 0;

Otras Rutinas Utiles


int fgetc(FILE *fp); Retorna el siguiente caracter del archivo referenciado por fp, o EOF si se
ha llegado al final del archivo. El tipo de retorno es int en lugar de char, debido a que el caracter
EOF debe ser manejado.

int *fgets(char *s, int n, FILE *fp); Retorna un apuntador a la cadena de caracteres
leida si resulta exitoso, o NULL si se llega al final del archivo o si ocurre un error. La cadena es
también almacenada en el arreglo de caracteres especificado como argumento. Un máximo de n-1
caracteres seran leídos. Lo cual da espacio para guardar el carácater de fin de cadena '\0'.

int fputs(const char s*, FILE *fp); Escribe una cadena de caracteres en el archivo, retorna
EOF si hubo alguna falla.

int fputc(int c, FILE *fp); Escribe un caracter solo en el archivo, si hubo algun problema
devuelve EOF.

int sprintf(char *buffer, const char *format, ...); Esta función es similar a fprintf,
excepto que los datos son escritos en un arrreglo (o buffer) de caracteres en lugar de en un archivo
de salida.

int sscanf(char *buffer, const char *format, ...); Esta función es similar a fscanf,
excepto que los datos son leidos de un arrreglo (o buffer) de caracteres en lugar de un archivo de
entrada.

______________________________________________________________________________________ 80
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Argumentos de Línea de Comando
C provee un mecanismo para pasar argumentos desde la línea de comandos al programa que se va a
ejecutar. Cuando el programa comieza su ejecución, la rutina main es llamada con dos argumentos:
un contador y un apuntador a un arreglo de cadenas de caracteres. El contador es llamdo por
convención argc y el apuntador argv. El uso de de argv es un poco truculento. Dado que argv es
un apundador a un arreglo de cadenas de caracteres, la primera cadena de caracteres es
referenciada por argv[0] (o *argv). La segunda cadena is referenciada por argv[1] (o *(argv + 1)),
la tercera por argv[2], y así sucesivamente. La primera cadena de caracteres, argv[0], contiene el
nombre del programa. Los argumentos comienzan realmente con argv[1].

Un ejemplo de llamada de un programa llamado copia, que copie un archivo en otro archivo seria:
copia fuente.txt destino.txt. Esto es, el nombre del programa mas dos argumentos que
corresponderian a los nombres de los archivos fuente y destino. Veamos un ejemplo que
simplemente muestra los argumentos de un programa.

#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
fprintf("The number of command line arguments is %d\n", argc);
fprintf("The program name is %s\n",argv[0]);
for (i = 1; i < argc; i++) {
fprintf("%s",argv[i]);
}
fprintf("\n");
return 0;
}

Referencias
Tutorial de Programación en C , conciso y bien escrito, con enlaces a muchos otros recursos para la
programación en C y C ++.

Comente y diga que hace el siguiente programa, si entrada es


pedro 188543.!

#include <stdio.h>

int main()
{
char ifilename[] = "entrada.txt";
char ofilename[] = "salida.txt";
char name[30];
int idNum;

FILE *ofp, *ifp; /* Declara apuntadores a archivo */


ifp = fopen(ifilename,"r"); /* Abre archivo de entrada */
fscanf(ifp,"%s %d",name,&idNum); /* Lee datos de entrada */
ofp = fopen(ofilename,"w"); /* Abre archivo de salida */
fprintf(ofp,"%d %s\n",idNum, name); /* Escribe los datos */
fclose(ifp); fclose(ofp); /* Cierra los archivos */
return 0;
______________________________________________________________________________________ 81
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
}

10 APLICACIONES

En esta sección crearemos un pequeño programa que genera 10 números al azar y luego los
ordena. En cursos de estadística se estudiará con mayor detalle lo que son los números aleatorios,
los cuales están relacionados con la forma de predecir un resultado antes de que este suceda, por
ejemplo en el lanzamiento de los dados. Las secuencias de números aleatorios son muy importantes
en las Ingenierías, debido a que muchos sistemas tienen aspectos que varían al azar. Este problema
en sí , nos lleva a analizar la generación al azar y el ordenamiento.

/* ej20.cpp
nombre: azar
fecha : 10.12.96
propósito: usar una función rand() que genera nº aleatorios y los ordena
*/
#include<stdio.h>
#include<stdlib.h>
#define MAX 20
int a[MAX];

void main()
{ int i, t, x, y;
for (i=0; i < MAX; i++)
{
a[i]=rand();
printf("%d\n", a[i]);
}
for (x=0; x< MAX-1; x++)
for (y=0; y< MAX-x-1; y++)
if (a[y] > a[y+1])
{
t=a[y];
a[y]=a[y+1];
a[y+1]=t;
}
printf("---\n");
for (i=0; i < MAX; i++)
printf("%d\n", a[i]);
}
Comentarios:
Este programa genera 20 nº al azar y los ordena según el método de la búrbuja. Este esquema es
posible realizarlo para escribir sus propias librerías. Practique este hecho separando el programa en
librerías

/* ej21.cpp
nombre: ordenar
fecha : 12.12.96

______________________________________________________________________________________ 82
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
propósito: ordenar una lista de notas
*/
#include<stdio.h>
#define MAX 20

void main()
{ int i, j, notas[MAX], suma=0, aux;

printf("\ningrese %d notas: ", MAX);


for (i=0; i < MAX; ++i)
{
scanf("%d", &notas[i]);
suma +=notas[i];
}
for (i=0; i< MAX-1; ++i)
for (j= MAX-1;i<j; --j)
if (notas[j-1] < notas[j])
{
aux=notas[j-1];
notas[j-1]=notas[j];
notas[j]=aux;
}
printf("\n\nlas notas ordenadas son: \n");
for (i=0; i < MAX; ++i)
printf("\n notas[%d] = %7d", i, notas[i]);
printf("\n\n%23d%s\n%23.1f%s\n\n", suma, "es la suma",
(double) suma /(double) MAX, "es el promedio del curso");
}
Comentarios:
Este programa utiliza el ordenamiento burbuja para ordenaruna lista de notas y luego calcula la
suma de ellas y el promedio. La eficiencia de este tipo de ordenamiento es relativa, pues existen
otras técnicas que pueden ejecutarse con mayor rapidez, sin embargo esto último carece de
importancia cuando el ordenamiento de los datos es esporádicos o los datos son pequeños. El
análisis de eficiencia de los algoritmos corresponde al curso de Diseño y Análisis de Algoritmos, sin
embargo aquí se mostrará el como medir el tiempo de ejecución utilizando la librería time.h de C.
El efecto de la expresión (double) suma /(double) MAX, es convertir el valor int de suma en un
valor double, respectivamente para MAX. Como este operador tiene mayor prioridad que la
división, se realizan las conversiones primero.

/* pe97c20.cpp
nombre: string como arreglos
fecha : 19.12.96
propósito: leer un caracter hasta una nueva línea, almacenándolos
en un array y terminando el string con un caracter NULL. Luego lo imprime
*/
#include <stdio.h>
#define MAX 20 /* dimensiona el tamaño del array */
#define TAM 100

void indire_incre(int *p);

main()
{ char mens[TAM], ch;
int i=0;
______________________________________________________________________________________ 83
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
printf("***strings de caracteres***\n\n");
printf("tipo de caracteres terminados por RETURN ó ENTER\n");

while ((ch =getchar())!='\n')


mens[i++]=ch;
mens[i]='\0';
i=0;

while (mens[i]!='\0')
putchar(mens[i++]);
printf("\n");
return 0;
}

/* pe97c21.cpp
nombre: arreglos a funciones
fecha : 19.12.96
propósito: usar funciones para leer valores de un array e imprimir los valores
*/
#include <stdio.h>
#define MAX 20 /* dimensiona el tamaño del array */

int leer_array(int valor[], int lim);


void impri_array(int valor[], int lim);

main()
{ int n,valor_examen[MAX];
printf("***Lista de Examenes***\n\n");
n=leer_array(valor_examen, MAX);
impri_array(valor_examen, n);
return 0;
}
/* función lectura de datos de un array */
int leer_array(int valor[], int lim)

{ int n, contador=0;
printf("Tipee el valor, EOF para salir\n");
while ((contador<lim)&&(scanf("%d", &n) != EOF)){
valor[contador]=n;
contador++;
}
return contador;
}
/* función que imprime lim elementos en el array */
void impri_array(int valor[], int lim)
{ int i;
printf("\n***Valor de los exámenes***\n\n");
for (i=0;i < lim; i++)
printf("%d\n", valor[i]);
}

/* pe97c22.cpp
nombre: leer y escribir
fecha : 19.12.96
propósito: leer y escribir hasta que un vacío sea encontrado.
______________________________________________________________________________________ 84
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Usa funciones leer e imprimir para los archivos estandar.
*/
#include <stdio.h>
#define TAM 100 /* dimensiona el tamaño del array */
void print_string(char s[]);
void lee_string(char s[]);

main()
{ char string[TAM];
do {
lee_string(string);
print_string(string);
} while (string[0]);
return 0;
}
/* función lectura de datos desde una entrada estandar hasta que una nueva
línea es leída */

void lee_string(char *s)


{ int i;
char c;
for (i=0;(c= getchar()) != '\n'; i++)
s[i]=c;
s[i]=NULL;
}

/* función que imprime un string a salida estandar y termina con una nueva
línea */

void print_string(char *s)


{ int i;
for (i=0;s[i]; i++)
putchar(s[i]);
putchar('\n');
}

/* Este algoritmo es similar al definido en pe97c20.cpp, la diferencia radica


en que este programa termina cuando el string vacío es leído*/

/* pe97c23.cpp
nombre: inicializar
fecha : 19.12.96
propósito: inicializar arreglos
*/
#include <stdio.h>
#define MAX 10 /* dimensiona el tamaño del array */

main()
{ int i, ex[MAX]= {12, 23,9, 17, 16, 49};
char saludo[MAX]={'H', 'o', 'l', 'a', '\0'};
char mensaje[]="El saludo del día es: ";

printf("***inicializaciónn de arreglos***\n\n");
printf("%s%s\n", mensaje, saludo);
printf("arreglo inicializado: \n");
for (i=0;i < MAX; i++)
______________________________________________________________________________________ 85
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
printf("%d\n", ex[i]);
return 0;
}

/* pe97c24.cpp
nombre: encontrar
fecha : 20.12.96
propósito: búsqueda lineal o secuencial en un array[] de tamaño lim de un item
key
*/
#include <stdio.h>
int busq_sec(int x[], int lim, int key);
void print_array_linea(int x[], int lim);

main()
{ int id[]= {45, 67, 12, 34, 25, 39};
int n, i;
printf("***búsqueda secuencial***\n\n");
printf("el arreglo es: \n");
print_array_linea(id, 6);/* función imprimir array horizontalmente*/
printf("ingrese un entero, EOF para salir: \n");
while(scanf("%d", &n) != EOF) {
i=busq_sec(id, 6, n);
if (i>=0)
printf("Item %d es encontrado en el indice %d\n", n, i);
else
printf("Item %d NO está en el array\n", n);
printf("ingrese un entero, EOF para salir: \n");
}
return 0;
}/* driver para testear la función busq_sec */

/* función búsqueda lineal */


int busq_sec(int x[], int lim, int key)
{ int i;
for (i=0; i < lim; i++)
if (x[i] == key)
return(i);
return(-1);
}
/* función imprimir array en línea */
void print_array_linea(int x[], int lim)
{ int i;
for (i=0; i<lim; i++) {
if (i % 10 ==0)
printf("\n");
printf("%d ", x[i]);
}
printf("\n");
}

/* pe97c25.cpp
nombre: ordenar por selección
fecha : 20.12.96
propósito: ordenar un arreglo por el método de Selección
______________________________________________________________________________________ 86
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
*/
#include <stdio.h>
#define MAX 10
void orden_selec(int x[], int lim);
int max_pos(int x[], int lim);
void print_array_linea(int x[], int lim);
int obt_maxpos(int x[], int tam);

main()
{ int datos[MAX]= {63, 75, 90, 12, 27};

printf("***arreglo original:\n\n");
print_array_linea(datos, 5);
orden_selec(datos, 5);
printf("\n***arreglo ordenado:\n\n");
print_array_linea(datos, 5);
return 0;
} /* función selección */
void orden_selec(int x[], int lim)
{ int tam, maxpos, tmp;
for (tam=lim ; tam > 1; tam--) {
maxpos=obt_maxpos(x, tam);
tmp= x[maxpos];
x[maxpos]=x[tam -1];
x[tam -1]=tmp;
}
}
/* función que retorna el indice del elemento más grande en el array x[]*/
int obt_maxpos(int x[], int tam)
{ int i, maxpos=0;
for (i=0; i < tam; i++)
maxpos=x[i] > x[maxpos] ? i:maxpos;
return maxpos;
}
/* función imprime un array entero de tamaño lim */
void print_array_linea(int x[], int lim)
{ int i;
for (i=0; i < lim; i++)
printf("%d ", x[i]);
}

APLICACION A CALCULO DE SUELDOS

Volvamos al problema dados al comienzo, cuando implementamos un programa para calcular


sueldos.

/* pe97c26.cpp
nombre: base de datos
Fecha : 20.12.96
propósito: calcular y almacenar datos para un número id´s.
Obteniéndo los datos, cálculo del pago e imprimir los datos para todos los id´s
*/
#include <stdio.h>
#define MAX 10
#define REG_LIMITE 40
______________________________________________________________________________________ 87
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
#define FACTOR 1.5

int obt_datos(int id[], float hrs[], float razon[], int lim);


void calc_pago(float hrs[], float razon[], float normal[], float sobre[], int
n);
void print_datos(int id[], float hrs[], float razon[], float reg[],
float over[],int n);

main()
{ int n, id[MAX];
float hrs[MAX], razon[MAX], normal_pago[MAX], sobre_pago[MAX];

printf("***programa sueldos***\n\n");
n=obt_datos(id,hrs,razon,MAX);
calc_pago(hrs,razon,normal_pago,sobre_pago, n);
print_datos(id,hrs,razon,normal_pago,sobre_pago, n);
return 0;
}
/* obtener datos para todos los id´s válidos y retornar el número de id´s*/
int obt_datos(int id[], float hrs[], float razon[], int lim)
{ int n=0;
while (n < lim) {
printf("ID <cero para salir>: ");
scanf("%d", id + n); /* id + n es lo mismo que &id[n] */
if (id[n] <=0) return n;
printf("Horas Trabajadas: ");
scanf("%f", hrs + n);
printf("Razón de pago: ");
scanf("%f", razon + n);
n++;
}
printf("no más espacio para datos-procesar datos\n");
return n;
}

/* calcular el pago regular y sobretiempo para cada id*/


void calc_pago(float hrs[], float razon[], float normal[], float sobre[],int n)
{ int i;
for (i=0; i < n; i++) {
if (hrs[i]<= REG_LIMITE) {
normal[i]=hrs[i]*razon[i];
sobre[i]=0;
}
else {
normal[i]=REG_LIMITE*razon[i];
sobre[i]=( hrs[i] - REG_LIMITE )* FACTOR * razon[i];
}
}
}
/* Imprimir la tabla de datos para todos los id´s */
void print_datos(int id[], float hrs[], float razon[], float normal[],
float sobre[], int n)
{ int i;
printf("***REPORTE FINAL - Pago de Sueldos***\n\n");
printf("%4s\t%5s\t%5s\t%6s\t%6s\t%6s\n", "ID", "HRS", "RAZON",
"NORMAL", "SOBRE", "TOTAL");
______________________________________________________________________________________ 88
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
for (i=0; i<n; i++)
printf("%4d\t%5.2f\t%5.2f\t%6.2f\t%6.2f\t%6.2f\n",
id[i], hrs[i], razon[i], normal[i], sobre[i], normal[i] +
sobre[i]);
}

/* pe97c27.cpp
nombre: ordenar base de datos
Fecha : 20.12.96
propósito: calcular y almacenar datos para un número id´s.
Obteniene los datos, ordena y calcula el pago e imprime los datos para todos
los id´s
*/
#include <stdio.h>
#define MAX 10
#define TRUE 1
#define FALSE 0
#define REG_LIMITE 40
#define FACTOR 1.5

int obt_datos(int id[], float hrs[], float razon[], int lim);


void calc_pago(float hrs[], float razon[], float normal[], float sobre[], int
n);
void print_datos(int id[], float hrs[], float razon[], float reg[],
float over[],int n);
void ordenar_datos(int id[], float hrs[], float razon[], int n);

main()
{ int n=0,id[MAX];
float hrs[MAX], razon[MAX], normal_pago[MAX], sobre_pago[MAX];

printf("***programa sueldos - datos ordenados***\n\n");


n=obt_datos(id,hrs,razon,MAX);
ordenar_datos(id, hrs, razon, n);
calc_pago(hrs,razon,normal_pago,sobre_pago, n);
print_datos(id,hrs,razon,normal_pago,sobre_pago, n);
return 0;
}
/* obtener datos para todos los id´s válidos y retornar el número de id´s*/
int obt_datos(int id[], float hrs[], float razon[], int lim)
{ int n=0;
while (n < lim) {
printf("ID <cero para salir>: ");
scanf("%d", id + n); /* id + n es lo mismo que &id[n] */
if (id[n] <=0) return n;
printf("Horas Trabajadas: ");
scanf("%f", hrs + n);
printf("Razón de pago: ");
scanf("%f", razon + n);
n++;
}
printf("no más espacio para datos-procesar datos\n");
return n;
}
/* ordena los datos */
______________________________________________________________________________________ 89
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
void ordenar_datos(int id[], float hrs[], float razon[], int lim)
{ int i, k, temp, swap=TRUE;
float ftmp;
for(i=0; swap && i < lim - 1; i++) {
swap=FALSE;
for(k=0; k < lim - i - 1; k++)
if (id[k] > id[k+1]) {
temp=id[k];
id[k]=id[k+1];
id[k+1]=temp;

ftmp= hrs[k];
hrs[k]=hrs[k+1];
hrs[k+1]=ftmp;

ftmp= razon[k];
razon[k]=razon[k+1];
razon[k+1]=ftmp;
swap=TRUE;
}
}
}

/* calcular el pago regular y sobretiempo para cada id*/


void calc_pago(float hrs[], float razon[], float normal[], float sobre[],int n)
{ int i;
for (i=0; i < n; i++) {
if (hrs[i]<= REG_LIMITE) {
normal[i]=hrs[i]*razon[i];
sobre[i]=0;
}
else {
normal[i]=REG_LIMITE*razon[i];
sobre[i]=( hrs[i] - REG_LIMITE )* FACTOR * razon[i];
}
}
}

/* Imprimir la tabla de datos para todos los id´s */


void print_datos(int id[], float hrs[], float razon[], float normal[],
float sobre[], int n)
{ int i;
printf("***REPORTE FINAL - Sueldos Ordenados***\n\n");
printf("%4s\t%5s\t%5s\t%6s\t%6s\t%6s\n", "ID", "HRS", "RAZON",
"NORMAL", "SOBRE", "TOTAL");
for (i=0; i<n; i++)
printf("%4d\t%5.2f\t%5.2f\t%6.2f\t%6.2f\t%6.2f\n",
id[i], hrs[i], razon[i], normal[i], sobre[i], normal[i] +
sobre[i]);
}

/* pe97c28.cpp
nombre: base de datos
______________________________________________________________________________________ 90
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Fecha : 20.12.96
propósito: búsqueda de algún item en una base de datos ordenada
*/
#include <stdio.h>
#define MAX 10
#define TRUE 1
#define FALSE 0
#define REG_LIMITE 40
#define FACTOR 1.5

int obt_datos(int id[], float hrs[], float razon[], int lim);


void calc_pago(float hrs[], float razon[], float normal[], float sobre[], int
n);
void print_datos(int id[], float hrs[], float razon[], float reg[], float
over[],int n);
void ordenar_datos(int id[], float hrs[], float razon[], int n);
void print_reg(int id[],float hrs[],float razon[],float normal_pago[], float
sobre_pago[],int i);
int busq_binaria(int y[], int lim, int key);

main()
{ int i, n=0, key, id[MAX];
float hrs[MAX], razon[MAX], normal_pago[MAX], sobre_pago[MAX];

printf("***programa sueldos - búsqueda de datos***\n\n");


n=obt_datos(id,hrs,razon,MAX);
ordenar_datos(id, hrs, razon, n);
calc_pago(hrs,razon,normal_pago,sobre_pago, n);
print_datos(id,hrs,razon,normal_pago,sobre_pago, n);

printf("Tipee un id <cero para salir>: ");


while (scanf("%d", &key) !=EOF && key !=0) {
i=busq_binaria(id, n, key);

if (i>=0)
print_reg(id, hrs, razon, normal_pago, sobre_pago, i);
else printf("ERROR - no existe id\n");

printf("Tipee un id <cero para salir>: ");


}
return 0;
}
/* busqueda binaria*/
int busq_binaria(int y[], int lim, int key)
{ int bajo, mitad, alto=lim-1;
bajo=0;
while (bajo<=alto){
mitad=(bajo+alto)/2;
if (key == y[mitad])
return(mitad);

else if (key < y[mitad])


alto=mitad-1;
else
bajo=mitad + 1;
}
______________________________________________________________________________________ 91
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
return(-1);
}
/* imprime registro, especificando su indice*/
void print_reg(int id[],float hrs[],float razon[],float normal_pago[], float
sobre_pago[],int i)
{
printf("***sueldo para el registro %d***\n\n", id[i]);
printf("%10s%10s%10s%10s%10s%10s\n", "ID", "HRS", "RAZON", "NORMAL", "SOBRE",
"TOTAL");

printf("%10d%10.2f%10.2f%10.2f%10.2f%10.2f\n",id[i], hrs[i], razon[i],


normal_pago[i], sobre_pago[i], normal_pago[i]+ sobre_pago[i] );
}
/* obtener datos para todos los id´s válidos y retornar el número de id´s*/
int obt_datos(int id[], float hrs[], float razon[], int lim)
{ int n=0;
while (n < lim) {
printf("ID <cero para salir>: ");
scanf("%d", id + n); /* id + n es lo mismo que &id[n] */
if (id[n] <=0) return n;

printf("Horas Trabajadas: ");


scanf("%f", hrs + n);
printf("Razón de pago: ");
scanf("%f", razon + n);
n++;
}
printf("no más espacio para datos-procesar datos\n");
return n;
}
/* ordena los datos */
void ordenar_datos(int id[], float hrs[], float razon[], int lim)
{ int i, k, temp, swap=TRUE;
float ftmp;
for(i=0; swap && i < lim - 1; i++) {
swap=FALSE;

for(k=0; k < lim - i - 1; k++)


if (id[k] > id[k+1]) {
temp=id[k];
id[k]=id[k+1];
id[k+1]=temp;

ftmp= hrs[k];
hrs[k]=hrs[k+1];
hrs[k+1]=ftmp;

ftmp= razon[k];
razon[k]=razon[k+1];
razon[k+1]=ftmp;
swap=TRUE;
}
}
}
/* calcular el pago regular y sobretiempo para cada id*/
void calc_pago(float hrs[], float razon[], float normal[], float sobre[],int n)
______________________________________________________________________________________ 92
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
{ int i;
for (i=0; i < n; i++) {
if (hrs[i]<= REG_LIMITE) {
normal[i]=hrs[i]*razon[i];
sobre[i]=0;
}
else {
normal[i]=REG_LIMITE*razon[i];
sobre[i]=( hrs[i] - REG_LIMITE )* FACTOR * razon[i];
}
}
}
/* Imprimir la tabla de datos para todos los id´s */
void print_datos(int id[], float hrs[], float razon[], float normal[], float
sobre[], int n)
{ int i;
printf("***REPORTE FINAL - Sueldos Ordenados***\n\n");
printf("%4s\t%5s\t%5s\t%6s\t%6s\t%6s\n", "ID", "HRS", "RAZON",
"NORMAL", "SOBRE", "TOTAL");
for (i=0; i<n; i++)
printf("%4d\t%5.2f\t%5.2f\t%6.2f\t%6.2f\t%6.2f\n",
id[i], hrs[i], razon[i], normal[i], sobre[i], normal[i] +
sobre[i]);
}

ESTRUCTURAS

______________________________________________________________________________________ 93
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Una estructura es un conjunto de variables que se referencian con un único nombre. Esto es muy
útil cuando queramos mantener junta información relacionada. Para definir una estructura,
utilizamos la palabra clave struct. Esta palabra le indica al compilador que lo que viene a
continuación es una estructura. La forma general para definir una estructura es la siguiente:

struct nombre_estructura
{
tipo variable1;
tipo variable2;
...
...
tipo variablei [=expresión]
...
...
tipo variableN;
} lista_variables_estructura;

Con esta definición realmente haremos 2 cosas: por una parte, estamos definiendo una plantilla de
estructuras, la cual tomará el nombre que indique el parámetro nombre_estructura, y por otra parte,
estamos definiendo una serie de variables con la forma de la plantilla definida. Estas variables son
las que vendrían dadas por el parámetro lista_variables_estructura. Cada campo de una estructura
puede ser inicializado en la declaración mediante una expresión de la misma forma a como se hacía
al declarar una variable de un tipo básico. Un ejemplo más concreto podría ser la siguiente
estructura:

struct Datos_Persona
{
char nombre[20];
char apellido1[20];
char apellido2[20];
char calle[50];
char ciudad[20] = "Sevilla";
unsigned long DNI;
} pers1, pers2, pers3, pers4;

Si posteriormente deseamos crear más variables utilizando la plantilla de estructura datos_persona,


bastaría con declararla de la siguiente forma:

struct Datos_Persona nueva_persona;

Hay que tener cuidado al elegir el lugar donde definimos la plantilla de estructura. Lo más normal
es declararla globalmente para que pueda ser utilizada en cualquier lugar del programa. A la hora de
definir la plantilla de estructura podemos omitir, o bien, el parámetro nombre_estructura, o bien, el
parámetro lista_variables_estructura. En el caso de que se omita el parámetro nombre_estructura,
sólo podremos usar la plantilla para definir las variables que aparezcan en el parámetro
lista_variables_estructura. Si posteriormente queremos declarar alguna otra variable utilizando esta
plantilla, tendremos que volver a definirla. Si el parámetro que omitimos es
lista_variables_estructura, lo que habremos hecho es crear una plantilla de estructura sin variables
asociadas, las cuales podremos crear en cualquier momento, usando el modo de declaración de
estructura visto anteriormente, es decir:

______________________________________________________________________________________ 94
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
struct nombre_estructura nombre_variable_nueva;

En ningún caso podremos omitir, a la vez, el nombre_estructura y la lista_variables_estructura.


Veamos ahora como podemos acceder a los elementos individuales que forman las estructuras. Para
dichos accesos se utiliza el operador ".". Veamos un ejemplo de acceso a una estructura.
Supongamos que tenemos la siguiente estructura:

struct coordenada
{
int x;
int y;
int z;
} a, b, c;

Para llenar la variable estructura "a" bastaría con hacer:

a.x = 5;
a.y = 23;
a.z = 10;

En cualquier momento podremos leer estos datos:

int eje_x, eje_y, eje_z;

eje_x = a.x;
eje_y = a.y;
eje_z = a.z;

Otra operación que podremos realizar son asignaciones entre variables estructuras con la misma
plantilla:

c = a;
b = c;

Los tipos de los elementos individuales de una estructura pueden ser simples (int, float, char, …) o
complejos (array, otra estructura, …). Veamos un ejemplo de una plantilla de estructura que
combina elementos individuales simples y complejos.

struct fecha
{
int dia;
int mes;
int anyo;
};

struct hora
{
int horas;
int minutos;
};

struct ficha
{
______________________________________________________________________________________ 95
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
char nombre[20];
char apellidos[40];
struct fecha dia_nacimiento;
struct hora hora_nacimiento;
};

Si queremos definir una variable tipo ficha, y queremos poner la fecha de nacimiento haremos:

struct ficha persona;

persona.fecha.dia = 20;
persona.fecha.mes = 2;
persona.fecha.anyo = 2001;

Otra posibilidad frecuentemente utilizada que se le ofrece al programador es la de definir tablas de


estructuras. De esta manera se permite gestionar una gran cantidad de información fácilmente. Un
ejemplo sencillo de esto sería por ejemplo una matriz de coordenadas. Teniendo en cuenta la
estructura coordenada definida más arriba podríamos definir la siguiente matriz:

struct coordenada matriz1[10][10];

En este caso una posible inicialización de la anterior matriz en el que todos sus componentes toman
como valor el origen de coordenadas quedaría de la siguiente manera:

for (cont_filas=0; cont<10; cont_filas++)


{
for (cont_columnas=0; cont_columnas<10; cont_columnas++)
{
matriz[cont_filas][cont_columnas].x = 0;
matriz[cont_filas][cont_columnas].y = 0;
matriz[cont_filas][cont_columnas].z = 0;
}
}

Ejemplos más complejos se podrían crear a partir de la estructura ficha de forma que se pudieran
gestionar los datos de toda una población de individuos. En este caso tendríamos por ejemplo:

// Vector que sirve para gestionar los datos personales de 100 individuos.

struct ficha poblacion[100];

Al igual que con el resto de tipos de variables, C permite crear variables punteros a estructuras. La
forma de declarar estas variables es:

struct nombre_estructura *nombre_varible_estructura;

Para poder acceder a los elementos individuales de una variable puntero a estructura se utiliza el
operador -> (el operador, denominado comunmente flecha, está formado por un signo menos
seguido de un signo de mayor, sin ningún espacio en blanco entre ellos). Siguiendo con el ejemplo
anterior de las coordenadas, vamos a definir un puntero a una variable de estructura de la siguiente
forma:
______________________________________________________________________________________ 96
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

struct coordenada a, b, *p, *q;

a.x = 5;
a.y = 23;
a.z = 10;

b = a;
p = &b; /* &b nos devuelve la dirección de la estructura b */

p->x = 2;
p->y = a.y;
p->z = a.z;

q = p; /* p y q apuntan a la misma estructura */

Al igual que ocurría en el apartado anterior también existe la posibilidad de declarar tablas de
estructuras. El formato sería el siguiente:

struct nombre_estructura *nombre_varible_estructura[N];


o
struct nombre_estructura *nombre_varible_estructura[N][M];

Por supuesto también se pueden declarar tablas de punteros a estructuras de más de dos
dimensiones pero al igual que ocurría con la declaración de tablas de tipos de datos básicos, no
suelen ser muy usadas.
Un ejemplo de declaración de un vector de punteros a estructuras y un acceso al campo de una
estructura sería la siguiente:

struct coordenada coor1, *vector_punteros_coordenadas[10];

vector_punteros_coordenadas[4] = &coor1;
vector_punteros_coordenadas[4]->y = 2;

Al igual que ocurría con los punteros a tipos básicos de datos también se pueden pasar punteros a
estructuras en las llamadas a funciones. Así por ejemplo tendríamos:

struct coordenada coordenada1, *p_coordenada1;


flota distancia;
coordenada1.x = 5;
coordenada1.y = -11;
p_coordenada1 = &coordenada1;
cambiar_a_punto_simétrico_en_cuadrante (p_coordenada1);
ó
cambiar_a_punto_simetrico_en_cuadrante (&coordenada1);
...
...
...
/* La siguiente función intercambia la coordenada x por la y */
void cambiar_a_punto_simétrico_en_cuadrante(struct coordenada *c)
{
int aux;

aux = (*c).x;
______________________________________________________________________________________ 97
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
(*c).x = (*c).y;
(*c).y = aux;
}

Nótese la equivalencia entre la notación c->x y (*c).x


En el lenguaje C, cuando hablamos de unión nos estamos refiriendo a una porción de memoria
compartida por varias variables, las cuales pueden ser de distintos tipos. Para declarar una unión
usamos la palabra clave union de la siguiente forma:

union nombre_union
{
tipo variable1;
tipo variable2;
...
...
tipo variableN;
} lista_variables_union;

Como se ve, la forma de declaración de declaración es similar a la de una estructura, de forma que
podremos declarar una variable, o bien colocando su nombre al final de la declaración inicial, o
posteriormente, utilizando una declaración aparte como muestra el siguiente código:

union nombre_union nombre_variable_nueva;

La forma de funcionamiento de las uniones es muy simple. Cuando definimos una unión y
declaramos variables asociadas, el compilador reservará para cada variable, un espacio de memoria
cuyo tamaño coincidirá con el tamaño del elemento más grande que se defina dentro de la unión.
Supongamos que tenemos la siguiente declaración:

union prueba_union
{
char letra;
char cadena[8];
int numero;
} u1;

El compilador reservará, para la variable u1, 8 bytes de memoria (que corresponde con el tamaño
del elemento cadena), de forma que en caso de acceder al elemento letra, estaremos accediendo al
primer byte de la variable, mientras que si accedemos al elemento numero, estaremos accediendo a
los dos primeros bytes de la variable. Por último, si accedemos al elemento cadena estaremos
accediendo a todos los bytes de la variable.
Una enumeración es un conjunto de constantes enteras con nombre, que especifica todos los
valores válidos que una variable de este tipo puede tener. La forma de definir una enumeración es:

enum nombre_enum{lista_de_enum} variables_de_enum;

Veamos un ejemplo:
enum color{blanco, amarillo, rojo, verde, azul, marron, negro} color_mesa;
Como con las estructuras, C permite definir posteriormente más variables enumeradas del tipo
color, la forma de hacerlo sería la siguiente:

enum color color_coche;


______________________________________________________________________________________ 98
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Una vez definidas las variables enumeradas, las siguientes operaciones serían válidas:

color_mesa = verde;

color_coche = color_mesa;

if(color_mesa == azul)
printf("La mesa es de color azul\n");

switch(color_coche)
{
case blanco:
printf("El coche es de color blanco\n");
break;

case amarillo:
printf("El coche es de color amarillo\n");
break;

case rojo:
printf("El coche es de color rojo\n");
break;

case verde:
printf("El coche es de color verde\n");
break;

case azul:
printf("El coche es de color azul\n");
break;

case marron:
printf("El coche es de color marron\n");
break;

case negro:
printf("El coche es de color negro\n");
break;
}

Para poder definir nuevos nombres de tipos de datos, usamos la palabra clave typedef. Con esto,
realmente no estamos creando un nuevo tipo de datos, sino que se define un nuevo nombre a un
tipo ya existente. También podemos usar typedef para asignar un nombre de tipo a una estructura o
a una enumeración, de forma que para declarar las variables de estos tipos no necesitaremos
precederlos de las palabras clave struct o enum, respectivamente. La forma general de la sentencia
typedef es:

typedef tipo nombre_nuevo_tipo;

Algunos ejemplos podrían ser:

typedef int ENTERO;


typedef unsigned char BYTE;

______________________________________________________________________________________ 99
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
typedef struct fecha FECHA;

De forma que podremos definir variables como:

ENTERO *e1, e2;


BYTE *b1, b2;
FECHA *f1, f2, lista_fechas[20];

Un uso típico al definir estructuras es:

typedef struct
{
float x,y,z;
} coordenada;

Si queremos definir una variable de este tipo de estructura basta con poner:

coordenada a;

Y a será una variable de estructura coordenada, lo que clarifica el código.

______________________________________________________________________________________ 100
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

11 ANEXOS

(I) TIPOS DE DATOS ESCALAR:

Recordemos que en un computador la memoria está organizada como una secuencia de bytes, en
donde cada byte posee una dirección. Por ejemplo si 1 byte es usado para almacenar variables
enteras con signo, el rango de valores ( en decimal ) es -128 a 127; para enteros sin signo tienen el
rango 0 a 255.

Si ahora 2 bytes son usados para representar enteros con signo, el rango es -32768 a +32767; y de
0 a 65535 para enteros sin signo. Si se utilizan 4 bytes para representar enteros el rango será
relativamente más grande. Similarmente para números de punto flotante; si se utilizan 4 bytes para
representar números de punto flotante la precisión será equivalente a considerar 7 dígitos decimales
significativos y una magnitud de 10E38 a 10E-38. Si más bytes fuesen utilizados para números de
punto flotante la precisión y el rango de variación será mucho mayor.
C proporciona una serie de tipos de datos adicionales que proporcionan una presición mayor, sin
embargo contrario a Pascal, C no posee tipo de dato boolean, de manera que para representar las
condiciones en los ciclos, se considera que todo valor distinto a cero es TRUE, y si es cero se
considera FALSE.

Tipo char, signed char: Este tipo de dato cubre 8 bits, es decir un Byte. Con ello se dejan
representar 2^8 cifras distintas. dado que este tipo de dato trabaja orientado a los simbolos puede
aceptar valores de -128 a +127.

Tipo unsigned char: Dado que en C no existe el tipo de dato “string”, son almacenadas cadenas
de caracteres en variables de este tipo.

Tipo enum: Este tipo de dato ocupa 2 Byte, pudiendo aceptar valores numéricos de -32768 a
+32767.

Tipo int: Para Turbo C++ es int de tamaño 2 Byte. Con el tipo de dato int puede el procesador
realizar cálculos rápidos.

Tipo unsigned int: Este tipo de dato es como unsigned char. Los valores que acepta en Turbo C+
+ se extienden de 0 hasta +65535.
Las constantes de este tipo son escritas usando el prefijo u ó U. Por ejemplo, 0xFFFU, 123U,
0777u, etc.

Tipo short, short int: Son tipos de datos idénticos a int. La ventaja de short int radica en que en
casi todo computador tiene tamaño de 16 bit, de manera que la portabilidad se optimiza .

Tipo long: Este tipo de dato cubre 32 bit o 4 Byte. Un campo de 2147483648 hasta +2147483647
esta a disposición, con esto pueden ser trabajados una gran cantidad de números. En caso que Ud.
trabaje con números más grandes o más pequeños que el campo antes descrito se recomienda
utilizar “float”.
Para enteros, una constante que represente un long int puede ser explicitada usando el prefijo l ó
L. Por ejemplo, 123L, 45678l, etc.
______________________________________________________________________________________ 101
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Tipo float: En muchos programas no es posible realizar algunos cálculos, pues no existe
computador o programa que pueda satisfacer la exactitud en los cálculos numéricos, tales números
se describen como números puntos flotante. Este tipo de dato requiere 32 bits, trabajando con una
exactitud de siete lugares y con valores que varían de 3.4*10^-38 hasta 3.4*10^+38 siendo el más
apropiado para cálculos científicos.
Las constantes de punto flotante son consideradas como doble precisión, cuyas constantes se
especifican con una f ó F. Por ejemplo, 34.567f, 3.141516F, extra precisión puede ser escrita con
el sufijo l ó L. Por ejemplo, 234567.171819L

Tipo double: Este tipo requiere 64 bits y trabaja con una precisión de 15 lugares abarcando una
variación de 1.7*10^-308 hasta 1.7*10^+308 y es utilizado para cálculos numéricos de alta
precisión.

Las declaraciones signed, unsigned, long, short son llamados MODIFICADORES, cuyo
objetivo es precisar con relativa exactitud cada necesidad.

Ejemplo 34: (Cálculos númericos en C )


/*file: ej10.cpp*/
#include<stdio.h>
int main(void)
{
int inum;
int iresul;
float fresul;
double dresul;
long double ldresul;
puts ("el resultado de un tercio.");
puts ("utilizando diferentes tipos de datos");

inum = 3;
iresul = 1/ inum;
printf ("el tipo de dato int\t\t1 /%d = %d\n", inum, iresul );
fresul = 1.0 / inum;
printf ("el tipo de dato float\t\t1 /%d = %.19f\n", inum, fresul);
dresul = 1.0 / inum;
printf ("el tipo de dato double\t\t1 /%d = %.19lf\n", inum, dresul );
ldresul = 1.0 / inum;
printf ("el tipo de dato long double\t1 /%d = %.19Lf\n", inum, ldresul );
return 0;
}/* fin de la función main() */
Comentarios:
En C no existe como en Pascal división entera, es importante que en fresul se escriba 1.0 en vez de
1, pues de otra manera fresul sería 0.000...otra alternativa mejor es fresul = (float) 1.0 / (float)
inum; pues obliga al compilador C transformar ambos valores al tipo dato float,

(II) TIPOS DE DATO ENUMERADO Y OTROS

______________________________________________________________________________________ 102
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Existen otros tipos de datos, llamados enumerado, ellos son: void , enum, typedef.

void se refiere a un objeto que no tiene valor de ningún tipo, comportamiento similar a funciones
que no retornan ningún valor, es decir funciones, como las que muestra el siguiente ejemplo la cual
solamente imprime un mensaje y no tiene ningún valor para retornar.

Ejemplo:
/* file.cpp
este programa introduce datos de tipo void
*/
void print_mensaje(void);

main( )
{
/* imprime mensaje */
print_mensaje( );
}
/* función imprime un mensaje */
void print_mensaje( void )
{
printf(“***Hola alumnos***\n”);
}

Como se ve ningún parámetro es requerido para la función print_mensaje( ), y no retorna ningún


valor. Solamente imprime su mensaje. Si es necesario retornar desde la función de tipo void antes
que llegue al final del cuerpo, entonces bastará con el segmento de programa

void print_mensaje( void )


{
printf(“***Hola alumnos***\n”);
return;
}

enum (enumerado) ya conocido en Pascal, y en lo substancial en C no cambia demasiado.


La enumeración puede estar implícita, por ejemplo:
enum { DO=1, LU, MA, MI, JU, VI, SA} dias; En donde DO está asociado con el valor 1, y el
resto dado en orden creciente, LU es 2, etc...
enum boolean {FALSE, TRUE}; aquí el nombre boolean puede ser usado para declarar variables
de tipo enumerado, por ejemplo enum boolean bandera1, bandera2;

typedef facilita definir sinónimos para tipos de datos, haciendo programas más legibles. Por
ejemplo, variables usadas para representar valores de años de personas u objetos pueden ser
definidos por un nuevo tipo, anno, es decir
typedef int anno;
anno age; /* la variable age puede tener tipos de valores anno. La diferencia es que podemos
tener más nombres para tipo de dato que el nombre genérico de int*/

(III) OPERADORES

______________________________________________________________________________________ 103
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
Los tipos de datos y el valor de las expresiones depende del tipo de datos de los operandos y el
orden de evaluación de los operadores , los cuales están determinados por la precedencia y la
asociatividad de ellos. Cuando los operadores tienen el mismo nivel de precedencia en una
expresión, el orden de la evaluación está determinada por la asociatividad. Criterio que por lo
demás posee Pascal. Sin embargo, la tabla siguiente hace un resúmen de las distintas precedencias
y asociaciones de los operadores.

Operadores nuevos ( x ++ ; -- x ; ?<>:<> ; , ; sizeof )

C proporciona una manera resumida de incrementar ó decrementar variables, y lo hace mediante


los operadores unarios ++ y -- respectivamente. Los que pueden ser utilizados como operadores
prefijo ó postfijo.
En postfijo, x++ incrementa el valor de x en 1, e y-- decrementa el valor de y en 1.
En prefijo, ++x incrementa el valor de x en 1, e --y decrementa el valor de y en 1.
La diferencia radica en la evaluación, por ejemplo
tam=5;
result= tam ++; en este caso, a result es asignado el valor de tam (5), y luego tam es
incrementado entonces result es 5 y tam es 6.

Por otra parte,


tam=5;
result= ++tam; en este caso, incrementa primero, y luego evalúa. En este caso result es 6.

C proporciona un operador resumido para casos tales como incrementar, decrementar, multiplicar,
dividir o mod valores dados. Por ejemplo,

x+ = 5; que corresponde a x = x +5;


y- = 12; corresponde a y = y - 12;
x* = 3; corresponde a x = x*3;
y/ = 5; corresponde a y = y / 5;
x% = 7; corresponde a x = x % 7;

Operador ?<>:<> es una alternativa de resumir algunas expresiones.

Por ejemplo, la expresión


if (x < y ) z = y; es equivalente a z = x < y ? y : x;
else z = x;

Una función que retorne el valor más grande entre dos argumentos double, puede escribirse,

double max(double x, double y)


{
return(x > y ? x : y );
}

Las macros pueden también utilizar este operador de manera bastante práctica.

Operador Coma
______________________________________________________________________________________ 104
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.

Veremos la utilidad de este operador a través de una estructura for, pues permite una asignación
múltiple de valores iniciales y el procesamiento múltiple de índices.
Por ejemplo,
for (suma=0, y=1; y < = n; ++y)
suma += y;
printf( “\n%d”, suma ); /* imprimirá la suma de los enteros de 1 a n */

Operador sizeof

sizeof simplemente permite evaluar el número de bytes usados para el tipo del resultado. Por
ejemplo, la expresión sizeof x evalúa el tamaño de x en bytes.

/* pe97c10.cpp
nombre: sizeof
fecha : 12.12.96
propósito: evalúa el número de bytes utilizados
*/

#include <stdio.h>

main()
{ /* declaracion */
int x;
double y;
/* imprime titulo */
printf("***operador sizeof***\n\n");
printf("tamaño de x es %d bytes \n", sizeof (x));
printf("tamaño de x+y es %d bytes\n\n",sizeof (x+y));
printf("tamaño de tipo de dato en bytes:\n");
printf("tamaño de tipo int es %d\n",sizeof (int));
printf("tamaño de tipo long int es %d\n", sizeof(long int));
printf("tamaño de tipo short int es %d\n",sizeof (short int));
printf("tamaño de tipo unsigned int es %d\n", sizeof(unsigned int));
printf("tamaño de tipo float es %d\n",sizeof (float));
printf("tamaño de tipo double es %d\n",sizeof (double));
return 0;
}
/* la utilidad es hacer que el programa sea portable de un sistema a otro*/

Comentario :
la salida es
***operador sizeof***

tamaño de x es 2 bytes
tamaño de x+y es 8 bytes

tamaño de tipo de datos en bytes:


tamaño de tipo int es 2
tamaño de tipo long int es 4
tamaño de tipo short int es 2
tamaño de tipo unsigned int es 2
tamaño de tipo float es 4

______________________________________________________________________________________ 105
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
tamaño de tipo double es 8

(IV) ESTRUCTURAS DE CONTROL

Las estructuras condicionales if y los ciclos while en C están realizados sobre la idea de
Expresión Boolena, al igual que en Pascal. Sin embargo dado que en C , NO existe tipo booleano.
Ud. puede usar como ya hemos mencionado el valor entero 0 como FALSE, mientras que
cualquier otro valor entero es TRUE.

Ejemplo 35: ( Traslación de if -then - else en Pascal a C )

if (x=y) and (j>k) then if ((x= =y) && (j>k))


z:=1 z =1;
else else
q:=10;{*codigo Pascal*} q =10;/*código C*/

Comentario: Lo siguiente también es legal en C

void main()
{
int a;
printf (“entre un número:”);
scanf (“%d”, a );
if (a)
{
hola, como estas
}
/* Si a es cualquier elemento que no sea 0, el codigo ( hola, como estas ) se
ejecutará. */

Ejemplo 36: ( Traslación de while en Pascal a C )

while a< b do while ( a < b )


begin {
blah blah blah blah
end; {* en Pascal *} }/*en C*/

Ejemplo 37: ( While de C como un for en C )

La lógica del for en C es la misma que en Pascal, es decir, se inicializa antes de ingresar al loop, se
condiciona el loop, y finalmente una forma de alcanzar la condición. El ejemplo 35 muestra que
while
de Pascal es equivalente al while de C, sin embargo este último puede ser interpretado como un for
en C, llamada forma simplificada.

x=1;
______________________________________________________________________________________ 106
Escuela Ingeniería en Computación, Universidad de La Serena
Programación en C Tutorial Dr. Eric Jeltsch
F.
while ( x < 10 ) for ( x = 1; x < 10; x++)
{ {
hola hola hola hola
x ++; /*x++ es lo mismo que x:=x+1 de Pascal */ }
}

Otro uso interesante del ciclo for es para crear un bucle infinito, por ejemplo
for( ; ; ) printf(“este ciclo siempre se estará ejecutando.\n”);

Ejemplo 38: ( Traslación de Repeat - until en Pascal a C )

C proporciona la estructura “ do-while “ que reemplaza la estructura “ repeat-until “.


do
{
blah blah
}
while ( a < b );

Nuevas Estructuras de Control ( Break , Continue y Switch )

Las cadenas de condiciones if- then-else surgen comúnmente en programación, ahora C a través
de la declaración switch proporciona una alternativa que hace más fácil manipular este tipo de
estructuras.

Las utilidades más prácticas de break es por ejemplo la interrupción de ciertos ciclos, pasando el
control a la proposición inmediatamente posterior al ciclo. continue también altera el flujo normal
de control en un loop. Cuando una declaración continue es ejecutada en un loop la iteración
ocurrente del cuerpo del loop es abortada;
Ejemplo: Supongamos que deseamos escribir a través de un loop una rutina que imprima los
enteros de 0 a 9, excepto para el 5. Utilizando continue queda.
n=0;
while ( n < 10 ) {
if ( n == 5 ) {
n= n + 1;
continue;
}
printf (“ el siguiente número es %d\n”, n );
n= n + 1;
}
El loop se ejecuta normalmente excepto para n=5 . En este caso, la condición if es TRUE; n es
incrementado y la declaración continue es ejecutada donde el control pasa a testear la condición del
loop ( n < 10 ). La ejecución del loop continue en forma normal desde este punto. Excepto para
n=5, todo valor desde 0 a 9 será impreso.

______________________________________________________________________________________ 107
Escuela Ingeniería en Computación, Universidad de La Serena

You might also like