Marcar inicio y fin con: oprimir Marcar inieio y fin con: Ubi ear cursor con las teclas de flecha Oprimir Marcar inieio y fin eon: y Ubicar cursor con las teclas de flecha Oprimir Oprimir <---7> Oprimir Oprimird,> Oprimir Oprimir Oprimir Oprimir Oprimir Ubi car el cursor Oprimir Dar el nombre del archivo
Seleccionar bloque OprimirDar el nombre del archivo
OprimirDar el Texto Oprimir Dar texto a buscar Dar texto a corregir
In troducci6n
11
Menu de compilaci6n Las funciones de compilacion de Turbo C estan disponibles a traves del menu de compilacion. Se tienen disponibles los comandos de este menu, cuando esta activa una ventana de edicion. Los comandos disponibles en este menu son: Compilar Hacer Enlazar Construir todo Informacion Quitar mensajes
(Compile) (Make) (Link) (Build All) (Informati on) (Remove Messages)
Para usar las instrucciones compilar, hacer, construir y enlazar, debe estar abierto un archivo en una ventana activa, y para usar hacer, construir y enlazar, debe estar definido un proyecto. EI compilador traduce un archivo con extension C (.C) 0 .CPP activo en una ventana de edicion a un archivo .OBI. Al estar compilando, en una caja de dialogo se presentan los resultados de la compilacion: lfneas compiladas, numero de errores y advertencias y memoria disponible, y en la ventana de mensajes se indican en detalle los errores y advertencias. Cada mensaje de error se vincula directamente con la linea de la ventana de edicion, en la cual se detecto el error. Esto se hace con el comando compilar (Compile). El comando hacer (Make) invoca al administrador de proyectos para ejecutar un proyecto, creando un archivo ejecutable con el nombre del proyecto 0 del archivo activo en la ventana de edicion. El comando enlazar (Link) toma los archivos incluidos en el proyecto activo y los enlaza. Es decir, determina todas las referencias cruzadas entre los archivos objeto generados para cada archivo fuente y crea el archivo ejecutable, orde.nando los segmentos de software y datos de manera adecuada. EI comando construir todo (Build All) reconstruye todos los archivos incluidos en el proyecto. Es similar al comando hacer, excepto que reconstruye todos los archivos, sean 0 no actuales, en los siguientes pasos: Borra los archivos de encabezado (.SYM), si existen Borra cualquier informacion de autodependencia registrada en el proyecto Pone en cero la fecha y la hora en todos los archivos objeto del proyecto Ejecuta el comando hacer El comando informacion (Information) muestra una caja de dialogo con las estadfsticas del archivo actual.
12
Fundamentos de Programaci6n en Lenguaje C
Con quitar mensajes (Remove Messages) se quita la ventana de mensajes de la pantalla de la computadora.
Menu de proyecto El menu de proyecto tiene los siguientes comandos: Crear 0 abrir un proyecto Cerrar un proyecto Agregar archivos al proyecto Borrar archivos del proyecto Establecer opciones de compilacion de un archivo en el proyecto Especificar el nombre del modulo objeto resultante, su ubicacion, si es un modulo de "overlay" y SI debe contener informacion de "overlay" Ver los archivos "include" de un archivo especifico del proyecto
Menu de depuraci6n Los comandos del menu de depuracion permiten especificar la informacion que se requiere verificar en la ventana de depuracion, y se dispone de los siguientes: Inspeccionar Evaluar/Modificar Invocar la Pila Observaciones Boton de puntos de pausa Puntos de pausa
(Inspect) (EvaluatelModify) (Call Stack) (Watches) (Toggle Breakpoint) (Breakpoints)
lnspeccionar (Inspect) abre una ventana de inspeccion, en la cual se examinan los valores que van tomando los elementos de datos. El tipo de elemento que se esta inspeccionando determina el tipo de informacion presentada en la ventana. Pueden inspeccionarse en Turbo C++ los siguientes tipos de datos: caracteres, enteros, reales, arreglos,. apuntadores, estructuras, tipos, uniones y funciones. Para abrir una ventana de inspeccion se coloca el cursor en el elemento de datos que se desea inspeccionar y se elige el comando inspeccionar 0 bien en el menu de depuracion, se elige el comando inspeccionar y se indica por medio de entrada por teclado, el concepto a inspeccionar. El estado 0 valor de dicho concepto se muestra en la ventana de inspeccion.
Introducci6n
13
La inspeccion se puede combinar con una corrida paso a paso, para verificar como ha variado el concepto a inspeccionar, al ejecutar una determinada instrucci6n del programa. Tambierr al terminar la ejecuci6n del programa. El comando observaciones (Watches) abre el menu de observacion que tiene los siguientes comandos: Agregar observaci6n Borrar observaci6n Edicion de observacion Eliminar observaciones
(Add Watch) (Delete Watch) (Edit Watch) (Remove All Watches)
Se pide la observaci6n de las variables 0 estructuras, del mismo modo que en la inspecci6n. La diferencia es que en el caso de las observaciones se esta vigilando constantemente como estan cambiando los valores 0 estados de la variable o dato a observar, y la inspecci6n s610 presenta la informaci6n cuando se pide. El comando evaluar y modificar (Evaluate and Modify) permite evaluar una expresion y/o modificar el valor de una variable 0 dato, durante una corrida paso a paso.
Menu de corrida Este menu tiene los comandos para correr los programas y para realizar las tareas de depuraci6n. Los comandos de que dispone son: Correr
(Run)
Reinicializar el programa
(Program Reset)
Ir al cursor
(Go to Cursor)
Traza a
(Trace Into)
Paso a paso
(Step Over)
Argumentos
(Arguments)
Corre el programa desde el inicio Reinida la ejecucion paso a paso Corre el programa hasta donde esta el cursor Corre el programa hasta la siguiente funcion Corre el programa instrucci6n por instruccion Indica los argumentos del programa
Para usar los comandos reinicializar, ir al cursor, traza y paso a paso se debe haber compilado y enlazado con la opcion de depuracion del menu de
14
Fundamentos de Programacion en Lenguaje C
depuracion puesta en "on". Si no se desea depurar el programa se puede quitar esta opcion.
Menu de opciones Este menu contiene comandos para ver y cambiar los panimetros de configuracion del ambiente de programacion del Turbo C++. Con el puede indicar el compilador a utilizar, de no desear el propio del ambiente; el enlazador, los directorios de trabajo, entre otras caracteristicas que Ie hagan sencilla su tarea de creacion de programas. La mayoria de los parametros se presentan mediante una caja de dialogo que faciIita la configuracion en cada caso. Las opciones del Inenu son: Aplicacion Compilador Transferencia Hacer Enlazador Bibliotecas Depurador Directorios ... Ambiente Salvar
(Application... ) (Compiler. .. ) (Transfer ... ) (Make ... ) (Linker ... ) (Librarian ... ) (Debugger... ) (Directories ... ) (Environment. .. ) (Save ... )
Menu de ventanas Contiene los comandos de manejo de ventanas. Las ventanas se pueden mover, amp li ar, poner en cascada, elegir la siguiente, cerrar, cerrar todas y listarlas. Como ya se comento, se puede tener abiertas tantas ventanas de edicion como se desee, por ejemplo, la de salida, la de mensajes del compilador, la de proyectos, la de registros y la de observacion.
1.4 HERRAMIENTAS METODOLOGICAS DE LA COMPUTACION En esta seccion se discuten de manera sucinta algunas metodologias que se han constituido en paradigmas de programacion. La programacion, ademas de ser una tarea de desarrollo tecnologico que requiere de los conocimientos cientifi-
Introduccion
15
cos de soporte del sistema que se necesita programar, es un arte que requiere de gran imaginacion y creatividad.
1.4.1 Programaci6n estructurada La estructura es el modelo 0 marco general con que se desarrolla un programa. Ella permite visualizar y entender mejor un algoritmo 0 programa y facilita la tarea de desarrollo y depuracion del mismo. Los elementos fundamentales de la estructura de un programa son: arquitectura, modularidad, funcionalidad de los distintos modulos, e infraestructura (bibliotecas y software reutilizable con que se cuente).
1.4.2 Especificaci6n La especificacion de un programa indica con todo detalle la tarea que se debe realizar. Por ejemplo, tratandose de una calculadora cientifica, la especificacion debe incluir: a. b. c. d. e.
f
Como debe ser la interfaz con el usuario: como presentar el menu de opciones, como adnlitir los datos, como presentar los resultados Que operaciones debe realizar Con que precision debe realizar las operaciones Como debe manejar los errores Que servicios de edicion de datos debe proporcionar al usuario Como se debe activar y desactivar, etcetera.
La especificacion establece la funcionalidad y define las caracteristicas del programa. Como otro ejemplo, consideremos el disefto e implementacion de un sistema de informacion. Cabe hacer notar que la especificacion de un sistema cualquiera, establece: a. b. c. d. e.
f
sus objetivos, las distintas necesidades de informacion que se requiere satisfacer, la distribucion fisica de las distintas entidades que deben utilizar el sistema, los datos que se deben capturar como entradas del sistema, las caracteristicas del equipo y de la interfaz de captura, la forma en que debe fIuir la informacion entre los distintos integrantes del sistema,
16
Fundamentos de Programaci6n en Lenguaje C
g. h. i.
las restricciones de acceso a la infonnacion, tanto al nivel de consulta como de modificacion, la rapidez con que se debe entregar la informacion, los elementos a incluir en los reportes y la precision y caracteristicas de la informacion que se requiere presentar, etcetera.
1.4.3 Arquitectura del programa La arquitectura de un programa es la concepcion global del mismo. Constituye el disefio de sistema del prograrna de computadora. Define a grandes bloques los elementos principales del sistema y su interrelacion, asi como la funcionalidad y caracteristicas de cada uno de esos bloques 0 modulos. Retomando el ejemplo de un sistema de informacion, su arquitectura estani integrada por la definicion estructural de los distintos modulos y la especificacion de como fluyen los datos y reportes entre cada uno de los integrantes del propio sistema. La figura 1.3 muestra la arquitectura de un sistema de informacion generico que tiene la filosofia cliente/servidor.
Estacion de Adquisici6n
Cliente de Consulta Cliente de Consulta
Cliente de Consulta
Cliente de Consulta
Figura 1.3: Arquitectura de un sistema de bases de datos cliente/servidor.
Introduccion
17
1.4.4 Diagrama de flujo El diagrama de flujo es otra importante herramienta metodologica. Su uso es muy adecuado para especificar detalladamente un algoritmo de solucion de un problema cualquiera, sea de ingenieria, matematico, etc. Por 10 anterior, su ambito de aplicacion ideal es en la especificaci6n de segmentos de programa 0 m6dulos que ejecutan tareas especificas, como pueden ser: un ordenanliento, una busqueda, la solucion de un sistema de ecuaciones, etc. A manera de ejemplo, la figura 1.4 muestra el diagrama de flujo del metodo de ordenamiento conocido como metodo de la burbuja.
~
T
< > J=\..lOPg 1
r
oftllalizar datos par (OSi hay desorden int °t .....- J
1 ~
:---
TCFE
~NO ~
J
Figura 1.4: Diagrama de flujo del algoritmo del me to do de la burbuja.
EI algoritmo del me to do de la burbuja para ordenar de manera creciente N elementos, funciona de la siguiente manera: 1) Para iniciar se asigna como TOPE, el elemento numero N del conjunto, se define e inicializa a cero una variable t que indica el ultimo elelnento intercambiado (sc intercambian los elementos para ordenarse); 2) Se realiza un proceso iterativo de analisis e intercambio de elementos desde el elemento 1 hasta el elemento TOPE-i, para ello se usa el indice J que se va incrementando hasta analizar todo el conjunto; 3)
18
Fundamentos de Programaci6n en Lenguaje C
Cuando se encuentra un par de elelnentos desordenados se intercambian y se Ie da a t el valor de J, al realizarse el intercambio; al terminar una iteracion t queda con el valor indice del ultimo elemento intercambiado; 4) Si al terminar la iteracion t=O, se termina el ordenamiento, si no, se da a TOPE el valor de J y se realiza otra iteracion. La figura 1.5 muestra los pasos de ordenamiento de siete numeros. Como se aprecia, los elementos se ordenan en cinco iteraciones. En la primera se lleva el 88 hasta la posicion de la extrema derecha y el tope queda ubicado en la posicion seis. En la segunda se lleva el 23, hasta antes del 88 y se recorre el TOPE a su izquierda. En la tercera se lleva el 19, en la cuarta el 16 y en la quinta y ultima iteracion quedan ordenados los numeros.
16 15 15 15 15 15 15 15 15 14
15 16 16 16 16 16 16 16 14 15
I
19 19 19 19 19 19 14 14 16 16
23 23 23 23 14 14 19 17 17 17
I
88 88 14 14 23 17 17 19 19 19
I
14 14 88 17 17 23 23 23 23 23
I
17 17 17 88 88 88 88 88 88 88
Figura 1.5: Ejemplo de ordenamiento por el metodo de la burbuja.
1.4.5 Especificacion en pseudocodigo La especificacion en pseudocodigo es otra herramienta metodologica de gran utilidad en el desarrollo de programas de computadora. Esta herramienta consiste en programar la estructura principal del programa, dividiendolo por modulos 0 funciones que realizan tareas especificas, las cuales se pueden especificar por medio de comentarios, en el lenguaje de programacion de trabajo; en nuestro caso el lenguaje C. Los detalles de programacion pueden realizarse despues con mayor facilidad, ya que se divide la tarea grande en tareas mas pequenas y manejables, como son los modulos 0 funciones. En e1 ejemplo 1.1 se muestra la especificacion en pseudocodigo de un programa que debe trazar una de las figuras geometricas 0 curvas que se indican en su menu, con los datos que Ie proporcione el usuario.
Introducci6n
19
Especificacion en pseudocodigo de un trazador de figuras geometricas
*/
Ejemplo 1.1: Trazador de figuras geometricas.
/*
int MENUO
{/* MENU presenta en la pantalla, el menu con la lista de figuras geometricas que puede desplegar el programa. Estas son: * Linea, dando el punto inicial y final * Circulo, dando el centro y el radio * Rectangulo, dando dos vertices * Polfgono, dando numero de lados y los vertices * Curva, dando una secuencia de puntos Funcion, dada su ecuacion y el rango de valores de la variable indep. * Elipse, dado su centro, longitud del eje mayor y del menor * Salir MENU entrega a su salida un numero entero que representa la seleccion realizada.
*
*/ } int LINEAO
{
/* Obtiene las coordenadas de /* Traza la linea */
los puntos inicial y final
*/
/* Retorna un valor negativo y un mensaje de error, si las coordenadas estan fuera de rango (0, simplemente, "Mensaje de error" en 10 sucesivo) */
} int CIRCULOO
{ /* Obtiene las coordenadas del centro y el radio * / el drculo */ /* Mensaje de error* /
/* Traza }
int RECTANGULOO
{
/* Obtiene las coordenadas de los vertices * / /* Traza el rectangulo * / /* Mensaje de error * / } int POLIGONOO
{
/*
Obtiene el numero y las coordenadas de los puntos * / /* Traza el Polfgono*/ /* Mensaje de error */
}
20
Fundamentos de Programaci6n en Lenguaje C
int CURVAO { /* Obtiene el numero y las coordenadas de los puntos */ 1* Traza la curva *1 1* mensaje de error *1 } int FUNCIONMATO { 1* Presenta un menu de funciones y pide la selecci6n*1 1* Obtiene los valores lImite de los parametros */ 1* Traza la curva correspondiente *1 /* mensaje de error *1 } int ELIPSEO { 1* Obtiene las coordenadas del centro y la longitud del eje mayor y del menor*I /* Traza la elipse * / 1* mensaje de error *1 } int mainO { int SALIR = 0; while (!SALIR) { 1* Presenta el MENU PRINCIPAL *1 if (J*Seleccion = Salir *1) SALIR =1; 1* Ejecuta la funcion correspondiente al MENU } }
Es evidente que hay que hacer la programacion detaIl ada de cada uno de los modulos de programa indicados como funciones en la especificacion, sin embargo, es obvio que la estructura del programa y las indicaciones de que debe hacer, ya estan contenidas en la especificacion en pseudocodigo (codigo en lenguaje C y comentarios).
1.4.6 Software reutilizable Una pnictica que ha dado resultados excelentes en el desarrollo de la computacion ha sido trabajar con el objetivo de ir construyendo sistemas basados en una infraestructura que va quedando como resultado del trabajo y la experiencia. El desarrollo del software con la vision de poderlo reutilizar en otros proyectos, ha
Introducci6n
21
pennitido construir poderosos ambientes de programaci6n como el que ha ido creandose alrededor de los lenguajes de programacion como FORTRAN y C. Las bibliotecas de estos lenguajes, que estan disponibles tanto en los ambientes de desarrollo propios (dellenguaje), como en paquetes de software adicionales, son verdaderamente impresionantes, ya que permiten desarrollar cosas que en otros tiempo~ hubiera sido increible el poderlas realizar. EI concepto de reutilizacion, consiste en el desarrollo de software de manera modular, en fonna de funciones, estructuras y modulos, los cuales pueden ser usados de manera sencilla, conociendo la especificacion precisa de los parametros de entrada con los que trabajan dichas funciones y las salidas 0 efectos que producen en el ambiente dichas funciones. Una funci6n 0 segmento de programa, puede verse como un modulo 0 susbsistema que tiene un conjunto de entradas, un conjunto de efectos sobre el ambiente de programacion y una salida que es el valor retomado, como se indica esquematicamente en la figura 1.6. Los modulos de programa generados con la filosofia de la figura 1.6, pueden quedar <.!isponibles como funciones, macros 0 estructuras de biblioteca, como software reutilizable.
ENTRADAS Parametros Variables Globales Estructuras Globales Perifericos Archivos
--------------1> -----1>1
SEGMENTO DE PROGRAMA
----t>
Figura 1.6: Modulo
0
SALIDAS Valor Retornado Variables Globales I-----I-c>:rstructuras Globaies Perifericos f-----+ Archivos ----------~
segmento de programa.
1.4.7 Programacion orientada a objetos EI concepto de software reutilizable se puede llevar a un grade de modularidad, tal que los modulos generados puedan manejarse como componentes u objetos con los cuales puedan construirse sistemas complejos. Un objeto es una estructura de datos que tiene asociado un conjunto de funciones 0 acciones que pueden realizarse sobre dicha estructura, y son construidos de manera tal que puede decirse que los datos y funciones se encuentran empaquetados como integrantes de un objeto, como si fueran componentes de una clase 0 tipo determinado. Al conjunto de funciones y operaciones que se Ie pueden aplicar al objeto, suele llamarsele el comportamiento.
22
Fundamentos de Programacion en Lenguaje C
Asi como hoy se construyen aparatos electronicos empleando componentes (chips, transistores, resistencias y demas componentes que pueden tambien emplearse para construir otros aparatos), en el software es posible tener objetos modulares con los cuales se puede integrar un sistema, como pueden ser menus, menus grMicos, ventanas, iconos, ventanas de dialogo, grMicas, hojas de calcu10, tablas, textos, sonidos digitales, imagenes, manejadores de raton, etcetera. En el ambiente de programacion orientada a objetos, los tipos de objetos se conocen como "c1ases", y los objetos son instancias de la c1ase, en el mismo sentido que las variables 0 estructuras son instancias de un tipo de datos. Orientado a objetos significa que el software se organiza como una coleccion de objetos definidos por su estructura de datos y su comportamiento (funciones y operaciones que pueden realizarse sobre el objeto). Los objetos tienen identidad (un objeto es diferente de cualquier otro, aun del mismo tipo de clase). Ademas, pueden construirse objetos complejos teniendo como base otros objetos, mediante el concepto de herencia. Para definir 0 especificar un objeto determinado, se establece primeramente su c1ase, esta especifica la estnlctura de datos y las funciones que pueden actuar sobre dicha estructura. Como ya se comento, los objetos se dec1aran como "instancias" de la c1ase especificada. La programacion orientada a objetos es actualmente una poderosa herramienta de desarrollo de software, que no solo ha influido grandemente en la construccion de sistemas de informacion y de otras herramientas para el desarrollo, sino que ha estado llegando a las aulas de cada vez mas escuelas en todo -el mundo. Sale del alcance de este libro la explicacion de la programacion orientada a objetos, sin embargo, se considera recomendable que antes de aprender algun lenguaje de programacion orientado a objetos, se conozca y se haya experimentado con un lenguaje como el C, estudiado en esta obra. Previendo esta situacion cuando se habla de tipos de datos, tambien se mencionan sus operaciones asociadas, para dar asi, una posible concepcion de "objeto".
1.5 LA COMPUTADORA Y LAS TELECOMUNICACIONES Comunicaciones y computacion han fonnado un binomio que en los ultimos quince anos han cambiado radicalmente la forma de trabajar y de vivir de nuestra sociedad. Hoy en dia, la computadora es indispensable en toda oficina, institucion educativa, comercio, centro de desarrollo 0 de asesoria de cualquier tipo, pero tambien ya es indispensable que toda computadora este conectada a las redes de transmision de datos, propias de la empresa 0 institucion, 0 bien a redes globales.
Introduccion
23
Internet ha sido un importante acontecimiento, pues ha pennitido interconectar pnicticamente a toda la comunidad mundial, empleando nuevas fonnas de comunicacion, como son: el correo electronico, los boletines electronicos, los bancos de infonnacion y sus buscadores asociados, el web (los escaparates electronicos y las hojas de presentacion institucional), los grupos de amilisis y discusion, los c1ubes de famiticos, los grupos 0 comunidades de investigaci6n y desarrollo, etcetera. De manera que comunicaciones y computacion han constituido una simbiosis de gran impacto en el desarrollo de la sociedad. Han penetrado juntas en casi todos los ambitos, a traves de herramientas modernizadoras que hacen mas eficiente la labor de los seres humanos. Por medio de ellas se han constituido sistemas nacionales e internacionales, como los sistemas bancarios, apoyados en los cajeros automaticos y sistemas de banca electr6nica; las redes de distribuci6n comercial, los sistemas de telereservaciones de transportes y servicios, etcetera. Conceptos como los de redes locales, redes metropolitanas, redes de area amplia, red digital de servicios integrados, red digital integrada, etc., generalizan su aplicacion en todos los ambitos, pennitiendo tender la infraestructura basica para implantar los sistemas distribuidos.Por otro lado, las comunicaciones han experimentado una marc ada tendencia hacia la digitalizacion, en la cual la computadora en sus diversas instancias (grandes procesadores, microprocesadores, microcontroladores, procesadores digitales de sefiales, entre otros), se ha constituido en el componente basico para el desarrollo de equipos y sistemas. Equipos como las centrales de conmutacion, o conmutadores, nodos de transmision de datos, multiplexores y concentradores, enrutadores, centrales de mantenimiento y administraci6n de redes, servidores de s~rvicios de comunicaciones e infonnacion, etc., son desarrollados por medio de una 0 varias computadoras integradas. Adicionalmente, hoy en dia coexisten redes digitales que manejan una gran diversidad de infonnacion, por ejemplo: telefonia, facsimil, sonido, animaciones, videoconferencia, television, etcetera. Puede parecer evidente al lector que en los diversos campos y actividades a que se ha referido en esta seccion, la programacion de las computadoras, el desarrollo del software, representa una de las actividades mas criticas e importantes. Y particularmente, la programaci6n en lenguaje C ha sido de gran importancia en el desarrollo de equipos y sistemas de control y de telecomunicaciones.
24
Fundamentos de Programacion en Lenguaje C
Preguntas suplementarias 1.1 Explique que es un Registro de un Procesador 1.2 Explique el Modelo de computadora de programa almacenado 0 modele de Von Newmann 1.3 Diga que es un puerto de entrada/salida 1.4 l,Que es un lenguaje de alto nivel? 1.5 l,Que significa lenguaje libre de contexto? 1.6 Explique que es la memoria principal de una computadora 1.7 Explique que es un sistema operativo 1.8 Explique el proceso que realiza una computadora cuando se Ie comanda para que ejecute un programa 1.9 Explique como se lleva a cabo el proceso para almacenar un archivo en un disco magnetico.
Capitulo 2
EL LEXICa DEL LENGUAJE
C
El16xico de un lenguaje de programacion, es el conjunto de palabras y simbolos con que se construyen las instrucciones y programas en dicho lenguaje. En la tenninologia de desarrollo de lenguajes de programacion, se dice que cada uno de esos elementos es un "token" 0 "elemento semantico". Como ya se puntualizo en el Capitulo 1, los lenguajes de programacion se utilizan para especificar en fonna sistematizada y ordenada las tare as que debe realizar una computadora para resolver un problema. En este capitulo estudiamos el lexico dellenguaje C.
2.1 TIPOS DE "TOKENS" En C, se tienen diferentes tipos de elementos semanticos pueden agrupar en los siguientes seis grandes gropos:
0
tokens, los cuales se
Separadores Constantes Identificadores Operadores Palabras clave Con estos elementos semanticos se construyen las distintas partes de que consta un programa.
2.2 SEPARADORES 2.2.1 Espacios, saltos tabulares, cambios de reng16n Para el lenguaje C, los espacios en blanco, los saltos tabulares y los cambios de renglon son separadores de elementos semanticos. Estos elementos no tienen
26
Fundamentos de Programacion en Lenguaje C
otro significado para C que el de separadores y por 10 tanto no causan ningun otro efecto en el programa especificado; a diferencia de otros lenguajes, como el BASIC 0 el FORTRAN, en los cuales el cambio de renglon representa el terminador de una instruccion. No obstante 10 anterior, no debe inc1uirse cambios de renglon 0 tabuladores como elementos de una cadena de caracteres, ya que se tiene un formato especial para representar estos elementos dentro de la cadena, como se vera mas adelante en este mismo capitulo. En el ejemplo 2.1 presentado a continuacion, se muestra como el cambio de renglon, los tabuladores y los espacios en blanco, pueden usarse para separar tokens, sin producir un efecto indeseable en el programa. En el ejemplo se usan tabuladores para dar mejor presentaci6n al programa, cambios de reng16n y comentarios intennedios en una dec1aracion de cadenas de caracteres, espacios en blanco entre los elementos semantic os de una funcion, en este caso una funci6n de salida estandar, y tambien se inc1uyen comentarios intennedios entre los tokens de esta misma funcion. EI lector puede compilar este programa y comprobar que los cambios de renglon, tabuladores y espacios en blanco, unicamente tienen la funci6n de separadores de tokens. Ejemplo 2.1: Uso de los cambios de rengl6n, tabuladores y espacios en blanco. mainO
{ NOMl[] = "JUAN CARLOS",
char
/*
Esta es una declaracion
*/
NOM2[] == "JULIO CESAR"; clrscrO; printf
("HOLA NO ECHEN
RELAJO %s %5 PORQUE .... ", /* Esta, es una funcion de salida estandar */
NOMl, NOM2); return (0);
}
2.2.2 Comentarios EI lenguaje C pennite introducir comentarios por medio de los caracteres /* y */. Los comentarios son tratados como espacios en blanco y pueden aparecer en cualquier parte del programa en donde pueda "escribirse" un espacio 0 un cam-
EI Lexica del Lenguaje C
27
bio de renglon, como se ilustra en el ejemplo 2.1. Algunos compiladores no permiten que se aniden comentarios, es decir, que aparezca un comentario encerrado con las parejas /* y */ dentro de otro comentario igualmente encerrado. El diagrama lexicografico del comentario aparece en la figura 2.1 a continuacion:
Figura 2.1: Diagrama lexicografico del comentario.
Los comentarios se usan para documentar el programa, de manera que sea mas flicil de leer y entender, tanto por el programador original, como por otros programadores que mas adelante pudieran revisar, corregir 0 simplemente utilizar ese programa. Otros ejemplos de comentarios son los siguientes:
/* -------------------------------------------------------------------------------------DEFINICION DE PARAMETROS DE TRABAJO
--------------------------------------------------------------------------------- */ 1***************************************************** ********
* * *
Programa principal: Protocolo de Comunicacion PPP Elaborada par: Juan H. 1. (SistematiCO) Fecha: 10 de septiembre, 1998
* *
*
****************************************************** *******1 /* DEFINICION DEL TAMANO DE LOS BUFERS
*/ #define TBUFT
10
/*
Tamafio del bufer de transmision
*/
El compilador de Turbo C++ tambien acepta comentarios de final de linea precedidos por dos diagonales (II), como en el siguiente ejemplo: #define TBUFT
10
/1 Tamafio del
bufer de transmision
28
Fundamentos de Programacion en Lenguaje C
2.2.3 El terminador de instrucci6n Cualquier instruccion (;). Ejemplos:
0
declaracion en C debe tenninarse con un punto y coma
char COMAN DO;
II Declaracion de variable tipo char
printf(" ALTAS: A \n"); II Funcion de bibliateca para printf(" BAJAS: B \n"); II imprimir en pantalla printf(" CAM BIOS: C \n"); printf(" SALIR: S \n\n"); printf("\n\n\n INDICA LA OPERACION QUE DESEAS REALIZAR:"); COMANDO = getcharC); while (getcharO != '\n');
/ / Instruccion asignacion /I Instruccion while
2.2.4 Las Haves { y } Las Baves {... } se utilizan para crear instrucciones compuestas, 0 bloques de instrucciones, de manera que sintacticamente sean consideradas por el compilador de C como el equivalente a un instruccion simple. La instruccion compuesta construida en esta fonna no tiene que terminarse con punto y coma (;), es decir, despues del canicter}, no debe, forzosamente, ir un punto y coma. Como ejemplo, a continuaeion se muestra una instruccion if, a la eual esta asociada una instruccion compuesta que se ejecuta si se cumple la expresion relacional AB) {
II Instruccion compuesta MAX = A; printf ("Ahara el maximo es %d\n", MAX);
}
II ... 2.2.5 Operadores En el contexto de una expresion, los operadores aritmeticos, relacionales, logicos, etc., ademas de su funcion como operador en el contexto semantico, fungen :omo separadores de los distintos elementos de la expresion. En el tema 2.6 se presentan los operadores, desde el punto de vista lexico, y en el Capitulo 4 se estudia su funcionamiento y caracteristicas semanticas.
EI Lexico del Lenguaje C
29
2.3 CONSTANTES En C es posible manejar tipos diferentes de constantes, como son: enteros numeros de punto flotante caracteres, Y cadenas En el diagrama lexicografico de la figura 2.2 se especifica la construcci6n de los distintos tipos de constantes numericas utilizadas en C; en la figura 2.3, el diagrama lexicografico de la constante "caracter", y en la figura 2.4, el diagrama de la constante cadena. En el Capitulo 4 se estudian con detalle los tipos de datos correspondientes a estos cuatro tipos de constantes.
2.3.1 Enteros Las constantes enteras pueden expresarse en cuatro diferentes formas: octal hexadecimal decimal, y entero de 10ngitud grande Las forma en que se expresan estas cuatro modalidades de enteros, es la siguiente: Octal. El numero debe comenzar con un cero. Ejemplos:
07
017
02177
02234
Hexadecimal. El numero debe empezar con los caracteres OX, 6 Ox. Ejemplos:
OxFF
OXFFFF
OXIA
OXIIFF
Decimal. El numero debe empezar con un digito dell a19. Ejemplos: 1986
12
4
2234
Fundamentos de Programacion en Lenguaje C
30
Enteros de longitud grande: El numero debe terminar con la letra I 0 l (mayuscula 0 minuscula). Ejemplos: 01
OxFFFFFFI
02234l
07l
2.3.2 Numeros de punto flotante Una constante de punto flotante consiste de una parte entera, un punto decimal, una parte fraccionaria, una e 0 E, Y un exponente entero con signo opcional. Puede no usarse la parte entera 0 la fraccionaria (no ambas a la vez) y el punto decimal 0 el exponente. Ejemplos: 1.5
2e-3
1.7S4E-S
7.345e3
Figura 2.2: Diagrama lexicografico de la constante numerica.
2.3.3 Caracteres Una constante tipo caracter, no es otra cosa que un caracter encerrado entre comillas sencillas (' .,. '). Ejemplos: 'x'
'I'
'Y'
'X'
'$'
Algunos caracteres especiales del lenguaje C, se representan de la siguiente manera: Linea Nueva Retroceso Tabulador Retorno de Carro
IF BS HT CR
'\n' '\b' '\t' '\r'
EI Lexico del Lenguaje C
Nueva pagina "slash" Comilla Nulo Patron de bits
FF
'\f'
\
'\\' '\' ,
I
31
'\0' '\ddd'
Los digitos ddd especifican el canicter deseado (en octal). Vease en la figura 2.3, el correspondiente diagrama lexicognifico. En dicho diagrama, el especificador es el caracter 0 patron de bits indicado en la !ista anterior.
---o~1
r
caracter
1_-8-1f.L
especificador
~o-
IJ
Figura 2.3: Diagrama lexicognifico de la constante canicter.
2.3.4 Cadenas Una cadena se expresa como una secuencia de caracteres entre comillas dobles (" ... "). Ejemplos: "Nombre de pila\n"
"Ocupacion \n"
Una cadena es un arreglo de caracteres y puede manejarse como arreglo en el contexto de un programa. Sin embargo, una constante no puede modificarse en el transcurso del programa.
i---'-~O·-
1-8-1
especificador
Figura 2.4: Diagrama lexicografico de la cadena.
32
Fundamentos de Programacion en Lenguaje C
2.3.5 Constantes simb61icas Por medio de la directiva de compilaci6n #define utilizada al inicio del prograrna, pueden definirse referencias simbolicas a constantes de cualquiera de los tipos referidos en las cuatro secciones anteriores. Vease el ejemplo mostrado a continuacion:
/* Ejemplo de definicion de constantes simbolicas */ #define #define
NOMBRE EDAD
"ROLANDO MENCHACA" 22
EI compilador, antes de realizar su tarea de compilacion, sustituye la cadena de la derecha, siempre que encuentra el nombre simbolico de la izquierda. Vease el diagrama sintactico de este tipo de construccion en la figura 2.5.
-1
caract.r
h I
---I>'0B
identlficador-
cadena
e----
numero
Figura 2.5: Diagrama sintactico de la definicion simbolica de una constante.
2.4
IDENTIFICADORES
2.4.1 Modo de construcci6n de un identificador Los identificadores se utilizan para designar los distintos tipos de variables, campos, constantes simbolicas y funciones que es po sible manejar en C. Un identificador puede ser cualquier secuencia de letras 0 digitos que comience con una letra. El caracter '_' puede usarse como cualquier letra. Para C, las letras mayilsculas son diferentes de las minusculas. Solo los primeros caracteres son significativos para distinguir entre un identificador y otro,
EI Lexico del Lenguaje C
33
aun cuando el identificador puede tener cualquier longitud, la cantidad de caracteres significativos depende del compilador en particular, en Turbo C++ es un panimetro que se puede configurar. Vease el diagrama lexicognifico del identificador en la figura 2.6. En ellenguaje C las palabras clave deben escribirse con minusculas. Lo anterior se debe a que para C las letras mayusculas son diferentes de las mimisculas, y por 10 tanto es diferente un identificador escrito con mayusculas de otro escrito con minusculas. POl' ejemplo, las siguientes variables son todas diferentes: Nombre_de_Pila NOMBRE_DE_PILA nombre_de_pila Nombre_De_Pila NombreDePiia NOMBRE_de_PlLA nombre_De_pila
------~.~--~~------~--------~~----------
I_-B
-,
Figura 2.6: Diagrama lexicografico del identificador.
2.4.2 Atributos de un identificador Son atributos de un identificador, la clase de almacenamiento que tiene asociado y el tipo de dato 0 significado semantico de las secuencias de bits encontradas en el almacenamiento reservado para el identificador.
Clase de almacenamiento La clase de almacenamiento asociada con un identifieador detennina su localizaei6n y tiempo de vida. Hay cuatro clases de almaeenamiento declarables, 10 eual detennina diferentes tipos de variables: automatieas, estatieas, externas y registro.
34
Fundamentos de Programaci6n en Lenguaje C
En el Capitulo 3 se describini el campo de aplicacian y demas caracteristicas de estos distintos tipos de variables.
Tipo dedato Como ya se indica, el tipo de dato es el significado que tiene una secuencia de bits almacenada en cierta localidad de memoria. Por ejelnplo, el "caracter" es un tipo de dato de ocho bits de longitud, con el que se pueden representar las letras mayusculas y minusculas del alfabeto, los digitos decimales, los signos de puntuacion gramatical, los operadores matematicos, etc. El "entero" es un tipo de dato, tipicamente de 16 bits, con el cual se pueden representar numeros enteros en el rango de -32768 a 32767. Ellenguaje C soporta varios tipos de datos que son: caracteres enteros enteros sin signo numeros de punto flotante numeros de punto flotante, de doble precision arreglos funciones apuntadores estructuras uniones, y archivos Ademas, los metodos de construccion de objetos cOlnplejos como los apuntadores, arreglos, estructuras y uniones, pueden conjugarse incluso de manera recursiva. En el Capitulo 5 se estudian los tipos de datos y las operaciones y funciones que pueden realizarse con ellos.
2.4.3 Uso del mismo identificador para diferentes tipos de elemento Los identificadores asociados con variables ordinarias, asi como los asociados con nombres de miembros de estructuras 0 uniones, y los asociados con los nombres de las estructuras 0 uniones, forman clases disjuntas de identificadores. Los nombres de declaraciones de tipo, con la palabra clave typedef, pertenecen a la misma clase que los identificadores ordinarios.
EI Lexico del Lenguaje C
35
De este modo, un mismo identificador puede usarse, bajo el mismo ambito 0 alcance, para diferentes aplicaciones, con algunas restricciones; por ejemplo, es ilegal dec1arar una estructura 0 union que contenga una instancia de ella misma, pero se permite que la estructura 0 union tenga apuntadores a instancias de las mismas. (Vease en la seccion 3.2.2 la explicacion del alcance 0 ambito de aplicacion de la dec1aracion de una variable.) Si el alcance 0 ambito de aplicacion de las variables es diferente, es decir, si no se aplican en el mismo segmento de programa (funcion 0 instruccion compuesta), entonces, el mismo identificador puede usarse para variables ordinarias diferentes (vease la seccion 3.2.2.).
2.5 QPERADORES Ellenguaje C soporta varios tipos de operadores que podemos c1asificar bajo los siguientes rubros: operadores aritmeticos operadores relacionales operadores logicos operadores logicos al nivel de bit operadores de asignacion operadores de manipulacion de datos en bajo nivel operadores de expresion primaria, y operadores especiales A continuacion se listan los distintos tipos de operadores y su significado semantico en C. La descripcion de las correspondientes operaciones aritmeticas y logicas se presenta, conjuntamente con la descripcion de los tipos de datos a los cuales son aplicables los diferentes operadores, en el Capitulo 5.
Operadores aritmeticos Negativo Suma Resta Multiplicacion Division Residuo
+
* I %
36
Fundamentos de Programaci6n en Lenguaje C
Operadores relacionales Menor que Mayor que Menor que 0 igual a Mayor que 0 igual a Igual que Diferente de
< > <=
>=
!=
Operadores logicos Negacion Funcion 0 Funcion Y
II &&
Operadores logicos al nivel de bit (compuertas logicas) Complemento a 1 Funcion 0 Funcion Y Funcion 0 Exclusiva
I & A
Operadores de asignacion Asignacion si~ple Incremento aritmetico Decremento aritmetico Incremento geometrico Decrementogeometrico Modulo Corrimiento a la derecha. Corrimiento a la izquierda Ventana 0 logica Ventana Y logica Ventana 0 Exclusiva
+=
*= /= %=
»= «=
1= &=
EI Lexico del Lenguaje C
37
Operadores lipo lenguaje ensamblador Corrimiento a la derecha Corrimiento ala izquierda Indireccion Apuntador Incremento geometrico Decremento geometrico
» «
* &
*= /=
Operadores de expresi6n primaria Apuntador Funcion Arreglo Campo (modo indirecto) Campo (modo directo)
*
() [] ->
Operadores de funciones especia/es Conversion de tipos (casta) Expresion condicional Expresion intermedia
(tipo) ?:
En el Capitulo 5 se explica la forma en que se usan los distintos operadores, en conjuncion con la descripcion de la fonna en que deben hacerse las declaraciones de los tipos de datos que se pueden manejar en ellenguaje C.
2.6 PALABRAS CLAVE Las palabras clave son los elementos semanticos base de las instrucciones y declaraciones que son posibles en un lenguaje como C. Las palabras clave no deben usarse como identificadores. A continuacion se presenta una lista de las palabras clave, basicas de C.
38
Fundamentos de Programacion en Lenguaje C
int char float double struct union long short unsigned auto extern register static typedef goto return sizeof break continue
if else for do while switch case default
tipo entero tipo caracter tipo punto flotante tipo doble precision Estructura Union tipo entero de longitud grande tipo entero de longitud pequefia tipo entero sin signo variable automatic a variable externa variable registro variable estatica definicion de tipo de dato instruccion salta incondicional instruccion dato a retomar funcion tamafio de objeto instruccion interrupcion instruccion continuar instruccion condicional subinstruccion "en caso contrario" instruccion ciclo instruccion ciclo con plueba al final instruccion ciclo con prueba al inicio instruccion salto condicional multidireccion etiqueta para especificar un caso posible con la instruccion switch etiqueta para todos los demas casos no especificados que pudieran ocurrir, en una instruccion switch
En el Capitulo 4 se explica la sintaxis y la fonna en que se usan las distintas instrucciones dellenguaje C.
EI Lexico del Lenguaje C
39
Ejercicios 2.1 2.2 2.3 2.4 2.5 2.6
Explique que es un operador loPara que se utilizan los comentarios en un programa en lenguaje C? loPara que se usan el punto y coma (;) y las Haves en lenguaje C? Explique desde el punto de vista computacional que es una constante Explique que es un canicter y que una cadena loCmil es el valor decimal de los siguientes mimeros en base octal? 07, 017, 12177 Y 02234
2.7
loCual es el valor decimal de los siguientes numeros en base hexadecimal? OxFF, OXFFFF, OX1A, OXllFF, Ox7L, Ox2234L
2.8 2.9 2.10 2.11
Presente la tabla de valores ASCII estandar. Diga que entiende por clase de almacenamiento y que por alcance de una variable. loSe puede usar el mismo identificador para diferentes datos, variables u objetos, en un mismo programa? Calcule las siguientes operaciones: (125 % 27) % 2 ! (255) 255 I 255 1000 A 1000
2.12
Si A = 10 calcule las siguientes operaciones: A += 17 A *= 25 A %= 10 A «= 2 A&= 8
2.13 2.14
(5 && 0) II (3) '" (255) 255 & 255
A -= 100
A/= 2 A »= 2 AI 8 A A= 8
Presente los valores maximos de los numeros: enteros sin signo, enteros, punto flotante y punto flotante de doble precision, en C estandar. loCuantos y cmiles tipos de datos estandar maneja ellenguaje C?
Capitulo :3
ESTRUCTURA DE UN PROGRAMA EN C En este capitulo se describe la estructura general de un programa en lenguaje C, con el objetivo principal de que el lector identifique las partes principales que debe tener un programa y pueda planificar la estructura de los programas que va a desarrollar. Particulannente, debe planificar y luego declarar las estructuras de datos principales que va a necesitar, dividir su proceso computacional en modulos que puedan ser desarrollados por medio de las funciones de C y definir, a traves de la funcion main, la manera en que se van a interrelacionar los distintos elementos para llevar a termino y hacer funcionar correctamente el programa.
3.1 ESTRUCTURA GENERAL Un programa en C esta compuesto por una secuencia de declaraciones de datos y estructuras de datos y de un conjunto de definiciones de funciones. Una de esas funciones debe ser main. Es usual que esta funcion sea la primera en ser definida, y antes de ella se declaran todas las variables 0 estructuras de datos globales (las que son aplicables durante toda la ejecucion del programa), as! como las distintas funciones con el tipo de dato que fetoman y el tipo de parametros. Despues de main se definen las distintas funciones de que consta el programa. Un programa en C, al menos debe constar de la funcion main. En la figura 3.1 se muestra e1 diagrama sintactico de la estructura general de un programa.
dec!araci6n de datos
Definici6n de funci6n
Figura 3.1: Diagrama sintactico de un programa en C.
42
Fundamentos de Programaci6n en Lenguaje C
A continuacion describiremos la estructura general de las declaraciones de tipos de datos y de las definiciones de funciones.
3.2 ESTRUCTURA DE UNA DECLARACION 3.2.1 Estructura general de una declaraci6n Una declaracion en C consta de un conjunto de especificadores y una lista de declaradores con sus inicializadores opcionales (vease el diagrama sintactico de la figura 3.2). Los especificadores pueden ser de clase de almacenamiento y/o de tipo de dato. Los declaradores constituyen los datos, variables 0 estructuras de datos a ser declarados, y los inicializadores constituyen los valores iniciales de las variables 0 estructuras de datos declaradas.
Especificador de Clase de Almacenamiento
------"L-t>j
Especificador d tip 0 d e d at 0
Figura 3.2: Diagrama sintactico de una declaracion.
3.2.2 Especificadores de c1ase de almacenamiento y a1cance Los especificadores de clase de almacenamiento determinan el tiempo de vida, alcance 0 ambito de aplicacion de las distintas variables declaradas. Se dispone de los siguientes especificadores de clase de almacenamiento en lenguaje C: auto static register extern typedef
variable automatic a variable estatica variable registro variable extema definicion de tipo
Estructura de un Programa en C
43
Excepto typedef y extern, estos especificadores Ie dan al compilador Ia clave para que asigne el tipo de memoria que se requiere para una determinada variable. En el caso de extern debe existir una dec1aracion externa de los correspondientes identificadores y por 10 tanto la asignacion de memoria requerida para elIos. Por dec1aracion externa se entiende una declaracion aparecida en un segmento de programa compilado por separado, 0 simplemente fuera de la funcion en que se declaro la variable extema (variable global).
Especificador de clase de Almacenamiento
- - - - - - - - - - -... 1Especificador de Tipo def--_ __ Dato
Figura 3.3: Diagrama sintactico del especificador.
El especificador typedef no implica que se reserve memoria, sino que permite crear castas 0 tipos complejos de datos; aunque semanticamente es diferente de los otros especificadores de c1ase de almacenamiento, sintacticamente es equivalente (ocupa la misma posicion en el contexte de la declaracion). En la figura 3.3 se muestra el diagrama sintactico del especificador. Una declaracion solo puede tener uno 0 ningun especificador de clase de aImacenamiento. Si no contiene especificador de clase de almacenamiento se considera que la variable es externa, si la dec1aracion se realizo en el segmento de declaraciones externas (globales) del programa. Si la declaracion se realiza en el interior de una funcion, por omision se considera que la variable es automatica. En el segmento de declaraciones externas solo pueden declararse variables externas 0 estaticas. Las declaraciones de variables tipo registro, solo son aplicables a tipos de datos que dependen de la implementacion (por ejemplo: enteros, caracteres 0 apuntadores). Empleando apropiadamente las declaraciones de variables registro, pueden construirse program as mas rapidos.
44
Fundamentos de Programaci6n en Lenguaje C
Alcance 0 ambito de un identificador 0 variable Existen dos clases de alcance 0 ambito de un identificador. Ellexicognifico y el semantico. EI ambito lexicografico detennina la region del programa en que puede emplearse el identificador sin que el compilador considere que esta indefinido (no se ha declarado) y que por 10 tanto exista error. El ambito lexicografico de las variables declaradas como extemas se extiende desde el lugar de la declaraci6n, hasta el final del archivo en el cual aparece la declaraci6n; el de los parametros formales se mantiene en toda la funci6n a la que pertenecen; el de los identificadores declarados dentro de un bloque (instrucciones encerradas entre Haves { ... }), se mantiene en todo el bloque; el de una etiqueta, abarca toda la funci6n en la cual aparece. En el inicio del bloque de c6digo- perteneciente a una funci6n 0 de cualquier otro bloque de c6digo, puede usarse un identificador anterionnente usado en una declaraci6n extema como variable local y en ese caso, la declaraci6n extema deja de aplicarse durante todo el bloque de c6digo correspondiente. Es conveniente recordar que los identificadores asociados con variables ordinarias, constituyen clases disjuntas con los asociados con estructuras 0 uniones o con los asociados con miembros de estructuras 0 uniones (vease Secc. 1.4.3), por 10 que puede usarse un mislno identificador para dos 0 nlas propositos, en el mismo segmento de c6digo. Vease tambien la declarad6n de estructuras 0 uniones en las secciones 5.6 y 5.7. EI ambito semantico se refiere principalmente a la regIa de que las referencias al mismo identificador extemo, son referencias al mismo objeto. En un programa repartido en varios archivos s610 debe haber una declaraci6n del identificador de una variable global que no haya sido calificada con el especificador extern; en todas las demas declaraciones debe usarse dicho declarador para referirse al objeto correspondiente. Si un identificador se declara como estatico, por medio del especificador static, el identificador no sera visible en otros archivos. Las funciones tambien pueden ser declaradas como estaticas.
3.2.3 Especificadores de tipo de dato . Los especificadores de tipo de dato, detenninan el tipo de dato 0 elemento de informaci6n que contendra el area de memoria reservada para las variables declaradas por medio de la declaraci6n en proceso. Vease en Ia figura 3.3 el diagrama sintactico del especificador. Podemos clasificar los tipos de datos en dos grandes grupos: datos basicos y datos compuestos.
Estructura de un Programa en C
45
Tipos de datos basicos Los tipos de datos basicos que maneja C y las palabras clave que constituyen sus correspondientes especificadores, son los siguientes: Tipe
Palabra
caracter entero de longitud estandar entero de longitud grande entero de longitud pequefia entero sin signo punto flotante (real) punto flotante doble precision apuntadores
char int long int short int unsigned float double
0
simbolo clave
*
En el Capitulo 5 se analizan con detaUe los distintos tipos de datos y las operaciones y funciones de biblioteca mas usuales que son aplicables a dichos tipos de datos. Los apuntadores se declaran anteponiendo al identificador 0 declarador el simbolo *. El tipo de dato al eual sefiala el apuntador es determinado por medio del correspondiente especificador.
Tipos de datos compuestos Ellenguaje C maneja varios tipos de datos compuestos, 0 estructuras, que agrupan varios datos de un mislno tipo 0 de tipos diferentes, los tipos compuestos mas simples son: Tipo
Palabra
arreglo estructura union archivo
[] struct union
0
simbolo clave
FILE
Estos tipos base pueden combinarse para formar estructuras mas complejas. Por medio del simbolo *, utilizado incluso en forma recursiva, se pueden declarar apuntadores a estructuras complejas, 0 estructuras complejas que contengan apuntadores 0 elementos constituidos porapuntadores. Pueden tambien crearse archivos que contengan secuencias de estructuras complejas 0 de tipos de datos basicos.
46
Fundamentos de Programacion en Lenguaje C
Las funciones que retoman un valor numerico 0 apuntador, pueden formar parte de expresiones 0 manejarse como un dato basico. Una explicacion mas detallada de los tipos de datos y las operaciones que son aplicables a eUos se da en el Capitulo 5. Por otro lado, por medio de la definicion de nombres de tipo descrita en la seccion 3.2.7, se pueden definir nuevos tipos de datos para usarse como especificadores para dec1arar variables de ese tipo 0 derivados de el.
3.2.4 Estructura de los declaradores Los dec1aradores estan determinados basicamente por los identificadores de las variables a ser dec1aradas y por operadores de expresion primaria. Los dec1aradores son calificados por los operadores de expresion primaria; 0 sea: el operador apuntador, *; el operador arreglo, [ ]; y el operador funcion, ( ). Pueden formarse estructuras de datos mas complejas, volviendo a calificar un dec1arador encerrado entre parentesis con estos operadores de expresion primaria. Vease en la figura 3.4 el diagrama sintactico &~l declarador.
Hsta de iniciaHi-
zadores IniciaBi1----1
zador
!dentificador I+------~'-r---L-----------'-_t>
Q)_--.Jl Figura 3.4: Diagrama sintactico del dec1arador.
Cada dec1arador de term ina una variable del tipo indicado por los especificadores de la dec1aracion. Si el especificador es del tipo T y el dec1arador tiene la forma *declarador, entonces la variable dec1arada es un apuntador a un dato de tipo T. Si el especificador indica que el dato es de tipo T y el declarador es de la forma declarador[expresion-dimension], la estructura de datos dec1arada es un
Estructura de un Programa en C
47
arreglo del tipo T, cuya dimensi6n esta definida por la expresi6n-dimensi6n, la cual debe ser constante de tipo entero sin signo. En C el primer elemento de un arreglo es el elemento O. Si el especificador indica que el objeto es de tipo T y el declarador es de la forma deciaradorO, entonces 10 que se dec1ara es una funci6n que retoma un dato de tipo T. Las funciones no pueden retomar arreglos, estructuras, uniones 0 funciones, pero S1 pueden retomar apuntadores a dichos elementos. Entre los parentesis se pueden definir, opcionalnlente, los panimetros de la funci6n, tal como se indica en la figura 3.4. La lista de inicializadores encerrada entre Haves, mostrada en la figura 3.4, se aplica a la declaraci6n de un arreglo 0 a la de una estructura. EI especificador debe indicar si se trata de una estructura 0 de un arreglo. Los arreglos de uniones pueden tener miembros de diferentes tipos, declarados de acuerdo a las reglas aplicables a las uniones, especificadas en esta secci6n y en el Capitulo 5. Un arreglo, estructura 0 uni6n no puede contener una funcion, pero sf un apuntador a una funci6n.
3.2.5 Ejemplos de dec1araciones sin inicializador El ejemplo 3.1, declara un entero I, un apuntador IP a un entero, una funcion F que retoma un entero, una funcion FIP que retoma un apuntador a un entero y un apuntador PFI a una funci6n que retorna un entero. Ejemplo 3.1: Declaracion de enteros y objetos relacionados.
El ejemplo 3.2 declara un arreglo tridimensional extemo con dimension 3x5x7. La estructuracion de arreglos en C es asociativa de izquierda a derecha, por 10 cual se interpreta que el arreglo X3D es de tres matrices de 5x7. 0 bien tres arreglos de cinco filas y siete columnas. Ejemplo 3.2: Declaracion de un arreglo tridimensional. extern int X3D[3][S][7];
En el ejemplo 3.3 se declara una estructura del tipo NODO con cuatro miembros, NOMBRE, que es un arreglo de 20 caracteres, CONTADOR, que es un entero, e IZQUIERDO y DERECHO, que son apuntadores a una estructura del mismo tipo NODO.
48
Fundamentos de Programacion en Lenguaje C
Ejemplo 3.3: Declaraci6n de una estructura. struct NODO {
char int struct NODO struct NODO };
NOMBRE [20]; CONTADOR; *IZQUIERDO; *DERECHO;
3.2.6 Inicializadores Ellenguaje C pennite inicializar cualquier variable declarada, a un valor 0 conjunto de valores especifico. El inicializador es el correspondiente valor 0 conjunto de valores que sera almacenado en la localidad 0 localidades de memoria de la variable que se requiere inicializar. El inicializador va precedido del signo =, como se aprecia en el diagrama sintactico de la figura 3.2. El inicializador consiste de una expresi6n 0 una lista de expresiones encerradas entre Haves, tal como se indica en el diagrama sintactico de la figura 3.5.
---~'I
e.presion
~~I,----elt._pr_eS_i6_n~-----;------ICD-
'-----fOI4Q
- -
Figura 3.5: Diagrama sintactico del inicializador.
Las expresiones que definen al inicializador de una variable estatica 0 externa deben ser constantes 0 una direcci6n de una variable declarada previamente, a 10 sumo modificada por un corrimiento determinado por una expresi6n cons!ante. Si una variable estatica 0 extema no es inicializada explicitamente, por omision C la inicializa a cero. Las variables automaticas 0 tipo registro pueden inicializarse con expresiones arbitrarias que involucren constantes, variables declaradas previamente 0 fun-
Estructura de un Programa en C
49
ciones. Si este tipo de variables no son inicializadas, C no las inicializa por omision, de manera que tendnin la "basura" que el sistema les hubiere dejado. Cuando se inicializa un apuntador, 0 un dato de tipo aritmetico, el inicializador consistini de una sola expresion, que puede encerrarse entre Haves { }. EI valor inicial del objeto es el correspondiente valor resultante de la evaluacion de la expresi6n. Al evaluar la expresi6n, C realiza las conversiones que normalmente efectlia en forma automatic a en la ejecucion de instrucciones de asignacion. Estas conversiones se describen en el Capitulo 5, conjuntamente con los diferentes tipos de datos y sus operaciones asociadas. Cuando la variable declarada es una estructura 0 arreglo, el inicializador esta constituido por una lista de expresiones separadas por comas (,) y encerradas entre Baves, {}. La lista debe escribirse de acuerdo al subindice u orden de la estructura 0 arreglo. Si uno 0 varios de los elementos de la estructura son otras estructuras, esta regIa se aplica recursivamente. Si se declaran menos elementos de los que debe tener el conjunto, todo el resto del conjunto se inicializa a ceros. Los elementos componentes de una estructura, que es a su vez elemento de otra estructura, pueden 0 no encerrarse entre Haves. C considera un error que una lista encerrada entre Baves tenga mas miembros que los que debe tener la correspondiente estructura. A continuacion se ilustra con ejemplos las reglas anteriores: Ejemplo 3.4: Inicializacion de un arreglo bidimensional, agrupando los datos por filas. float Y[4][3]
= {{1,3,5},{2,4,6},{3,5,7},};
Ejemplo 3.5: Inicializacion de un arreglo bidimensional, sin agrupar por filas. float Y[4][3] = {1,3,5,2,4,6,3,5,7,};
Las declaraciones de los ejemplos 3.4 y 3.5 son equivalentes; 1,3 y 5, inicializan la primera fila de un arreglo de cuatro filas x tres columnas; 2,4 y 6, la segunda; 3, 5 y 7, la tercera; y la cuarta se inicializa a ceros. Ejemplo 3.6: Inicializacion truncada de un arreglo. float Y[3][4] = {1,2,3,4};
En el ejemplo 3.6 se inicializa la primera fila de un arreglo de 3x4, con los valores 1,2,3 y 4; el resto del arreglo se inicializa con ceros. EI lenguaje C no permite que se inicialicen uniones, tampoco permite que se inicialicen estructuras 0 arreglos declarados como automaticos.
50
Fundamentos de Programaci6n en Lenguaje C
Un arreglo de caracteres puede inicializarse con una cadena (recuerdese que la cadena es en sl una constante y a la vez un arreglo de caracteres). EI ejemplo 3.7 ilustra este tipo de declaracion. Ejemplo 3.7: Declaracion e inicializacion de una cadena de caracteres. char
*mensaje
= "Error de sintaxis en la linea %d \n";
Recuerdese que \n representa un cambio de renglon. Los caracteres %d indican posiciones en el arreglo cuyo contenido puede ser modificado por el programa.
3.2.7 Declaraci6n de nombres tipo Hay dos casos en los cuales es necesario definir tipos de datos con nombres especiales: a) para especificar conversiones de tipo por medio de una casta 0 ins truccion de conversion de tipo y, b) para contar con un argumento del operador sizeof, para determinar y especificar la longitud de un tipo 0 estructura de datos.
La definicion de un nombre tipo puede realizarse por medio de una de claracion, en la cual, como especificador de clase de alrnacenamiento, se utiliza la palabra clave typedef. Los ejemplos 3.8 y 3.9 muestran, respectivamente, la forma en que se declaran y usan los nombres tipo, deiinidos por medio del especificador typedef. En la seccion 4.16 se describe la sintaxis de las conversiones de tipo. Ejemplo 3.8: Declaracion de un tipo de dato mediante typedef. typedef struct {double REAL, IMAGINARIO;} COMPLEJO;
En este ejemplo se declara un nombre tipo denominado COMPLEJO, el cual es una estructura compuesta de dos elementos que son, anlbos, reales de doble precision y se denominan REAL e IMAGINARIO, respectivamente. Ejemplo 3.9: Uso de un tipo de dato declarado con typedef. COMPLEJO Z, *AP_Z;
Estructura de un Programa en C
51
En este ejemplo, aprovechando la dec1aracion descrita en el ejemplo 3.8, se declara una variable de tipo COMPLEJO y un apuntador a una variable del mismo tipo.
3.3
ESTRUCTURA DE UNA DEFINICION DE FUNCION
3.3.1 Estructura general de la definicion de una funcion Como puede apreciarse en el diagrama sintactico de la figura 3.6, la definicion de una funcion consta de un especificador, el declarador de la fundon y el cuerpo de la funcion.
Especificador
Declarador de Funci6n
Cue rp 0 de la Funci6n
1-
Figura 3.6: Diagrama sintactico de la definicion de una funcion.
EI especificador establece el tipo de dato que retomara 0 entregara como resultado la funcion cada vez que se invoque; el declarador de la funcion incluye su identificador, y la declaracion de sus parametros, los cuales son opcionales, y, finalmente, el cuerpo de la funcion, que debe estar encerrado entre Haves, constituye la rutina de programacion que se ejecuta cada que se invoca la funcion. El cuerpo de la funci6n debe tener, al menos, las Haves con que se inicia y termina la definicion. Si el cuerpo de la funcion no incluye instruccion alguna, la funcion no realiza tarea alguna; sin embargo, el compilador de C considera correcta la sintaxis de la funcion. A continuacion se describe cada uno de estos elementos de la definicion de una funcion en ellenguaje C.
3.3.2 Especificador y valor retomado Una funcion que retoma algun valor (0 funcion retomante), puede ser utilizada como expresion primaria y, por 10 tanto, puede formar parte de expresiones mas complejas. Por ejemplo, dicha funcion puede sumarse, multiplicarse, restarse, dividirse, etc., con otras expresiones. EI valor que debe retomar la funcion, es el valor que tamara la misma, el cual sera utilizado para evaluar la correspondiente
52
Fundamentos de Programaci6n en Lenguaje C
expresion compleja de la que fonna parte. Por medio de la instruccion return se define el valor que retomani la funcian. La sintaxis de la expresion se describe en la seccion 4.1, conjuntamente con la de la instruccion de asignacion, y Ia de la instruccion return, en la seccion 4.12. La sintaxis del especificador es la misma que se describe en la seccion 3.2. No obstante, en este caso los unicos especificadores de clase de almacenamiento pennitidos son extern y static. El tipo de valor que retoma la funcion se define con el especificador de tipo de dato, el cual tiene la sintaxis descrita en la seccion 3.2.3, excepto que el valor a retornar solo puede ser de uno de los tipos basicos indicados en la seccion 3.2.3.0 sea que, no se puede retomar una estructura, arreglo 0 funcion; sin embargo, sf puede retornarse un apuntador a cualquier tipo de estructura de datos.
3.3.3 Declarador de funci6n y parametros formales En la figura 3.7 se muestra el diagrama sintactico del declarador de funcion, que consta del identificador de funcion y la lista de panimetros encerrada entre parentesis. C solo pennite el paso de panimetros por valor y unicamente de los tipos basicos, indicados en la seccion 3.2.3. No obstante, el no contar con una forma directa de pasar parametros por referencia 0 de tipo estructura, arreglo 0 funcion, no es una limitacion de C, ya que pueden pasarse apuntadores a cualquiera de este tipo de datos y con ello pueden resolverse las necesidades de programacion que pudieran tenerse al respecto y tambien se resuelve el problema del paso de parametros por referencia. Como se menciono con anterioridad, los parametros son opcionales, pero si se incluyen, toda la lista de parametros debe ser declarada entre los parentesis, tal como se indica en la figura 3.7. Estas declaraciones tienen la misma sintaxis descrita en la seccion 3.2, pero cada parametro debe tener una declaracion independiente; es decir, no se pueden incluir dos 0 mas parametros en una misma declaracion. Se reca1ca que el campo de aplicacion de los parametros fonnales, es todo el cuerpo de la funcion.
----<>
Id entitle ill d 0 r
---e{!)-.-----------r--e>t lis ta de '---- deelaraeiones de param etros
_
Figura 3.7: Diagrama sintactico del declarador de funcion.
Estl1Jctura de un Programa en C
53
3.3.4 Cuerpo de la funci6n En la figura 3.8 se muestra la sintaxis del cuerpo de la funcion y, como se aprecia, consta de dos partes: la lista de declaraciones particulares de la funcion y la lista de instrucciones. decla'aclon
~
I
instruccion
T0~
~~
Figura 3.8: Diagrama sintactico del cuerpo de la funcion.
En el cuerpo de la funcion pueden declararse variables que solo tienen aplicacion en la propia funcion. A tiempo de ejecucion, se obtienen las areas de memoria que son reservadas para estas variables, y una vez que termina de ejecutarse la funcion, dichas areas se reutilizan para almacenar otras variables. La sintaxis de estas declaraciones es identica a la descrita en la sec cion 3.2,y puede declararse cualesquiera de los tipos de dato 0 estructura ahi referidos. Luego de las declaraciones, se deben escribir las instrucciones. C cuenta con instrucciones de control de secuencia para ejecucion condicional, ejecucion condicional multiple, ciclos de ejecucion con pruebas de salida del ciclo al principio 0 al final, ciclos con variable 0 variables de control, salto incondicional, salida incondicional de un ciclo, 0 continuacion de cicIo; pueden hacerse llamadas a otras funciones del mismo programa,.o de las bibliotecas estandar. El lenguaje C tambien cuenta con diversos tipos de asignacion y, ademas, pueden construirse instrucciones compuestas mediante las Baves { }. La descripcion de todas estas instrucciones se realiza en el Capitulo 4.
3.3.5 Recursividad En C, las funciones pueden usarse recursivamente, es decir, pueden Bamarse a sf mismas, en el cuerpo de su definicion. En el ejemplo 3.14, se muestra la definicion de una funcion recursiva. Con elIas se pueden ahorrar muchas lineas de programacion en la solucion de algunos problemas, por ejemplo, para el ca1cul0 de series aritmeticas 0 geometricas, para las busquedas en estructuras de datos tipo arbol, etcetera.
54
Fundamentos de Programacion en Lenguaje C
EI lenguaje C no pennite definir funciones dentro de otras funciones, tal y como 10 penniten otros lenguajes, por ejemplo, Pascal. A esto se Ie conoce como "declaracion anidada de funciones".
3.3.6 Funciones de biblioteca estcindar EI lenguaje C tiene varias bibliotecas estandar de funciones y estructuras de datos especiales para realizar distintos tipos de tareas. Por ejemplo, las funciones de entrada y salida para comunicacion con perifericos; para calculo de funciones matematicas; para dibujo, etc. La tabla 3.1 muestra algunas de las bibliotecas estandar mas usuales. Dependiendo de la implantacion, el compilador puede reconocer directamente estas funciones, incluyendo la biblioteca correspondiente mediante la directiva include; 0 puede ser necesario declararlas como funciones extemas y despues se requerira concatenar el archivo del programa con las correspondientes bibliotecas, por medio del concatenador 0 enlazador (0 ligador) del correspondiente sistema. En el mercado existen bibliotecas adicionales aplicables en areas especificas de programacion, como son: bases de datos, graficacion, interfaces de usuario, comunicaciones, instrumentacion virtual, procesamiento de senales, etc. Estas bibliotecas pueden hacer mucho mas facilla tarea de programacion.
Tabla 3.1: Algunas importantes bibliotecas de C. Biblioteca
Descri pCion
Prueba de clasificacion de caracteres Entrada/Salida Numeros de error Constantes de punto t10tante Graficas Limites definidos en la implantacion Funciones matematicas Saltos no-locales Sefiales Listas de argumentos variables Entrada y salida estandar Funciones de utileria Funciones para cadenas Funciones de fecha y hora
Estructura de un Programa en C
55
En el Capitulo 5 se describen algunas de las funciones matematicas y de manejo de caracteres de las bibliotecas de C; en el 6 se describen las funciones en forma general; en el 7 las funciones de entrada/salida estandar y otros elementos de programacion que son utilizados por C como interfaz con el sistema operatiVO, y en el 8 se describen algunas funciones utilizadas en graficaci6n.
3.4 EJEMPLOS SENCILLOS DE SEGMENTOS DE PROGRAMAS EN C Ejemplo 3.10: Programa "vacio.c".
/* Esta funcion no ejecuta acciones, se puede usar mientras se prueba un segmento de programa
*/ void VACIAO {} mainO {
VACIAO; }
La funcion VACIA del ejemplo 3.10 representa la sintaxis minima que debe tener la declaracion de una funcion en C, para que sea reconocida como correcta por el compilador. Declaraciones como esta son utiles cuando se esta elaborando un programa y hay una determinada funcion que todavia no se desarrolla. El programa del ejemplo 3.10 no genera mensajes de error al compilarse y se ejecuta correctamente, pero 10 que hace es, simplemente, nada. Ejemplo 3.11: Pro gram a "maximo.c", calculo del valor maximo.
/* La funcion MAXIMO da como resultado el valor maximo de los tres enteros que se Ie pasan como argumentos int MAXIMO(int A, int B, int C)
*/
/ / Notese que cada parametro tiene su / / declaracion independiente
{
int M; M = (A > B) ? A : B; return (( M > C ) ? M : C); }
mainO {
/ / Uso de la expresion condicional para elegir el / / mayor entre A y B, asignandolo a M / / Retorno del mayor de entre M y C
56
Fundamentos de Programacion en Lenguaje C
printf("\nEI maximo es %d\n", MAXIMO(-25, 3, -75));
}
El ejemplo 3.11 ya es un programa completo que realiza una tarea especifica: encontrar el valor maximo de tres variables y presentarlo en pantalla. La funcion MAXIMO retoma el valor del mayor de tres datos que Ie son pasados como parametros. Utiliza una variable cuyo campo de aplicacion solo es local, que es M. Para definir el valor del mayor de los tres datos de entrada, utiliza el operador condicional ?. Primero compara dos de los datos y Ie asigna el valor del mayor a la variable M, luego compara la variable M con el otro dato de entrada y retoma el mayor de los dos. Mediante la funcion printf utilizada en main, se imprime el valor maximo de los enteros: -25,3, -75. Lo que se presenta en la pantalla es: EI maximo es 3. El programa del ejemplo 3.12 es ya un poco mas elaborado, y busca en la secuencia de caracteres que se Ie da como entrada, la palabra BUSCADA, e imprime cada linea que contiene esa palabra. Si la linea no tiene la palabra no la imprime. El programa consta de tres funciones: main, LEE e IN DICE. Ademas, main hace uso de la funcion de salida estandar printf y LEE hace uso de la funcion de entrada estandar getchar. En el programa se hace uso de diversas estructuras que se discuten mas adelante con detenimiento y por ello se aconseja no analizar en este momento estos aspectos, sino unicamente la estructura general del prograrna.
Ejempl0 3.12: Programa "busca.c", busqueda de una secuencia de caracteres conocida. #define MAXLONG
100
/* --------------------------------------------------------------------------------------------*/ /* Programa Principal */ int mainO { char LINEA[MAXLONG]; while( LEE( LINEA, MAXLONG ) > 0) if (INDICE(LINEA,uBUSCADA") >= 0) printf( 11%5", LINEA );
}
/* int LEE( char S[ { int 1=0;
Lectura de la Siguiente Unea
J,
int L )
*/
Estructura de un Programa en C
57
char C; while ( (--L > 0) &&·(C = getcharO != EOF) && (C != '\n'» S [1++] = C; if (C == '\n') S [1++] = C;
S [I]
= '\0';
return I;
}
/*
Indice de la Cadena Buscada
*/
int INDICE( char S[], char T[ ])
{ int I, J, K, R; R = -1; for ( 1=0; S[ 1 ] != '\0'; 1++ ) for(J=I, K=O; (T[K] !='\O') && (S[J] ==T [K]); J++, K++) if (T [K+l] == '\0' ) R = I; return R;
}
No obstante 10 anterior, a continuacion se describe el funcionamiento de la~ funciones LEE e INDICE. La funcion LEE obtiene la siguiente linea del archivo de entrada y la guarda en el arreglo LINEA, cuyo apuntador a su inicio se Ie pasa como parametro a LEE. Si la linea leida es de longitud cero (0), es decir, si ya no hay otra linea que leer, se suspende la ejecucion del programa; si no, se prosigue leyendo e imprimiendo (si la linea contiene la cadena BUSCADA). Obtiene la siguiente linea de la entrada (tec1ado), caracter por caracter, por medio de la funcion estandar getchar, hasta obtener un cambio de renglon (,\n'), llegar al final del archivo, 0 que se exceda la longitud maxima pennitida para la linea, la cual se Ie pasa por medio del parametro L; y va guardando los caracteres en fonna secuencial en el arreglo cuya direcci6n es la dada por el parametro S. Al encontrar el cambio de reng16n, 10 guarda en S, luego guarda un caracter nulo y retoma la longitud de la cadena. Si encontro el final del archivo, retoma un cero (0). La funcion IN DICE prueba si la cadena BUSCADA esta contenida en LINEA, retoma la indicacion de que S1 se encuentra, y luego main, por medio de la funcion estandar printf imprime la linea. IN DICE compara la cadena BUSCADA cuya direccion de inicio se Ie pasa por medio del parametro T, con la cadena aimacenada en el bufer de la siguiente linea, cuya direccion se Ie pasa a IN DICE, por medio del parametro S. La comparacion se va realizando recorriendo la linea y comparando caracter por caracter, hasta encontrar que aparece Ia cadena BUSCADA 0 que ya se tennin6 la linea. En este programa se usan las estructuras de programacion para iteraciones c1clicas while y for, la instruccion condicional if, la instruccion de asignacion
58
Fundamentos de Programacion en Lenguaje C
simple, la instruccion return y las funciones estandar getchar 0 printf. Todos estos elementos de programacion se discutiran con detenimiento, en los Capitulos 4,5 Y 6.
Ejemplo 3.13: Programa "potene.e", ealcula potencias enteras de un entero. PROGRAMA PARA CALCULAR POTENCIAS /***** mainO { int I; for ( 1==0; I < 10; ++1) printf("%d %d %d \n", I, POWER(2,I), POWER(-3,I»; } CALCULO DE LA POTENCIA /***** int POWER(int X, int N) { int I, P==l; for (1==1; I <==N; ++1) P==P*X; return P; }
*****/
*****/
El programa del ejemplo 3.13 ealcula e imprime las diez primeras poteneias de 2 y de -3, inc1uida la poteneia eero. Haee uso de la funeion estandar printf y de la funeion POWER, la eual ea1cula en forma iterativa la poteneia N de un entero X. N YX son parametros de la funcion POWER. Esta funeion se reeseribio en el ejemplo 3.14 como una funcion recursiva, con el proposito de ilustrar la forma en que se eonstruye este tipo de funcion. Ejemplo 3.14: Programa "potree.e", funei6n reeursiva para ca1cular las poteneias enteras de un entero. /*--------------------------------------------------------------------------------------------*/ /* FUNCION RECURSIVA */ int POWER(int X, int N) { if (N==O) return (1); else return (X*POWER(X,N-1»; } int mainO {
int BASE, EXP; printf("BASE == \n''); scanf("%d", &BASE); printf("EXP == \n"); scanf("%d", &EXP); printf("RESULTADO = %d\n", POWER(BASE, EXP»; }
Estructura de un Programa en C
59
Si comparamos esta forma recursiva de calcular una potencia, con la del ejemplo 3.13, quiza pensemos que no es muy atractivo usar el concepto de recursividad. Sin embargo, en algunos casos puede ser el unico recurso para resolver un problema eficientemente.
3.5 LA FUNCION PRINCIPAL main y sus ARGUMENTOS argc yargv Para que un programa en C sea ejecutable, debe contener a la funci6n principal main, que es la que inicia la ejecucion del programa cuando se ordena la ejecucion por medio de una linea de comando del sistema operativo 0 al elegir el icono correspondiente en una GUI (Unidad de Interfaz Grafica). De manera que como minimo, los programas deben tener la declaraci6n de una funci6n main. Cuando se ejecuta un programa en C como comando del sistema operativo, pueden emplearse argumentos de dicha linea de comando, los cuales quedan a disposici6n de la funci6n main, a traves de dos elementos: el contador de argumentos, argc, y un arreglo de apuntadores a cadenas de caracteres, argv. El elemento argv[O] tiene el apuntador al nombre del comando, y los siguientes elementos del arreglo tienen los apuntadores a cada uno de los argumentos de la linea de comando. As!, argc siempre es mayor que cero. EI programa del ejemplo 3.15 ilustra de manera simple este mecanismo, enviando el eco de los parametros del comando, hacia el monitor 0 salida estandar. Ejemplo 3.15: Programa "args.c". /*--------~-----------------------------------------------------------------------------------* /
/*
ILUSTRACION DEL
usa DE LOS ARGUMENTOS DE main
*/
maine int argc, char *argv[ ] ) { int I; for ( 1=0; 1 < argc; 1++) printf("D/os D/oC ", argv[IJ, (I < argc - 1) ? ' , : '\n'); }
El contador de argumentos argc y los argumentos, son parametros de main, si se desea pasarlos a otros segmentos del programa, deben copiarse en otras variables extemas.
60
Fundamentos de Programacion en Lenguaje C
Ejercicios 3.1 Racer un programa de computadora en lenguaje C, para resolver ecuaciones de segundo grado. 3.2 Racer un programa de computadora en lenguaje C, para ca1cular una tabla de intereses mensuales, sobre un prestamo no saldado, usando la formula de interes compuesto y teniendo como datos de entrada: el monto del prestamo y el interes anual. 3..3 Racer un programa de computadora en lenguaje C, para ca1cular el maximo comun divisor de los enteros que se Ie den como entrada. 3.4 Racer un programa de computadora en C, para ca1cular el numero de combinaciones de n de m elementos.
Capitulo 4
INSTRUCCIONES
EI lenguaje C cuenta con un conjunto de instrucciones mas amplio y poderoso que otros como Fortran, Pascal 0 Basic. Se dice que es un lenguaje intermedio, porque tiene instrucciones tipicas de un lenguaje de alto niv393el 0 de tercera generacion, como los anteriormente referidos, y tambien instrucciones u operaciones que son propias de los lenguajes ensambladores, como son las instrucciones de logica bit a bit, los corrimientos de bits, la aritmetica de apuntadores, etc. En este capitulo se estudia el conjunto de instrucciones dellenguaje C y se ilustra su uso.
4.1 ASIGNACION SIMPLE EI operador = es el de asignacion. Una operacion de asignacion simple indica que el valor del operando del lade derecho, que debe ser una expresion, se va a asignar a la variable dellado izquierdo de la igualdad. La operacion de asignacion simple, terminada en punto y coma (;), se convierte en una instruccion de asignacion y tiene la siguiente sintaxis: variable
= expresion;
La asignacion simple se aplica a todos los tipos de datos y apuntadores y puede usarse como parte de una expresion de lenguaje C, ya que es en SI misma una expresion que toma el valor de la asignacion. Ejemplos de asignacion simple son: ENT = OxFFF; APCAD = "ROLANDO MENCHACA"; REAL = 5.67 E-2; Cateto = Hipotenusa * sin(2*Plj360*Angulo);
CAR = 'd'; APCAD2 = APCAD + 9; Max_Long = 500; Max_Long = 500;
En los ejemplos anteriores se asigna el valor hexadecimal FFF a la variable entera ENT, se asigna el valor ASCII del caracter 'd' a la variable CAR, se asigna
62
Fundamentos de Programacion en Lenguaje C
la direccion de inicio de la cadena "ROLANDO MENCHACA", al apuntador a cadena de caracteres APCAD y se asigna la direccion de inicio de la misma cadena mas 9 caracteres (bytes), al apuntador APCAD2; se asigna el valor 5.67 x 10-2 a la variable de punto flotante REAL; y finalmente, se Ie asigna el valor resultante del producto de la variable Hipotenusa por el seno del angulo Angulo expresado en radianes. Al realizar las operaciones 0 instrucciones de asignacion se pueden realizar conversiones de tipo de dato automaticamente, siguiendo las reglas indicadas en el punto 4.16: La expresion dellado derecho de la instruccion asignacion, es la expresion usual de cualquier lenguaje de programacion. Es un valor que se calcula a tiempo de corrida y se asigna a la variable dellado derecho de la operacion de asignacion, como ya se indico. El valor de la expresion tiene que ser compatible con el tipo de dato de la variable dellado derecho. A continuacion se describe con mas detalle la sintaxis de una expresion.
t-'--,---~I e x pre s i 6 n '----------'
Figura 4.1: Sintaxis de la expresion.
Sintaxis de fa expresi6n Las expresiones son conjuntos de operaciones compatibles que dan un valor definido, su sintaxis se presenta mediante el diagrama de la figura 4.1, en la cual la expresion primaria que se observa puede ser un identificador, el identificador calificado por el operador direccion &~ el de indireccion *~ el de elemento de arreglo [fndice], 0 el de elemento de estructura 0 union, mediante la notacion punto (.) 0 flecha (-». Asimismo, puede ser una funcion con 0 sin argumentos. La figura 4.1 tambien indica que la construccion de expresiones es recurs iva y que pueden encerrarse entre parentesis algunas sub-expresiones que forman parte de la expresion total. Obviamente en este caso los parentesis deben quedar balanceados, es decir, debe haber el mismo numero de parentesis de apertura que de cierre y semanticamente deb en representar la operacion que se desea. El uso de parentesis ayuda a entender y especificar mejor una expresion, por ella es recomendable.
Instrucciones
63
Los operadores pueden ser cualquiera de los indicados en la seccion 2.6. Debe recordarse que hay operadores unarios y binarios, 10 cual tambien es denotado por la figura 4.1. Algunos ejemplos de expresiones validas son los siguientes:
'a' &32 -A + 2
*B
++A 2 * ARR[IND++] + B (A < B) && (C>D)
A + B * C + D + E + 634 + G/F (A + B) * C (A A 8) A (C & D) A * sin(2 * PI * f * t ) !(A < B) II (!(C>D))
Podemos ahora clasificar las expresiones de acuerdo al tipo de dato que dan como resultado en: expresiones canicter expresiones aritmeticas enteras expresiones aritmeticas de punto flotante expresiones logicas expresiones apuntador Obviamente, las expresiones canicter dan como resultado un canicter, el cual tambien puede considerarse como un entero de 8 bits de longitud. Dicho canicter puede ser el resultado de operaciones aritmeticas, logicas bit a bit, etc. Las expresiones enteras pueden dar un entero con 0 sin signo, 0 bien un entero corto o largo, dependiendo de las variables, constantes 0 funciones que la compongan. Las expresiones de punto flotante pueden ser de precision estandar 0 de doble precision. Las expresiones logicas son el resultado de operaciones relacionales 0 logicas. Un caracter 0 entero, usado como expresion logica, es verdadero si es distinto de cero y falso si es cero. Finalmente, las expresiones apuntador dan como resultado un apuntador a un elemento 0 estructura de datos, de acuerdo con el tipo de apuntador que intervenga en la expresion. Al calcular una expre.sion el programa puede hacer conversiones automatic as de tipo de dato, como se explica en la seccion 4.16. La descripcion de los tipos de operaciones se estudia conjuntamente con los tipos de datos en el Capitulo 5.
4.2 ASIGNACION COMPUESTA Las operaciones de asignacion compuesta cuyos operadores fueron presentados en la seccion 1.6.5, terminadas en punto y coma (;), se convierten en instrucciones de asignacion compuesta, y su sintaxis es la siguiente: variable + = expresion ; variable -= expresi6n ;
64
Fundamentos de Programacion en Lenguaje C
variable *= expresion ; . variable /= expresion ; variable 0/0 = expresion ; variable < < = expresion ; variable »= expresion ; variable /= expresion ; variable &= expresion ; variable A= expresion ;
Las instrucciones de asignacion compuesta se aplican a los diferentes tipos de datos y apuntadores, como se vera especificamente en el Capitulo 5. Algunos ejemplos de instrucciones de asignaci6n compuesta son los siguientes: ENTA += 2; REAL *= 10; ENTC 0/0 = 8;
ENTB -= 3; REAL /= 0.1; car 1= 32;
En los ejemplos se incrementa en 2 la variable entera ENTA, se decrementa en 3 la variable entera ENTB, se multiplica por lOla variable de punto flotante REAL, se divide entre 0.1, equivalente a multiplicar por lOa la variable REAL, se sustituye ENTC por el residuo de la division entera de la misma entre 8, a la variable car se Ie pone el bit 5 aI, 10 que equivale a asegurarse de que sea minuscuIa, y a la variable CAR se Ie pone el bit 5 a 0, convirtiendo su contenido a mayUscula. Los operadores de asignacion compuesta, como el de asignaci6n simple tambien pueden emplearse para formar expresiones.
4.3
INSTRUCCION Ex PRESION
Una expresi6n terminada en punto y coma (;) se convierte en una instrucci6n expresion. Ejemplos de esta que tienen sentido son las de incremento, decremento, corrimiento a la derecha 0 a la izquierda, funci6n, etc. La sintaxis de este tipo de instrucci6n es: expresion;
Ejemplos de este tipo de instrucci6n son: ++VAR; VAR++;
--VAR; VAR--;
UNS«l; printf ("HOLA\n");
UNS»l;
Instrucciones
65
4A INSTRUCCION COMPUESTA (BLOQUE) La instrueeion eompuesta 0 bloque de instrueeiones, es una seeueneia de dec1araeiones y de instrueeiones eneerrada entre Haves, la eual, para euestiones sintaetieas, puede considerarse como una sola instrucei6n. Su sintaxis es: { lista_declaraciones lista_dejnstrucciones }
Un ejemplo de ins true cion eompuesta es el siguiente: { float LOCAL; printf("Esta es una instruccion compuesta\n"); LOCAL += 171; printf("LOCAL = %e\n", LOCAL); LOCAL *= 1000; printf("LOCAL = %e\n", LOCAL); LOCAL += 595; printf("LOCAL = %e\n", LOCAL);
}
4.5 INSTRUCCION CONDICIONAL La instrueeion eondieional 0 instrueei6n if-else se utiliza para tomar deeisiones y eambiar la secuencia de ejecuci6n de un programa. La sintaxis de esta instruccion es: if ( expresion_16gica ) instruccion 1 else instruccion2
La parte else es opcional. La manera en que opera la instrueeion condicional es la siguiente: se evalua la expresi6n que debe ser una expresion 16gica, si resulta verdadera se ejecuta la instrucci6n 1; si la expresion logica resulta falsa, se ejecuta la instrueeion2, 0 si se omiti6 la parte else no se ejecuta la instruecion 1. Ejeniplo de aplieaei6n de esta instrueeion es el siguiente: unsigned A, B;
if (A < B ) printf("A es menor que B\n"); else printf("A es mayor que B");
Puede suponerse que las lineas punteadas representan una secueneia de instrucciones en las cuales se alteran los val ores de las variables A y B.
66
4.6
Fundamentos de Programacion en Lenguaje C
INSTRUCCION while
La instruccion while representa una estructura de programacion iterativa que tiene la forma de la figura 4.2. En dicha figura se muestra que la estructura while tiene una prueba al inicio que determina si se ejecuta la siguiente iteracion. En el cuerpo del cicIo while esta la instruccion que se va a ejecutar de manera iterativa, la cual puede ser una instruccion compuesta. La expresion puede ser constante (Ia tautologia), y entonces se tiene que abandonar el cicIo por medio de una instruccion de salta incondicional (goto), una instruccion de ruptura de cicIo, como break, 0 una expresion de terminacion del programa, como exitO. La sintaxis de esta instruccion es: while (expresion-,ogica) instruccion
EI programa del ejemplo 4.1. ilustra el uso del while, se trata de un programa que cuenta todos los espacios en blanco que tiene un archivo, e imprime el resultado de la cuenta en el monitor, con el mensaje: "El numero de blancos del Archivoes . El nombre del archivo se Ie pasa como parametro de la linea de comandos 0 bien, luego de que el programa 10 pregunta interactivamente. Antes de terminar, el programa cierra el archivo que se uso para contar los blancos, 10 cual es muy conveniente.
expresi6n
j
II
~
instrucci6n
Figura 4.2: Estructura while.
Ejemplo 4.1: Programa "cuentbla.c", contador de espacios en blanco de un archivo. # include
FILE *ARCHIVO; main(int argc, char *argv[])
Instrucciones
67
{ char CAR, *ACAR = argv[l], ARCH[20]; int NCAR; if(argc == 1) {printf("NOMBRE DEL ARCHIVO?\n"); sca nf(" 0/05" ,ARCH); ACAR = ARCH; ARCHIVO = fopen(ARCH,lr");} else ARCHIVO = fopen(argv[l],lr"); while «CAR = getc(ARCHIVO)) > 0) if (CAR == ") NCAR++; printf("EI numero de blancos del Archivo 0/05 es %d \n", ACAR, NCAR); fclose(ARCHIVO); return 0;
}
Con la instruccion while de la doceava linea (del codigo anterior), se realiza el conteo de blancos, despues de abrir el archivo con la estructura if, que empieza en la septima linea.
4.7 INSTRUCCION dO".while La instruccion do-while es otra estructura de programacion iterativa que tiene la forma de la figura 4.2. Como se ve en dicha figura, la estructura while tiene su prueba que determina si se ejecuta la siguiente iteracion, al final del ciclo. Igualmente, en el cuerpo del ciclo do-while esta la instruccion que se va a ejecutar de manera iterativa, la cual desde luego puede ser una instruccion compuesta. Esta estructura es equivalente al repeat-until de Pascal.
j instrucci6n
1
FALSO
Figura 4.3: Estructura do-while.
68
Fundamentos de Programacion en Lenguaje C
La expresi6n logica de la prueba final, igualmente puede ser una tautologfa, y entonces se tiene que abandonar el ciclo por medio de la instrucci6n goto, break 0 exitO, mediante alguna otra prueba de control de secuencia intermedia. La sintaxis de esta instrucci6n es: do instrucci6n while (expresi6nJ6gica);
En el programa del ejemplo 4.2 se ilustra el uso del do-while. En el se cuenta el numero de comas (,) que tiene un archivo de caracteres. Ejemplo 4.2: Programa "cuentcom.c", contador de comas dentro de un archivo. # includeFILE *ARCHIVO; main(int argc, char *argv[]) { char CAR, *ACAR = argv[l], ARCH[20]; int NCAR = 0; if(argc == 1) {printf("NOMBRE DEL ARCHIVO?\n"); scanf("%s",ARCH); ACAR = ARCH; ARCHIVO = fopen(ARCH,"r");} else ARCHIVO = fopen(argv[l],"rll); do if «CAR = getc(ARCHIVO)) == ',') NCAR++; while (CAR> 0); printf("EI numero de Comas del Archivo 0/05 es %d \n", ACAR, NCAR); fclose(ARCHIVO); return 0;
}
La forma en que esta realizado este programa es muy similar a la del ejemplo 4.1, s6lo se cambia la instrucci6n while por una estructura do-while y el mensaje de la funci6n de salida printf. En la pnictica es menos usual esta estructura que while 0 for; sin embargo, especialmente cuando la instruccion del cuerpo del do-while se tiene que ejecutar por 10 menos una vez, es conveniente usarla.
4.8 lNsTRuCCION for La instrucci6n for es heredera del ciclo iterativo do original de Fortran y de las instrucciones for-next de Basic y Pascal; sin embargo, el for de C es mucho mas flexible y poderoso. Algunas de las caracteristicas que 10 hacen una estructura mas poderosa son: inicializaci6n multiple de variables, expresi6n logica de
Instrucciones
69
prueba de cualquier complejidad y no unicamente restringida a que la variable de control alcance un valor final y especificacion multiple de incrementos. La sintaxis del for es la siguiente: for (expr_inicializadoras; expr_de_control; expr_de_incremento) instrucci6n; La estructura de la instruccion for se muestra en la figura 4.3. Como se ve, la prueba que determina si se ejecuta la siguiente iteracion, en este caso es al principio del cielo. En el cuerpo del cielo for esta la instruccion que se va a ejecutar de manera iterativa, la cual desde luego, puede ser una instruccion compuesta. La expresion 16gica de la prueba, puede ser una tautologia, en cuyo caso se tiene que abandonar el cielo par media de la instruccion goto, break 0 exitO, mediante alguna otra prueba de control de secuencia intermedia; 0 bien una expresion logica compleja que implique el cumplimiento 0 incumplimiento de diversas premisas.
inicializaci6n
. - - - < - . expresi6n
/'-----t>
I instrucci6n
Figura 4.4: Estructura for.
4.9 INSTRUCCION switch-case La instruccion switch-case representa una estructura de programacion de decision multiple y ha sido de gran utilidad en la programacion estructurada. Su sintaxis es: switch( expresi6n_entera ) {
case exp_const : instrucci6n;
70
Fundamentos de Programacion en Lenguaje C
default exp_const : instrucci6n;
}
La instruccion switch-case opera de la siguiente manera: se avalua la expresion entera que sigue a la palabra switch y que constituye la expresion de control; y a continuacion se ejecutan las instrucciones en el cuerpo del switch, empezando con la que se encuentra marcada por el case (caso) que tiene como etiqueta un valor constante que toma la expresion de control del switch, en un momento dado. La figura 4.4 muestra la estructura basica de esta instruccion. En esta figura se ve que se ejecutan todas las instrucciones asociadas a los casos que comienzan a partir del caso que se cumple, es decir, el correspondiente al valor que toma la expresion de control.
No "-
~~i J
~
I
Instr~cci6nl
No •
Figura 4.5: Estructura switch-case.
Puede salirse desde puntos intennedios de la estructura switch-case mediante la instruccion break; tal como se muestra en el ejemplo 4.3, que contiene la estructura de ejecucion del menu principal de una ca1culadora estadistica; en la estructura de la figura 4.5 tambien se muestra el uso de break para salir de la estructura switch-case. Esta estructura es muy apropiada para realizar menus de comandos, como el que se ilustra en el ejemplo 4.3. En ese ejemplo, la funcion MENUESTADISTICO presenta el menu en la pantalla, pide el comando y regresa el numero de comando que eligio el usuario. Este numero de comando define los casos que conforman las etiquetas de la estructura y, por 10 tanto, se ejecuta unicamente la fun-
Instrucciones
71
cion estadistica que fue elegida. Se supone que METERDATOS cargara los datos en forma interactiva con el usuario. Ejemplo 4.3: Estructura switch-case para una ca1culadora estadistica. switch (MENUESTADISTICOO) { case 1: PROMEDIO(DATOS); break;} case 2: VARIANZA(DATOS); break;} case 3: MODA(DATOS); break;} case 4: MEDIANA(DATOS); break;} case 5: DESVSTANDAR(DATOS); break;} case 6: DISTACUMU(DATOS); break;} case 7: MAXIMO(DATOS); break;} case 8: MINIMO(DATOS); break;} case 9: METERDATOS(DATOS) break;} case 10: exit(O); /* salir del programa */ }
Expresi6n
I
1
Figura 4.6: Estructura switch-case con break.
4.10 INSTRUCCION break La instruccion break se utiliza para abandonar una estructura iterativa 0 switchcase en un punto cualquiera de la estructura y no necesariamente al principio 0 al final. break saca al programa del cicio 0 estructura, en el nivel de anidamiento actual. La figura 4.7 ilustra una estructura while, en la cual se usa break en conjuncion con una instruccion de decision if para salir del ciclo iterativo.
72
Fundamentos de Programacion en Lenguaje C
1
Figura 4.7: Estructura while con un break.
4.11
INSTRUCCION
continue
Esta instrucci6n representa una fonna de alterar la secuencia de ejecuci6n dentro de un cicIo iterativo, al encontrarse continue se reinicia la ejecuci6n del cicIo, sin realizar algunas instrucciones posteriores que se encuentren dentro del cicIo. La figura 4.8 muestra esta variante de estructura lograda con continue.
1
.----~-------~-------------~
1
~~ I L~
[comi rwe;I
i
I
instruccion
Figura 4.8: Estructura while con un continue.
Instrucciones
73
4.12 INsTRuccroNEs return y exit Estas instrucciones tambien permiten alterar la secuencia de ejecuci6n del programa. Al encontrarse una instrucci6n return se da por terminada la ejecuci6n de la funci6n actual, se retoma al punto de donde se llam6 la funci6n y se entrega el resultado numerico producto de la ejecuci6n de la funci6n. EI argumento de return es precisamente el valor que se retoma 0 entrega como resultado. La sintaxis de esta instrucci6n es: return expresion;
La instrucci6n exitO permite dar por terminada la ejecuci6n del programa. Tiene un argumento que es ademas el valor que retoma el programa al sistema operativo, exit(O) implica la terminaci6n normal.
4.13 INsTRuccrON goto-etiquetas EI goto es una instrucci6n que cay6 en el desprestigio al entrar en el ambito de la computaci6n la programaci6n estructurada. La gran mayorfa de los programadores expertos recomiendan que no se use esta instrucci6n, en virtud de que un programa bien disefiado y estructurado no debe requerir del uso de ella. Sin embargo, esta disponible y en algunos casos complejos se puede requerir de ella para inventar nuevas estructuras de programaci6n. Los ejemplos 4.4, 5.28 Y 5.29 ilustran el uso de la pareja de recursos de C, representada por la etiqueta y el goto.
4.14 lNsTRuccrONNuLA La instrucci6n nula 0 instrucci6n vacfa, puede ser muy util en algunos casos y consiste unicamente del terminador de instrucci6n (;). Un ejemplo de uso importante de la instrucci6n nula es el siguiente: while ( getcharO != '\n');
Dentro de la expresi6n 16gica de control del while se incluye una funci6n de entrada de caracteres getchar, la cual toma caracteres del buffer de entrada hasta que encuentra un cambio de rengl6n. La instrucci6n del cuerpo del while es una instrucci6n nula.
74
Fundamentos de Programaci6n en Lenguaje C
4.15
EXPRESION CONDICIONAL
La expresion condicional es una altemativa a Ia instruccion if-else, la cual tiene la ventaja de que puede usarse como parte de una expresion. Toma un valor que se puede utilizar como operando de otra operacion aritmetica, relacional, logica o funcional. Por ejemplo, consideremos que A, Bye son variables de punto flotante que tomaron un determinado valor en el transcurso de la ejecucion del programa. Entonces la instruccion de asignacion: C += ((A> B)?A:B)*2 Ie asigna a C el valor mayor entre A y B, multiplicado por dos.
4.16
INSTRUCCION DE CONVERSION (CASTA)
Cuando se realiza una operacion con operandos de diferentes tipos, dichos operandos se convierten a un tipo comnn, de acuerdo a un conjunto de reglas de conversion. Se realiza la conversion en forma automatica, cuando dicha conversion es de un tipo de datos angosto a uno ancho. Es decir, de entero a flotante, de caracter a entero 0 flotante, de flotante a doble precision, etc. La conversion se realiza automaticamente cuando no bay posibilidades de perdida de informaci6.n. De otra manera, la conversion debe realizarse mediante una operacion de casta 0 conversion. EI programa del ejemplo 4.4 ilustra el uso de la openicion de castas con apuntadores que es la aplicacion mas frecuente, el cual permite visualizar en diversos formatos, cualquier dato numerico almacenado en la computadora. Ejemplo 4.4: Programa "uso_casLc". /* Este programa se usa para ilustrar como es posible visualizar cualquier tipo de dato almacenado en memoria principal convirtiendolo por medio de castas (u operadores de conversion de tipo de dato). EI dato que se requiere inspeccionar se introduce interactivamente, a traves de la terminal. Este dato, puede ser un natural 0 entero sin signo, un entero, 0 un real. Luego este mismo dato se puede presentar en diversos formatos: binario, octal, decimal, hexadecimal. De este modo se observa la estructura que utiliza C para almacenar estos tipos de datos.
*/ typedef char *apcar; char buffer[S], *AP_real, *AP_entero, *AP_natural;
Instrucciones
int Entero; float Real; unsigned Natural; mainO { unsigned comando; int aux,au; AP_entero = (apcar) &Entero; AP_real = (apcar) &Real; AP_natural= (apcar) &Natural;
/ / operacion de conversion / / operacion de conversion / / operacion de conversion
INICIO: for (aux = 0; aux<=S,buffer[aux]=O;aux++); printf("\t\tLISTA DE OPCIONES\n\n"); printf("\t\t\t SALIR: O\n\n"); printf("\t\t\t ENTERO: 1\n\n"); printf("\t\t\t REAL: 2\n\n"); printf("\t\t\t NATURAL: 3\n\n"); printfC'Introduce el numero que corresponde a tu eleccion\n"); scanfC'%d",&comando); if (comando == 0) goto FINAL; switch (comando) { case l:{printf("VALOR DEL ENTERO? \n"); scanf("%d",&Entero); strncat(buffer, AP_entero,2); buffer[3] = 0; break;} case 2:{printf("VAlOR DEL REAL? \nll); scanf("%f',&Real); strncat(buffer, AP_real,6); buffer[S] = 0; break;} case 3:{printf("VALOR DEL NATURAL? \n"); scanf("%d",&Natural); strncat(buffer, AP_natural,2); buffer[3] = a;} } printf("\t\tLISTA DE FORMATOS DE SALIDA\n\n"); printfC'\t\t\t SALIR: O\n\n"); printfC'\t\t\t ocrAl: 1\n\n"); printf("\t\t\t DECIMAL: 2\n\n"); printf("\t\t\t HEXADECIMAL: 3\n\n"); printf("\t\t\t BINARIO: 4\n\n"); printfC'Introduce el numero que corresponde a tu eleccion\n"); scanf("%d" ,&comando); if (comando == 0) goto FINAL; printf("INFORMACION AlMACENADA\n\n"); for (aux = 0; aux <= 5; aux++) { switch (comando) { case 0: break; case l:{printf("%o",buffer[aux]); break;} case 2:{printf("%d",buffer[aux]); break;}
75
76
Fundamentos de Programacion en Lenguaje C
case 3:{printfC'%x",buffer[aux]); break;} case 4:{for (au=7;au>=0;au--) printfC%c",«l <
Ejercicios 4.1. Hacer un programa de computadora en lenguaje C que ca1cule la exponencial de un numero real (positivo 0 negativo) utilizando la serie de Taylor de dicha funcion. Con un error menor del 0.001 010. 4.2. Hacer un programa de computadora en lenguaje C que calcule el seno, el coseno y la tangente de un angulo (expresado en radianes) utilizando la serie de Taylor de dichas funciones. Con un error menor del 0.0010/0. 4.3. Hacer un programa de computadora que calcule los puntos de cruce de un circulo con una recta, dando como datos de entrada el centro y radio del circulo y la pendiente y ordenada al origen de la recta. 4.4. Hacer un programa de computadora que ca1cule el determinante de una matriz de numeros reales. 4.5. Hacer un programa de computadora que resuelva un sistema de N ecuaciones lineales con N incognitas, utilizando el metodo de Gauss-Jordan.
Capitulo 5
TIPOS DE DATOS
En este capitulo se describen los tipos y estructuras de datos que soporta ellenguaje C, asi como las funciones y operaciones que pueden realizarse con dichos datos.
5.1 ENTEROS 5.1.1 Introducci6n C soporta dos diferentes tipos de numeros enteros: sin signo y con signo. Los primeros obedecen a las leyes de la aritmetica binaria y como su nombre 10 indica no tienen signo. En los segundos, el bit mas significativo del espacio en memoria que ocupan, es el bit de signo; pueden ser de tres diferentes longitudes: corta, estandar y grande, y la longitud 0 espacio de memoria que ocupan depende de la implementacion del compilador. En algunos casos no se dispone de las tres diferentes longitudes, cuando esto ocurre, el compilador toma al entero corto 0 largo especificado, como entero de longitud estandar. Ambos tipos de enteros encuentran una gran cantidad de aplicaciones en los programas de computadora, y el lenguaje C dispone de aritmetica y logica de enteros muy completa. A continuacion se describen las caracteristicas de estos diferentes tipos de datos, asi como de las funciones y operadores que son aplicables a ellos.
5.1.2 Enteros sin signo Notacion En un programa escrito en C es posible manejar enteros sin signo por medio del sistema de numeracion de base octal, hexadecimal 0 decimal, tanto en los formatos utilizados en las funciones de entrada/salida estandar como en la especifi-
78
Fundamentos de Programaci6n en Lenguaje C
cacion de constantes. En el ejemplo 5.1, mostrado a continuacion, se ilustra el manejo de enteros utilizando los distintos tipos de notacion ya referidos. Ejemplo 5.1: Ilustracion del uso de enteros en notacion hexadecimal, octal y decimal.
1***********************
Programa entnotac.c
**********************1
mainO {
int A, B, C, D, E; A=OxFF; / /Notacion Hexadecimal A-=15; /INotacion Decimal B=A+OxA; /INotacion Hexadecimal C=017 + 020; /INotacion Octal D=OxF + 15*16; IIMezcla de Notacion Hexadecimal y Decimal printf("A = %x B = %x C= %x D = %x \n", A, B, C, D); / /Impresion en Notacion Hexadecimal printf("A = %d 8 = %d C= °lod D = °lod \n", A, 8, C, D); / /Impresion en Notacion Decimal }
Declaracion La declaracion de un entero sin signa se realiza de la siguiente forma: especificador_de_alm
unsigned
identificador, identificador, ... ;
Notese que bajo una misma declaracion pueden incluirse varias variables del correspondiente tipo; en este caso, enteros sin signo. El especificador de tipo de almacenamiento especificador_de_alm, es el ya explicado en la seccion 2.2.2; establece la forma en que las variables van a ser implantadas en memoria. En este caso, el almacenamiento puede ser tipo: Register extern auto static
registro externo automatico, estatico
0
Como ya se comento en el Capitulo 2, es posible inicializar las variables al hacer la declaracion. As!, la declaracion queda de la siguiente manera:
Tipos de Datos
79
especificador_de_aIm unsigned identificador=inicializador/ identificador= inicializador/ ... ;
No todas las variables de una misma declaraci6n deben ser inicializadas. Los siguientes son algunos ejemplos de declaraciones de variables enteras sin signo: extern unsigned unsigned
A, B, C; LOCAL = 255, OTRALOCAL;
Longitud La longitud logica de un entero sin signo es la longitud de una palabra del procesador del sistema. Por ejemplo, 16 bits en los microprocesadores 8086/88 y 80286 de Intel (bus AT), 6 68000 de Motorola; 32 bits en el caso de los procesadores 80386, 80486 y Pentium (bus EISA); sin embargo, dicha longitud es determinada en la pnktica, en el momenta del desarrollo del compilador y por ejemplo, en el caso del Turbo C, la longitud utilizada es de 16 bits, con los cuales el rango de valores que toma un entero sin signo es de 0-65535, el valor maximo del entero sin signo se define en la biblioteca, es UINT_MAX Y es igual a 65535 (2 16 _1).
Tabla 5.1: Operaciones aritmeticas con enteros sin signo. Suma
A+B
Resta
A-B
Multiplicacion A*B
Division
A/B
Residuo
A% B
EI resultado es la suma de los enteros A y Byes tambien un entero sin signo. El resultado es la resta de A y Byes un entero sin signo. La resta se efectua siguiendo las reglas de la aritmetica de base 2 EI resultado es la multiplicaci6n de A y B, es un entero sin signo. La multiplicaci6n se efectua siguiendo las reglas de la aritmetica de base 2 y se trunca a la izquierda EI resultado es la division de A entre B, es un entefO sin signo; si la division no es exacta, el resultado es la parte entera del cociente. La division se efectua siguiendo las reglas de la aritmetica de base 2 EI resultado es el residuo de la division de A entre Byes un entero sin signo. La division se efectua siguiendo las reglas de la aritmetica de base 2
80
Fundamentos de Programacion en Lenguaje C
5.1.3 Operaciones matematicas y 16gicas con enteros sin signa Las operaciones matematicas y logicas aplicables a los enteros sin signo son las operaciones aritmeticas indicadas en la tabla 5.1, las operaciones relacionales de la tabla 5.2, las operaciones logicas de la tabla 5.3, las operaciones logicas bit a bit indicadas en la tabla 5.4, las de asignacion presentadas en la tabla 5.5 y las operaciones tipo ensamblador indicadas en la tabla 5.6. Debemos suponer que las variables A y B utilizadas en las tablas fueron dec1aradas como enteros sin SIgnO. La tabla 5.1 ilustra la sintaxis de las operaciones aritmeticas e indica el resultado que se obtiene al efectuar dichas operaciones. Observese que no puede realizarse la operacion unaria NEGATIVO de un numero sobre un entero sin SIgnO. El ejemplo 5.2 ilustra las operaciones aritmeticas que pueden realizarse entre dos variables enteras sin signo. Notese que si una resta 0 suma se sale del rango de valores, se producira un error semantico que no puede detectar el compilador y que debe ser interpretado por el programador. As!, la resta 3-5 se sale de rango y por eso se obtiene como resultado el numero 65 534. Ya se indico que el valor maximo de un entero sin signo, en Turbo C++, es 65535. Ejemplo 5.2: Ejemplos de operaciones aritmeticas con enteros sin signo.
1*
Sean A Y B enteros sin signo, y A
=3
Y B
= 5;
entonces:
*1 A+B=8 A + 65 533 = 0 A - B = 65534 A * B = 15 AI B = 0 AO/oB=3
La tabla 5.2 muestra las operaciones re1acionales que pueden realizarse con numeros enteros sin signo en el lenguaje C, su sintaxis y el resultado que se produce cuando se aplican. Las operaciones relacionales se usan en las instrucciones de control, como parte de una expresi6n logica para tomar una decision de secuencia. Tambien pueden usarse conjuntamente con las operaciones logicas que se muestran en la tabla 5.3, como parte de expresiones logicas de primer orden, de acuerdo al programa 0 algoritmo de que se trate.
Tipos de Datos
81
Tabla 5.2: Operaciones relacionales con enteros sin signo. Menor que
A
Mayor que
A>B
Menor 0 igual que
A <= B
Mayor 0 igual que
A >= B
Igual que
A == B
Diferente de
A!= B
El resultado es 1, si A es men or que B yO, si no es asi EI resultado es 1, si A es mayor que B, y 0, si no es asi El resultado es 1, si A es menor 0 igual que By 0, si no es asi El resultado es 1, si A es mayor 0 igual que B yO, si no es asi El resultado es 1, si A es identico que B y 0, si no es asi El resultado es 1, si A es diferente de B y 0, si no es as!
La tabla 5.3 muestra las operaciones logicas basicas, es decir, la negacion 10gica, la disjuncion 0 funcion 0 y la conjuncion 0 funcion Y. Estas operaciones logicas tienen la semantic a y, por 10 tanto, la tabla de verdad de la logica proposicional bivalente (0,1); sin embargo, puede considerarse que los dos valores 10gicos que pueden tomar las variables son cero y no cero. En C no se define un tipo logico especial como el tipo BOOLEAN de Pascal, el cual solo tom a los valores TRUE (cierto) y FALSE (falso). Los operandos de las operaciones logicas pueden tener valores no cero distintos de uno y C de todas maneras considera que dicho valor corresponde al valor CIERTO, mientras que solo si el valor del operando es cero, considera que este es FALSO.
Tabla 5.3: Operaciones logicas con enteros sin signo. Negacion
!A
El resultado es 1, si A vale 0, y 0, si A vale 1
Funcion 0
A II B
El resultado es 1, si A 0 B, cualquiera de las dos, tienen el valor de 1, y 0 solo si ambas tienen el valor 0
Funcion Y
A&& B
El resultado es 0, si A 0 B, cualquiera de las dos, tienen el valor de 0, y 1, solo si ambas tienen el valor 1
82
Fundamentos de Programacion en Lenguaje C
EI ejemplo 5.3 ilustra las operaciones relacionales y logicas. Notese que con las operaciones relacionales pueden construirse expresiones logicas de primer orden, combimindolas con las operaciones logicas. Notese tambien que un valor distinto de uno, pero tambien distinto de cero (falso), se toma como si fuera 1 (cierto). Ejemplo 5.3: Ejemplos de operaciones relacionales y logicas con enteros sin signo. Sean A, B Y C enteros sin signo y A=C=O, B=1 0, entonces ... !(A < B) A< B A> B A <= B A >= B A == B A!= B A II B A && B (!A) && B !A (A II B) && C (A < B) && B
= 0 = 1 =0 = 1 = 0 =0 = 1 = 1 =0 = 1 =1 = 0 =1
Tabla 5.4: Operaciones logicas bit a bit con enteros sin signo. Complemento a 1 -A
Funcion 0
AlB
Funcion Y
A&B
Funcion 0 exc1u- A /\ B SIva
El resultado de esta operacion es el mimero sin signo cuyos bits son los complementos de los bits del numero representado por la variable A. Por ejemplo, si A=O, -A=65535 EI resultado es el numero sin signo cuyos bits son el producto de la disjuncion (funcion 0) de los bits de los numeros representados por las variables A y B. Por ejemplo, si A=3 y B=3, AIB=3 EI resultado es el numero sin signo cuyos bits son el producto de la conjuncion (funcion Y) de los bits de los numeros representados por las variables A y B. Por ejemplo, si A=O y B=65535, A&B=O El resultado de esta operacion es el numero sin signo cuyos bits son el producto de la funcion 0 exc1usiva de los bits de los numeros representados por las variables A y B. Por ejemplo, si A=7 y B=3, A!\B=4
Tipos de Datos
83
La tabla 5.4 muestra las operaciones logicas bit a bit, aplicables a variables enteras sin signo. Estas operaciones son tipicas de los lenguajes ensambladores; sin embargo, el lenguaje C tambien cuenta con ellas, 10 cual 10 hace apropiado para el desarrollo de sistemas en los que es necesario manipular los bits de una variable, en forma independiente. Como en el caso de las tablas ya presentadas, la tabla 5.4 muestra la sintaxis de las operaciones en su segunda columna yen la tercera, la descripcion de los resultados que se obtienen al realizar la operacion. El manejo de operaciones logicas al nivel de bit Ie da a C un gran poder y 10 hace adecuado para trabajar con programas que antes de que apareciera este lenguaje, solo podian desarrollarse en lenguaje ensamblador. Particularmente, la realizacion de sistemas de control digital por computadora, se facilita al contar con las operaciones logicas presentadas en la tabla anterior. Por ejemplo, cada bit puede ser uno de los bits de las entradas de control de un mUltiplexor, selector 0 distribuidor, 0 uno de los bits de uno de los campos de un protocolo, y como estos se pueden dar muchos mas ejemplos. Las operaciones se realizan bit a bit, a manera de compuertas logicas 0 como en las instrucciones logicas tipicas de un lenguaje ensamblador; considerando que las variables y constantes son numeros binarios, esto implica que el bit 0 de uno de los operandos se combina con el bit 0 del otro, el bit 1 con el otro bit 1, Y as! sucesivamente. Para esto, debemos considerar que la longitud de los enteros sin signo es de 16 bits. Vease el ejemplo 5.4, en el eual supusimos que se han dec1arado tres variables: A, B Y C, las cuales se usan posteriormente en las distint as operaciones logicas bit a bit. Notese que se pueden realizar operaciones complejas combinando estas operaciones 10gicas en expresiones, de manera recursiva. Ejemplo 5.4: Operaciones logicas bit a bit con enteros sin signo expresadas en forma binaria y decimal, sean:
A B
=
C
/*** -A -B
Entonces:
-c AlB A&B AAB A& (-B)
= =
0110011001100110 0000000001111111 0000000000000000
=
26214 127 0
***/
1001100110011001 1111111110000000 = 1111111111111111 = o 1 100 1 100 1 111 1 1 1 0000000001100110 0110011000011001 0110011000000000 =
= = =
39321 65408 65535 = 26239 = 102 26137 = 26112
84
Fundamentos de Programacion en Lenguaje C
La tabla 5.5 muestra las operaciones de asignaci6n simple y compuesta que pueden realizarse en C; como es usual, presenta la sintaxis de la operacion y el resultado que se obtiene. La asignacion es una operacion que puede realizarse como parte de una expresion, 0 bien como una instrucci6n. Como se coment6 en el Capitulo 4, asignar un valor a una variable equivale a guardar en la localidad reservada para esa variable, el valor de la expresion colocada del lado derecho del operador de asignacion. Esta operacion es la Hamada asignacion simple en la tabla 5.5. Las asignaciones compuestas implican un cambio en el valor que tiene almacenado el operando del lado izquierdo de la operacion de asignacion, cambio que puede ser un incremento aritmetico 0 geometrico, un ventaneo logico, un corrimiento de los bits, etc., tal como se indica en la tabla 5.5.
Tabla 5.5: Operaciones de asignacion con enteros sin signo Asignacion simple
A=B
Incremento aritmetico
A += B
Decremento aritmetico
A -= B
Incremento geometrico
A *= B
Decremento geometrico
A/=
Modulo
A 0/0= B
Corrimiento a Ia derecha
A»= B
Corrimiento a la izq.
A«= B
Ventana 0 Iogica:
A 1= B
Ventana Y Iogica:
A&= B
Ventana 0 exc1usiva
A A= B
B
Se Ie asigna a A el valor de B. B representa una expresion de tipo entero sin signo Se Ie asigna a A el valor que tenia asignado mas el valor de B. Se Ie asigna a A el valor que tenia asignado, decrementado en el valor de B Se Ie asigna a A el valor que tenia asignado, multiplicado por el valor de B Se Ie asigna a A el cociente del valor que tenia asignado, entre el valor de B Se Ie asigna a A el residuo de la division entera del valor que tenia asignado, entre el valor de B Se Ie asigna a A el valor binario que tenia, recorrido a la derecha B posiciones. Los bits de la izquierda se rellenan con ceros Se Ie asigna a A el valor binario que tenia asignado recorrido a la izquierda B posiciones. Los bits de la derecha se rellenan con ceros Se Ie asigna a A el valor binario que resulta de la operacion logica a nivel de bit
AlB Se Ie asigna a A el valor binario que resulta de la operacion logica a nive! de bit
A&B Se Ie asigna a A el valor binario que resulta de la operacion AA B
Tipos de Datos
85
En el ejemplo 5.5 se ilustran las operaciones de asignacion. Debe observarse que estas estan sujetas a las mismas restricciones que las operaciones aritmeticas y logicas normales; es decir, si se sale de rango la operacion, ocurren errores semanticos que el programador debe detectar, en virtud de que el compilador no puede realizar dicha tarea. El ejemplo 5.5 presenta la operacion y el resultado que se guardaria en el operando de la izquierda. Se considera que los distintos ejemplos presentados son independientes, y que al realizarse la operacion las variables tienen los valores indicados al inicio del ejemplo. Ejemplo 5.5: Ejemplo de operaciones de asignacion compuesta con enteros sin signo. Sean A y B enteros sin signo, y ... A = 25 B = 10
/***** entonces: *****/ A= A += A-= A *= AI: A 0/0= AI= A &= A A: A»= A«=
B B B B B B B
B B B B
implica implica implica implica implica implica implica implica implica implica implica
que que que que que que que que que que que
A A A A A A A A A A A
= = = = = = = = = = =
10 35 15 250 2 5 27 8 19 0 27 648
La tabla 5.6 muestra otros operadores, con los cuales pueden manipularse los bits de una variable entera sin signo, como es el corrimiento de los bits a la derecha y/o a la izquierda, adicionalmente se presenta el incremento y el decremento unitario, el calculo 0 determinacion de la direccion que ocupa una variable (apuntador) y el contenido de una localidad, de la cual se cuenta con la direccion (indireccion). Las operaciones de corrimiento a la derecha, corrimiento a la izquierda, indireccion, apuntador, incremento unitario y decremento unitario son operaciones tipicas de los ensambladores de los procesadores y microprocesadores de las computadoras; sin embargo, de la misma forma que para el caso de las operaciones logicas a nivel de bit, el lenguaje C dispone de estos mecanismos que 10 hac en muy poderoso y adecuado para aplicarse en el desarrollo de tare as que de otra forma solo serian posibles en lenguaje ensamblador.
86
Fundamentos de Programacion en Lenguaje C
Las operaciones de incremento 0 decremento unitario son muy utiles, porque pueden usarse para modificar el valor de una variable, en este caso un entero sin signo, a la vez que integran una expresion aritmetica 0 logica. Cuando se coloca el operador a la derecha de la variable, primero se calcula el valor de la expresion y luego se incrementa 0 decrementa el valor de la variable, y cuando el operador se coloca dellado izquierdo, primero se incrementa 0 decrementa la variable y luego se calcula el valor de la expresion que se desea calcular.
Tabla 5.6: Operaciones tipo lenguaje ensamblador con enteros sin signo. Corrimiento a la derecha
Corrimiento a la izqui erda
Indireccion Apuntador Incremento unitario
Decremento unitario
A »1 El resultado de esta expresion, es el valor bi-
nario que tenia asignado A, recorrido a la derecha una posicion. El primer bit de la izquierda se rellena con cero. Este valor se Ie queda asignado a A. A «1 El resultado de esta expresion, es el valor binario que tenia asignado A, recorrido a la izquierda una posicion. El primer bit de la derecha se rellena con cero. Este valor se Ie queda asignado a A. *A Esta expresion da el valor que se encuentra almacenado en la direccion A. &A El resultado de esta expresion es la direccion de la variable A. ++A Esta expresion da el valor que tenia asignado A, incrementado en uno. Este valor se Ie A++ queda asignado a A. --A Esta expresion da el valor que tenia asignado A-A, decrementado en uno. Este valor se Ie queda asignado a A
Las operaciones de indireccion y de calculo de una direccion permiten manejar de una manera sumamente flexible la memoria principal de la computadora, ya que el programador puede tener acceso de esta manera, a cualquier localidad de memoria; es decir, a manejar apuntadores de manera muy basta y flexible. El operador de indireccion (*) se puede usar de manera recursiva; asi por ejemplo *(*AP)) da el valor almacenado en la localidad de memoria cuya direccion esta almacenada en la variable AP.
Tipos de Datos
87
La tabla 5.6 muestra otras dos operaciones relacionadas con los enteros sin signa que son: la detenninacion de la longitud de memoria ocupada por el tipo de dato y la conversion de tipo de dato u operacion casta.
Tabla 5.7: Operaciones de tipo con enteros sin signo. Longitud del operando
sizeof (A)
Conversion de tipos
(unsigned)OT
Siendo A un entero sin signo, el resultado de esta expresion, es la longitud del entero sin signo que maneja el sistema. EI resultado de esta expresion, es Ia conversion del tipo de la expresion OT a un entero sin signo.
Los operadores sizeof y (unsigned) penniten calcular el numero de bits que ocupa el dato en memoria y convertir un dato a entero sin signo, respectivamente. Esta ultima operacion tambien se denomina en el ambiente de programacion como operacion de casta. A continuacion se comenta mas ampliamente esta operacion.
Conversiones de tipo En la evaluacion de una expresion cualquiera, se realizan las conversiones de tipo en fonna automatica, siguiendo las reglas indicadas en la seccion 4.16. En particular, cuando se combinan en una expresion aritmetica, un entero sin signo y un entero con signo, 0 un 'caracter, el resultado es un entero sin signo. Asimismo, al asignar el valor de un caracter, 0 entero con signo, a una variable declarada como entero sin signo, por medio de cualquiera de las instrucciones de asignacion, la conversion de tipos se hace automaticamente. No as! en el caso de un dato real 0 de punto flotante, en cuyo caso se debe indicar la operacion explicitamente, por medio del operador de "casta" que no es mas que el tipo al que se desea convertir encerrado entre parentesis, como se muestra en la tabla 5.7. Para convertir un entero sin signo a un entero de longitud grande, se llena con ceros los lugares excedentes. Tambien se pueden convertir enteros sin signo a caracteres, y en este caso, se trunca por la izquierda. Para convertir un real a un entero sin signo, se trunca la parte fraccionaria. Y para convertir un entero de longitud grande a uno sin signo, se eliminan los bits de orden superior.
88
Fundamentos de Programaci6n en Lenguaje C
Mediante el programa del ejemplo 5.6 se ilustra el funcionamiento de los distintos operadores aplicables a variables y constantes de tipo unsigned. Ejemplo 5.6: Programa "unsigned.c", ejemplos de operaciones con enteros sin SIgnO. #includeunsigned UNSA, UNSB; void DATOSO, ARITMETICASO, RELACIONALESO, LOGICASO, LOGIBITO, ASIGNACIONO, ESPECIALESO, ENSAMBLADORO; mainO
{ char COMAN DO; printf("\t\tPROGRAMA PARA ILUSTRAR EL MANEJO DE NUMEROS SIN SIGNO\n\n"); DATOSO; getcharO; while (1) { getcharO; printf("\tAhora presentamos los resultados de las operaciones\n"); printf("\tque pueden reallzarse can numeros sin signo. \n\n\n"); printf("\t\tEUGE UNA OPCION\n\n\n"); printf("\t\tMETER OTROS DATOS\t\tO\n"); printf("\t\tOPERACIONES ARITMETICAS\t\tA\n"); ll printf(lI\t\tOPERACIONES RELACIONALES\tR\n ); printf("\t\tOPERACIONES LOGICAS\t\tL\n"); printf("\t\tOPERACIONES LOGICAS DE BITS\tB\n"); printf("\t\tOPERACIONES DE ASIGNACION\tS\n"); printf(lI\t\tOPERACIONES DE ENSAMBLADOR\tM\n"); pri ntf(lI\t\tOPERACION ES ESPECIALES\tE\n \n \n"); printf(lI\t\tTERMINAR LA EJECUCION\t\tT\n\n\n"); printf("\tIntroduce el Comando del Tipo de Operaciones que deseas Observar\nll); COMANDO getcharO; switch (COMAN DO) { case 'A':{ARITMETICAS(UNSA,UNSB); break;} case 'B':{LOGIBIT(UNSA,UNSB); break;} case 'E':{ESPECIALESO; break;} case 'L':{LOGlCAS(UNSA,UNSB); break;} case 'M':{ENSAMBLADOR(UNSA); break;} case 'O':{DATOSO;break;} case 'R':{RELACIONALES(UNSA,UNSB); break;} case 'S':{ASIGNACION(UNSA,UNSB); break;}
=
Tipos de Datos
case 'T:exit(O); } } }
void DATOSO { pri ntf("\t\t\t\tDATOS\n \n "); printf("\t\tSe usaran para ilustrar las distintas operaciones con\n"); printf("\t\tenteros sin signo las variables UNSA y UNSB. \n"); printf("\t\tIntroduce a continuacion los valores de UNSA y UNSB. \n"); printf("\n\n\n\t\tUNSA = "); scanf("% u",&UNSA); "); printf("\n\t\tUNSB scanf("%u" ,&UNSB); } void ARITMmCAS(unsigned UNS1, unsigned UNS2) { printf("\n\n\t\tOPERACIONES ARITMETICAS\n\n"); printf("\t\tUNSl + UNS2 = %u\n", UNSl +UNS2); printf("\t\tUNSl - UNS2 = %u\n", UNSI-UNS2); printf("\t\tUNSl * UNS2 = %u\n", UNSl *UNS2); printf("\t\tUNSl / UNS2 = %u\n", UNS1/UNS2); printf("\t\tUNSl \% UNS2 = %d\n", UNSl % UNS2); printf("\t\tOprime\n"); getcharO; } void RELACIONALES(unsigned UNS1, unsigned UNS2) { printf("\t\tOPERACIONES RELACIONALES\n\n"); printf("\t\tUNSl < UNS2 = %u\n", (UNSl UNS2 = %u\n", (UNS1>UNS2»; printf("\t\tUNSl <= UNS2 = %u\n", (UNS1<=UNS2»; printf("\t\tUNSl >= UNS2 = %u\n", (UNS1>=UNS2»; printf(lI\t\tUNSl == UNS2 = %u\n", (UNS1==UNS2»; printf("\t\tUNSl != UNS2 = %u\n", (UNS1!=UNS2»; printf("\t\tOprime \n"); getcharO; } void LOGICAS(unsigned UNS1, unsigned UNS2) { printf("\t\tOPERACIONES LOGICAS\n\n"); printf("\t\t!UNSl = %u\n", (!UNS1»; printf("\t\tUNSl II UNS2 = %u\n", (UNSIIIUNS2»; printf("\t\tUNSl && UNS2 = %u\n", (UNS1&&UNS2»; printf("\t\tOprime \n");
89
90
Fundamentos de Programacion en Lengl!aje C
getcharO; } void LOGIBIT(unsigned UNSl, unsigned UNS2) { printf("\t\tOPERACIONES LOGICAS A NIVEL DE BIT\n\n"); printf("\t\tIVUNS1 = %u\n", (IVUNS1»; printf("\t\tUNSl I UNS2= %u\n", (UNSIIUNS2»; printf("\t\tUNSl & UNS2 = %u\n", (UNSl&UNS2»; printf("\t\tUNSl 1\ UNS2 = %u\n", (UNS1I\UNS2»; printf("\t\tOprime\n"); getcharO; } void ESPECIALESO { printf("\t\tOPERACIONES ESPECIALES\n\n"); printf("\t\tsizeof(unsigned) = %d\n", sizeof(unsigned»; printf("\t\t(unsigned)(2.S) = %u\n", (unsigned)(2.S»; printf("\t\tOprime \n"); getcharO; } void ENSAMBLADOR(unsigned UNS1) { unsigned *AP = &UNSl; printf("\t\tOPERACIONES TIPO ENSAMBLADOR\n\n"); printfC'\t\tUNS1> > 1 = %u\n", (UNS1> > 1»; printf("\t\tUNS1«1 = %u\n", (UNS1«1»; printf("\t\t*(&UNS1)= %u\n", *(AP»; printf("\t\t&UNSl = %u\n", &UNS1); printf("\t\t++UNSl = %u\n", ++UNS1); printf("\t\t--UNS1 = %u\n", --UNS1); printf("\t\tOprime \n"); getcharO; }
void ASIGNACION(unsigned UNS1, unsigned UNS2) { unsigned UNSC; printfC'\t\tOPERACIONES DE ASIGNACION\n\n"); printf("\t\tUNSC = UNS1 => UNSC = %u\n", (UNSC=UNS1»; printf("\t\tUNSC += UNSl => UNSC = %u\n", (UNSC+=UNS1»; printfC'\t\tUNSC -= UNS1=> UNSC = %u\n", (UNSC-=UNS1»;
Tipos de Datos
91
printf("\t\tUNSC *= UNSl => UNSC = O/ou\n", (UNSC*=UNSl)); printf("\t\tUNSC /= UNSl => UNSC = O/ou\n", (UNSC/=UNS1)); printf("\t\tUNSC \% == UNS2 ==> UNSC == %u\n", (UNSc%==UNS2)); printf("\t\tUNSC «= UNS2 ==> UNSC == %u\n", (UNSC«=UNS2)); printf("\t\tUNSC »== UNS2 => UNSC == O/ou\nll, (UNSC»==UNS2)); printfC'\t\tUNSC 1= UNSl => UNSC == %u\nll, (UNSq==UNSl)); printfC'\t\tUNSC &= UNSl ==> UNSC == O/ou\nll, (UNSC&=UNSl)); printf(lI\t\tUNSC A= UNSl => UNSC = O/ou\nll, (UNSCA=UNSl)); printf("\t\tOprime\n"); getcharO; }
5.1.4 Enteros con signo
Declaracion Como ya se menciono, C cuenta con tres diferentes longitudes de enteros con signo que son: corta, estandar y grande; las variables de este tipo se declaran de acuerdo a la siguiente sintaxis: especificador_de_aim especificador_de_aim especificador_de_aim
short int int long int
dentificador, identiicador, ... ; identiicador, identiicador, ... ; identiicador, identiicador, ... ;
En la declaracion de enteros de longitud corta 0 grande se puede omitir la palabra clave int. Por otro lado, si un compilador no tiene implantadas las longitudes corta y/o larga, se considera que el entero declarado con alguna de esas longitudes es de longitud estandar. Como en el caso de los enteros sin signo 0 como en cualquier otro caso de' declaracion de variables, se puede hacer la inicializacion de las variables directamente en la declaracion, tal como se muestra en el ejemplo 5.7, en el cual se hace la declaracion e inicializacion de las variables y luego se imprimen sus valores. Obviamente, la impresion de los enteros largos debe hacerse con el formato de enteros largos. Ejemplo 5.7: Programa "entdlong.c", declaracion e enicializacion de variables enteras. mainO { short int
CORTOl = 1, CORT02;
92
Fundamentos de Programacion en Lenguaje C
int ENTEROl = -10, ENTER02; long int LARGOl = 100000, LARG02; printf("CORTOl = 0/od\tCORT02 = %d\n", CORT01,CORT02); printf("ENTEROl = 0/od\tENTER02 = %d\n", ENTER01,ENTER02); printf("LARGOl = 0/0Id\tLARG02 = %ld\n", LARG01,LARG02);
Operaciones aritmeticas sobre enteros Las operaciones aritmeticas que pueden realizarse sobre los enteros con signo (largos, estandar y cortos), son las mismas .que se indican en la tabla 5.1 Y son aplicables a los enteros sin signo; adicionalmente, puede realizarse la operacion unaria: "negativo de". Las diferencias entre las operaciones con enteros de diferente longitud estan precisamente en la longitud de los numeros y por en de en los valores maximos de las variables que pueden emplearse. En el programa del ejemplo 5.11 se muestra una forma de obtener los valores maximos y minimos de los diferentes tipos de enteros, empleando los identificadores de la biblioteca. Estos son: INT_MAX, INT_MIN, LONG_MAX, LONG_MIN, SHRT_MAX Y SHRT_MIN. Si se mezclan enteros de diferente longitud en una expresion aritmetica, el resultado tiene la longitud del entero mas largo que interviene en la misma. En una asignacion se hace la conversion al tipo de dato de la variable a la que se Ie esta asjgnando valor, pero si esta es de menor longitud que la expresion dellado derecho de la asignacion, se genera un error semantico que no es detectado por el compilador, ya que se truncan los octetos mas significativos y solo queda almacenada la informacion que cabe en el area de memoria asignada a la variable. En el ejemplo 5.8 se ilustran las operaciones aritmeticas con enteros. En la operacion modulo 0 residuo (A % B), debe tomarse en cuenta que dicho residuo conserva el signo del dividendo. Ejemplo 5.8: Operaciones aritmeticas con enteros.
1* Sean A y B enteros con signa declarados como enteros estandar, y A = 17 Y B = -13; entonces: */
-A
= -17
A+B A-B A*B AlB
=4 = 30 = -221 = -1 =4
A% B
Tipos de Datos
93
Operaciones relacionales y logicas sobre enteros con signa Las operaciones relacionales de la tabla 5.2 tambien se aplican en su totalidad a los numeros enteros con signo de cualquiera de las tres longitudes. Unicamente hay que tomar en cuenta que un numero es menor entre mas negativo es. De igual manera se .aplican las operaciones logicas de la tabla 5.3 a los enteros con signo, con las mismas reglas de semantica que en el caso de los enteros sin signo; es decir, una cantidad no cero se considera que tiene el valor logico CIERTO y la que tiene el valor cero, toma el valor logico FALSO. El ejemplo 5.9 a continuacion, ilustra las operaciones relacionales y logicas realizadas con enteros con signo. Obviamente, por 10 que se comento en el parrafo anterior, en estos tipos de operaciones pueden mezc1arse operandos enteros de diferente tipo sin consecuencias en errores sintacticos 0 semanticos, siempre y cuando se tengan en cuenta los efectos de los truncamientos que se producen. Ejemplo 5.9: Operaciones logicas y relacionales con enteros. /* Sean A y B enteros con signo y A= 0, B = -10 Y C = 10; entonces: */ A< B A> B A <= B A >= B A == B A!= B A II B A&&B !A l(A> B) (A II B) && C (A < B) && B
= = = = = = = =
0 1 0 1 0 1 1 0 1 0 1 0
Operaciones logicas a nivel de bit y operaciones tipo lenguaje ensamblador, sobre enteros con signo Las operaciones indicadas en las tabla 5.4. y 5.6 tambien se aplican a los mimeros enteros con signo, desde luego, en el contexto de este tipo de numeros. A continuacion presentamos el ejemplo 5.10, en el que se ilustran las operaciones indicadas en las tablas referidas.
94
Fundamentos de Programacion en Lenguaje C
Ejemplo 5.10: Operaciones logicas a nivel de bit con enteros. Expresadas en forma binaria y decimal, sean:
A B
=
C
=
0110011001100110 111 1 1 1 1 1 1 000 0 001 0000000000000000
=
26214 -127 0
/*** Entonces: *** / -A -B -C AlB A&B AAB A & (-B) A»1 A«1 B++ B--
= = = = = = =
1001100110011001 0000000001111110 1 1 1 1 1 1 1 1 1 111 1 111 1 1 1 1 1 1 1 1 1 1 100 1 1 1 0110011000000000 100 1 100 1 1 1 100 1 1 1 0000000001100110 001 100 1 100 1 100 1 1 1100110011001100 111 1 1 1 1 1 1 0 0 0 0 0 1 0 1111111110000000
= = = = = = = = = =
-26215 126 -1 -25 26112 -26137 102 13107 -13108 -126 -128
5.l.5 Funciones estandar de enteros con signo En la bibliotecase cuenta con tres importantes funciones que realizan acciones relacionadas con enteros, estas son: la funcion atoi, que convierte una cadena ASCII que indica un numero entero, al correspondiente valor entero, la funcion rand 9 que da un entero pseudo-aleatorio y la funcion abs 9 que da el valor absoluto de su argumento entero. El uso de estas tres funciones de biblioteca se ilustra por medio de la funcion FUNCIONES, del programa "enteros.c" del ejemplo 5.11; este pennite probar las operaciones con enteros y entender 10 que sucede en situaciones criticas. Es recomendable correr el programa y probar sus resultados con diferentes datos. El programa esta construido de manera modular; inicialmente presenta un menu con los distintos grupos de operaciones, de los cuales se elige uno con la letra indicada en el menu y se obtienen los resultados de las operaciones del grupo. Ejemplo 5.11: Programa "enteros.c", operaciones y funciones con enteros.
#include#include #include int INTA, INTB;
Tipos de Datos
95
void DATOSO, ARITMmCASO, RELACIONALESO, LOGICASO, LOGIBITO, ASIGNACIONO, ESPECIALESO, ENSAMBLADORO, VMAXIMOSO, FUNCIONESO; mainO
{ char COMAN DO; printf("\t\t\tPROGRAMA PARA ILUSTRAR EL MANEJO DE NUMEROS ENTEROS\nll); DATOSO; getcharO; while (1) { getcharO; ll printf("\tAhora presentamos los resultados de las operaciones\n ); printf(,,\t que pueden realizarse con numeros enteros.\n\n\n"); ll printf(lI\t\tEUGE UNA OPCION\n\n\n ) ; printf("\t\tMETER OTROS DATOS\t\tO\n"); printf("\t\tOPERACIONES ARITMmCAS\t\tA\n"); printfC'\t\tOPERACIONES RELACIONALES\tR\n"); printf("\t\tOPERACIONES LOGICAS\t\tL\nll); printf("\t\tOPERACIONES LOGICAS DE BITS\tB\nll); printf("\t\tOPERACIONES DE ASIGNACION\tS\n"); printf("\t\tOPERACIONES DE ENSAMBLADOR\tM\n"); printf("\t\tOPERACIONES ESPECIALES\t\tE\n"); ll printf("\t\tVALORES MAXIMOS DE LOS ENTEROS\tV\n ); printf("\t\tFUNCIONES QUE MANEJAN ENTEROS\tF\n\n\n"); printf("\t\tTERMINAR LA EJECUCION\t\tT\n\n\n"); printf("\tIntroduce el Comando del Tipo de Operaciones que deseas Observar\nll); COMAN DO = getcharO; switch (COMANDO)
{ case'A':{ARITMmCAS(INTA,INTB); break;} case 'B':{LOGIBIT(INTA,INTB); break;} case 'E':{ESPECIALESO; break;} case 'F':{FUNCIONES(INTA,INTB); break;} case 'L':{LOGICAS(INTA,INTB); break;} case 'M':{ENSAMBLADOR(INTA); break;} case 'O':{DATOSO;break;} case 'R':{RELACIONALES(INTA,INTB); break;} case 'S':{ASIGNACION(INTA,INTB); break;} case 'V':{VMAXIMOSO; break;} case'T:exit(O);
} } } void DATOSO
96
Fundamentos de Programacion en Lenguaje C
{
printf(l\t\t\t\tDATOS\n\n"); printf("\t\tSe usaran para ilustrar las distintas operaciones con\n"); printf("\t\t enteros sin signo las variables INTA e INTB. \n"); printf(,,\t\tIntroduce a continuaci6n los valores de INTA y INTB.\n"); printf("\n\n\n\t\tINTA = "); scanf("%d" ,&INTA); printf("\n\t\tINTB = "); scanf("%d",&INTB); } void ARITMETICAS(int INT1, int INT2) { printf("\n\n\t\tOPERACIONES ARITMETICAS\n\n"); printf("\t\tINTl + INT2 = %d\n", INT1+INT2); printf("\t\tINTl - INTI = %d\n", INTl-INT2); printf("\t\tINTl * INT2 = %d\n", INTl *INT2); printf("\t\tINTl / INT2 = %d\n", INTl/INT2); printf("\t\tINTl \°/0 INT2 = O/od\n", INTI % INT2); printf("\t\tOprime\n"); getcharO; } void RELACIONALES(int INTl, int INT2) { printf("\t\tOPERACIONES RELACIONALES\n\n"); printf("\t\tlNTl < INT2 = °/od\n", (INTl INT2 = %d\n", (INT1>INT2»; printf("\t\tlNTl <= INT2 = °/od\n", (INTl <=INT2»; printf("\t\tINTl >= INT2 = %d\n", (INTl>=INT2»; printf("\t\tlNTI == INT2 = O/od\n", (INTI==INT2»; printf("\t\tINTl != INT2 = %d\n\n", (INT1!=INT2»; printf("\t\tOprime \n"); getcharO; } void LOGICAS(int INTl, int INT2) { printf("\t\tOPERACIONES LOGICAS\n\n"); printf("\t\t!INTl = %d\n", (lINTI»; printf("\t\tINTl II INT2 = %d\n", (INTlIIINT2»; printf("\t\tINTI && INT2 = %d\n", (INTI&&INT2»; printf("\t\tOprime \n"); getcharO; } void LOGIBIT(int INTl, int INT2) { printf("\t\tOPERACIONES LOGICAS A NIVEL DE BIT\n\n"); printf("\t\tINTI = %o\n", INTl);
Tipos de Datos
printfC'\t\tlNT2 = %o\n", INT2); printf("\t\trvINT1 = %o\n", (rvINT1)); printf("\t\tINT1 1 INT2 = %o\n", (INT1IINT2)); printf("\t\tINT1 & INT2 = %o\n", (INT1&INT2)); printf("\t\tINT1 A INT2 = %o\n", (INT1 AINT2)); printf("\tLos resultados estan en numeraci6n octal\n"); printf("\t\tOprime\n"); getcharO; } void ESPECIALESO { printfC'\t\tOPERACIONES ESPECIALES\n \n"); printf("\t\tsizeof(int) = %d\n", sizeof(int)); printf("\t\tsizeof(short int) = %d\n", sizeof(short int)); printfC'\t\tsizeof(long int) = %d\n", sizeof(long int)); printf("\t\t(int)( -2.5) = %d\n", (int)( -2.5)); printf("\t\tOprime \n"); getchar(); } void ENSAMBLADOR(int INT1) { int *AP = &INT1; printfC'\t\tOPERACIONES TIPO ENSAMBLADOR\n\n"); printf("\t\tINT1»1 = %d\n", (INT1»1)); printf("\t\tINTI < < 1 = %d\n", (INT1 < < 1)); printf("\t\t*(&INT1)= %d\n", *(AP)); printfC'\t\t&INT1 = %x\n", &INT1); printf("\t\t++INTl = %d\n", ++INTl); printf("\t\t--INTl = %d\n", --INT1); printf("\t\tOprime \n"); getcharO; } void ASIGNACION(int INTi, int INT2) { int INTC = 0; printf("\t\tOPERACIONES DE ASIGNACION\n\n"); printf("\t\tINTC = INT1 => INTC = %d\n", (INTC=INT1)); printf("\t\tINTC += INT1 => INTC = %d\n", (INTC+=INT1)); printf("\t\tINTC -= INTl ==> INTC == %d\n", (INTC-=INT1)); printfC'\t\tINTC *= INT1 => INTC == %d\n", (INTC*=INT1)); printf("\t\tINTC /= INT1 ==> INTC = %d\n", (INTC/=INT1)); printfC'\t\tINTC \%= INT2 => INTC = %d\n", (INTc%==INT2)); printf("\t\t INTC = %0 O\n", (INTC =255)); printf("\t\tINTC «=INT2 => INTC = %0 O\n", (INTC«=INT2)); printf("\t\tlNTC »=INT2 => INTC = 0/0 0 O\n", (INTC»=INT2)); printf("\t\tINTC 1= INT1 => INTC == %0 O\n", (INTCI=INTl)); printf("\t\tINTC &= INTI => INTC == %0 O\n", (INTC&=INT1)); printf("\t\tlNTC A= INT1 => INTC = %0 O\n ll , (INTC A ==INT1));
97
98
Fundamentos de Programacion en Lenguaje C
printf(,,\tLos resultados maracados con una 0 al final estan en numeraci6n octal\n"); printf("\t\tOprime\n"); getcharO;
} void VMAXIMOSO { printf("\t\tVALORES MAXIMOS DE LOS ENTEROS\n\n"); printf(,,\t\tshort MINIMO = %d\n", SHRT_MIN); printf("\t\tshort MAXIMO = %d\n", SHRT_MAX); printf("\t\tint MINIMO = %d\n", INT_MIN); printf("\t\tint MAXIMO = %d\n", INT_MAX); printf("\t\tlong MINIMO = %ld\n", LONG_MIN); printf("\t\tlong MAXIMO = %ld\n", LONG_MAX); printf("\t\tOprime\n"); getcharO; } void FUNCIONES(int INT1, int INT2) { printf("\t\tFUNCIONES QUE MANEJAN ENTEROS\n\n"); printf("\tCONVERSION CADENA A ENTERO CON LA FUNCION atoi( const char *s)\n"); printf("\t\t\tatoi(\11996\") = %d\n", atoi("1996")); printf("\tNUMERO PSEUDOALEATORIO CON LA FUNCION randO\n"); printf("\t\t\trandO = %d\n", randO); printf("\tVALOR ABSOLUTO DE UN ENTERO CON LA FUNCION abs(int n)\n"); printf("\t\t\tabs(INT1) = %d\n", abs(INT1)); printf("\t\t\tabs(INT2) = %d\n", abs(INT2)); printf("\t\tOprime \n"); getcharO; }
5.1.6 Reglas de precedencia y asociatividad La precedencia y la asociatividad son conceptos muy importantes que se aplican en el calculo de las expresiones aritmeticas, logicas y relacionales. La tabla 5.8 muestra las reglas de precedencia y asociatividad de los operadores en ellenguaje C. En esta tabla, los operadores estan presentados en orden de precedencia, por filas. Las primeras filas tienen mas precedencia que las ultimas. Los operadores que aparecen en la misma fila tienen igual precedencia.
Tipos de Datos
99
Las reglas de precedencia y asociatividad de la tabla 5.8 se aplican a las operaciones con enteros, pero igualmente se aplican a los demas tipos de datos a los cuales se aplican las operaciones correspondientes. Dichas reglas implican, por ejemplo, que en una expresi6n aritmetica, primero se realizan las multiplicaciones y luego las sumas y restas. Igualmente, primero se calculan las funciones y luego se realizan los productos y divisiones.
Tabla 5.8: Precedencia y asociatividad de los operadores . . Operadores
C) [ ] -> ++ -!
-
* /
Asociatividad
*
& CTIPO) sizeof
0/0
+ » « < <= > >= !=
-& /\
I &&
II ?:
= += -= *= /= 0/0= &= /\= 1= «= »= I
5.2
izquierda a derecha derecha a izquierda izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha izquierda a derecha derecha a izquierda derecha a izquierda izquierda a derecha
CARACTERES
5.2.1 Introducci6n Los caracteres son los simbolos de escritura unitarios que utiliza el ser humano para formar sus lenguajes. Hasta antes del advenimiento de los modos grcificos, las entradas y salidas a las computadoras se realizaban casi en su totalidad por medio de caracteres. Aun en la actualidad la entrada/salida de caracteres es uno de los medios de comunicacion con la computadora mas utilizados. Por ejemplo, la entrada por teclado se realiza por medio de caracteres ASCII y la salida a la mayoria de las impresoras de golpe tambien es por medio de caracteres. Asimismo, la salida a la pantalla de video, en sistemas operativos como MS-DOS y UNIX, es por medio de caracteres ASCII.
100
Fundamentos de Program acion en Lenguaje C
Los caracteres se han codificado en 5, 6, 7 u 8 bits. El C6digo Baudot, que fue uno de los primeros c6digos de caracteres, empleaba 5 bits. El c6digo ASCII, introducido por la ANSI, primeramente utiliz6 7 caracteres, luego introdujo un conjunto de caracteres gnificos, ademas de los caracteres de escritura y emple6 c6digos de 8 bits. Ellenguaje C soporta el tipo caracter y Ie asocia las caracteristicas numericas de los enteros sin signo que obedecen a las leyes de la aritmetica binaria, con la salvedad de que su longitud 0 espacio de memoria es de un octeto 0 byte. A continuaci6n se describen las caracteristicas de este tipo de datos, as! como de las funciones y operadores que son aplicables a ellos. Ejemplo 5.12: Programa "asciis.c", que da una lista de los caracteres ASCII imprimibles, con c6digo numerico mayor a 32 (40 en octal) y menor que 127 (177 en octal). #includemainO { char c='\40' ,CR=O; printf("USTA DE CARACTERES Y SUS VALORES ASCII\n"); while (c<'\177'){printf("% c\t% d\t",c,c++ ); if (CR++ >= 4){printf("\n"); CR=O;}} return 0;
}
La tabla 5.9 a continuaci6n, muestra la salida de caracteres ASCII y sus valores numeric os que entrega el programa del ejemplo 5.12. Tabla 5.9: Lista de caracteres imprimibles y sus valores ASCII. ! ( /
6 =
0
K R Y g n u
I
33 40 47 54 61 68 75 82 89 96 103 110 117 124
" )
0 7 >
E L S Z
a h 0
v }
34 41 48 55 62 69 76 83 90 97 104 111 118 125
#
*
1 8 ?
F M T [ b i P w
-
35 42 49 56 63 70 77 84 91 98 105 112 119 126
$
+ 2 9 @ G N
U \
c j q x ......
36 43 50 57 64 71 78 85 92 99 106 113 120 127
%
, 3 :
A
H 0 V
] d k
r y
37 44 51 58 65 72 79 86 93 100 107 114 121
&
4 ,
B I
P W A
e
1 s
z
38 45 52 59 66 73 80 87 94 101 108 115 122
,
5 <
C J
Q X
f m
t {
39 46 53 60 67 74 81 88 95 102 109 116 123
Tipos de Datos
101
5.2.2 DECLARACION La dec1aracion de una variable tipo canicter se realiza de la siguiente fonna: especificador_de_alm inicializador, ... ;
char identificador = inicializador, identificador =
Notese que bajo una misma dec1aracion pueden inc1uirse varias variables del tipo char y que es posible inicializar todas las variables al hacer la dec1aracion, como se comento en los casos de las dec1araciones de enteros; 0 tambien se puede omitir la inicializacion de algunas 0 de todas las variables dec1aradas. El especificador de tipo de almacenamiento especificador_de_alm, es el ya explicado en la Secc.3.2.2; en este caso las variables pueden ser automaticas, estaticas, extemas 0 registro y tambien se pueden declarar sinonimos del tipo char, mediante typedef.
5.2.3 Conversion caracteres'a enteros Los caracteres se pueden manejar como enteros de 8 bits que representan su codigo ASCII. De manera que la conversion de enteros a caractcres y viceversa es automatica. No obstante, al convertir un entero a caracter se truncan los 8 bits mas significativos del entero. El programa del ejemplo 5.13 ilustra estas conversiones, se imprime tanto el caracter A como el numero 1089 en notacion octal para que se observe que los 8 bits menos significativos de ambos son iguales, de manera que al truncar a 8 bits el numero 1089 da como resultado el caracter A. Las impresiones en pantalla resultado de la corrida, se presentan al final del programa. Al ejecutar una operacion aritmetica en la que intervienen enteros y caracteres, el resultado es un entero.Ejemplo 5.13: Programa "charconv .c", ilustra el proceso de conversion de caracteres a enteros y visceversa. mainO { char C ='A'; int ENT = 1089; printf("ASCII DE A = %d\n", C); printf("A EN OCTAL = %o\n", C); printf("1089 EN OCTAL = %o\n", ENT); printf("(char) 1089 = %c\n", C = (char)ENT); return 0; }
102
Fundamentos de Programacion en Lenguaje C
/* IMPRESIONES RESULTADO DE LA CORRIDA ASCII DE A A EN OCTAL 1089 EN OCTAL (char) 1089
= = = =
65 101 2101 A
*/
5.2.4 Operaciones con caracteres De hecho, todas las operaciones que son posibles con enteros sin signo son posibles con los caracteres. Esto Ie da un enonne poder de manejo de caracteres al lenguaje C, ya que pueden hacerse conversiones y manipulaciones con caracteres que no estan disponibles en otros lenguajes. Por ejemplo, para convertir de mayusculas a minusculas basta restar 32, 0 pasar el caracter por una compuerta & con 95. El ejemplo 5.14 ilustra estas conversiones y divers as operaciones en las que intervienen caracteres. Debe recalcarse por otro lado, que los caracteres confonnan un conjunto ordenado y por 10 tanto pueden aplicarse a ellos las operaciones relacionales basicas; es decir, se pueden comparar para detenninar si son iguales, uno es mayor que el otro, etcetera. Ejemplo 5.14: Operaciones con caracteres. Sean A y B caracteres, y A='A' Y B = 'a'; entonces: A +.2 B-2 A*2 A/2 A % 255 !(A < B) A< B A> B A <= B A >= B A == B A!= B A II B A&&B !A
= = = = = = = = = = = = = =
'c'
' ,
'e' ', 'A' 0 1 1 1 0 0 1 1 1 0
A 132 B & 95 AAB A« 1 B» 1 A++ A-B++ B-sizeof( char) 512 + A A+B A - B A I B A& B
= = = = =
'a' 'A' 32
= = = = = =
'@'
'e' '-'
'B'
= =
'b'
'" 1 577 162 32 'a' 'A'
= =
'6'
5.2.5 Funciones estandar que operan con caracteres Algunas de las funciones estandar mas importantes con las que se manejan caracteres, son las mostradas en la tabla 5.10, las cuales se encuentran en la bi-
Tipos de Datos
103
blioteca, por 10 que debe incluirse esta biblioteca en los programas que utilicen dichas funciones. N6tese que un canicter es el argumento de todas estas funciones. Tabla 5.10: Biblioteca isalnum(c) isalpha(c) iscntrl(c) isdigit(c) isgraph(c) islower(c) isprint(c) ispunct(c) isspace(c) isupper(c) isxdigit( c) tolower(c) toupper(c)
Toma el valor verdadero (1), si el argumento es letra 0 mimero Toma el valor verdadero (1), si el argumento es letra Toma el valor verdadero (1), si el argumento es canicter de control Toma el valor verdadero (1), si el argumento es numero Toma el valor verdadero (1), si el argumento es imprimible, excepto espacio Toma el valor verdadero (1), si el argumento es letra minuscula Toma el valor verdadero (1), si el argumento es imprimible Toma el valor verdadero (1), si el argumento es imprimible, excepto espacio, letra 0 numero Toma el valor verdadero (1), si el argumento es espacio, avance de linea, nueva linea, retorno de carro, tabulador, tabulador vertical Toma el valor verdadero {I), si el argumento es letra mayliscula Toma el valor verdadero (1), si el argumento es dig ito hexadecimal Convierte a minuscula, regresa la minuscula Convierte a mayliscula, regresa la mayliscula
La tabla 5.11 muestra un conjunto de funciones de entrada/salida que manejan caracteres. Por medio de estas funciones puede tenerse comunicaci6n interactiva con el usuario, metiendo y sacando caracteres. EI uso de estas funciones se ilustra mas ampliamente en el Capitulo 7. Estas funciones se encuentran disponibles a traves de la biblioteca stdio.h Tabla 5.11: BibliotecagetcharO getchO getc(buffer) putchar(c) putc(c,buffer) ungetc( c,buffer)
Mete un canicter de la entrada estandar, debe usarse retorno de carro para indicar que ya esta tecleado el caracter Mete un caracter de la entrada estandar, no se necesita usar el retorno de carro como en el caso anterior Retorna el siguiente caracter del buffer del archivo Escribe el caracter c en la salida estandar Escribe el caracter c en el buffer del archivo Regresa el caracter, de nuevo al buffer del archivo
104
5.3
Fundamentos de Programacion en Lenguaje C
NUMEROS DE PUNTO FLOTANTE
5.3.1 Introducci6n Originalmente, los procesadores trabajaban a nivel de lenguaje de maquina con mimeros enteros. Para trabajar con numeros reales 0 de punto flotante, hubo necesidad de crear un codigo numerico especial, asi como todos los programas para ejecutar las distintas operaciones aritmeticas y funciones reales que pueden realizarse con este tipo de numeros. Posteriormente las computadoras inc1uyeron un hardware especial para realizar las operaciones de punto flotante u operaciones con numeros reales, conocido como el procesador de punto flotante. Esto se hizo porque las operaciones de punto flotante eran las que los procesadores ejecutaban mas lentamente y los procesadores de punto flotante, como los de la familia 80x87, ejecutan dichas operaciones directamente y mas rapido. Hoy, las maquinas Pentium han incorporado el co-procesador en el mismo chip. Existe un estandar que es el IEEE 754-85, que especifica eI codigo para representar numeros de punto flotante en una computadora digital. Este estandar debe ser respetado tanto en el disefio de procesadores de punto flotante como de compiladores y herramientas diversas de hardware y software. El estandar 75485 define el formato basico de los numeros de punto fiotante, las operaciones aritmeticas y de redondeo que deben realizar los procesadores y los compiladores, adem as de las distintas situaciones de excepcion,o de error, como es la division entre cero, el desbordamiento, etcetera. Como puede apreciarse en la figura 5.1, los fonnatos de los numeros de punto flotante simples y los de doble precision tienen tres eiementos que son: signo, exponente y fraccion. Segun se aprecia en la figura referida, la longitud del signo es de 1 bit en los dos esquemas, la del exponente es de 8 bits en el caso de los numeros de punto flotante simples (float en C) y de 11 en el caso de los de doble precision (double en C), y final mente la de la fraccion es de 23 bits en el caso de los mimeros de punto fiotante simples y de 52, en el caso de los de doble precision. Es evidente que el codigo numerico estandarizado para representar numeros reales tiene la estructura general de los numeros con notacion cientifica, excepto que no se contempla una parte entera, sino solamente la parte fraccionaria. De esta manera pueden almacenarse rangos de numeros sumamente amplios y con una gran precision. En la practica, los valores maximos que maneja el procesador 0 el compilador dependen de la forma en que se implemento el sistema. Obviamente, los numeros reales constituyen un tipo de datos fundamental en la ingenieria y en los sistemas computacionales, practicamente todos los lenguajes de programacion soportan el manejo de este tipo de numeros, ya que con
Tipos de Datos
105
ellos se trabajan todo tipo de problemas y aplicaciones. A continuacion se explican los distintos aspectos del tratamiento computacional de los numeros reales en el lenguaje C, las dec1araciones, las operaciones que pueden realizarse con ellos, las funciones de biblioteca que tiene disponibles para trabajar con este tipo de datos, etcetera.
bMs
bms bMs
[oms
52 bMs
bms bMs bMs
bi~
bms
bit man os signlificOltivo
Mas
bms
significa~i"o
Figura 5.1: Formato de los numeros de punto flotante.
5.3.2 Declaraci6n La dec1aracion de numeros de punto flotante y de uoble precision, en lenguaje C es como sigue: especificador_de_aim float identificador= inicializador, identificador=inicializador, ... ; especificador_de_a Im dou ble identificador= in icia Iizador, identificador= in icia Iizador, ... ;
Debe tomarse en cuenta que es opcionalla inicializacion de las variables, y en este caso el especificador de almacenamiento puede ser extern, static, auto 0 typedef.
La implantacion de una variable de tipo float en un registro no es posible en la nlayoria de los ambientes de programacion, sin embargo, si esto se especifica, normalmente el compilador solo ignora la solicitud. Obviamente, cuando se hace una definicion de tipo con typedef no se inicializa el correspondiente identificador. EI uso de los otros tres especificadores de almacenamiento determina el campo de aplicacion de la variable, como ya se ha venido explicando con anterioridad.
106
Fundamentos de Programaci6n en Lengljaje C
5.3.3 Conversion punto flotante-entero Tal como en los casos de los enteros y los caracteres, se pueden hacer conversiones de otros tipos de dato a numeros de punto flotante 0 de doble precision, mediante la operacion de casta, en la forma ilustrada en el ejemplo 5.15. Ejemplo 5.15: Conversiones de tipo a punto flotante. Supongamos que REAL es una variable dec1arada como tipo float, ENTERO una variable entera y DOBLEP una de doble precision. As!, podemos hacer las siguientes conversiones: REAL DOBLEP
=
(float)ENTERO; (double) REAL;
En general, cuando una operacion se realiza con operandos de diferente tipo, estos se convierten automaticamente, siguiendo un conjunto reducido de reglas. Las conversiones que podrian implicar perdidas de informacion por truncamiento, como es la de un float a entero 0 a caracter, pueden producir una advertencia del compilador ("warning"). Las reglas de conversion que se aplican mas frecuentemente durante la realizacion de una operacion aritmetica en la que intervienen numeros enteros y de punto flotante, son las siguientes: a) b)
Si cualquiera de los operandos es double, el otro se convierte a double. Si uno de los operandos es float y el otro es de longitud igual 0 menor, se convierte el otro operando a float.
No esta permitido el uso de un numero de punto flotante como subindice.
5.3.4 Operaciones con numeros de punto flotante Las operaciones aritmeticas, relacionales y logicas, que ya fueron descritas en su aplicacion a enteros y caracteres, se aplican tambien a los numeros de punto flotante. Las operaciones tipo ensamblador, logicas bit a bit y corrimientos son ilegales en el caso de los numeros de punta flotante. Los incrementos y decrementos igualmente pueden aplicarse. En el ejemplo 5.16 se ilustran las operaciones con numeros de punto flotante, estas operaciones se realizaron con el programa "reales.c" del ejemplo 5.17. Las operaciones marcadas como ilegales en los ejemplos 5.16 y 5.l7, son sefialadas como ilegales por el compilador del lenguaje C.
Tipos de Datos
Ejemplo 5.16: Operaeiones tonees:
d~
punto flotante. Sean A = 255.5 Y B = - 556, en-
1* Operaciones Aritmeticas *1 A+ B = -300.5 A*B = -142058.0 A % 255 es ilegal -B = 556.0
1* Operaciones A <. B
A <= B A == B !(A < B)
Relacionales
-A
*1
0
= 0 = 0 = 1
=
= -811.5 = -0.459532 = -255.5
B-A
AlB
1* Operaciones Logicas *1 A II B =1 !A
107
A>B A >= B A!= B
=1 =1
A&&B
=1
=
1
0
Operaciones de Logicas Binaria */ A&B es ilegal A A B es ilegal AlB es ilegal - A es ilegal B »1 es ilegal A «1 es ilegal
1*
1* Incrementos y ++A --A
= =
Decrementos 256.5 255.5
*/ ++B --B
= =
-555.0 -556.0
=
8
1* Operaciones Diversas */ sizeof(float) =
4
sizeof(double)
Ejemplo 5.17: Programa "reales.e", ilustra las operaeiones con numeros de punto flotante. mainO { float A = 255.5, B = -556; printf("A + B = 0/06.1f\t\t", A + B); printf("A * B = 0/06.1f\t", A * B); printf(IIA % 255 es ilegal\t"); printf("A < B = %d\t\t",A < B); printf("A <= B = %d\t\t",A <= B); printf(IIA == B = %d\t\t",A == B); printf("A II B = %d\t\t",A II B); printf("!A = %d\t\t", !A); printf("- B = 0/06.1f\t\t", -6);
printf("B - A = o/06.1f\n", B - A); printf("A / B = %f\n", A / B); printf("!(A < B)= %d\n",!(A < B»; printf("A> B = %d\n",A > B); printf("A >= B = %d\n",A >= B); printf("A != B = %d\n",A!= B); printf("A && B = %d\n",A && B); printf("- A = 0/o6.1f\n", -A); printf("A A B es ilegal\n");
108
Fundamentos de Programacion en Lenguaje C
printf("A < < 1 es ilegal\t\t"); printfC'B > > 1 es ilegal\n"); printf("++A == %6.1f\t\t",++A); printf("--A == %6.1f\n",--A); printf("--B == %6.1f\n",--8); printf("++B == %6.1f\t\t",++B); printf("AIB es ilegal\t\t"); printf("A&8 es ilegal\n"); printf("sizeof(f1oat) == %d\n", sizeof(float)); printf("sizeof( double)= %d\n", sizeof( double)); return 0; }
5.3.5 Reglas de precedencia En las operaciones con numeros reales 0 de punto flotante simples 0 de doble precision se aplican las mismas reglas de precedencia indicadas en la tabla 5.8.
5.3.6 Funciones estandar con numeros de punto flotante En la bibliotecase dispone de las funciones matematicas basicas, como son las funciones trigonometricas, logaritmicas, exponenciales, hiperbolicas, etc. Estas funciones tienen como parametros, numeros de punto flotante de doble precision y retoman numeros de doble precision; no obstante, si se les pasa como argumento un numero de tipo float, normal mente el compilador no indica error. .En la tabla 5.12 se qescriben brevemente las funciones de la biblioteca . Algo que es relevante y se debe remarcar es que ellenguaje C no cuenta con un operador de exponenciacion y que esta operacion se realiza por medio de la funcion pow(X,Z), donde X es la base y Z el exponente. Tambien debe remarcarse que por medio de estas funciones puede realizarse cualquier calculo algebraico 0 trigonometrico y se pueden programar formulas con funciones hiperbolicas, directamente. EI programa "fcreales.c" del ejemplo 5.18, ilustra el manejo de las funciones matematicas de la biblioteca , de manera simple. Recuerdese que la biblioteca se incluye mediante la directiva #include. En el programa, por sencillez se declaran e inicializan dentro de main las variables que se utilizanin como parametros de las funciones. Se puede analizar la operacion de las funciones y los errotes que se producen cambiando los valores de inicializacion de las variables, recompilando y ejecutando nuevamente el programa. El valor 0 retomado por la funcion main solo indica la terminacion normal del programa. En el ejemplo 5.19 se muestran los resultados que el programa del ejemplo 5.18 present:l en la pantalla del monitor. En el programa del ejemplo 5.18, A es un angulo dado en radianes, X un numero cuyo modulo debe ser menor que la unidad, B debe ser un numero real
Tipos de Datos
109
mayor que cero, Y y Z son numeros reales cualquiera y N debe ser un entero. Si el m6dulo de X es mayor que uno, obviamente se producini un error al calcular el arco sene 0 el arco coseno. De igual manera si B es negativo se producini un error al calcular ellogaritmo natural 0 de base diez.
Tabla 5.12: Bibliotecasin(A) cos(A) tan(A) asin(X) acos(X) atan(Y) sinh(B) cosh(B) tanh(B) exp(B)
log(C) log10(C) pow(X,Z) sqrt(B)
ceil(Z) floor(Z) fabs(B) Idexp(8,N) fmod(BrA)
Seno de A, A esta en radianes Coseno de A, A esta en radianes Tangente de A, A esta en radianes Arco sene de X, el resultado esta en el range [-1[/2, 1[12], X debe estar en el ran go [-1, 1] Arco coseno de X, el resultado esta en el rango [0, 1[], X debe estar en el rango [-1, 1] Arco tangente de Y, el resultado esta en el rango [-1[/2, 1[12] Seno hiperb6lico de B Coseno hiperb6lico de B Tangente hiperb6lica de B Funci6n exponencial, tf3 Logaritmo natural de C, In(C), C>O Logaritmo de base 10 de C, log/O(C), C>O Potencia Z de X, X Z, Ocurre error de dominio si X=O y Z<=O, 0 si X <0 Y Z no es entero Raiz cuadrada de B, .J B , B> =0 Menor entero no menor que Z, retomado como double Mayor entero no mayor que Z, retomado como double Valor absoluto de B, IBI B*2N Residuo de punto flotante de BIA, con el mismo signo que B. Si A es cero ocurre un error de desbordamiento
Ejemplo 5.18: Programa "fcreales.c", ilustra el manejo de las funciones de numeros de punto flotante. #includemainO
{ double A int N
= 3;
= 2.1, B = 5.3, X = 0.74, Y = 1.01, Z = 3.1;
110
Fundamentos de Programacion en Lenguaje C
printf("\nsin(A) = %6.4f\t",sin(A»; printfC'tan(A) = %6.4f\t",tan(A»; printf("acos(X)= %6.2f\t",acos(X»; printf("sinh(B)= %e\t",sinh(B»; printf("tanh(B)= %e\t", tanh(B»; printf("log(B) = %e\t",log(B»; printf("pow(X,Z)= %e\t",pow(X,Z»; printfC'ceil(Z) = %e\t",ceil(Z»;
printf("cos(A) = %6.4f\n", cos(A»; printf("asin(X)= %6.2f\n", asin(X»; printf("atan(Y)= %6.2f\n", atan(Y»; printf("cosh(B)= %e\n" ,cosh(B»; printf("exp(B) = %e\n",exp(B»; printf("log10(B)= %e\n",loglO(B»; printf("sqrt(B) = %e\n",sqrt(B»; printf("floor(Z) = %e\n", f1oor(Z»;
printf("fabs(B) = %e\tl1,fabs(B));
printf("ldexp(B,N)= %e\nl1 ,ldexp(B,N));
printf("fmod(B,A)= %e\n",fmod(B,A»; return 0; }
Ejemplo 5.19: Resultados de la corrida del programa "fcreales.exe". sin(A) tan(A) acos(X) sinh(B) tanh(B) 10g(B) pow(X,Z) ceil(Z) fabs(B) fmod(B,A)
5.4
0.8632 = -1.7098 = 0.74 1.00165ge+02 = 9.999502e-01 = 1.667707e+00 = 3.932044e-01 = 4.000000e+00 = 5.300000e+00 = 1.100000e+00
cos (A) asin(X) atan(Y) r:osh(B) exp(B) log10(B) sqrt(B) f1oor(Z) Idexp(B,N)
= -0.5048 0.83 = 0.79 = 1.00170ge+02 = 2.003368e+02 = 7.24275ge-Ol = 2.302173e+00 = 3.000000e+00 = 4.240000e+Ol
=
ARREGLOS
5.4.1 Introduccion Los arreglos fueron introducidos y utilizados desde los primeros lenguajes de programaci6n. Son estructuras de datos que permiten materializar computacionalmente, elementos como las matrices matematicas, cadenas de caracteres, secuencias y c6digos numericos que representan imagenes, sonidos u otro tipo de senales, entre otras cosas. Los arreglos permiten realizar conjuntos de datos de un mismo tipo y posteriormente ordenarlos, consultarlos de manera aleatoria, etcetera. Los arreglos pueden ser unidimensionales 0 multidimensionales y en lenguaje C pueden tenerse ademas de los arreglos de datos basicos, arreglos de apuntadores, de uniones, de estructuras de datos, etc. Computacionalmente hablando, podemos decir que un arreglo es un conjunto de datos de la misma es-
Tipos de Datos
111
tructura 0 tipo que se almacenan secuencialmente en la memoria principal y pueden ser accesibles directamente por medio del indice 0 indices. Los indices deben ser variables enteras y definen el elemento del arreglo al cual nos estamos refiriendo. Como 10 ilustra la figura 5.2, el primer elemento de un arreglo corresponde al de indice 0, tratandose de un arreglo bidimensional sena el elemento 0,0, y as} sucesivamente.
VECfOR1:
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
456 980 678 -12 76 45 51 5 26 25
[0][0]
[0][1] [0][2] [1][0]
-90 876 400 -12
[1][1]
8
[1][2] [2][0] [2][1] [2][2]
0 34
5 78
Figura 5.2: Arreglos.
La figura 5.2 ilustra de manera esquematica, la estructura de un arreglo unidimensional de 10 elementos, al que se ha llamado VECfOR1 y otro bidimensional de 3 x 3 elementos al que se Ie da el nombre de MATRIZ_A. Es evidente que la direcci6n donde se inicia el almacenamiento del arreglo, esta determinada por la etiqueta VECTOR1, y en el caso del arreglo bidimensional del ejemplo, la direcci6n de inicio de dicho arreglo esta definido por la etiqueta MATRIZ_A. A
112
Fundamentos de Programacion en Lenguaje C
manera de ilustracion, en las distintas localidades de los arreglos se han almace- . nado datos enteros.
5.4.2 Dec1araci6n La declaracion de arreglos sigue la estructura general de las declaraciones, descrita en la seccion 3.2, es decir, consta de un especificador de clase de almacenamiento opcional, un especificador de tipo de dato, el identificador del arreglo, el operador u operadores de arreglo con su respectiva dimension y el inicializador opcional. Cuando se declara e inicializa un arreglo de caracteres, la dimension es opcional. De la siguiente fonna: especificador_de_alm esp_de_tipo_dato inicializador .... ;
idenhftdi~enSi6n][dimensi6n] ... = ,
5.4.3 Inicializaci6n En la seccion 3.2.6 se explico la sintaxis de la iniCializacion de arreglos; esto es, por medio del conjunto de valores iniciales de los distintos elementos del arreglo, agrupados por medio de Haves. No obstante, hay que reconocer que dicha inicializacion representa un problema espechll de interpretacion, porque se requiere tener clara la fonna en que se asignan los val ores a los distintos elementos del arreglo. EI programa "arreglos.c", mostrado en el ejemplo 5.20 presentado a continuaci6n, ilustra la forma en que son asignados los distintos valores iniciales a los elementos del arreglo. La asignacion de valores al arreglo unidimensional es trivial, es en orden de secuencia del valor de,1 indice, como se aprecia en el programa y en el resultado de su corrida, para el caso de la inicializacion del arreglo VECTORl. Con respecto al arreglo bidimensional MATRIZ_A, se puede notar que si el primer indice representa la fila y el segundo la columna de una matriz, los valores iniciaies se asignan fila por fila. As!, a la primera fila de la matriz se Ie asignan los valores 00, 01, 02 y 03, a la segunda los valores 10, 11, 12, Y 13 y a la tercera 20, 21, 22 Y 23. En el propio ejemplo 5.20 se muestra el resultado de la corrida. Se eligieron los valores de manera que quedara claro este mecanismo de asignacion. En el ejemplo podemos decir que MATRIZ_A tiene 3 vectores de 4 elementos 0 3 filas y 4 columnas.
Tipos de Datos
113
Ejemplo 5.20: Programa "arreglos.c", inicializacion de arreglos. Este programa ilustra el manejo de arreglos unidimensionales y bidimensionales. mainO { int VECTOR1[10] = {O, 10, 20, 30, 40, 50, 60, 70, 80, 90}; / /Inicializacion int MATRIZ_A[3][4] = {OO, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23}; / /Inicializacion int 11, 12; printfC1\n\n\n\n\n\n\n\n\n\nll); printfC1ELEMENTOS DE VECTOR1: \nll); for (11=0; 11<10; 11++) {printfCVECTOR1[%d] = %d\n ll , 11, VECTOR1[11]);} printfC'\nELEMENTOS DE MATRIZ_A: \n"); for (11=0; 11 <3; I1 ++) for (12=0; 12<4; 12++) {printf(IIMATRIZ_Al[%d][%d] = %d\n 11,12,MATRIZ_A[Il][12]);} return 0; } ll
,
ELEMENTOS DE VECTOR1:
ELEMENTOS DE MATRIZ_A:
VECTOR1[0] VECTOR1[1] VECTOR1[2] VECTOR1[3] VECTOR1[ 4] VECTOR1[S] VECTOR1[6] VECTOR1[7] VECTOR1[8] VECTOR1[9]
MATRIZ_Al[O][O] MATRIZ_Al[O][l] MATRIZ_Al[0][2] MATRIZ_A1[0][3] MATRIZ_A1[1][1] MATRIZ_Al[1][2] MATRIZ_Al[1][3] MATRIZ_Al[2][O] MATRIZ_Al[2][1] .MATRIZ_Al[2][2] MATRIZ_Al[2][3]
= = = = = = = = = =
0 10 20 30 40 50 60 70 80 90
= = = = = = = = = = =
00 01 02 03 11 12 13 20 21 22 23
5.4.4 Ejemplo de aplicacion El ejemplo 5.21, muestra una aplicaci6n importante de los arreglos que es la aritmetica de matrices. Ilustra las operaciones basicas de suma y resta de matrices, multiplicaci6n de un vector por una matriz y multiplicaci6n de una matriz por otra matriz. Ejemplo 5.21: Programa "oparreg.c", operaciones matematicas basicas con arreglos. Este programa ilustra algunas operaciones con ARREGLOS en forma de aritmetica de matrices.
114
Fundamentos de Programaci6n en Lenguaje C
mainO {
int VECTOR1[3] = {O, 10, 20}, VECTOR2[3] = {O, 0, O}; int MATRIZ_A[3][3] = {O, 1, 2, 10, 11, 12, 20, 21, 22}; int MATRIZ_B[3][3] = {O, 1, 2, 10, 11, 12, 20, 21, 22}; int MATRIZ_C[3][3] = {O, 0, 0, 0, 0, 0, 0, 0, O}; int 11, 12, 13; printf("\n\n\n\n\n\n\n\n\n\n"); printf("SUMA DE MATRIZ_A Y MATRIZ_B: \n"); for (11=0; 11<3; 11++) for (12=0; 12<3; 12++) {
printf("MATRIZ_C[O/od][O/od] = °/od\n", Il,I2,(MATRIZ_C[I1][I2]=MATRIZ_A[Il][12]+MATRIZ_B[11][12])); } printf("RESTA DE MATRIZ_A Y MATRIZ_B: \n"); for (11=0; 11<3; 11++) for (12=0; 12<3; 12++) { printf("MATRIZ_C[O/od][O/od] = °/od\n", 11,I2,(MATRIZ_C[ll][12]=MATRIZ_A[11][12]-MATRIZ_B[Il][12]); } printf("MULTIPLICACION DE MATRIZ_A POR VECTOR1: \n"); for (11=0; 11 <3; 11 ++) {
for (12=0; 12<3; 12++) VECTOR2[1l] += MATRIZ_A[Il][12] printf("VECTOR2[O/od] = °/od\n", 11, VECTOR2[11]); }
* VECTOR1[12];
printf("MULTIPLICACION DE MATRIZ_A POR MATRIZ_B: \n"); for (11=0; Il <3; 11 ++) for (12=0; 12<3; 12++) { for (13=0; 13<3; 13++) {MATRIZ_C[Il][I2] += MATRIZ_A[U][13] * MATRIZ_B[13][12];} printf("MATRIZ_C[O/od][O/od] = O/od\n", 11, 12, MATRIZ_C[I1][I2]); }
return 0; } RESULTADOS DE LA CORRIDA
Tipos de Datos
MATRIZ_C[O][O] MATRIZ_C[O][l] MATRIZ_C[0][2] MATRIZ_C[l][O] MATRIZ_C[l][l] MATRIZ_C[1][2] MATRIZ_C[2][0] MATRIZ_C[2][1] MATRIZ_C[2][2]
= = = = = = = = =
0 2 4 20 22 24 40 42 44
MATRIZ_C[O][O] MATRIZ_C[O][l] MATRIZ_C[0][2] MATRIZ_C[l][O] MATRIZ_C[1][1] MATRIZ_C[1][2] MATRIZ_C[2][0] MATRIZ_C[2][1] MATRIZ_C[2][2]
= = = = = = = = =
115
0 0 0 0 0 0 0 0 0
MULTIPLIC. DE MATRIZ_A POR VECTORl:
MULTIPLIC. DE MATRIZ_A POR MATRIZ_B:
VECTOR2[0] = 50 VECTOR2[1] = 350 VECTOR2[2] = 650
MATRIZ_C[O][O] MATRIZ_C[O][l] MATRIZ_C[0][2] MATRIZ_C[l][O] MATRIZ_C[1][1] MATRIZ_C[1][2] MATRIZ_C[2][0] MATRIZ_C[2][1] MATRIZ_C[2][2]
= = = = = = = = =
50 53 56 350 383 416 650 713 776
Debe recordarse que la suma y resta de matrices puede efectuarse si las matrices tienen las mismas dimensiones y que la suma 0 la resta se efecruan sumando 0 restando los elementos de la misma posicion. En el ejemplo 5.21, se dec1aran tres matrices de dimension 3 x 3 (MATRIZ_C, MATRIZ_A Y MATRIZ_B) Y dos vectores (VECTOR1 y VECTOR2). La suma y la resta de matrices se realiza elemento por elemento, variando los indices, mediante dos instrucciones for anidadas y calculando los valores de los elementos de la matriz resultante, por medio de la expresion MATRIZ_C[U][I2]=MATRIZ_A[U][I2] ± MATRIZ_B[Il][I2], que se ejecuta y luego ahi mismo se manda a imprimir su resultado. Por otro lado, se puede efectuar la multiplicacion de dos matrices A y B, si sus dimensiones son mxn y nxp, resultando una matriz de dimension m x p. Los elementos Cij de la matriz resultante se obtienen del producto de los vectores representados por la fila i de la matriz A y la columna j de la matriz B. Un caso particular es el producto de dos vectores, que puede verse como el producto de una matriz m x 1 por otra 1 x p, el resultado es un numero que se calcula como el producto punto vectorial. Por ejemplo: <3,2,3> x <1,5,1> = 3+10+3 = 16. Asi pues, cada elemento de la matriz resultante del producto de dos matrices, puede verse como el producto punto de los vectores que representan la fila i de la matriz A, por la columna j de la matriz B. Se remarca con negrillas la expresion matematica que materializa este producto de vectores, en el programa del ejemplo 5.21
116
5.5
Fundamentos de Programacion en Lenguaje C
APUNTADORES
5.5.1 Introducci6n El uso de apuntadores ha dado a los lenguajes de programacion un mayor poder y flexibilidad, ya que es posible referirse a traves de ellos a to do tipo de datos 0 de estructuras. Con los apuntadores se pueden tener todos los modos de direccionamiento posibles y por medio de ellos es factible pasar todo tipo de informacion entre un segmento de programa 0 funcion y otro. El lenguaje C permite el uso de apuntadores referidos a todo tipo de datos, estructuras y funciones, 10 cual, como ya se comento, Ie da un gran poder y flexibilidad. Los apuntadores son variables que contienen una direccion de memoria principal y estan asociados a un tipo particular de dato 0 estructura; sin embargo, mediante el uso de la operacion casta se puede cambiar el tipo de apuntador, 0 10 que es 10 mismo, el tipo de dato asociado al apuntador. Existe ademas el apuntador al tipo void que pennite referirse a una direccion de memoria cualquiera, sin que tenga asociado un tipo de dato especifico. Al asociarle un tipo de dato a un apuntador, se Ie asocia tambien la longitud del objeto unitario, de manera que un incremento del apuntador implica sefialar al siguiente elemento del mismo tipo, almacenado en memoria. Este incremento puede representar un aumento de la direccion a que se refiere el apuntador, de 1, 2, 4, 6 0 n bytes, dependiendo del tipo de dato 0 estructura asociado. Por ejemplo: Una variable tipo char ocupa un byte Un entero short ocupa dos bytes Un entero long ocupa cuatro bytes Un elemento tipo float ocupa cuatro byte Un tipo double ocupa seis bytes Una cadena de caracteres puede ocupar cualquier numero de bytes La figura 5.3 muestra como ejemplo, la estructura en memoria del arreglo de caracteres Hamado MENSAJE. Como puede apreciarse, MENSAJE tiene 20 localidades de memoria de un byte cada una. En cada localidad se encuentra almacenado un canicter. Por otro lado, MENSAJE representa tambien la direccion de inicio de este arreglo de caracteres; su valor de direccion puede ser almacenado en otra variable de tipo apuntador como APCHAR, que entonces estara apuntando a la misma direccion de inicio del arreglo, tal como 10 sefiala la figura 5.3.
Tipos de Datos
APCHAR:I
117
MENSAJEI~ MENSAJE:
H 0 L A Q
APCHAR+5
U
E
T A L IE
5
T A S '\n' '\0'
Figura 5.3: Apuntadores a caracteres.
Ellenguaje C pennite manejar los valores de los apuntadores ordenados en la misma fonna que las direcciones de memoria que ocupan los datos que tienen asociados. Asimismo, pennite realizar operaciones aritmeticas para ca1cular la direcci6n especifica que ocupa un dato 0 elemento de un arreglo 0 estructura. Por ejemplo, en la figura 5.3 APCHAR + 6 apunta al canicter numero 6 del arreglo (como sabemos, el primer elemento del arreglo es el numero 0). Debe insistirse en que el identificador MENSAJE representa la direcci6n de inicio del arreglo de caracteres, es un valor constante asignado por el compilador y no puede ser modificado. EI contenido de la localidad MENSAJE[O], en el caso del ejemplo de la figura 5.3. es el caracter H y el apuntador APCHAR + 5 apunta al canicter contenido en la'localidad MENSAJE[S] que es el canicter Q. N6tese que la cadena de caracteres esquematizada en la figura 5.3, como cualquier otra cadena manejada en len.guaje C, tennina en el canicter nulo (\0). Debe suponerse que la cadena y el apuntador a caracter de la figura 5.3 fueron creados mediante dec1araciones como las siguientes: char MENSAJE[20] = "HOLA QUE TAL ESTAS \n"; char *APCHAR = MENSAJE;
La figura 5.4 representa un arreglo de enteros que ilustra el caso de los apuntadores a entero de tipo short, en este caso el incremento unitario del apuntador representa dos bytes, ya,que la longitud de un entero short es de dos bytes.
118
Fundamentos de Programacion en Lenguaje C
Un entero short puede representarse por cuatro digitos hexadecimales como se aprecia en la misma figura.
APCCR+5
Figura 5.4: Apuntadores a enteros cortos.
En la figura 5.4, CORTOS representa un arreglo de 20 enteros tipo short; APCOR contiene la direccion de CORTOS y por 10 tanto apunta al inicio de CORTOS y APCOR + 5 apunta al sexto elemento del arreglo (ya que el primero es el elemento CORTOS[O]). En la figura 5.5 se ilustran los apuntadores a enteros largos. En este caso se tiene un arreglo de 20 enteros largos (de 4 bytes) que se ha llamado LARGOS. Ahora en APLARGOS que es un apuntador a entero de tipo long se ha almacenado la direccion de inicio del arreglo LARGOS y por 10 tanto apunta a dicha direccion, mientras que APLARGOS + 5 apunta al sexto entero largo del arreglo. Los ejemplos de las figuras 5.3, 5.4 Y 5.5, ilustran como incrementa el valor de un apuntador 0 de una expresion con apuntadores, dependiendo del tipo de datos asociado al mismo. ASl, si se trata de apuntadores a canicter, el incremento unitario del apuntador representa un incremento real de un byte, tratandose de un apuntador a un entero corto, dicho incremento unitario representa dos bytes y si se trata de un apuntador a enteros largos, el incremento unitario representa cuatro bytes.
Tipos de Datos
APLARGOs:1 LARGOS ~ LARGOS:
F
~
;A
~
0 r" 3
... 7
F 111
l ~
J I
APLARGOS +5
~
[)F
F~ £_1
FA FF 10
no ~
119
;; F
1
~
;; F
J: 1 'l d ~ g
~
A.
~
1
4
Figura 5.5: Apuntadores a enteros largos.
Cuando se trata de apuntadores a estructuras, la longitud unitaria asociada con la aritmetica de apuntadores es la de la estructura, es decir la suma de las longitudes de todos los campos que integran la estructura.
5.5.2 Dec1araci6n La declaraci6n de apuntadores, igualmente, sigue la estructura general de las declaradones descrita en la secci6n 2.2; 0 sea, consta de un especificador de clase de almacenamiento opcional, un especificador de tipo de dato, el operador apuntador, el identificador 0 bien una expresi6n que representa el declar.ador del apuntador y el inicializador opcional. De la siguiente forma: especificador_de_aim Esp_de_tipo_dato *( declarador) = inicializador; EI declarador puede ser un identificador calificado por uno 0 varios operadores de arreglo 0 apuntador, segun 10 que se requiera declarar; es decir podemos realizar declaraciones de tipos compuestos, mezclando operadores de tipo de estructura de datos. En el ejemplo 2.1 se incluyeron algunas dec1araciones de apuntadores; particularmente un apuntador a un entero, una funci6n que retorna un apuntador a un entero y un apuntador a una funci6n que retorna un entero. El ejemplo 5.22 presenta otras declaraciones de apuntadores, la primera linea de dicho ejemplo
120
Fundamentos de Programaci6n en Lenguaje C
muestra la declaracion de un apuntador a canicter, un apuntador a un apuntador a canicter (doble indireccion), un apuntador a arreglo de caracteres, un arreglo de apuntadores a canicter, una funcion que retoma un apuntador a canicter y un apuntador a una funcion que retoma un canicter. La segunda linea del ejemplo declara un conjunto similar de apuntadores, pero ahora aplicados a enteros y la tercera es tambien similar pero se aplica ahora a numeros de punto flotante. Ejemplo 5.22: Declaraciones de apuntadores. char
*APCHR, *(*APAPCHR), (*APSARCHR)[ ], *APARCHR[ ], *FAPCHRO, (*APFCHR)O;
int
*APENT, *(*APAPENT), (*APSARENT)[ ], *APARENT[ ], *FAPENTO, (*APFENT)O;
float
*APFLO, *(*APAPFLO), (*APSARFLO)[ ], *APARFLO[ ], *FAPFLOO, (*APFFLO)O;
5.5.3 EI operador de direcci6n de memoria & y el operador de contenido de memoria * El operador & se usa para referirse ala direccion en que se encuentra almacenado un dato. Asi:
P = &C; asigna la direccion del dato C a la variable P y decimos que P apunta a C. EI operador & solo se aplica a datos que se encuentran en memoria principal, no se puede aplicar a expresiones, constantes 0 variables tipo registro. EI operador * se usa para referirse al dato que se encuentra almacenado en una direcci6n indicada por un apuntador. EI ejemplo 5.23 ilustra el manejo de estos dos operadores, se declaran dos variables enteras: X y Y, un arreglo de enteros Z, un apuntador a unentero APE, una variable de punto flotante y un apuntador a una variable de punto flotante F y APF, respectivamente, y con estos elementos se hacen distintas operaciones que se comentan en el propio ejemplo. Ejemplo 5.23: Manejo de apuntadores con los operadores & y float F, * APF; int X=l, Y=2, Z[lO];
*.
/* Declaraci6n de variable y apuntador a variable de punto flotante */ /* Declaraci6n variables enteras y arreglo de enteros
*/
Tipos de Datos int *APE; APE = &X; Y = *APE; *APE = 0; APE = &2[0]; *APE = *APE + 10; Y = *APE + 1; APF = (float*) APE; F = *APF;
121
/* Declaraci6n de apuntador a entero * / /* APE apunta a X * / /* Se asigna a Y el valor contenido en la direcci6n a que Apunta APE, que es igual a X, entonces Y = 1 */ /* Se la asigna a X el valor de 0 */ /* APE apunta ahora la direcci6n del elemento 0 de Z */ /* Se incrementa en 10 el contenido de Z[O] */ /* Se Ie asigna a Y el contenido de Z[O] + 1 * / /* Se Ie asigna al apuntador APF la direcci6n contenida en APE convirtiendola a direcci6n de numero de punto flotante */ II Se Ie asigna a F el contenido de la dir. apuntada por II APF
5.5.4 Modos de direccionamiento EI modo de direccionamiento es el procedimiento que utiliza la computadora para determinar la direccion don de se encuentra almacenado un dato, para acceder a dicho dato. En terminos generales, el lenguaje C permite cuatro modos de direccionamiento basicos: Inmediato Directo Indirecto Indexado Ellenguaje C materializa estos modos por medio de los apuntadores y de los operadores de direccion & y de contenido * Se tiene direccionamiento "inmediato" cuando el dato que se va a utilizar en una operacion es una constante que forma parte de la correspondiente instruccion. En el ejemplo 5.24, las dos primeras Hneas de programa utilizan direccionamiento inmediato, respecto a las constantes entera y caracter que aparecen en las instrucciones de asignacion. Las lineas tercera y cuarta utilizan direccionamiento "directo", porque los datos que se utilizan en las expresiones matematicas son variables en las cuales se tiene almacenada la informacion. Las line as quinta y sexta utilizan direccionamiento "indirecto", porque los datos se obtienen de la direccion que esta almacenada en los apuntadores. Finalmente, las lineas septima y octava utilizan direccionamiento "indexado" porque se refieren a arreglos y estructuras de datos, la direccion del dato al que se requiere acceder es la direccion base 0 direccion de inicio del arreglo 0 estructura, mas un corrimiento, dado en funcion del lugar que ocupa el dato dentro del arreglo 0 estructura. 0
122
Fundamentos de Programacion en Lenguaje C
Ejemplo 5.24: Ilustraci6n de los modos de direccionamiento.
ENTA CAR ENTC FLOTA
+=
ENTA FLOTA
= =
ENTA
=
ENTA
=
=
= +=
2-I
ENTA es una variable entera */ CAR es una variable carckter */ ENTC Y ENTB son variables enteras */ FLOB; FLOTA Y FLOTB son variables de punto flotante */ *APENTB; /* APENTB es un apuntador a entero */ *APFLOTB + 125; /* APFLOTB es apuntador a variable de punto flotante */ ARRA[3] + ARRA[4]; /* ARRA es un arreglo de numeros enteros */ *(ARRA + 3);
'at - 32; ENTA + ENTC;
/* /* /* /*
5.5.5 Apuntadores, arreglos y aritmetica de apuntadores Consideremos la declaraci6n:
int A[lO]; la cual define un arreglo de tamafio 10, 0 sea tiene diez elementos, diez variables que integran el arreglo, 0 diez areas de memoria que almacenan un dato de tipo entero cada una. La figura 5.6 muestra de manera esquematica este arreglo. Las direcciones de cada elemento estan representadas por los especificadores de los elementos A[O], A[l], etc., y los cuadros representan las areas de memoria en donde se almacenan los datos. Supongamos que en alguna parte del programa se Ie asignan a los distintos elementos del arreglo los valores que se presentan en la misma figura 5.6.
A[O] A[l]
28 -3
Al21
8 -90
A[3]
A[4]
56
A[5]
98
A[6] A[7]
-7 77 54 577
A[8]
A[91
Figura 5.6: Esquema de un arreglo de datos.
Tipos de Datos
123
Ahora consideremos que existe la declaracion de un apuntador a un entero que se denomina APE; esto es int *APE;
de manera que la asignacion: APE = &A[O];
hace que el apuntador APE senale al elemento 0 del arreglo A; es decir, APE contiene la direccion del elemento A[O]. En la figura 5.7 se ilustra este concepto. La asignacion
x = *APE; copia el contenido de A[O] en X. Si APE apunta a un determinado elemento de un arreglo, entonces APE + 1 apunta al siguiente elemento del arreglo y APE + I apunta al I-esimo elemento despues de APE y asi sucesivamente. La figura 5.8 ilustra la manera en que operan los apuntadores. Muestra como al sumar un numero entero a un apuntador 0 direccion en memoria, se apunta 0 direcciona al siguiente elemento del mismo tipo.
X:
A[O] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9]
I
APE:
28
I
28 -3 8 -90 56 98 -7
-
77 54 577
Figura 5.7: Apuntador.
&A[O]
I
124
Fundamentos de Programacion en Lenguaje C
APE:
&A[O] &A[O]+1
A[O] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9]
28 8
&A[O]-!l-2
=L_-----C
-90 56 98
77 54
577
Figura 5.8: Incrementos de apuntadores.
La aritmetica de apuntadores se aplica al direccionamiento indexado de datos. En otras palabras, incrementar en 1 un apuntador, equivale a encontrar la direccion del siguiente dato y no de la siguiente localidad de memoria. Por otro lado, el nombre de un arreglo es sinonimo de la direccion del elemento inicial del mismo arreglo. De manera que es 10 mismo asignar direcciones de 'las siguientes dos formas: APE = &A[O];
APE
= A;
Asimismo, una referencia a A[I], tambien puede escribirse como *(A + I). Hay una importante diferencia entre el nombre de un arreglo y un apuntador y es que el apuntador es una variable que puede cambiar de valor al asigmirselo por medio de expresiones como APE=A, APE++, etc. En cambio, la direccion del arreglo en memoria es constante y no se puede modificar. Asi pues, son incorrectas las siguientes expresiones: A = APE;
A++;
Paso como parametro del apuntador a un arreglo Cuando un nombre de arreglo se pasa como argumento de una funcion, 10 que se pasa es la direccion de la localidad inicial de memoria del arreglo, copiandose
Tipos de Datos
125
dicho valor de direccion inicial del arreglo en la variable interna que representa el panimetro. El ejemplo 5.25 ilustra el paso de parametros de tipo apuntador a un arreglo y el manejo basico de" la aritmetica de apuntadores; el ejemplo presenta un segmento de programacion de una funcion que calcula la longitud de una cadena. Ejemplo 5.25: Funcion que calcula la longitud de una cadena. int LONGCAD(char *5) { int N; for (N = 0; *5 != '\0'; 5++) N++; return (N); }
En general, solo se pueden ejecutar operaciones aritmeticas con apuntadores que tengan asociado el mismo tipo de dato. Son validas todas las operaciones relacionales entre operandos tipo apuntador, como son: Menor que Mayor que Menoroigualque Mayor 0 igual que Igual que Diferente de
< > <= >= !=
Tambien son validas la suma y la resta aritmeticas, siempre y cuando no den como resultado una direccion de un dato de otro tipo diferente del de los operandos, ya que en este caso se produce un error semantico. EI ejemplo 5.~6 ilustra el uso de una resta de apuntadores para retornar la longitud de una cadena, es un procedimiento alternativo al de la funcion del ejemplo 5.25. Ejemplo 5.26: Funcion que calcula la longitud de una cadena (metodo 2). int LONGCAD2( char *5) { char *APCAR = 5; while ( *APCAR != '\0') APCAR++; return (APCAR - 5);
}
126
Fundamentos de Programacion en Lenguaje C
5.5.6 Cadenas y apuntadores a canicter Como ya se indico, una cadena es un arreglo de caracteres. El arreglo termina con el canicter nulo '\0' que es el terminador de cadena que utiliza ellenguaje C por disefio. La longitud total de una cadena es igual al numero de caracteres que tiene dicha cadena, mas uno. Cuando se realiza una operacion como: char *PMENSAJE; PMENSAJE = "ya es el momenta";
10 que se hace es asignar a PMENSAJE la direccion de inicio de la cadena constante "ya es el momento". No se copia la cadena. Esto hace que en C no se puedan procesar las cadenas como elementos unitarios, tienen que procesarse como arreglos de elementos, en este caso caracteres. Ahora debe reiterarse que el nombre de un arreglo, en este caso de caracteres, es equivalente a una direccion con stante que no se puede modificar. Consideremos las siguientes dec1araciones: char AMENSAJE[ ]; char * PMENSAJE ="yo soy cadena";
En este caso, AMENSAJE es un arregl0, se pueden modificar sus caracteres individualmente, pero AMENSAJE siempre apuntara al inicio de la cadena. Por el contrario, PMENSAJE es un apuntador, y modificando su contenido se hara referencia a otro lade; es decir, a otra cadena. Sobre estos apuntadores se puede hacer aritmetica de apuntadores. Por ejemplo, las siguientes instrucciones: PMENSAJE += 4; printf("%s", PMENSAJE);
hacen que se imprima en pantalla el siguiente mensaje: oy cadena
Inicializacion de arreglos de apuntadores a cadenas de caracteres El ejemplo 5.27 muestra un problema importante que es el de la inicializacion de un arreglo de apuntadores a cadenas de caracteres. En el ejemplo 5.27 se tiene una funcion Hamada MES_NOMBRE, la cual da como resultado (retorna) el apuntador al nombre de mes que corresponde al numero que se Ie pasa como
Tipos de Datos
127
panimetro. N6tese que la inicializaci6n del arreglo hace que se escriban en ellos apuntadores al inicio de cada cadena, mientras que las cadenas se almacenan de acuerdo a las tecnicas del compilador, sin que tenga que preocuparse de ella el programador. Ejemplo 5.27: Programa "meses.c", inicializacion de arreglos de apuntadores. mainO { int MES; printfC'Dame el Numero de MES: \n"); scanf("%d", &MES); printf("EI Nombre del MES es: %s\n",MES_NOMBRE(MES»; }
char *MES_NOMBRE(int M) { char *NOMBRE[ ] = { "mes ilegal", "enero", "feb rero ", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre" } return ((M < 1 }
II
M > 12) ? NOMBRE[O]: NOMBRE[M];
Funciones que operan con cadenas El lenguaje C dispone de varias funciones de biblioteca para el manejo de cadenas. Ya se indic6 que las cadenas no se pueden tratar como datos unitarios, sino que se tienen que procesar sus caracteres de manera individual. Las funciones de la bibliotecade C, permiten manejar las cadenas como si fueran ele .. mentos unitarios, en esta biblioteca se dispone de funciones para copiar, comparar y concatenar; buscar ocurrencias de un prefijo en una cadena, determinar su longitud, etcetera. La tabla 5.13 muestra, en su primera columna, el prototipo de la funcion, omitiendo los tipos de los argumentos: 5, cs y ct que son cadenas de caracteres, n es un entero y c un caracter. La segunda columna de la tabla presenta la descripcion de la tarea que realiza cada una de las funciones de la primera columna. Por ejempl0, mediante la funci6n strcmp se puede realizar un ordenamiento alfabetico de cadenas de caracteres. Mediante la funci6n strstr se pueden hacer busquedas de ocurrencias de cadenas en un archivo de caracteres, y asi sucesivamente.
128
Fundamentos de Programaci6n en Lenguaje C
Tabla 5.13: Biblioteca
char *strcpy(s,et) char *strncpy(s,et,n)
char *strcat(s,et) char *strncat(s,et,n)
int *strcmp(cs,et) int *strncmp(s,et,n) char *strchr(cs,c) char *strrchr(sc,c) size_t *strspn( cs,et) size_t *strcspn(cs,et) char *strpbrk(cs,et)
char *strstr(cs,et) size_t *strlen(cs) char *strerror( n) char *strtok(s,et)
Copia la cadena ct a la cadena s inc1uyendo '\0', retoma el apuntador al inicio de la cadena s Copia hasta n caracteres de la cadena ct a la cadena s reyena con '\0' si ct tiene menos de n caracteres, retoma el apuntador al inicio de la cadena s Concatena la cadena ct al final de la cadena s, retoma el apuntador al inicio de la cadena s Concatena hasta n caracteres de la cadena ct al final de la cadena s inc1uyendo '\0', retoma el apuntador al inicio de la cadena s Compara la cadena cs con la cadena ct, retoma <0 si cs0 si cs>ct. Compara hasta n caracteres de la cadena cs con la cadena ct, retoma <0 si cs 0 si cs>ct. Retoma un apuntador a la primera ocurrencia de c en cs o el NULO si c no esta presente. Retoma un apuntador a la ultima ocurrencia de c en cs 0 el NULO si c no esta presente. Retoma la longitud del prefijo de cs que contiene los caracteres de ct. Retoma la longitud del prefijo de cs que consiste en los caracteres que no estan ct. Retoma un apuntador a la primera ocurrencia en la cadena cs de cualquier caracter de la cadena ct 0 el NULO si ninguno esta presente. Retoma un apuntador a la primera ocurrencia en la cadena ct en la cadena cs 0 el NULO si no esta presente. Retoma la longitud de la cadena cs. Retoma un apuntador a una cadena defmida por la implantaci6n, correspondiente al error n. Busca en s tokens delimitados por caracteres de ct
EI programa del ejemplo 5.27 prueba algunas de las funciones de la tabla 5.13. En este programa tambien se introducen por medio de la directiva #define algunas macros para definir mensajes; con estas macros se forman los mensajes del menu. En este ejemplo, el cic10 interactivo de presentaci6n del menu y ejecuci6n de la elecci6n, se forma con la pareja ETIQUETA-GOTO.
Tipos de Datos
129
Ejemplo 5.28: Programa "ejsm-cad.c".
/* Defnicion de Macros de Mensajes */ #define L1 "RUTINA5 DE PRUEBA DE LA5 FUNCIONE5 DE MANEJO DE CADENA5" #define L2 "\t\t 5upongase que 51 y 52 son cadenas\n\n" #define L3 "\tstrcat" #define 13 "\tagrega una copia de 52 al final de 51 \n" #define L4 "\tstrncat" #define 14 "\tagrega n caracteres de 52 a 51 \n" #define LS "\tstrcmp" #define 15 "\tcompara 52 y 51 \n" #define L6 "\tstrncmp" #define 16 "\tcompara n caracteres de 52 con 51 \n" #define L10 n\tswab" #define 110 "\tintercambia de posicion los caracteres de 52 cada 2 y los pasa a 51\n" #define L7 "\tstrcpy" #define 17 "\tcopia 52 en 51 \n" #define L8 "\tstrncpy" #define 18 "\tcopia n caraeteres de 52 en 51 \n" #define L9 "\tstrlen" #define 19 "\tda la longitud de 52\n"
/* Rutina de espera para lectura del instructivo en pantalla */ #define letrsper printf("Para continuar oprime"); #define espera letrsper while (getcharO!= I\n') #define pagina printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n") int comando/N,RCOMP; char 5[1], 51[40], 52[10]; comparacion(int com)
{ if (com == 3){ printf("No. de caracteres a comparar\n"); scanf("%d",&N); RCOMP = strncmp(51,52,N);} else RCOMP = strcmp(51 /52); pagina; printf("RE5ULTADO DE LA COMPARACION\n"); if (RCOMP ==O)printf("\n\ncadenas iguales\n"); else printf("\n\ncadenas diferentes\n"); printf("Resultado de la comparacion RCOMP = %d\n\n\n\n",RCOMP); getcharO; espera; } mainO { pagina; printfC'%s%s%s%s%s%s%s%s%s%s",L1,L2,L3,13,L4,14,LS,15,L6,16); printfC%s%s%s%s%s%s%s%s\n\n\n\n\n\n\n",L10,110,L7,17,L8,18,L9,19);
130
Fundamentos de Programacion en Lenguaje C
espera; pagina; CICLO: printfCIINDlCA TU ELECCION CON EL NUMERO A LA DERECHA DE LA INST Y\n"); printf("\n \n%s\tO\n%s\t1 \n%s\t2\n%s\t3\n%s\t4\nII ,L3,L4, L5 ,L6,Ll 0); printf(l%s\t5\n%s\t6\n%s\t7\n\n\n\n\n\n\n\n",L7,L8,L9); scanf("%d",&comando); pagina; if (comando < 4) {printf("CADENA 5l\n"); scanf("%s",5l);} if (comando < 7) {printf("CADENA 52\nn); scanf("%s",52);} switch (comando) { case O:{ strcat(5l,52); break;} case 1:{ printf("No. de caracteres a agregar\n"); scanf("%d",&N); strncat(51,52,N); break;} case 2: case 3:{comparacion(comando); break;} case 4:{ printf("No de bytes a \n"); scanf("%d",&N); swab(52,51,N); break;} case 5:{strcpy(5l,52); break;} case 6:{ printf("No. de caracteres a copiar\n"); scanf("%d",&N); strncpy(5l,52,N); break;} case 7:{printf("CAOENA\n"); scanf("%s",51); printf("La longitud es de %d caracteres\n\n\n",strlen(5l»; getcharO; espera;} } pagina; printf("ESTADO FINAL DE LAS CADENA5\n"); printf("\n\nCADENA 51 \n%s\n\n\nCADENA 52\n%s\n",51,52); printf("\nContinuamos?\n"); scanf("%s",5); if «5[0] ==lsl)11 (5[0] == 151» goto CICLO; }
5.5.7 Apuntadores vs. arreglos multidimensionales En lugar de los arreglos multidimensionales,se pueden manejar arreglos de apuntadores a arreglos. Por ejemplo, las siguientes dec1araciones pueden ser equivalentes: int A[10][20]; /* Arreglo de 10 filas y 20 columnas */ /* Equivalente a decir arreglo de 10 vectores de 20 elementos * / /* Arreglo de 10 apuntadores a entero */ int *8[10];
Tipos de Datos
131
Con las dos declaraciones podemos referimos a arreglos bidimensionales de enteros. La diferencia es que en el caso de la declaracion del arreglo B, podriamos estar refiriendonos a 10 arreglos de enteros de diferente dimension, mientras que el caso de la declaracion de A, se refiere a 10 arreglos de enteros, todos de longitud 20. Esta flexibilidad en el manejo de arreglos puede ser util en algunas circunstancias. En el manejo de cadenas es fundamental, como ya se ha hecho notar con anterioridad.
5.5.8 Apuntadores a funciones Los apuntadores a funcion pueden dar una gran flexibilidad y poder a los programas. En el Capitulo 6 se presenta un programa que materializa un automata de estados finitos como analizador lexicografico que hace un uso muy amplio de los apuntadores a funcion. La declaracion de una apuntador a funcion es de la siguiente forma: int (*APFUNCION)( );
Suponiendo que BAJAS es una funcion perfectamente bien definida en el programa, podemos realizar la siguiente asignacion en el transcurso del programa: APFUNCION
= BAJAS;
Luego, podemos mandar a ejecutar de forma altemativa la funcion BAJAS, con la siguiente instruccion: (*APFUNCION)O;
En el Capitulo 6 se retoma este aspecto, al discutir la ejecucion de funciones en calidad de instrucciones.
5.6 ESTRUCTURAS 5.6.1 Introducci6n Una estructura es una coleccion de variables de tipos diversos, agrupadas bajo un nombre, las cuales se pueden declarar y manejar como un objeto unitario. Las estructuras permiten manejar los datos correspondientes a las distintas ca-
132
Fundamentos de Programaci6n en Lenguaje C
racteristicas de un mismo elemento u objeto, de manera integrada, permiten disenar tablas 0 bases de datos que se pueden crear y actualizar, de manera sencilla y eficiente. De hecho, el concepto de tabla 0 base de datos tan utilizado en nuestros dias en los sistemas de informacion, esta basado en este tipo de datos. Otros lenguajes, como Pascal, cuentan tambien con este tipo de datos, con el nombre de tipo record. De manera que podemos decir que una estructura es un conjunto de elementos 0 campos que podemos ver y tratar como un registro unitario. La figura 5.9 ilustra de manera grafica, la forma en que esta concebida una estructura. En el ejemplo de la figura, la estructura tiene 6 campos: Nombre, Numero de Control, Edad, Sexo, Telt§fono y Direcci6n. Estos datos forman un registro, por ejemplo, de un directorio de empleados 0 de proveedores.
NOMBRE
NO. CONT
DIRECCION
Figura 5.9: Estructura de datos.
5.6.2 Declaraci6n Una estructura se dec lara por medio de la palabra clave struct9 como se indica a continuacion: especificador_de_aim struct nombrestruc {listadecampos} listadeclaradores; Como en los casos anteriores, el primer elemento de la dec1aracion es el tipo de almacenamiento, el cual tambien puede ser el especificador de definicion de tipo typedef, usado con la finalidad de definir un tipo de estructura general que luego se pueda usar para declarar arreglos de estructuras 0 similares. La lista de campos tiene el formato de una lista de dec1araciones y define todos los elementos 0 campos de la estructura, y la lista de declaradores representa las variables con la esttuctura disenada, que van a existir durante la ejecucion del programa. EI ejemplo 5.29, ilustra la dec1aracion y manejo de elementos de estructuras. Define la estructura de datos llamada COMPLEJO, luego por medio de ella crea una variable y un apuntador a este tipo de variable estructura. Finalmente, ilustra
Tipos de Datos
133
el manejo de la notacion punto y/o ->, para trabajar con los campos 0 elementos de la estructura. EI manejo de numeros complejos por medio de estructuras puede ser util en la elaboracion de programas de computadora para implantar sistemas de ingenieria basados en el analisis por medio de numeros de este tipo. Podemos pensar en analizadores de circuito, sistemas de codificacion. etcetera. Ejemplo 5.29: Programa "estrucjs.c", estructura para manejar numeros complejos. #include{float REAL, IMAG; } COMPLEJO; typedef struct COMPLEJO Z, *AP_Z; mainO { (&z)->REAL = 3.5; Z.IMAG = 2.5; printf("REAL = %f\tIMAGINARIO= %f\n", Z.REAL, Z.IMAG); AP_Z = &2; printf("REAL = %f\tIMAGINARIO = %f\n", AP_Z->REAL, AP_Z->IMAG); printf("DIRECCION DEL APUNTADOR A Z = %x\n", AP_2); getcharO; return (0); }
5.6.3 Referencia a elementos de una estructuralnotaci6n punto En el mismo ejemplo 5.29 se ilustra la forma en que puede hacerse referencia para asignar valor 0 para trabajar con el valor que tiene asignado un campo de la estructura asi, por ejemplo, (&z)->REAL es una notacion equivalente a Z.REAL. EI Programa FECHADOR del ejemplo 5.30 ilustra tambien el manejo de los val ores de los elementos de una estructura. La explicacion de como funciona el programa FECHADOR, se incluye en forma de comentarios al mismo programa, que ilustra como puede determinarse la fecha y hora, por medio de la estructura tm de la bibliotecay de las funciones asctime, ctime, gmtime y localtime. En este programa tambien se incluye la definicion de algunas macros, e incluso Lespera tiene mas de una linea de codigo. Vease que correctamente utilizadas, las macros pueden hacer mas legible y mas corto el codigo fuente de un programa.
134
Fundamentos de Programacion en Lenguaje C
Ejemplo 5.30: Programa "fechador.c". /***************************************************************** Este programa constituye una prueba de algunas rutinas de temporizacion con las que cuenta el sistema operativo UNIX. Estas rutinas son lIamadas de sistema accesibles desde el lenguaje C y que pueden utilizarse para desarrollar sistemas de reloj 0 temporizadores. Las subrutinas de biblioteea que se prueban en este programa son: char *asctimeO, *ctimeO; struct tm *gmtimeO, *localtimeO;
La estructura tm tiene el siguiente formato: struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; };
segundos minutos horas dia mes a):j;o dia de la semana dia del a):j;o
A ctime se Ie pasa el tiempo transcurrido desde la fecha y hora base, en segundos, a traves del entero largo tiempo. Y devuelve una cadena de caracteres, con fa fecha y hora correspondiente. A asctime se Ie da la estructura que tiene almacenados todos los datos relativos a la fecha, y la rutina entrega una cadena de caracteres con la hora y fecha. A loealtime se Ie da er tiempo transcurrido en segundos, y deposita todos los datos relativos a la fecha, en la estructura tm apuntada por reloj. gmtime es identica a loea/time, excepto porque los datos que deja en reloj son los correspondientes a la hora de Greenwich. time obtiene el tiempo transcurrido total medido por el sistema, dado en segundos. *****************************************************************/
/* MACROS */ #define Lespera
printf("Para continuar oprime");\ while (getcharO!= '\n')
Tipos de Datos
#define Lsaltos(b)
135
{int a;for (a=O;a
# includelong tiempo,tiempol; struct tm *reloj; char *fecha; mainO { Lsaltos(7) ; printf("\t******************************************************\nll); printf("\t* *\n"); printf("\t* *\n"); printf("\t* *\n"); printf("\t* *\n"); printfC'\t* PRUEBA DE LAS RUTINAS DE *\n"); FECHA Y HORA *\n"); printf("\t* printf("\t* *\n"); *\n"); printf("\t* printf("\t* *\n"); printfC'\t******************************************************\nll); Lsaltos(7); Lespera; INICIO: tiempo = time(O); fecha = ctime(&tiempo); printf("fecha:\n\n%s\n\n",fecha); Lespera; reloj = localtime(&tiempo); printf("H9RA LOCAL\n"); printf("ANO:\t\t\t%d\nll,(*reloj).tm_year); printf("MES: \t\t\t%d\n" ,(*reloj). tm_mon+ 1); printf("DIA:\t\t\t%d\n",(*reloj).tm_mday); printf("HORAS: \t\t\t%d\n",(*reloj). tm_hour); printfC'MINUTOS:\t\t%d\nll,(*reloj).tm_min); printf("SEGUN DOS: \t\t%d\n" ,(*reloj). tm_sec); printf("DIA DE LA SEMANA:\t%d\n",(*reloj).tm_wday); printf("DIA DEL ANO:\t\t%d\nll,(*reloj).tm_yday); printf("tm_isdst: \t\t%d\n",(*reloj). tm_isdst); Lespera; printf("HORA CON asctime(reloj)\n"); printf("%s\n",ascti me( re Ioj) ); Lespera; reloj = gmtime(&tiempo); printf("HORA GMT\n"); printf("ANO:\t\t\t%d\n",(*reloj).tm_year); printf("MES: \t\t\t%d\n11 ,(*reloj). tm_mon+ 1);
136
Fundamentos de Programacion en Lenguaje C
printf("DIA:\t\t\t%d\n",(*reloj).tm_mday); printf("HORAS:\t\t\t%d\n",(*reloj).tm_hour); printf("MINUTOS:\t\t% d\n",(*reloj).tm_min); printf(ISEGUNDOS:\t\t%d\n",(*reloj).tm_sec); printf("DIA DE LA SEMANA:\t%d\n",(*reloj).tm_wday); printf("DIA DEL ANO:\t\t%d\n",(*reloj).tm_yday); pri ntf("tm_isdst: \t\t%d\n",(*reloj). tm_isdst); Lespera; printf(ltAHORA PUEDES SALIR DEL PROGRAMA CON AZ Y\n"); if (getcharO < 0) exit(O); goto INICIO;
}
5.7 UNIONES 5.7.1 Declaraci6n Las uniones son similares en la forma de declararse a las estructuras, y permiten tener estrueturas 0 arreglos de tipo variable. Por medio de una union podemos meter un campo en una estructura que puede variar de tipo, dependiente del eontexto. Un ejemplo de declaracion de union es el siguiente: union UN_TT {int ENTERO; float REAL; char CAR;} U[SO];
donde U es un arreglo de 50 elementos que pueden ser indistintamente, entero, real 0 eanieter.
5.7.2 Referencia a elementos de una uni6n1notaci6n punto La referencia a los elementos d~ una union se haee de la misma manera que la de una estructura. Es decir, mediante la notaei6n punto 0 mediante la notaeion fleeha (- >).
5.8 ARCHIVOS El manejo de arehivos se realiza con los elementos que nos proporeiona la biblioteca stdio.h. El ejemplo 5.31 ilustra el manejo de archivos de caraeteres. Debe destaearse el tipo FILE de esta biblioteea, que es un apuntador a un arehivo el eual tiene que ser empleado en el manejo de arehivos. El programa AGREGA del
Tipos de Datos
137
ejemplo 5.31, abre archivos de entrada y de salida, y va agregando el contenido del archivo de entrada al final del archivo de salida. La apertura de archivos se realiza mediante la funcion fopen, el nombre del archivo de salida se Ie puede pasar como panimetro en la linea de comando. Los archivos de entrada se abren solo para lectura, y el de salida para escribir a partir del final de dicho archivo. Ejemplo 5.31: Programa "agrega.c", agregar codigo al archivo de salida. # includevoid LAgrega(FILE *LCB, char (*LAR» {
char Lauxb; FILE *LENTRADA; if«LENTRADA=fopen(LAR,"r"»!=O) { while «Lauxb = getc(LENTRADA» != EOF) {putc(Lauxb,LCB);ff1ush(LCB);} fclose(LENTRADA); }
}
FILE *SALIDA; char ARCH[80], CAR[2]; maine argc,argv) int argc; char *argv[]; {
if(argc == 1) {printf("NOMBRE DEL ARCHIVO DE SALIDA?\n"); scanf("%s",ARCH); SALIDA = fopen(ARCH,la"):} else SALIDA = fopen(argv[l],"a"); INICIO: printf("AGREGAR ARCHIVO?\n"); scanf("%s", CAR); if (CAR[O] == In') goto FIN; printf("NOMBRE DEL ARCHIVO?\n"); scanf("%s",ARCH); LAgrega(SALIDA, ARCH); goto INICIO; FIN: fclose(SALIDA); return 0; }
138
Fundamentos de Programacion en Leng!1aje C
5.9 DEFINICI6N DE TIPOS COMPUESTOS La definicion de tipos compuestos de datos se realiza mediante el especificador typedef. Pueden crearse de esta manera, arreglos de estructuras 0 uniones, apuntadores a estructuras, arreglos de apuntadores a estructuras, arreglos de apuntadores a funcion, arreglos de estructuras con apuntadores a otras estructuras, funciones 0 arreglos, etc., los cuales pueden usarse como tipos de datos especificos. Como ilustracion de la definicion de nuevos tipos de datos, retomemos los ejemplos 3.8 y 3.9 en que definimos el tipo COMPLEJO compuesto por una pareja de elementos reales, y posteriormente declaramos una variable de este tipo. En el ejemplo 5.32, tenemos un programa que hace aritmetica de numeros complejos, aprovechando los elementos anteriores. Ejemplo 5.32: Programa "complex.c", aritmetica de numeros complejos. #include#include typedef struct {float REAL, IMAG;} COMPLEJO; typedef struct {float MAGN, ANG;} POLARCOMP; COMPLEJO VCA, VCB, VCC; POLARCOMP VPA, VPB, VPC; COMPLEJO *SUMAC(COMPLEJO *A, COMPLEJO *B, COMPLEJO *C) { C->REAL=A->REAL + B->REAL; C->IMAG=A->IMAG + B->IMAG; return C; } COMPLEJO *RESTAC(COMPLEJO *A, COMPLEJO *B, COMPLEJO *C) {C->REAL=A->REAL-B->REAL; C->IMAG=A->IMAG-B->IMAG; return C; } POLARCOMP *POLARC(COMPLEJO *A, POLARCOMP *B) { B->MAGN = sqrt(pow(A->REAL,2)+pow(A->IMAG,2»; B->ANG = atan(A->IMAG/A->REAL); return B; } COMPLEJO *CARTEC(POLARCOMP *A, COMPLEJO *8) { B->REAL = A->MAGN * cos(A->ANG); B->IMAG = A->MAGN * return B; }
sin(A->A~G);
COMPLEJO *MULTIPC(COMPLEJO *A, COMPLEJO *B, COMPLEJO *C) { POLARCOMP POA, POB, POC; POLARC(A, &POA); POLARC(B, &POB); POC.MAGN = POA.MAGN * POB.MAGN; POC.ANG = POA.ANG + POB.ANG; CARTEC(&POC, C); return C;}
Tipos de Datos
139
COMPLEJO *DIVISIC(COMPLEJO *A, COMPLEJO *B, COMPLEJO *C) { POLARCOMP POA, POB, POC; POLARC{A, &POA); POLARC{B, &POB); POC.MAGN = POA.MAGN / POB.MAGN; POC.ANG = POA.ANG - POB.ANG; CARTEC(&POC, C); return C; } mainO
{ VCA.REAL=l; VCA.IMAG=l; VCB.REAL=l; VCB.IMAG=-l; SUMAC(&VCA, &VCB, &VCC); printf("SUMA DE COMPLEJOS\n"); printf("VCC.REAL = O/of\n", VCC.REAL); printf("VCC.IMAG = %f\n", VCC.IMAG); RESTAC(&VCA, &VCB, &VCC); printf("RESTA DE COMPLEJOS\n"); printf("VCC.REAL = %f\n", VCC.REAL); printf("VCC.IMAG = %f\n ll , VCC.IMAG); MULTIPC(&VCA, &VCB, &VCC); printf("MULTIPLICACION DE COMPLEJOS\n"); printf("VCC.REAL = %f\nll, VCC.REAL); printf("VCC.IMAG = %f\nll, VCC.IMAG); DIVISIC(&VCA, &VCB, &VCC); printf("DIVISION DE COMPLEJOS\n"); printf("VCC.REAL = %f\n", VCC.REAL); printf("VCC.IMAG = %f\n", VCC.IMAG); return 0; }
A partir de la definicion del tipo de numeros complejos, se pueden declarar otras estructuras, como sena la de una matriz de impedancias complejas que representa un sistema electrico 0 un sistema amilogo. El ejemplo 5.32 muestra la declaracion de una matriz de numeros complejos de 3 x 3 que representa la matriz de impedancias complejas de un circuito de tres mallas: COMPLEJO CIRCUITO[3][3];
140
Fundamentos de Programacion en Lenguaje C
Ejercicios 5.1 5.2
5.3 5.4 5.5 5.6 5.7 5.8 5.9
5.10 5.11 5.12
5.13
5.14
Racer un programa en C, para resolver ecuaciones de segundo grado. Racer un programa en C, para calcular una tabla de cargospor interes anual y abono a capital de un prestamo; calculando los intereses sobre saldos insolutos y teniendo como datos de entrada, el capital, el porcentaje de interes anual y el numero de afios. Racer un programa en C, para calcular el maximo comun divisor de el numero de enteros que se Ie den como entrada. Racer un programa para calcular el numero de combinaciones de n de m elementos. Racer un programa para resolver un sistema de m ecuaciones con m incognitas, aplicando el metodo de Gauss-Jordan. Racer un programa para ordenar alfabeticamente en forma ascendente y descendente una lista de nombres, utilizando el metodo de la burbuja. Racer un programa que separe en silabas un texto. Racer un programa para calcular las funciones de Bessel de primera especie y orden 0,1,2 Y 3. Racer un programa que ilustre la forma en que operan las funciones de manejo de caracteres de la tabla 5.10. Idea: admitir un caracter mediante la funcion getchO, aplicarle las funciones de la tabla 5.10 y mostrar los resultados. Racer una funci6n que convierta una cadena de caracteres escrito en letras mayusculas a uno escrito con minusculas, y viceversa. Racer un programa que ilustre la forma en que operan las funciones de manejo de caracteres de la tabla 5.11. Racer un programa que calcule el circuito equivalente de Thevenin de un circuito electrico compuesto de 4 mallas. Las impedancias propias y mutuas de las mall as pueden introducirse de manera interactiva al programa. Racer un programa que calcule sumas, restas, producto punto y producto cruz de vectores tridimensionales, cuyas componentes tienen valores realess Disefiar una estructura de datos para un directorio personal y hacer un programa que muestre el contenido del directorio para una letra de encabezado que se Ie indique de manera interactiva.
Capitulo 6
FUNCIONES
6.1 INTRODUCCION En ellenguaje C, las funciones: 1. son bloques de programaci6n que realizan tare as especificas 2. facilitan la labor de programacion 3. penniten construir nuevos sistemas a partir de trabajos desarrollados por otras personas 4. penni ten que los sistemas grandes sean desarrollados por un equipo de varias personas 5. facilitan la labor de planeaci6n, desarrollo y documentacion de los programas Las funciones son herederas de las llamadas a subrutina de Fortran, sin embargo, su usa intensivo como elementos de programacion estructurada se introdujo con los procedimientos y funciones de Algol y Pascal. Son dos las modalidades en que pueden usarse las funciones de C definidas con la sintaxis indicada en la sec cion 3.3: como expresiones primarias y por 10 tanto como parte de expresiones mas complejas, tal como se muestra en el ejemplo 6.1; 0 bien como instrucciones expresion, llamadas 0 utilizadas como en el ejemplo 6.2. Las funciones usadas como expresiones primarias son equivalentes a las function de Pascal, y usadas como instruccion expresi6n son equivalentes a los procedure del mismo lenguaje. En otras palabras, las funciones de C pueden usarse ya sea como expresiones primarias, 0 bien como procedimientos 0 llamadas a procedimientos. En el ejemplo 6.1 pow y sin se utilizan como expresiones primarias. Por su parte, en el ejemplo 6.2, printf y scanf se usan como instrucciones expresi6n.
142
Fundamentos de Programaci6n en Lenguaje C
Ejemplo 6.1: Uso de funciones como expresion primaria. float A,X, Y; A = 2*pow(X,Y) + 3*pow(X,2) + 7*sin(2*3.1416);
Ejemplo 6.2: Uso de funciones como instrucciones expresion. float A,X, Y; printf("DAME LOS VALORES DE X Y Y\n");
scanf("%f %figura", &X, &Y);
En este capitulo proponemos algunas metodologias, como la especificacion en pseudocodigo y la especificacion de la arquitectura del programa, para ilustrar la manera en que puede descomponerse un programa en estructuras y funciones, como bloques perfectamente bien delimitados con los cuales puede construirse de manera logica y simple un prograina complejo. Un programa debe ser una secuencia de funciones, como ya se establecio en el Capitulo 2, una de ellas debe ser la funcion mainO. Las funciones con la terminacion punto y coma (;) se pueden usar como instrucciones expresion 0 instrucciones de Hamada a funcion, en el cuerpo de la funcion mainO 0 dentro de cualquier otra funcion, dandole los valores adecuados a los parametros 0 argumentos. Incluso se pueden Hamar a sf mismas, en 10 que se conoce como una Hamada recursiva, como ya se menciono en el Capitulo 2.
6.2
SINTAXIS DE UNA FUNCION EN
C
Como ya se habia explicado, la sintaxis de una funcion en C es la siguiente: tipo_de_dato nombrefuncion ( declarac_argumentos ) {
declaraciones instrucciones }
Las llamadas a la funcion dentro de otra funcion nen el siguiente formato: nombrefuncion( argumentos);
0
de la funcion mainO tie-
Funciones
143
En la secci6n 6.3 se ilustra, por medio de una especificaci6n en pseudoc6digo, el uso de las funciones como instrucciones de Hamada a funci6n. Es evidente que problemas complejos, como puede ser el desarrollo de una caIculadora, pueden resolverse mejor dividiendo la tarea en bloques funcionales de mas facil y clara realizaci6n e integnindolos en una estructura que permita su empleo en la resoluci6n del problema global. Tambien como ya se coment6, las funciones pueden usarse como expresiones primarias, dentro de una operaci6n aritmetica, relacional, 16gica 0 de un argumento de otra funci6n que al caIcularse debe dar un valor constante. Vease el ejemplo 6.1.
6.3
ESPECIFICACION EN PSEUDOCODIGO
La especificaci6n en pseudoc6digo es una herramienta util para la planeaci6n y documentaci6n de un programa escrito en C. Para realizar la especificaci6n debemos separar nuestro programa en tareas pequefias, las cuales pueden desarrollarse por medio de funciones. En forma de comentarios especificamos que debe hacer cada una de las funciones en las que fue separado el programa principal. En el ejemplo 6.3, se muestra la especificaci6n en pseudoc6digo de una caIculadora estadfstitica, la cual se ha estructurado de acuerdo a las siguientes funciones: 1.
Un menu principal, desde el cual se llama la rutina para meter los datos y para caIcular los distintos parametros estadfsticos inherentes a los datos que se tienen capturados. Este menu principal se presenta permanentemente, hasta que se selecciona la opci6n de salir del programa.
2.
Una funci6n por cada uno de los caIculos estadfsticos que va a realiza,r la computadora. Cada una de las funciones realiza el caIculo y presenta el resultado al usuario, luego regresa al menu principal
Ejemplo 6.3: Especificaci6n en pseudoc6digo de una caIculadora estadfstica basica. mainO { while (!* mientras no se seleccione la salida del programa *f) { switch (!*ejecuta */ MENUESTADISllCOO /* Menu Principal */)
{ case 1: {PROMEDIO(DATOS); break;}
144
Fundamentos de Programacion en Lenguaje C case case case case case case case case case
2: 3: 4: 5:
{VARIANZA(DATOS); break;} {MODA(DATOS); break;} {MEDIANA(DATOS); break;} {DESVSTANDAR(DATOS); break;} 6: {DISTACUMU(DATOS); break;} 7: {MAXIMO(DATOS); break;} 8: {MINIMO(DATOS); break;} 9: {METERDATOS(DATOS) break;} 10: exit(O); /* salir del programa */
} } int MENUESTADISTICO()
{ /* Esta funcion debe presentar el menu con los tipos de calculo que se realizan en el programa. Debe solicitar la seleccion al usuario. Finalmente debe entregar como resultado un numero que indica que funci6n se seleccion6
*/ } float PROMEDIO(l* Tiene como argumento el arreglo de datos base del calculo *J)
{ /* calcula el promedio y presenta el resultado * /
} float VARIANZA{/* Tiene como argumento el arreglo de datos base del calculo * f) { /* calcula la varianza y presenta el resultado* / } float MODA(!* Tienecomo argumento eJ arreglo de datos base del calculo *f)
{ /* caJcula la moda y presenta el resultado*/
} float MEDIANA(!*Tiene como argumento el arreglo de datos base del calculo *J)
{ /* caJcula la mediana y presenta el resultado* /
} float DESVSTANDAR(j*Tiene como argo el arreglo de datos base del calculo *J)
{ /* calcula la desviaci6n estandar y presenta el resultado* /
} float DISTACUMU(!* Tiene como argumento el arreglo de datos base del calculo *f)
{
Funciones
145
/* calcula la distribuci6n acumulativa de 10 en 10 por ciento y presenta el resultado* /
} float MAXIMO(f* arreglo de datos base del calculo * f) { /* calcula el maximo y presenta el resultado* / } float MINIMO(f*Tiene como argumento el arreglo de datos base del calculo * /) { /* calcula el mlnimo y presenta el resultado* / } float METEDATOS(f* arreglo de datos base del calculo *J) { /* pide los datos y los mete en un arreglo para poder calcular despues los distintos valores estadfsticos * / }
Como puede apreciarse del ejemplo 6.3, dividir el programa en funciones que realizan tareas especificas, permite visualizar mas facilmente 10 que debe hacerse para desarrollarlo. A continuacion recordaremos la definicion de los conceptos estadisticos que maneja este programa, con la finalidad de que se visualice como deben programarse las distintas funciones. Promedio: Es la suma de todos los datos a promediar, entre el numero total de datos. Si N es el numero total de datos y DAT[i] es el arreglo donde se encuentran los datos, entonces el promedio es:
p = =L,--DA_ll-=-=.i] N Varianza: Es el segundo momento estadistico central. Es el valor promedio del cuadrado de las desviaciones, respecto a la media 0 valor promedio de los datos. En otras palabras, es la suma, de las diferencias de los datos con su valor promedio elevadas al cuadrado, entre el numero total de datos. Matematicamente:
v = 2:(DAr{i]- p)2 N
Desviacion estdndar: es la raiz cuadrada de la varianza. Matematicamente:
146
Fundamentos de Programaci6n en Lenguaje C
DS =.Jv
Moda: Es el dato que mas veces se presenta en un conjunto de datos. En algunos casos puede ser conveniente agrupar los datos por subconjuntos y determinar la MODA, que es el que contenga un mayor numero de datos del conjunto total. Mediana: Es el valor maximo encontrado en el conjunto de datos, mas el valor minimo entre dos; es decir: MED = MAX + MIN 2
Maximo: Es el valor maximo de los datos que se encuentran en el conjunto de datos. Minima: Es el valor minimo de los datos que se encuentran en el conjunto de datos. Distribucion acumulativa: Es una representaci6n ordenada de un conjunto de datos que ilustra la forma en que se concentran sus ocurrencias por rango de valor. Es costumbre representar los resultados de algunas pruebas 0 experimentos, en terminos de su distribuci6n acumulativa en percentiles, 0 sea, los cruces de la curva de distribuci6n con determinados valores de porcentaje, particularmente son usuales los percentiles delIO, 20, 30, 40, 50, 60, 70, 80, 90 y 100 por ciento. Como ejempl0, a continuaci6n se presenta una tabla de 100 datos, su distribuci6n acumulativa en 10 rangos de valores y sus percentiles, tanto en forma de tabla como en la figura 6.1.
Ejemplo 6.4: Calculo de la distribuci6n acumulativa. Datos
347 59 90 658 620 15 26 521 718 349
408 26 863 32 790 28 77 310 231 516
541 236 751 279 900 133 65 355 894 433
78 890 860 29 730 243 220 605 532 190
901 754 201 407 660 255 19 540 101 80
32 368 503 17 72 292 96 147 711 911
123 12 400 914 180 171 299 880 293 648
89 470 206 11 22 595 940 369 604 793
25 45 42 8 456 51 10 121 225 169
1 351 120 173 210 5 68 196 999 837
Funciones
Grupo
Ocurrencias
Oc. Rei. (%)
Dist. Acum.
1-99 100-199 200-299 300-399 400-499 500-599 600-699 700-799 800-899 900-999
30 12 13 7 6 7 6 7 6 6
30 12 13 7 6 7 6 7 6 6
30 42 55 62 68 75 81 88 94 100
Percentil·%
Dist. Acum.
10 20 30 40 50 60 70 80 90 100
1-99 100-199 200~299
300-399 500-599 600-699 800-899 900-999
DISTRIBUCION ACUMULATIVA 100 90
iii 1-99
80
11D1100-199
70
.200-299
i= z 60
.300-399
w 50 a:: w 40 a. 0
.500-599 .600-699
30
.800-899
20 10
l1li900-999
0 DATOS
Figura 6.1: Distribuci6n acumulativa en terminos de percentiles.
147
148
Fundamentos de Programaci6n en Lenguaje C
6.4 VALOR RETORNADO De manera generica, una funcion realiza un calculo y entrega un resultado, e1 cual se dice que es el valor retornado. El tipo de valor retornado puede ser cualquiera de los que maneja ellenguaje C. Por ejemplo: void char short int long
float double unsigned
Tambien puede ser un apuntador a una estructura 0 union 0 a cualquiera de los tipos de la lista anterior Hay que recalcar que el valor retornado es el resultado que entrega la funcion. Por ejemplo, la funcion DESVSfANDAR de la calculadora estadistica debe calcular la desviacion estandar de los datos que se encuentran en el arregio cuya direccion se Ie pasa como parametro y dar como valor retomado el valor de la desviacion estandar de dichos datos. La funcion MINIMO opera de manera similar, pero retoma el minimo valor de los datos almacenados en el arreglo de datos, y asi sucesivamente. Como un ejemplo adicional, consideremos una funcion matematica: f(x)
= 1 + 2X2 - 3~
Esta funcion da como resultado un numero real, si la variable ipdependiente
x es real. De manera que puede programarse como una funcion que retoma un numero de tipo float, con un parametro de entrada 0 parametro formal tambien de tipo float, como se muestra en el ejemplo 6.5, a continuacion: Ejemplo 6.5: Ilustracion del concepto de valor retomado. float FX(float X) { return (1 + 2*pow(X,2) - 3*pow(X,3» }
Luego se puede llamar a esta funcion des de e1 programa principal 0 funcion 0 desde cualquier otra funcion, en fonna de expresion primaria, por ejempIo: main
Funciones
149
float Y; Y = FX(S.);
Evidentemente, Y es una variable de tipo float con aplicacion en el ambito en donde se esta llamando a la funcion FX;
6.5 DECLARACIONES DE LOS ARGUMENTOS Una funcion puede tener cualquier numero de argumentos 0 parametros, los cuales se dec1aran dentro de los parentesis con la misma sintaxis de las dec1araciones de variables 0 estructuras de datos. Sin embargo, especialmente en el caso de Turbo C ++, cada parametro debe tener indicado su tipo de dato y las dec1araciones van separadas por comas. El ejemplo 6.6 ilustra la dec1aracion y uso de parametros de una funcion. En dicho ejemplo, la funcion getop tiene dos parametros: un apuntador a un arreglo de caracteres y un entero. En la cadena de caracteres que se Ie indica al ser Hamada, getopO coloca los caracteres tec1eados en la entrada estandar, hasta que encuentra un fin de archivo 0 caracter negativo, que es el final de archivo. Tambien se Ie pasa como parametro a getopO el numero maximo de caracteres que va a leer de la entrada estmdar.
Ejemplo 6.6: Dec1aracion de argumentos de una funcion. int getop( char CAD[ ], int NC) {
int ind; while (((CAD[ind++] = getchO) > 0) && (ind < NC)); }
Dentro del segmento de codigo de la funcion, los parametros son valores constantes que se encuentran disponibles para realizar un calculo, determinar una direccion, etc. A tiempo de ejecucion, cada vez que es Hamada la funcion, el sistema operativo otorga espacio para almacenar los valores de los parametros, los cuales quedan disponibles de esta fonna, invocandolos desde dentro de la funcion con el nombre con que fueron dec1arados entre los parentesis que siguen al nombre de la funcion, en este caso getop. Al ser Hamada la funcion desde mainO u otra funcion, se Ie tienen que asignar a los parametros, los valores con los que va a trabajar en esta instancia de Hamada. Al llamar la funcion en otra ocasion se Ie pueden dar, obviamente, otros valores a los parametros.
150
Fundamentos de Programacion en Lenguaje C
6.5.1 Paso de panimetros por valor y por referencia Ellenguaje C s6lo pennite el paso de parametros por valor, es decir, como valores constantes. No pennite el paso de parametros por referencia, pero S1 pennite pasar apuntadores como parametros de una funei6n. Los parametros 0 argumentos de una funci6n son los val ores con los que se evaluara la funci6n, en cada instancia en que dieha funci6n es invocada. Por ejemplo, consideremos el siguiente segmento de programa: float \lTl; \lTl
=
\lTl
= sin(30*2*3.1416/360);
VTl
= sin(-45*2*3.1416/360);
sin(45*2*3.1416/360);
En este segmento se llama tres veces a la funci6n seno (sin) de la biblioteca math.h de C. Primero se ea1cula el seno de 45°, luego el seno de 30° y finalmente el seno de -45°. Evidentemente, se Ie han pas ado en las tres llamadas valores detenninados del parametro de la funci6n seno que debe ser un numero de punto flotante que represente el angulo en radianes. Suponiendo que tenemos dec1arada una variable ANGG que expresa el angulo en grados, y que en algun momento se Ie asign6 un valor, tambien podemos hacer una invocaci6n como la siguiente:
\lTl
= sin(ANGG*2*3.1416/360);
dado que la expresi6n ANGG*2*3.1416/360 tiene un valor eonstante. Los apuntadores se pasan como parametros para que dentro de la funci6n se detennine la direeci6n de una estruetura de datos, un arreglo 0 una funei6n, con la eual se haran referencias a diehas estructuras, 0 se comandara la ejecuci6n de la correspondiente funci6n.
6.5.2 Paso de panimetros de tipo apuntador A manera de ilustraci6n del paso de parametros de tipo apuntador, consideremos en el ejemplo 6.7, el uso de un apuntador a un arregio como parametro de una funci6n. Consideremos la funci6n MENU, a la cual se Ie pasan, como parametros,
Funciones
151
un apuntador a una cadena de caracteres, denominado TITULO, la direccion inicial de un arreglo de apuntadores a cadena de caracteres, Ilamado OPCIONES, y un numero, llamado NOPCI, que indica cmintas opciones tiene el menu. Cada cadena representa una opcion del menu e indica la accion a realizar y el comando que se debe introducir para seleccionar dicha opcion. La funcion menu retorna un entero que representa la accion seleccionada. En la funcion main se declaran las distintas cadenas que van a formar el menu y se llama a la funcion MENU, desde un cicIo iterativo que termina cuando la opcion seleccionada retomada por MENU es la que corresponde a la salida del programa. A MENU, se Ie pasan como parametros, el titulo, Ilamado TIT1, Ia lista de opciones, Hamada NOMOPCIONES, y el numero de opciones, denominado NUOPCIONS. La estructura de esta funcion permite apreciar que es posible desarrollar programas 0 funciones que constituyen un esqueleto 0 estructura base que puede ser reutilizada una y otra vez, unicamente cambiandole los argumentos 0 parametros. Los menus, por ejemplo, se han ido convirtiendo en elementos de principal importancia en el desarrollo de software. EI programa "menu.c", hace uso de las instrucciones de entrada/salida estandar printf y scanf, pero es posible utilizar otros recursos, como los modos graflCOS y las ventanas, para hacer menus y cajas de dialogo mas atractivos. Ejemplo 6.7: Programa "menu.c", ilustraci6n del paso de parametros por apuntador. int MENU( char TITULO[ ], char *OPCIONES[ ], int NOPCI) { int IND, OPS; printf("%s\n ",TITULO); for (IND = 0; IND < NOPCI; IND++) printf("%s", OPCIONES[IND]); printf("Indica tu eleccion con el numero que aparece frente a la OPCION y\n"); scanf(" % d",&OPS); return (OPS); } mainO { int NUOPCIONS = 10, OPSELEC=O; char TITl[ ] = "\tCALCULADORA ESTADISTICA\n"; char *NOMOPCIONES[ ] =
{ "PROMEDIO\t\t\t( 1)\n", "VARIANZA\t\t\t(2)\n",
152
Fundamentos de Programacion en Lenguaje C IIMODA\t\t\t\t(3)\nll, IIMEDIANA\t\t\t\t(4)\n", "DESVIACION ESTANDAR\t\t(S)\n", IIDISTRIBUCION ACUMULATIVA\t(6)\n ll , ll MAXI MO\t\t\t\t(7)\n , IIMINIMO\t\t\t\t(8)\nl1, "METERDATOS\t\t\t(9)\nII , IISALIR\t\t\t\t(10)\nll 11
}; c1rscrO; while «OPSELEC = MENU(TIT1, NOMOPCIONES, NUOPCIONS)) != 10) {cirscrO; printf("La Opcion Seleccionada es la D/od\nll, OPSELEC);}
}
6.6 ALCANCE DE UN NOMBRE DE IDENTIFICADOR DENTRO DE UNA FUNCION
EI aIcance de un nombre es la parte del programa dentro de la cual conserva su significado dicho nombre. EI aIcance de una variable declarada al principio de una funci6n, es la funci6n en que se encuentra declarada. Esta es una variable local. Las variables locales con el mismo nombre, pero declaradas en diferentes funciones no tienen relacian. EI aIcance de una variable extema, como ya se comenta en el Capitulo 2, abarca desde el punto en donde se dec lara hasta el final del archivo en que es declarada. Consideremos el ejemplo 6.8, sp y val son visibles en pop y push pero no en main, pop y push tampoco son visibles en main. Ejemplo 6.8: A1cance de una variable extema. mainO{···} int sp = 0; double val[MAXVAL]; void push(double f){ ... } double pop(void) { ... }
Las lineas del tipo extern int sp;
declaran la variable para el resto del archivo pero no reserva espacio en memoria, suponiendo que ya estan declaradas en otro archivo.
Funciones
153
6.7 LA FUNCION PRINCIPAL mainO Todo programa debe tener una funcion principal, que es la funcion main(int argc, char *argv[ ])
Al arrancar la ejecucion de un programa se inicia con esta funcion y desde ella se Haman las otras funciones que conforman el programa.
6.7.1 Argumentos de la funci6n mainO Los argumentos de la funcion main son: argc es de tipo entero e indica el numero de argumentos que se Ie estan pasando al programa desde la linea de comando argv es de tipo arreglo de apuntadores a cadenas de caracteres. Representa la lista de parametros que se Ie pasaron al programa desde la linea de comando; argv[O] por convencion apunta al nombre del programa A continuacion se muestra otra version del programa que reimprime como eco los argumentos que se Ie pasan desde la linea de comando. Ejemplo 6.9: Programa "argums.c". /* Imprimir en forma de eco los argumentos de un· Programa * / #includemain(int argc, char *argv[]) {int N = 0; while (argc-->O) printf("%s%s",argv[N++ ],(argc>O)?" ":""); printf("\n"); return(O); }
6.8 RECURSIVIDAD Ya se comentaron en el Capitulo 2 algunos aspectos del concepto de recursividad. Debemos agregar que para la solucion de algunos problemas, como la construccion y busqueda en arboles binarios, y en general para problemas que pueden calcularse 0 determinarse con base en el mismo problema pero con una dimension menor, los algoritmos recursivos facilitan los calculos.
154
Fundamentos de Programaci6n en Lengu,aje C
Para ejemplificar la forma en que funcionan los algoritmos recursivos, en el ejemplo 6.10 se presenta un programa que resuelve el problema de las torres de Hanoi. EI algoritmo de solucion recurs iva de este problema esta basado en que para pasar M discos de un poste a otro, usando como auxiliar uno intermedio, primero hay que pasar M-1 discos al poste auxiliar, para as{ poder pasar el disco de mas abajo al poste de destino, y luego hay que pasar los M-l discos que se encuentran en el poste auxiliar. Para pasar estos M-l discos, del poste auxiliar al poste de destino final, primero hay que pasar M-2 discos al poste usado inicialmente de origen y ahora usado como auxiliar, para pasar el segundo disco mas grande al poste de destino, y asi sucesivamente. La solucion recursiva del problema de las torres de Hanoi es un ejemplo muy ilustrativo de 10 que es una funcion recursiva y su utilidad computacional; sin embargo, hay problemas de mucho mayor importancia practica, como es el algoritmo de la transformada nipida de Fourier, que ha sido considerado como una de las aportaciones mas importantes de la ingenieria computacional, para el avance de la instrumentacion y de la ciencia en general. Hay funciones matematicas que son recursivas en esencia, como son las funciones calculadas por medio de series; otras mas bien son iterativas y hay que tener cui dado de no abusar de esta herramienta, porque es un alto consumidor de recursos computacionales. Ejemplo 6.10: Programa "hanoi.c". /* Programa recursivo que ejecuta el juego de las torres de Hanoi */ #include#include #include void torresh(m, detorre, atorre, auxtorre) unsigned m; char detorre, atorre, auxtorre; { /*Si solo se va a mover un disco, ejecutar el movimiento y regresar */ if (m==l) {printf("\n%s%d%s%c%s%c","mover disco ",m," de la torre ",detorre," a la torre ",atorre); return; } /* Mover los n-l discos de arriba de A a B usando C como auxiliar */ torresh(m-l, detorre, auxtorre, atorre); /* Mover el disco restante de A a C * / printfC"\n%s%d%s%c%s%c","mover disco ",m," de la torre ",detorre," a la
Funciones
155
torre ",atorre); getcharO; /* Mover los n-l discos de arriba de B a C usando A como auxiliar */ torresh(m-l, auxtorre, atorre, detorre);
}
mainO { unsigned n; clrscrO; pri ntf("\n \n \n \n \n \n "); printf("\t\t DAME El NUMERO DE DISCOS: "); scanf("%d" ,&n); torresh(n,'A','C,'B'); printf("\n\t TERMINAMOS\n"); }
6.9 EJECUCION DE FUNCIONES POR DIRECCIONAMIENTO INDlRECTO
En la seccion 5.5.8, se vio de manera preliminar, el uso de apuntadores para Hamar a la ejecucion de funciones. Este recurso puede dar posibilidades extraordinarias, en virtud de que permite una gran flexibilidad en el manejo de funciones; desde un mismo punto de decision, dependiendo de las circunstancias, se puede comandar la ejecucion de funciones muy diferentes. En esta seccion, se presenta un programa que materializa un analizador lexicogratico, por medio de una maquina de estados finitos. La maquina de estados finitos a su vez esta realizada por medio de una tabla de transiciones que tiene como uno de sus campos, un apuntador a funcion que permite determinar si h
156
Fundamentos de Programacion en Lenguaje C
secuencia de bits: 01111110. La maquina da una senal cuando detecta esta bandera.
~o-
BANDERA
o
Figura 6.2: Ejemplo de una ffiaquina de estados finitos.
Ejemplo 6.11: Programa "analx.c". /* Reconocedor LexicogrMico * / #include#include #include #include /* DEFINICION DE LA TABLA DE PALABRAS RESERVADAS DEL LENGUAJE DE ESPECIFICACION *1 #define NO_PAL_RES 54 typedef struct { char *PALAB_RS; unsigned CODIGO; } CO_PA_RES;
/* Palabra Reservada */ /* Codigo de Palabra Reservada
*1
/* TABLA DE PALABRAS RESERVADAS */
=
CO_PA_RES TAB_PALAB_RS(] { Ox0102, "ACCIONES", OxOl07, "ARRANCA_TEMP", Ox0105, "ACTUAL_BUF", Ox0108, "BIB", OxOlOa, "BUFFERS", OxOlOc, "COMANDO", OxOlOe, "DESCARTA_UJI",
"ACCION_G", Ox0103, "AmVO_SI", OxOl04, "ARRANCA_PRO" ,OxO 106 "BIT', OxOl09, "CAMPOli, OxOlOb, "DECREMENTA", Ox010d, "DEVUELVE", OxOlOf,
Funciones "EDO_INICIAL", "ENVIA_UII", "ESfADOS" , "FIJA", "FIN_BIB", "FORMATO", "INICIA", "MEF", "NADA", "NO_VACIO", "OBTEN", "PARA_PROCESO", "PREDICADOS", "REAL", "51_CAMPO", "SI_DISPONIBLE" , "SI_M ENSAJ E", "SI_NO_DISP", "SINO_VENT' , "VACIO",
Ox0110, Ox0112, Ox0114, Ox0116, Ox0118, OxOlla, OxOl1c, OxOl1e, Ox0120, Ox0122, Ox0124, Ox0126, Ox0128, Ox012a, Ox012c, Ox012e, Ox0130, Ox0132, Ox0134, Ox0136,
"ENTREGA_UIS", "ENVIA_S", "ERROR", "FINTRANS", II FIN_MEF", "INCREMENTA", "LIBERA", "MENSAJES", "NATURAL", "OCTETO", "PARA_TEMP", "PASA", "PRINCIPAL", "SI_BIT", "SI_CONDICION", "51_ERROR" , "SI_MEN_BC" , "SI_VENTANA", "TRANSICIONES", "VAR_ESTADO",
157
Ox0111, Ox0113, Ox0115, Ox0117, Ox0119, OxOl1b, Ox011d, OxOl1f, Ox0121, Ox0123, Ox0125, Ox0127, Ox0129, Ox012b, Ox012d, Ox012f, Ox0131, Ox0133, Ox0135, Ox0137
}; /*ESTRUCTURAS PARA ESPECIFICAR LA MAQUINA DE ESTADOS FINITOS DEL ANALIZADOR LEXICOGRAFICO*/ struct TRANS_LEX
/* Estructura de las transiciones * /
{ int (*CARACTERES)O; unsigned EST_FINAL; struct TRANS_LEX *«*SIG_ESTADO)[]);
/* predicado de activacion /* es estado final? */ /* Siguiente estado * /
*/
}; typedef struct TRANS_LEX TRANSI_LEX; typedef TRANSI_LEX TR_LEX[]; /* tabla de transiciones */ typedef TR_LEX *ATR_LEX; /* apuntador a tabla de transiciones * / ATR_LEX ESTADOLEX, A_IDEN, A_NUME, A_INIC, A_TEXl, A_TEX2, A_TEX3, A_TEX4, A_TEX5, A_COM1, A_COM2, A_COM3; int ALFABETOO, GATOO, COLONO, SEMICOLO, ESPACIOSO, ALFANUMO, OTROO, RESERVS(char *), PAREN_AO, PAREN_CO, COMAO, DIGITOO, BIHEXAO, DIAGONALO, ASTERISO, ERRLEXI(int), VALUE(char *); int SAL_LEX
= 0;
char SIG_CAR; char yytext[80]; int yyleng;
/* variable que indica el estado final en el que se quedo el automata de estados finitos */ /* siguiente caracter tornado del archivo de entrada */ /* cadena reconocida */ /* longitud de la cadena reconocida * /
158
Fundamentos de Programaci6n en Lenguaje C
int yylineno; char SIG_LlNEA[80]; int yylval = 0;
/* numero de linea actual * / /* siguiente linea leida */ /* valor de la cadena reconocida */
/* ESPECIFICACION DE LA MAQUINA DE ESTADOS FINITOS QUE DEFINE EL ANALIZADOR LEXICOGRAFICO */ TR_LEX INICIAL ={ {ESPACIOS, 0, &A_INIC}, {COLON, I:', &A_INIC}, {SEMICOL, I;', &A_INIC}, {PAREN_A, ,(" &A_INIC}, {PAREN_C, I)"~ &A_INIC}, {COMA, I,', &A_INIC}, {ALFABETO, 0, &A_IDEN}, {DIGITO, 0, &A_NUME}, {GATO, 0, &A_TEX1}, {DIAGONAL, 0, &A_COM1}, {OTRO, OxffOl, &A_INIC} };
TR_LEX IDENTIFICADOR ={ {ALFANUM, 0, &A_IDEN}, {OTRO, Ox010l, &A_INIC} };
TR_LEX NUMERO = { {DIGITO, 0, &A_NUME}, {BIHEXA, 0, &A_NUME}, {OTRO, Ox0138, &A_INIC} };
TR_LEX TXT_Cl = { {GATO, 0, &A_TEX2}, {OTRO, Oxff02, &A_INIC} };
TR_LEX TXT_C2 = { {PAREN~A, 0, &A_TEX3}, {OTRO, Oxff02, &A_INIC} }; TR_LEX TXT_ C3 = { {GATO, 0, &A_TEX4}, {OTRO, 0,&A_TEX3}
};
TR_LEX TXT_C4 = { {GATO, 0, &A_TEXS}, {OTRO, 0,&A_TEX3} }; TR_LEX TXT_CS = { {PAREN_C, Ox0139, &A_INIC}, {OTRO, 0, &A_TEX3}
Funciones
}; TR_LEX
159
={
COM_1 {ASTERIS, 0, &A_COM2}, {OTRO, Oxff03, &A_INIC}
}; TR_LEX
COM_2 = { {ASTERIS, {OTRO,
0, &A_COM3}, O,&A_COM2}
};
TR_LEX
COM_3 = { {ASTERIS, 0, &A_COM3}, {DIAGONAL,OxOf03, &A_COM3}, {OTRO, 0, &A_COM2}
};
/*
RUTINA DE INICIALIZACION DE LOS APUNTADORES A LA TABLA DE ESTADOS DE LA MAQUINA DE ESTADOS FINITOS
*/
int INICIALEXO { A_IDEN = (ATR_LEX) IDENTIFICADOR; A_INIC = (ATR_LEX) INICIAL; A_NUME = (ATR_LEX) NUMERO; A_TEX1 = (ATR_LEX) TXT_C1; A_TEX2 = (ATR_LEX) TXT_C2; A_TEX3 = (ATR_LEX) TXT_C3; A_TEX4 = (ATR_LEX) TXT_C4; A_TEXS = (ATR_LEX) TXT_CS; A_COM! = (ATR_LEX) COM_1; A_COM2 = (ATR_LEX) COM_2; A_COM3 = (ATR_LEX) COM_3; return 0; }
/*
RUTINAS PARA IMPLEMENTAR LOS PREDlCADOS DE ACTIVACION DE LA MAQUINA DE ESTADOS FINITOS En general, todas estas rutinas prueban si el siguiente caracter bajo analisis, esta dentro de un rango, es igual a un determinado caracter. */
°
ALFABETOO
{ if «(SIG_CAR <= 'Z') && (SIG_CAR >= 'A'))II «SIG_CAR <= 'z') && (SIG_CAR >= 'a'))) return 1; else return 0; } ALFANUMO {
160
Fundamentos de Programaci6n en Lenguaje C
if «SIG_CAR=='_') II
«SIG_CAR <= 'Z') && (SIG_CAR >= 'A'»I I «SIG_CAR <= 'z') && (SIG_CAR >= 'a'»11 «SIG_CAR <= '9') && (SIG_CAR >= '0'») return 1;
else return 0; }
DIGITOO {
if «SIG_CAR <= '9') && (SIG_CAR :>= '0'» return 1; else return 0; }
BIHEXAO { if «(SIG_CAR <= 'F') && (SIG_CAR >= 'A'»I I «SIG_CAR <= 'f) && (SIG_CAR >= 'a'»11 (SIG_CAR == 'x') II (SIG_CAR >= 'X'» return 1; else return 0; }
PAREN_C() { if (SIG_CAR == I)') return 1; else return 0; }
PAREN_AO { if (SIG_CAR == '(') return 1; else return 0; }
COMAO {
if (SIG_CAR == ',') return 1; else return 0; }
GATOO {
if (SIG_CAR == '#') return 1; else return 0; } COLONO {
if (SIG_CAR
== I:') return 1;
Funciones
161
else return 0;
} SEMICOLO
{
== ';') return 1;
if (SIG_CAR else return 0;
} DIAGONALO
{
== 'I') return 1;
if (SIG_CAR else return 0;
} ASTERISO
{
== '*') return 1;
if (SIG_CAR else return 0;
} ESPACIOSO
{ if «SIG_CAR else return 0;
== ' ')//CSIG_CAR == '\n')//CSIG_CAR == '\t')) return 1;
} OTROO { return 1; }
/*
RUTINA DE BUSQUEDA EN LA TABLA DE PALABRAS RESERVADAS Esta rutina se realiza mediante el algoritmo de busqueda dicotomica, en la tabla de palabras reservadas. *1
int RESERVS(CADEN) char *CADEN;
{ int int int int int
AUX; COD Ox101; 1* Inicializacion con el codigo de identificador BAJO = 0; MEDIO; ALTO = NO_PAL_RES;
while (BAJO <
{
=
= ALTO)
MEDIO BAJO + (ALTO - BAJO)/2; if «AU X strcmp(TAB_PALAB_RS[MEDIO].PALAB_RS, CADEN))
=
{ COD
*1
= TAB_PALAB_RS[MEDIO].CODIGO;
break;
== 0)
162
Fundamentos de Programacion en Lenguaje C
} else { if (AUX < 0) BAJO = MEDIa + 1; else ALTO = MEDIa - 1; } } printfC'%d\n" ,COD); return COD; } /* RUTINA PARA DETERMINAR EL VALOR DE UNA CADENA NUMERICA */ int VALUE(CANT) char *CANT;
{ int AUX = 1; int N2=0; int VAL=O; if (CANT[O]=='O')
{ switch (CANT[l])
{ case 'b': case 'B':{ N2=yyleng-2; while «CANT[N2]!='b')&& (CANT[N2]!='B'» { VAL=VAL+(CANT[N2-- ]-'O')*AUX; AUX*=2;
} break; } case 'x': case 'X':{ N2=yyleng-2; while «CANT[N2]!='x')&& (CANT[N2]!='X'» { if (CANT[N2] < ='9') VAL=VAL+(CANT[N2--]-'O')*AUX; else VAL = VAL + (9 + (CANT[N2--] & OxOf»*AUX; AUX*=16;
} break;
} default: { N2=yyleng-2; while «CANT[N2]!=' ')&& (N2)=0))
{ VAL=VAL+(CANT[N2--]-'O')*AUX; AUX*=8; } break;
Funciones
163
}
} } else VAL:;: atoi(CANT); return (VAL);
} /* RUTINA DE SEGUIMIENTO DEL AUTOMATA DE ESTADOS FINITOS DEL ANALIZADOR LEXICOGRAFICO *1 ATR_LEX TRANSLEX(ESTADO) ATR_LEX ESTADO;
{ int CONT = 0; while (1) { if «*ESTADO)[CONTJ.CARACTERESO
{
==
1)
=
SAL_LEX (*ESTADO)[CONTJ.EST_FINAL; return(*(*(*ESTADO)[CONTJ·SIG_ESTADO»;
} else ++CONT; }
} /* RUTINA DE ANALISIS LEXICOGRAFICO Esta rutina escudrina en el archivo de entrada, hasta encontrar un elemento semantico dellenguaje bajo analisis. Esto 10 hace lIamando repetidamente a la rutina de seguimiento del aut6mata de estados finitos, luego de leer un caracter mas, hasta lIegar a un estado final. Cuando se lIega a un estado final, la rutina retorna, con el valor del token reconocido. yylexO, entrega ademas, la longitud del token reconocido y la cadena que re presenta a dicho token, a traves de las variables yyleng y yytext, respectivamente.
*/ int yylexO { int N2 = 0; yylval :;: 0; SAL_LEX = 0; yyleng = 0; /* longitud del token */ ESTADOLEX = (ATR_LEX) INICIAL; while (SAL_LEX == 0)
{
SIG_CAR = SIG_LINEA[N2++]; if (!«ESTADOLEX == (ATR_LEX)INICIAL)&&ESPACIOSO» yytext[yyleng++] = SIG_CAR; ESTADOLEX = TRANSLEX(ESTADOLEX);
164
Fundamentos de Programacion en Lenguaje C
} yytext[yyleng] = 1\01; switch (SAL_LEX) { case Ox010l:{SAL_LEX = RESERVS(yytext); break;} case Ox0138:{ yylval=VALUE(yytext); break;} case OxffOl:{ERRLEXI(l); break;} case Oxff02:{ERRLEXI(2); break;} case Oxff03:{ERRlEXI(3); break;} } return (SAL_LEX);
}
/*
RUTINA DE ATENCION DE ERRORES LEXICOGRAFICOS
*/
ERRLEXI(TIPO) intTIPO;
{ switch (TIPO)
{ case 1:{ printf("CARACfER NO ACEPTADO POR LA GRAMATICA DE LEMEF\n"); break;
} case 2:{ printf(IISE ESPERABA UN TEXTO EN C\n"); break;
} case 3:{
ll printf("SE ESPERABA UN COMENTARIO\n );
}
} return 0; } int salida=!; mainO { INICIALEXO; while (salida)
{ int NSIG_CAR=O; ll printf("ESCRIBE EL ELEMENTO SEMANTICO QUE DESEAS RECONOCER\n ); while «SIG_LINEA[NSIG_CAR++ ]=getcharO) != '\n'); SIG_LINEA[ --NSIG_CAR]=I\OI; printf("Se reconocio el token no. %d\nl1, yylexO); printf(" %s\n", yytext); if (yylval!=O) printf(" VALOR = %d\nll, yylval);
Funciones
165
while (getcharO != '\n'); printf("Deseas continuar con esta prueba?\n"); if (getcharO == In') salida =0; while (getcharO != '\n'); } return 0;
}
Ejercicios 6.1
Desarrollar el programa de la calculadora estadistica especificado en el ejemplo 6.3.
6.2 Desarrollar una funcion que ordene alfabeticamente un conjunto de cadenas materializadas por medio de un arreglo de apuntadores a cadena y el conjunto ordenado se almacene en otro arreglo de apuntadores. La funcion debe tener como panimetros los apuntadores a ambos arreglos. Desarrollar un programa para probar esta funcion. 6.3
Desarrollar una funcion que presente un juego de menus de dos niveles. Un primer menu es la entrada 0 menu principal del sistema y permite acceder a menus especificos que ofrecen diversos servicios. Las etiquetas de cada menu 0 apuntadores a elIas deben pasarse como panimetros a la funcion MENU, 10 mismo que apuntadores a funciones que ejecuten los servicios ofrecidos en los submenus (segundo nivel).
Capitulo 7
FUNCIONES DE ENTRADA Y SALIDA
7.1 lNTRODUCCION Las funciones de entrada y salida no forman parte de la definicion del 1enguaje C, pero normalmente esuin disponibles en la biblioteca estandar de cualquier sistema. Por ejemplo, ellas permiten a los programas: a. b.
c. d.
interactuar con el Qsuario, almacenar y recuperar informacion de los dispositivos de almacenamiento secundario, manipular informacion en la memoria principal, y recibir y transmitir informacion a traves de una red.
La tabla 7.1 muestra las fases por las que deben pasar todas las operaciones de entrada/salida, con cualquier dispositivo periferico. Las operaciones de lectura y escritura pueden realizarse sobre dispositivos de entrada/salida, archivos en dispositivos de almacenamiento secundario 0 buffers impJantados en memoria principal. Ademas de las operaciones basicas realizadas durante las tres fases de trabajo con archivos de entrada/salida, existen otras operaciones con archivos. La tabla 7.2 muestra algunas de estas.
7.2
NOMBRES DE ARCHIVOS
Los nombres de los archivos y de los dispositivos de entrada/salida son dependientes del sistema operativo. En el DOS la identificacion de un archivo consta de tres elementos: la ruta de directorio, el nombre y la extension. La ruta de directorio esta dada por el nombre del dispositivo y la secuencia de nombres de directorio separados por el caracter '\' que indica la ubicacion del archivo en el arbol de directorios del dispositivo en donde se encuentra almacenado. En virtud de que en la especificacion de cadenas en lenguaje C el caracter '\' se usa como
168
Fundamentos de Programacion en Lenguaje C
canicter de control, para que el compilador 10 interprete correctamente se debe escribir doble '\\'. El nombre de archivo es un identificador construido con caracteres alfanumericos, la maxima longitud de este nombre de archivo es de ocho caracteres. La extension, de tres caracteres, va separada del nombre por medio del caracter punto C.). Ejemplos de nombres de archivos en DOS son los siguientes: A:\EJEMPLOS\DATOS.OOl C:\TRABAJO\EJERCICI\TEXTO.TXT
Tabla 7.1: Fases de operacion de un archivo de entrada/salida. Fase
Apertura del archivo
Operaci6n: funci6n de E/S
Con apuntador: fopen freopen
Con descriptor: open creat
Transferencia de Informacion
Cierre del archivo
lectura de caracteres: getch, getc lectura de lineas: gets, fgets read lectura de bytes: lectura de objetos: fread lecturas formateadas: scanf, fscanf escrituras formateadas: printf, fprintf escritura de objetos: fwrite write escritura de bytes: escritura de lineas: gets, fgets escritura de caracteres: putchar, putc Con apuntador: fclose Con descriptor: close
Tabla 7.2: Otras operaciones con archivos. Operaci6n
Comandar el flujo de salida Borrar archivo Cambio de nombre de archivo Crear un archivo temporal Crear un nombre temporal Posicionamiento
Funci6n ff1ush remove rename tmpfile tmpnam fseek, Iseek
Funciones de Entrada y Salida
7.3
169
ENTRADA Y SALIDA EST ANDAR
7.3.1 Biblioteca de entrada/salida estandar Para poder hacer uso de las rutinas de entrada y salida estandar se debe incluir en el programa la biblioteca stdio.h, mediante la llamada al preprocesador #include
Esta biblioteca, ademas de las rutinas de entrada/salida, contiene la definicion de diversas estructuras de datos, constantes e identificadores que son importantes en la formulaci6n de los segmentos de entrada/salida de los programas. Algunos de los mas importantes son los siguientes: stdin stdout stderr EOF
NULL FILE
Nombre del archivo de entrada estandar Nombre del archivo de salida estandar Nombre del archivo de error estandar Valor entregado por las rutin as de lectura cuando encuentran el final de un archivo 0 algun error Apuntador nulo Estructura us ada para declarar apuntadores de archivos
Las funciones de entrada/salida estandar operan con la entrada y la salida estandar del sistema. Normalmente, la entrada estandar es el teclado de la terminal 0 computadora de usuario y la salida estandar es la pantalla. Las funciones de entrada y salida estandar son las mostradas en la tabla 7.3.
Tabla 7.3: Funciones de entrada/salida estandar. Funci6n getch, getchar
putchar scanf printf gets puts
Operaci6n
Entrada estandar caracter por caracter. Salida estandar canicter por caracter. Entrada formateada. Salida formate ada. Entrada estandar linea por linea Salida estandar linea por linea
170
Fundamentos de Programacion en Lenguaje C
7.3.2 Redireccionamiento de la entrada y la salida estandar La entrada y salida estandar asociadas con un programa pueden modificarse en el momento de la ejecuci6n. La entrada estandar se modifica mediante una linea de comando como la siguiente: PROGRAMA < entrada
donde PROGRAMA es el nombre del programa ejecutable que se ha desarrollado. La nueva entrada (especificada) puede ser un archivo en un disco u otro puerto de E/S. Por su parte, la salida estandar puede modificarse mediante un comando como: PROGRAMA > salida
Igualmente, la salida, puede ser un archivo en disco u otro dispositivo, como puede ser la impresora. Si se requiere modificar tanto la entrada como la salida, el comando seria el siguiente: PROGRAMA < entrada > salida
A traves de los ductos (pipes, en ingles) de DOS 0 de UNIX, tambien es posible conectar la salida estandar de un programa con la entrada de otro que esta corriendo en paralelo, el correspondiente programa en este caso seria el siguiente: PROGRAMA
I OTROPROGRAMA
7.3.3 Funciones de entrada y salida estandar Funcion estandar de entrada getchO/getcharO Por medio de la instrucci6n: getchO;
se obtiene el siguiente caracter introducido al sistema por la entrada estandar que regulannente es el tec1ado de la computadora. Como se hace notar en la
Funciones de Entrada y Salida
171
secci6n 4.17, una funci6n retomante, como getchO, puede fonnar parte de cualquier expresi6n compleja, siguiendo la sintaxis indicada en la misma secci6n. El valor retomado por getchO 'es el caracter introducido en la entrada estandar. El prototipo de getch es char getch(void);
Cuando getchO encuentra el final del archivo que se esta leyendo, retoma el valor -1, el cual indica fin de archivo (EOF). La instrucci6n getcharO;
ejecuta la misma operaci6n que getchO, pero opera hasta que se introduce un canicter(retorno de carro).
Funcion estandar de salida putchar(C) Para escribir un caracter en la salida estandar, se utiliza la funci6n putchar(C), cuyo prototipo es: int putchar( char C);
donde C es el caracter que se desea imprimir. La funci6n putchar retoma el caracter impreso en la salida.
Funcion estandar de salida printf(FORMATO, CONCEPTOS) Por medio de esta funci6n se puede escribir una cadena de caracteres cualquiera, en la salida estandar del sistema. Esta funci6n fonnatea los distintos tipos de datos basicos manejados por C, convirtiendolos en caracteres que luego son enviados a la salida estandar. El prototipo de esta funci6n es: int printf(char *FORMATO, ARG1, ARG2, ... ) printf retorna el numero de caracteres impreso en la salida estandar. Tiene un numero de parametros variable, pero dicho numero debe coincidir con los especificadores de [ormato indicados en la cadena FORMATO.
172
Fundamentos de Programaci6n en Lenguaje C
ARG1, ARG2, ... , constituyen los datos que seran convertidos a una cadena de caracteres y posteriormente senin copiados en la salida estandar. FORMATO, es una cadena de control encerrada entre comillas (" "), la cual contiene dos tipos de elementos que son: caracteres ordinarios y especificadores de conversion. Los caracteres ordinarios son copiados directamente en la salida estandar. Por su parte, los especificadores de conversion, determinan el formato con que deberan imprimirse los argumentos de printf. Los especificadores de conversion se inician con el caracter % y terminan con el caracter que de term ina el tipo de conversion a realizar. Entre el signo % y el canicter de conversion pueden existir los elementos calificadores indicados en la tabla 7.4.
Tabla 7.4: Caliticadores de especificador de formato. Un signo-
que indica ajuste a la izquierda.
Una cadena de digitos
que indica el ancho mininlo del campo.
Un punto
que indica un campo de longitud fija. La longitud es determinada por una cadena de digitos subsecuente al punto.
El modificador de longitud I
que indica que el argumento es un entero largo.
Una cadena de digitos con un punto intermedio
En el caso de cantidades de punto £10tante 0 de doble precision, indica el numero total de digitos, y el numero de digitos a la derecha del punto decimal que deben imprimirse.
Los caracteres de conversion permiten indicar la impresion de numeros enteros en formato decimal, octal 0 hexadecimal, numeros reales en formato decimal o en notacion exponencial, caracteres y cadenas de caracteres; dichos caracteres de conversion son los de la tabla 7.5. El ejemplo 7.1 ilustra el uso de. printf, en dicho ejempl0 se presentan algunos errores tipicos, como en la sexta linea en la que se indica un campo numerico con numero de digitos de la fraccion, mayor que la longitud total del campo y luego el argumento es una cadena. Asimismo, en la primera linea falta un argumento de tipo cadena.
Funciones de Entrada y Salida
173
Tabla 7.5: Especificadores de formato de impresion. d 0
x u
e 5
e
f
9
~l ar~mento se convierte a notacion deciI?al. EI argumento se convierte a notacion octal sin signo y sin ceros a la izquierda. EI argumento se convierte a notacion hexadecimal sin signo y sin el Ox precedente. EI argumento se convierte a notacion decimal sin signo. EI argumento es un solo caracter. EI argumento es una cadena de caracteres. Los caracteres de la cadena se imprimen hasta que se encuentra un caracter nulo 0 hasta que el numero de caracteres indicado por el especificador fue alcanzado. EI argumento es de punto flotante 0 doble precision y se convierte a una notacion de la forma: [-]m.nnnnnn E±xx donde la longitud de la cadena de n's es la precision 0 numero de digitos ala derecha del punto decimal que deb en imprimirse. La precision por omision es de 6 digitos. EI argumento es de punto flotante 0 doble precision y se convierte a una notacion de la forma: [-]mmm.nnnnnn donde la longitud de la cadena de n's es la precision 0 numero de digitos ala derecha del punto decimal. La precision por omision es de 6 digitos Indica que se use el formato %e 0 %f, el que resulte mas corto. No se imprimen los ceros no significativos.
Ejemplo 7.1: Uso de printf. printf("Hola %e %d 0/05 \n", '\t', 15); printf("0/0-s.2f\n", 123.234); printf("%s.2f\n", 3.234); printf("0/o10s\n", "Hola''); printf("0/0-.3s\n", "Hola"); printf("%s.7f\n", "123456789',);
II
Imprime: Hola 15 (null) / / Error, imprime correcto 123.23 II Imprime: 3.23 1/ Imprime: Hola / / Imprime: Hoi / / Error, imprime 0.00000000
EI campo numerico del printf de la segunda linea del ejemplo 7.1 tambien esta especificado en forma incorrecta, porque si se desea imprimir dos digitos decimales y la parte entera, el campo debe tener una longitud minima total de 6 digitos y no de 5, ya que el punto decimal tambien se cuenta. Con el printf de la
174
Fundamentos de Programaci6n en Lenguaje C
tercera linea se imp rime 3.23, con el de la cuarta se imprime el mensaje Hola cargado a la derecha y con una longitud de 10 caracteres, y con el de la quinta linea se imprime el mensaje Hoi, truncando asi esta cadena.
Funcion estandar de entrada scanf(CONTROL, CONCEPTOS) Por medio de la funcion scanf se puede leer una cadena de caracteres cualquiera de la entrada estandar del sistema. Esta funcion formatea los distintos tipos de datos basicos manejados por C, convirtiendolos de los caracteres que son enviados por la entrada estandar, al formato del correspondiente tipo de dato. Regresa EOF cuando encuentra este elemento en la entrada 0 bien ocurre un error y cuando tiene exito regresa el mimero de datos convertidos y asignados. El prototipo de esta funcion es la siguiente: int scanf(char *CONTROL, ARG1, ARG2, ... )
donde ARG1, ARG2, ... , son los argumentos de scanf, y constituyen las direcciones donde se almacenaran los datos que seran leidos de la entrada estandar. Todos estos argumentos indican variables en las cuales se van a depositar los datos y por 10 tanto deben ser apuntadores. CONTROL, es una cadena de control encerrada entre comillas, la cual puede contener espacios, tabuladores y cambios de renglon que son transparentes a la interpretacion y por 10 tanto, son ignorados; caracteres ordinarios (diferentes de 0/0), los cuales se espera que coincidan con el subsiguiente canicter de entrada (que no sea un espacio); y especificadores de conversion, que determinan el formato de los argumentos de scanf. Los especificadores de conversion se inician con el car:kter % y terminan con el caracter que determina el tipo de conversion a realizar. Entre el signo % y el caracter de conversion puede haber un numero que especifica el ancho del campo, 0 el canicter * que indica supresion. Los caracteres de conversion son los indicados en la tabla 7.6. Tabla 7.6: Especificadores de Formato de Conversion de Entrada d 0
x h C
s f
El dato de entrada es entero decimal. El dato de entrada es entero octal. EI dato de entrada es entero hexadecimal. EI dato de entrada es entero corto. El dato de entrada es un solo canicter. El dato de entrada es una cadena de caracteres. EI dato de entrada es de punto flotante, con formato: signo opcional, cadena de numeros con punto decimal opcional, y exponente opcional conteniendo e 0 E Y el exponente.
Funcionesde Entrada y Salida
175
Los especificadores d, 0 Y x, pueden ser precedidos por una I, en cuyo caso, indicaran que el dato de entracla es un entero largo. El ejemplo 7.2 ilustra el uso de scanf (CONTROL, CONCEPT05). N6tese que en este caso los argumentos deben ser los apuntadores a las variables 0 arreglos, en donde se guardaran los datos de entrada Ejemplo 7.2: Uso de scanf. scanf("%d", &CUENTA); / /Mete un entero decimal a la variable CUENTA scanf("%s", CADENA); //Mete una cadena de caracteres al arreglo que //empieza en la direccion CADENA scanf("%20s", CADENA); //Mete una cadena de 20 caracteres al arreglo que //empieza en la direccion CADENA scanf("%c%c%c", &A, &B, &C);//Mete tres caracteres a las variables A, B Y C scanf("%s ", NOMBRE); //Mete una cadena de caracteres al arreglo que //empieza en la direccion NOMBRE scanf("%f", &REAL); / /Mete un numero de punto f10tante a la variable //REAL
Funcion estandar de entrada gets(S) Esta funci6n lee la siguiente linea de entrada y la deja en el arreglo apuntado por la cadena especificada en su argumento. Reemplaza el cankter de fin de linea por '\0' y regresa el apuntador a la cadena capturada, 0 bien EOF si encontr6 el final del archivo 0 hubo error en la captura. Su prototipo es: char *gets( char *5)
en donde 5 es un arreglo en el que se va a guardar la cadena de entrada.
Funcion estandar de salida puts(S) Esta funci6n escribe la cadena indicada en su argumento y un retorno de carro y nueva linea en la salida estandar. Regresa EOF si ocurri6 un error de escritura. De otra manera regresa un valor no-negativo. No altera el contenido del arreglo apuntado por el argumento. Su prototipo es: char *puts( char *5)
En la funci6n GETNUM, cuyo programa se muestra en el ejemplo 7.3, se ilustra el uso de gets y puts. GETNUM retorna el valor numerico entero de la cadena
176
Fundamentos de Programaci6n en Lenguaje C
de digitos que se tecleo a la entrada, e indica que debe ser un numero si no se tecleo un numero entero. Para esto utiliza el hecho de que gets deja en la 111tima posicion de la cadena el '\0'. Ejemplo 7.3: Uso de gets y puts.
/* Programa GETNUM.C */ GETNUMO
{ char NUM[80], n; do{ gets(NUM); if (!NUMERO(NUM)) {puts("debe ser numero\n"); n=O;} else n = 1; } while (!n); return (atoi(NUM));
} NUMERO(char *5) { int T; for ( T=O; S[T]; T ++) if (!isdigit(S[T])) return 0; return 1;
} mainO
{ printf ("EI numero tecleado es %d \n\n",GETNUMO); }
7.4 ACCESO A ARCHIVOS Ademas de la entrada y la salida estandar, la biblioteca de E/S estandar de C, stdio.h, pennite tener acceso a uno 0 varios archivos. Se dispone de funciones para abrir y cerrar archivos para lectura, escritura 0 lectura y escritura, de fun-
ciones para leer y para escribir sobre los archivos, en las diferentes formas indicadas en la tabla 5.1 (caracteres, lineas, secuencias fonnateadas, bytes y objetos), y de funciones para posicionarse en un punto especifico de un archivo. Para poder leer 0 escribir sobre un archivo, primeramente se debe abrir 0 crear el archivo. Se pueden abrir 0 crear archivos con dos modalidades, para trabajar con un apuntador al archivo, 0 para trabajar con un descriptor del archivo. EI apuntador se obtiene mediante las funciones fopen 0 freopen, y el descriptor es retomado por las funciones open y create.
Funciones de Entrada y Salida
177
7.4.1 Acceso a archivos mediante apuntadores a archivo Apertura del archivo con lafunci6n fopen(NOMBRE,MODO) Con la Hamada a esta funci6n, se obtiene un apuntador de tipo FILE, al cual puede despues hacerse referencia para realizar las transferencias de informaci6n. Esta funci6n tiene el siguiente prototipo: FILE fopen(char *NOMBRE, char *MODO); El tipo FILE esta declarado en la biblioteca, por 10 cual, para acceder a archivos en memoria 0 en disco, 0 de otro dispositivo perif6rico, es necesario incluir esta biblioteca en el encabezado del programa. NOMBRE es el nombre del archivo al que se va a acceder; obviamente, este nombre debe tener la estructura de nombres de archivo del sistema operativo. MODO es el modo en que se va a trabajar el archivo que puede ser: "r"
"w"
"a" "r+" "w+" "a+"
Abre archivo de texto para lectura Crea archivo de texto para escritura, descarta el contenido previo si existe Agrega; abre 0 crea un archivo para escribir al final Abre un archivo para actualizaci6n, lectura 0 escritura Crea archivo de texto para actualizaci6n; descarta contenidos previos Agrega, abre 0 crea el archivo de texto para actualizaci6n, escribiendo al final
El modo actualizaci6n permite la lectura y escritura del mismo archivo. Si se Ie agrega una b a la cadena se indica que el archivo es binario. El apuntador entregado por la funci6n fopen debe guardarse en una variable de tipo apuntador a archivo (tipo FILE), para que luego pueda hacerse referencia a 61. Por ejemplo: FILE *SALIDA; SALIDA
/ / Declaracion del apuntador a archivo
= FOPEN("A:\\Archivo1.txt:", "w"); 1/ Asignacion del apuntador de archivo
Transferencias de datos a archivos de caracteres Una vez abierto el archivo se pueden hacer transferencias de datos mediante funciones de entrada/salida que operan con apuntadores a archivos que fueron
178
Fundamentos de Programaci6n en Lenguaje C
obtenidos con fopen, las cuales tambien se tienen disponibles en la biblioteca stdio.h. Las funciones de la tabla 7.7 son las que operan con archivos de caracteres. Tabla 7.7: Funciones de entrada/salida para archivos de caracteres getc putc fscanf fprintf fgets fputs ungetc
Entrada de archivo canicter por canicter. Salida a archivo caracter por caracter. Entrada formateada desde un archivo de texto Salida formateada hacia un archivo de texto Entrada de archivo, linea por linea, de maximo N caracteres Salida a archivo, linea por lfnea Regresa un caracter leido al buffer del archivo
Las seis primeras funciones de la lista anterior funcionan de manera similar a las funciones de E/S estandar, con la unica salvedad de que el archivo de entrada o salida es el especificado como tipo FILE, y cuyo apuntador fue obtenido con la funcion fopen. EI prototipo de estas funciones es: char getc(FlLE *ARCHIVO); char putc(int C, FILE *ARCHIVO); int fprintf(FILE *ARCHIVO, char *CONTROL, ARG1, ARG2, ... ); int fscanf(FILE *ARCHIVO, char *CONTROL, ARG1, ARG2, ... ); char *fgets(char *5, int N, FILE *ARCHIVO); char *fputs(char *5, FILE *ARCHIVO);
En todos los casos, ARCHIVO es el apuntador a archivo retomado por fopen. es el numero maximo de caracteres que se van a leer, 0 bien una lfnea. Los otros argumentos tienen exactamente la misma descripcion que en el caso de las funciones de E/S estandar. ungetc regresa el caracter indicado, al buffer del ARCHIVO. Su prototipo es N
char *ungetc(int C, FILE *ARCHIVO);
donde C es el caracter y ARCHIVO el apuntador al archivo obtenido con fopen.
Entrada/Salida de objetos diversos. Funciones fread y
fwrite
La biblioteca stdio.h tambien cuenta con funciones que permiten dar entrada 0 salida a datos de fndole diversa y no como secuencias de caracteres. Las funcio-
Funciones de Entrada y Salida
179
nes fread y fwrite realizan estas tareas, es decir envian a un archivo cuyo apuntador se obtuvo con fopen objetos de cualquier longitud, 0 bien leen del archivo, objetos de cualquier longitud. EI prototipo de estas dos funciones es: size_t fread(const void *PTR, size_t SIZE, size_t NOBl, FILE *ARCHIVO) size_t fwrite(const void *PTR, size_t SIZE, size_t NOBl, FILE *ARCHIVO)
donde PTR es el buffer de salida 0 de entrada, de donde se toman 0 en donde se colocan los NOB] objetos de tamafio SIZE que se van a leer 0 a escribir en el archivo cuyo apuntador es ARCHIVO. fread retoma el numero de objetos leidos y fwrite el numero de objetos escritos.
7.4.2 Acceso a archivos mediante descriptores de archivos EI descriptor de archivos es un entero positivo que el sistema operativo proporciona si tiene exito la operacion de apertura de un archivo mediante la funcion open. Si el archivo existe 0 se puede crear y si se tiene autorizado el acceso a el, la operacion de apertura tiene exito. EI descriptor de archivo se emplea en lugar del nombre del archivo al realizar las operaciones de E/S. Los descriptores de los tres archivos estandar son:
o 1
2
stdin stdout stderr
Los descriptores de archivQs son una opci6n a los apuntadores a archivo de tipo FILE, los cuales se han descrito con anterioridad, y son retomados por la funcion fopen. Ademas, son utilizados por funciones de E/S como read y write y otras funciones de control de dispositivos de E/S.
Apertura del archivo con lafunci6n
open(NOMBRE,ACCESO,MODO)
Esta funcion se encuentra en la biblioteca io.h. Con la Hamada a la funcion open(NOMBREI MODO), se obtiene un entero, al cual representa el descriptor de archivo al que despues debe hacerse referencia para realizar transferencias de informacion. Tiene el prototipo: int open(const char *NOMBRE1 int ACCESO [
I
unsigned MODO] );
180
Fundamentos de Programacion en Lenguaje C
donde NOMBRE es la trayectoria y nombre del archivo que se va a abrir, ACCESO es una de las siguientes constantes, cuando se trabaja con archivos previamente creados: O_RDONLY O_WRONLY O_RDWR
Abre solo para lectura Abre solo para escritura Abre para lectura y escritura
Se puede crear un archivo con la opcion de acceso O_CREATE
Crea y abre un archivo
En este ultimo caso se debe indicar el MODO, que puede ser uno de los siguientes: Crea archivo solo para lectura Crea archivo solo para escritura Si se desean ambos modos, se debe crear el archivo combinando los dos modos mediante la operacion logica binaria " I". Las constantes se encuentran definidas en la biblioteca fcntl,h, por 10 cual tambien se debe de incluir.
Funciones read y write Estas funciones permiten dar salida 0 entrada a secuencias de bytes, como archivos binarios, y se encuentran en la biblioteca io.h. La sintaxis de read es: int read(int DA, char BUFER[ ], int NBYTES); donde DA es el descriptor de archivos obtenido con open, BUFER es el area de memoria en donde se depositara la informacion leida y NBYTES el numero de bytes que se requiere leer. read retoma el numero de bytes leidos, el cual puede ser diferente de NBYTES, si se Uega al fin de archivo 0 hay algun error. La sintaxis de write es: int write(int DA, char BUFER[ ], int NBYTES);
Funciones de Entrada y Salida
181
donde DA es el descriptor de archivos obtenido con open, BUFER es el area de memoria de donde se tomara la informacion a la que se Ie va a dar salida y NBYTES e1 numero de bytes que se requiere escribir. write retoma el numero de bytes escritos, el cual puede ser diferente de NBYTES si hay algun error.
7.5 ACCESO ALEATORIO A ARCHIVOS La entrada y la salida normalmente es secuencial, cada lectura 0 escritura en el archivo ocurre en una posicion justo despues de la anterior. Cuando es necesario leer de acuerdo a una secuencia aleatoria, puede usarse la funcion fseek para posicionar el apuntador en un e1emento cualquiera del archivo. La sintaxis de esta funcion es: long fseek(FIlE *ARCHIVO, long DESP, int ORIGEN); donde ARCHIVO es el apuntador retomado por una funcion fopen al abrir un archivo. ORIGEN puede ser 0, 1 0 2, para especificar que el desplazamiento sera desde el principio del archivo, desde la posicion actual 0 desde el final del archivo, respectivamente, y DESP es el desplazamiento. Algunos ejemplos de uso de esta funcion, son: fseek(ARCHI, Ol, 2) fseek(ARCHI, Ol, 0)
1* Se va al final del archivo *1 1* Se va al principio del archivo *1
Iseek es similar a fseek pero opera con el descriptor de archivos obtenido mediante la Hamada open. El programa del ejemplo 7.4 ilustra una aplicacion de las instrucciones open, creat y Iseek. El programa "agregatx.c", abre 0 crea e1 archivo que se Ie indica, recorre el apuntador al final del archivo y agrega el texto que se Ie dio al programa a traves de la entrada estandar (teclado). Ejemplo 7.4: Programa "agregatx.c".
1******* Este programa prueba la forma en que debe emplearse la lIamada
open; y/o la lIamada creat, para agregar texto a un archivo en disco. EI programa interactua con el usuario, abre y cierra el archivo indicado por el parametro, agregando el texto que se introduce, al solicitar el programa el nuevo texto a agregar. Notese que para poder agregar texto se debe utilizar la lIamada Iseek para posicionarse en el lugar adecuado. ********************************/ #include#include
182
Fundamentos de Programacion en Lenguaje C
int SALIDA; mainO
{ int ap, CON; char nuevotexto[512], NOMBRE[20]; for(CON = 0; CON < 512; CON++ )nuevotexto[CON] = 0; printf("DAME EL NOMBRE DEL ARCHIVO\n"); scanf("%s", NOMBRE); if «SALIDA=open(NOMBRE,O_RDWR,O))
}
7.6 CONVERSION DE FORMATOS EN MEMORIA La biblioteca de entrada/salida stdio.h tambien cuenta con funciones amllogas a printf y scanf, las cuales se utilizan para hacer conversion de [onnatos, con base en cadenas de caracteres almacenadas en la memoria principal del sistema. EI prototipo de estas funciones es el siguiente: sprintf(char *CADENA, char *CONTROL, ARG1, ARG2, ... ) sscanf(char *CADENA, char *CONTROL, ARG1, ARG2, ... )
En ambos casos, CADENA es el arreglo 0 cadena de caracteres en memoria sobre la cual operaran las funciones sprintf y sscanf. Con sprintf se escribe sobre el arreglo apuntado por CADENA, la lista de conceptos indicados como los argumentos ARG1, ARG2, ... , fonnateados con la estructura de la cadena de control, como una secuencia de caracteres. Con scanf se lee del arreglo la secuencia de conceptos en fonna de caracteres y se almacenan en las variables indicadas por los argumentos. El apuntador de archivo en este caso se cambia por el apuntador al arreglo de caracteres. Una aplicacion importante de este tipo de conversion de [onnatos es en el caso de la salida de texto en modo gnifico, ya que la funcion o uttextxy, que se encuentra en la biblioteca graphics.h, solo transfiere a la pantalla gnifica cadenas de caracteres, por 10 cual antes de poder usar esta funcion de salida gnifica de textos, es necesario convertir los datos que se van a presentar en una cadena de caracteres, mediante la funcion sprintf, como 10 indica el ejemplo 7.5. La funcion outtextxy se estudia en el Capitulo 8.
Funciones de Entrada y Salida
183
Ejemplo 7.5: Uso de la Funcion sprintf. sprintf(BUfER,"%4.1f',VXMIN); outtexb
7.7 MANEJO DE TEXTO EN PANTALLA CON POSICIONAMIENTO ALEATORlO Y VENTANAS DE TEXTO
La biblioteca conio.h cuenta con un conjunto de funciones de entrada/salida que permiten manejar el texto que es impreso en la pantalla de la computadora colocandose aleatoriamente en la posicion que se desee 0 dando entrada a la informacion desde alguna posicion particular, en la cual se haya ubicado el cursor. Asimismo, cuenta con una funcion que pennite cerrar el area de trabajo a una ventana especifica. En la tabla 7.8 se da una lista de estas funciones.
Tabla 7.8: Funciones de Entrada-Salida para ventanas de texto. clrscr cscanf cprintf cgets cputs gotoxy textattr textbackground textcolor wherex wherey window
Limpia la ventana con el color de fondo Entrada formateada desde la ventana de texto Salida formateada hacia la ventana de texto Entrada desde la ventana, linea por linea Salida a ventana, linea por linea Se coloca el cursor en la posicion (x,y) (columna, fila) Define los atributos de color del texto y el fondo en la ventana Define el color del fondo en la ventana Define el color del texto en la ventana Retorna la columna de la posicion actual del cursor Retorna la fila de la posicion actual del cursor Define la ventana de texto de trabajo
7.7.1 Ventanas de texto En el modo texto, la pantalla de la computadora puede verse como una matriz de caracteres de 25 line as por 80 columnas, tal como se muestra en la figura 7.1. La esquina superior izquierda de la pantalla tiene las coordenadas (1,1) y la inferior derecha (80,25).
184
Fundamentos de Programaci6n en Lenguaje C
80
2
Figura 7.1: Fonnato de la pantalla en el modo canicter.
En ausencia de otro tipo de instrucciones, los caracteres de salida de la computadora se imprimen secuencialmente sobre ella, con la salvedad de que los caracteresprovocan que se continue la impresion al principio de la siguiente fila. Es posible reducir la ventana de trabajo y no usar toda la pantalla, mediante la funci6n window que se encuentra en la biblioteca conio.h. La sintaxis de esta funcion es: void window( int rZQ, int CIMA, int DER, int ABAJO); window define una ventana de texto sobre la pantalla definida como se indica
en la figura 7.1. Si las coordenadas especificadas en sus argumentos no son validas, se ignora la orden de especificar una ventana de trabajo. No retoma valor. Las coordenadas rZQ, crMA representan la esquina superior izquierda de ·la ventana y DER, ABAJO definen las coordenadas de la esquina inferior derecha de la misma. El tamano minimo de una ventana es de una columna por una linea. La ventana por omisi6n es la pantalla completa y tiene las coordenadas (1, 1, 80, 25). El ejemplo 7.6 es un programa que ilustra la manera como se crea una ventana de texto, en un cuadro cuyo vertices superior .izquierdo e inferior derecho son
Funciones de Entrada y Salida
185
los puntos (10,10) Y (40,11), respectivamente. Luego de crear la ventana, define nuevos colores de impresion mediante las funciones textcolor y textbackground, limpia la ventana con el color de fondo mediante clrscr, y finalmente imprime un texto en la ventana con la funcion cprintf. La tabla 7.8 muestra las funciones de entrada/salida de texto, posicionamiento y especificacion en ventanas de texto. Ejemplo 7.6: Uso de ventanas de texto.
/* Programa VENTEXT.c */ #includeint main(void) { window(10,10,40,11); textcolor(BLACK) ; textbackground(WHITE) ; clrscrO; cprintf("Hola estoy probando ventanas\r\n lf ) ; cprintf("grandes y potentes"); return 0;
}
7.7.2 Especificacion de las caracteristicas de la ventana Pueden controlarse las siguientes caracteristicas de las salidas a una ventana de texto:
a. Color del texto b. Color del fondo c. Posicion del cursor El color del texto que se imprimini se define con la funcion textcolor, cuyo prototipo es: void textcolor(int newcolor); Tambien se puede definir con la funcion textattr. El color del fondo de la ventana se define con la funcion textbackground, la cual tiene el siguiente prototipo: void textbackground(int newcolor);
186
Fundamentos de Programacion en Lenguaje C
La funcion textattr pennite definir tanto el color del texto, como el color de fondo y la condicion de parpadeo. Su prototipo es: void textattr(int newattr);
donde newattr define los colores y la condicion de parpadeo tiene el siguiente fonnato: 7 B
6 b
donde: ffff bbb B
5 b
4 b
3 f
2
f
1 f
0 f
4-bit de color de texto (0 to 15) 3-bit de color de fondo (0 to 7) bandera de parpadeo
Para posicionarse dentro de la ventana y desde ahi realizar la siguiente impresion 0 la siguiente captura, se utiliza la funcion gotoxy, que tiene el prototipo: void gotoxy(int x, int V);
Por otro lado, se puede detenninar por programa la posicion actual del cursor mediante las funciones cuyo prototipo es: int wherex(void); int wherey(void);
donde wherex nos indica la columna y wherey la fila.
7.7.3 Funciones de entrada/salida a la ventana de texto Con las ventanas de texto se pueden imprimir 0 leer textos mediante las funciones: int cprintf(char *CONTROL, ARG1, ARG2, ... ); int cscanf(char *CONTROL, ARG1, ARG2, ... ); char *cgets(char *5, int N); char *cputs(char *5);
Si la cadena escrita por el programa no cabe en la ventana, ocurre un mal funcionamiento de la impresion.
D
Funciones de Entrada y Salida
187
El ejemplo 7.7 ilustra el uso de ventanas de texto. EI programa presenta un menu de texto de una base de datos, en una ventana creada mediante la funci6n window.
Ejemplo 7.7: Programa "menugr.c".
/*
Este programa presenta un MENU en una ventana de dialogo
*/
# include# include void ESCRIBE(int X,int Y,char *CADENA)
{ gotoxy(X, V); cprintf("%s" ,CADENA);
} void VENTANA(int X1,int Yl,int X2,int Y2,int FONDO,int COLOR,int TEXCOLOR)
{ window(X1 + 1,Y1 + 1,X2+ 1,Y2+ 1); textbackground(FON DO); clrscrO; window(Xl, Y1 ,X2, Y2); textbackground( COLOR); textcolor(TEXCOLOR); clrscrO;
}
/*
MENU se encarga de recoger la opcion deseada por el usuario y regresa un valor entero correspondiente a la opcion */
int MENU(void)
{ char OPCION; do{ clrscrO; VENTANA(2,2,2S,4,LIGHTGREEN,LIGHTGRAY,BLACK); ESCRIBE(2,2,"Elija una opci6n"); VENTANA(2,6,2S,23,LIGHTGREEN,LIGHTGRAY,BLACK); ESCRIBE(2,l,"(1) Altas"); ESCRIBE(2,3,"(2) Consultas"); ll ESCRIBE(2,S,"(3) Bajas ) ; ESCRIBE(2,7,"( 4) CargarJl); ESCRIBE(2,9,"(S) Modificar"); ESCRIBE(2,11,1I(6) Ordenar"); ESCRIBE(2,13,"(7) Guardar"); ESCRIBE(2,1S,"(8) Salir");
188
Fundamentos de Programacion en Lenguaje C
ESCRIBE(2,17,"su opcion es: "); OPCION=getchO; }while (!(OPCION>='l' && OPCION<='8'»; return(OPCION);
} mainO
{ VENTANA(O,O,25,79,BLACK,BLACK,WHITE); c1rscrO; while (1)
{ switch(MENUO)
{ case '8': exit(O);
} } }
Ejercicios 7.1 Desarrollar un programa en lenguaje C que implante una base de datos de calificaciones de los alumnos de un programa de estudios que tiene 12 asignaturas. Los alumnos deben tener un numero de registro y la base de datos debe contener informacion acerca de: Escuela de procedencia Promedio en licenciatura Fecha de titulacion RFC
Si cubrieron los requisitos de ingreso Principal area de interes EI programa debe poder dar los siguientes servicios: Reporte de calificaciones por alumno Consultas Altas Bajas Cambios 7.2 Desarrollar un programa que abra un archivo *.bmp y dibuje en pantalla la grafica almacenada de esta manera. 7.3 Desarrollar un programa que presente en pantalla la informacion contenida en un archivo de base de datos tipo dBase III+, registro por registro.
Capi.tulo 8
GRAFICOS
8.1 INTRODUCCION En este capitulo se examinan varias caracteristicas de las herramientas para manejar graficos, disponibles en algunos ambientes de programaci6n en C, como es el caso de Turbo C y el Turbo C++. Se describen las funciones basicas para la representaci6n de graficos, tales como putpixel, line, circle, arc, rectangle, drawpoly y ellipse, la forma de imprimir en la pantalla textos graficos y funciones graficas mas elaboradas como: bar, bar3d, pieslice y sector. Se proporciona, adem as , un conocimiento general del manejo de graficos en el ambiente de una computadora personal, con compiladores como los antes referidos. Debe mencionarse que la biblioteca de graficos es una de las menos portables entre los distintos ambientes de programaci6n en C. Es importante que el c6digo para mostrar graficos en la pantalla del monitor sea independiente de la tatjeta de video y de su manejador. Se pueden crear este tipo de programas con la ventaja de que solo se necesitara una version del mismo, y no una para cada tipo de adaptador de video. La manera de lograr esto se presenta en la seccion 8.2.1. Para trabajar con las funciones graficas se tiene que incluir el archivo graphics.h.
8.2 INICIALIZACION DEL SISTEMA DE GRAFICOS 8.2.1 Modos de Inicializaci6n Para usar las funciones graficas es necesario poner previamente el manejador de video en uno de los modos graficos, mediante la funci6n initgraph( ), la cual tiene el prototipo: void far initgraph(int far *MANEJADOR, int far *MODO, char far *CAMINO);
190
Fundamentos de Programacion en Lenguaje C
La funcion initgraph( ) carga en memoria un manejador de graticos que corresponde al numero indicado por el panimetro MANEJADOR. Ninguna funcion grafica se puede ejecutar si no esta cargado en memoria un manejador de graficos. El parametro MODO es un numero entero que especifica el modo de video que van a emplear las funciones graficas. Por ultimo, se puede especificar la rota ("path") en donde se encuentra el manejador, por medio del parametro CAMINO. Si no se especifica una rota 0 camino, el programa busca en el directorio de trabajo actual. Los manejadores de graficos estan contenidos en archivos con extension .BGI, los cuales deb en encontrarse disponibles en el sistema. Para especificar el manejador debe usarse su numero 0 macronombre (definido en la biblioteca graphics.h). Los valores de controlador que se utilizan son los de la tabla 8.1. Cuando se utiliza DETECT, initgraph detecta automaticamente el tipo de tarjeta de video que tiene el sistema, selecciona el modo de video de mas alta resolucion y establece MANEJADOR y MODO en sus valores correctos. Si el programa establece el modo, tiene que usar uno de los modos graticos que se muestran en la tabla 8.2. Ladeteccion de tarjeta Ie permite al programa ajustarse automaticamente a cualquier entomo de graficos. Tabla 8.1: Manejadores de graficos. Macro
DETECT CGA MCGA EGA EGA64 EGAMONO IBM8514 HERCMONO ATT400 VGA PC3270
Equivallente
0 1 2 3 4 5 6 7 8 9 10
Para dejar de usar un modo de video grafico y volver al modo de texto, se usa c10segraph 0 bien restorecrtmode. Sus prototipos son void far closegraph(void) void far restorecrtmo de (void)
Graficos
191
Se debe usar la funcion c10segraph cuando el programa se va a continuar ejecutando en modo texto. Esta funcion descarga la memoria usada para guardar los juegos de caracteres, controladores y memorias intermedias y restablece el modo video que estaba antes de la Hamada a initgraph. Si el programa va a cambiar entre los modos texto y graficos se use restorecrtmode el cual vuelve a poner el adaptador de video en el modo en que estaba antes de la primera Hamada a initgraph, pero no elimina los juegos de caracteres, controladores 0 memorias intermedias intemas. Si el programa se esta acabando, no tiene importancia la funcion que se utilice.
Tabla 8.2: Manejadores y modos de los graficos de Turbo C. Manejador
Modo
CGA
CGACO CGACI CGAC2 CGAC3 CGAHI MCGACO MCGACI MCGAC2 MCGAC3 MCGAMED MCGAHI EGALO EGAHI EGA64LO EGA64HI EGAMONOHI HERCMONOHI ATT400CO ATT400Cl ATT400C2 ATT400C3 ATT400CMED ATT400CHI VGALO VGAMED VGAHI PC3270HI IBM8514LO IBM8514HI
MCGA
EGA EGA64 EGAMONO HERC ATT400
VGA
PC3270
Valor
0 1 2 3 4 0 1 2 3 4 5 0 1 0 1 3 0 0 1 2 3 4 5 0 1
2 0 0 1
Resoluci6n
320X200 320X200 320X200 320X200 640X200 320X200 320X200 320X200 320X200 640X200 640X480 640X200 640X350 640X200 64QX350 640X350 720X348 320X200 320X200 320X200 320X200 640X200 640X200 640X200 640X350 640X480 720X350 640X480 lO24X768
192
Fundamentos de Programacion en Lenguaje C
8.2.2 Determinacion del entomo gnifico Cuando se deja al sistema de gnificos de Turbo C que establezca el modo de video, el programa necesita alguna forma de conocer el entomo. La funcion getviewsettings retorna las dimensiones de la ventana y getmaxcolor el numero de colores permitidos en el modo de video aplicable. Los programas de graficos pueden usar los valores retomados por getviewsettings y getmaxcolor para que se ajuste automaticamente a los diferentes modos de video.
Limites de la ventana de trabajo de gnificos La funcion getviewsettings retoma las dimensiones de la ventana de trabajo de graficos. Su prototipo es void far getviewsettings(struct viewporttype far *INFO) La estructura viewporttype se define en graphics.h de la siguiente forma: struct viewporttype {int IZQ, CIMA, DER, FONDO; int CLIP; } Los campos IZQ, CIMA, DER y FONDO contienen las coordenadas de los extremos superior izquierdo e inferior derecho de la ventana, el concepto de ventana se explica en la seccion 8.3. CLIP define la forma de funcionamiento de la ventana; cuando CLIP es cero, no hay corte de las salidas que sobrepasan los limites de la ventana. Cuando es distinto de cero, se realiza el corte para impedir que se sobrepasen los limites.
Colores disponibles Los diferentes modos de video permiten un numero distinto de colores. Por ejemplo, e1 modo 4 de CGA permite 4 colores, pero el modo 14 de EGANGA soporta 16. Se tiene que usar getmaxcolor para determinar cuantos colores hay disponibles. Su prototipo es int far getmaxcolor(void) La funcion getmaxcolor( ) retorna e1 numero maximo de colores permitido por el modo de video aplicable. En todos los modos el primer color es cero, el segundo 1 y as! sucesivamente hasta ellimite soportado por el modo de video.
Graficos
193
Conocer el numero maximo del colores implica que se conocen todos los colores disponibles.
8.3 VENTANAS y COLORES 8.3.1 Ventanas Normalmente, los graticos se trabajan asociados a una ventana, que es el area de la pantalla que muestra la salida gr::ifica. Por omision toda la pantalla del monitor es la ventana activa, pero por programa se pueden definir otras ventanas. Las funciones que ejecutan graficas trabajan sobre la ventana especificada, y no sobre toda la pantalla. El extremo superior izquierdo de una ventana es siempre la posicion 0,0 y a partir de 61 se cuenta la posicion a la derecha (coordenada X) y hacia abajo (coordenada Y) en pixeles. Por otro lado, el tipo mas comun de ventana corta la salida en su limite, no permitiendo que se presente informaci6n grafica mas alla de dicho limite. La figura 8.1 muestra la idea de la ventana de graficos.
Figura 8.1: Ventana de graficos.
8.3.2 Establecimiento de la vent ana de trabajo El establecimiento de la ventana de trabajo para presentar graficos en Turbo C se puede relacionar con la informaci6n de los limites de la ventana obtenidos en la estructura viewporttype que se explico en la secci6n 8.2.2. En el ejemplo 8.3 se aplica esto para definir la ventana para presentar una gnifica de barras.
194
Fundamentos de Programacion en Lenguaje C
La ventana por omisi6n es toda la pantalla, con el numero de pixeles definido en la columna de resoluci6n de la tabla 8.2. La ventana se puede redefinir mediante la funci6n setviewport, cuyo prototipo es: void far setviewport(int IZQ, int CIMA, int DER, int ABAJO, int CLIP); Como en el caso e la estructura viewporttype, las coordenadas de la esquina superior izquierda de la ventana son IZQ,DER y las de la esquina inferior derecha DER,ABAJO. CLIP determina el tipo de ventana: igual a 0 para una ventana con recorte y distinto de cero para ventanas sin recorte.
8.3.3 Establecimiento del color del dibujo. Selecci6n del color del dibujo Se puede indicar el color de dibujo a utilizar usando setcolor, cuyo prototipo es void far setcolor(int color); El valor de color tiene que estar en el range valido para el modo grafico vigente que pudo ser determinado por getmaxcolor. Una vez indicado el color, las operaciones descritas en la secci6n 8.4, se realizan con el color establecido.
8.4
FUNCIONES BAsICAS DE GRAFICACION
8.4.1 Pixel, linea, rectangulo, poligono, arco, circulo yelipse Las funciones basicas para el trazado de graficos son las que dibujan un punto, una linea, un rectangulo, un poligono, un arco, un circulo y una elipse. En Turbo C, estas funciones son llamadas putpixel, line, rectangle, drawpoly, arc, Circle, y ellipse, respectivamente. Sus prototipos son los siguientes: void void void void void void void
far far far far far far far
putpixel(int X, int V, int COLOR); line(int INICIOX, int INICIOY, int FINX, int FINY); rectangle(int IZQ, int CIMA, int DER, int FONDO); drawpoly(int NUMPUNTOS, int far *PUNTOS) arc(int X, int Y, int ANGULOI, int ANGULOF, int RADIO); circle(int X, int V, int RADIO); ellipse(int X, int Y, int ANGULOI, int ANGULOF, int RADIOX, int RADIOY);
Graficos
195
La funcion putpixel dibuja un punta del color indicado como panimetro, en la posicion determinada par X e Y. La funcion line dibuja una linea desde la posicion especificada por INICIOX, INICIOYa la posicion especificada por FINX, FINY del color previamente especificado. Por omision, el color deldibujo es blanco. La funcion rectangle dibuja un rectcingulo del color vigente, cuyo vertice superior izquierdo tiene las coordenadas IZQ, CIMA Y cuyo vertice inferior derecho tiene las coordenadas DER, FONDO. La funcion drawpoly dibuja un poligono de NUMPUNTOS puntos del color vigente, cuyos vertices estcin definidos en el arreglo de enteros cuyo apuntador inicial es PUNTOS. La funcion arc dibuja un arco de un circulo de radio RADIO del color vigente, con el centro en la posicion especificada por X, Y, desde el cingulo ANGULOI, hasta el cingulo ANGULOF. La funcion circle dibuja un circulo de radio RADIO del color vigente, con el centro en la posicion especificada por X, Y. Si alguna de las coordenadas estuviera fuera de rango, no se hacen correcciones automaticas. Finalmente, la funcion ellipse dibuja una elipse del color vigente de radio horizontal RADIOX, radio vertical RADIOY, centro en la posicion especificada por X, Y, desde el cingulo ANGULOI, hasta el cingulo ANGULOF.
Tabla 8.3: Modelos de relleno. Macros
Valor
EMPTY_FILL SOLID FILL LINE FILL L TSLASH FILL SLAH FILL BKSLASH FILL LTBKSLASH XHATCH FILL INTERLEAVE WIDEDOT FILL
0 1 2 3 4 5 6 8 9 10
CLOSEDOT_FILL
11
USER FILL
12
Significado
Relleno con color de fondo Relleno con color solido Relleno con line as Relleno con el simbolo III, poco denso Relleno con el simbolo I II Relleno can el simbolo \\\ Relleno con el simbolo \\\, poco denso Relleno can rayado Relleno can superposicion Relleno con puntos ampliamente espaciados ReBeno can puntas muy poco espaciados Relleno con modelos personalizados
196
Fundamentos de Programacion en Lenguaje C
8.4.2 Rellenado de un area Se puede rellenar de color cualquier figura cerrada usando la funci6n f1oodfill. Su prototipo es: void far f1oodfill(int X, int Y, int COLOR_ BORDE);
Para esto se llama f100dfill con las coordenadas de un punto dentro de la figura y eI color de las lineas que constituyen su contomo como panimetros. Debe tenerse la seguridad de que el objeto que se esta rellenando es completamente cerrado. Si no 10 es, la zona situada fuera de la figura se rellenara tambien. EI color con que se rellena el objeto esta determinado por el modo y color de relleno vigentes. Por omisi6n, Turbo C usa el color de fondo, pero se puede cambiar la forma en que se rellenan los objetos usando la funci6n setfillstyle, cuyo prototipo es: void far setfillstyle(int MODELO, int COLOR);
Los valores para el parametro MODELO y sus equivalentes macro (definidos en graphics.h) se dan en la tabla 8.3.
8.4.3 Funciones para dibujar y rellenar poligonos drawpoly y fillpoly Como ya se indic6, se puede dibujar un poligono de forma particular, por medio de la funci6n drawpoly, cuyo prototipo es void far drawpoly(int NUMPUNTOS, int far *PUNTOS)
La funci6n drawpoly( ) dibuja un poligono usando el color de dibujo vigente. El parametro NUMPUNTOS especifica el numero de vertices del poligono. Cada vertice se especifica por medio de sus coordenadas X, Y, almacenadas en el arreglo de mimeros enteros apuntado por PUNTOS, con la coordenada X en primer lugar; por 10 cual, dicho arreglo tiene que ser al menos dos veces mayor que el numero de puntos. Por ejemplo, para definir el primer vertice como 10,20, se podria usar el siguiente c6digo: int PUNTOS[TAM]; PUNTOS[O] = 10; PUNTOS[l] = 20;
Donde TAM es el doble del numero de puntos del poligono.
Graficos
197
Se puede rellenar el poligono con el color de relleno y los modele de relleno aplicables usando fillpoly, cuyo prototipo es void far fillpoly(int NUMPUNTOSt int far *PUNTOS)
La funcion fillpoly rellena el objeto definido por el arreglo indicado por PUNTOS en el color de dibujo actual. El numero de vertices tiene que ser igual a NUMPUNTOS.
El programa del ejemplo 8.1 proporciona una muestra de como funcionan drawpoly y fillpoly. Dicho programa inicializa el arreglo de puntos con 500 vertices generados aleatoriamente, dibuja el poHgono definido por estos puntos y 10 rellena. El programa crea un poligono nuevo, cada vez que se ejecuta. Ejemplo 8.1: Programa "poligono.c".
/*
Uso de drawpoly( ) y de fillpoly( )
#include #include #include #include
#define MAX 200 maine ) { int CONTROLADOR, MODO; int POLIGONO [MAX]; int I; struct viewporttype INFO; CONTROLADOR = DETECf; initgraph(&CONTROLADOR, &MODO, "C:\\BORLANDC\ \BGI"); getviewsettings«struct viewporttype far*) &INFO); randomizeO; for(I=O; I
} setcolor(CYAN); drawpoly(MAX/2, POLIGONO);
*/
198
Fundamentos de Programacion en Lengl!aje C
setfilistyle(SOLID_FILL, LIGHTBLUE); filipoly(MAX/2, POLIGONO); getch( ); closegraph( ); return 0; }
8.4.4 Funciones para cambiar de ubicacion en la ventana de gnificos La biblioteca graphics.h contiene funciones para ubicarse en la ventana de graficos y desde ahi continuar la elaboracion de un grafico. Estas funciones son moveto y moverel. Tamben cuenta con funciones para dibujar una linea a partir de donde se quedo el dibujo previamente hasta otra posicion especifica, como son lineto y linerel. En el modo grafico no hay un cursor visible que indique la posicion en donde se encuentra el dibujo, pero la posicion actual de impresion en la pantalla se mantiene como si hubiera un cursor invisible. En el ambiente de Turbo C esta posicion se conoce como el CP 0 posicion actual de dibujo ("Current Position") y se modi fica por medio de las funciones moverel 0 moveto, cuyo prototipo es: void far moveto(int X, int V); void far moverel(int DX, int DY); moveto mueve la posicion actual al punto de coordenadas X,Y. Por su parte moverella mueve una distancia DX ert pixeles, la cual si es positiva indica moverse a la derecha y si es negativa, moverse a la izquierda; y una distancia DY, positiva hacia abajo y negativa hacia arriba. Con lineto y linerel se mueve la posicion actual, pero ademas se traza una linea entre la posicion previa y la nueva a la que se desplazo el CP. Los prototipos de estas funciones son: void far lineto(int X, int V); void far linerel(int DX, int DY);
8.4.5 Ejemplo Como otro ejemplo (8.2) de aplicacion, en esta sec cion se presenta el programa de computadora en lenguaje C del Trazador de Figuras Geometricas y Grafica-
Graficos
199
dor de Funciones, cuya especificacion en pseudocodigo se presento en la seccion 1.1. Ejemplo 8.2: Programa "figfln.c". /*
TRAZADOR DE FIGURAS GEOMETRlCAS Y GRAFlCADOR DE FUNCIONES
#include #include #include #include #·include #include
/*
para utilizar randomize
*/
/* para incluir valores maximos de flotantes */
int CONTROLADOR, MODO; struct viewporttype INFO; /* Estructura para crear una ventana gratica de texto * / struct PVTG { int VXI,VYI,VXF,VYF,BDCOLOR,FONDCOLOR,TXCOLOR,TIPOC,TAMC;
}; /* Inicializacion de las Ventanas usadas en el Programa */ struct struct struct struct struct
PVTG PVTG PVTG PVTG PVTG
=
PVTO {40, 10, 175, 175,WHITE,GREEN,WHITE,O}; PVTl = {180,300,450,400,WHITE,GREEN,WHITE,O}; PVT2 = {180,300,315,435,WHITE,GREEN,WHITE,O}; PVT3 = {330,260,600,360,WHITE,GREEN,WHITE,O}; PVT4 = {50,40,590,440, WHITE,GREEN, WHITE,O};
/* Funcion para crear ventanas'de color */ int VENTTXTG(struct PVTG *PV) { setcolor(PV- > BDCOLOR); setfillstyle(SOLID_FILL, PV-> BDCOLOR); rectangle( PV - > VXI + 5, PV- > VYI -5, PV- > VXF+ 5, PV- > VYF- 5); floodfill(PV->VXI+6,PV->VYI, PV->BDCOLOR); setcolor(PV-> FONDCOLOR); setfilistyle(SOLID_FILL,PV-> FONDCOLOR); rectangle(PV- > VXI,PV-> VYI,PV- > VXF, PV- > VYF); floodfill(PV->VXI+l,PV->VYI+l, PV->FONDCOLOR); setcolor(PV-> TXCOLOR); settextstyle(PV-> TIPOC,HORIZ_DIR,PV-> TAMC); }
*/
200
Fundamentos de Programacion en Lenguaje C
int MENUO
{ /* MENU presenta en la pantalla el menu con la lista de figuras geometricas que puede trazar el programa. MENU entrega a su salida un numero entero que representa la selecci6n realizada. */ int SELECCION; /*Crear una ventana gratica de texto con parametros de la estructura PVTO */ VENlTXTG(&PVTO); /* Escribir el MENU */ outtextxy(40,20,"* [1] LINEA dando el punto inicial y final"); outtextxy(40,40,"* [2] CIRCULO dando el centro y el radio"); outtextxy(40,60,"* [3] ELIPSE dados su centro y longitudes de los ejes"); outtextxy(40,80,"* [4] RECfANGULO dando dos vertices"); ll outtextxy(40,lOO,"* [5] POLIGONO dando numero de lados y vertices ); outtextxy(40,120,11* [6] CURVA dando una secuencia de puntos"); outtextxy(40,140,11* [7] FUNCION dada su ecuaci6n y el rango de valores de la var. "); outtextxy(40,160,"* [8] Salir");
/* Retornar la selecci6n * / return (getchO-'O');
} int LINEAO { int PIX, PlY, PFX, PFY; char BUFER[20]; /* Crea una ventana grafica de texto */ VENTTXTG(&PVT1); /* Obtener las coordenadas de los puntos inicial y final y hacer el eco de dichos datos */ outtextxy(190,320,"Coordenadas del Punto Inicial PI"); outtextxy(240,340, II PIX: "); cscanf("%dll,&PIX); sprintf(BUFER,"%d" ,PIX); outtextxy(275,340,BUFER); outtextxy(320,340, II PlY: "); cscanf("%dll,&PIY); sprintf(BUFER,"%d",PIY); outtextxy(355,340,BUFER); outtextxy(190,360,"Coordenadas del Punto Final PF"); outtextxy(240,380,"PFX: "); cscanf(l%d",&PFX); . sprintf(BUFER,"%d",PFX); outtextxy(275,380,BUFER); outtextxy(320,380,"PFY: "); cscanf("%d",&PFY); sprintf(BUFER,"%d",PFY); outtextxy(355,380,BUFER);
/* Borrar la pantalla* / cleardeviceO;
Graficos
201
/* Trazar la linea */ line(PlX, PlY, PFX, PFY); /* Hacer una pausa */ getchO; getcharO;
} int ClRCULOO { int CX, CY, R; char BUFER[20]; /* Crear una ventana de texto grafica * / VENTIXTG(&PVT1); /* Obtiene las coordenadas del centro y el radio y hace el eco de dichos datos outtextxy(190,320,"Coordenadas del Centro"); outtextxy(240,340,"CX: "); cscanf("%d",&CX); sprintf(BUFER,"%d",CX); outtextxy(275,340,BUFER); outtextxy(320,340,"CY: "); cscanf(l%d",&CY); sprintf(BUFER, II %d II ,CY); outtextxy(355,340,BUFER); outtextxy(190,360,"Radio del drculo"); outtextxy(240,380,"R: "); cscanf("%d ,&R); sprintf(BUFER,"%d",R); outtextxy(275,380,BUFER);
*/
ll
/* Borrar la pantalla*/ cleardeviceO;
/* Traza el
drculo * / circle(CX,CY,R);
/*
Hacer una pausa * / getchO; getcharO;
} int ELIPSEO { int AlNI = 0, AFIN = 360; int LEX, LEY, CX, CY; char BUFER[20];
/*
Crear una ventana de texto grafica VENTIXTG(&PVT1);
*/
/* Obtiene las coordenadas del centro y la longitud de los ejes mayor y menor */ outtextxy(190,320,"Coordenadas del Centro"); outtextxy(240,340,"CX: "); cscanf("%d",&CX);
202
Fundamentos de Programacion en Lenguaje C
sprintf(BUFER,"%d",CX); outtextxy(275,340,BUFER); outtextxy(320,340/'CY: "); cscanf("%d",&CY); sprintf(BUFER,"%d",CY); outtextxy(355,340,BUFER); outtextxy(190,360,"Longitud de los ejes"); outtextxy(240,380,"LEX: "); cscanf("%d",&LEX); sprintf(BUFER,"%d",LEX); outtextxy(275,380,BUFER); outtextxy(320,380,"LEY: "); cscanf("%d",&LEY); sprintf(BUFER,"%d",LEY); outtextxy(355,380,BUFER); /* Borrar la pantalla*/ cleardeviceO; /* Traza la elipse */ ellipse(CX,CY,AINI,AFIN,LEX/2,LEY/2);
/*
Hacer una pausa * / getchO; getcharO;
} int RECTANGULOO { int VIX, VIY, VFX, VFY; char BUFER[20]; /* Crea una ventana grMica de texto */ VENTTXTG(&PVT1); /* Obtiene las coordenadas de los vertices y hace el eco de dichos datos * / outtextxy(190,320,"Coordenadas del Vertice Inicial"); outtextxy(240,340,"VIX: "); cscanf("%d",&VIX); sprintf(BUFER,"%d",VIX); outtextxy(275,340,BUFER); outtextxy(320,340,"VIY: "); cscanf("%d",&VIY); sprintf(BUFER,"%d",VIY); outtextxy(355,340,BUFER); outtextxy(190,360,"Coordenadas del Vertice Final"); outtextxy(240,380,"VFX: "); cscanf("%d",&VFX); sprintf(BUFER,"%d",VFX); outtextxy(275,380,BUFER); outtextxy(320,380,"VFY: "); cscanf("%d",&VFY); sprintf(BUFER,"%d",VFY); outtextxy(355,380,BUFER); /* Borrar la pantalla*/ cleardeviceO; /* Traza el rectangulo * / rectangle(VIX, VIY, VFX, VFY); /* Hacer una pausa * / getchO; getcharO;
}
Graficos
203
int POLIGONOO
{ int NP, AUX, TAM, PPOLIGONO[40]; char BUFER[20]; void *BUFFG; /* Crea una ventana grafica de texto */ VENTIXTG(&PVT1); /* Obtiene el numero y las coordenadas de los puntos y hace el eco de dichos datos */ outtextxy(190,320,"Numero de puntos del polfgono"); outtextxy(240,340,"NP: "); cscanf("%d",&NP); sprintf(BUFER,"%d",NP); outtextxy(275,340,BUFER); outtextxy(190,360,"Coordenadas del Vertice"); TAM = imagesize(240,380,380,390); BUFFG = malloc(TAM); getimage(240,380,380,390,BUFFG); for(AUX=O; AUX<2*NP; AUX++) { outtextxy(240,380,"X: "); cscanf("%d",&(PPOLIGONO[AUXD); sprintf(BUFER,l%d",PPOLIGONO[AUX++ ]); outtextxy(275,380,BUFER); outtextxy(320,380,"Y: fI); cscanf("%d",&(PPOLIGONO[AUX]»; sprintf(BUFER,"%d",PPOLIGONO[AUX]); outtextxy(355,380,BUFER); putimage(240,380,BUFFG,COPY_PUT); } /* Borrar la pantalla*/ cleardeviceO; /* Traza el rectangulo * / drawpoly(NP,PPOLIGONO); /* Hacer una pausa * / getchO; getcharO;
} int CURVAO { /* Obtiene el numero y las coordenadas de los puntos * / int NP, AUX, TAM, PPUNTOS[40]; char BUFER[20]; void *BUFFG; /* Crea una ventana grafica de texto * / VENTIXTG(&PVT1) ; /* Obtiene el numero y las coordenadas de los puntos y hace el eco de dichos
204
Fundamentos de Programaci6n en Lenguaje C
datos */ outtextxy(190,320,"Numero de puntos de la curva"); outtextxy(240,340,"NP: "); cscanf("%d",&NP); sprintf(BUFER,"%d",NP); outtextxy(275,340,BUFER); outtextxy(190,360,"Coordenadas del Punto"); TAM = imagesize(240,380,380,390); BUFFG = malloc(TAM); getimage(240,380,380,390,BUFFG); for(AUX=O; AUX<2*NP; AUX++)
{ outtextxy(240,380,"X: "); cscanf("%d",&(PPUNTOS[AUX]»; sprintf(BUFER, II %d " ,PPUNTOS[AUX+ + ]); outtextxy(275,380,BUFER); outtextxy(320,380,"Y: "); cscanf("%d",&(PPUNTOS[AUX]»; sprintf(BUFER,"%d" ,PPUNTOS[AUXD; outtextxy(355,380, BUFER); getcharO; putimage(240,380,BUFFG,COPY_PUT); } /* Borrar la pantalla*/ cleardeviceO; /* Traza la curva .*/ moveto(PPUNTOS[O],PPUNTOS[l]); for(AUX=l; AUX
} int FUNCIONMATO
{ int SEL,CONT; unsigned COOX=PVT4.VXI,COOY=PVT4.VYI,RANGO=1; char BUFER[20]; float VXMIN, VXMAX, VYMAX, VYMIN, VYINI, VX, VY; /* Crear una ventana grMica de texto * / VENTIXTG(&PVT2); /* Presenta el menu de funciones y pedir la selecci6n */ outtextxy(185,305,"* [1] sin(x) "); outtextxy(185,325,"* [2] cos(x) "); outtextxy(185,345,"* [3] tan(x) "); outtextxy(185,365,"* [4] log (x) "); outtextxy(185,385,"* [5] loglO(x),,); outtextxy(185,405,"* [6] exp(x) "); outtextxy(185,425,"* [7] pow(2,x)");
Graficos
/* Obtener selecci6n * / SEL = getchO-'O'; /* Obtener el rango de valores de la variable independiente */ VENTTXTG(&PVT3); outtextxy(340,290,"Rango de valores del Argumento"); outtextxy(360,310,"VXMIN: "); cscanf(tl%f',&VXMIN); sprintf(BUFER,"%4.1f',VXMIN); Quttextxy(395,310,BUFER); outtextxy( 440,310,"VXMAX: "); cscanf("%f',&VXMAX); sprintf(BUFER,"%4.1f',VXMAX); outtextxy( 480,310,BUFER); /* Borrar la pantalla*/ cleardeviceO; /* Obtener los valores limite de los para metros de la funci6n * / if (VXMAX10) VYINI = 10; else if (VYINI < -10) VYINI = -10; break;} case 4:{if «VXMIN > O)&&(VXMAX>O)) {VYMAX = 10g(VXMAX); VYMIN = 10g(VXMIN); VYINI = 10g(VXMIN);} else RAN GO = 0; break;} case 5:{if «VXMIN > O)&&(VXMAX>O)) {VYMAX = 10glO(VXMAX); WMIN = 10glO(VXMIN); VYINI = log10(VXMIN);} else RANGO = 0; break;} case 6:{VYMAX = exp(VXMAX); VYMIN = exp(VXMIN); VYINI = exp(VXMIN);break;} case 7:{VYMAX = pow(VXMAX,2); VYMIN 0; VYINI = pow(VXMIN,2);} }
=
/* Presentar el marco */ / / Presentar ventana VE NTTXTG (&PVT4); if (!RANGO) {outtextxy(200,300,"RANGO CON VALORES NO VALIDOS");} else {// Presentar marco cudriculado setlinestyle(DOTTED_LINE,O,NORM_WIDTH); for (CONT 1; COOX < PVT4.VXF; CONT ++)
205
206
Fundamentos de Programacion en Lenguaje C
{COOX = Pvr4.VXI + 20*CONT;line(COOX,Pvr4.VYI,COOX,Pvr4.VYF);} for (CONT = 1; COOY < Pvr4.VYF; CONT++) {COOY = Pvr4.VYI + 20*CONT;line(pvr4.VXI,COOY,Pvr4.VXF,COOY);} /* Trazar la curva correspondiente */ / / Coordenadas del punto inicial coox = Pvr4.VXI; COOY = Pvr4.VYF (int)«pvr4.VYF-Pvr4.VYI)*«VYINI-VYMIN)/(VYMAX-VYMIN))); /1 Mover el CP al punto inicial moveto( COOX,COOY); IIGraficar setlinestyle(SOLID_LIN E,O,THICK_WIDTH); for (COOX = Pvr4.VXI; COOX <= Pvr4.VXF; COOX++) { /I Calculo del argumento de la funci6n VX = «VXMAX-VXMIN)/(pvr4.VXF-Pvr4.VXI))*(COOX-PVT4.VXI) +VXMIN; switch (SEL)
{ case l:{VY = sin(VX); break;} case 2: {VY = cos(VX); break;} case 3:{VY = tan(VX); if (VY > 10) VY= 10; else if (VY < -10) VY = -10; break;} case 4:{VY = log(VX); break;} case 5:{VY = log10(VX); break;} case 6:{VY = exp(VX);break;} case 7:{VY = pow(VX,2);} } COOY = Pvr4.VYF(int)«pvr4.VYF-Pvr4.VYI)*«VY-VYMIN)/(VYMAX-VYMIN))); Iineto(COOX,COOY); } setlinestyle(SOLID_LINE,O,NORM_WIDTH);
}
1*
Hacer una pausa getchO; getcharO;
} int mainO { int SALIR = 0;
*1
Graficos
207
CONTROLADOR = DETECT; initgraph(&CONTROLADOR, &MODO, "C:\\BORLANDC\\BGI"); getviewsettings«struct viewporttype far*) &INFO); while (!SALIR) { /*Borrar la pantalla*/ cleardeviceO; /* lIenar pantalla con relleno* / setcolor(GREEN); setfilistyle(WIDE_DOT_FILL,GREEN); rectangle(O,O,INFO.right,INFO.bottom); f1oodfill(5, 5, GREEN); switch (MENUO) /* Presenta el MENU PRINCIPAL */ { /* Ejecuta la funci6n correspondiente al MENU */ case l:{LINEAO;break;} case 2:{CIRCULOO;break;} case 3:{ELIPSEO;break;} case 4:{RECTANGULOO;break;} case 5:{POLIGONOO;break;} case 6:{CURVAO;break;} case 7:{FUNCIONMATO;break;} case 8: SALIR = 1;
} cleardeviceO; }
}
8.5
FUNCIONES GRAFICAS
AVANZADAS
8.5.1 Gnificas de pastel Una de las funciones avanzadas de la biblioteca de graficas de Turbo C es pieslice, que dibuja en la pantalla del monitor un sector de una gnifico de pastel en el color y forma de relleno vigentes; su prototipo es void far pieslice(int X, int Y, int AINICIO, int AFIN, int RADIO);
En este caso, X e Y especifican el centro de un circulo imaginario del que se corta un trozo de pastel, en pixeles. El principio y el final del arco del sector estan definidos por AINICIO y AFIN, los cuales se especifican en grados, quedando
208
Fundamentos de Programacion en Lenguaje C
el angulo de cero grados en la horizontal derecha. El radio del sector se especifica, en pixeles, por medio del parametro RADIO. El programa del ejemplo 8.3. ilustra el uso de la funci6n pieslice. EI prograrna pide al usuario el numero de partes del pastel, y el tamafio relativo de cada parte dado como un numero entero. Acepta un maximo de 36 partes. Ca1cula un factor de normalizaci6n que se usa para transformar los datos de entrada en un arco de circulo proporcional. Finalmente se dibuja la grafica de pastel centrada en la pantalla, ca1culado el principio y el final de cada arco con base en los datos nonnalizados. EI programa funciona con cualquier tipo de tarjeta de video usual. Ejemplo 8.3: Programa "pastel.c". /*
Construye grMicas de pastel a partir de los datos dados por el usuario
#include#include #include #include #define MAX 36 maine )
{ int MANEJADOR= DETECf, MODO, MAXCOLOR; int PARTE[MAX], CONTADOR, I, TAMT, COLOR, PRINCIPIO, FIN; float FACfORN; struct viewporttype INFO; initgraph(&MANEJADOR, &MODO, "C:\\TC\\BGI"); getviewsettings«struct viewporttype far*) &INFO); MAXCOLOR = getmaxcolor( ); /* Entrada del Numero y Tamafio de los Trozos de Pastel * / do {printf(""Cuantos partes tiene el PASTEL (1-36) 7: "); scanfC'%d" ,&CONTADOR);} while(CONTADOR36); for(I=O;I
{
*/
Graficos
209
setfillstyle(SOLID_FILL, COLOR); /* calculo del punto final * / FIN = FIN + (int) PARTE[I]*FACTORN; /* si es el ultimo segmento, se asegura de que el ultimo radio es en el grado 360 */ if ((I==CONTADOR-l) && FIN!=360) FIN =360; pieslice(INFO.right/2, INFO.bottom/2, PRINCIPIO, FIN, INFO.bottom/4); PRINCIPIO = FIN; if(I%2) FIN++;/* se promedian los efectos de truncado */ /* se reciclan los colores si es necesario*/ if (COLOR == MAXCOLOR) COLOR = 1; } getch(); closegraph(); return 0;
}
8.5.2 Creaci6n de gnificas de barra Otra funcion de gnificacion de Turbo C dibuja barras que pueden ser usadas para crear gnificas de barra. Esta funcion es Hamada bar y su prototipo es void far bar(int IZQ, int CIMA, int DER, int FONDO);
La funcion bar dibuja una barra rectangular que tiene su extremo superior izquierdo definido por IZQ, CIMA y su extremo inferior derecho por DER, FONDa. La barra se rellena con el modele y color vigentes. EI programa del ejemplo 8.4. ilustra el uso de esta funcion. Primero solicita al usuario el numero de entradas y despues la informacion y calcula un factor de normalizacion. Por ultimo, visualiza una representacion en grafica de barras de los datos introducidos, usando barras verticales. EI factor de normalizacion tiene dos objetivos: i) asegurar que la barra mas grande cabe en la pantalla, y b) asegurar que se use la altura de toda la pantalla. La funcion bar calcula el factor de normalizacion encontrando la barra de mayor valor y dividiendo la altura de la pantalla entre ese valor. De esta manera los datos con valores mayores que el tamafio de la pantalla en pixeles son automaticamente escalados. Las relaciones entre los datos se mantienen iguales, unicamente cambia la escala. El programa calcula tambien automaticamente la anchura y el espaciamiento entre las barras.
210
Fundamentos de Programaci6n en Lenguaje C
Ejemplo 8.4: Programa "barrasv.c". /* Construye diagramas de barras a partir de datos introducidos por el usuario */ #include #include #include #include
#define MAX 100 maine ) { int CONTROLADOR, MODO, MAXCOLOR; int TBARRAS[MAX], BARRAS, I, TAMM, COLOR; float FACTORN; struct viewporttype INFO; int VIZQ, VCIM, VDER, VABA; int ESPACIADO; CONTROLADOR = DETECT; initgraph(&CONTROLADOR, &MODO, "C:\\TC\\BGI"); getviewsettings«struct viewporttype far*) &INFO); MAXCOLOR = getmaxcolor( ); VIZQ = INFO.left + 200; VCIM = INFO.top + 200; VDER = INFO.right - 200; VABA = INFO.bottom - 200; do {printf(""Cuantas barras se van a graficar (1-29) ? : "); scanf("%d" ,&BARRAS);} while(BARRAS<1 && BARRAS>29); for(I=O; ITAMM) TAMM=TBARRAS[I]; FACTORN = (float)(VABA-VCIM)/(float)TAMM; /* Determinacion de la distancia entre barras */ ESPACIADO = (VDER-VIZQ)/BARRAS; setfilistyle(SOLID_FILL, BLACK); rectangle(O, 0, INFO.right, INFO.bottom); floodfill(11, 11, WHITE); for(I=O, COLOR=1; I
{
Graficos
211
setfilistyle(SOLID_FILL, COLOR); bar(VIZQ+I*ESPACIADO, VABA - (int)FACTORN*TBARRAS[I], VIZQ+(I+l)*(ESPACIADO), VABA); /* se reciclan los colores si es necesario * / if(COLOR == MAXCOLOR) COLOR = 1; } getch( ); closegraph( ); return 0;
}
Se pueden hacer graticas de barra tridimensionales usando bar3d, que tiene el prototipo void far bar3d (int IZQ, int CIMA, int DER, int FOND, int PROFU, int INDICSUP);
La funci6n bar3d( ) es igual que la bar excepto que produce una barra tridimensional de PROFU pixeles. La barra es realizada en el color de dibujo vigente. Si se quiere una barra de dos dimensiQnes, se usa bar3d con una profundidad de cero. Si INDICSUP no es cero, se afiadinl. una cima a la barra; si es cero, la barra no tendnl cima. La profundidad que debe tener una barra es algo subjetivo. EI manual de Turbo C recomienda que una barra tridimensional tenga una profundidad igual a un cuarto de su anchura. Usando esta regIa, se ha sustituido la Hamada a bar en el programa del ejemplo 8.4 por la Hamada a bar3d, para obtener el programa del ejemplo 8.5. Ejemplo 8.5: Programa "tribarra.c". /* Construye diagramas de barras tridimensionales a partir de los datos dados por~ usuario */ #include#include #include #include #define MAX 30 main( ) { int CONTROLADOR, MODO, MAXCOLOR; int TBARRAS[MAX], BARRAS, I, TAMM, COLOR; float FACTORN; struct viewporttype INFO; int ESPACIADO;
212
Fundamentos de Programacion en Lenguaje C
CONTROLADOR = DETECT; initgraph(&CONTROLADOR, &MODO, "C:\\TC\\BGI"); getviewsettings((struct viewporttype far*) &IN FO); MAXCOLOR = getmaxcolor( ); do {printf(""Cuantas barras se van a graficar (1-29) ? : "); scanf("%d",&BARRAS);} while(BARRAS29); for(I=O; I TAMM) TAMM=TBARRAS[I]; FACTORN = (float) INFO.bottom/(f1oat)TAMM; /* Determinaci¢n de la distancia entre barras */ ESPACIADO = INFO.right/BARRAS; for(I=O, COLOR=1; I
{ setfilistyle(SOLID_FILL, COLOR); bar3d(I*ESPACIADO, INFO.bottom - (int) FACTORN*TBARRAS[I], (I+1)*ESPACIADO, INFO.bottom, (ESPACIADO /4),1); /* se reciclan los colores si es necesario */ if(COLOR == MAXCOLOR) COLOR = 1;
} getch( ); closegraph( ); return 0;
}
8.5.3 Procesamiento de graficos en pantalla. Uso de getimage
y putimage
La funcion getimage se usa para copiar una region de la ventana de gnificos en una memoria intermedia. La funcion putimage pone el contenido de una memoria intermedia en la pantalla. Las funciones tienen los siguientes prototipos. void far getimage(int IZQ, int CIMA, int DER, int FONDO, void far *BUF); void far putimage(int IZQ, int CIMA, void far *BUF, int OPER);
Graficos
213
La funcion getimage copia el contenido de una porcion rectangular de la pantalla, definida por sus coordenadas superior izquierda e inferior derecha, en la memoria intennedia apuntada por buf. putimage se usa para visualizar la memoria intennedia de datos graficos almacenados previamente con getimage. Es necesario especificar las coordenadas de la esquina superior izquierda del area donde se qui ere que sea presentada la imagen. El valor de OPER detennina el modo en el que se dibuja la imagen sobre el contenido de la pantalla. Los modos validos enumerados en graphics.h se muestran en la tabla 8.4.
Tabla 8.4: Modos de dibujo. Nombre
COpy PUT XOR PUT OR PUT AND PUT NOT PUT
Valor
0 1 2
3 4
Significado
Sobreescribir en el destino OR-exclusivo con el destine OR-inclusivo con el destino AND con el destino Invertir la imagen fuente
El tamafio de la memoria intennedia, en bytes, para una region dada, se detennina por medio de la funcion imagesize. Se debe usar esta funcion y no intentar calcular manualmente el espacio necesario, ya que imagesize da el valor correcto con independencia del modo de video que este en uso. Su prototipo es unsigned far imagesize(int IZQ, int CIMA, int DER, int FONDO);
El programa del ejemplo 8.6 muestra las funciones getimage, imagesize y putimage. El programa lIena de amarillo toda la pantalla; crea un cuadrado, 10 rellena de blanco y Ie dibuja dos lineas diagonales y dos lineas bisectoras; despues copia el cuadrado usando, primero el modo COPY_PUT, despues el modo XOR_PUT, el modo NOT_PUT, el modo AND_PUT y el modo OR_PUT, en otra parte de la pantalla. Para progresar en la ejecucion del programa se oprime cualquier tecla. Ejemplo 8.6: Programa "grafmov.c".
/* Este programa muestra como se puede mover una imagen grafica utilizando getimage, imagesize y putimage. OPRIMA CUALQUIER TECLA PARA AVANZAR EL PROGRAMA */ C rOC
214
Fundamentos de Programaci6n en Lenguaje C
#include#include #include maine ) { int CONTROLADOR, MODO; int TAM; void *BUF; struct viewporttype INFO; clrscrO; printf("OPRIMA CUALQUIER TECLA PARA AVANZAR EL DIBUJO\n"); getchO; CONTROLADOR = DETECT; initgraph(&CONTROLADOR, &MODO, "C:\\TC\\BGI"); getviewsettings«struct viewporttype far *) &INFO); setfiIIstyle(SOLID_FILL, YELLOW); rectangle(10, 10, INFO.right, INFO.bottom); floodfill(11, 11, YELLOW); getchO; setcolor(BLACK); rectangle(20, 20, 100, 100); getchO; /* crear un recuadro */ setfilistyle(SOLID_FILL, WHITE); floodfill(21, 21, BLACK); getchO; setcolor (BLACK); line(20, 20, 100, 100); line(20, 100, 100, 20); line(20, 60, 100, 60); line(60, 20, 60, 100); getch( ); /* DESPLAZAR LA IMAGEN * / /* primero se toma el tama±o de la imagen */ TAM = imagesize(20, 20, INFO.right/4, INFO.bottom/4); if(TAM != -1) {/* se asigna memoria para la imagen */ BUF = malloc(TAM); if(BUF){ getimage(20, 20, 100, 100, BUF); putimage(INFO.right/3, INFO.bottom/3, BUF, COPY_PUT); getchO; putimage«INFO.right/4)*3, (INFO.bottom/4)*3, BUF, XOR_PUT); getchO; putimage(20, (INFO.bottom/4)*3, BUF, NOT_PUT); getchO; putimage«INFO.bottom/4)*3, 20, BUF, AND_PUT); getchO; putimage«int)«INFO.bottom/4)*3.8), 20, BUF, OR_PUT);
Graficos
215
} }
geteh( ); closegraph( ); return 0; }
En el ejemplo 8.6 se usa malloe para obtener a tiempo de corrida, el segmento de memoria necesario para almacenar el area grafica. malloe retoma un apuntador al area de memoria obtenida, el cual se guarda en BUF, luego se Ie pas a como parametro este apuntador a getimage para que se almacene ahi la grafica requerida.
8.6 SALIDA DE TEXTO EN MODO GRAFICO 8.6. 1 U so de outtext y outtextxy Aunque las funciones de entrada/salida estandar de texto en lenguaje C, tales como printf~ pueden usarse en la mayoria de los modos graficos, no son la alternativa mas adecuada. Por ejemplo, para aprovechar al maximo el entomo grafico de Turbo C es conveniente usar las funciones de salida de texto en modo grafico descritas en esta seccion. La funcion grafica que presenta texto en una ventana grafica es outtext. Su prototipo es void far outtext(char *CAD); Esta funcion escribe la cad~na apuntada por CAD en la posicion predefinida con la funcion moverel 0 moveto~ 0 bien a partir de donde se encuentra el CP. Como ya se indico en la seccion 8.4.4, en el modo grafico no hay cursor visible, pero la posicion actual de impresion en la pantalla se mantiene como si hubiera un cursor invisible, mediante 10 que se conoce como el CP (posicion actual) y se modifica por medio de las funciones moverel 0 moveto, cuyo uso se ilustra en el programa del ejemplo 8.7. Las principales ventajas de usar outtext son sus posibilidades de presentar el texto en diferentes tipos de caracteres ("Fonts"), tamafios 0 direcciones y cortar la salida que desbordaria la ventana. Por el contrario, printf no puede cortar la salida y solo imprime en el formato estandar. Para cambiar el estilo, tamafio 0 direccion de impresion de la salida de texto lograda con outtext, se usa settextstyle, cuyo prototipo es void far settextstyle(int TIPO, int DIRECCION, int TAMANOCAR);
216
Fundamentos de Programacion en Lenguaje C
El panimetro TIPO determina el conjunto de caracteres a usaf. Por omision, se usa el tipo de mapas de 8 x 8 bits. Se puede dar a TIPO uno de los valores de la Tabla 8.5. (Las macros se definen en graphics.h) La direccion en que se visualiza el texto, izquierda a derecha 0 de abajo arriba, se determina por medio del valor de DIRECCION que puede ser, 0 bien HORIZ_DIR (0) 0 VERT_DIR (1).
Tabla 8.5: Tipos de conjuntos canicter ("fonts"). Tipo de "font" (Macro)
DEFAULT FONT TRIPLEX FONT SMALL FONT SANS- SERIF- FONT GOTHIC FONT SCRIPT FONT SIMPLEX FONT TRIPLEX- SCR- FONT COMPLEX FONT EUROPEAN FONT BOLD FONT
Valor
0 1 2 3
4 5 6 7 8 9 10
Significado
1* Tipo de 8x8 de mapas de bits *1 1* Tipo Triplex *1 1* Tipo Small *1 1* Tipo Sans Serif *1 1* Tipo Gotico *1 1* Tipo Script *1 1* Tipo Simplex *1 1* Tipo Triplex SCR *I 1* Tipo Complex *1 1* Tipo Europeo *1 1* Tipo Bold *1
El panimetro T AMANOCAR es un multiplicador que aumenta el tamafio del canicter. Puede tener un valor de 0 a 10. El programa del ejempl0 8.7 muestra el uso de la funci6n settextstyle( ) y de los distintos juegos de caracteres en varios tamafios. Ejemplo 8.7: Programa "textog.c". /*Demostracion del uso de distintos tipos y tamanos de letra */ #inc\ude#inc\ude #inc\ude maine ) { int CONTROLADOR, MODO; CONTROLADOR = DETECT; /* autodeteccion */ initgraph(&CONTROLADOR, &MODO, "C:\\TC\\BGI"); /* Letra Normal */ moveto(20,20);
Graficos
outtext ("FRMG con Letra Normal"); getch( ); /* Letra pequena, tamano 1 */ moveto(20,40); settextstyl e(SMALL_FO NT, HORIZ_DIR, 1); outtext ("FRMG con Letra Pequena Tamano 1"); getch( );
/*
Letra Gatica, tamano cinco veces mayor que el normal */ moveto(20,60); settextstyle(GOTHIC_FONT, HORIZ_DIR, 5); outtext("FRMG con Gatica 5"); getch( );
/*
Letra Triplex, tamano cinco veces mayor que el normal */ moveto(20, 100); settextstyle(TRIPLEX_FONT, HORIZ_DIR,5); outtext("FRMG con Triplex 5"); getch( );
/*Letra Sans Serif, tamano cinco veces mayor que el normal */ moveto(20, 150); settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 5); outtext("FRMG con Sans Serif 5"); getch( ); /*Letra Script, tamano cinco veces mayor que el normal */ moveto(20, 200); settextstyle(SCRIPT_FONT, HORIZ_DIR, 5); outtextC'FRMG con Script 5"); getch( ); /*Letra Simplex tamano cinco veces mayor que el normal moveto(20, 250); settextstyle(SIMPLEX_FONT, HORIZ_DIR, 5); outtext("FRMG con Simplex 5"); getch( );
*/
/*Letra Triplex SCR tamano cinco veces mayor que el normal */ moveto(20, 300); settextstyle(TRIPLEX_SCR_FONT, HORIZ_DIR, 5); outtext("FRMG con Triplex SCR 5"); getch( ); /*Letra Complex tamano cinco veces mayor que el normal */ moveto(20, 350); settextstyle(COIv.PLEX_FONT, HORIZ_DIR, 5);
217
21 8
Fundamentos de Programaci6n en Lenguaje C
QuttextC'FRMG con Complex 5"); getch( ); /*Letra European tamano cuatro veces mayor que el normal moveto(20, 400); settextstyle(EUROPEAN_FONT, HORIZ_DIR, 4); outtext("FRMG con European 4"); getch( ); /*Letra Bold tamano tres veces mayor que el normal moveto(200; 20); settextstyle(BOLD_FONT, HORIZ_DIR, 3); outtext("FRMG con Bold 3"); getch( );
*/
*/
closegraph( ); return 0; }
Cuando se cambia e1 tamafio del tipo de letra (font), en lugar de los mapas de 8x8 bits, las dimensiones del nuevo tipo son multiplicadas por el valor de TAMANOCAR. Es decir, si T AMANOCAR es 2, los caracteres asignados a los bits senin dibujados en un cuadrado de 16x16 pixels. Sin embargo, el tipo de caracteres tec1eados esta espaciado proporcionalmente, 10 que qui ere decir que la i, por ejemplo, sera mas estrecha que la m. Para predefinir la altura y anchura de un cankter del tipo tec1eado, se usan las funciones textheight y textwidth, respectivamente, las cuales tienen los prototipos: int far textheight(char far *CAD); int far textwidth( char far *cad); Estas funciones proporcionan la altura y anchura, en pixels, de las cadenas indicadas por el apuntador a cadena CAD. Otro importante detalle a saber acerca de outtext es que no procesa caracteres de nueva linea. No hay modo de hacer que realice un retorno de carro 0 un salto de linea. Para poner un texto en una posicion especifica de la ventana, se usa la funcion outtextxy, cuyo prototipo es void far outtextxy(int X, int Y, char *CAD); La cadena CAD sera impresa en el punto con las coordenadas especificas dentro de la ventana de trabajo. Si X, Y 0 ambas estan fuera de rango, no se visualizaran la salida.
Graficos
219
En el ejemplo 8.8 se usa la funci6n outtextxy para crear una pancarta que presente un mensaje movil con letras grandes en la pantalla. EI mensaje se traslada de derecha a izquierda hasta que se pulse una tecla. Este programa utiliza el tipo de caracteres asignado a bits. El tamano de cada canicter es nueve veces mayor que su estandar; por consiguiente, la altura y anchura de cada uno es 9x 8, 0 72, pixels. Ejemplo 8.8: Programa "pancarta.c".
1*
Pancarta desplaza un mensaje de derecha a izquierda utilizando letras en tamano grande *1 #include#include #include #include #include #define MAX 1024 maine )
{ int CONTROLADOR, MODO; int ALTO, ANCHO; char *APCAD, CAR[2], MENSAJE[MAX]; struct viewporttype INFO; intTAM; long TEMP; void *BUF, *BUFBORRAR; printf("Escriba su mensaje de menos de a/ad caracteres:\n",MAX); gets(MENSAJE); CONTROLADOR = DETECT; II autodeteccion initgraph(&CONTROLADOR, &MODO f "C: \ \ TC\\BGI"); getviewsettings((struct viewporttype far *) &INFO); settextstyle(DEFAULT_FONT, HORIZ_DIR, 9); ALTO = 9*8; Ilia letra normal tiene 8 pixels de alto ANCHO = 9*8; II la letra normal tiene 8 pixels de ancho
1*
Se obtiene una area de memoria para almacenar la parte de la pantalla que contiene el mensaje, menos el espacio del caracter de la izquierda. *1 TAM = imagesize(ANCHO, (INFO.bottom/2)-(ALTO/2), INFO.right, (INFO.bottom/2)+(ALTO/2»;
220
Fundamentos de Programaci6n en Lenguaje C
BUF = (void *) malloc(TAM); if(!BUF) { ll printf("error de asignacion de memoria ) ; exit(l);
} /* Se obtiene el area de memoria que se va autilizar para borrar la letra de la derecha en cuya posicion se va a escribir el caracter subsiguiente. */ TAM = imagesize(O, 0, ANCHO, ALTO); BUFBORRAR = malloc(TAM); if(!BUFBORRAR) { printf("error de asignacion de memoria"); exit(l); } /* Se toma un segmento de pantalla en blanco del tamaiio de un caracter */ getimage(O, 0, ANCHO, ALTO, BUFBORRAR); APCAD = MENSAJE; CAR[l] = '\0'; setcolor(LIGHTMAGENTA); //Pone el color del texto while(!kbhitO) //Mientras no se oprima una tecla
{
/* desplaza el texto existente hacia la izquierda */ getimage(ANCHO, (INFO.bottom/2)-(ALTO/2), INFO.right, (INFO.bottom/2)+(ALTO/2), BUF); putimage(O, (INFO.bottom/2)-(ALTO/2), BUF, COPY_PUT); /* toma el caracter siguiente * / *CAR = *APCAD; /* borrar la posicion del caracter nuevo * / putimage(INFO.right-ANCHO, (INFO.bottom/2)-(ALTO/2), BUFBORRAR, COPY_PUT); /* Imprime el siguiente caracter */ outtextxy(INFO.right-ANCHO, (INFO.battom/2)-(ALTO/2), (char far *) CAR); APCAD++; for (TEMP=O; TEMP < 450000; TEMP++ );/ jTemparizacion para el efecta de mav. / / vuelve al principia del mensaje if(! *APCAD) APC = MENSAJE; } getch( ); closegraph( ); return 0; }
Graficos
8.7
221
OTRAS FUNCIONES GRAFICAS
8.7.1 Cambio del estilo de linea Por omision, las lineas que se dibujan en Turbo C son solidas, pero se puede especificar otro tipo de linea, como puede ser punteada, a rayas, con puntos y rayas 0 personalizadas. Para cambiar la forma en que se dibujan las lineas se utiliza setlinestyle( ), cuyo prototipo es void far setlinestyle(int ESTILO, unsigned MODELO, int ESPESOR);
El panlmetro ESTILO define el estilo de la linea. Debe ser uno de los valores enumerados en la tabla 8.6, los cuales estan definidos como macros en la biblioteca graphics.h.
Tabla 8.6: Estilos de linea. Valor
Significado
SOLID_LINE DOTTED LINE CENTER LINE DASHED LINE USERBIT LINE
Linea continua Linea punteada Linea de punto y ray a (raya-punto-raya) Linea a rayas Linea definida por el usuario
Si ESTILO es igual a USERBIT_LINE, el modelo de 16 bits de "modelo" determina como se grafica la linea. Cada bit de la constante modelo corresponde a un pixel. Si se pone a 1 ese bit, el pixel se activa; en caso contrario se desactiva. El parametro ESPESOR debe tener uno de los valores indicados en la tabla 8.7. Tabla 8.7: Espesores de linea. Valor
Significado
NORM WIDTH THICK WIDTH
Anchura de 1 pixel Anchura de 3 pixels
El programa del ejemplo 8.8 grafica lineas con los diferentes tipos de estilos disponibles en Turbo C.
222
Fundamentos de Programacion en Lenguaje C
Ejemplo 8.9: Programa "lineas.c".
/*
Demostracion de estilos de linea
*/
#include#include #include maine ) { int CONTROLADORr MODO; struct viewporttype INFO; CONTROLADOR = DETECT; initgraph(&CONTROLADOR, &MODO I "c: \ \borlandc\\bgi"); getviewsettings«struct viewporttype far *) &INFO); /* por omision linea continua */ line(O, 0, INFO.right, 0); getch( ); setlinestyle(DOTIED_LINE, 0, NORM_WIDTH); line(O, 10, INFO.right, 10); getch( ); setlinestyle(CENTER_LINE, 0, NORM_WIDTH); line(O, 20, INFO.right, 20); getch( ); setlinestyle(DASHED_LINE, 0, NORM_WIDTH); line(O, 30, INFO.right, 30); getch( ); setlinestyle(USERBIT_LINE, 146, THICK_WIDTH); line(O, 40, INFO. right, 40); getch( ); closegraph( ); return 0;
}
8.7.2 Creaci6n de modelos de relleno diferentes Turbo C proporciona varios modelos de relleno, no obstante, puede ser Util definir otros modelos de relleno. Para crear modelos de relleno se usa setfillpattern, cuyo prototipo es void far setfrllpattern(char far *MODELO, int COLOR);
Graficos
223
La funcion setfillpattern( ) define un modelo de relleno que luego puede ser usado por funciones, como fJoodfill( ), de acuerdo a 10 indicado en el modelo. El modelo se encuentra en un arreglo de caracteres apuntado por MODELO, este tiene que tener una longitud de 8 bytes por 10 menos. EI color especificado por el parametro entero COLOR se ve en la pantalla cuando esta activado el bit del pixel correspondiente; de 10 contrario, se presenta el pixel con el color de fondo. La manera mas faci! de disefiar un modelo de relleno es dibujando una matriz de 8x8 en una hoja de papel cuadriculado, coloreando los cuadros adecuados. Se empieza por la fila superior, codificando cada cuadro en blanco como un 0 y cada cuadro coloreado como un 1. Se convierte el valor binario de cada fila en un numero decimal 0 hexadecimal y se asignan los valores a los bytes del arreglo apuntado por MODELO, empezando por la fila superior. Otro modo de crear un modelo de rellenado es generandolo con el generador de numeros aleatorios. El programa del ejemplo 8.10 produce un nuevo modelo de relleno cada vez que se ejecuta, debido a que la funcion randomize( ) hace que rand( ) proporcione una nueva secuencia aleatoria cada vez que se ejecuta el programa. La salida de rand( ) se convierte en un entero en el rango de 0 a 255, al aplicarle la operacion modulo. Ejemplo 8.10: Programa "relleno.c".
/*
*/
Creaci6n de modelos de relleno aleatorizados
#include #include #include #include
/*
para utilizar randomize
*/
maine )
{ int CONTROLADOR, MODO; struct viewporttype INFO; char BUF_RELLENAR[8]; int I; CONTROLADOR = DETECT; initgraph(&CONTROLADOR, &MODO, "C: \ \BORLANDC\\BGI"); getviewsettings«struct viewporttype far *) &INFO); outtextxy(O,300,"oprime S para salir"); while (1)
{
/*
RUTINA PARA FORMAR UN FORMATO ALEATORIO PARA RELLENAR randomize( ); for(I=O; 1<8; 1++) BUF_RELLENAR[I] = rand( ) % 256;
*/
224
Fundamentos de Programacion en Lenguaje C
setfillpattern(BUF_RELLENAR, LIGHTBLUE); setfillstyle(USER_FILL, LIGHTBLUE); rectangle(O, 0, INFO.right/2, INFO.bottom/2); floodfill(l, 1, WHITE); if (getch( )=='5') break; } closegraph( ); return 0;
}
Ejercicios 8.1
Realizar un programa de computadora en lenguaje C que tome informacion de un archivo de datos y presente en pantalla una gnifica de barras tridimensional multiserie. El archivo de datos debe contener un encabezado con la siguiente informacion, ademas de los datos de la grafica: Titulo de la grafica, Numero de series de datos, Nombres de los ejes y las series, Dimensiones del dibujo Adicionalmente, debe contener una tabla con las series de datos que se van a graficar.
8.2 Realice un programa similar al del ejercicio 8.1, pero en lugar de presentar una grafica de barras que presente un conjunto de graficas de pastel. Uno por cada serie de datos. 8.3 Realizar un programa que presente un dibujo y posteriormente mueva interactivamente el dibujo a traves de la pantalla, como respuesta a que el usuario oprima alguna de las teclas de flecha. Si se sale el dibujo de la vetana de graficacion, el programa debe seiializarlo con un sonido y no reaiice el movimiento del dibujo. 8.4 Realizar un programa que dibuje en la pantalla los siguientes simbolos, cambiando su tamaiio, si se oprimen las teclas de flechas, hacia arriba para aumentar el tamaiio, hacia abajo para decrementarlo: Integral, Sumatoria, Raiz cuadrada, Llave izquierda, Llave derecha Corchete izquierdo, Corchete derecho
FUNDAMENTOS DE PROGRAMACION EN LENGUAJE
C
BIBLIOGRAF1A
Adams, L., Windows Visualization Programming with C/C++, McGraw Hi)),
1994. Borland, Turbo C++ Programer's Guide, Borland Intemational, 1990. Borland, Turbo C++ Getting Started, Borland Intemational, 1990. Borland, Turbo C++ Library Reference, Borland Intemational, 1990. Donovan, S. J., A Programmert's Guide to the Mouse, Mc Graw Hill, 1992. Dorfman, L., y M. 1. Neuberger, C lv/emory Management Techniques, McGraw
Hill, 1993. Embree, P. M., y B. Kimble, C Language Algorithms for Digilal Signal Processing, Prentice Hall, 1991. English, V. A., Advanced Toolsfor Windows Developers, SYBEX, 1993. Hekmatpour, S., C++ Gura para Programadores en C, Prentice Hall , 1992. Jamsa, K ., Lenguaje C Bihlioteca de Funciones, McGraw Hill, 1986. Kay, C. D. , y R. J. Levine, Graphics File Formats, McGraw Hill, 1993. Kemighan, W. B., Y M . D. Ritchie, El Lenguaje de Programaci6n C, Segunda Edici6n, Prentice Hall, 1991. !v1organ, D., Practical DSP Modeling Techniques and Programming in C, John Wiley, 1995. Pratt, T. W., Lenguajes de Programacion. Diseiio e Implementaci6n , Prentice Hall , 1984. Ranade, J., y S. Zamir, C++ Primer for C Programmers, NIcGraw Hill, 1994. Schildt, H., Programaci6n en en Lenguaje C, McGraw Hill, 1988 . Schildt. H., Programaci6n en TurhoC, McGraw Hill, 1989. Schildt, H., C Gura para Usuarios Expertos, McGraw Hill, 1989. Schildt, H., TurhoC Programaci6n Avanzada, Segunda Edici6n, McGraw Hill, 1990. Schildt, H., The Craft of C. Take Charge Programming, McGraw Hill. 1992. Schildt, H ., C++ from the Ground Up, McGraw Hill , 1993. Sedgewick, R., Algorithms in C, Addison Wesley, J 990. Stroustrup, B., El C++ Lenguaje de Programaci6n, SegunJa Edici6n, Addis()1l Wesley, ] 993. Tenenbaum. A. M., Y. Langsam, y A. M. Augenstein, Estrucfuras de Datos ('1/ C, Prentice Hall, 1993.
Impreso en los Talleres Graficos del INSTITUTO POLlTECNICO NACIONAL,
Tresguerras 27, 06040 Mexico, OF Septiembre 2003. Edici6n: 2 000 ejemplares.