Programacion en Java

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 639

Programacin

Lenguajes de programacin
Al desarrollarse las primeras computadoras electrnicas, se vio la necesidad de programarlas, es decir, de almacenar en memoria la informacin sobre la tarea que iban a ejecutar. Las primeras se usaban como calculadoras simples; se les indicaban los pasos de clculo, uno por uno. John Von Neumann desarroll el modelo que lleva su nombre, para describir este concepto de "programa almacenado". En este modelo, se tiene una abstraccin de la memoria como un conjunto de celdas, que almacenan simplemente nmeros. Estos nmeros pueden representar dos cosas: los datos, sobre los que va a trabajar el programa; o bien, el programa en s. Cmo es que describimos un programa como nmeros? Se tena el problema de representar las acciones que iba a realizar la computadora, y que la memoria, al estar compuesta por switches correspondientes al concepto de bit, solamente nos permita almacenar nmeros binarios. La solucin que se tom fue la siguiente: a cada accin que sea capaz de realizar nuestra computadora, asociarle un nmero, que ser su cdigo de operacin (opcode). Por ejemplo, una calculadora programable simple podra asignar los opcodes: 1 = SUMA, 2 = RESTA, 3 = MULTIPLICA, 4 = DIVIDE. Supongamos que queremos realizar la operacin 5 * 3 + 2, en la calculadora descrita arriba. En memoria, podramos "escribir" el programa de la siguiente forma: Localidad Opcode Significado Comentario 0 5 5 En esta localidad, tenemos el primer nmero de la frmula 1 3 * En esta localidad, tenemos el opcode que representa la multiplicacin. 2 3 3 En esta localidad, tenemos el segundo nmero de la frmula 3 1 + En esta localidad, tenemos el opcode que representa la suma. 4 2 2 En esta localidad, tenemos el ltimo nmero de la frmula Podemos ver que con esta representacin, es simple expresar las operaciones de las que es capaz el hardware (en este caso, nuestra calculadora imaginaria), en la memoria. La descripcin y uso de los opcodes es lo que llamamos lenguaje de mquina . Es decir, la lista de cdigos que la mquina va a interpretar como instrucciones, describe las capacidades de programacin que tenemos de ella; es el lenguaje ms primitivo, depende directamente del hardware, y requiere del programador que conozca el funcionamiento de la mquina al ms bajo nivel. los lenguajes ms primitivos fueron los lenguajes de mquina. Esto, ya que el hardware se desarroll antes del software, y adems cualquier software finalmente tiene que expresarse en el lenguaje que maneja el hardware. La programacin en esos momentos era sumamente tediosa, pues el programador tena que "bajarse" al nivel de la mquina y decirle, paso a pasito, cada punto de la tarea que tena que realizar. Adems, deba expresarlo en forma numrica; y por supuesto, este proceso era propenso a errores, con lo que la productividad del programador era muy limitada. Sin embargo, hay que recordar que en estos momentos, simplemente an no exista alternativa. El primer gran avance que se dio, como ya se coment, fue la abstraccin dada por el Lenguaje Ensamblador, y con l, el nacimiento de las primeras herramientas automticas para generar el

cdigo mquina. Esto redujo los errores triviales, como poda ser el nmero que corresponda a una operacin, que son sumamente engorrosos y difciles de detectar, pero fciles de cometer. Sin embargo, an aqu es fcil para el programador perderse y cometer errores de lgica, pues debe bajar al nivel de la forma en que trabaja el CPU, y entender bien todo lo que sucede dentro de l. Con el desarrollo en los 50s y 60s de algoritmos de ms elevado nivel, y el aumento de poder del hardware, empezaron a entrar al uso de computadoras cientficos de otras ramas; ellos conocan mucho de Fsica, Qumica y otras ramas similares, pero no de Computacin, y por supuesto, les era sumamente complicado trabajar con lenguaje Ensamblador en vez de frmulas. As, naci el concepto de Lenguaje de Alto Nivel, con el primer compilador de FORTRAN (FORmula TRANslation), que, como su nombre indica, inici como un "simple" esfuerzo de traducir un lenguaje de frmulas, al lenguaje ensamblador y por consiguiente al lenguaje de mquina. A partir de FORTRAN, se han desarrollado innumerables lenguajes, que siguen el mismo concepto: buscar la mayor abstraccin posible, y facilitar la vida al programador, aumentando la productividad, encargndose los compiladores o intrpretes de traducir el lenguaje de alto nivel, al lenguaje de computadora. Hay que notar la existencia de lenguajes que combinan caractersticas de los de alto nivel y los de bajo nivel (es decir, Ensamblador). Mi ejemplo favorito es C: contiene estructuras de programacin de alto nivel, y la facilidad de usar libreras que tambin son caractersticas de alto nivel; sin embargo, fue diseado con muy pocas instrucciones, las cuales son sumamente sencillas, fciles de traducir al lenguaje de la mquina; y requiere de un entendimiento apropiado de cmo funciona la mquina, el uso de la memoria, etctera. Por ello, muchas personas consideramos a lenguajes como C (que fue diseado para hacer sistemas operativos), lenguajes de nivel medio.

Java
El lenguaje de programacin Java, fue diseado por la compaa Sun Microsystems Inc, con el propsito de crear un lenguaje que pudiera funcionar en redes computacionales heterogneas ( redes de computadoras formadas por ms de un tipo de computadora, ya sean PC, MAC's, estaciones de trabajo, etc.),y que fuera independiente de la plataforma en la que se vaya a ejecutar. Esto significa que un programa de Java puede ejecutarse en cualquier mquina o plataforma. El lenguaje fue diseado con las siguientes caractersticas en mente: Simple. Elimina la complejidad de los lenguajes como "C" y da paso al contexto de los lenguajes modernos orientados a objetos. Orientado a Objetos. La filosofa de programacin orientada a objetos es diferente a la programacin convencional. Familiar. Como la mayora de los programadores estn acostumbrados a programar en C o en C++, el sintaxis de Java es muy similar al de estos. Robusto. El sistema de Java maneja la memoria de la computadora por ti. No te tienes que preocupar por apuntadores, memoria que no se est utilizando, etc. Java realiza todo esto sin necesidad de que uno se lo indique. Seguro. El sistema de Java tiene ciertas polticas que evitan se puedan codificar virus con este lenguaje. Existen muchas restricciones, especialmente para los applets, que limitan lo que se puede y no puede hacer con los recursos crticos de una computadora. Portable. Como el cdigo compilado de Java (conocido como byte code) es interpretado, un programa compilado de Java puede ser utilizado por cualquier computadora que tenga implementado el interprete de Java. Independiente a la arquitectura. Al compilar un programa en Java, el cdigo resultante un tipo de cdigo binario conocido como byte code. Este cdido es interpretado por diferentes computadoras de igual manera, solamente hay que implementar un intrprete para cada plataforma. De esa manera Java logra ser un lenguaje que no depende de una arquitectura computacional definida.

Multithreaded. Un lenguaje que soporta multiples threads es un lenguaje que puede ejecutar diferentes lneas de cdigo al mismo tiempo. Interpretado. Java corre en mquina virtual, por lo tanto es interpretado. Dinmico. Java no requiere que compiles todas las clases de un programa para que este funcione. Si realizas una modificacin a una clase Java se encarga de realizar un Dynamic Bynding o un Dynamic Loading para encontrar las clases.

Java puede funcionar como una aplicacin sola o como un "applet", que es un pequeo programa hecho en Java. Los applets de Java se pueden "pegar" a una pgina de Web (HTML), y con esto puedes tener un programa que cualquier persona que tenga un browser compatible podr usar. Nota: Diferencia entre Java y CGI La diferencia es esencialmente simple, un CGI se ejecuta en el servidor mientras que un programa en Java se ejecuta en la mquina del usuario. Java funciona de la siguiente manera: El compilador de Java deja el programa en un Pseudocdigo (no es cdigo maquinal) y luego el intrprete de Java ejecuta el programa (lo que se conoce como el "Java Virtual Machine"). Por eso Java es multiplataforma, existe un intrprete para cada mquina diferente. Nota: El cdigo maquinal es el cdigo binario que la computadora entiende y puede ejecutar. Para entender bien como funciona un applet de Java vean el siguiente ejemplo: 1. Existe un cdigo de Java en un servidor de Web. (Los cdigos de Java se caracterizan por tener la extensin *.class). 2. Una persona en Internet, con un browser compatible con Java, realiza una coneccin al servidor. 3. El servidor enva el documento HTML y el cdigo en Java (*.class). 4. En la computadora del usuario remoto llegan ambos, y la Mquina Virtual de Java, que est en el browser, transforma el cdigo Java en un cdigo que entienda la mquina local y se ejecuta el programa dentro de la pgina de Web. 5. Si el usuario realiza otra conexin a otro URL o se sale del browser, el programa se deja de ejecutar y en la computadora no queda rastro de el.

Ejemplo de tutorial de Java: En Java hay tres tipos de comentarios: // comentarios para una sola lnea /* comentarios de una o ms lneas */ /** comentario de documentacin, de una o ms lneas */ Los dos primeros tipos de comentarios son los que todo programador conoce y se utilizan del mismo modo. Los comentarios de documentacin, colocados inmediatamente antes de una declaracin (de variable o funcin), indican que ese comentario ha de ser colocado en la documentacin que se genera automticamente cuando se utiliza la herramienta de Java, javadoc.

Dichos comentarios sirven como descripcin del elemento declarado permitiendo generar una documentacin de nuestras clases escrita al mismo tiempo que se genera el cdigo. En este tipo de comentario para documentacin, se permite la introduccin de algunos tokens o palabras clave, que harn que la informacin que les sigue aparezca de forma diferente al resto en la documentacin. Identificadores Los identificadores nombran variables, funciones, clases y objetos; cualquier cosa que el programador necesite identificar o usar. En Java, un identificador comienza con una letra, un subrayado (_) o un smbolo de dlar ($). Los siguientes caracteres pueden ser letras o dgitos. Se distinguen las maysculas de las minsculas y no hay longitud mxima. Seran identificadores vlidos: identificador nombre_usuario Nombre_Usuario _variable_del_sistema $transaccion y su uso sera, por ejemplo: int contador_principal; char _lista_de_ficheros; float $cantidad_en_Ptas;

C
C es un lenguaje de programacin diseado por Dennis Ritchie, de los Laboratorios Bell, y se instal en un PDP-11 en 1972; se dise para ser el lenguaje de los Sistemas Operativos UNIX1. A su vez, UNIX es un Sistema Operativo desarrollado por Ken Thompson, quin utiliz el lenguaje ensamblador y un lenguaje llamado B para producir las versiones originales de UNIX, en 1970. C se invent para superar las limitaciones de B. C es un lenguaje maduro de propsitos generales que se desarroll a partir de estas races; su definicin aparece en 1978 en el apndice ``C Reference Manual'' del libro The C Programming Language, de Brian W. Kernighan y Dennis M. Ritchie (Englewood Cliffs, Nueva Jersey, PrenticeHall 1978), pero el estndar recomendable ms reciente apareci en junio de 1983, en el documento de los Laboratorios Bell titulado The C Programming Language-Reference Manual, escrito por Dennis M. Ritchie. Un programa en C

Generalizando, un programa en C consta de tres secciones. La primera seccin es donde van todos los ``headers''. Estos ``headers'' son comnmente los ``#define'' y los ``#include''. Como segunda seccin se tienen las ``funciones''. Al igual que Pascal, en C todas las funciones que se van a ocupar en el programa deben ir antes que la funcin principal (main()). Declarando las funciones a ocupar al principio del programa, se logra que la funcin principal est antes que el resto de las funciones. Ahora, solo se habla de funciones ya que en C no existen los procedimientos. Y como ltima seccin se tiene a la funcin principal, llamada main. Cuando se ejecuta el programa, lo primero que se ejecuta es esta funcin, y de ah sigue el resto del programa. Los smbolos { y } indican ``begin'' y ``end'' respectivamente. Si en una funcin o en un ciclo while, por ejemplo, su contenido es de solamente una lnea, no es necesario usar ``llaves'' ({ }), en caso contrario es obligacin usarlos. Ejemplo de un programa en C /*Programa ejemplo que despliega el contenido de "ROL" en pantalla*/ #include <stdio.h> #define ROL "9274002-1" despliega_rol() { printf("Mi rol es : \%s\n", ROL); } void main() { despliega_rol(); } /* Fin programa */

Pascal
Pascal es un lenguaje de programacin de alto nivel de propsito general; esto es, se puede utilizar para escribir programas para fines cientficos y comerciales. El lenguaje de programacin Pascal fue desarrollado por el profesor Niklaus (Nicols) Wirth en Zurich, Zuiza, al final de los aos 1960s y principios de los 70s. Wirth dise este lenguaje para que fuese un buen primer lenguaje de programacin para personas comenzando a aprender a programar. Pascal tiene un nmero relativamente pequeo de conceptos para aprender y dominar. Su diseo facilita escribir programas usando un estilo que est generalmente aceptado como prctica estndar de programacin buena. Otra de las metas del diseo de Wirth era la implementacin fcil. l dise un lenguaje para el cual fuese fcil escribir un compilador para un nuevo tipo de computadora. program Sorting;

{ Este programa lee un natural y una secuencia de N caracteres de la entrada estandar; construye un indice para ordenarlos de menor a mayor e imprime en la salida la secuencia ordenada. } uses CRT; Const Max = 10; Espacio = ' '; Enter = chr (13); type Indice = 1..Max; Cantidad= 0..Max; SecOfChar = record elems : array [Indice] of char; ult : Cantidad; end; SecOfInd = record elems : array [Indice] of Indice; ult : Cantidad; end; Natural = 0..MaxInt; function PosMin (idx: SecOfInd; i: Indice; s: SecOfChar): Cantidad; { Devuelve la posicion en el indice idx del menor caracter en s, para las posiciones >= i. } var j: Indice; pm: Cantidad; begin if i > idx.ult then pm := 0 else begin

pm := i; for j := i+1 to idx.ult do if s.elems[idx.elems[j]] < s.elems[idx.elems[pm]] then pm := j; end; PosMin := pm; end; procedure Swap (var idx: SecOfInd; i,j: Indice); { Intercambia las posiciones i j en idx. } var tmp: Indice; begin if (i<=idx.ult) and (j<=idx.ult) then begin tmp := idx.elems[i]; idx.elems[i] := idx.elems[j]; idx.elems[j] := tmp; end; end; procedure InicInds (var idx: SecOfInd; cant: Indice); { Construye la secuencia de indices 1,2,3,...,n. Sera el indice inicial para el ordenamiento de una secuencia de caracteres c1,c2,...,cn. } var n: Natural; begin n := cant; idx.ult := n; while n > 0 do begin idx.elems [n] := n;

n := n-1; end; end; procedure InicSecChar (var s: SecOfChar); { Devuelve la secuencia vacia. } begin s.ult := 0; end; function Llena (s: SecOfChar): Boolean; begin Llena := s.ult = Max; end; { PRE: not Llena(s) } procedure InsCar (var s: SecOfChar; c: char); { Inserta el caracter c en la secuencia s } begin s.ult := s.ult + 1; s.elems [s.ult] := c; end; procedure IndSelSort (s: SecOfChar; var ind: SecOfInd); { Construye el indice que ordena la secuencia s. Ordena el indice inicial 1,2, ..., n por el metodo de selection sort } var i: Indice; begin InicInds (ind, s.ult); for i := 1 to ind.ult-1 do begin

Swap (ind, i, PosMin (ind, i, s)); end end; procedure WriteSorted (idx: SecOfInd; s: SecOfChar); { Imprime en la salida estandar la secuencia s ordenada segun el indice idx } var i: Indice; begin write ('Ordenado: '); for i := 1 to idx.ult do write (s.elems[idx.elems[i]],' '); writeln; end; procedure LeerCar (var c: char; var ok: boolean; sep: Char); { Lee de la entrada estandar un caracter seguido del caracter sep } var c1, c2: char; begin c := ReadKey; write (c); c1 := ReadKey; write (c1); ok := c1 = sep; end; procedure LeerSecOfChar (var s: SecOfChar; cant: Natural; var ok: Boolean); { Construye una secuencia de cant caracteres provistos por el procedimeinto LeerCar. Si cant > Max trunca. } var bien: Boolean; i: Natural;

10

ch, sep: Char; begin writeln ('Ingrese ',cant, ' caracteres separados por blancos. Enter para terminar '); write (' > '); InicSecChar (s); i := 1; ok := true; sep := Espacio; while ok and (i <= cant) and not Llena (s) do begin if i = cant then sep := Enter; LeerCar (ch, bien, sep); i := i+1; ok := ok and bien; if ok then InsCar (s, ch); end; end; procedure LeerCant (var n: Natural); { Lee de la entrada estandar un natural <= Max } begin repeat writeln ('Ingrese cantidad de caracteres (<=',Max,')'); write (' > '); readln (n); until n <= Max;

11

end; procedure Continuar (var seguir: Boolean); var car: Char; begin writeln; writeln ('Otro ? (s/n)'); write (' > '); car := ReadKey; writeln (car); seguir := car in ['s','S']; end; var cant: Natural; cars: SecOfChar; inds: SecOfInd; seguir, ok: boolean; begin repeat ClrScr; LeerCant (cant); LeerSecOfChar (cars, cant, ok); if ok then begin IndSelSort (cars, inds); writeln; WriteSorted (inds, cars); end else begin

12

writeln; writeln ('Error en los datos'); end; Continuar (seguir); until not seguir; end.

QBasic
Qbasic es un lenguaje de alto nivel, el cual consiste en instrucciones que los humanos pueden relacionar y entender. El compilador de Qbasic se encarga de traducir el mismo a lenguaje de mquina. Un programa es una secuencia de instrucciones. El proceso de ejecutar esas instrucciones se llama correr el programa. Los programas contienen las funciones de entrada, procesamiento y salida. La persona que resuelve problemas mediante escribir programas en la computadora se conoce como programador. Despus de analizar el problema y desarrollar un plan para solucionarlo, escribe y prueba el programa que instruye a la computadora como llevar a cabo el plan. El procedimiento que realiza el programador se define como "problem solving". Pero es necesario especificar que un programador y un usuario no son lo mismo. Un usuario es cualquier persona que use el programa. Ejemplo de qbasic, para hacer una calculadora DIM total AS DOUBLE DIM number AS DOUBLE DIM secondNumber AS DOUBLE DIM more AS STRING DIM moreNumbers AS STRING DIM operation AS STRING total = 0 more = "y" moreNumbers = "c" CLS WHILE more = "y" INPUT "Enter the first number"; number

13

total = number WHILE moreNumbers = "c" COLOR 14 PRINT "The total is:"; total COLOR 7 PRINT "Select an operation" COLOR 2 PRINT "(+)" COLOR 5 PRINT "(-)" COLOR 1 PRINT "(x)" COLOR 4 INPUT "(/)"; operation COLOR 7 CLS IF operation = "+" THEN REM where we do additions PRINT "Enter the number to Add to"; total INPUT secondNumber total = secondNumber + total COLOR 14 PRINT "The total is now:"; total COLOR 7 ELSE IF operation = "-" THEN

14

REM subtraction PRINT "Enter the number to Subtract from"; total INPUT secondNumber total = total - secondNumber COLOR 14 PRINT "The total is now:"; total COLOR 7 ELSE IF operation = "x" THEN REM multiplication PRINT "Enter the number to Multiply"; total; "by" INPUT secondNumber total = secondNumber * total REM * is the multiplication sign in programs COLOR 14 PRINT "The total is now:"; total COLOR 7 ELSE IF operation = "/" THEN REM division PRINT "Enter the number to Divide"; total; "by" INPUT secondNumber IF secondNumber = 0 THEN COLOR 4 PRINT "You cannot divide by zero" COLOR 7

15

ELSE total = total / secondNumber REM / is the division sign in programs END IF COLOR 14 PRINT "The total is now:"; total COLOR 7 ELSE PRINT "you must select an operation" END IF END IF END IF END IF INPUT "Do you wish to continue (c) or start with new numbers (n)";moreNumbers CLS WEND COLOR 14 PRINT "The grand total is:"; total COLOR 7 INPUT "Do you wish to make more calculations (y - n)"; more moreNumbers = "c" REM if we don't put "moreNumbers" back to y, it will always REM come back to "Do you wish to make more calculations" and never REM ask for numbers again

16

REM (try it) total = 0 REM if we don't reset the total to 0, it will just REM keep on adding to the total WEND END

Ensamblador
Cuando abstraemos los opcodes y los sustituimos por una palabra que sea una clave de su significado, a la cual comnmente se le conoce como mnemnico , tenemos el concepto de Lenguaje Ensamblador. As, podemos definir simplemente al Lenguaje Ensamblador de la siguiente forma: Lenguaje Ensamblador es la primera abstraccin del Lenguaje de Mquina, consistente en asociar a los opcodes palabras clave que faciliten su uso por parte del programador Como se puede ver, el Lenguaje Ensamblador es directamente traducible al Lenguaje de Mquina, y viceversa; simplemente, es una abstraccin que facilita su uso para los seres humanos. Por otro lado, la computadora no entiende directamente al Lenguaje Ensamblador; es necesario traducirle a Lenguaje de Mquina. Originalmente, este proceso se haca a mano, usando para ello hojas donde se escriban tablas de programa similares al ejemplo de la calculadora que vimos arriba . Pero, al ser tan directa la traduccin, pronto aparecieron los programas Ensambladores, que son traductores que convierten el cdigo fuente (en Lenguaje Ensamblador) a cdigo objeto (es decir, a Lenguaje de Mquina). Una caracterstica que hay que resaltar, es que al depender estos lenguajes del hardware, hay un distinto Lenguaje de Mquina (y, por consiguiente, un distinto Lenguaje Ensamblador) para cada CPU. Por ejemplo, podemos mencionar tres lenguajes completamente diferentes, que sin embargo vienen de la aplicacin de los conceptos anteriores: 1.Lenguaje Ensamblador de la familia Intel 80x86 2.Lenguaje Ensamblador de la familia Motorola 68000 3.Lenguaje Ensamblador del procesador POWER, usado en las IBM RS/6000. Tenemos 3 fabricantes distintos, compitiendo entre s y cada uno aplicando conceptos distintos en la manufactura de sus procesadores, su arquitectura y programacin; todos estos aspectos, influyen en que el lenguaje de mquina y ensamblador cambie bastante. Ventajas y desventajas del Lenguaje Ensamblador Una vez que hemos visto la evolucin de los lenguajes, cabe preguntarse: En estos tiempos "modernos", para qu quiero el Lenguaje Ensamblador? El proceso de evolucin trajo consigo algunas desventajas, que ahora veremos como las ventajas de usar el Lenguaje Ensamblador, respecto a un lenguaje de alto nivel: 1.Velocidad

17

2.Eficiencia de tamao 3.Flexibilidad Por otro lado, al ser un lenguaje ms primitivo, el Ensamblador tiene ciertas desventajas respecto a los lenguajes de alto nivel: 1.Tiempo de programacin 2.Programas fuente grandes 3.Peligro de afectar recursos inesperadamente 4.Falta de portabilidad Velocidad El proceso de traduccin que realizan los intrpretes, implica un proceso de cmputo adicional al que el programador quiere realizar. Por ello, nos encontraremos con que un intrprete es siempre ms lento que realizar la misma accin en Lenguaje Ensamblador, simplemente porque tiene el costo adicional de estar traduciendo el programa, cada vez que lo ejecutamos. De ah nacieron los compiladores, que son mucho ms rpidos que los intrpretes, pues hacen la traduccin una vez y dejan el cdigo objeto, que ya es Lenguaje de Mquina, y se puede ejecutar muy rpidamente. Aunque el proceso de traduccin es ms complejo y costoso que el de ensamblar un programa, normalmente podemos despreciarlo, contra las ventajas de codificar el programa ms rpidamente. Sin embargo, la mayor parte de las veces, el cdigo generado por un compilador es menos eficiente que el cdigo equivalente que un programador escribira. La razn es que el compilador no tiene tanta inteligencia, y requiere ser capaz de crear cdigo genrico, que sirva tanto para un programa como para otro; en cambio, un programador humano puede aprovechar las caractersticas especficas del problema, reduciendo la generalidad pero al mismo tiempo, no desperdicia ninguna instruccin, no hace ningn proceso que no sea necesario. Para darnos una idea, en una PC, y suponiendo que todos son buenos programadores, un programa para ordenar una lista tardar cerca de 20 veces ms en Visual Basic (un intrprete), y 2 veces ms en C (un compilador), que el equivalente en Ensamblador. Por ello, cuando es crtica la velocidad del programa, Ensamblador se vuelve un candidato lgico como lenguaje. Ahora bien, esto no es un absoluto; un programa bien hecho en C puede ser muchas veces ms rpido que un programa mal hecho en Ensamblador; sigue siendo sumamente importante la eleccin apropiada de algoritmos y estructuras de datos. Por ello, se recomienda buscar optimizar primero estos aspectos, en el lenguaje que se desee, y solamente usar Ensamblador cuando se requiere ms optimizacin y no se puede lograr por estos medios. Tamao Por las mismas razones que vimos en el aspecto de velocidad, los compiladores e intrpretes generan ms cdigo mquina del necesario; por ello, el programa ejecutable crece. As, cuando es importante reducir el tamao del ejecutable, mejorando el uso de la memoria y teniendo tambin beneficios en velocidad, puede convenir usar el lenguaje Ensamblador. Entre los programas que es crtico el uso mnimo de memoria, tenemos a los virus y manejadores de dispositivos (drivers). Muchos de ellos, por supuesto, estn escritos en lenguaje Ensamblador. Flexibilidad

18

Las razones anteriores son cuestin de grado: podemos hacer las cosas en otro lenguaje, pero queremos hacerlas ms eficientemente. Pero todos los lenguajes de alto nivel tienen limitantes en el control; al hacer abstracciones, limitan su propia capacidad. Es decir, existen tareas que la mquina puede hacer, pero que un lenguaje de alto nivel no permite. Por ejemplo, en Visual Basic no es posible cambiar la resolucin del monitor a medio programa; es una limitante, impuesta por la abstraccin del GUI Windows. En cambio, en ensamblador es sumamente sencillo, pues tenemos el acceso directo al hardware del monitor. Resumiendo, la flexibilidad consiste en reconocer el hecho de que Todo lo que puede hacerse con una mquina, puede hacerse en el lenguaje ensamblador de esta mquina; los lenguajes de alto nivel tienen en una u otra forma limitantes para explotar al mximo los recursos de la mquina. Tiempo de programacin Al ser de bajo nivel, el Lenguaje Ensamblador requiere ms instrucciones para realizar el mismo proceso, en comparacin con un lenguaje de alto nivel. Por otro lado, requiere de ms cuidado por parte del programador, pues es propenso a que los errores de lgica se reflejen ms fuertemente en la ejecucin. Por todo esto, es ms lento el desarrollo de programas comparables en Lenguaje Ensamblador que en un lenguaje de alto nivel, pues el programador goza de una menor abstraccin. Programas fuente grandes Por las mismas razones que aumenta el tiempo, crecen los programas fuentes; simplemente, requerimos ms instrucciones primitivas para describir procesos equivalentes. Esto es una desventaja porque dificulta el mantenimiento de los programas, y nuevamente reduce la productividad de los programadores. Peligro de afectar recursos inesperadamente Tenemos la ventaja de que todo lo que se puede hacer en la mquina, se puede hacer con el Lenguaje Ensamblador (flexibilidad). El problema es que todo error que podamos cometer, o todo riesgo que podamos tener, podemos tenerlo tambin en este Lenguaje. Dicho de otra forma, tener mucho poder es til pero tambin es peligroso. En la vida prctica, afortunadamente no ocurre mucho; sin embargo, al programar en este lenguaje vern que es mucho ms comn que la mquina se "cuelgue", "bloquee" o "se le vaya el avin"; y que se reinicialize. Por qu?, porque con este lenguaje es perfectamente posible (y sencillo) realizar secuencias de instrucciones invlidas, que normalmente no aparecen al usar un lenguaje de alto nivel. En ciertos casos extremos, puede llegarse a sobreescribir informacin del CMOS de la mquina (no he visto efectos ms riesgosos); pero, si no la conservamos, esto puede causar que dejemos de "ver" el disco duro, junto con toda su informacin. Falta de portabilidad Como ya se mencion, existe un lenguaje ensamblador para cada mquina; por ello, evidentemente no es una seleccin apropiada de lenguaje cuando deseamos codificar en una mquina y luego llevar los programas a otros sistemas operativos o modelos de computadoras. Si

19

bien esto es un problema general a todos los lenguajes, es mucho ms notorio en ensamblador: yo puedo reutilizar un 90% o ms del cdigo que desarrollo en "C", en una PC, al llevarlo a una RS/6000 con UNIX, y lo mismo si despus lo llevo a una Macintosh, siempre y cuando est bien hecho y siga los estndares de "C", y los principios de la programacin estructurada. En cambio, si escribimos el programa en Ensamblador de la PC, por bien que lo desarrollemos y muchos estndares que sigamos, tendremos prcticamente que reescribir el 100 % del cdigo al llevarlo a UNIX, y otra vez lo mismo al llevarlo a Mac.

20

Programacin en C
Para familiarizarnos con el lenguaje C lo mejor es comenzar haciendo pequeos programas. El objetivo ser dar una primera impresin de como trabaja el C, sin profundizar demasiado en todas sus caractersticas. En esta primera parte se abordarn los mtodos que son comunes a todos los lenguajes de programacin estructurada. Los programas en C estn formados por una serie de lneas de cdigo que se ejecutan sucesivamente. Todos los programas se dividen en bloques. Cada bloque de cdigo se encierra en funciones. La ejecucin del programa siempre comienza en la funcin main(). Esta funcin es la encargada de llamar a las dems. Para escribir la funcin main se coloca al principio de la funcin el siguiente cdigo: main() { Luego escribimos todas las lneas de cdigo. A cada lnea de cdigo le llamaremos sentencia. Despus de cada sentencia escribiremos un ';'. En C todas las sentencias acaban con un . Hay varios tipos de sentencia. Las ms comunes la llamada a una funcin. Cuando hace esto el programa llama a la funcin. Entonces se ejecuta el cdigo de la funcin. Cuando la funcin finaliza la ejecucin devuelve el control a la funcin main(). Las funciones son muy difciles de reconocer, pues a continuacin de su nombre van los parntesis. Para acabar el cdigo de una funcin siempre escribiremos al principio de una nueva lnea una llave de cierre '}'. Por ejemplo: #include <stdio.h> main() { printf("hola, mundo\n"); } En este programa la funcin main consta de una sola sentencia, que es la llamada a la funcin printf(). La funcin printf imprime en la salida habitual, que generalmente es el terminal en el que trabajamos, el mensaje que le demos. El mensaje que queremos imprimir es hola, mundo, seguido de un avance del cursor al principio de la lnea siguiente. El mensaje lo debemos encerrar entre comillas ", para que el compilador sepa cual es su longitud. La funcin printf() es una funcin que pertenece a la librera estndar del C. Esta librera tiene un conjunto muy amplio de funciones listas para usar, lo que nos evitar el trabajo de usarlas. Para

21

usar esta librera debemos avisar al compilador. Por ello incluimos como primera lnea del programa la lnea #include <stdio.h>. En esta librera hay un montn de funciones tiles. Para comenzar daremos una lista de alguna de ellas, con una pequea explicacin de lo que hacen, aunque no nos extenderemos en su uso. Cuando comencemos a usarlas rpidamente nos daremos cuenta de su uso. Estas funciones son: printf(mensaje) imprime un mensaje en el terminal putchar(carcter) escribe un carcter en el terminal getchar() recoge un carcter del terminal scanf(variables) lee variables del terminal gets() lee una lnea del terminal De estas funciones, printf() y scanf() son las mas complicadas de usar, y las que admiten ms posibilidades de funcionamiento. De echo son unas de las mas complicadas de la librera estndar. Estructura de un programa en C Un programa en C es simplemente un fichero de caracteres que contiene un conjunto de instrucciones que un programa especial, el compilador o traductor, se encargar de transformar en un programa que la computadora pueda ejecutar. Un programa normalmente suele estar compuesto de tres partes: la seccin de variables, que especifica los datos y sus tipos que vamos a manejar a lo largo del programa. la funcin principal, que se suele llamar "main", que ser la que defina la estructura del programa. las funciones o subrutinas auxiliares, que son llamados por la rutina principal, la funcin main. Estas subrutinas se suelen colocar despus de la funcin main. Cuando la envergadura del programa es grande se suele fragmentar el programa en varias partes, incluyendo cada parte en un fichero separado. El lenguaje C define el mtodo que debemos seguir para separar las diferentes partes del programa. Normalmente colocaremos en cada fichero todas las subrutinas y funciones que se encarguen de una tarea del programa. As la funcin main ir "llamando" a las subrutinas a medida que las vaya necesitando. Un primer vistazo a la programacin estructurada: las funciones

22

Como hemos visto en C los programas constan de una rutina principal, de nombre main y una serie de subrutinas asociadas. En C las rutinas se crean mediante una funcin. Una funcin es un fragmento de cdigo que realiza una tarea. En C las funciones siempre tienen un nombre, que viene dado por un identificador. Por ejemplo main es el identificador de la funcin main, que es la rutina principal de todo programa en C. Para escribir la funcin main solo tenemos que colocar al principio de una lnea su identificador, seguido de los caracteres '(' y ')'. Es costumbre entre los programadores de C de escribir el primer parntesis pegado al identificador de la funcin, ya que facilita enormemente su clasificacin como funcin. En realidad esto no es necesario ya que el compilador salta los espacios automticamente. A continuacin del ultimo parntesis se escribe el carcter de abrir llaves '{' . Tambin es costumbre entre los programadores el escribir esta llave en la lnea siguiente, y no a continuacin del parntesis, aunque al compilador le da igual, y no protestar si introducimos o quitamos espacios en blanco. Recordemos que decamos que el C es un lenguaje de formato libre. A continuacin de la llave se escribe el cdigo de la funcin. Por ltimo, al principio de una nueva lnea debemos incluir otra llave '}', que servir de cierre de nuestra funcin. Un ejemplo de funcin main puede ser: main() { printf("Hola, mundo\n"); } Esta funcin casi constituye un programa completo, salvo que al intentar compilarlo el compilador se puede quejar y decirnos que no conoce el identificador printf. Si repasamos nuestra lista de palabras reservadas veremos que no aparecen ni la palabra main ni la palabra printf, sin embargo el sistema slo se quejar probablemente de la palabra printf. Esto se debe a que la palabra main suele estar predefinida como funcin en la mayora de los sistemas en C. La funcin printf simplemente imprime en la salida del sistema (generalmente la pantalla del usuario) la frase que le demos. La funcin printf forma parte de lo que se conoce como librera estndar del C. Con casi todos los compiladores se suele incluir un conjunto de funciones predefinidas que realizan las funciones mas usuales de los lenguajes de programacin: entrada y salida de datos por pantalla, creacin y manipulacin de ficheros y otras muchas funciones. En esto el lenguaje C es muy potente, pues la librera estndar es muy amplia. Para avisar al compilador que vamos a usar la funcin printf simplemente debemos aadir una lnea al principio de nuestro programa, quedando ahora el programa como sigue: #include <stdio.h> main() { printf("Hola, mundo\n"); } Este es ya un programa completo en C, uno de los ms simples que se pueden crear. Simplemente escribe un mensaje de salida en nuestro terminal. El significado de sus partes se ir viendo ms adelante.

23

El cdigo de un programa en C El C es un lenguaje de formato libre. Esto quiere decir que consiste en que un programa est formado por comandos que estn separados por espacios en blanco y tambin. Para ello el C considera como espacios en blanco, no slo el carcter blanco ' ', sino tambin el carcter de tabulador '\t' y el carcter de nueva lnea '\n' o '\r'. El numero de espacios que empleemos no vara el significado del programa. Aunque al compilador del lenguaje le da igual el nmero de espacios en blanco que insertemos entre los comandos, por motivos de legibilidad seguiremos una serie de normas. La primera de ellas hace referencia a los comentarios. Un comentario es una lnea que se incluye en el programa, cuya misin consiste en aclarar la funcin de una parte concreta del programa a otro lector, o incluso al mismo programador. En C hay dos formas de incluir estos comentarios. La primera es incluir el texto que sirve de comentario al principio de la seccin, entre dos smbolos especiales: el /* o principio de comentario y el */ o fin de comentario. Todo el texto que incluyamos entre ellos el compilador lo ignorar , incluyendo los saltos de lnea. Por ejemplo si una seccin del programa se encarga de ofrecer los resultados finales del programa, podramos incluir en el cdigo el siguiente comentario: /* esta seccin se encarga de imprimir los datos en la impresora asignada */ Y aqu ira el resto del programa. El otro tipo de comentarios se suele usar para sealar una determinada lnea del programa. Para ello escribimos el comentario a la derecha de la lnea a comenta. Por ejemplo, si en una lnea aparece el comando gets(), podramos poner a la derecha en un comentario lo que hace: gets(linea); /* recoge una lnea del teclado */ Este tipo de comentario es el que usaremos en muchas de la explicaciones de ahora en adelante. La seccin de variables globales Normalmente en todo programa en C hay una seccin de variables globales. En las variables globales almacenaremos datos que deben ser accesibles a todo el programa. Cuando el programa es pequeo, por ejemplo si consta de un slo fichero, por comodidad se suelen definir todas las variables como globales. Esto tiene como ventaja que no deberemos definir el tipo de las variables en las funciones auxiliares, ya que ser n directamente visibles para ellas. Como inconveniente tenemos que al ser las variables globales accesibles por todas las funciones podremos modificarlas por error, por lo que pueden aparecer errores difciles de depurar. La mejor estrategia es dejar como variables globales slo las estrictamente necesarias, definiendo en cada funcin las variables que necesitemos. La funcin main Si el programa es pequeo es probable que la mayor parte del programa se halle dentro de la funcin main. Cuando el programa comienza a tener un tamao mayor conviene dejar para la funcin main slo el cuerpo del programa. Al principio de la funcin main colocaremos todas las rutinas de inicializacin. Si vamos a manejar algn fichero a lo largo del programa la funcin main es un buen lugar para abrirlo. Tambin se suele procesar en la funcin main los mensajes de bienvenida, en los que se muestra el nombre del autor y dems informacin. Si el programa admite par metros en la lnea de rdenes, la funcin main debe procesarlos, ya que la funcin main tiene normalmente acceso a la lnea de argumentos con que fue ejecutado el programa.

24

Las variables El siguiente paso para programar en C consiste en presentar las variables. Las variables permiten guardar informacin. Los principales tipos de datos son los datos numricos, los caracteres y las cadenas de caracteres. En principio situaremos las variables al principio del programa. Para utilizar una variable situaremos antes de la funcin main el nombre de la variable precedido de su tipo. En principio vamos a usar tres tipos de variables: los enteros, cuyo especificador de tipos es la palabra reservada int, los caracteres, cuya palabra reservada es char, y las cadenas de caracteres, cuyo especificador de tipo es char *. Los enteros son cualquier nmero positivo o negativo sin decimal, de digamos "tamao pequeo". Los caracteres son letras, nmeros, signos de puntuacin y algn carcter especial, como el fin de lnea y la campanilla. Las cadenas de caracteres son un conjunto de caracteres, de cualquier longitud encerado entre comillas dobles". Comencemos creando un programa con una variable numrica, numero1: #include <stdio.h> int numero1 = 1; main() { printf("numero1 vale %d\n", numero1); } Como siempre, haremos el programa siguiendo la estructura que hemos seguido. Observar que al crear la variable le hemos colocado un = 1. Esto permite almacenar un 1 en nuestra variable. Luego dentro de la funcin main salo tenemos que llamar a printf() para imprimir nuestra variable. Aqu ya empezamos a ver complicaciones en el uso de printf(). Printf coge nuestra cadena de caracteres numero1 vale %d\n. El %d indica que printf debe insertar en el mensaje el valor de la variable que a continuacin le demos, en nuestro caso numero1. El carcter \n es un carcter especial, el de nueva lnea, y le dice a printf() que debe avanzar el cursor al principio de la lnea siguiente. El valor de las variables es, como su propio nombre indica, variable. Podemos alterar su valor en cualquier punto del programa. La forma mas sencilla de hacerlo es mediante una sentencia de asignacin. Para asignar un nombre a una variable se escribe su identificador seguido de un = y el nuevo valor. Este tipo de sentencia es el segundo ms importante. As: #include <stdio.h> int i = 1; main() { printf("el antiguo valor de i es %d\n", i); i = 2;

25

printf("el nuevo es %d\n", i); } El lado derecho de una asignacin es una expresin. Una expresin es otro tipo de sentencia, quiz s el tercero ms importante. Una expresin es una sentencia que al ser evaluada devuelve un valor. Los tipos de expresiones ms frecuentes son las operaciones aritmticas: suma, resta, multiplicaciones y divisiones. Para escribir una expresin aritmtica usaremos una notacin similar a la usada en las calculadoras, incluyendo el uso de parntesis. Por ejemplo: #include<stdio.h> int i; main() { i = 2 + 2; printf(" 2 + 2 = %d\n" , i); } Otros ejemplos de operaciones aritmticas son: i = 2 * 2 + 6; i = 2 * (2+ 6); i = 1 - 4 / 2; Observar que el * y el / tienen mayor "precedencia" que el + y el -, es decir al evaluar la expresin se evala antes un * y un - que un + o un -. El tema de la evaluacin de expresiones es mas complejo, y lo dejaremos para un captulo mas avanzado. Adems contamos con otro tipo de expresiones, el resultado de llamar a una funcin. Lo veremos mejor con el tipo de variable carcter. Las variables de tipo carcter se emplean de modo anlogo a las de tipo entero, salvo que ahora almacenan un carcter. En el siguiente ejemplo crearemos una variable y despus de imprimirla la asignaremos el resultado de una expresin del tipo llamada a funcin. Utilizaremos la funcin getchar() que devuelve el siguiente carcter disponible desde el terminal. #include <stdio.h> char c = 'a' ; main() {

26

putchar(c); putchar('\n'); printf("introduce un carcter por el terminal\n"); c = getchar(); printf("el primer carcter que has "); printf("introducido es: %c\n", c); } Observar que el modificador para imprimir un carcter con printf() es %c. Si sustituimos este modificador por el %d se imprimir el cdigo ASCII del carcter introducido. Aqu ya podemos observar una caracterstica muy importante del C, que es la equivalencia de tipos. El tipo de datos entero y el tipo de datos carcter son intercambiables, es decir, si una expresin espera un entero y le damos un carcter y le damos un entero, el compilador promocionar el carcter a entero. Esto siempre es posible. El proceso inverso tambin se puede hacer. Si a una variable de tipo carcter y le damos un entero el compilador tratar de meterlo como pueda. Si el entero es pequeo esto se har sin problemas. Si el entero es grande, el compilador truncar el entero, es decir, le cortar un trozo. El proceso de truncado se ve mejor usando representacin binaria. Los caracteres se suelen representar mediante bytes, es decir 8 bits. Los enteros suelen ser dos o cuatro bytes. El compilador para pasar de un carcter a entero simplemente le aade un byte a cero. Para pasar de entero a carcter el compilador se olvida de todos los bytes salvo el de menor orden. Por eso la conversin entre tipos funciona la mayora de las ocasiones. As la funcin getchar() devuelve un entero, no un carcter. Esto se hace as por otro motivo un poco ms oscuro, relacionado con el fin de fichero, que explicaremos al tratar el tema de los ficheros. No obstante, la funcin getchar() funciona sin complicaciones. As mismo la funcin putchar() admite un carcter o un entero, pero siempre los imprime como carcter. Cadenas de caracteres y arreglos. Las cadenas de caracteres nos permiten manejar texto con facilidad. Ya hemos visto que la funcin printf nos permite sacar una cadena por pantalla. Esto nos hace pensar que las cadenas de caracteres pueden servir para almacenar tiras de caracteres, como nombres o texto. Sin embargo poseen una pega: son constantes, por lo que no se pueden alterar. Para almacenar grupos de caracteres utilizaremos los arreglos de caracteres. Identificadores y palabras reservadas El lenguaje C est formado por un conjunto pequeo de palabras clave o comandos y una serie de operadores. Hay cerca de 40 palabras claves, frente a las 150 del BASIC o 200 que poseen otros lenguajes, como el COBOL y el PASCAL. Estas palabras son: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct typedef union unsigned void volatile while

27

A este conjunto de palabras se les denomina "palabras reservadas". Cuando en el C creemos nuestras propias subrutinas y conjuntos de datos les deberemos poner nombres. Para nombrar las funciones (subrutinas) y variables (datos) utilizaremos los identificadores. Un identificador es un conjunto de caracteres alfanumricos (letras y nmeros) que comienzan siempre por una letra. Para formar un identificador usaremos indiferentemente maysculas y minsculas, nmeros y tambin el carcter '_' (subrayado), aunque este carcter tiene un significado especial, por lo que en principio debemos evitar su uso. Como identificador vale cualquier secuencia de letras y nmeros, de cualquier longitud, pero con dos limitaciones: no podemos empezar con un nmero las palabras reservadas no se pueden usar como identificador, aunque un identificador puede comenzar con una palabra reservada. De aqu la denominacin de palabras reservadas. Ejemplos de identificadores validos seran: hola hola127 printf whiledo Variables Una variable es un lugar donde se puede almacenar temporalmente un dato. En C las variables tienen un nombre que las identifica, y sirve para hacer referencia a ellas. Tambin tienen un tipo, que es el tipo de datos que puede almacenar. Por ejemplo podemos tener una variable de tipo entero de nombre num. Observar que para dar un nombre a una variable tenemos que usar un identificador. Para crear una variable en un lugar determinado del un programa escribiremos primero el tipo de variable y luego el identificador con el que queremos nombrar la variable, seguido todo de un ';'. Por ejemplo: int numero; /* crea la variable numero de */ /* tipo entero */ char caracter1; /* crea una variable de tipo caracter1 */ las variables se pueden inicializar, es decir, darles un valor inicial, en el momento de creacin. Para ello detrs del identificador ponemos el caracter1 '=' seguido del valor inicial. Los valores iniciales pueden ser cualquier constante vlida para el tipo de variable que creemos. Por ejemplo: int numero = 0; /*crea la variable entera numero */ /* y la inicializa a 0*/ char c = 'a'; /* crea una variable caracter1 y */

28

/* la inicializa a 'a' */ Duracin de las variables Las variables pueden ser de dos tipos: estticas y dinmicas. Las estticas se crean al principio del programa y duran mientras el programa se ejecute. Las variables son dinmicas si son creadas dentro de una funcin. Su existencia est ligada a la existencia de la funcin. Se crean cuando la funcin es llamada y se destruyen cuando la funcin o subrutina devuelve el control a la rutina que la llam. Las variables estticas se utilizan para almacenar valores que se van a necesitar a lo largo de todo el programa. las variables dinmicas se suelen utilizar para guardar resultados intermedios en los clculos de las funciones. Como regla general una variable es esttica cuando se crea fuera de una funcin y es dinmica cuando se crea dentro de una funcin. Por ejemplo en el siguiente programa : #include <stdio.h> int numero1 = 1; main() { int numero2 = 2; printf("%d, %d\n", numero1, numero2); } hemos creado la variable esttica numero1, que dura todo el programa, y la variable numero2, que dura slo mientras se ejecuta la funcin main(). En este programa tan pequeo , la funcin main() es la que ocupa todo el tiempo de ejecucin, por lo que no apreciaremos diferencia en el uso de ambas, aunque ms adelante si se ver su uso. Alcance de las variables Otra caracterstica de las variables es su alcance. El alcance se refiere a los lugares de un programa en los que podemos utilizar una determinada variable. Distinguiremos as dos tipos principales de variables: globales y locales. Una variable es global cuando es accesible desde todo el programa, y es local cuando solo puede acceder a ella la funcin que la creo. Tambin hay una norma general para el alcance de las variables: una variable es global cuando se define fuera de una funcin, y es local cuando se define dentro de una funcin. En nuestro ejemplo anterior numero1 es una variable global y numero2 es una variable local. Dentro de las variables globales hay dos tipos: las que son accesibles por todos los ficheros que componen nuestro programa y las que son accesibles solo por todas las funciones que componen un fichero. Esto es debido a que normalmente los programas en C se fragmentan en mdulos ms

29

pequeos, que son mas fciles de manejar y depurar. Por ello hay veces que nos interesar que una variable sea accesible desde todos los mdulos, y otras solo queremos que sea accesible por las funciones que componen un determinado modulo. Por defecto todas las variables globales que creemos son accesibles por todos los ficheros que componen nuestro programa. Modificadores de tipo Podemos fcilmente modificar el alcance y la duracin de una variable que tiene asignado por defecto: Esto es una operacin muy comn y til. Para hacerlo antepondremos al tipo de la variable un modificador, que es una palabra reservada, que cambiar estas caracterstica. El primer modificador es la palabra clave static. Cuando a una variable local se le aade el modificador static pasa de ser dinmica a ser esttica. As la duracin de la variable se ampla a la duracin del programa completo. Observar que una variable esttica solo se crea una vez, al principio del programa, por lo que la inicializacin solo se produce una vez para una variable esttica. Adems el modificador static tiene otro uso. Si aadimos este modificador a una variable global, definida fuera de una funcin, entonces modificamos su alcance: pasa de tener alcance global a todos los ficheros del programa a ser solo accesible por las funciones del fichero en el que se crea. Otro modificador usual es externa. Este modificador se usa cuando una variable que se creo en otro modulo se quiere usar en un modulo. Cuando aadimos a la variable este modificador el compilador queda advertido de que la variable ya existe en otro modulo, por lo que el compilador no tiene que crearla, sino simplemente usarla. Entonces a este tipo de proceso se le llama declaracin de tipo de variable. Por ejemplo: extern int numero; main() { printf("%d\n", numero); } es un programa en que declaramos la variable externa numero, que habremos creado en otro modulo. Una diferencia muy importante entre una definicin y una declaracin es que en la definicin no se reserva espacio en la memoria para la variable, y en la definicin si se crea. Hay otros modificadores que no son tan usuales: auto, volatile y register. El modificador auto indica que una variable local es dinmica (en la terminologa del C automtica). Observar que por defecto las variables locales a una funcin son automticas, por lo que no se usa. Sin embargo todos los compiladores la reconocen y no protestan si la usamos. Por ejemplo: main() { auto numero = 1;

30

printf("%d\n", numero); } crea una variable automtica entera. Si quitamos auto el programa no se diferencia. El modificador register se usa ms a menudo, sobre todo en la llamada "programacin de sistemas". Recordemos que el C fue creado para este tipo de programacin. Este modificador le pide al compilador que almacene la variable en un registro de la maquina, que es el lugar ms eficiente para guardar las variables. Esto se hace porque el trabajo con los registros del procesador es mucho ms r pido que el trabajo con la memoria central. Hay dos detalles importantes: normalmente no hay muchos registros libres en el procesador, pues el compilador los usa para otros propsitos. Entonces el modificador register es mas bien un ruego que una orden. Otro aspecto es que muchos compiladores realizan trabajos de optimizacin, que son modificaciones en el cdigo que generan que hace trabajar al programa ms deprisa. Aun as, en rutinas crticas, que deben ejecutarse deprisa se suele usar. El modificador volatile se usa para decirle que el contenido de la variable puede ser modificado en cualquier momento desde el exterior de nuestro programa, sin que podamos evitarlo. Lo que hace el modificador es instruir al compilador para que lea de nuevo el valor de la variable de la memoria cuando tenga que usarlo. Este modificador evita que el compilador no genere cdigo para optimizar la variable. Evidentemente el uso de volatile excluye el uso de register y viceversa. Ambos modificadores, register y voltiles se emplean de manera anloga al modificador auto. Tipos de datos en C Un programa normalmente lo que hace es procesar un conjunto de datos para obtener alguna conclusin de ellos. Pos ejemplo un programa de estadsticas recoge una serie de datos y elabora grficas que nos ayudan a tomar decisiones. En C los datos de una aplicacin se representa haciendo uso de un conjunto de datos predefinidos en el lenguaje. Distinguiremos para empezar tres tipos de datos fundamentales: caracteres, enteros y cadenas de caracteres (en ingles "strings"). El tipo de datos carcter consiste de un nico carcter y se suele representar por su carcter en cdigo ASCII situado entre apstrofes. Por ejemplo: 'p' /* la letra p minscula */ '1' /* el numero 1 */ ' ' /* el carcter en blanco */ Hay otras formas de representar caracteres, que se emplean cuando es un carcter que no se puede introducir directamente desde el teclado. Para ello debemos conocer su cdigo ASCII. Para representar el carcter de numero ascii 27, (el cdigo para el carcter ESCAPE), basta colocar el numero ascii en el sistema octal precedido de la barra atrs, y todo ello entre apstrofes, tal y como hacemos para los dems caracteres: '\27**' /* representa el cdigo ESCAPE, de ASCII 27 */ En C hay algunos caracteres especiales que se usan frecuentemente. Estos caracteres tiene una representacin especial. Algunos de ellos son:

31

'\n' /* carcter nueva lnea */ '\r' /* retorno de carro (carriage return) */ '\t' /* tabulador */ El siguiente tipo de datos es el entero. Consiste en un nmero sin parte decimal, aunque puede tener signo. Generalmente con el tipo de datos entero no representamos nmeros muy grandes. son ejemplos: 0 124 -2000 El tipo de datos entero nos permitir hacer operaciones aritmticas, como la suma y la multiplicacin. El tipo de datos entero es quizs el ms importante de todos los tipos de datos, y muchas veces es el tipo d datos por defecto, es decir, cuando el compilador se encuentre con un dato y no le hayamos dicho cual es su tipo supondr que es un entero. Esto se ve claramente con las funciones, ya que siempre devuelven un valor, que por defecto es un entero. El ltimo tipo de datos importante en C es la cadena de caracteres. Est formada por un conjunto de caracteres encerrados entre comillas. Podemos usar todos los caracteres del conjunto ASCII, incluso los especiales. Los caracteres normales se incluyen entre las comillas tal cual, sin necesidad de apstrofes, y los especiales se incluyen utilizando la representacin del C. Por ejemplo: "Hola, mundo\n" En este ejemplo observamos la cadena de caracteres "Hola, mundo", a la que hemos aadido un carcter de retorno de carro al final. El motivo de ello es que ms adelante cuando la imprimamos el carcter de retorno de carro \n actuar como un comando que obligar al cursor a avanzar una lnea y situarse al principio de la siguiente. Los tipos de datos carcter, entero y cadena no son los nicos que posee el lenguaje C, pero si los mas importantes. Como avance podemos decir que tambin posee el tipo de datos en coma flotante, que es el que usan las calculadoras cientficas, varios tipos da datos enteros, de diferente tamao, el tipo de datos entero sin signo, un tipo de datos cientfico de mayor precisin, y otros tipos de datos que sirven para fabricarnos un tipo de datos a medida de nuestra necesidad: arreglos, estructuras y uniones. Pero esto es un tema ms avanzado. Tipos de enteros El tipo de datos entero admite varias variantes, que le permiten representar cantidades ms grandes. Aunque el tamao de los enteros depende de la implementacin con la que estemos trabajando, suele ser habitual que tenga un tamao de 16 bits. Por ello el entero ms grande que se puede representar suele estar en torno al 32000. Cuando queremos alcanzar un nmero mas alto y no nos importa el signo le podemos pedir al compilador que el tipo de datos sea "sin signo". Esto se hace con la palabra reservada unsigned. As el tipo de datos entero sin signo se comporta como un nuevo tipo de datos. Podemos definir variables del tipo unsigned int, al igual que lo hacamos con enteros normales. Normalmente cuando nos referimos al tipo de datos sin signo nos estamos refiriendo al tipo entero sin signo.

32

Por ello cuando definamos un entero sin signo no har falta escribir la palabra reservada int. Un ejemplo de definicin con inicializacin de un unsigned es: unsigned u = 40000; Podramos tambin haber escrito unsigned int, pero no suele ser habitual. Si trabajamos con enteros de 16 bits, el nmero 40000 no cabria en un entero normal. Otro tipo de datos til es el entero largo, que anlogamente al entero sin signo se representa con la palabra reservada long. Las palabras long y unsigned son modificadores de tipo, ya que actan sobre uno de los tipos incorporados. Por ejemplo: long largo = 100000; define una variable de tipo entero largo. Hay ms modificadores de tipo. El modificador short hace referencia a un entero corto, de pequeo tamao. Hay que tener en cuenta que el tamao de cada tipo de datos depende del sistema. En C estndar un entero corto tiene al menos 16 bits. Muchas veces el entero corto y el entero coinciden. En los sistemas basados en microprocesadores de 32 bits suele ser frecuente encontrar enteros de 32 bits de anchos, por lo que no coinciden el entero corto y el entero. Adems tambin podemos definir enteros cortos y largos sin signo. Por ejemplo: unsigned short int u = 1; unsigned long l = 1000000; El signo de los caracteres Dependiendo del sistema el tipo de datos carcter puede tener signo o no. Normalmente es una cuestin que no posee relevancia, pero por si necesitsemos que el tipo de datos carcter lleve o no signo, podemos aplicarle dos modificadores: unsigned para caracteres sin signo, y signed para caracteres con signo. Tipos de datos en coma flotante Para representar nmeros decimales el C posee dos tipos de datos: nmeros en coma flotante con simple y doble precisin. El tipo de datos float corresponde al nmero de simple precisin. El tipo double representa a los de doble precisin. Expresiones Las expresiones son sentencias que tras realizar determinada una accin devuelven un resultado. En C la mayora de las expresiones devuelven un entero. Hay tres tipos de expresiones: Valores por la izquierda o lvalue (del ingles left value). Al evaluarlas devuelven la direccin de un cierto dato, que nos permitir acceder a l para inicializarlo, modificarlo, etc. Se llaman as porque son las expresiones que normalmente se colocan en el lado izquierdo del = en una expresin de asignacin. Suelen ser nombres de variables, elementos de un arreglo, etc.

33

Valores por la derecha o rvalue (del ingles right value). Al evaluarlas obtenemos un dato de cierto tipo. Normalmente son las expresiones que se colocan a la derecha del = en la asignacin. Ejemplos de ellos son los contenidos de las variables, los datos contenidos en los arreglos y los valores constantes. Llamada a una funcin. Si la funcin no es de tipo void, al llamarla devolver un dato. Este dato puede ser usado como una expresin del tipo valor por la derecha. Tipos de expresiones El primer tipo a considerar dada su importancia es la asignacin. La sintaxis de una asignacin es lvalue = rvalue; Para evaluar una asignacin el compilador evala el lado derecho. El dato que obtiene es entonces cargado en la direccin que resulta de evaluar el lado izquierdo. Por ejemplo: i = 1; introduce el entero 1 en la direccin de la variable i. Como toda expresin la asignacin devuelve un valor, que es el mismo que se carga en la variable. Esto puede ser usado para inicializar varias variables con el mismo valor. Por ejemplo, si tenemos tres variables enteras i, j y k: i = j = k = 0; las inicializa a las tres a 0. Observar que una misma variable puede estar en ambos lados de una asignacin: i = i + 1; incrementa la variable i en una unidad. El segundo tipo de expresiones son los operadores. Hay varios tipos de operadores: -operadores aritmticos: Son el +, - , *, % y /, que realizan las operaciones aritmticas bsicas. Estas expresiones tienen de sintaxis: rvalue operador rvalue Los operadores aritmticos son: + el operador de suma - el operador de resta * el la multiplicacin / la divisin entera % el resto de la divisin (operador modulo). Por ejemplo:

34

i = a + 1; j = b * 4 + 10 / 2; Operadores de incremento y decremento. Sirven para incrementar una cierta variable. Admiten cuatro posibles combinaciones: ++lvalue incrementa el contenido de lvalue y devuelve el contenido nuevo --lvalue decrementa el contenido de lvalue y devuelve el contenido nuevo lvalue++ incrementa el contenido de lvalue y devuelve el valor que contena antes de incrementarlo. lvalue-- decrementa el contenido de lvalue y devuelve el valor que contena antes de decrementarlo. Por ejemplo: i = j++; /* carga en i el valor de j y */ /* luego incrementa j */ i = ++j; /* incrementa j y luego carga su valor en i */ Estos operadores son muy usados en las variables de los bucles y con los punteros. El tipo de datos que devuelven es el del lvalue. Operadores de comparacin. Son: < "menor que" > "mayor que" <= "menor o igual que" >= "mayor o igual que" == "igual que" != "no igual que" La sintaxis para estas expresiones son: rvalue operador rvalue El valor que devuelve es de tipo entero: devuelve un 0 si el resultado de la comparacin es falso y un valor distinto de 0 si el resultado de la comparacin es verdadero. En C el cero se toma como valor falso, y cualquier valor diferente del cero es verdadero. El valor concreto empleado para

35

representar el valor verdadero es irrelevante, y normalmente depende del sistema empleado. Cuando lo que comparamos son caracteres, se compara realmente su cdigo ASCII. Por ejemplo: 1 > 2 devuelve un valor verdadero 1 == 1 devuelve un valor verdadero '1' == 1 devuelve falso 'a' < 'b' es verdadero Operadores lgicos Realizan las operaciones lgicas habituales en el lgebra de Bool. Realizan el AND (Y lgico), el OR (O lgico) y el NOT (negacin lgica). Son: && AND (Y lgico) || OR (O lgico) ! NOT (negacin lgica Los dos primeros son operadores binarios y el tercero es un operador unario. Su sintaxis es: expresion1 && expresion2 expresion1 || expresion2 !expresion1 El resultado de evaluar la expresin AND es verdadero si ambos son verdaderos, y falso en caso contrario. El resultado de evaluar la expresin OR es verdadero si alguna o las dos expresiones es verdadera. Es falsa si las dos expresiones son falsas. El resultado de la expresin NOT es falso si la expresin es verdadera, y verdadero si la expresin es verdadera. Para evaluar estos operadores se toma como verdadero un valor de la expresin distinto de 0 y como falso un valor 0. Control del flujo del programa En C las sentencias se ejecutan sucesivamente una tras otra. Esto define un camino o direccin segn la cual se va desarrollado el programa. Sin embargo, habr momentos en que el programa deba ejecutar determinadas partes dependiendo del estado en el que se halle el programa o de las variables externas. Esto permitir modificar el orden de la ejecucin para adaptarse al estado del programa y bifurcar hacia nuevas subrutinas cuando se cumplan ciertas condiciones, que el programador fijar de antemano. La sentencia if La primera sentencia de control es la sentencia if. Admite dos tipos de sintaxis: if (expresin1)

36

sentencia1; o tambin: if (expresin1) sentencia1; else sentencia2; Esta sentencia es equivalente a la que poseen la mayora de lenguajes de programacin y sirve para bifurcar en un punto de programa. la sentencia if permite tomar decisiones al programa. En su primera forma la sentencia1 slo se ejecuta si el resultado de evaluar la expresin1 es verdadero (distinto de cero). En la segunda forma tenemos dos posibilidades: si al evaluar la expresin1 el resultado es verdadero se ejecuta la sentencia1, pero si el resultado es falso se ejecuta la sentencia2. En cualquier caso slo una de las dos sentencias se ejecuta. Por ejemplo: if (numero1 == 1) puts("la variable numero1 vale 1"); else puts("la variable numero1 no vale 1"); Tras evaluarse la expresin if y ejecutarse la sentencia adecuada, el programa continua con la lnea siguiente a la de la ultima sentencia del if. Para la sentencia if vale como expresin cualquier expresin v lida en C, incluso las asignaciones y llamadas a funciones. El caso en que la expresin es una asignacin suele ser sorprendente, ya que en la mayora de los lenguajes este tipo de expresiones no es valido. Como sentencia vale cualquier tipo de sentencia v lida en C, entre ellas la propia sentencia if. En este caso hablaremos de sentencias if anidadas. Por ejemplo: if (num > 0) if (num == 1) puts("num es igual a 1") else puts("num es mayor que 1) else puts("num es menor que 1"); Cuando hay dos if anidados y a continuacin hay un else, este else pertenece al ultimo if. As en el caso anterior el primer else corresponde al segundo if. Si queremos que un else pertenezca al primer if de un if anidado deberemos encerrar al segundo entre parntesis. Por ejemplo:

37

if (num > 0) { if (num == 1) puts("num es igual a 1"); } else puts("num es menor que 0"); Cuando necesitamos ejecutar varias sentencias que depende de un if, utilizaremos la sentencia de tipo bloque de sentencias. Un bloque de sentencias es un grupo de sentencias encerradas entre llaves { y }. Por ejemplo: if (num >= 0) { printf("num %d\n"); if (num == 0) puts("num 0"); if (num >= 1) puts("num mayor o igual a 1"); } El bucle while Un bucle es un conjunto de sentencias que se ejecutan repetidamente hasta que se alcanza una condicin de fin de bucle o condicin de salida. El bucle while es el tipo de bucle ms sencillo. En su modo ms simple se escribe: while (expresin1) sentencia1; El bucle while comienza por evaluar la expresin1. Si es cierta se ejecuta la sentencia1. Entonces se vuelve a evaluar la expresin1. De nuevo si es verdadera se vuelve a ejecutar la sentencia1. Este proceso continua hasta que el resultado de evaluar la expresin es falso. Por esto se le llama a esta expresin la condicin de salida. Por ejemplo: int variable = 10; while (variable) printf("la variable vale %d\n", variable--);

38

En este caso se imprimir el valor de la variable hasta que se llegue a 1. Normalmente en las sentencias del bucle while se coloca alguna instruccin que modifique la expresin de control. Lo ms habitual es utilizar un bloque de sentencias en vez de una sentencia en vez de una sentencia nica. Por ejemplo: int variable = 10; while (variable) { printf("valor de la variable %d\n", variable); printf("valor tras decrementar la variable %d\n", variable); } El bucle do-while La sintaxis de este bucle es: do sentencia1; while (expresin1); Su funcionamiento es anlogo el del bucle while, salvo que la expresin de control se evala al final del bucle. Esto nos garantiza que el bucle do-while se ejecuta al menos una vez. Es menos habitual que el bucle while. Podemos incluir dentro del bucle un grupo de sentencias, en vez de la sentencia1. Este es el nico bucle que no necesita llaves para encerrar un grupo de sentencias. Por ejemplo: char c = '9'; do printf("numero actual %c\n", c); --c; /* ahora la decrementamos como si fuera entera */ while (c >= '0'); El bucle for La sintaxis del bucle for es: for (inicio, control, incremento) sentencia1;

39

Este bucle se utiliza para realizar una accin un nmero determinado de veces. Est compuesto de tres expresiones: la de inicio, la de control y la de incremento, y una sentencia. Su versin ms sencilla es: for (i =0; i < 10; i++) printf("i vale %d\n", i); Esta versin del bucle imprime un mensaje en la pantalla mientras que no se alcance la condicin de salida, i == 10. El funcionamiento del bucle for es el siguiente: Primero se ejecuta la expresin de inicio. Normalmente esta es una expresin de asignacin a una variable, que le da un valor inicial. Luego se comprueba la expresin de control. Si esta expresin es verdadera se ejecuta la sentencia, o el grupo de sentencias. Si la expresin es falsa el bucle finaliza. Tras ejecutarse la sentencia se evala la expresin de incremento. Habitualmente lo que hace esta expresin es incrementar la variable de control. A continuacin se vuelve al segundo paso. El bucle finaliza cuando la expresin de control es falsa. En un bucle for podemos omitir la expresin de inicio, por ejemplo si sabemos que la variable ya esta inicializada: int i = 0; for ( ; i <= 10; ++i) printf("%d\n", i); Tambin podemos omitir la expresin de incremento. Esto es habitual cuando la variable de control ya se modifica dentro del bucle. Por ejemplo: int i= 10; for ( ; i < 10; ) printf("i = %d\n", i++); El bucle for es equivalente a un bucle while escrito del siguiente modo: inicio; while (control) { sentencia1;

40

incremento; } Este bucle while puede servirnos para salir fcilmente de dudas al escribir un bucle for, ya que se ve claramente el orden de ejecucin de las expresiones y sentencias dentro del bucle for. Como se ve, en cada pasada del bucle for se sigue el orden: evaluacin de control, ejecucin de sentencia1 y evaluacin de incremento. Las sentencias break y continue Hay veces en que interesa romper un bucle en una determinada posicin, para ejecutar una nueva pasada del bucle o para finalizar su ejecucin Esto suele ser habitual cuando el bucle tiene una gran complicacin o cuando necesitamos salir "por las malas" de l. Esto ultimo suele ser frecuente cuando en el bucle se producen "condiciones de error". Para realizar estos dos tipos de salto disponemos de dos sentencias, la sentencia break y la sentencia continue. La sentencia break rompe la ejecucin de un bucle o bloque de instrucciones y continua en la instruccin que siga al bucle o bloque. Por ejemplo: int a = 10; while (1) { if (a-- <= 1) break; printf("%d\n", a); } Aunque en apariencia este es un bucle sin fin, ya que la condicin con while (1) siempre es cierta, este bucle se acabar cuando la variable a valga 1. El bucle simplemente decrementa la variable e imprime su valor. La sentencia continue rompe la ejecucin habitual del bucle y procede a evaluar de nuevo la expresin del bucle. Acta como si se saltase al final del bloque de un bucle. Por ejemplo: int a = 1; while (a < 10) { printf("%d\n", a); if (a==7) continue; } La sentencia de seleccin mltiple switch

41

Esta sentencia sirve para agrupar varias sentencias if en una sola, en el caso particular en el que una variable es comparada a diferentes valores, todos ellos constantes, y que realiza acciones si coincide con ellos. Su sintaxis es: switch (control) { case exp1: sent1; break; case exp2: sent2; break; default sent0; break; } Su sintaxis es ms complicada que la de anteriores bucles, ya que agrupa un mayor nmero de acciones y posibilidades en una sola sentencia. El modo de funcionamiento es el siguiente: Primero se evala la expresin de control. A continuacin se compara con la expresin de la primera etiqueta case. Si son iguales se ejecuta la sentencia1. Luego se vuelve a comparar la expresin de control con la etiqueta del segundo case. De nuevo , si son iguales se ejecuta la sentencia2. Se repite el proceso hasta agotar todas las etiquetas case. Si al llegar a la etiqueta default no se ha ejecutado ninguna otra sentencia. Esta es la accin por defecto. La etiqueta default es opcional. Si no la ponemos el programa simplemente salta a la lnea siguiente. Hay que tener cuidado con un aspecto de este bucle. Cuando una expresin de una etiqueta case es igual a la sentencia de control, se ejecutan todas las sentencias que sigan hasta que se alcance una nueva etiqueta case, y luego se vuelve a comparar la expresin de control. Este mecanismo tiene una ventaja y un inconveniente. La ventaja es que no necesitamos encerrar entre llaves el grupo de sentencias a ejecutar para cada etiqueta. El inconveniente es que al agotar las sentencias de una determinada etiqueta la sentencia switch prosigue con la siguiente etiqueta case. Habitualmente lo que se pretende es que tras ejecutar el grupo de sentencias se finalice el switch. Para evitar el que se ejecuten ms sentencias habitualmente se acaba cada grupo de sentencias con una sentencia break. La sentencia break pasa entonces la ejecucin a la siguiente lnea de programa que prosiga al bucle switch. Tambin se permite poner etiquetas mltiples para un mismo grupo de sentencias. Si dejamos una etiqueta case sin sentencias a ejecutar entonces se asocia a la siguiente etiqueta. Esto es til para ejecutar una misma accin para distintos valores de la expresin. Funciones Una funcin es una rutina o conjunto de sentencias que realiza una determinada labor. En C todas las funciones devuelven un valor, que por defecto es un entero. las funciones admiten argumentos, que son datos que le pasan a la funcin las sentencias que la llaman. Definicin de una funcin La sintaxis habitual en la definicin de una funcin es:

42

tipo identificador(lista_de_argumentos) { /* bloque de cdigo */ } Donde: tipo es el tipo de datos devuelto por la funcin identificador es el nombre de la funcin. Debe ser un identificador valido. lista_de_argumentos es una lista de variables, separadas por comas, que conforman los datos que le pasamos a la funcin. El tipo y la lista de argumentos son opcionales. Si omitimos el tipo, la funcin por defecto devolver un entero. Muchas veces el valor devuelto por la funcin es ignorado en el programa. La lista de argumentos es tambin opcional. Un ejemplo es la funcin main(), que en principio no tiene argumentos. Podemos escribir como ejemplo: hola() { printf("hola\n"); } Que simplemente es una funcin que cuando es llamada imprime en pantalla un mensaje de saludo. Cuando el programa al ejecutarse alcanza la llave de cierre '}' de la funcin, esta finaliza y devuelve el control al punto del programa que la llam. Retorno de valores Cuando la funcin finaliza hemos dicho que se devuelve un valor. Este valor en principio no est definido, es decir, puede devolver cualquier cosa. Para obligar a la funcin a retornar un determinado valor se utiliza la sentencia return, seguida del valor a retornar. Como todas las sentencias en C se debe acabar con un ';'. Por ejemplo: lista() { return 1;

43

} devuelve el entero 1 cada vez que es llamada. En C podemos devolver cualquier tipo de datos de los llamados escalares. Los tipos de datos escalares son los punteros, tipos numricos y el tipo carcter. En C no se pueden devolver arreglos ni estructuras. Paso de parmetros a una funcin Utilizando la lista de argumentos podemos pasar par metros a una funcin. En la lista de par metros se suele colocar un conjunto de identificadores, separados por comas, que representar cada uno de ellos a uno de los par metros de la funcin. Observar que el orden de los par metros es importante. Para llamar a la funcin habr que colocar los par metros en el orden en que la funcin los espera. Cada par metro puede tener un tipo diferente. Para declarar el tipo de los par metros aadiremos entre el parntesis ')' y la llave '{' una lista de declaraciones, similar a una lista de declaraciones de variables. Es habitual colocar cada par metro en una lnea, tabulados hacia la derecha. Asi: imprime(numero, letra) int numero; char letra; { printf("%d, %c\n", numero, letra); } es una funcin que admite dos variables, una entera u otra de tipo carcter. Paso de par metros por valor y por referencia En los lenguajes de programacin estructurada hay dos formas de pasar variables a una funcin: por referencia o por valor. Cuando la variable se pasa por referencia funcin puede acceder a la variable original. Este enfoque es habitual en lenguajes como el Pascal. En C sin embargo todos los par metros se pasan por valor. La funcin recibe una copia de los par metros y variables, y no puede acceder a las variables originales. Cualquier modificacin que efectuemos sobre un par metro no se reflejar en la variable original. Esto hace que no podamos alterar el valor de la variable por equivocacin. Sin embargo, en determinadas ocasiones necesitaremos alterar el valor de la variable que le pasamos a una funcin. Para ello en el C se emplea el mecanismo de los punteros, que se ve mas adelante. Declaracin y comprobacin de tipos

44

Al igual que para las variables, cuando una funcin se va a usar en un programa antes del lugar donde se define, o cuando una funcin s define en otro fichero (funciones externas), la funcin se debe declarar. La declaracin de una funcin consiste en especificar el tipo de datos que va a retornar la funcin. Esto es obligatorio cuando vamos a usar una funcin que no devuelve un entero. Adems en la declaracin se puede especificar el nmero de argumentos y su tipo. Una declaracin tpica de funcin es: tipo identificador( lista_de_argumentos_con_tipo ); Esto avisa al compilador de que la funcin ya existe, o que la vamos a definir despus. La lista de argumentos con tipo difiere de la lista de argumentos antes presentada en que el tipo de cada argumento se coloca dentro de la lista, antes de su correspondiente identificador, como hacamos en la definicin de variables. Por ejemplo: char print(int numero, int letra); declara una funcin que devuelve un carcter y tiene dos par metros, un entero y un carcter. La lista de argumentos permite al compilador hacer comprobacin de tipos, ya que el tipo y numero de argumentos debe coincidir en la declaracin, definicin y llamada a una funcin. Este tipo de especificacin del tipo de argumentos tambin se puede emplear en la definicin de las funciones, aunque lo contrario no es posible. Asi: char print(int numero, int letra) { printf("%d, %c\c", numero, letra); } es otra definicin v lida para la funcin print que hemos empleado. Arreglo Los arreglos son conjuntos de datos de un mismo tipo, el tipo base. A cada uno de los datos de un arreglo le llamaremos elemento de arreglo, y esta designado por un nmero. En C al primer elemento de un arreglo le corresponde siempre el nmero 0, y los dems tienen la numeracin consecutiva. Declaracin de una matriz. Para crear un arreglo de n elementos de un cierto tipo se introduce la lnea: Tipo identificador [n];

45

donde n es una constante de tamao fijo. Si el arreglo es esttico o global el compilador crea el espacio para la matriz al principio del programa, generalmente en el rea de datos. Si es de tipo automtico, reservar el espacio en la pila de datos. Como todos los tipos de datos , un arreglo se puede inicializar. Si el arreglo es esttico, por defecto cada elemento se inicializa a 0. Si es din mico los valores de cada elemento no est n definidos y antes de usarlos los debemos inicializar. Para inicializar un arreglo en el momento de su creacin aadiremos tras el identificador y los corchetes de tamao un = y la serie de valores. Cada valor debe ser una constante v lida para el tipo de datos del arreglo, y cada valor ir separado del valor precedente mediante una coma. Para abrir y cerrar la serie de valores usaremos las llaves. Por ejemplo: int vector [4]={0, 1,2,3 }; char hola[] = { 'h', 'o', 'l', 'a', '\0'}; No podemos dar un nmero de valores mayor al tamao del arreglo , pero si podemos dar menos de los necesarios. El compilador siempre rellenar los dems con ceros. El compilador siempre asignar el primer valor al primer elemento del arreglo, y los dems los asignar consecutivamente. Como siempre acabaremos la lnea con un ;. Acceso a los miembros de un arreglo Para usar un elemento de un arreglo se utiliza el identificador y el nmero de orden del elemento. Al primer elemento siempre le corresponde el nmero 0. Asi printf ("%d", vector[0]) imprimira el contenido del primer elemento del arreglo que definimos antes, que lo habamos inicializado a 0. En el lenguaje C no se hace ningn control acerca de si intentamos leer un nmero de elemento mayor que el ultimo nmero del arreglo. Esto es lo que llama sobrepasar el lmite, y el compilador deja al programador la tarea de preocuparse por los lmites del arreglo. Si los sobrepasamos, pueden ocurrir resultados imprevisibles (normalmente modificaremos alguna otra variable del programa, o incluso bloquearemos el programa). Tamao de los arreglos El tamao de los arreglos es siempre constante y se especifica al crear el arreglo. Hay dos formas de especificar el tipo: ndice dndoselo explcitamente al compilador o hacindolo implcitamente. El primer modo es el ya sealado anteriormente . Para dar un tamao al arreglo simplemente indicamos el nmero de elementos entre los corchetes. Este es el modo ms habitual de dar el tamao, sobre todo si no se va a inicializar en el momento de su creacin. El otro modo consiste en hacer que sea el compilador el que decida el tamao. Esto se hace cuando en la creacin del arreglo le damos una lista de valores iniciales . En este caso si omitimos el tamao del arreglo el compilador ajusta el tamao del arreglo segn el nmero de elementos que le demos para inicializar el arreglo. Por ejemplo: int vetor[] = { 1, 2, 3, 4, 5, 6 }; Este ultimo mtodo es muy cmodo, sobre todo si el arreglo va a tener un tamao pequeo y todos los valores iniciales son constantes.

46

Cadenas de caracteres Hay un tipo de arreglos de especial importancia; las cadenas de caracteres. Una cadena de caracteres es un arreglo de caracteres que acaba con el carcter nulo. En C siempre las cadenas de caracteres acaban con este carcter. Esto se hace as por dos motivos: el tamao de la cadena no tiene un lmite prefijado: puede ser tan grande como lo permita la memoria. Las operaciones de manipulacin de cadenas de caracteres se simplifican bastante. El inconveniente es que para conocer el tamao de la cadena normalmente necesitamos recorrerla con un bucle, aunque esto suele hacerse rpidamente. Para inicializar una cadena de caracteres basta crear un arreglo de caracteres, en el que no necesitamos definir el tamao e inicializarlo con la cadena de caracteres entrecomillada. Observar que el compilador siempre aade un carcter nulo al final, por lo que el tamao del arreglo es una unidad mayor del aparente. Por ejemplo: char cadena[] = "abracadabra" /* cadena de 12 caracteres*/ Los caracteres especiales como el tabulador \t y el retorno de carro \r se almacenan como un nico carcter. El carcter nulo est representado por un 0. Esto nos permitir utilizar comparaciones con este carcter en los bucles que recorren cadenas de caracteres . Arreglos multidimensionales En C se pueden construir arreglos de arreglos , es decir tipos de arreglos tipos de arreglos cuyos elementos son a su vez arreglos. Dado que ahora necesitaremos un ndice para situarnos dentro del arreglo principal y otro ms para movernos dentro de cada uno de los nuevos arreglos , diremos que los arreglos de arreglos poseen dos dimensiones. A un arreglo de dos dimensiones se le suele llamar matriz, y a un arreglo de una dimensin, vector. Las matrices son tipos de datos ampliamente usados en matemticas. Normalmente diremos que un ndice representa a las filas de la matriz y otro a las columnas. Otro uso habitual de las matrices es para representar tablas de valores. Para crear una matriz de enteros, es decir, un arreglo de arreglos de enteros, lo haremos de modo anlogo a cuando crebamos un arreglo, salvo que ahora aadiremos el nuevo ndice entre corchetes. Por ejemplo: int matriz[8][9]; declara una matriz de 8 filas por 9 columnas, o 9 por 8 columnas, segn queramos representar. La eleccin de cual ndice representa las filas y cual las columnas es arbitrario. Podemos usar la norma habitual en matemticas: el de la izquierda representa filas y el de la derecha columnas. Acceso a los miembros de una matriz Para acceder a un miembro concreto de una matriz, siguiendo el convenio anterior, colocaremos su nmero de fila y de columna entre corchetes. Por ejemplo: printf("%d\n", matriz[1][2]); imprime el elemento correspondiente a la fila 1, columna 2. Para inicializar las matrices disponemos ahora de dos formas:

47

-incluir todos los elementos de la matriz entre llaves, ordenados por filas y por columnas. Para ello se hace del siguiente modo: tras el signo igual en la definicin abrimos una llave. A continuacin colocamos cada columna de valores, todos ellos separados por sus comas. Cada columnas de valores debe ir encerrada entre llaves, separando una columna de otra por comas. Al final cerramos la llave que habamos abierto al principio. Por ejemplo: int matriz[2][3] = { {1, 2, 3}, {4, 5, 6} }; define una matriz de 2 filas con 3 columnas por fila. -incluir la lista completa de elementos de la matriz entre llaves, separados por comas, uno tras otro sin separar las columnas. Para colocar todos los valores correctamente necesitamos saber como accede el compilador a los elementos de una matriz. El compilador supone que en la memoria est n guardados los elementos ordenados por filas, es decir, est n juntos todos los datos correspondientes a una fila. As la matriz la podamos haber inicializado con: int matriz[2][3] = { 1, 2, 3, 4, 5, 6}; y obtendremos el mismo resultado de antes. Vemos que los tres primeros elementos corresponden a la fila 1, y los tres siguientes son los de la fila 2. Cuando el compilador necesita acceder al elemento de fila i y columna j hace el siguiente calculo: multiplica (i - 1) por el nmero de columnas, y al resultado le suma (j - 1). Con ello accede a la matriz como si fuese de una sola dimensin. En nuestro caso el elemento matriz[1][3] ser el que ocupe el lugar 0 * 3 + 2, es decir, el tercer elemento de un arreglo unidimensional. Los dos unos que aparecen restando se deben a que el compilador empieza a contar los elementos de la matriz desde el 0. Como cabe esperar se pueden formar arreglos de cualquier dimensin. Por ejemplo un arreglo de tres dimensiones podra ser: tresdim[10][15][20]; Esto podra representar un conjunto de 10 tablas, cada una de 15 filas y 20 columnas por fila. Para inicializarla procederamos de un modo anlogo al caso bidimensional. Punteros Cuando queramos pasar un dato a una funcin normalmente pasamos una copia del dato. Esto es sencillo de hacer y r pido, siempre que no queramos modificar mediante la funcin el dato original o que el dato sea pequeo. Otro enfoque consiste en decirle a la funcin donde encontrar los datos. Para ello le pasamos a la funcin una direccin. Con ella la funcin podr acceder a los datos utilizando un puntero. Un puntero es un nuevo tipo de datos, que no contiene un dato en si, si no que contiene la direccin donde podemos encontrar el dato. Decimos que un puntero "apunta" a un dato, pudiendo alterar dicho dato a travs del puntero. Definicin de un puntero Para poder usar punteros y direcciones de datos vamos a introducir dos nuevos operadores. el primero es el operador puntero, que se representa con un asterisco *. el operador puntero nos permite definir las variables como punteros y tambin acceder a los datos. El otro nuevo operador, el operador direccin, nos permite obtener la direccin en la que se halla ubicada una variable en la memoria. Vemos que el operador direccin es el complementario al operador puntero.

48

Para definir un puntero lo primero que hay que tener en cuenta es que todo puntero tiene asociado un tipo de datos. Un puntero se define igual que una variable normal, salvo que delante del identificador colocaremos un asterisco. Por ejemplo: char *pc; /*puntero a carcter */ char *pi; /* puntero a entero */ Normalmente al definir un puntero lo solemos inicializar para que apunte a algn dato. Disponemos de tres formas de inicializar un puntero: Inicializarlo con la direccin de una variable que ya existe en memoria. Para obtener la direccin en la que est ubicada una variable colocamos delante del identificador de la variable el operador direccin &. No se suele dejar espacios entre el signo & y el identificador. Por ejemplo: char *p = &p1; Asignarle el contenido de otro puntero que ya est inicializado: char *p = &p1; char *p2 = p; /* p ya est inicializado */

Inicializarlo con cualquier expresin constante que devuelva un lvalue. Las mas frecuentes son una cadena de caracteres, el identificador de un arreglo, el identificador de una funcin, y otros muchos. Este es un detalle importante: los identificadores de funciones y de arreglos son en si mismos valores por la izquierda (lvalues), por lo que se pueden usar directamente para inicializar punteros. Una forma adicional de inicializarlo es darle directamente una posicin de memoria. Este mtodo no es portable, ya que depende del sistema, pero suele ser muy til en programacin de sistemas, que es uno de los usos fundamentales del C. Un error muy frecuente consiste en no inicializar el puntero antes de usarlo. Este error frecuentemente lo localiza el compilador y avisa de ello. Desreferenciacion de un puntero Una vez que el puntero apunta a un objeto o dato en la memoria podemos emplear el puntero para acceder al dato. A este proceso se la llama desreferenciar el puntero, debido a que es una operacin inversa a obtener la direccin de una variable. Para desreferenciar un puntero se utiliza el operador puntero. Para acceder al dato al que apunta el puntero basta colocar el asterisco * delante del identificador. Como norma de buena escritura no se deja ningn espacio entre el * y el identificador, aunque el compilador lo acepte. Un puntero desreferenciado se comporta como una variable normal. Por ejemplo: int entero = 4, *p = &entero; printf("%d %d \n", *p, entero);

49

Aritmtica de punteros. Un uso habitual de los punteros es para recorrer los arreglos. En efecto, comencemos por crear un arreglo y un puntero al comienzo del arreglo. int arreglo[] ={ 1, 2, 3, 4, 5}; int *p = arreglo; En este momento el puntero apunta al primer miembro del arreglo. Podemos modificar fcilmente el primer miembro, por ejemplo: *p = 5; printf("%d\n", arreglo[0]; Ya que un puntero es una variable tambin, le podemos sumar una cantidad. Sin embargo el resultado no se parece al que obtenemos con variables. Si a un puntero de tipo carcter le sumamos 1 o lo incrementamos, el contenido de la variable puntero es aumentado una unidad, con lo que el puntero a caracteres apuntara al siguiente miembro del arreglo. En principio este es el efecto que necesitaremos. Supongamos que tenemos ahora nuestro puntero a enteros, y apunta al principio del arreglo. Si nuestro sistema necesita dos bytes para representar los enteros, tras incrementar un puntero en una unidad veremos que el contenido de la variable puntero ha aumentado en dos unidades. Esto lo podemos ver utilizando la funcin printf con el modificador %p. En efecto: printf("%p\n", p); /* usamos el puntero anterior */ ++p; printf("%p\n", p; El resultado es que el puntero apunta ahora al siguiente elemento del arreglo. Este modo de incrementar el puntero es muy conveniente pues nos permite recorre un arreglo fcilmente. Para recorrer el arreglo slo tendremos que crear un puntero apuntando al principio del arreglo e irlo incrementando mientras manipulamos los datos del arreglo. Por ejemplo, el siguiente bucle imprime todos los caracteres de una cadena: MP! char *p = "Hola, mundo.\n"; /* la cadena es un lvalue */ while (*p) putchar(*p++); Aqu observamos como se usa el operador incremento con los punteros. Ya que el operador puntero tiene mayor precedencia que el operador incremento, en la expresin *p++ primero se desreferencia el puntero, usndose en con la funcin putchar(), y luego se incrementa el puntero. Por eso no hacen falta los parntesis con este operador. Si quisisemos incrementar el carcter al que apunta el puntero, debemos encerrar entre parntesis al operador puntero y al puntero. Por ejemplo:

50

char *p = "Hola, mundo.\n"; /* la cadena es un lvalue */ ++(*p); /* Aparecer una I */ while (*p) putchar(*p++); Cadenas de caracteres La manipulacin de cadenas de caracteres est implementado en la librera estndar del C. Como habamos definido, una cadena de caracteres es un arreglo de caracteres cuyo ultimo carcter es el carcter nulo '\0'. Para definir una cadena de caracteres basta definir un arreglo de caracteres del tamao conveniente, dejando espacio para el carcter nulo. Por ejemplo: char cadena[100] = "Hola"; La mayora de las funciones de cadenas de la librera estndar comienzan con el prefijo str y se hayan definidas en el fichero de cabecera <string.h>. Las funciones mas importantes de esta librera son: size_t strlen( const char *s); La funcin strlen() devuelve el tamao de una cadena de caracteres, sin incluir el carcter nulo de terminacin. Por ejemplo: printf("numero de caracteres de la palabra hola = %d", strlen("hola"); Necesita un par metro de tipo puntero a carcter y devuelve un size_t, que suele est r definido como entero sin signo. char *strcpy(char *s1, const char *s2); La funcin strcpy() copia la cadena s2 en la cadena s1, incluyendo el carcter de terminacin y devuelve un puntero a s1. Los dos parametros que necesita son punteros a caracteres, y devuelve un puntero a caracteres. Deberemos asegurarnos de que s1 tiene sitio para almacenar la cadena s2. char *strcat(char *s1, const char *s2); La funcin strcat() copia la cadena s2 al final de la cadena s1. Para ello busca el carcter de terminacin de s1 y a partir de all va colocando sucesivamente los caracteres de s2, incluyendo el carcter de terminacin. Los par metros que necesita y que devuelve son del mismo tipo que los de strcpy. Tampoco hace comprobaciones de que exista espacio para la cadena s2, ya que de ello debe asegurarse el programador. char *strchr(const char *s, int c);

51

La funcin strchr() busca el carcter c a lo largo de la cadena s. si lo encuentra devuelve un puntero a la primera posicin del carcter. Si falla la bsqueda devuelve un puntero nulo. La funcin tiene dos par metros, el puntero a la cadena en la que buscar el carcter y el carcter a buscar. Devuelve un puntero a caracteres. int strcmp(const char *s1, const char *s2); La funcin strcmp() compara dos cadenas de caracteres. Para ello compara elementos sucesivos de ambas cadenas hasta que encuentra dos elementos diferentes. Si ambas cadenas son iguales la funcin devuelve un 0. Si el elemento diferente es menor en s1 entonces devuelve un nmero negativo, y si es mayor en s1 entonces devuelve un nmero positivo. para comparar los caracteres la funcin toma los caracteres como enteros sin signo. Al utilizar el cdigo ASCII para representar las cadenas, los nmeros tienen un cdigo menor que las minsculas, y stas que las maysculas. Esta funcin no es muy eficiente cuando se trata de ordenar cadenas en las que aparecen caracteres acentuados o ees, pero es fcil construir una a medida. char *strncat(char*s1, const char *s2, size_t n); La funcin strncat() sirve para copiar un fragmento de la cadena s2 en al final de la cadena s1. Necesita tres par metros, dos punteros a caracteres y un nmero del tipo size_t, generalmente un unsigned. La funcin copia los primeros n caracteres de s2 al final de s1 y luego aade un carcter nulo al final de s1. La funcin devuelve un puntero a carcter que apunta a s1. Estructuras Una estructura es un tipo de datos compuesto por un grupo de datos, cada uno de los cuales puede ser de un tipo distinto. A cada componente de la estructura se le llama campo. Las estructuras tiene su equivalente en otros lenguajes de programacin, como el Pascal, en los registros. Tambin se llaman registros a los grupos de datos en la terminologa de las bases de datos. Definicin de una estructura Para la definicin de estructuras el C dispone de la palabra reservada estruct. Para crear una estructura primero comenzamos por definir el tipo de estructura. Para ello se procede de manera parecida a la definicin de una variable, con algunas modificaciones. Primero colocamos la palabra reservada struct y luego el identificador que dar nombre al nuevo tipo de estructura. Luego abriremos llaves y comenzaremos a definir los campos de la estructura. Cada campo se define como una variable normal, es decir, dando su tipo y un identificador. Vale cualquier definicin de tipo habitual, incluso punteros y estructuras. El identificador servir para designar a cada campo. Cada definicin de campo acabar con un punto y coma, como siempre. Finalizaremos la definicin de la estructura con una llave de cierre y un punto y coma. Por ejemplo: struct fecha { /* para almacenar una fecha */ int dia; char mes[14]; int anyo;

52

}; double real; double imaginario; }; Una vez que hemos definido un tipo de estructura ya podemos definir variables estructuras de dicho tipo. Esto se hace de una forma anloga a la definicin de variables normales, esto es, se pone la palabra reservada struct, el identificador del tipo de estructura y el identificador de la nueva estructura. Por ejemplo: struct fecha fecha_de_hoy; struct complejo x, y; Una estructura tambin se puede inicializar. Para ello se dan los valores iniciales entre llaves, separados por comas, al igual que hacamos son los arreglos. La novedad es que ahora cada dato puede tener un tipo diferente. Por ejemplo: struct fecha fecha_actual = { 12, "Enero", 1900}; struct complejo x = {1, 1}; Hay dos posibilidades ms en la definicin de estructuras. La primera es definir una variable estructura a la vez que se define el tipo de estructura. Para ello hasta dar los identificadores de las nuevas variables estructuras despus de la llave de cierre. Por ejemplo: struct complejo{ double x ; double y; } z1, z2; Adems podemos definir variables estructuras sin tipo especfico. Para ello basta omitir el identificador del tipo de estructura en la definicin de la estructura, dando slo el identificador de la variable estructura. De este modo la nueva variable va asociada al tipo creado. Por ejemplo: struct { int dia; char mes[14]; int anyo; } mi_aniversario;

53

Campos de bits Hay un nuevo tipo de datos que solo se puede usar con estructuras: el campo de bits. Un campo de bits se comporta igual que un entero sin signo, slo que al definir el campo de bits se define el nmero de bits que lo compondr . Por Ejemplo: struct comida { unsigned clase : 2; /* dos bites para el tipo */ unsigned temporada : 1; /* a 1 si es de temporada */ unsigned es_perecedero :1, es_congelado : 1; }; Si el tamao del campo de bits es 0 nos permite alinear el siguiente campo sobre un entero. Hay que tener en cuenta que la alineacin de los campos de bits y de los dems campos la define el compilador. Hay que tener en cuenta que no se puede obtener la direccin de un campo de bits. El C estndar define la macro offsetof() para calcular el desplazamiento de un campo de una estructura desde el principio de la misma. El uso de campos de bits permite empaquetar informacin pequea eficientemente dentro de una estructura. Su uso est bastante extendido en la programacin de sistemas. Paso de estructuras a las funciones En C estndar esta permitido pasar una estructura como argumento de una funcin, devolverla como resultado de una funcin, asi como asignar una estructura completa a una funcin. Esto no era posible en los primeros compiladores de C. No est permitido comparar estructuras completas. Por ejemplo: struct complejo { double x, y; } z1, z2, suma; struct complejo suma_complejos( struct complejo j1, struct complejo j2); suma = suma_complejos(j1, j2); /*esta funcin se definira aparte */ if (j1 > j2) puts("es mayor j1"); /*esto no est permitido */

54

Sin embargo esto no es lo ms eficiente. Como las estructuras tienen a menudo un tamao considerable suele ser conveniente pasarlas a travs de punteros. Para obtener la direccin donde se halla una estructura usaremos el operador direccin &, como con una variable normal. Para definir un puntero a una estructura usaremos el operador *. Por ejemplo: struct complejo j; struct complejo *pj = &j; Acceso a los campos de una estructura. Para acceder individualmente a cada campo de una estructura se usa el operador punto '.'. Para ello colocamos el operador de la estructura , un punto y el identificador del campo. Cada campo de una estructura designado mediante este operador se comporta como si de una variable del mismo tipo que el campo se tratase. Podemos realizar todas las operaciones habituales de las variables: asignacin, uso de punteros, llamadas a funciones con el campo como par metro: struct complejo z = {1,1}; printf("z vale %f, i%f\n", z.x, z.y); z.x = z.y = 0; Para acceder a los campos de una estructura a travs de un puntero tenemos un nuevo operador, el operador puntero a campo -> (un guin seguido de un signo "mayor que"). Para acceder a un campo de una estructura a travs de un puntero a ella basta poner el identificador del puntero, el nuevo operador puntero a campo y luego el identificador del campo. Por ejemplo: struct complejo z = {1, 1}; struct complejo *pz = &z; printf("%f, i%f\n", pz->x, pz->y); Podramos haber usado la notacin habitual para punteros y el operador punto de acceso a campos, pero nos aparece un nuevo inconveniente: ya que el operador punto tiene mayor precedencia que el operador puntero tendramos que encerrar el identificador y el operador puntero entre parntesis, para luego aplicarles el operador punto. En efecto, las siguientes expresiones son equivalentes: pz->x = 1; (*pz).x = 1; Si omitimos los parntesis se hara lo siguiente: pz sera considerado como una estructura, luego el operador punto dara el campo x de la estructura y el operador puntero intentara acceder a donde apuntase el campo x. Vemos que hay dos errores, ya que ni pz es una estructura ni el campo x es un campo puntero. Esta expresin sera v lida si tuvisemos una estructura del tipo: struct elemento { int tipo;

55

char *nombre; } uno = {1, "estructura"}; printf("%c\n",*uno.nombre); /*imprime la primera letra de nombre */ Uniones Una unin es un tipo de datos formado por un campo capaz de almacenar un solo dato pero de diferentes tipos. Dependiendo de las necesidades del programa el campo adoptar uno de los tipos admitidos para la unin. Para definir uniones el C utiliza la palabra reservada unin. La definicin y el acceso al campo de la unin es anlogo al de una estructura. Al definir una variable de tipo unin el compilador reserva espacio para el tipo que mayor espacio ocupe en la memoria. Siempre hay que tener en cuenta que slo se puede tener almacenado un dato a la vez en la variable. En C es responsabilidad del programador el conocer que tipo de dato se est guardando en cada momento en la unin. Para definir una unin seguimos la misma sintaxis que para las estructuras. Por ejemplo: unin dato_num { int num1; float num2; } dato; define una unin en la que el campo puede ser de tipo entero o de tipo nmero con coma flotante. Las uniones normalmente se emplean como campos en las estructuras. Para llevar la cuenta del tipo de datos almacenado en la unin normalmente se reserva un campo en la estructura. Por ejemplo: struct dato_num { int tipo; unin { float simple; double doble; }dato; }; Las uniones son especialmente tiles para la realizacin de registros de bases de datos, ya que permiten almacenar informacin de diferentes tipos dentro de los registros. En programacin de

56

sistemas es usual encontrarlas dentro de las estructuras de datos de las rutinas, ya que permiten una gran flexibilidad a la hora de almacenar informacin. Tipos de datos enumerados Gestin de la memoria En C se pueden almacenar variables y estructuras de datos en tres lugares diferentes: la pila para las variables automticas, la memoria global para las variables globales y la memoria dinmica. La pila es una seccin de la memoria que es gestionada automticamente por el compilador y es donde se almacenan las variables locales. El compilador crea el espacio para la variable en tiempo de ejecucin y libera el espacio ocupado cuando la variable deja de usarse (cuando salimos del mbito o funcin en que se declar). Por eso reciben el calificativo de automticas. Las variables estticas globales se almacenan en la seccin de la memoria llamada memoria global. El compilador tambin se encarga de proporcionarles espacio a estas variables, pero lo hace en tiempo de compilacin, por lo que el espacio queda reservado durante toda la ejecucin del programa. Generalmente los valores iniciales de las variables globales son asignados en tiempo de compilacin. El ultimo tipo de memoria es el almacenamiento libre (free store en ingles) conocido habitualmente como la memoria dinmica (heap en ingles). El compilador no se hace cargo de ella, sino que es el programador el que solicita su uso a travs de funciones predefinidas que se encargan de manejar la memoria dinmica. Estas funciones son malloc, calloc, realloc y free, y se definen en el fichero de cabecera <stdlib.h>. Estas funciones generalmente trabajan solicitando directamente porciones de memoria al sistema operativo. Su uso es muy sencillo y generalmente se usan a travs de punteros. La funcin malloc() nos permite solicitar memoria al sistema. Posee un nico argumento: el tamao del espacio que queremos reservar. En C el tamao de un tipo de datos es el nmero de caracteres que ocupa en la memoria, ya que el tipo carcter tiene un tamao de un byte generalmente. El tamao de un tipo de datos tambin se puede calcular con el operador sizeof. La funcin malloc se define como: void * malloc(size_t longitud); y su nombre viene de memory alloc (asignacin de memoria, en ingles). Necesita un par metro, que es la longitud del espacio que vamos a reserva. Este par metro es del tipo size_t, que es el tipo empleado en c para medir tamaos de tipos. Normalmente se suele definir size_t como unsigned, y el valor longitud representa el nmero de caracteres que se asignan. La funcin malloc devuelve un puntero del tipo puntero a caracteres al espacio de memoria asignado. Si el sistema no puede proporcionar la memoria pedida devuelve un puntero nulo. El espacio que devuelven las funciones malloc, c alloc y realloc no est inicializado, por lo que lo que debe inicializarlo el programador. Para liberar el espacio asignado con malloc basta llamar a la funcin free, (liberar en ingles). Esta funcin se define como:

57

void free(void *ptr); y su argumento es el puntero que devolvi malloc. No devuelve ningn valor y funciona igualmente con los punteros que devuelven calloc y realloc. La funcin calloc funciona de modo anlogo a malloc salvo que tiene un par metro adicional, numelem, que le permite especificar el nmero de objetos a asignar. Se define como: void *calloc(size_t numelem, size_t longitud); Como la funcin malloc devuelve un puntero al espacio de memoria asignado y un puntero null si no ha sido posible asignar el espacio. Normalmente se utiliza para asignar espacio para un grupo de datos del mismo tipo. La funcin realloc nos permite modificar el tamao del espacio asignado con malloc o calloc. Se define como: void *realloc(void *ptr, size_t longitud); El primer argumento ptr es un puntero a un espacio previamente asignado con calloc o malloc. El segundo es la nueva longitud que le queremos dar. Devuelve un puntero al nuevo espacio asignado o un puntero nulo si fall la asignacin. Adems la funcin copia el contenido del antiguo espacio en el nuevo al comienzo de este. Esto siempre lo puede hacer se la longitud del anterior espacio asignado es menor que la nueva longitud solicitada. El espacio sobrante en este caso no se inicializa. Si el espacio solicitado es de menor tamao que el anterior se copia slo la parte que quepa, siempre desde el principio, al comienzo del nuevo espacio asignado. Uso de las funciones de asignacin de memoria Para reservar espacio en la memoria dinmica para un arreglo comenzaremos asignando el espacio con malloc. Por ejemplo: #include <stdlib.h> main() { char *p; int i; p = (char *) malloc(1000); for (i = 0; i < 1000; ++i) p[i] = getchar(); for (i = 999; i >= 0; --i)

58

putchar(p[i]); free(p); } Este programa lee los primeros 1000 caracteres de la entrada estndar y los imprime en orden inverso. Se puede ver el uso de malloc y de free. En la lnea de llamada a malloc hemos introducido una variante: hemos forzado una conversin del tipo devuelto por malloc. Esto lo hemos hecho porque conviene acostumbrarse a asignar a los punteros otros punteros del mismo tipo. Aunque en C estndar se puede asignar un puntero void a cualquier puntero, es conveniente realizar el moldeado del tipo, ya que en C++, el lenguaje C avanzado, esto no est permitido. Muchos compiladores nos avisar n si intentamos asignar un puntero void a otro puntero, aunque nos permiten compilar el programa sin problemas. Los compiladores de C++ directamente paran la compilacin. Ya que no nos cuesta mucho hacer el moldeado, es buena costumbre el hacerlo. Funciones variadic En C se permite definir funciones con un nmero variable de argumentos. Son las llamadas funciones variadic. El ejemplo ms comn de funcin variadic es la funcin printf(). En C estndar se define un mtodo para crear funciones variadic. Para ello se proporciona la cabecera stdarg.h que incluye los tipos y macros que permiten crear dichas funciones. Declaracin de funciones variadic Una funcin variadic se declara igual que las dems funciones salvo que en su lista de argumentos aparece en ultimo lugar el smbolo de elipsis (tres puntos). Puede tener otros argumentos adicionales, pero el ultimo siempre es una elipsis. Un ejemplo de declaracin variadic es la funcin printf(), que se declara como: int printf(char *formato, ...); En esta declaracin observamos que la funcin printf() necesita al menos un argumento, que debe ser del tipo puntero a carcter y luego un nmero variable de argumentos. Como la funcin no conoce a priori el nmero de argumentos debemos disear algn mtodo de decirle el nmero de argumentos. Hay dos mtodos sencillos. El primero, que es el usado en printf(), es suministrarle a la funcin la informacin sobre el nmero de argumentos en uno de los argumentos. La cadena de formato pasada como primer argumento a printf() le indica los argumentos que van a continuacin y el tipo de los mismos. Otro mtodo usual es utilizar como ultimo argumento uno con significado especial. Por ejemplo en una funcin que calcule el mayor de los argumentos podemos colocar como ultimo argumento el nmero 0, o en una funcin en la que se hagan operaciones con punteros el puntero nulo. Definicin de funciones variadic Para definir una funcin variadic debemos seguir los siguientes pasos: incluimos como ultimo argumento de la lista de argumentos la elipsis.

59

en la lista de variables declaramos una variable de tipo va_list. Este tipo, al igual que las dems macros necesarias est declarado en stdarg.h, por lo que nos debemos asegurar de incluir dicha cabecera. ejecutar la macro va_start antes de comenzar a leer los argumentos. La macro va_start necesita dos argumentos. El primero es de tipo va_list, y debe ser la variable que hemos declarado anteriormente. El segundo es el nombre del ultimo argumento que se declara en la lista de argumentos. Hay algunas restricciones en el tipo de este argumento. No es seguro utilizar como ultimo argumento un arreglo, un float o algn tipo que cambie al ser promocionado. Es seguro usar un puntero y un entero normal. para in leyendo los argumentos restantes se va ejecutando la macro va_arg sucesivamente. Esta macro va leyendo de la lista de argumentos cada argumento, segn el tipo que le proporcionemos. Necesita dos argumentos: el primero es la variable de tipo va_list que habamos definido y el segundo es un tipo, no una variable. Devuelve el valor del tipo que hemos solicitado. cuando hayamos ledo todos los argumentos debemos ejecutar la macro va_end, cuyo nico argumento es la variable de tipo va_list que hemos definido.

Como ejemplo crearemos una funcin que imprime todas las cadenas de caracteres que le pasemos como argumentos en la salida estndar: #include <stdarg.h> void prints(char *s, ...) { char *p; va_list arg; va_start(arg, s); puts(s); while ((p = va_arg(arg, char *)) != NULL) puts(s); va_end(arg); } Entrada y salida estndar Un programa en C se comunica con el usuario y con el sistema a travs de las funciones de entrada y salida. Con estas funciones se pueden solicitar y enviar datos al terminal del usuario y a otros programas. Adems podemos elegir entre enviar datos binarios o enviarlos como cadenas de texto. Las funciones de entrada y salida en C ms habituales son las que forman parte de la llamada "librera estndar". Originalmente esta librera fue implementada para atender las necesidades del sistema operativo UNIX, aunque es habitual encontrarlas en cualquier compilador de C, incluso en sistemas que difieren bastante del UNIX, como son los entornos grficos y de ventanas.

60

Entrada y salida de caracteres En la librera estndar se definen las dos principales vas de comunicacin de un programa en C: la entrada estndar y la salida estndar. Generalmente est n ambas asociadas a nuestro terminal de manera que cuando se imprimen datos en la salida estndar los caracteres aparecen en el terminal, y cuando leemos caracteres de la entrada estndar los leemos del teclado del terminal. La entrada y salida estndar trabaja con caracteres (en modo carcter), con datos o nmeros binarios. Es decir, todos los datos que enviemos a la salida estndar deben ser cadenas de caracteres. Por ello para imprimir cualquier dato en la salida estndar primero deberemos convertirlo en texto, es decir, en cadenas de caracteres. Sin embargo esto lo haremos mediante las funciones de librera, que se encargan de realizar esta tarea eficientemente. Comenzaremos con las dos funciones principales de salida de caracteres: putchar() y getchar(). La funcin putchar escribe un nico carcter en la salida estndar. Su uso en sencillo y generalmente est implementada como una macro en la cabecera de la librera estndar. La funcin getchar() devuelve el carcter que se halle en la entrada estndar. Esta funcin tiene dos particularidades. La primera es que aunque se utiliza para obtener caracteres no devuelve un carcter, sino un entero. Esto se hace as ya que con un entero podemos representar tanto el conjunto de caracteres que cabe en el tipo carcter (normalmente el conjunto ASCII de caracteres) como el carcter EOF de fin de fichero. En UNIX es habitual representar los caracteres usando el cdigo ASCII, tanto en su versin de 7 bits como en su versin ampliada a 8 bits. Estos caracteres se suelen representar como un entero que va del 0 al 127 o 256. El carcter EOF entonces es representado con un -1. Adems esto tambin lo aplicaremos cuando leamos los ficheros binarios byte a byte. Una tercera funcin de caracteres que no es muy frecuente es la funcin ungetchar(). Con ella devolvemos al sistema el ultimo carcter que hemos ledo con getchar(). No se puede llamar dos veces seguidas a ungetchar. El porqu queda ms claro al explicar el uso de ungetchar. Habitualmente cuando leemos un conjunto de caracteres de la entrada estndar le pediremos que sean de un determinado tipo. si por ejemplo queremos leer un dato numrico bastar con hacer un bucle que lea nmeros (caracteres numricos). El bucle normalmente terminar cuando el carcter ledo no sea un nmero. La mejor forma de saber si el siguiente carcter es un nmero es leerlo. Pero a leerlo, si no es un nmero ya no estar disponible para futuras lecturas. Aqu es desde se usa ungetchar(). Una vez que hemos comprobado que no es un nmero lo devolvemos, y as estar listo para la siguiente lectura. Visto esto podemos seguir con las funciones gets() y puts(). La funcin puts() simplemente se imprime una cadena de caracteres en la salida estndar. Le debemos proporcionar la direccin donde encontrar la cadena de caracteres. Como ejemplo vamos a dar una implementacion sencilla de esta funcin: void putchar(char *p) { while (*p) putchar(*p++); } realmente la funcin puts es ms complicada, pues devuelve un EOF si ha ocurrido algn error. Para imprimir datos de un modo ms general el C dispone de la funcin printf(), que se ocupa de la impresin formateada en la salida estndar.

61

La funcin printf() imprime los datos en la salida estndar segn una cadena de control. Est definida en la cabecera estndar stdio.h como: int printf(const char *formato, ...); La funcin printf() tiene varias caractersticas peculiares. La primera es que es una funcin comn nmero variable de argumentos. Normalmente a estas funciones se las llama variadic, y se reconocen porque incluyen en su lnea de argumentos el smbolo de elipsis (tres puntos ...). Slo el primer par metro es obligatorio, y es del tipo puntero constante a carcter. Esta cadena tiene dos funciones: imprimir un mensaje en la salida estndar y formatear los dems argumentos que se la pasan a la funcin para ser impresos como texto. Funcionamiento de la funcin printf() Si llamamos a la funcin printf() simplemente con una cadena de caracteres la funcin fprintf la imprime de modo parecido a como lo hace la funcin puts(). Por ejemplo: printf("Hola, mundo\n"); imprime la cadena "Hola, mundo\n" en la salida estndar. Pero adems la funcin printf es capaz de imprimir otros tipos de datos como variables numricas en la salida estndar. Para ello debemos avisar a la funcin printf() de que le pasamos como argumento una variable, ya que la funcin no tiene modo alguno de saber si le hemos pasado algn par metro. El modo de hacerlo es insertando cdigos de control en la cadena de formato. Estos cdigos normalmente van precedidos del carcter %. Por ejemplo el cdigo %d representa enteros en formato decimal. As la forma de imprimir una variable entera en la salida estndar es: printf("esto es un entero: %d\n", 10); Cuando printf() se encuentra el cdigo %d en la cadena de formato lee el siguiente argumento de la funcin, que debe ser un entero, y lo convierte en su representacin decimal como cadena de caracteres. La cadena que representa al nmero sustituye al cdigo %d de la cadena de formato y se imprime la cadena resultante. Hay una gran variedad de cdigos de control para formatear los diferentes tipos de datos. Los ms importantes son: para imprimir caracteres y cadenas de caracteres: %c imprime un carcter %s imprime una cadena de caracteres. Esta cdigo permite imprimir cadenas sin que printf() mire si la cadena contiene posibles cdigos de control. %% imprime el carcter % %p imprime la direccin donde apunta un puntero. El tipo de dato impreso depende de la implementacion. Se suele usar en la depuracin de programas o sistemas para imprimir enteros

62

%d imprime un entero en su representacin decimal %u imprime un entero sin signo en su representacin decimal %x imprime un entero en su representacin hexadecimal %o imprime un entero en su representacin octal %ld imprime un entero largo en su representacin decimal %hd imprime un entero corto en su representacin hexadecimal. Recordar que los enteros cortos se pasan como enteros para imprimir nmeros reales (con decimales) %f imprime un valor del tipo doble precisin como su valor real en simple precisin. Recordar que los floats se pasan como doubles %e imprime un valor double en sus representaciones doble precisin. La cadena generada es del tipo +-ddd.ddd e+-ddd Estos cdigos de control pueden ser en la mayora de los casos completados con cdigos de alineacin y de signos. Los cdigos de alineacin se colocan entre el signo % y el cdigo de control. Los ms frecuentes son: - para justificar por la izquierda una conversin + aade un signo + a los valores positivos ' ' (espacio) aade un espacio a los valores con signo que no tengan signo ms o menos # para aadir el prefijo octal 0 en una conversin a octal, o el prefijo 0x en una conversin a hexadecimal 0 rellena con ceros antecedentes en una conversin cuando se ha especificado en ancho que es mayor que el resultado de la conversin

63

Entre el cdigo de alineacin y el cdigo de control podemos insertar un valor de anchura de campo que controla el ancho de la conversin. Por ejemplo: printf(":%3d:", 4); /* imprime : 3: */ Tambin podemos especificar un valor que controla el nmero de dgitos decimales en un valor real. Este valor se coloca tras la anchura de campo precedido de un punto. Por ejemplo: printf("%.3f", 3.99999); /* imprime 3.999 */ Para cadenas de caracteres tambin podemos insertar un valor que permite escoger cuantos caracteres se imprimen de la cadena. Para ello daremos este valor tras un punto, al igual que hacemos para el valor de precisin. Por ejemplo: printf("%.4s\n", "Hola, mundo\n"); /* imprime Hola */ La funcin scanf() La funcin scanf() hace el trabajo inverso a la funcin printf(), es decir, examina la entrada estndar y carga valores en variables. Se define como: int scanf(const char *formato, ...); Esta funcin trabaja de un modo parecido a como lo hace printf(). Necesita una cadena que indica el formato de los datos que se deben leer. La cadena de formato no se imprime, sino que slo sirve para que scanf() determine el tipo de datos a leer. El resto de los argumentos deben ser punteros a las variables donde se deben almacenar los datos ledos. Por ejemplo: scanf("%d", &i); lee un entero en formato decimal y lo almacena en la variable i. Hay que tener cuidado de pasar siempre punteros a scanf(), por lo que para guardar datos en variables normales deberemos emplear el operador direccin &. Los cdigos de control son Anlogos a los de printf, es decir, %d., %e, %s , ... La funcin scanf() es bastante sensible a los errores. Si el usuario introduce los datos incorrectamente la funcin scanf() simplemente falla. Si queremos realizar una funcin de lectura ms robusta podemos realizar lo siguiente: leemos la entrada en un arreglo de caracteres. Para ello simplemente usaremos la funcin gets() exploramos el arreglo de caracteres manualmente paso a paso. Para ello podemos usar la funcin sscanf().

La funcin sscanf se define como: int sscanf(const char *s, const char *formato, ...);

64

y realiza una tarea parecida a scanf(), pero explorando la cadena apuntada por s en vez de la entrada estndar. De este modo podemos ir explorando la cadena leda previamente con gets() paso a paso e informando al usuario del lugar donde ha cometido un error al introducir los datos. La funcin scanf salta los espacios precedentes cuando se lee un entero. Si la funcin no ha terminado de leer los datos pedidos espera a leer una nueva lnea de la entrada estndar. Esto lo hace as porque para efectos de formato la funcin scanf() coincidir al carcter de nueva lnea y al carcter de tabulador como un espacio en blanco, y la funcin scanf() salta los espacios en blanco. Este efecto de nuevo se puede evitar con el procedimiento anteriormente descrito.Ficheros El sistema de ficheros habitual en C estndar est enfocado al sistema operativo UNIX, aunque otros sistemas operativos ofrecen con los compiladores de C una librera de funciones que emula en mayor o menor grado a la implementacion UNIX. El sistema de ficheros de UNIX El sistema UNIX organiza el sistema de ficheros en directorios. Cada directorio puede contener ficheros y otros directorios. Cada archivo o subdirectorio est identificado por un nombre y una extensin opcional. Es sistema UNIX considera a los dispositivos tambin como archivos, de manera que se utilizan las mismas funciones para escribir y leer de los dispositivos que las empleadas con ficheros habituales. Los archivos se manejan con la librera estndar del C mediante las estructuras de archivo FILE. Al ejecutar un programa en UNIX el sistema provee automticamente tres archivos al programa: la entrada estndar, la salida estndar y la salida de error estndar. El usuario no necesita realizar ninguna operacin previa de apertura para emplearlos. De hecho las funciones de entrada y salida estndar envan y recogen datos a travs de estos ficheros. Apertura y cierre de un fichero Para abrir un fichero primero debemos crear una variable de tipo puntero a FILE. Este puntero permitir realizar las operaciones necesarias sobre el fichero. Este puntero deber apuntar a una estructura de tipo FILE. Estas estructuras son creadas por el sistema operativo al abrir un fichero. Para poder inicializar nuestro puntero a fichero bastar llamar a la funcin fopen(). Esta funcin intenta abrir un fichero. Si tiene xito crear una estructura de tipo FILE y devuelve un puntero a FILE que apunta a la estructura creada. En caso de no poder abrir el fichero devuelve en puntero nulo. La funcin fopen() se define en la cabecera estndar stdio.h como: FILE *fopen( const char * filename, const char *modo); Necesita dos argumentos del tipo puntero a carcter. Cada uno de ellos debe apuntar a una cadena de caracteres. El primero indica el nombre del fichero a abrir. En UNIX y otros sistemas se puede especificar con el nombre del fichero el directorio donde se abrir el fichero. El segundo indica el modo en el que se abrir el fichero. Hay que tener cuidado en pasar un puntero a cadena de caracteres y no un solo carcter. Es fcil cometer la equivocacin de pasar como segundo argumento un carcter 'r' en vez de la cadena "r". Los modos ms frecuentes de abrir un fichero son: "r" Abre un fichero de texto que exista previamente para lectura. "w" Crea un fichero de texto para escritura si no existe el fichero con el nombre especificado, o trunca (elimina el anterior y crea uno nuevo) un fichero anterior

65

"a" Crea un fichero de texto si no existe previamente o abre un fichero de texto que ya exista para aadir datos al final del fichero. Al abrir el fichero el puntero del fichero queda posicionado a "rb" Funciona igual que "r" pero abre o crea el fichero en modo binario. "wb" Anlogo a "w" pero escribe en un fichero binario. "ab" Anlogo a "a" pero aade datos a un fichero binario. "r+" Abre un fichero de texto ya existente para lectura y escritura. "w+" Abre un fichero de texto ya existente o crea uno nuevo para lectura y escritura. "a+" Abre un fichero de texto ya existente o crea un fichero nuevo para lectura y escritura. El indicador de posicin del fichero queda posicionado al final del fichero an "r+b" "rb+" Funciona igual que "r+" pero lee y escribe en un fichero binario. "w+b" "wb+" Anlogo a "w+" pero en modo binario. "a+b" "ab+" Anlogo a "a+" pero en modo binario. Una llamada tpica a la funcin fopen() es la siguiente: FILE *fp; if (( fp = fopen( "mifichero", " r")) = = NULL) perror( "No puedo abrir el fichero mifichero\n"); /* imprime un mensaje de error */ Para cerrar un fichero basta llamar a la funcin fclose que se define en stdio.h como: int fclose(FILE *fichero); Su argumento es un puntero a una estructura FILE asociada a algn fichero abierto. Esta funcin devuelve 0 en caso de xito y EOF en caso de error. Lectura y escritura sobre un fichero Para leer y escribir en un fichero en modo texto se usan funciones anlogas a las de lectura y escritura de la entrada y salida estndar. La diferencia estriba en que siempre deberemos dar un puntero a FILE para indicar sobre que fichero efectuaremos la operacin, ya que podemos tener simultneamente abiertos varios ficheros. Las funciones que trabajar con ficheros tienen nombres

66

parecidos a las funciones de entrada y salida estndar, pero comienzan con la letra f. Las ms habituales son: int fprintf( FILE *fichero, const char *formato, ... ); /* trabaja igual que printf() sobre el fichero */ int fscanf( FILE *fichero, const char *formato, ... ); /* trabaja igual que scanf() sobre el fichero */ int fputs( const char *s, FILE *fichero ); /* escribe la cadena s en el fichero */ int fputc(int c, FILE *fichero); /* escribe el carcter c en el fichero */ int fgetc( FILE *fichero); /* lee un carcter del fichero */ char *fgets( char *s, int n, FILE * fichero); /* lee una lnea del fichero */ Hay una equivalencia entre las funciones de lectura y escritura estndar y las funciones de lectura y escritura de ficheros. Normalmente las funciones de lectura y escritura estndar se definen en la cabecea estndar como macros. As la lnea: printf("hola\n"); es equivalente a la escritura en el fichero stdout: fprintf(stdout, "hola\n"); A los ficheros stdin y stdout normalmente accederemos con las funciones de lectura y escritura estndar. Estos ficheros son automticamente abiertos y cerrados por el sistema. Para escribir en la salida de error estndar deberemos usar las funciones de ficheros con el fichero stderr. Normalmente en UNIX se redirige la salida de error estndar a la impresora. Esta salida de error es muy til en los procesos por lotes y cuando se usan filtros. Un filtro es simplemente un programa que lee datos de la entrada estndar, los procesa y los enva a la salida estndar. Por ello es conveniente que no se mezclen los mensajes de error con el resultado del proceso. Un ejemplo de filtro sera un programa que expande los caracteres de tabulacin en espacios en blanco. Si el programa se llama convierte y queremos procesar el fichero mifichero, debemos escribir la lnea: cat mifichero | convierte > nuevofichero

67

Hemos usado los mecanismos del UNIX de redireccion (> enva la salida estndar de un programa a un fichero), de tubera ( | conecta la salida estndar de un programa con la entrada estndar de otro) y la utilidad cat, que enva un fichero a la salida estndar. Lectura y escritura de datos binarios Para leer y escribir grupos de datos binarios, como por ejemplo arreglos y estructuras, la librera estndar provee dos funciones: fread() y fwrite(). Se declaran en stdio.h como: size_t fread(void *p, size_t longitud, size_t numelem, FILE *fichero); size_t fwrite(void *p, size_t longitud, size_t numelem, FILE *fichero); La funcin fread() lee del fichero pasado como ultimo argumento un conjunto de datos y lo almacena en el arreglo apuntado por p. Debemos especificar en longitud la longitud del tipo de datos a leer y en numelem el nmero de datos a leer. La funcin fwrite() se comporta igual que fread() pero escribe los datos desde la posicin apuntada por p en el fichero dado. Como siempre para usar estas funciones debemos abrir el fichero y cerrarlo despus de usarlas. Por ejemplo para leer un arreglo de 100 enteros: int arreglo[100]; FILE *fp; fp = fopen("mifichero", "rb"); fread(arreglo, sizeof(int), 100, fp); fclose(fp); Estas funciones devuelven el nmero de elementos ledos. Para comprobar si ha ocurrido un error en la lectura o escritura usaremos la funcin ferror(FILE *fichero), que simplemente devuelve un valor distinto de 0 si ha ocurrido un error al leer o escribir el fichero pasado como argumento. Al escribir datos binarios en un fichero debemos tener en cuenta consideraciones de portabilidad. Esto es debido a que el orden en que se almacenan los bytes que componen cada tipo de datos en la memoria puede variar de unos sistemas a otros, y las funciones fread() y fwrite() los leen y escriben segn est n en la memoria. Operaciones especiales con los ficheros Para comprobar si hemos alcanzado el fin de fichero, por ejemplo cuando leemos un fichero binario con fread(), podemos emplear la funcin feof(), que se define en stdio.h como: int feof( FILE *fichero); Esta funcin devuelve un 0 si no se ha alcanzado el fin de fichero y un valor distinto de 0 si se alcanz el fin de fichero.

68

Para comprobar si ha ocurrido un error en la lectura o escritura de datos en un fichero disponemos de la funcin ferror, que se declara en stdio.h como: int ferror( FILE *fichero); Esta funcin devuelve un valor distinto de 0 si ha ocurrido algn error en las operaciones con el fichero y un 0 en caso contrario. Estas dos funciones trabajan leyendo los indicadores de fin de fichero y error de la estructura FILE asociada a cada fichero. Podemos limpiar ambos indicadores utilizando la funcin clearerr(), que se define en stdio.h como: void clearerr( FILE *fichero); Posicionamiento del indicador de posicin del fichero Cuando se manejan ficheros de acceso aleatorio se necesita poder colocar el indicador de posicin del fichero en algn punto determinado del fichero. Para mover el puntero del fichero la librera estndar proporciona la funcin fseek(), que se define en stdio.h como: int fseek( FILE *fichero, long desplazamiento, int modo); La funcin devuelve un 0 si ha tenido xito y un valor diferente en caso de error. El argumento desplazamiento seala el nmero de caracteres que hay que desplazar el indicador de posicin. Puede ser positivo o negativo, o incluso 0, ya que hay tres modos diferentes de desplazar el indicador de posicin. Estos modos se indican con el argumento modo. En stdio.h se definen tres macros que dan los posibles modos. La macro SEEK_SET desplaza al indicador de posicin desde el comienzo del fichero. La macro SEK_CUR desplaza el indicador de posicin desde la posicin actual y la macro SEEK_END desplaza al indicador de posicin desde el final del fichero. Para este ultimo modo deberemos usar un valor de desplazamiento igual o menor que 0. Para ver en que posicin se halla el puntero del fichero podemos usar la funcin ftell(), que se define en stdio.h como: long ftell( FILE *fichero); Para un fichero binario ftell() devuelve el nmero de bytes que est desplazado el indicador de posicin del fichero desde el comienzo del fichero. Adems para llevar el indicador de posicin al comienzo del fichero tenemos la funcin rewind(), que se define en stdio.h como: void rewind( FILE * fichero); Esta funcin simplemente llama a fseek(fichero, 0L, SEEK_SET) y luego limpia el indicador de error. Entrada y salida con tampn Cuando la librera estndar abre un fichero le asocia un tampn (buffer) intermedio, que permite agilizar las lecturas y escrituras. As cada vez que se lee un dato del fichero primero se leen datos hasta llenar el tampn, y luego se van leyendo del tampn a medida que se solicitan. De este modo

69

se accede al sistema de ficheros ms eficientemente, ya que para leer un grupo de datos slo se efectan unas pocas lecturas del fichero. Este proceso se repite para la escritura de datos. Cuando se envan a un fichero se van almacenando temporalmente en el tampn y cuando se llena el tampn se escribe su contenido en el fichero. Esto plantea varios problemas. El primero es que siempre se debe vaciar el tampn cuando se cierra el fichero. Esto lo hace automticamente la funcin fclose(). Sin embargo las dems funciones no lo hacen. Por ello si estamos escribiendo datos en un fichero de lectura y escritura y queremos leer datos, primero debemos vaciar el tampn, para que los datos que leamos estn actualizados. Para ello la librera estndar proporciona la funcin fflush(), que se define en stdio.h como: int fflush( FILE *fichero); Esta funcin devuelve un 0 si tiene xito y EOF en caso de error. Adems si fichero es un puntero nulo entonces fflush() vaca los tampones de todos los fichero abiertos para escritura. Para alterar el tamao del tampn de un fichero podemos llamar a la funcin setvbuf() inmediatamente despus de abrir el fichero. Esta funcin se define en stdio.h como: int setvbuf(FILE *flujo, char *buffer, int modo, size_t longitud); El argumento longitud fija el tamao del tampn. Es argumento nos indica el tipo de tampn elegido. Hay tres tipos: tampn de lnea, tampn completo y sin tampn. Para especificar estos tres tipos de tampones se definen en stdio.h las macros _IOFBF (que indica tampn completo), _IOLBF (que indica tampn de lnea) y _IONBF ( que indica que el fichero no tiene tampn ). El argumento buffer apunta al lugar donde queremos que est el tampn. Si pasamos como argumento buffer un puntero nulo el sistema se encargar de reservar el lugar del tampn y lo liberar al cerrar el fichero. Podemos asignar a un fichero un tamao de tampn grande para que el sistema realice menor nmero de operaciones de lectura y escritura. si el fichero es interactivo( por ejemplo un terminal) quizs nos ser ms til ajustar el tampn al modo de lnea o incluido eliminar el tampn. Deberemos probar diferentes valores y modos para as determinar el mejor tampn a usar. Operaciones miscelneas con ficheros La librera estndar proporciona algunas funciones adicionales para manejar ficheros. Por ejemplo la funcin remove(), que se define en stdio.h como: int remove(const char *nombrefichero); Esta funcin elimina el fichero de nombre nombrefichero. Conviene cerrar el fichero antes de eliminarlo. Tambin disponemos de una funcin para renombrar el fichero. La funcin rename(), definida en stdio.h como: int rename(const char *antiguo, const char *nuevo); intenta renombrar al fichero de nombre antiguo. si tiene xito devuelve un 0. Hay que asegurarse antes de que no exista un fichero de nombre nuevo. Otra funcin para abrir ficheros es freopen(), que se define en stdio.h como:

70

FILE *freopen( const char *nombre, const char *modo, FILE *fichero); Esta funcin cierra el fichero pasado como tercer argumento y lo abre con el nuevo nombre y modo especificado. Devuelve un puntero a FILE que apunta al nuevo fichero abierto, o un puntero nulo en caso de error, tal y como lo hace fopen(). El Preprocesador de C El preprocesado es una parte de la compilacin en la que se hacen algunas tareas sencillas. Las fundamentales son: supresin de comentarios. expansin de macros. inclusin del cdigo de las cabeceras. conversin de las secuencias de escape en caracteres dentro de cadenas de caracteres y de constantes de tipo carcter.

El preprocesado puede ser de dos tipos: externo (lo realiza un programa adicional) o interno(se preprocesa y compila a la vez. En UNIX el preprocesado es externo, ya que lo hace el programa cpp, que es ejecutado automticamente por el compilador cc. Es bastante instructivo preprocesar un fichero y revisar el cdigo fuente resultante. Directivas de preprocesado Para realizar las diferentes acciones que admite el preprocesado disponemos de una serie de directivas de preprocesado, que son como comandos que instruyen al Preprocesador para realizar las expansiones. Todas las directivas del Preprocesador comienzan con el carcter # seguida del nombre de comando. El signo # debe estar al comienzo de una lnea, para que el Preprocesador lo pueda reconocer. La ms sencilla de las directivas es #include. Esta directiva debe ir seguida de un nombre de fichero. El nombre debe ir entrecomillado o encerrado entre signos de mayor y menor. Lo que hace el Preprocesador es sustituir la lnea donde se halla la directiva por el fichero indicado. Por ejemplo: #include <stdio.h> #include "stdio.h" La diferencia entre encerrar el nombre del fichero entre comillas o entre signos de mayor y menor es que al buscar el fichero con las comillas la bsqueda se hace desde el directorio actual, mientras que entre signos de mayor y menor la bsqueda se hace en un directorio especial. Este directorio vara con la implementacion, pero suele estar situado en el directorio del compilador. El Preprocesador y el compilador ya conocen donde se ubica el directorio. Todas las cabeceras estndar se hallan en ese directorio. Se puede incluir cualquier tipo de fichero fuente, pero lo habitual es incluir slo ficheros de cabecera. Hay que tener en cuenta que el fichero incluido es preprocesado. Esto permite expandir algunos tipos de macros y ajustar la cabecera al sistema mediante las directivas de preprocesado. Para ello se suelen usar macros que actan como banderas. Definicin de macros

71

En C una macro es un identificador que el Preprocesador sustituye por un conjunto de caracteres. Para definir una macro se dispone de la directiva #define. Su sintaxis es: #define identificador conjunto de caracteres Se utiliza habitualmente en los ficheros de cabecera para definir valores y constantes. Por ejemplo: #define EOF -1 #define SEEK_SET 0 #define BUFSIZ 512 Otro uso muy normal en los ficheros de cabecera emplear un smbolo definido con #define como bandera (selector para condiciones), utilizndolo con la directiva #ifdef. Una macro definida con #define no se puede redefinir a menos que la siguiente definicin coincida con la primera exactamente. Para redefinir una macro primero debemos eliminarla con la directiva #undef. Debemos acompaar a la directiva #undef con el nombre de la macro. Estilo de programacin: normas de indentacion Definicin de las normas de indentacion que se usar n a lo largo del curso en los programas en C. Se seguir n normas muy parecidas a las empleadas habitualmente por otros autores en libros recientes de programacin, con algunas variaciones propias. Estilo y disposicin general del programa. Cada archivo contendr en primer lugar un comentario donde se explicar brevemente el cometido del mismo. Por ejemplo: /* * HOLA.C Este programa imprime en mensage de saludo en la salida * estndar. */ A continuacin ir una lista de las cabeceras que se incluir n en l: #include <stdio.h> #include <stdlib.h> #include "hola.h" A continuacin la lista de variables globales: int i = 0,j,numero = 0,num2;

72

char c,temp; A continuacin la lista de declaracin de funciones, si no se usa un fichero de cabecera para este propsito. void impmensage(char *mensage); FILE *pidefichero(char *mensage); A continuacin la funcin main() y luego las dems. main(argc, argv) int argc; char *argv[]; { lista de variables locales; lista de sentencias; } Escritura de funciones Se comienza en la primera columna. Primero va el tipo de la funcin. Luego el nombre y la lista de argumentos. Entre el nombre y el parntesis de la lista de argumentos no se deja espacios en blanco. Detraes de cada coma que separa los argumentos se deja un espacio en blanco. Al final se cierra el parntesis. A continuacin va la declaracin de los tipos de los argumentos. Cada declaracin de tipo de argumento va en una lnea, precedida de un tabulador. A continuacin se abren las llaves de la funcin. Luego el cuerpo de la funcin, separando la lista de variables y la lista de declaracin de las funciones de las dems sentencias. Todas ellas tienen como mnimo un tabulador a la izquierda, no pudiendo empezar en la primera columna. Se finaliza con la llave de cierre. char *funcin1(arg1, arg2, arg3) int arg1; char *arg2; struct *sutipo arg3; { int i,

73

j; char *puntero; setencia1; . . sentencian; } Declaracin de variables Se tratar n de colocar por tipos en columnas, procurando que coincidan las comas y los puntos y comas: char carct1,c; Podremos incluir una breve explicacin de su uso: char temp; /* variable temporal */ Bucle while Si consta de una sola sentencia se separa la lnea de la expresin de la lnea de la sentencia. Se deja un espacio en blanco entre la palabra while y el parntesis de la expresin: while (expresin) sentencia; Si el bucle consta de un bloque de sentencias se aade la llave de comienzo de bloque detraes de la expresin, en la misma lnea. Las diferentes lneas del bloque las colocaremos debajo de la lnea de la expresin, colocndoles un tabulador al principio de cada lnea. Por ultimo, a la misma altura que las sentencias del bloque, colocaremos la llave de cierre: while (expresin) { /* comentario muy breve */ sentencia1; sentencia2; } Sentencia if/else Tras la palabra if, un espacio en blanco y la expresin entre parntesis.

74

La palabra else, si la hay, va a la misma altura que la palabra if. Como norma general seguiremos las mismas convenciones que para el bucle while en el asunto de los bloques y las sentencias, tanto para la para if como para else: if (expresin) sentencia1; else sentecia2; o tambin: if (expresin) { sentencia1; sentencia2; } else { sentencia3; sentencia4; } Forma general de un programa en C Declaraciones globales main( ) { variables locales sentencias } f1( ) { .........

75

} ... ... fn ( ) { ......... } Nombre de identificadores Son los nombres usados para referirse a las variables, funciones, etiquetas y otros objetos definidos por el usuario. La longitud de un identificador en Turbo C puede variar entre 1 y 32 caracteres. El primer carcter debe ser una letra o un smbolo de subrayado, los caracteres siguientes pueden ser letras, nmeros o smbolos de subrayado. Correcto: cont, cuenta23, balance_total Incorrecto: 1cont, hola!, balance...total En C las maysculas y las minsculas se tratan como distintas Tipos de datos Existen cinco tipos de datos atmicos: Tipo char int float double void bits 8 16 32 64 0 rango 0 a 255 -32.768 a 32.767 3,4 E -38 a 3,4 E +38 1,7 E -308 a 1,7 E +308 sin valor

(*) El void se usa para declarar funciones que no devuelven ningn valor o para declarar funciones sin parmetros. Modificadores de tipos signed unsigned long

76

short Los modificadores signed, unsigned, long y short se pueden aplicar a los tipos base entero y carcter. Sin embargo, long tambin se puede aplicar a double. Tipo char unsigned char Signed char int unsigned int signed int short int unsigned short int signed short int long int signed long int float double long double bits 8 8 8 16 16 16 16 16 16 32 32 32 64 64 Rango -128 a 127 0 a 255 -128 a 127 -32.768 a 32.767 0 a 65.535 -32.768 a 32.767 -32.768 a 32.767 0 a 65.535 -32.768 a 32767 -2147483648 a 2147483647 -2147483648 a 2147483647 3,4 E -38 a 3,4 E +38 1,7 E -308 a 1,7 E +308 1,7 E -308 a 1,7 E +308

Modificadores de acceso Las variables de tipo const no pueden ser cambiadas durante la ejecucin del programa. Por ejemplo, const int a; Declaracin de variables Todas las variables han de ser declaradas antes de ser usadas. Forma general: tipo lista_de_variables; int i,j,l; short int si; Existen tres sitios donde se pueden declarar variables: dentro de las funciones (variables locales), en la definicin de parmetros de funciones (parmetros formales) y fuera de todas las funciones (variables globales). Variables externas Si una funcin situada en un fichero fuente desea utilizar una variable de este tipo declarada en otro fichero, la debe declarar (o mejor dicho referenciar) con la palabra extern. Archivo 1 Archivo 2

77

int x,y; extern int x,y; char ch; extern char ch; main ( ) void func1( ) { { x=120; x=y/10; . . . . . . . . . . . . . } } Variable estticas (static) Tienen memoria asignada durante toda la ejecucin del programa. Su valor es recordado incluso si la funcin donde est definida acaba y se vuelve a llamar ms tarde. Ejemplo: series (void) { static int num; num=num+23; return (num); } Variables registro El especificador register pide a Turbo C que mantenga el valor de una variable con ese especificador de forma que se permita el acceso ms rpido a la misma. Para enteros y carcteres esto significa colocarla en un registro de la CPU. Slo se puede aplicar a variables locales y a los parmetros fomales de una funcin. Son ideales para el control de bucles. pot_ent (int m, register int e) { register int temp; temp=1; for ( ; e; e--) temp *=m;

78

return (temp); } Sentencias de asignacin Forma general: nombre_variable = expresion; Abreviaturas en C x=x+10 <----------> x+=10 x=x-10 <----------> x-=10 Conversin de tipos Se da cuando se mezclan variables de un tipo con variables de otro tipo. El valor de la derecha de la asignacin se convierte al tipo del lado izquierdo. Puede haber prdida de los bits ms significativos en un caso como: short = long Inicializacin de variables Tipo nombre_variable = constante; char c='a'; int primero=0; float balance=123.23; Todas las variables globales se inicializan a cero sino se especifica otro valor inicial. Las variables locales y register tendrn valores desconocidos antes de que se lleve a cabo su primera asignacin. Constantes Tipo dato Ejemplo de constantes char 'a' '\n' '9' int 1 123 -234 float 123.23 Una constante de tipo cadena de caracteres est constituida por una secuencia de caracteres entre comillas dobles "Hola". Carcteres con barra invertida

79

\n Nueva lnea \t Tabulacin horizontal \b Espacio atrs \r Retorno de carro \f Salto de pgina \\ Barra invertida \' Comilla simple \" Comilla doble Operadores En C hay tres clases de operadores: aritmticos, relacionales y lgicos, y a nivel de bits. Aritmticos - resta + suma * producto / divisin % mdulo (resto de la divisin entera) -- decrementar ++ incrementar

x=10; x=10; y=++x; y=x++; y=11 y=10 Relacionales En C cierto es cualquier valor distinto de cero. Falso es cero. > mayor que >= mayor o igual que

80

< menor que <= menor o igual que == igual != distinto Lgicos && y || o ! no El operador ? Exp 1 ? Exp 2 : Exp 3 Se evala exp1 si es cierto se evala exp2 y toma ese valor para la expresin. Si exp1 es falso evala exp3 tomando su valor para la expresin. Ejemplo: x=10: y=x>9 ? 100 : 200 --------> y = 100 Los operadores de punteros & y * & devuelve la direccin de memoria del operando. Ejemplo: m=&cont; coloca en m la direccin de memoria de la variable cont & (la direccin de) * devuelve el valor de la variable ubicada en la direccin que se especifica. Ejemplo: q=*m; coloca el valor de cont en q. *(en la direccin) Sizeof Es un operador monario que devuelve la longitud, en bytes, de la variable o del especificador de tipo al que precede.

81

Ejemplo: flat f; printf ("%f",sizeof f); Mostrara 4 printf ("%d", sizeof (int)); Mostrara 2 El nombre del tipo debe ir entre parntesis. ESTRUCTURAS CONDICIONALES If if (expresion) { ............ ............ } else { ........... ........... } Switch switch (variable) { case cte1 : ........... ........... break; case cte2 : ........... ........... break; ..................

82

.................. default : ........... ........... } Switch slo puede comprobar la igualdad. BUCLES For for (inicializacin; condicin; incremento) sentencia inicializacin -> asignacin condicin -> expresin relacional Ejemplo: for (x=1; x<=100; x++) printf ("%d",x); Imprime los nmeros del 1 al 100 While while (condicin) sentencia; Ejemplo: while (c!='A') c=getchar( ); Do / While Analiza la condicin al final. do { ........... ........... } while (condicin); Break Tiene dos usos: para finalizar un case en una sentencia switch. para forzar la terminacin inmediata de un bucle.

83

Exit Para salir de un programa anticipadamente. Da lugar a la terminacin inmediata del programa, forzando la vuelta al S.O. Usa el archivo de cabecera stdlib.h Ejemplo: #include <stdlib.h> main (void) { if (!tarjeta_color( )) exit(1); jugar( ); } Continue Hace comenzar la iteracin siguiente del bucle, saltando as la secuencia de instrucciones comprendida entre el continue y el fin del bucle. do { scanf("%d",&num); if (x<0) continue; printf("%d",x); } while (x!=100); Funciones tipo nombre_funcion (lista de parametros) { ............ ............ } tipo, especifica el tipo de valor que devuelve la sentencia return de la funcin. Llamada por valor

84

Copia el valor de un argumento en el parmetro formal de la subrutina. Los cambios en los parmetros de la subrutina no afectan a las variables usadas en la llamada. int cuad (int x); main ( ) cuad (int x) { { int t=10; x=x*x; printf ("%d %d",cuad(t),t); return(x); return 0; } } Salida es << 100 10 >> Llamada por referencia Es posible causar una llamada por referencia pasando un puntero al argumento. Se pasa la direccin del argumento a la funcin, por tanto es posible cambiar el valor del argumento exterior de la funcin. int x,y; inter (int *x,int *y) inter (&x,&y); { int temp; temp=*x; *x=*y; *y=temp; } Arrays Todos los arrays tienen el 0 como ndice de su primer elemento. char p [10]; array de carcteres que tiene 10 elementos, desde p[0] hasta p[9]. Para pasar arrays unidimensionales a funciones, en la llamada a la funcin se pone el nombre del array sin ndice. Ejemplo:

85

main ( ) Si una funcin recibe un array unidimensional, se puede { declarar el parmetro formal como un puntero, como un int i[10]; array delimitado o como un array no delimitado. func1 (i); } func1 (int *x) /puntero/ func1 (int x[10]) /array delimitado/ func1 (int x[ ]) /array no delimitado/ Inicializacin de arrays Forma general de inicializacin de un array: tipo nombre_array [tamao] = {lista de valores}; lista de valores, es una lista de constantes separadas por comas, cuyo tipo es compatible con el tipo del array. La primera constante se coloca en la primera posicin del array, la segunda constante en la segunda posicin y as sucesivamente. Ejemplo: int i[10]={1,2,3,4,5,6,7,8,9,10}; Los arrays de carcteres que contienen cadenas permiten una inicializacin de la forma: char nombre_array [tamao]="cadena"; Se aade automticamente el terminador nulo al final de la cadena. Ejemplo char cad[5]="hola"; equivalentes char cad[5]={'h','o','l','a','\o'}; Es posible que C calcule automticamente las dimensiones de los arrays utilizando arrays indeterminados. Si en la inicializacin no se especifica el tamao el compilador crea un array suficientemente grande para contener todos los inicializadores presentes. char e1[ ]="error de lectura \n"; Cadenas

86

Aunque C no define un tipo cadena, estas se definen como un array de carcteres de cualquier longitud que termina en un carcter nulo ('\0'). Array que contenga 10 caracteres: char s[11]; Una constante de cadena es una lista de caracteres encerrada entre dobles comillas. Funciones de manejo de cadenas Archivo de cabecera string.h char *strcpy (char *s1, const char *s2); copia la cadena apuntada por s2 en la apuntada por s1. Devuelve s1. char *strcat (char *s1, consta char *s2); concatena la cadena apuntada por s2 en la apuntada por s1, devuelve s1. int strlen (const char *s1); devuelve la longitud de la cadena apuntada por s1. int strcmp (const char *s1, const char *s2); compara s1 y s2, devuelve 0 si con iguales, mayor que cero si s1>s2 y menor que cero si s1<s2. Las comparaciones se hacen alfabticamente. Arrays Bidimensionales Se declaran utilizando la siguiente forma general: tipo nombre_array [tamao 2 dim] [tamao 1 dim]; Ejemplo -> int d [10][20]; Cuando se utiliza un array bidimensional como argumento de una funcin realmente slo se pasa un puntero al primer elemento, pero la funcin que recibe el array tiene que definir al menos la longitud de la primera dimensin para que el compilador sepa la longitud de cada fila. Ejemplo: funcin que recibe un array bidimensional de dimensiones 5,10 se declara as: func1 (int x[ ][10]) { .................... } Arrays y Punteros Un nombre de array sin ndice es un puntero al primer elemento del array. Ejemplo: Estas sentencias son idnticas:

87

char p[10]; - p - &p[0] int *p, i[10]; p=i; ambas sentencias ponen el valor 100 en el sexto elemento de i. i[5]=100; *(p+5)=100; Esto tambin se puede aplicar con los arrays de dos o ms direcciones. int a[10][10]; a=&a[0][0]; a[0][4]=*((*a)+4); Memoria dinmica Malloc (n) reserva una porcin de memoria libre de n bytes y devuelve un puntero sobre el comienzo de dicho espacio. Free (p) libera la memoria apuntada con el puntero p.

Ambas funciones utilizan el archivo de cabecera stdlib.h Si no hay suficiente memoria libre para satisfacer la peticin, malloc ( ) devuelve un nulo. Ejemplo: char *p; p=malloc(1000); Estructuras La forma general de una definicin de estructura es: struct etiqueta { tipo nombre_variable; tipo nombre_variable; ............................. .............................

88

} variables _de_estructura Ejemplo: struct dir { char nombre[30]; char calle[40]; char ciudad[20]; char estado[3]; unsigned long int codigo; } info_dir; A los elementos individuales de la estructura se hace referencia utilizando . (punto). Ejemplo: info_dir.codigo = 12345; Forma general es: nombre_estructura.elemento Una estructura puede inicializarse igual que los vectores: struct familia { char apellido[10]; char nombrePadre[10]; char nombreMadre[10]; int numerohijos; } fam1={"Garcia","Juan","Maria",7}; Arrays de estructuras Se define primero la estructura y luego se declara una variable array de dicho tipo. Ejemplo: struct dir info_dir [100]; Para acceder a una determinada estructura se indexa el nombre de la estructura:

89

info_dir [2].codigo = 12345; Paso de estructuras a funciones Cuando se utiliza una estructura como argumento de una funcin, se pasa la estructura ntegra mediante el uso del mtodo estndar de llamada por valor. Ejemplo: struct tipo_estructura { int a,b; char c; }; void f1 (struct tipo_estructura param); main ( ) { struct tipo_estructura arg; arg.a = 1000; f1(arg); return 0; } void f1 (struct tipo_estructura param) { printf ("%d",param.a); } Punteros a estructuras Declaracin: struct dir * pruntero_dir; Existen dos usos principales de los punteros a estructuras: Para pasar la direccin de una estructura a una funcin. Para crear listas enlazadas y otras estructuras de datos dinmicas.

90

Para encontrar la direccin de una variable de estructura se coloca & antes del nombre de la estructura. Ejemplo: struct bal { float balance; char nombre[80]; } persona; struct bal *p; p = &persona; (coloca la direccin de la estructura persona en el puntero p) No podemos usar el operador punto para acceder a un elemento de la estructura a travs del puntero a la estructura. Debemos utilizar el operador flecha -> p -> balance Tipo enumerado enum identificador {lista de constantes simblicas}; Ejemplo: enum arcoiris {rojo, amarillo, verde, azul, blanco}; (realmente asigna rojo=0, amarillo=1, ...) printf ("%d %d", rojo, verde); imprime 0 2 en pantalla Podemos especificar el valor de uno o ms smbolos utilizando un inicializador. Lo hacemos siguiendo el smbolo con un signo igual y un valor entero. enum moneda {penique, niquel, diez_centavos, cuarto=100, medio_dolar, dolar}; Los valores son: penique 0, niquel 1, diez_centavos 2, cuarto 100, medio_dolar 101, dolar 102 Punteros int x=5, y=6; int *px, *py;

91

px=py; copia el contenido de py sobre px, de modo que px apuntar al mismo objeto que apunta py. *px=*py; copia el objeto apuntado por py a la direccin apuntada por px. px=&x; px apunta a x. py=0; hace que py apunte a nada (NULL). px++; apunta al elemento siguiente sobre el que apuntaba inicialmente Ejemplo:

Se puede sumar o restar enteros a y de punteros. p1=p1+9; p1 apunta al noveno elemento del tipo p1 que est ms all del elemento al que apunta actualmente. Punteros y arrays char cad[80], *p1; p1 = cad p1 ha sido asignado a la direccin del primer elemento del array cad. Para acceder al quinto elemento de cad se escribe: cad[4] o *(p1+4) Arrays de punteros Array de punteros a enteros: int *x [10];

92

Para asignar la direccin de una variable entera llamada var al tercer elemento del array de punteros, se escribe: x[2]=&var; Para encontrar el valor de var: *x[2] Punteros a punteros puntero -> variable Indireccin simple puntero -> puntero -> variable Indireccin mltiple float **balancenuevo; balancenuevo no es un puntero a un nmero en coma flotante, sino un puntero a un puntero a float. Ejemplo: main(void) { int x, *p, **q; x=10; p=&x; q=&p; printf("%d",**q); /* imprime el valor de x */ return 0; } E/S por consola getche ( ) lee un carcter del teclado, espera hasta que se pulse una tecla y entonces devuelve su valor. El eco de la tecla pulsada aparece automticamente en la pantalla. Requiere el archivo de cabecera conio.h putcahr ( ) imprime un carcter en la pantalla. Los prototipos son: int getche (void);

93

int putchar (int c); Ejemplo: main ( ) /* cambio de mayscula / minscula */ { char car; do { car=getche( ); if (islower(car)) putchar (toupper (car)); else putchar (tolower (car)); } while (car=!'.') } Hay dos variaciones de getche( ) : Getchar ( ): funcin de entrada de caracteres definida por el ANSI C. El problema es que guarda en un buffer la entrada hasta que se pulsa la tecla INTRO. Getch ( ): trabaja igual que getche( ) excepto que no muestra en la pantalla un eco del carcter introducido.

gets ( ) y puts ( ) Permiten leer y escribir cadenas de caracteres en la consola. gets ( ) lee una cadena de caracteres introducida por el teclado y la sita en la direccin apuntada por su argumento de tipo puntero a carcter. Su prototipo es: char * gets (char *cad); Ejemplo: main ( ) { char cad[80]; gets (cad); printf ("La longitud es %d", strlen (cad)); return 0;

94

} puts ( ) escribe su argumento de tipo cadena en la pantalla seguido de un carcter de salto de lnea. Su prototipo es: char * puts (const char *cad); E/S por consola con formato printf ( ) El prototipo de printf ( ) es: int printf (const char *cad_fmt, ...); La cadena de formato consiste en dos tipos de elementos: caracteres que se mostrarn en pantalla y rdenes de formato que empiezan con un signo de porcentaje y va seguido por el cdigo del formato. %c un nico caracter %d decimal %i decimal %e notacin cientfica %f decimal en coma flotante %o octal %s cadena de caracteres %u decimales sin signo %x hexadecimales %% imprime un signo % %p muestra un puntero Las rdenes de formato pueden tener modificadores que especifiquen la longitud del campo, nmero de decimales y el ajuste a la izquierda. Un entero situado entre % y el cdigo de formato acta como un especificador de longitud mnima de campo. Si se quiere rellenar con ceros, se pone un 0 antes del especificador de longitud de campo. %05 rellena con ceros un nmero con menos de 5 dgitos. %10.4f imprime un nmero de al menos diez caracteres con cuatro decimales.

95

Si se aplica a cadenas o enteros el nmero que sigue al punto especifica la longitud mxima del campo. %5.7s imprime una cadena de al menos cinco caracteres y no ms de siete. scanf ( ) Su prototipo es: int scanf ( ) (const char *cadena_fmt, ...); Ejemplo: scanf ("%d",&cuenta);

96

Curso de programacin en Java


1. INTRODUCCIN A JAVA
1.1 Origen de Java Sun Microsystems, lder en servidores para Internet, uno de cuyos lemas desde hace mucho tiempo es "the network is the computer" (lo que quiere dar a entender que el verdadero ordenador es la red en su conjunto y no cada mquina individual), es quien ha desarrollado el lenguaje Java, en un intento de resolver simultneamente todos los problemas que se le plantean a los desarrolladores de software por la proliferacin de arquitecturas incompatibles, tanto entre las diferentes mquinas como entre los diversos sistemas operativos y sistemas de ventanas que funcionaban sobre una misma mquina, aadiendo la dificultad de crear aplicaciones distribuidas en una red como Internet. He podido leer ms de cinco versiones distintas sobre el origen, concepcin y desarrollo de Java, desde la que dice que este fue un proyecto que rebot durante mucho tiempo por distintos departamentos de Sun sin que nadie le prestara ninguna atencin, hasta que finalmente encontr su nicho de mercado en la aldea global que es Internet; hasta la ms difundida, que justifica a Java como lenguaje de pequeos electrodomsticos. Hace algunos aos, Sun Microsystems decidi intentar introducirse en el mercado de la electrnica de consumo y desarrollar programas para pequeos dispositivos electrnicos. Tras unos comienzos dudosos, Sun decidi crear una filial, denominada FirstPerson Inc., para dar margen de maniobra al equipo responsable del proyecto. El mercado inicialmente previsto para los programas de FirstPerson eran los equipos domsticos: microondas, tostadoras y, fundamentalmente, televisin interactiva. Este mercado, dada la falta de pericia de los usuarios para el manejo de estos dispositivos, requera unos interfaces mucho ms cmodos e intuitivos que los sistemas de ventanas que proliferaban en el momento. Otros requisitos importantes a tener en cuenta eran la fiabilidad del cdigo y la facilidad de desarrollo. James Gosling, el miembro del equipo con ms experiencia en lenguajes de programacin, decidi que las ventajas aportadas por la eficiencia de C++ no compensaban el gran coste de pruebas y depuracin. Gosling haba estado trabajando en su tiempo libre en un lenguaje de programacin que l haba llamado Oak, el cual, an partiendo de la sintaxis de C++, intentaba remediar las deficiencias que iba observando. Los lenguajes al uso, como C o C++, deben ser compilados para un chip, y si se cambia el chip, todo el software debe compilarse de nuevo. Esto encarece mucho los desarrollos y el problema es especialmente acusado en el campo de la electrnica de consumo. La aparicin de un chip ms barato y, generalmente, ms eficiente, conduce inmediatamente a los fabricantes a incluirlo en las nuevas series de sus cadenas de produccin, por pequea que sea la diferencia en precio ya que, multiplicada por la tirada masiva de los aparatos, supone un ahorro considerable. Por tanto, Gosling decidi mejorar las caractersticas de Oak y utilizarlo. El primer proyecto en que se aplic este lenguaje recibi el nombre de proyecto Green y consista en un sistema de control completo de los aparatos electrnicos y el entorno de un hogar. Para ello se construy un ordenador experimental denominado *7 (Star Seven). El sistema presentaba una interfaz basada en la representacin de la casa de forma animada y el control se llevaba a cabo mediante una pantalla sensible al tacto. En el sistema apareca Duke, la actual mascota de Java.

97

Posteriormente se aplic a otro proyecto denominado VOD (Video On Demand) en el que se empleaba como interfaz para la televisin interactiva. Ninguno de estos proyectos se convirti nunca en un sistema comercial, pero fueron desarrollados enteramente en un Java primitivo y fueron como su bautismo de fuego. Una vez que en Sun se dieron cuenta de que a corto plazo la televisin interactiva no iba a ser un gran xito, urgieron a FirstPerson a desarrollar con rapidez nuevas estrategias que produjeran beneficios. No lo consiguieron y FirstPerson cerr en la primavera de 1994. Pese a lo que pareca ya un olvido definitivo, Bill Joy, cofundador de Sun y uno de los desarrolladores principales del Unix de Berkeley, juzg que Internet podra llegar a ser el campo de juego adecuado para disputar a Microsoft su primaca casi absoluta en el terreno del software, y vio en Oak el instrumento idneo para llevar a cabo estos planes. Tras un cambio de nombre y modificaciones de diseo, el lenguaje Java fue presentado en sociedad en agosto de 1995. Lo mejor ser hacer caso omiso de las historias que pretenden dar carta de naturaleza a la clarividencia industrial de sus protagonistas; porque la cuestin es si independientemente de su origen y entorno comercial, Java ofrece soluciones a nuestras expectativas. Porque tampoco vamos a desechar la penicilina aunque haya sido su origen fruto de la casualidad. 1.2 Caractersticas de Java Las caractersticas principales que nos ofrece Java respecto a cualquier otro lenguaje de programacin, son: Simple Java ofrece toda la funcionalidad de un lenguaje potente, pero sin las caractersticas menos usadas y ms confusas de stos. C++ es un lenguaje que adolece de falta de seguridad, pero C y C++ son lenguajes ms difundidos, por ello Java se dise para ser parecido a C++ y as facilitar un rpido y fcil aprendizaje. Java elimina muchas de las caractersticas de otros lenguajes como C++, para mantener reducidas las especificaciones del lenguaje y aadir caractersticas muy tiles como el garbage collector (reciclador de memoria dinmica). No es necesario preocuparse de liberar memoria, el reciclador se encarga de ello y como es un thread de baja prioridad, cuando entra en accin, permite liberar bloques de memoria muy grandes, lo que reduce la fragmentacin de la memoria. Java reduce en un 50% los errores ms comunes de programacin con lenguajes como C y C++ al eliminar muchas de las caractersticas de stos, entre las que destacan: aritmtica de punteros no existen referencias registros (struct) definicin de tipos (typedef) macros (#define) necesidad de liberar memoria (free)

98

Aunque, en realidad, lo que hace es eliminar las palabras reservadas (struct, typedef), ya que las clases son algo parecido. Adems, el intrprete completo de Java que hay en este momento es muy pequeo, solamente ocupa 215 Kb de RAM. Orientado a objetos Java implementa la tecnologa bsica de C++ con algunas mejoras y elimina algunas cosas para mantener el objetivo de la simplicidad del lenguaje. Java trabaja con sus datos como objetos y con interfaces a esos objetos. Soporta las tres caractersticas propias del paradigma de la orientacin a objetos: encapsulacin, herencia y polimorfismo. Las plantillas de objetos son llamadas, como en C++, clases y sus copias, instancias. Estas instancias, como en C++, necesitan ser construidas y destruidas en espacios de memoria. Java incorpora funcionalidades inexistentes en C++ como por ejemplo, la resolucin dinmica de mtodos. Esta caracterstica deriva del lenguaje Objective C, propietario del sistema operativo Next. En C++ se suele trabajar con libreras dinmicas (DLLs) que obligan a recompilar la aplicacin cuando se retocan las funciones que se encuentran en su interior. Este inconveniente es resuelto por Java mediante una interfaz especfica llamada RTTI (RunTime Type Identification) que define la interaccin entre objetos excluyendo variables de instancias o implementacin de mtodos. Las clases en Java tienen una representacin en el runtime que permite a los programadores interrogar por el tipo de clase y enlazar dinmicamente la clase con el resultado de la bsqueda. Distribuido Java se ha construido con extensas capacidades de interconexin TCP/IP. Existen libreras de rutinas para acceder e interactuar con protocolos como http y ftp. Esto permite a los programadores acceder a la informacin a travs de la red con tanta facilidad como a los ficheros locales. La verdad es que Java en s no es distribuido, sino que proporciona las libreras y herramientas para que los programas puedan ser distribuidos, es decir, que se corran en varias mquinas, interactuando. Robusto Java realiza verificaciones en busca de problemas tanto en tiempo de compilacin como en tiempo de ejecucin. La comprobacin de tipos en Java ayuda a detectar errores, lo antes posible, en el ciclo de desarrollo. Java obliga a la declaracin explcita de mtodos, reduciendo as las posibilidades de error. Maneja la memoria para eliminar las preocupaciones por parte del programador de la liberacin o corrupcin de memoria. Tambin implementa los arrays autnticos, en vez de listas enlazadas de punteros, con comprobacin de lmites, para evitar la posibilidad de sobreescribir o corromper memoria resultado de punteros que sealan a zonas equivocadas. Estas caractersticas reducen drsticamente el tiempo de desarrollo de aplicaciones en Java. Adems, para asegurar el funcionamiento de la aplicacin, realiza una verificacin de los bytecodes, que son el resultado de la compilacin de un programa Java. Es un cdigo de mquina virtual que es interpretado por el intrprete Java. No es el cdigo mquina directamente entendible por el hardware, pero ya ha pasado todas las fases del compilador: anlisis de instrucciones, orden de operadores, etc., y ya tiene generada la pila de ejecucin de rdenes.

99

Java proporciona, pues: Comprobacin de punteros Comprobacin de lmites de arrays Excepciones Verificacin de byte-codes Arquitectura neutral Para establecer Java como parte integral de la red, el compilador Java compila su cdigo a un fichero objeto de formato independiente de la arquitectura de la mquina en que se ejecutar. Cualquier mquina que tenga el sistema de ejecucin (run-time) puede ejecutar ese cdigo objeto, sin importar en modo alguno la mquina en que ha sido generado. Actualmente existen sistemas run-time para Solaris 2.x, SunOs 4.1.x, Windows 95, Windows NT, Linux, Irix, Aix, Mac, Apple y probablemente haya grupos de desarrollo trabajando en el porting a otras plataformas.

El cdigo fuente Java se "compila" a un cdigo de bytes de alto nivel independiente de la mquina. Este cdigo (byte-codes) est diseado para ejecutarse en una mquina hipottica que es implementada por un sistema run-time, que s es dependiente de la mquina. En una representacin en que tuvisemos que indicar todos los elementos que forman parte de la arquitectura de Java sobre una plataforma genrica, obtendramos una figura como la siguiente:

100

En ella podemos ver que lo verdaderamente dependiente del sistema es la Mquina Virtual Java (JVM) y las libreras fundamentales, que tambin nos permitiran acceder directamente al hardware de la mquina. Adems, habr APIs de Java que tambin entren en contacto directo con el hardware y sern dependientes de la mquina, como ejemplo de este tipo de APIs podemos citar: Java 2D: grficos 2D y manipulacin de imgenes Java Media Framework : Elementos crticos en el tiempo: audio, video... Java Animation: Animacin de objetos en 2D Java Telephony: Integracin con telefona Java Share: Interaccin entre aplicaciones multiusuario Java 3D: Grficos 3D y su manipulacin Seguro La seguridad en Java tiene dos facetas. En el lenguaje, caractersticas como los punteros o el casting implcito que hacen los compiladores de C y C++ se eliminan para prevenir el acceso ilegal a la memoria. Cuando se usa Java para crear un navegador, se combinan las caractersticas del lenguaje con protecciones de sentido comn aplicadas al propio navegador. El lenguaje C, por ejemplo, tiene lagunas de seguridad importantes, como son los errores de alineacin. Los programadores de C utilizan punteros en conjuncin con operaciones aritmticas. Esto le permite al programador que un puntero referencie a un lugar conocido de la memoria y pueda sumar (o restar) algn valor, para referirse a otro lugar de la memoria. Si otros programadores conocen nuestras estructuras de datos pueden extraer informacin confidencial de nuestro sistema. Con un lenguaje como C, se pueden tomar nmeros enteros aleatorios y convertirlos en punteros para luego acceder a la memoria:

101

printf( "Escribe un valor entero: " ); scanf( "%u",&puntero ); printf( "Cadena de memoria: %s\n",puntero ); Otra laguna de seguridad u otro tipo de ataque, es el Caballo de Troya. Se presenta un programa como una utilidad, resultando tener una funcionalidad destructiva. Por ejemplo, en UNIX se visualiza el contenido de un directorio con el comando ls. Si un programador deja un comando destructivo bajo esta referencia, se puede correr el riesgo de ejecutar cdigo malicioso, aunque el comando siga haciendo la funcionalidad que se le supone, despus de lanzar su carga destructiva. Por ejemplo, despus de que el caballo de Troya haya enviado por correo el /etc/shadow a su creador, ejecuta la funcionalidad de ls persentando el contenido del directorio. Se notar un retardo, pero nada inusual. El cdigo Java pasa muchos tests antes de ejecutarse en una mquina. El cdigo se pasa a travs de un verificador de byte-codes que comprueba el formato de los fragmentos de cdigo y aplica un probador de teoremas para detectar fragmentos de cdigo ilegal -cdigo que falsea punteros, viola derechos de acceso sobre objetos o intenta cambiar el tipo o clase de un objeto-. Si los byte-codes pasan la verificacin sin generar ningn mensaje de error, entonces sabemos que: El cdigo no produce desbordamiento de operandos en la pila El tipo de los parmetros de todos los cdigos de operacin son conocidos y correctos. No ha ocurrido ninguna conversin ilegal de datos, tal como convertir enteros en punteros. El acceso a los campos de un objeto se sabe que es legal: public, private, protected. No hay ningn intento de violar las reglas de acceso y seguridad establecidas

El Cargador de Clases tambin ayuda a Java a mantener su seguridad, separando el espacio de nombres del sistema de ficheros local, del de los recursos procedentes de la red. Esto limita cualquier aplicacin del tipo Caballo de Troya, ya que las clases se buscan primero entre las locales y luego entre las procedentes del exterior. Las clases importadas de la red se almacenan en un espacio de nombres privado, asociado con el origen. Cuando una clase del espacio de nombres privado accede a otra clase, primero se busca en las clases predefinidas (del sistema local) y luego en el espacio de nombres de la clase que hace la referencia. Esto imposibilita que una clase suplante a una predefinida. En resumen, las aplicaciones de Java resultan extremadamente seguras, ya que no acceden a zonas delicadas de memoria o de sistema, con lo cual evitan la interaccin de ciertos virus. Java no posee una semntica especfica para modificar la pila de programa, la memoria libre o utilizar objetos y mtodos de un programa sin los privilegios del kernel del sistema operativo. Adems, para evitar modificaciones por parte de los crackers de la red, implementa un mtodo ultraseguro de autentificacin por clave pblica. El Cargador de Clases puede verificar una firma digital antes de realizar una instancia de un objeto. Por tanto, ningn objeto se crea y almacena en memoria, sin que se validen los privilegios de acceso. Es decir, la seguridad se integra en el momento de compilacin, con el nivel de detalle y de privilegio que sea necesario. Dada, pues la concepcin del lenguaje y si todos los elementos se mantienen dentro del estndar marcado por Sun, no hay peligro. Java imposibilita, tambin, abrir ningn fichero de la mquina local (siempre que se realizan operaciones con archivos, stas trabajan sobre el disco duro de la

102

mquina de donde parti el applet), no permite ejecutar ninguna aplicacin nativa de una plataforma e impide que se utilicen otros ordenadores como puente, es decir, nadie puede utilizar nuestra mquina para hacer peticiones o realizar operaciones con otra. Adems, los intrpretes que incorporan los navegadores de la Web son an ms restrictivos. Bajo estas condiciones (y dentro de la filosofa de que el nico ordenador seguro es el que est apagado, desenchufado, dentro de una cmara acorazada en un bunker y rodeado por mil soldados de los cuerpos especiales del ejrcito), se puede considerar que Java es un lenguaje seguro y que los applets estn libres de virus. Respecto a la seguridad del cdigo fuente, no ya del lenguaje, JDK proporciona un desemsamblador de byte-code, que permite que cualquier programa pueda ser convertido a cdigo fuente, lo que para el programador significa una vulnerabilidad total a su cdigo. Utilizando javap no se obtiene el cdigo fuente original, pero s desmonta el programa mostrando el algoritmo que se utiliza, que es lo realmente interesante. La proteccin de los programadores ante esto es utilizar llamadas a programas nativos, externos (incluso en C o C++) de forma que no sea descompilable todo el cdigo; aunque as se pierda portabilidad. Esta es otra de las cuestiones que Java tiene pendientes. Portable Ms all de la portabilidad bsica por ser de arquitectura independiente, Java implementa otros estndares de portabilidad para facilitar el desarrollo. Los enteros son siempre enteros y adems, enteros de 32 bits en complemento a 2. Adems, Java construye sus interfaces de usuario a travs de un sistema abstracto de ventanas de forma que las ventanas puedan ser implantadas en entornos Unix, Pc o Mac. Interpretado El intrprete Java (sistema run-time) puede ejecutar directamente el cdigo objeto. Enlazar (linkar) un programa, normalmente, consume menos recursos que compilarlo, por lo que los desarrolladores con Java pasarn ms tiempo desarrollando y menos esperando por el ordenador. No obstante, el compilador actual del JDK es bastante lento. Por ahora, que todava no hay compiladores especficos de Java para las diversas plataformas, Java es ms lento que otros lenguajes de programacin, como C++, ya que debe ser interpretado y no ejecutado como sucede en cualquier programa tradicional. Se dice que Java es de 10 a 30 veces ms lento que C, y que tampoco existen en Java proyectos de gran envergadura como en otros lenguajes. La verdad es que ya hay comparaciones ventajosas entre Java y el resto de los lenguajes de programacin, y una ingente cantidad de folletos electrnicos que supuran fanatismo en favor y en contra de los distintos lenguajes contendientes con Java. Lo que se suele dejar de lado en todo esto, es que primero habra que decidir hasta que punto Java, un lenguaje en pleno desarrollo y todava sin definicin definitiva, est maduro como lenguaje de programacin para ser comparado con otros; como por ejemplo con Smalltalk, que lleva ms de 20 aos en cancha. La verdad es que Java para conseguir ser un lenguaje independiente del sistema operativo y del procesador que incorpore la mquina utilizada, es tanto interpretado como compilado. Y esto no es ningn contrasentido, me explico, el cdigo fuente escrito con cualquier editor se compila generando el byte-code. Este cdigo intermedio es de muy bajo nivel, pero sin alcanzar las instrucciones mquina propias de cada plataforma y no tiene nada que ver con el p-code de Visual Basic. El byte-code corresponde al 80% de las instrucciones de la aplicacin. Ese mismo cdigo es el que se puede ejecutar sobre cualquier plataforma. Para ello hace falta el run-time, que s es completamente dependiente de la mquina y del sistema operativo, que interpreta dinmicamente

103

el byte-code y aade el 20% de instrucciones que faltaban para su ejecucin. Con este sistema es fcil crear aplicaciones multiplataforma, pero para ejecutarlas es necesario que exista el run-time correspondiente al sistema operativo utilizado. Multithreaded Al ser multithreaded (multihilvanado, en mala traduccin), Java permite muchas actividades simultneas en un programa. Los threads (a veces llamados, procesos ligeros), son bsicamente pequeos procesos o piezas independientes de un gran proceso. Al estar los threads contruidos en el lenguaje, son ms fciles de usar y ms robustos que sus homlogos en C o C++. El beneficio de ser miltithreaded consiste en un mejor rendimiento interactivo y mejor comportamiento en tiempo real. Aunque el comportamiento en tiempo real est limitado a las capacidades del sistema operativo subyacente (Unix, Windows, etc.), an supera a los entornos de flujo nico de programa (single-threaded) tanto en facilidad de desarrollo como en rendimiento. Cualquiera que haya utilizado la tecnologa de navegacin concurrente, sabe lo frustrante que puede ser esperar por una gran imagen que se est trayendo. En Java, las imgenes se pueden ir trayendo en un thread independiente, permitiendo que el usuario pueda acceder a la informacin en la pgina sin tener que esperar por el navegador. Dinamico Java se beneficia todo lo posible de la tecnologa orientada a objetos. Java no intenta conectar todos los mdulos que comprenden una aplicacin hasta el tiempo de ejecucin. Las librera nuevas o actualizadas no paralizarn las aplicaciones actuales (siempre que mantengan el API anterior).

Java tambin simplifica el uso de protocolos nuevos o actualizados. Si su sistema ejecuta una aplicacin Java sobre la red y encuentra una pieza de la aplicacin que no sabe manejar, tal como se ha explicado en prrafos anteriores, Java es capaz de traer automticamente cualquiera de esas piezas que el sistema necesita para funcionar. Java, para evitar que los mdulos de byte-codes o los objetos o nuevas clases, haya que estar trayndolos de la red cada vez que se necesiten, implementa las opciones de persistencia, para que no se eliminen cuando de limpie la cach de la mquina. Cul es la ventaja de todo esto?Qu gano con Java?

104

Primero: No debes volver a escribir el cdigo si quieres ejecutar el programa en otra mquina. Un solo cdigo funciona para todos los browsers compatibles con Java o donde se tenga una Mquina Virtual de Java (Mac's, PC's, Sun's, etc). Segundo: Java es un lenguaje de programacin orientado a objetos, y tiene todos los beneficios que ofrece esta metodologa de programacion (ms adelante doy una pequea introduccin a la filosofa de objetos). Tercero: Un browser compatible con Java deber ejecutar cualquier programa hecho en Java, esto ahorra a los usuarios tener que estar insertando "plug-ins" y dems programas que a veces nos quitan tiempo y espacio en disco. Cuarto: Java es un lenguaje y por lo tanto puede hacer todas las cosas que puede hacer un lenguaje de programacin: Clculos matemticos, procesadores de palabras, bases de datos, aplicaciones grficas, animaciones, sonido, hojas de clculo, etc. Quinto: Si lo que me interesa son las pginas de Web, ya no tienen que ser estticas, se le pueden poner toda clase de elementos multimedia y permiten un alto nivel de interactividad, sin tener que gastar en paquetes carsimos de multimedia.

Todo esto suena muy bonito pero tambien se tienen algunas limitantes: La velocidad. Los programas hechos en Java no tienden a ser muy rpidos, supuestamente se est trabajando en mejorar esto.Como los programas de Java son interpretados nunca alcanzan la velocidad de un verdadero ejecutable. Java es un lenguaje de programacin. Esta es otra gran limitante, por ms que digan que es orientado a objetos y que es muy fcil de aprender sigue siendo un lenguaje y por lo tanto aprenderlo no es cosa fcil. Especialmente para los no programadores. Java es nuevo. En pocas palabras todava no se conocen bien todas sus capacidades.

Pero en general Java posee muchas ventajas y se pueden hacer cosas muy interesantes con esto. Hay que prestar especial atencin a lo que est sucediendo en el mundo de la computacin, a pesar de que Java es relativamente nuevo, posee mucha fuerza y es tema de moda en cualquier medio computacional. Muchas personas apuestan a futuro y piensan en Java. La pregunta es : Estarn en lo correcto? La verdad es que no se, pero este manual no es para filosofar sobre el futuro del lenguaje sino para aprender a programarlo. 1.3 HotJava HotJava, en pocas palabras, es un navegador con soporte Java (Java-enabled), desarrollado en Java. Como cualquier navegador de Web, HotJava puede decodificar HTML estndar y URLs estndares, aunque no soporta completamente el estndar HTML 3.0. La ventaja sobre el resto de navegadores, sin soporte Java, es que puede ejecutar programas Java sobre la red. La diferencia con Netscape, es que tiene implementado completamente los sistemas de seguridad que propone Java, esto significa que puede escribir y leer en el disco local, aunque esto hace disminuir la seguridad, ya que se pueden grabar en nuestro disco programas que contengan cdigo malicioso e introducirnos un virus, por ejemplo. No obstante, el utilizar esta caracterstica de HotJava es decisin del usuario. 1.4 Java para aplicaciones corporativas Java actualmente est en boca de todos, Java e Intranet son las palabras de moda. Pero, surge la pregunta de si esta es una buena tecnologa para desarrollar aplicaciones corporativas. Y la respuesta es afirmativa y voy a proponer argumentos para esa afirmacin. En donde la red sea algo crtico, Java facilita tremendamente la vida de la programacin corporativa.

105

Durante aos, las grandes empresas se han convencido de que la "red" corporativa es la arteria por donde fluye la sangre que mantiene vivo su negocio. Desde el gran servidor de sus oficinas centrales, hasta los servidores de las delegaciones, las estaciones de trabajo de los programadores y la marabunta de PCs, la informacin va fluyendo de unos a otros. Para muchas compaas, la Red es la Empresa. Si esta red no se mantiene sana, los pedidos no llegan, el inventario no se actualiza, el software no se desarrolla adecuadamente, los clientes no estn satisfechos y, fundamentalmente, el dinero no entra. La necesidad de diagnosticar y reducir la arterioesclerosis de la red, hace que se estn inyectando continuamente nuevas metodologas que subsanen este grave problema. Es Java la medicina? Est claro que cuando vemos un cepillo animado limpiando los dientes, cubos movindose en 3-D, o una banda de gatos locos en applets de Java, nos convencemos de que es el lenguaje idneo para Internet. Pero, qu pasa con las aplicaciones corporativas, sera una buena tecnologa all donde la red es el punto crtico? Vamos a intentar responder comparando las capacidades de Java contra la lista de necesidades de la red corporativa. Desarrollo rpido de aplicaciones Hace aos, se deca que los programadores pronto desapareceran. Los generadores automticos de programas, eliminaran a los generadores humanos y el mundo sera un lugar mejor para vivir. Desafortunadamente, quienes decan esto no tuvieron en cuenta una acelerada demanda de software de calidad para muy diferentes aplicaciones. Sin embargo, la tecnologa de objetos pronto vino a intentar facilitar la tarea, adoptando el modelo de "generar parte de un programa", as, generando la parte bsica de un programa (los objetos), se podra conectar con otras partes para proporcionar diferentes utilidades al usuario. El lenguaje C++ es una buena herramienta, pero no cumple totalmente la premisa. Visual Basic y NextStep, se acercan cada vez ms al poder de los objetos. Java facilita la creacin de entornos de desarrollo-aplicaciones de modo similar, pero adems es flexible, poderoso y efectivo. Los programadores ahora disponen de herramientas de programacin de calidad beta, que apuntan hacia esa meta, como son el Java WorkShop de SunSoft, el entorno Java de Borland, el Caf de Symantec, y pronto, herramientas ms sofisticadas como Netcode o FutureTense. Esto proporciona una gran progresin a los entornos de desarrollo Java. Aplicaciones efectivas y eficientes Las aplicaciones que se crean en grandes empresas deben ser ms efectivas que eficientes; es decir, conseguir que el programa funcione y el trabajo salga adelante es ms importante que el que lo haga eficientemente. Esto no es una crtica, es una realidad de la programacin corporativa. Al ser un lenguaje ms simple que cualquiera de los que ahora estn en el cajn de los programadores, Java permite a stos concentrarse en la mecnica de la aplicacin, en vez de pasarse horas y horas incorporando APIs para el control de las ventanas, controlando minuciosamente la memoria, sincronizando los ficheros de cabecera y corrigiendo los agnicos mensajes del linker. Java tiene su propio toolkit para interfaces, maneja por s mismo la memoria que utilice la aplicacin, no permite ficheros de cabecera separados (en aplicaciones puramente Java) y solamente usa enlace dinmico. Muchas de las implementaciones de Java actuales son puros intrpretes. Los byte-codes son interpretados por el sistema run-time de Java, la Mquina Virtual Java (JVM), sobre el ordenador del usuario. Aunque ya hay ciertos proveedores que ofrecen compiladores nativos Just-In-Time (JIT). Si la Mquina Virtual Java dispone de un compilador instalado, las secciones (clases) del byte-code de la aplicacin se compilarn hacia la arquitectura nativa del ordenador del usuario.

106

Los programas Java en ese momento rivalizarn con el rendimiento de programas en C++. Los compiladores JIT no se utilizan en la forma tradicional de un compilador; los programadores no compilan y distribuyen binarios Java a los usuarios. La compilacin JIT tiene lugar a partir del bytecode Java, en el sistema del usuario, como una parte (opcional) del entorno run-time local de Java. Muchas veces, los programadores corporativos, ansiosos por exprimir al mximo la eficiencia de su aplicacin, empiezan a hacerlo demasiado pronto en el ciclo de vida de la aplicacin. Java permite algunas tcnicas innovadoras de optimizacin. Por ejemplo, Java es inherentemente multithreaded, a la vez que ofrece posibilidades de multithread como la clase Thread y mecanismos muy sencillos de usar de sincronizacin; Java en s utiliza threads. Los desarrolladores de compiladores inteligentes pueden utilizar esta caracterstica de Java para lanzar un thread que compruebe la forma en que se est utilizando la aplicacin. Ms especficamente, este thread podra detectar qu mtodos de una clase se estn usando con ms frecuencia e invocar a sucesivos niveles de optimizacin en tiempo de ejecucin de la aplicacin. Cuanto ms tiempo est corriendo la aplicacin o el applet, los mtodos estarn cada vez ms optimizados (Guava de Softway es de este tipo). Si un compilador JIT est embebido en el entorno run-time de Java, el programador no se preocupa de hacer que la aplicacin se ejecute ptimamente. Siempre he pensado que en los Sistemas Operativos tendra que aplicarse esta filosofa; un optimizador progresivo es un paso ms hacia esta idea. Portabilidad para programador y programa En una empresa de relativo tamao hay una plyade diferente de ordenadores. Probablemente nos encontremos con estaciones de trabajo Sun para el desarrollo de software, hordas de PCs para cada empleado, algn Mac en el departamento de documentacin, una estacin de trabajo HP en administracin y una estacin SGI en la sala de demos. Desarrollar aplicaciones corporativas para un grupo tan diferente de plataformas en excesivamente complejo y caro. Hasta ahora era complicado convencer a los programadores de cada arquitectura que utilizasen un API comn para reducir el coste de las aplicaciones. Con un entorno run-time de Java portado a cada una de las arquitecturas de las plataformas presentes en la empresa y una buena librera de clases ("packages" en Java), los programadores pueden entenderse y encontrar muy interesante trabajar con Java. Esta posibilidad har tender a los programadores hacia Java, justo donde otros intentos anteriores con entornos universales (como Galaxy o XVT) han fracasado. Estos APIs eran simplemente inadecuados, no orientados a redes y, verdaderamente, pesados. Una vez que los programas estn escritos en Java, otro lado interesante del asunto es que los programadores tambin son portables. El grupo de programadores de la empresa puede ahora enfrentarse a un desarrollo para cualquiera de las plataformas. La parte del cliente y del servidor de una aplicacin estarn ahora escritas en el mismo lenguaje. Ya no ser necesario tener un grupo que desarrolle en Solaris en del departamento de I+D, programadores trabajando sobre Visual Basic en el departamento de documentacin y programadores sobre GNU en proyectos especiales; ahora todos ellos podrn estar juntos y formar el grupo de software de la empresa. Costes de desarrollo En contraste con el alto coste de los desarrollos realizados sobre estaciones de trabajo, el coste de creacin de una aplicacin Java es similar al de desarrollar sobre un PC. Desarrollar utilizando un software caro para una estacin de trabajo (ahora barata) es un problema en muchas empresas. La eficiencia del hardware y el poco coste de mantenimiento de una

107

estacin de trabajo Sun, por ejemplo, resulta muy atractivo para las empresas; pero el coste adicional del entorno de desarrollo con C++ es prohibitivo para la gran mayora de ellas. La llegada de Java e Intranet reducen considerablemente estos costes. Las herramientas Java ya no estn en el entorno de precios de millones de pesetas, sino a los niveles confortables de precio de las herramientas de PCs. Y con el crecimiento cada da mayor de la comunidad de desarrolladores de software freeware y shareware que incluso proporcionan el cdigo fuente, los programadores corporativos tienen un amplio campo donde moverse y muchas oportunidades de aprender y muchos recursos a su disposicin. El xito que Internet ha proporcionado a los equipos de software corporativos es un regalo. El precio del software es ahora el mismo para un poderoso equipo corriendo Unix que para un PC. Incluso Netscape tiene al mismo precio la versin Unix de su servidor Web SuiteSpot que la versin PC/NT. Esta es la filosofa de precios que parece ser ser la que se siga con las herramientas basadas en Java. Mantenimiento y soporte Un problema bien conocido que ocurre con el software corporativo es la demanda de cuidados y realimentacin. Java no es, ciertamente, la cura para la enfermedad del mantenimiento, pero tiene varias caractersticas que harn la vida del enfermero ms fcil. Uno de los componentes del JDK es javadoc. Si se usan ciertas convenciones en el cdigo fuente Java (como comenzar un comentario con /** y terminarlo con */), javadoc se puede fcilmente generar pginas HTML con el contenido de esos comentarios, que pueden visualizarse en cualquier navegador. La documentacin del API de Java ha sido creada de este modo. Esto hace que el trabajo de documentar el cdigo de nuevas clases Java sea trivial. Otro gran problema del desarrollador corporativo es la creacin y control de makefiles. Leerse un makefile es como estar leyendo la historia de empresa. Normalmente se pasan de programador a programador, quitando la informacin que no es esencial, siempre que se puede. Esto hace que muchos de los makefiles de las aplicaciones contengan docenas de libreras, una mirada de ficheros de cabecera y ultra-confusos macros. Es como mirar en el estmago de la ballena de Jons. Java reduce las dependencia de complejos makefiles drsticamente. Primero, no hay ficheros de cabecera. Java necesita que todo el cdigo fuente de una clase se encuentre en un solo fichero. Java tiene la inteligencia de make en el propio lenguaje para simplificar la compilacin de bytecodes. Por ejemplo: public class pepe { // Fichero: pepe.java Guitarra flamenca ; } public class guitarra { // Fichero: guitarra.java } % javac -verbose pepe.java

108

[parsed pepe.java in 720ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms] [checking class pepe] [parsed .\\Guitarra.java in 50ms] [wrote pepe.class] [checking class Guitarra] [wrote .\\Guitarra.class] [done in 2300ms] El compilador Java se da cuenta de que necesita compilar el fichero guitarra.java. Ahora vamos a forzarlo a que recompile pepe.java sin cambiar guitarra.java, podremos comprobar que el compilador de byte-code Java no recompila innecesariamente el fichero guitarra.java. % javac -verbose pepe.java [parsed pepe.java in 440ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 160ms] [checking class pepe] [loaded .\\Guitarra.java in 0ms] [wrote pepe.class] [done in 1860ms] Ahora, si modificamos guitarra.java (aadiendo, por ejemplo, otro miembro a la clase) y compilamos pepe.java, el compilador Java se dar cuenta de que debe recompilar tanto pepe.java como guitarra.java % javac -verbose pepe.java [parsed pepe.java in 710ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms] [checking class pepe] [parsed .\\Guitarra.java in 0ms] [wrote pepe.class] [checking class Guitarra]

109

[wrote .\\Guitarra.class] [done in 2640ms] En el libro Just Java de Peter van der Linden hay un captulo excelente acerca del compilador de Java, si tienes oportunidad, no dejes de leerlo. Aprendizaje Si la empresa est llena de programadores de C++ con alguna experiencia en el manejo de librera grficas, aprendern rpidamente lo esencial de Java. Si el equipo de ingenieros no conoce C++, pero maneja cualquier otro lenguaje de programacin orientada a objetos, les llevar pocas semanas dominar la base de Java. Lo que s que no es cierto es que haya que aprender C++ antes de aprender Java. Si los ingenieros de la empresa no conocen ningn lenguaje orientado a objetos, s que tienen que aprender los fundamentos de esta tecnologa antes de nada, y luego aplicarlos a la programacin con Java. El anlisis y diseo orientado a objetos debe ser comprendido antes de intentar nada con Java. Los programadores de Java sin un fondo de conocimientos de OOA/D producirn cdigo pobre. Adems, los libros sobre Java crecen como la espuma, ya hay ms de 25 publicados, y si buscas "Progamming in Java" en la Red, encontrars 312 Web sites, y 30 ms dedicados a "Learning Java". Y si esto, evidentemente, no es el sustituto de un instructor humano, hay ya varias empresas que ofrecen enseanza de Java, entre ellas, Sun.

2. INSTALACIN DEL JDK


Actualmente ya hay entornos de desarrollo integrados completos para Java, diferentes del JDK de Sun. Symantec dispone de un compilador de Java para Windows 95 y Windows NT, con las ventajas del aumento de velocidad de proceso y capacidades multimedia que esto proporciona, Symantec Caf. Borland tambin est trabajando en ello y la nueva versin de su entorno de desarrollo soporta Java. Sun ha lanzado la versin comercial de su propio entorno de desarrollo para Java, el Java Workshop, enteramente escrito en Java. Y Microsoft ha puesto en el mercado Visual J++, que sigue el estilo de todas sus herramientas de desarrollo. No obstante, trataremos solamente el JDK, que hasta el momento es lo ms conocido. El entorno bsico del JDK de Java que proporciona Sun est formado por herramientas en modo texto, que son: java, intrprete que ejecuta programas en byte-code. javac, compilador de Java que convierte el cdigo fuente en byte-code. javah, crea ficheros de cabecera para implementar mtodos para cualquier clase. javap, es un descompilador de byte-code a cdigo fuente Java. javadoc, es un generador automtico de documentos HTML a partir del cdigo fuente Java. javaprof, es un profiler para aplicaciones de un solo thread. HotJava, es un navegador Web escrito completamente en Java. El entorno habitual pues, consiste en un navegador que pueda ejecutar applets, un compilador que convierta el cdigo fuente Java a byte-code y el intrprete Java para ejecutar los programas. Estos son los componenetes bsicos para desarrollar algo en Java. No obstante se necesita un editor para escribir el cdigo fuente, y no son estrictamente necesarias otras herramientas como el debugger, un entorno visual, la documentacin o un visualizador de jerarqua de clases. Tan es as, que disponiendo del navegador Netscape 2.0 no se necesita ni tan siquiera el JDK (a peticin de varios amigos que disfrutan del uso de Linux pero no disponen de soporte ELF para poder utilizar el JDK portado por Randy Chapman, les indicar como conseguir utilizar el compilador embebido en Netscape). 2.1 Windows

110

La versin del JDK para Windows es un archivo autoextraible. Se necesitan alrededor de 6 Mb de espacio libre en disco. Ejecutar el fichero, que desempaquetar el contenido del archivo. El directorio donde se instale no es importante, pero supondremos que se instala en el raiz del disco C:, en cuyo caso los archivos colgarn de c:\java. Es necesario aadir c:\java\bin a la variable de entorno PATH. Adems de los ficheros java, el JDK incluye dos libreras dinmicas, MSVCRT20.DLL y MFC30.DLL, que se instalarn en el directorio de Java. Si tienes ninguna copia de estos ficheros en tu ordenador (probablemente en el directorio system de Windows) copia estos ficheros en el directorio c:\java\bin. Si estos ficheros ya estn en tu ordenador, elimina las copias extra que instala el JDK. 2.2 Solaris La versin del JDK para Solaris es un fichero tar comprimido. Se necesitan alrededor de 9 Mb de disco para descomprimir el JDK, aunque el doble de espacio sera una cifra ms cmoda. Ejecutar los siguientes comandos: % uncompress JDK-beta-solaris2-sparc.tar.Z % tar xvf JDK-beta-solaris2-sparc-tar Puedes descomprimir el archivo en tu directorio home, o, si tienes privilegios de supervisor, en algn sitio ms conveniente de /usr/local para que todos los usuarios tengan acceso a los ficheros. Sin embargo, los privilegios del supervisor no son necesarios para instalar y ejecutar Java. Por simplicidad, supondr que has descomprimido el JDK en /usr/local, aunque el path completo donde se haga no tiene relevancia (tambin es posible colocarlo en /opt que es donde residen todas las aplicaciones de Solaris). Si lo has colocado en un sitio diferente, simplemente sustituye /usr/local por ese directorio (si lo has descomprimido en tu home, puedes utilizar ~/java y ~/hotjava, en vez del path completo). Es necesario aadir /usr/local/java/bin a la variable de entorno PATH. Utiliza el siguiente comando (suponiendo que tengas el shell csh o tcsh): set path=($PATH /usr/local/java/bin) Tambin puedes aadir esta lnea al final del fichero .profile y .cshrc, y ya tienes el sistema listo para ejecutar applets. Si quieres desembarazarte de la ventana que aparece cada vez que lances el appletviewer con la licencia de Sun, crea un directorio que se llame .hotjava en el directorio java/bin y ya no volvers a verla. 2.3 Linux Necesitas un kernel que soporte binarios ELF, por lo tanto tu Linux debe ser la versin 1.2.13 o superior, las anteriores tienen un bug que hacen que javac no funcione. Necesitas tambin Netscape, versin 2.0b4 o posterior. Sobre la versin 1.2.13 del kernel de Linux, hay que seguir los pasos que indico para conseguir que JDK funcione: Bajarse el JDK, linux.jdk-1.0-try4.static-motif.tar.gz y

linux.jdk-1.0 try1.common.tar.gz a /usr/local, descomprimirlo y hacer 'tar xvf'

111

En el fichero .java_wrapper (si no existe, crearlo) cambiar las variable J_HOME y PRG, para que queden como:

J_HOME=/usr/local/java PRG=/usr/local/java/bin Bajarse la librera libc.5.2.18.bin.tar.gz a /, descomprimirla, hacer 'tar xvf'. Asegurarse de que /lib/libc.so.5 es un link simblico a este nuevo fichero.Si no lo es, hacer el /lib 'ln -s libc.so.5.2.18 libc.so.5' Bajarse ld-so.1.7.14.tar.gz a un directorio temporal, descomprimirlo y hacer 'tar xvf'. Ejecutar 'instldso.sh' y eliminar el directorio temporal. Aadir /usr/local/java a la variable de entorno PATH. Si se desea que est fijada para todos los usuarios, incorporar el directorio a la varible PATH que se fija en el fichero /etc/profile. Bajarse netscape-v202-export.i486-unknown-linux.tar.z a usr/local/netscape, descomprimirlo y hacer 'tar xvf' Crear un link en /usr/local/bin a /usr/local/netscape/netscape

Esto debera ser suficiente para compilar cualquier cosa en Java/Linux. En caso de tener problemas, es el momento de recurrir a las FAQ. Siguiendo los pasos indicados ya se puede ejecutar el ejemplo del Tic-Tac-Toe que propone la hoja de instalacin que Sun ha incluido en todas sus versiones y que en Linux consistira en cambiarse al directorio de la demo: % cd /usr/local/java/demo/TicTacToe ejecutar el visualizador de applets sobre la pgina html: % appletviewer example1.html y a jugar a las tres en raya. Por cierto, que el algoritmo que usa el ordenador est falseado por lo que es posible ganarle. 2.4 Compilacin sin JDK Parece raro, pero se puede conseguir. Lo nico necesario es el navegador Netscape 2.0. Este navegador, junto con la mquina virtual Java (JVM) y el sistema run-time, tiene un compilador Java. Si no se dispone del Java Development Kit (JDK), que no est disponible para todas las plataformas, pero s de la versin de Netscape para nuestra plataforma, aqu van los pasos a seguir para utilizar el compilador de Java embebido en Netscape.

112

Como necesito partir de algn punto para tomarlo como referencia, voy a suponer que estamos sobre Linux y que vamos a prescindir del JDK de Randy Chapman. Lo que habra que hacer sera lo siguiente. Primero. Instalar Netscape en el ordenador. Asegurarse de entender perfectamente y leerse hasta el final el fichero README, para seguir las instrucciones especficas de la instalacin de Netscape en la plataforma y que Netscape funcione perfectamente. En nuestro caso, en que vamos a intentar compilar cdigo Java con Netscape sobre Linux, la pieza clave es la situacin del fichero moz2_0.zip, que en mi mquina est en /usr/local/netscape/java/classes. Segundo. Extraer de una copia cualquiera del JDK (aunque sea de otra plataforma), el fichero java/lib/classes.zip y guardarlo en el mismo sitio que el fichero moz2_0.zip; esta localizacin no es necesaria, pero simplifica la estructura. Tercero. Fijar la variable de entorno CLASSPATH para que Netscape pueda encontrar sus propias clases adems de las clases del Java de Sun. Asegurarse de incluir el "directorio actual", para poder compilar a la vez que se usan los ficheros .zip de Netscape y Sun. Por ejemplo:

setenv CLASSPATH .:/usr/local/netscape/java/classes/moz2_0.zip : /usr/local/netscape/java/classes/classes.zip Cuarto. Compilar el cdigo Java (applet o aplicacin) con el comando: netscape -java sun.tools.javac.Main [fichero].java (sustituir el nombre del fichero con el cdigo Java en vez de [fichero]). Esto convertir el cdigo fuente Java en byte-code, generndose el archivo [fichero].class. Quinto. Comprobar si se puede ejecutar la aplicacin con el comando:

netscape -java [clase] (sustituir el nombre de la clase de la aplicacin -la que contiene la rutina mainen vez de [clase]). Sexto. Si se ha compilado un applet Java, construir una pgina html que lo utilice para visualizarlo con el navegador en su forma normal. O tambin se puede visualizar utilizando el appletviewer, ejecutando:

netscape -java sun.applet.AppletViewer [clase] Desgraciadamente, la sentencia anterior no parece funcionar en todos los sistemas. Hay amigos mos que no han sido capaces de visualizar applets con este mtodo. Para aprovechar el tiempo, se puede crear un script que recoja los pasos 3, 4 y 6. Si estamos utilizando el csh, el contenido del script sera:

113

#/bin/csh -f setenv CLASSPATH .:/usr/local/netscape/java/classes/moz2_0.zip: /usr/local/netscape/java/classes/classes.zip netscape -java sun.tools.javac.Main $1 y lo almacenaramos como javac. Se ha de hacer el script ejecutable y cambiar /bin/csh por el path completo donde est situado el csh. De forma semejante podemos definir el intrprete java y el appletviewer, sustituyendo la lnea adecuada de llamada a Netscape.

3. CONCEPTOS BSICOS DE JAVA


3.1 Programacin en Java Cuando se programa en Java, se coloca todo el cdigo en mtodos, de la misma forma que se escriben funciones en lenguajes como C. Comentarios En Java hay tres tipos de comentarios: // comentarios para una sola lnea /* comentarios de una o ms lneas */ /** comentario de documentacin, de una o ms lneas */ Los dos primeros tipos de comentarios son los que todo programador conoce y se utilizan del mismo modo. Los comentarios de documentacin, colocados inmediatamente antes de una declaracin (de variable o funcin), indican que ese comentario ha de ser colocado en la documentacin que se genera automticamente cuando se utiliza la herramienta de Java, javadoc. Dichos comentarios sirven como descripcin del elemento declarado permitiendo generar una documentacin de nuestras clases escrita al mismo tiempo que se genera el cdigo. En este tipo de comentario para documentacin, se permite la introduccin de algunos tokens o palabras clave, que harn que la informacin que les sigue aparezca de forma diferente al resto en la documentacin. Identificadores Los identificadores nombran variables, funciones, clases y objetos; cualquier cosa que el programador necesite identificar o usar.

114

En Java, un identificador comienza con una letra, un subrayado (_) o un smbolo de dlar ($). Los siguientes caracteres pueden ser letras o dgitos. Se distinguen las maysculas de las minsculas y no hay longitud mxima. Seran identificadores vlidos: identificador nombre_usuario Nombre_Usuario _variable_del_sistema $transaccion y su uso sera, por ejemplo: int contador_principal; char _lista_de_ficheros; float $cantidad_en_Ptas; Palabras clave Las siguientes son las palabras clave que estn definidas en Java y que no se pueden utilizar como indentificadores: abstract continue for new switch boolean default goto null synchronized break do if package this byte double implements private threadsafe byvalue else import protected throw case extends instanceof public transient catch false int return true char final interface short try class finally long static void const float native super while Palabras Reservadas

115

Adems, el lenguaje se reserva unas cuantas palabras ms, pero que hasta ahora no tienen un cometido especfico. Son: cast future generic inner operator outer rest var Literales Un valor constante en Java se crea utilizando una representacin literal de l. Java utiliza cinco tipos de elementos: enteros, reales en coma flotante, booleanos, caracteres y cadenas, que se pueden poner en cualquier lugar del cdigo fuente de Java. Cada uno de estos literales tiene un tipo correspondiente asociado con l. Enteros: byte 8 bits complemento a dos short 16 bits complemento a dos int 32 bits complemento a dos long 64 bits complemento a dos Por ejemplo: 21 077 0xDC00 Reales en coma flotante: float 32 bits IEEE 754 double 64 bits IEEE 754 Por ejemplo: 3.14 2e12 3.1E12 Booleanos: true false Caracteres: Por ejemplo: a \t \u???? [????] es un nmero unicode Cadenas: Por ejemplo: "Esto es una cadena literal" Arrays Se pueden declarar en Java arrays de cualquier tipo:

116

char s[]; int iArray[]; Incluso se pueden construir arrays de arrays: int tabla[][] = new int[4][5]; Los lmites de los arrays se comprueban en tiempo de ejecucin para evitar desbordamientos y la corrupcin de memoria. En Java un array es realmente un objeto, porque tiene redefinido el operador []. Tiene una funcin miembro: length. Se puede utilizar este mtodo para conocer la longitud de cualquier array. int a[][] = new int[10][3]; a.length; /* 10 */ a[0].length; /* 3 */ Para crear un array en Java hay dos mtodos bsicos. Crear un array vaco: int lista[] = new int[50]; o se puede crear ya el array con sus valores iniciales: String nombres[] = { "Juan","Pepe","Pedro","Maria" }; Esto que es equivalente a: String nombres[]; nombres = new String[4]; nombres[0] = new String( "Juan" ); nombres[1] = new String( "Pepe" ); nombres[2] = new String( "Pedro" ); nombres[3] = new String( "Maria" ); No se pueden crear arrays estticos en tiempo de compilacin: int lista[50]; // generar un error en tiempo de compilacin Tampoco se puede rellenar un array sin declarar el tamao con el operador new:

117

int lista[]; for( int i=0; i < 9; i++ ) lista[i] = i; Es decir, todos los arrays en Java son estticos. Para convertir un array en el equivalente a un array dinmico en C/C++, se usa la clase vector, que permite operaciones de insercin, borrado, etc. en el array. Operadores Los operadores de Java son muy parecidos en estilo y funcionamiento a los de C. En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden de precedencia: . [] () ++ -! ~ instanceof

*/% +<< >> >>> < > <= >= == != &^| && || ?: = op= (*= /= %= += -= etc.) , Los operadores numricos se comportan como esperamos: int + int = int Los operadores relacionales devuelven un valor booleano. Para las cadenas, se pueden utilizar los operadores relacionales para comparaciones adems de + y += para la concatenacin: String nombre = "nombre" + "Apellido";

118

El operador = siempre hace copias de objetos, marcando los antiguos para borrarlos, y ya se encargar el garbage collector de devolver al sistema la memoria ocupada por el objeto eliminado. Separadores Slo hay un par de secuencias con otros caracteres que pueden aparecer en el cdigo Java; son los separadores simples, que van a definir la forma y funcin del cdigo. Los separadores admitidos en Java son: () - parntesis. Para contener listas de parmetros en la definicin y llamada a mtodos. Tambin se utiliza para definir precedencia en expresiones, contener expresiones para control de flujo y rodear las conversiones de tipo. {} - llaves. Para contener los valores de matrices inicializadas automticamente. Tambin se utiliza para definir un bloque de cdigo, para clases, mtodos y mbitos locales. [ ] - corchetes. Para declarar tipos matriz. Tambin se utiliza cuando se referencian valores de matriz. ; - punto y coma. Separa sentencias. , - coma. Separa identificadores consecutivos en una declaracin de variables. Tambin se utiliza para encadenar sentencias dentro de una sentencia for. . - punto. Para separar nombres de paquete de subpaquetes y clases. Tambin se utiliza para separar una variable o mtodo de una variable de referencia. 3.2 Control de Flujo Muchas de las sentencias de control del flujo del programa se han tomado del C: Sentencias de Salto if/else if( Boolean ) { sentencias; } else { sentencias; } switch switch( expr1 ) { case expr2:

119

sentencias; break; case expr3: sentencias; break; default: sentencias; break; } Sentencias de Bucle Bucles for for( expr1 inicio; expr2 test; expr3 incremento ) { sentencias; } El siguiente trocito de cdigo Java que dibuja varias lneas en pantalla alternando sus colores entre rojo, azul y verde. Este fragmento sera parte de una funcin Java (mtodo): int contador; for( contador=1; contador <= 12; contador++ ) { switch( contador % 3 ) { case 0: setColor( Color.red ); break; case 1: setColor( Color.blue ); break; case 2:

120

setColor( Color.green ); break; } g.drawLine( 10,contador*10,80,contador*10 ); } Tambin se soporta el operador coma (,) en los bucles for for( a=0,b=0; a < 7; a++,b+=2 ) Bucles while while( Boolean ) { sentencias; } Bucles do/while do { sentencias; }while( Boolean ); Excepciones try-catch-throw try { sentencias; } catch( Exception ) { sentencias; } Java implementa excepciones para facilitar la construccin de cdigo robusto. Cuando ocurre un error en un programa, el cdigo que encuentra el error lanza una excepcin, que se puede capturar y recuperarse de ella. Java proporciona muchas excepciones predefinidas. Control General del Flujo break [etiqueta]

121

continue [etiqueta] return expr; etiqueta: sentencia; En caso de que nos encontremos con bucles anidados, se permite el uso de etiquetas para poder salirse de ellos, por ejemplo: uno: for( ) { dos: for( ) { continue; // seguira en el bucle interno continue uno; // seguira en el bucle principal break uno; // se saldra del bucle principal } } En el cdigo de una funcin siempre hay que ser consecuentes con la declaracin que se haya hecho de ella. Por ejemplo, si se declara una funcin para que devuelva un entero, es imprescindible que se coloque un return final para salir de esa funcin, independientemente de que haya otros en medio del cdigo que tambin provoquen la salida de la funcin. En caso de no hacerlo se generar un Warning, y el cdigo Java no se puede compilar con Warnings. int func() { if( a == 0 ) return 1; return 0; // es imprescindible porque se retorna un entero } 3.3 Clases Las clases son lo ms simple de Java. Todo en Java forma parte de una clase, es una clase o describe como funciona una clase. El conocimiento de las clases es fundamental para poder entender los programas Java.

122

Todas las acciones de los programas Java se colocan dentro del bloque de una clase o un objeto. Todos los mtodos se definen dentro del bloque de la clase, Java no soporta funciones o variables globales. Esto puede despistar a los programadores de C++, que pueden definir mtodos fuera del bloque de la clase, pero esta posibilidad es ms un intento de no separarse mucho y ser compatible con C, que un buen diseo orientado a objetos. As pues, el esqueleto de cualquier aplicacin Java se basa en la definicin de una clase. Todos los datos bsicos, como los enteros, se deben declarar en las clases antes de hacer uso de ellos. En C la unidad fundamental son los ficheros con cdigo fuente, en Java son las clases. De hecho son pocas las sentencias que se pueden colocar fuera del bloque de una clase. La palabra clave import (equivalente al #include) puede colocarse al principio de un fichero, fuera del bloque de la clase. Sin embargo, el compilador reemplazar esa sentencia con el contenido del fichero que se indique, que consistir, como es de suponer, en ms clases. Tipos de Clases Hasta ahora slo se ha utilizado la palabra clave public para calificar el nombre de las clases que hemos visto, pero hay tres modificadores ms. Los tipos de clases que podemos definir son: abstract Una clase abstract tiene al menos un mtodo abstracto. Una clase abstracta no se instancia, sino que se utiliza como clase base para la herencia. final Una clase final se declara como la clase que termina una cadena de herencia. No se puede heredar de una clase final. Por ejemplo, la clase Math es una clase final. public Las clases public son accesibles desde otras clases, bien sea directamente o por herencia. Son accesibles dentro del mismo paquete en el que se han declarado. Para acceder desde otros paquetes, primero tienen que ser importadas. synchronizable Este modificador especifica que todos los mtodos definidos en la clase son sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde distintos threads; el sistema se encarga de colocar los flags necesarios para evitarlo. Este mecanismo hace que desde threads diferentes se puedan modificar las mismas variables sin que haya problemas de que se sobreescriban. 3.4 Variables y Mtodos de Instancia Una clase en Java puede contener variables y mtodos. Las variables pueden ser tipos primitivos como int, char, etc. Los mtodos son funciones. Por ejemplo, en el siguiente trozo de cdigo podemos observarlo: public MiClase { int i;

123

public MiClase() { i = 10; } public void Suma_a_i( int j ) { i = i + j; } } La clase MiClase contiene una variable (i) y dos mtodos, MiClase que es el constructor de la clase y Suma_a_i( int j ). Ambito de una variable Los bloques de sentencias compuestas en Java se delimitan con dos llaves. Las variables de Java slo son vlidas desde el punto donde estn declaradas hasta el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias compuestas, y cada una puede contener su propio conjunto de declaraciones de variables locales. Sin embargo, no se puede declarar una variable con el mismo nombre que una de mbito exterior. El siguiente ejemplo intenta declarar dos variables separadas con el mismo nombre. En C y C++ son distintas, porque estn declaradas dentro de mbitos diferentes. En Java, esto es ilegal. Class Ambito { int i = 1; // mbito exterior { // crea un nuevo mbito int i = 2; // error de compilacin } } Mtodos y Constructores Los mtodos son funciones que pueden ser llamadas dentro de la clase o por otras clases. El constructor es un tipo especfico de mtodo que siempre tiene el mismo nombre que la clase. Cuando se declara una clase en Java, se pueden declarar uno o ms constructores opcionales que realizan la inicializacin cuando se instancia (se crea una ocurrencia) un objeto de dicha clase. Utilizando el cdigo de ejemplo anterior, cuando se crea una nueva instancia de MiClase, se crean (instancian) todos los mtodos y variables, y se llama al constructor de la clase: MiClase mc;

124

mc = new MiClase(); La palabra clave new se usa para crear una instancia de la clase. Antes de ser instanciada con new no consume memoria, simplemente es una declaracin de tipo. Despus de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc ser igual a 10. Se puede referenciar la variable (de instancia) i con el nombre del objeto: mc.i++; // incrementa la instancia de i de mc Al tener mc todas las variables y mtodos de MiClase, se puede usar la primera sintaxis para llamar al mtodo Suma_a_i() utilizando el nuevo nombre de clase mc: mc.Suma_a_i( 10 ); y ahora la variable mc.i vale 21. Finalizadores Java no utiliza destructores (al contrario que C++) ya que tiene una forma de recoger automticamente todos los objetos que se salen del alcance. No obstante proporciona un mtodo que, cuando se especifique en el cdigo de la clase, el reciclador de memoria (garbage collector) llamar: // Cierra el canal cuando este objeto es reciclado protected void finalize() { close(); } 3.5 Alcance de Objetos y Reciclado de Memoria Los objetos tienen un tiempo de vida y consumen recursos durante el mismo. Cuando un objeto no se va a utilizar ms, debera liberar el espacio que ocupaba en la memoria de forma que las aplicaciones no la agoten (especialmente las grandes). En Java, la recoleccin y liberacin de memoria es responsabilidad de un thread llamado automatic garbage collector (recolector automtico de basura). Este thread monitoriza el alcance de los objetos y marca los objetos que se han salido de alcance. Veamos un ejemplo: String s; // no se ha asignado todavia s = new String( "abc" ); // memoria asignada s = "def"; // se ha asignado nueva memoria // (nuevo objeto) Ms adelante veremos en detalle la clase String, pero una breve descripcin de lo que hace esto es; crear un objeto String y rellenarlo con los caracteres "abc" y crear otro (nuevo) String y colocarle los caracteres "def".

125

En esencia se crean dos objetos: Objeto String "abc" Objeto String "def" Al final de la tercera sentencia, el primer objeto creado de nombre s que contiene "abc" se ha salido de alcance. No hay forma de acceder a l. Ahora se tiene un nuevo objeto llamado s y contiene "def". Es marcado y eliminado en la siguiente iteracin del thread reciclador de memoria. 3.6 Herencia La Herencia es el mecanismo por el que se crean nuevos objetos definidos en trminos de objetos ya existentes. Por ejemplo, si se tiene la clase Ave, se puede crear la subclase Pato, que es una especializacin de Ave. class Pato extends Ave { int numero_de_patas; } La palabra clave extends se usa para generar una subclase (especializacin) de un objeto. Una Pato es una subclase de Ave. Cualquier cosa que contenga la definicin de Ave ser copiada a la clase Pato, adems, en Pato se pueden definir sus propios mtodos y variables de instancia. Se dice que Pato deriva o hereda de Ave. Adems, se pueden sustituir los mtodos proporcionados por la clase base. Utilizando nuestro anterior ejemplo de MiClase, aqu hay un ejemplo de una clase derivada sustituyendo a la funcin Suma_a_i(): import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); } } Ahora cuando se crea una instancia de MiNuevaClase, el valor de i tambin se inicializa a 10, pero la llamada al mtodo Suma_a_i() produce un resultado diferente: MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 );

126

En Java no se puede hacer herencia mltiple. Por ejemplo, de la clase aparato con motor y de la clase animal no se puede derivar nada, sera como obtener el objeto toro mecnico a partir de una mquina motorizada (aparato con motor) y un toro (aminal). En realidad, lo que se pretende es copiar los mtodos, es decir, pasar la funcionalidad del toro de verdad al toro mecnico, con lo cual no sera necesaria la herencia mltiple sino simplemente la comparticin de funcionalidad que se encuentra implementada en Java a travs de interfaces. 3.7 Control de Acceso Cuando se crea una nueva clase en Java, se puede especificar el nivel de acceso que se quiere para las variables de instancia y los mtodos definidos en la clase: public public void CualquieraPuedeAcceder(){} Cualquier clase desde cualquier lugar puede acceder a las variables y mtodos de instacia pblicos. protected protected void SoloSubClases(){} Slo las subclases de la clase y nadie ms puede acceder a las variables y mtodos de instancia protegidos. private private String NumeroDelCarnetDeIdentidad; Las variables y mtodos de instancia privados slo pueden ser accedidos desde dentro de la clase. No son accesibles desde las subclases. friendly (sin declaracin especfica) void MetodoDeMiPaquete(){} Por defecto, si no se especifica el control de acceso, las variables y mtodos de instancia se declaran friendly (amigas), lo que significa que son accesibles por todos los objetos dentro del mismo paquete, pero no por los externos al paquete. Es lo mismo que protected. Los mtodos protegidos (protected) pueden ser vistos por las clases derivadas, como en C++, y tambin, en Java, por los paquetes (packages). Todas las clases de un paquete pueden ver los mtodos protegidos de ese paquete. Para evitarlo, se deben declarar como private protected, lo que hace que ya funcione como en C++ en donde slo se puede acceder a las variables y mtodos protegidos de las clases derivadas. 3.8 Variables y Mtodos Estticos En un momento determinado se puede querer crear una clase en la que el valor de una variable de instancia sea el mismo (y de hecho sea la misma variable) para todos los objetos instanciados a

127

partir de esa clase. Es decir, que exista una nica copia de la variable de instancia. Se usar para ello la palabra clave static. class Documento extends Pagina { static int version = 10; } El valor de la variable version ser el mismo para cualquier objeto instanciado de la clase Documento. Siempre que un objeto instanciado de Documento cambie la variable version, sta cambiar para todos los objetos. De la misma forma se puede declarar un mtodo como esttico, lo que evita que el mtodo pueda acceder a las variables de instancia no estticas: class Documento extends Pagina { static int version = 10; int numero_de_capitulos; static void annade_un_capitulo() { numero_de_capitulos++; // esto no funciona } static void modifica_version( int i ) { version++; // esto si funciona } } La modificacin de la variable numero_de_capitulos no funciona porque se est violando una de las reglas de acceso al intentar acceder desde un mtodo esttico a una variable no esttica. Todas las clases que se derivan, cuando se declaran estticas, comparten la misma pgina de variables; es decir, todos los objetos que se generen comparten la misma zona de memoria. Las funciones estticas se usan para acceder solamente a variables estticas. class UnaClase { int var; UnaClase() { var = 5;

128

} UnaFuncion() { var += 5; } } En el cdigo anterior, si se llama a la funcin UnaFuncion a travs de un puntero a funcin, no se podra acceder a var, porque al utilizar un puntero a funcin no se pasa implcitamente el puntero al propio objeto (this). Sin embargo, s se podra acceder a var si fuese esttica, porque siempre estara en la misma posicin de memoria para todos los objetos que se creasen de UnaClase. 3.9 this Y super Al acceder a variables de instancia de una clase, la palabra clave this hace referencia a los miembros de la propia clase. Volviendo al ejemplo de MiClase, se puede aadir otro constructor de la forma siguiente: public class MiClase { int i; public MiClase() { i = 10; } // Este constructor establece el valor de i public MiClase( int valor ) { this.i = valor; // i = valor } public void Suma_a_i( int j ) { i = i + j; } } Aqu this.i se refiere al entero i en la clase MiClase.

129

Si se necesita llamar al mtodo padre dentro de una clase que ha reemplazado ese mtodo, se puede hacer referencia al mtodo padre con la palabra clave super: import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); super.Suma_a_i( j ); } } En el siguiente cdigo, el constructor establecer el valor de i a 10, despus lo cambiar a 15 y finalmente el mtodo Suma_a_i() de la clase padre (MiClase) lo dejar en 25: MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 ); 3.10 Clases Abstractas Una de las caractersticas ms tiles de cualquier lenguaje orientado a objetos es la posibilidad de declarar clases que definen como se utiliza solamente, sin tener que implementar mtodos. Esto es muy til cuando la implementacin es especfica para cada usuario, pero todos los usuarios tienen que utilizar los mismos mtodos. Un ejemplo de clase abstracta en Java es la clase Graphics: public abstract class Graphics { public abstract void drawLine( int x1,int y1,int x2, int y2 ); public abstract void drawOval( int x,int y,int width, int height ); public abstract void drawArc( int x,int y,int width, int height,int startAngle,int arcAngle ); .. }

130

Los mtodos se declaran en la clase Graphics, pero el cdigo que ejecutar el mtodo est en algn otro sitio: public class MiClase extends Graphics { public void drawLine( int x1,int y1,int x2,int y2 ) { <cdigo para pintar lneas -especfico de la arquitectura-> } } Cuando una clase contiene un mtodo abstracto tiene que declararse abstracta. No obstante, no todos los mtodos de una clase abstracta tienen que ser abstractos. Las clases abstractas no pueden tener mtodos privados (no se podran implementar) ni tampoco estticos. Una clase abstracta tiene que derivarse obligatoriamente, no se puede hacer un new de una clase abstracta. Una clase abstracta en Java es lo mismo que en C++ virtual func() = 0; lo que obliga a que al derivar de la clase haya que implementar forzosamente los mtodos de esa clase abstracta. 3.11 Interfaces Los mtodos abstractos son tiles cuando se quiere que cada implementacin de la clase parezca y funcione igual, pero necesita que se cree una nueva clase para utilizar los mtodos abstractos. Los interfaces proporcionan un mecanismo para abstraer los mtodos a un nivel superior. Un interface contiene una coleccin de mtodos que se implementan en otro lugar. Los mtodos de una clase son public, static y final. La principal diferencia entre interface y abstract es que un interface proporciona un mecanismo de encapsulacin de los protocolos de los mtodos sin forzar al usuario a utilizar la herencia. Por ejemplo: public interface VideoClip { // comienza la reproduccion del video void play(); // reproduce el clip en un bucle void bucle(); // detiene la reproduccion void stop();

131

} Las clases que quieran utilizar el interface VideoClip utilizarn la palabra implements y proporcionarn el cdigo necesario para implementar los mtodos que se han definido para el interface: class MiClase implements VideoClip { void play() { <cdigo> } void bucle() { <cdigo> } void stop() { <cdigo> } Al utilizar implements para el interface es como si se hiciese una accin de copiar-y-pegar del cdigo del interface, con lo cual no se hereda nada, solamente se pueden usar los mtodos. La ventaja principal del uso de interfaces es que una clase interface puede ser implementada por cualquier nmero de clases, permitiendo a cada clase compartir el interfaz de programacin sin tener que ser consciente de la implementacin que hagan las otras clases que implementen el interface. class MiOtraClase implements VideoClip { void play() { <cdigo nuevo> } void bucle() { <cdigo nuevo> } void stop() { <cdigo nuevo>

132

} 3.12 Mtodos Nativos Java proporciona un mecanismo para la llamada a funciones C y C++ desde nuestro cdigo fuente Java. Para definir mtodos como funciones C o C++ se utiliza la palabra clave native. public class Fecha { int ahora; public Fecha() { ahora = time(); } private native int time(); static { System.loadLibrary( "time" ); } } Una vez escrito el cdigo Java, se necesitan ejecutar los pasos siguientes para poder integrar el cdigo C o C++: Utilizar javah para crear un fichero de cabecera (.h) Utilizar javah para crear un fichero de stubs, es decir, que contiene la declaracin de las funciones Escribir el cdigo del mtodo nativo en C o C++, es decir, rellenar el cdigo de la funcin, completando el trabajo de javah al crear el fichero de stubs Compilar el fichero de stubs y el fichero .c en una librera de carga dinmica (DLL en Windows '95 o libXX.so en Unix) Ejecutar la aplicacin con el appletviewer

Ms adelante trataremos en profundidad los mtodos nativos, porque aaden una gran potencia a Java, al permitirle integrar a travs de librera dinmica cualquier algoritmo desarrollado en C o C+ +, lo cual, entre otras cosas, se utiliza como mtodo de proteccin contra la descompilacin completa del cdigo Java. 3.13 Paquetes La palabra clave package permite agrupar clases e interfaces. Los nombres de los paquetes son palabras separadas por puntos y se almacenan en directorios que coinciden con esos nombres. Por ejemplo, los ficheros siguientes, que contienen cdigo fuente Java: Applet.java, AppletContext.java, AppletStub.java, AudioClip.java

133

contienen en su cdigo la lnea: package java.applet; Y las clases que se obtienen de la compilacin de los ficheros anteriores, se encuentran con el nombre nombre_de_clase.class, en el directorio: java/applet Import Los paquetes de clases se cargan con la palabra clave import, especificando el nombre del paquete como ruta y nombre de clase (es lo mismo que #include de C/C++). Se pueden cargar varias clases utilizando un asterisco. import java.Date; import java.awt.*; Si un fichero fuente Java no contiene ningn package, se coloca en el paquete por defecto sin nombre. Es decir, en el mismo directorio que el fichero fuente, y la clase puede ser cargada con la sentencia import: import MiClase; Paquetes de Java El lenguaje Java proporciona una serie de paquetes que incluyen ventanas, utilidades, un sistema de entrada/salida general, herramientas y comunicaciones. En la versin actual del JDK, los paquetes Java que se incluyen son: java.applet Este paquete contiene clases diseadas para usar con applets. Hay una clase Applet y tres interfaces: AppletContext, AppletStub y AudioClip. java.awt El paquete Abstract Windowing Toolkit (awt) contiene clases para generar widgets y componentes GUI (Interfaz Grfico de Usuario). Incluye las clases Button, Checkbox, Choice, Component, Graphics, Menu, Panel, TextArea y TextField. java.io El paquete de entrada/salida contiene las clases de acceso a ficheros: FileInputStream y FileOutputStream. java.lang Este paquete incluye las clases del lenguaje Java propiamente dicho: Object, Thread, Exception, System, Integer, Float, Math, String, etc.

134

java.net Este paquete da soporte a las conexiones del protocolo TCP/IP y, adems, incluye las clases Socket, URL y URLConnection. java.util Este paquete es una miscelnea de clases tiles para muchas cosas en programacin. Se incluyen, entre otras, Date (fecha), Dictionary (diccionario), Random (nmeros aleatorios) y Stack (pila FIFO). 3.14 Referencias Java se asemeja mucho a C y C++. Esta similitud, evidentemente intencionada, es la mejor herramienta para los programadores, ya que facilita en gran manera su transicin a Java. Desafortunadamente, tantas similitudes hacen que no nos paremos en algunas diferencias que son vitales. La terminologa utilizada en estos lenguajes, a veces es la misma, pero hay grandes diferencias subyacentes en su significado. C tiene tipos de datos bsicos y punteros. C++ modifica un poco este panorama y le aade los tipos referencia. Java tambin especifica sus tipos primitivos, elimina cualquier tipo de punteros y tiene tipos referencia mucho ms claros. Todo este maremgnum de terminologa provoca cierta consternacin, as que vamos a intentar aclarar lo que realmente significa. Conocemos ya ampliamente todos los tipos bsicos de datos: datos base, integrados, primitivos e internos; que son muy semejantes en C, C++ y Java; aunque Java simplifica un poco su uso a los desarrolladores haciendo que el chequeo de tipos sea bastante ms rgido. Adems, Java aade los tipos boolean y hace imprescindible el uso de este tipo booleano en sentencias condicionales.

4. PROGRAMAS BSICOS EN JAVA


4.1 Una mnima aplicacin en java La aplicacin ms pequea posible es la que simplemente imprimir un mensaje en la pantalla. Tradicionalmente, el mensaje suele ser "Hola Mundo!". Esto es justamente lo que hace el siguiente fragmento de cdigo: // // Aplicacin HolaMundo de ejemplo // class HolaMundoApp { public static void main( String args[] ) { System.out.println( "Hola Mundo!" ) ; }

135

} HolaMundo Vamos ver en detalle la aplicacin anterior, lnea a lnea. Esas lneas de cdigo contienen los componenetes mnimos para imprimir Hola Mundo! en la pantalla. // // Aplicacin HolaMundo de ejemplo // Estas tres primera lneas son comentarios. Hay tres tipos de comentarios en Java, // es un comentario orientado a lnea. class HolaMundoApp { Esta lnea declara la clase HolaMundoApp. El nombre de la clase especificado en el fichero fuente se utiliza para crear un fichero nombredeclase.class en el directorio en el que se compila la aplicacin. En nuestro caso, el compilador crear un fichero llamado HolaMundoApp.class. public static void main( String args[] ) { Esta lnea especifica un mtodo que el intrprete Java busca para ejecutar en primer lugar. Igual que en otros lenguajes, Java utiliza una palabra clave main para especificar la primera funcin a ejecutar. En este ejemplo tan simple no se pasan argumentos. public significa que el mtodo main puede ser llamado por cualquiera, incluyendo el intrprete Java. static es una palabra clave que le dice al compilador que main se refiere a la propia clase HolaMundoApp y no a ninguna instancia de la clase. De esta forma, si alguien intenta hacer otra instancia de la clase, el mtodo main no se instanciara. void indica que main no devuelve nada. Esto es importante ya que Java realiza una estricta comprobacin de tipos, incluyendo los tipos que se ha declarado que devuelven los mtodos. args[] es la declaracin de un array de Strings. Estos son los argumentos escritos tras el nombre de la clase en la lnea de comandos: %java HolaMundoApp arg1 arg2 ... System.out.println( "Hola Mundo!" ); Esta es la funcionalidad de la aplicacin. Esta lnea muestra el uso de un nombre de clase y mtodo. Se usa el mtodo println() de la clase out que est en el paquete System. El mtodo println() toma una cadena como argumento y la escribe en el stream de salida estndar; en este caso, la ventana donde se lanza la aplicacin. }

136

} Finalmente, se cierran las llaves que limitan el mtodo main() y la clase HolaMundoApp. Compilacion y Ejecucion de HolaMundo Vamos a ver a continuacin como podemos ver el resultado de nuestra primera aplicacin Java en pantalla. Generaremos un fichero con el cdigo fuente de la aplicacin, lo compilaremos y utilizaremos el intrprete java para ejecutarlo. Ficheros Fuente Java Los ficheros fuente en Java terminan con la extensin ".java". Crear un fichero utilizando cualquier editor de texto ascii que tenga como contenido el cdigo de las ocho lneas de nuestra mnima aplicacin, y salvarlo en un fichero con el nombre de HolaMundoApp.java. Para crear los ficheros con cdigo fuente Java no es necesario un procesador de textos, aunque puede utilizarse siempre que tenga salida a fichero de texto plano o ascii, sino que es suficiente con cualquier otro editor. Compilacin El compilador javac se encuentra en el directorio bin por debajo del directorio java, donde se haya instalado el JDK. Este directorio bin, si se han seguido las instrucciones de instalacin, debera formar parte de la variable de entorno PATH del sistema. Si no es as, tendra que revisar la Instalacin del JDK. El compilador de Java traslada el cdigo fuente Java a byte-codes, que son los componentes que entiende la Mquina Virtual Java que est incluida en los navegadores con soporte Java y en appletviewer. Una vez creado el fichero fuente HolaMundoApp.java, se puede compilar con la lnea siguiente: %javac HolaMundoApp.java Si no se han cometido errores al teclear ni se han tenido problemas con el path al fichero fuente ni al compilador, no debera aparecer mensaje alguno en la pantalla, y cuando vuelva a aparecer el prompt del sistema, se debera ver un fichero HolaMundoApp.class nuevo en el directorio donde se encuentra el fichero fuente. Si ha habido algn problema, en Problemas de compilacin al final de esta seccin, hemos intentado reproducir los que ms frecuentemente se suelen dar, se pueden consultar por si pueden aportar un poco de luz al error que haya aparecido. Ejecucin Para ejecutar la aplicacin HolaMundoApp, hemos de recurrir al intrprete java, que tambin se encuentra en el directorio bin, bajo el directorio java. Se ejecutar la aplicacin con la lnea: %java HolaMundoApp y debera aparecer en pantalla la respuesta de Java: %Hola Mundo!

137

El smbolo % representa al prompt del sistema, y lo utilizaremos para presentar las respuestas que nos ofrezca el sistema como resultado de la ejecucin de los comandos que se indiquen en pantalla o para indicar las lneas de comandos a introducir. Problemas de compilacin A continuacin presentamos una lista de los errores ms frecuentes que se presentan a la hora de compilar un fichero con cdigo fuente Java, nos basaremos en errores provocados sobre nuestra mnima aplicacin Java de la seccin anterior, pero podra generalizarse sin demasiados problemas. %javac: Command not found No se ha establecido correctamente la variable PATH del sistema para el compilador javac. El compilador javac se encuentra en el directorio bin, que cuelga del directorio java, que cuelga del directorio donde se haya instalado el JDK (Java Development Kit). %HolaMundoApp.java:3: Method printl(java.lang.String) not found in class java.io.PrintStream. System.out.printl( "HolaMundo!); ^ Error tipogrfico, el mtodo es println no printl.

%In class HolaMundoApp: main must be public and static Error de ejecucin, se olvid colocar la palabra static en la declaracin del mtodo main de la aplicacin.

%Cant find class HolaMundoApp Este es un error muy sutil. Generalmente significa que el nombre de la clase es distinto al del fichero que contiene el cdigo fuente, con lo cual el fichero nombre_fichero.class que se genera es diferente del que cabra esperar. Por ejemplo, si en nuestro fichero de cdigo fuente de nuestra aplicacin HolaMundoApp.java colocamos en vez de la declaracin actual de la clase HolaMundoApp, la lnea: class HolaMundoapp { se crear un fichero HolaMundoapp.class, que es diferente del HolaMundoApp.class, que es el nombre esperado de la clase; la diferencia se encuentra en la a minscula y mayscula. 4.2 El Visor de applets de Sun (appletviewer) El visualizador de applets (appletviewer) es una aplicacin que permite ver en funcionamiento applets, sin necesidad de la utilizacin de un navegador World-Wide-Web como HotJava, Microsoft Explorer o Nescape. En adelante, recurriremos muchas veces a l, ya que el objetivo del tutorial es el lenguaje Java. Applet

138

La definicin ms extendida de applet, muy bien resumida por Patrick Naughton, indica que un applet es "una pequea aplicacin accesible en un servidor Internet, que se transporta por la red, se instala automticamente y se ejecuta in situ como parte de un documento web". Claro que as la definicin establece el entorno (Internet, Web, etc.). En realidad, un applet es una aplicacin pretendidamente corta (nada impide que ocupe ms de un gigabyte, a no ser el pensamiento de que se va a transportar por la red y una mente sensata) basada en un formato grfico sin representacin independiente: es decir, se trata de un elemento a embeber en otras aplicaciones; es un componente en su sentido estricto. Un ejemplo en otro mbito de cosas podra ser el siguiente: Imaginemos una empresa, que cansada de empezar siempre a codificar desde cero, disea un formulario con los datos bsicos de una persona (nombre, direccin, etc.). Tal formulario no es un dilogo por s mismo, pero se podra integrar en dilogos de clientes, proveedores, empleados, etc. El hecho de que se integre esttica (embebido en un ejecutable) o dinmicamente (intrpretes, DLLs, etc.) no afecta en absoluto a la esencia de su comportamiento como componente con que construir dilogos con sentido autnomo. Pues bien, as es un applet. Lo que ocurre es que, dado que no existe una base adecuada para soportar aplicaciones industriales Java en las que insertar nuestras miniaplicaciones (aunque todo se andar), los applets se han construido mayoritariamente, y con gran acierto comercial (parece), como pequeas aplicaciones interactivas, con movimiento, luces y sonido... en Internet. Llamadas a Applets con appletviewer Un applet es una mnima aplicacin Java diseada para ejecutarse en un navegador Web. Por tanto, no necesita preocuparse por un mtodo main() ni en dnde se realizan las llamadas. El applet asume que el cdigo se est ejecutando desde dentro de un navegador. El appletviewer se asemeja al mnimo navegador. Espera como argumento el nombre del fichero html que debe cargar, no se le puede pasar directamente un programa Java. Este fichero html debe contener una marca que especifica el cdigo que cargar el appletviewer: <HTML> <APPLET CODE=HolaMundo.class WIDTH=300 HEIGHT=100> </APPLET> </HTML> El appletviewer crear un espacio de navegacin, incluyendo un rea grfica, donde se ejecutar el applet, entonces llamar a la clase applet apropiada. En el ejemplo anterior, el appletviewer cargar una clase de nombre HolaMundo y le permitir trabajar en su espacio grfico. Arquitectura de appletviewer El appletviewer representa el mnimo interfaz de navegacin. En la figura se muestran los pasos que seguira appletviewer para presentarnos el resultado de la ejecucin del cdigo de nuestra clase.

139

Esta es una visin simplificada del appletviewer. La funcin principal de esta aplicacin es proporcionar al usuario un objeto de tipo Graphics sobre el que dibujar, y varias funciones para facilitar el uso del objeto Graphics. Ciclo de vida de un Applet Cuando un applet se carga en el appletviewer, comienza su ciclo de vida, que pasara por las siguientes fases:

Se crea una instancia de la clase que controla el applet. En el ejemplo de la figura anterior, sera la clase HolaMundo. El applet se incializa. El applet comienza a ejecutarse. El applet empieza a recibir llamadas. Primero recibe una llamada init (inicializar), seguida de un mensaje start (empezar) y paint (pintar). Estas llamadas pueden ser recibidas asncronamente. 4.3 Escribir Applets Java Para escribir applets Java, hay que utilizar una serie de mtodos, algunos de los cuales ya se hay sumariado al hablar de los mtodos del appletviewer, que es el visualizador de applets de Sun. Incluso para el applet ms sencillo necesitaremos varios mtodos. Son los que se usan para arrancar (start) y detener (stop) la ejecucin del applet, para pintar (paint) y actualizar (update) la pantalla y para capturar la informacin que se pasa al applet desde el fichero HTML a travs de la marca APPLET. init ( ) Esta funcin miembro es llamada al crearse el applet. Es llamada slo una vez. La clase Applet no hace nada en init(). Las clases derivadas deben sobrecargar este mtodo para cambiar el tamao

140

durante su inicializacin, y cualquier otra inicializacin de los datos que solamente deba realizarse una vez. Deberan realizarse al menos las siguientes acciones: Carga de imgenes y sonido El resize del applet para que tenga su tamao correcto Asignacin de valores a las variables globales Por ejemplo: public void init() { if( width < 200 || height < 200 ) resize( 200,200 ); valor_global1 = 0; valor_global2 = 100; // cargaremos imgenes en memoria sin mostrarlas // cargaremos msica de fondo en memoria sin reproducirla } destroy ( ) Esta funcin miembro es llamada cuando el applet no se va a usar ms. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para hacer una limpieza final. Los applet multithread debern usar destroy() para "matar" cuanquier thread del applet que quedase activo. start ( ) Llamada para activar el applet. Esta funcin miembro es llamada cuando se visita el applet. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para comenzar una animacin, sonido, etc. public void start() { estaDetenido = false; // comenzar la reproduccin de la msica musicClip.play(); }

141

Tambin se puede utilizar start() para eliminar cualquier thread que se necesite. stop ( ) Llamada para detener el applet. Se llama cuando el applet desaparece de la pantalla. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para detener la animacin, el sonido, etc. public void stop() { estaDetenido = true; if( /* se est reproduciendo msica? */ ) musicClip.stop(); } resize ( int width,int height ) El mtodo init() debera llamar a esta funcin miembro para establecer el tamao del applet. Puede utilizar las variables ancho y alto, pero no es necesario. Cambiar el tamao en otro sitio que no sea init() produce un reformateo de todo el documento y no se recomienda. En el navegador Netscape, el tamao del applet es el que se indica en la marca APPLET del HTML, no hace caso a lo que se indique desde el cdigo Java del applet. width Variable entera, su valor es el ancho definido en el parmetro WIDTH de la marca HTML del APPLET. Por defecto es el ancho del icono. height Variable entera, su valor es la altura definida en el parmetro HEIGHT de la marca HTML del APPLET. Por defecto es la altura del icono. Tanto width como height estn siempre disponibles para que se puede chequear el tamao del applet. Podemos retomar el ejemplo de init(): public void init() { if( width < 200 || height < 200 ) resize( 200,200 ); ... paint( Graphics g )

142

Se llama cada vez que se necesita refrescar el rea de dibujo del applet. La clase Applet simplemente dibuja una caja con sombreado de tres dimensiones en el rea. Obviamente, la clase derivada debera sobrecargar este mtodo para representar algo inteligente en la pantalla. Para repintar toda la pantalla cuando llega un evento Paint, se pide el rectngulo sobre el que se va a aplicar paint() y si es ms pequeo que el tamao real del applet se invoca a repaint(), que como va a hacer un update(), se actualizar toda la pantalla. Podemos utilizar paint() para imprimir nuestro mensaje de bienvenida: void public paint( Graphics g ) { g.drawString( "Hola Java!",25,25 ); // Dibujaremos la imgenes que necesitemos } update( Graphics g ) Esta es la funcin que se llama realmente cuando se necesita actualizar la pantalla. La clase Applet simplemente limpia el rea y llama al mtodo paint(). Esta funcionalidad es suficiente en la mayora de los casos. De cualquier forma, las clases derivadas pueden sustituir esta funcionalidad para sus propsitos. Podemos, por ejemplo, utilizar update() para modificar selectivamente partes del rea grfica sin tener que pintar el rea completa: public void update( Graphics g ) { if( estaActualizado ) { g.clear(); // garantiza la pantalla limpia repaint(); // podemos usar el mtodo padre: super.update() } else // Informacin adicional g.drawString( "Otra informacin",25,50 ); } repaint() A esta funcin se la debera llamar cuando el applet necesite ser repintado. No debera sobrecargarse, sino dejar que Java repinte completamente el contenido del applet.

143

Al llamar a repaint(), sin parmetros, internamente se llama a update() que borrar el rectngulo sobre el que se redibujar y luego se llama a paint(). Como a repaint() se le pueden pasar parmetros, se puede modificar el rectngulo a repintar. getParameter ( String attr ) Este mtodo carga los valores parados al applet va la marca APPLET de HTML. El argumento String es el nombre del parmetro que se quiere obtener. Devuelve el valor que se le haya asignado al parmetro; en caso de que no se le haya asignado ninguno, devolver null. Para usar getParameter(), se define una cadena genrica. Una vez que se ha capturado el parmetro, se utilizan mtodos de cadena o de nmeros para convertir el valor obtenido al tipo adecuado. public void init() { String pv; pv = getParameter( "velocidad" ); if( pv == null ) velocidad = 10; else velocidad = Integer.parseInt( pv ); } getDocumentBase ( ) Indica la ruta http, o el directorio del disco, de donde se ha recogido la pgina HTML que contiene el applet, es decir, el lugar donde est la hoja en todo Internet o en el disco. getCodeBase ( ) Indica la ruta http, o el directorio del disco, de donde se ha cargado el cdigo bytecode que forma el applet, es decir, el lugar donde est el fichero .class en todo Internet o en el disco. print ( Graphics g ) Para imprimir en impresora, al igual que paint() se puede utilizar print(), que pintar en la impresora el mapa de bits del dibujo.

5. EL DEPURADOR DE JAVA - jdb


El depurador de Java, jdb es un depurador de lnea de comandos, similar al que Sun proporciona en sus Sistemas, dbx. Es complicado de utilizar y un tanto crptico, por lo que, en principio, tiene escasa practicidad y es necesaria una verdadera emergencia para tener que recurrir a l.

144

Trataremos por encima los comandos que proporciona el jdb, pero sin entrar en detalles de su funcionamiento, porque no merece la pena. Casi es mejor esperar a disponer de herramientas visuales para poder depurar con cierta comodidad nuestro cdigo Java. Para poder utilizar el depurador, las aplicaciones Java deben estar compiladas con la opcin de depuracin activada, -g. Posteriormente se puede lanzar appletviewer con la opcin de depuracin, debug, y habremos puesto en marcha jdb. 5.1 Depurar HolaMundo Hemos modificado nuestro applet de ejemplo para utilizarlo en nuestra sesin de ejemplo con el depurador. Se compilara con el comando: %javac -g hm.java y el contenido de nuestro applet HolaMundo modificado y guardado en el fichero hm.java sera el siguiente: // // Applet HolaMundo de ejemplo, para depurar // import java.awt.Graphics; import java.applet.Applet; public class hm extends Applet { int i; public void paint( Graphics g ) { i = 10; g.drawString( "Hola Mundo!",25,25 ); } } Una vez compilado, iniciamos la sesin lanzando el visor de applets de Sun con la opcin de depuracin, utilizando el comando: %appletviewer -debug hm.html El fichero hm.html contiene las lneas mnimas para poder activar el applet, estas lneas son las que reproducimos: <html>

145

<applet code=hm.class width=100 height=100> </applet> </html> Se inicia pues la sesin con el depurador y vamos a ir reproduciendo lo que aparece en la pantalla a medida que vamos introduciendo comandos: %appletviewer -debug hm.html Loading jdb... 0xee301bf0:class(sun.applet.AppletViewer) > 5.2 Comando help El comando help proporciona una lista de los comandos que estn disponibles en la sesin de jdb. Esta lista es la que sigue, en donde hemos aprovechado la presencia de todos los comandos para comentar la accin que cada uno de ellos lleva a cabo. >help ** command list ** threads [threadgroup] -- lista threads thread <thread id> -- establece el thread por defecto suspend [thread id(s)] -- suspende threads (por defecto, todos) resume [thread id(s)] -- contina threads (por defecto, todos) where [thread id]|all -- muestra la pila de un thread threadgroups -- lista los grupos de threads threadgroup <name> -- establece el grupo de thread actual print <id> [id(s)] -- imprime un objeto o campo dump <id> [id(s)] -- imprime toda la informacin del objeto locals -- imprime las variables locales de la pila actual classes -- lista las clases conocidas methods <class id> -- lista los mtodos de una clase

146

stop in <class id>.<method> -- fija un punto de ruptura en un mtodo stop at <class id>:<line> -- establece un punto de ruptura en una lnea up [n frames] -- ascender en la pila de threads down [n frames] -- descender en la pila de threads clear <class id>:<line> -- eliminar un punto de ruptura step -- ejecutar la lnea actual cont -- continuar la ejecucin desde el punto de ruptura catch <class id> -- parar por la excepcin especificada ignore <class id> -- ignorar la excepcin especificada list [line number] -- imprimir cdigo fuente use [source file path] -- ver o cambiar la ruta del fichero fuente memory -- informe del uso de la memoria load <classname> - carga la clase Java a ser depurada run <args> - comienza la ejecucin de la clase cargada !! - repite el ltimo comando help (or ?) - lista los comandos exit (or quit) - salir del depurador > 5.3 Comando threadgroups El comando threadgroups permite ver la lista de threads que se estn ejecutando. Los grupos system y main deberan estar siempre corriendo. >threadgroups 1.(java.lang.ThreadGroup)0xee300068 system 2.(java.lang.ThreadGroup)0xee300a98 main > 5.4 Comando threads

147

El comando threads se utiliza para ver la lista completa de los threads que se estn ejecutando actualmente. >threads Group system: 1.(java.lang.Thread)0xee300098 clock handler cond 2.(java.lang.Thread)0xee300558 Idle thread run 3.(java.lang.Thread)0xee3005d0 sync Garbage Collector cond 4.(java.lang.Thread)0xee300620 Finalizer thread cond 5.(java.lang.Thread)0xee300a20 Debugger agent run 6.(java.tools.debug.BreakpointHandler)0xee300b58) Breakpoint handler cond Group main: 7.(java.lang.Thread)0xee300048 main suspended > 5.5 Comando run El comando run es el que se utiliza para arrancar el appletviewer en la sesin de depuracin. Lo teclearemos y luego volveremos a listar los threads que hay en ejecucin. >run run sun.applet.AppletViewer hm.html running... main[1]threads threads Group sun.applet.AppletViewer.main: 1.(java.lang.Thread)0xee3000c0 AWT-Motif running 2.(sun.awt.ScreenUpdater)0xee302ed0 ScreenUpdater cond. Waiting Group applet-hm.class: 3.(java.lang.Thread)0xee302f38 Thread-6 cond. Waiting main[1]

148

El visor de applets de Sun aparecer en la pantalla y mostrar el conocido mensaje de saludo al Mundo. Ahora vamos a rearrancar el appletviewer con un punto de ruptura, para detener la ejecucin del applet, y podamos seguir mostrando los comandos disponibles en el jdb. main[1]exit %appletviewer -debug hm.html Loading jdb... 0xee3009c8:class(sun.applet.AppletViewer) >stop in hm.paint Breakpoint set in hm.paint >run run sun.applet.AppletViewer hm.html running... Breakpoint hit: hm.paint(hm.java:9) AWT-Motif[1] 5.6 Comando where El comando where mostrar la pila de ejecucin del applet. AWT-Motif[1]where [1]hm.paint(hm.java:9) [2]sun.awt.motif.MComponentPeer.paint(MComponenetPeer.java:109) [3]sun.awt.motif.MComponentPeer.handleExpose(MComponenetPeer.java:170) AWT-Motif[1] 5.7 Comando use El comando use nos informa del camino donde jdb va a buscar los ficheros fuentes que contienen el cdigo Java de las clases que se estn depurando. Por defecto, utilizar el camino que se especifique en la variable de entorno CLASSPATH. AWT-Motif[1]use /usr/local/java/classes: AWT-Motif[1]

149

5.8 Comando list El comando list mostrar el cdigo fuente actual al comienzo del punto de ruptura que hayamos fijado. AWT-Motif[1]list 9 public void paint( Graphics g ) { 10 => i = 10; 11 g.drawString( "Hola Mundo!",25,25 ) ; 12 } 13 } AWT-Motif[1] 5.9 Comando dump El comando dump nos permitir ahora ver el valor del objeto g pasado desde el appletviewer. AWT-Motif[1]dump g g = (sun.awt.motif.X11Graphics)0xee303df8 { int pData = 1342480 Color foreground = (java.awt.Color)0xee302378 Font font = (java.awt.Font)0xee302138 int originX = 0 int originY = 0 float scaleX = 1 float scaleY = 1 Image image = null } AWT-Motif[1] 5.10 Comando step El comando step nos porporciona el mtodo para ejecutar la lnea actual, que estar siendo apuntada por el indicador si hemos utilizado el comando list.

150

AWT-Motif[1]step Breakpoint hit: hm.paint(hm.java:11) AWT-Motif[1]list 9 public void paint( Graphics g ) { 10 i = 10; 11 => g.drawString( "Hola Mundo!",25,25 ); 12 } 13 } AWT-Motif[1]

6. AWT
6.1 Introduccin al AWT AWT es el acrnimo del X Window Toolkit para Java, donde X puede ser cualquier cosa: Abstract, Alternative, Awkward, Another o Asqueroso; aunque parece que Sun se decanta por Abstracto, seriedad por encima de todo. Se trata de una biblioteca de clases Java para el desarrollo de Interfaces de Usuario Grficas. La versin del AWT que Sun proporciona con el JDK se desarroll en slo dos meses y es la parte ms dbil de todo lo que representa Java como lenguaje. El entorno que ofrece es demasiado simple, no se han tenido en cuenta las ideas de entornos grficos novedosos, sino que se ha ahondado en estructuras orientadas a eventos, llenas de callbacks y sin soporte alguno del entorno para la construccin grfica; veremos que la simple accin de colocar un dibujo sobre un botn se vuelve una tarea harto complicada. Quiz la presin de tener que lanzar algo al mercado haya tenido mucho que ver en la pobreza de AWT. JavaSoft, asegura que esto slo era el principio y que AWT ser multi-idioma, tendr herramientas visuales, etc. En fin, al igual que dicen los astrlogos, el futuro nos deparar muchas sorpresas. La estructura bsica del AWT se basa en Componentes y Contenedores. Estos ltimos contienen Componentes posicionados a su respecto y son Componentes a su vez, de forma que los eventos pueden tratarse tanto en Contenedores como en Componentes, corriendo por cuenta del programador (todava no hay herramientas de composicin visual) el encaje de todas las piezas, as como la seguridad de tratamiento de los eventos adecuados. Nada trivial. No obstante y pese a ello, vamos a abordar en este momento la programacin con el AWT para tener la base suficiente y poder seguir profundizando en las dems caractersticas del lenguaje Java, porque como vamos a ir presentando ejemplos grficos es imprescindible el conocimiento del AWT. Mientras tanto, esperemos que JavaSoft sea fiel a sus predicciones y lo que ahora veamos nos sirva de base para migrar a un nuevo y maravilloso AWT. 6.2 Interface de Usuario La interface de usuario es la parte del programa que permite a ste interactuar con el usuario. Las interfaces de usuario pueden adoptar muchas formas, que van desde la simple lnea de comandos hasta las interfaces grficas que proporcionan las aplicaciones ms modernas.

151

La interface de usuario es el aspecto ms importante de cualquier aplicacin. Una aplicacin sin un interfaz fcil, impide que los usuarios saquen el mximo rendimiento del programa. Java proporciona los elementos bsicos para construir decentes interfaces de usuario a travs del AWT. Al nivel ms bajo, el sistema operativo transmite informacin desde el ratn y el teclado como dispositivos de entrada al programa. El AWT fue diseado pensando en que el programador no tuviese que preocuparse de detalles como controlar el movimiento del ratn o leer el teclado, ni tampoco atender a detalles como la escritura en pantalla. El AWT constituye una librera de clases orientada a objeto para cubrir estos recursos y servicios de bajo nivel. Debido a que el lenguaje de programacin Java es independiente de la plataforma en que se ejecuten sus aplicaciones, el AWT tambin es independiente de la plataforma en que se ejecute. El AWT proporciona un conjunto de herramientas para la construccin de interfaces grficas que tienen una apariencia y se comportan de forma semejante en todas las plataformas en que se ejecute. Los elementos de interface proporcionados por el AWT estn implementados utilizando toolkits nativos de las plataformas, preservando una apariencia semejante a todas las aplicaciones que se creen para esa plataforma. Este es un punto fuerte del AWT, pero tambin tiene la desventaja de que una interface grfica diseada para una plataforma, puede no visualizarse correctamente en otra diferente. 6.3 Estructura del AWT La estructura de la versin actual del AWT podemos resumirla en los puntos que exponemos a continuacin: Los Contenedores contienen Componentes, que son los controles bsicos No se usan posiciones fijas de los Componentes, sino que estn situados a travs de una disposicin controlada (layouts) El comn denominador de ms bajo nivel se acerca al teclado, ratn y manejo de eventos Alto nivel de abstraccin respecto al entorno de ventanas en que se ejecute la aplicacin (no hay reas cliente, ni llamadas a X, ni hWnds, etc.) La arquitectura de la aplicacin es dependiente del entorno de ventanas, en vez de tener un tamao fijo Es bastante dependiente de la mquina en que se ejecuta la aplicacin (no puede asumir que un dilogo tendr el mismo tamao en cada mquina) Carece de un formato de recursos. No se puede separar el cdigo de lo que es propiamente interface. No hay ningn diseador de interfaces (todava)

6.4 Componentes y Contenedores Una interface grfica est construida en base a elementos grficos bsicos, los Componentes. Tpicos ejemplos de estos Componentes son los botones, barras de desplazamiento, etiquetas, listas, cajas de seleccin o campos de texto. Los Componentes permiten al usuario interactuar con la aplicacin y proporcionar informacin desde el programa al usuario sobre el estado del programa. En el AWT, todos los Componentes de la interface de usuario son instancias de la clase Component o uno de sus subtipos. Los Componentes no se encuentran aislados, sino agrupados dentro de Contenedores. Los Contenedores contienen y organizan la situacin de los Componentes; adems, los Contenedores son en s mismos Componentes y como tales pueden ser situados dentro de otros Contenedores. Tambin contienen el cdigo necesario para el control de eventos, cambiar la forma del cursor o modificar el icono de la aplicacin. En el AWT, todos los Contenedores son instancias de la clase Container o uno de sus subtipos.

152

Los Componentes deben circunscribirse dentro del Contenedor que los contiene. Esto hace que el anidamiento de Componentes (incluyendo Contenedores) en Contenedores crean rboles de elementos, comenzando con un Contenedor en la raiz del rbol y expandindolo en sus ramas.

7. GRAFICOS
7.1 Objetos Grficos En pginas anteriores ya se ha mostrado cmo escribir applets, cmo lanzarlos y los fundamentos bsicos de la presentacin de informacin sobre ellos. Ahora, pues, querremos hacer cosas ms interesantes que mostrar texto; ya que cualquier pgina HTML puede mostrar texto. Para ello, Java proporciona la clase Graphics, que permite mostrar texto a travs del mtodo drawString(), pero tambin tiene muchos otros mtodos de dibujo. Para cualquier programador, es esencial el entendimiento de la clase Graphics, antes de adentrarse en el dibujo de cualquier cosa en Java. Esta clase proporciona el entorno de trabajo para cualquier operacin grfica que se realice dentro del AWT. Juega dos importantes papeles: por un lado, es el contexto grfico, es decir, contiene la informacin que va a afectar a todas las operaciones grficas, incluyendo los colores de fondo y texto, la fuente de caracteres, la localizacin y dimensiones del rectngulo en que se va a pintar, e incluso dispone de informacin sobre el eventual destino de las operaciones grficas (pantalla o imagen). Por otro lado, la clase Graphics proporciona mtodos que permiten el dibujo de primitivas, figuras y la manipulacin de fonts de caracteres y colores. Tambin hay clases para la manipulacin de imgenes, doble-buffering, etc. Para poder pintar, un programa necesita un contexto grfico vlido, representado por una instancia de la clase Graphics. Pero, como esta clase es abstracta, no se puede instanciar directamente; as que debemos crear un componente y pasarlo al programa como un argumento a los mtodos paint() o update(). Los dos mtodos anteriores, paint() y update(), junto con el mtodo repaint() son los que estn involucrados en la presentacin de grficos en pantalla. El AWT, para reducir el tiempo que necesitan estos mtodos para realizar el repintado en pantalla de grficos, tiene dos axiomas: Primero, el AWT repinta solamente aquellos Componentes que necesitan ser repintados, bien porque estuviesen cubiertos por otra ventana o porque se pida su repintado directamente Segundo, si un Componente estaba tapado y se destapa, el AWT repinta solamente la porcin del Componente que estaba oculta

En la ejecucin del applet que aparece a continuacin, EjemploGraf.java, podemos observar como se realiza este proceso. Ignorar la zona de texto de la parte superior del applet de momento, y centrar la mirada en la parte coloreada. Utilizando otra ventana, tapar y destapar parte de la zona que ocupa el applet. Se observar que solamente el trozo de applet que estaba cubierto es el que se repinta. Yendo un poco ms all, solamente aquellos componentes que estn ocultos y se vuelvan a ver sern los que se repinten, sin tener en cuenta su posicin dentro de la jerarqua de componentes. La pantalla en Java se incrementa de izquierda a derecha y de arriba hacia abajo, tal como muestra la figura:

153

Los pixels de la pantalla son pues: posicin 0 + ancho de la pantalla - 1. En los textos, el punto de insercin se encuentra en la lnea base de la primera letra. 7.2 Mtodos para Dibujos Vamos a presentar mtodos para dibujar varias figuras geomtricas. Como estos mtodos funcionan solamente cuando son invocados por una instancia vlida de la clase Graphics, su mbito de aplicacin se restringe a los componentes que se utilicen en los mtodos paint() y update(). Normalmente los mtodos de dibujo de primitivas grficas funcionan por pares: un mtodo pinta la figura normal y el otro pinta la figura rellena. drawLine( x1,y1,x2,y2 ) drawRect( x,y,ancho,alto ) fillRect( x,y,ancho,alto ) clearRect( x,y,ancho.alto ) drawRoundRect( x,y,ancho,alto,anchoArco,altoArco ) fillRoundRect( x,y,ancho,alto,anchoArco,altoArco ) draw3DRect( x,y,ancho,alto,boolean elevado ) fill3DRect( x,y,ancho,alto,boolean elevado ) drawOval( x,y,ancho,alto ) fillOval( x,y,ancho,alto ) drawArc( x,y,ancho,alto,anguloInicio,anguloArco ) fillArc( x,y,ancho,alto,anguloInicio,anguloArco ) drawPolygon( int[] puntosX,int[] puntosY[],numPuntos ) fillPolygon( int[] puntosX,int[] puntosY[],numPuntos )

154

drawString( string s,x,y ) drawChars( char data[],offset,longitud,x,y ) drawBytes( byte data[],offset,longitud,x,y ) copyArea( xSrc,ySrc,ancho,alto,xDest,yDest ) 7.3 Mtodos para Imagenes Los objetos Graphics pueden mostrar imgenes a travs del mtodo: drawImage( Image img,int x,int y,ImageObserver observador ); Hay que tener en cuenta que el mtodo drawImage() necesita un objeto Image y un objeto ImageObserver. Podemos cargar una imagen desde un fichero de dibujo (actualmente slo se soportan formatos GIF y JPEG) con el mtodo getImage(): Image img = getImage( getDocumentBase(),"fichero.gif" ); La forma de invocar al mtodo getImage() es indicando un URL donde se encuentre el fichero que contiene la imagen que queremos presentar y el nombre de ese fichero: getImage( URL directorioImagen,String ficheroImagen ); Un URL comn para el mtodo getImage() es el directorio donde est el fichero HTML. Se puede acceder a esa localizacin a travs del mtodo getDocumentBase() de la clase Applet, como ya se ha indicado. Normalmente, se realiza el getImage() en el mtodo init() del applet y se muestra la imagen cargada en el mtodo paint(), tal como se muestra en el ejemplo siguiente: public void init() { img = getImage( getDocumentBase(),"pepe.gif" ); } public void paint( Graphics g ) { g.drawImage( img,x,y,this ); } En el applet Imagen.java, podemos ver el ejemplo completo. Su ponemos en l la existencia del fichero "Imagenes/pepe.gif": import java.awt.*; import sun.awt.image.URLImageSource;

155

import java.applet.Applet; public class Imagen extends Applet { Imagen pepe; public void init() { pepe = getImage( getDocumentBase(),"Imagenes/pepe.gif" ); } public void paint( Graphics g ) { g.drawString( pepe,25,25,this ); } } 7.4 Sonido en Java Java tambin posee mtodos predefinidos para reproducir sonido. El ordenador remoto no necesita tener un reproductor de audio; Java realizar la reproduccin (evidentemente, el ordenador remoto, en donde se ejecuta el applet, necesitar disponer de hardware de sonido). Reproduccin de sonido La forma ms fcil de reproducir sonido es a travs del mtodo play(): play( URL directorioSonido,String ficheroSonido ); o, simplemente: play( URL unURLdeSonido ); Un URL comn para el mtodo play() es el directorio donde est el fichero HTML. Se puede acceder a esa localizacin a travs del mtodo getDocumentBase() de la clase Applet: play( getDocumentBase(),"sonido.au" ); para que esto funcione, el fichero de la clase y el fichero sonido.au deberan estar en el mismo directorio. Reproduccin Repetitiva Se puede manejar el sonido como si de imgenes se tratara. Se pueden cargar y reproducir ms tarde. Para cargar un clip de sonido, se utiliza el mtodo getAudioClip():

156

AudoClip sonido; sonido = getAudioClip( getDocumentBase(),"risas.au" ); Una vez que se carga el clip de sonido, se pueden utilizar tres mtodos: sonido.play(); para reproducir el clip de sonido. sonido.loop(); para iniciar la reproduccin del clip de sonido y que entre en un blucle de reproduccin, es decir, en una repeticin automtica del clip. sonido.stop(); para detener el clip de sonido que se encuentre en ese instante en reproduccin. 7.5 Entrada por Ratn Una de las caractersticas ms tiles que ofrece Java es el soporte directo de la interactividad. La aplicacin puede reaccionar a los cambios producidos en el ratn, por ejemplo, sin necesidad de escribir ninguna lnea de cdigo para su control, solamente indicando qu se quiere hacer cuando el ratn haga algo. El evento ms comn en el ratn es el click. Este evento es gobernado por dos mtodos: mouseDown() (botn pulsado) y mouseUp() (botn soltado). Ambos mtodos son parte de la clase Applet, pero se necesita definir sus acciones asociadas, de la misma forma que se realiza con init() o con paint().

8. EXCEPCIONES EN JAVA
8.1 Manejo de Excepciones Vamos a mostrar como se utilizan las excepciones, reconvirtiendo nuestro applet de saludo a partir de la versin iterativa de HolaIte.java: import java.awt.*; import java.applet.Applet; public class HolaIte extends Applet { private int i = 0; private String Saludos[] = { "Hola Mundo!", "HOLA Mundo!",

157

"HOLA MUNDO!!" }; public void paint( Graphics g ) { g.drawString( Saludos[i],25,25 ); i++; } } Normalmente, un programa termina con un mensaje de error cuando se lanza una excepcin. Sin embargo, Java tiene mecanismos para excepciones que permiten ver qu excepcin se ha producido e intentar recuperarse de ella. Vamos a reescribir el mtodo paint() de nuestra versin iterativa del saludo: public void paint( Graphics g ) { try { g.drawString( Saludos[i],25,25 ); } catch( ArrayIndexOutOfBoundsException e ) { g.drawString( "Saludos desbordado",25,25 ); } catch( Exception e ) { // Cualquier otra excepcin System.out.println( e.toString() ); } finally { System.out.println( "Esto se imprime siempre!" ); } i++; } La palabra clave finally define un bloque de cdigo que se quiere que sea ejecutado siempre, de acuerdo a si se captur la excepcin o no. En el ejemplo anterior, la salida en la consola, con i=4 sera: Saludos desbordado

158

Esto se imprime siempre! 8.2 Generar Excepciones en Java Cuando se produce un error se debera generar, o lanzar, una excepcin. Para que un mtodo en Java, pueda lanzar excepciones, hay que indicarlo expresamente. void MetodoAsesino() throws NullPointerException,CaidaException Se pueden definir excepciones propias, no hay por qu limitarse a las predefinidas; bastar con extender la clase Exception y proporcionar la funcionalidad extra que requiera el tratamiento de esa excepcin. Tambin pueden producirse excepciones no de forma explcita como en el caso anterior, sino de forma implcita cuando se realiza alguna accin ilegal o no vlida. Las excepciones, pues, pueden originarse de dos modos: el programa hace algo ilegal (caso normal), o el programa explcitamente genera una excepcin ejecutando la sentencia throw (caso menos normal). La sentencia throw tiene la siguiente forma: throw ObtejoExcepction; El objeto ObjetoException es un objeto de una clase que extiende la clase Exception. El siguiente cdigo de ejemplo origina una excepcin de divisin por cero: class melon { public static void main( String[] a ) { int i=0, j=0, k; k = i/j; // Origina un error de division-by-zero } } Si compilamos y ejecutamos esta aplicacin Java, obtendremos la siguiente salida por pantalla: > javac melon.java > java melon java.lang.ArithmeticException: / by zero at melon.main(melon.java:5) Las excepciones predefinidas, como ArithmeticException, se conocen como excepciones runtime. Actualmente, como todas las excepciones son eventos runtime, sera mejor llamarlas excepciones irrecuperables. Esto contrasta con las excepciones que generamos explcitamente, que suelen ser mucho menos severas y en la mayora de los casos podemos recuperarnos de ellas. Por ejemplo,

159

si un fichero no puede abrirse, preguntamos al usuario que nos indique otro fichero; o si una estructura de datos se encuentra completa, podremos sobreescribir algn elemento que ya no se necesite. 8.3 Excepciones Predefinidas Las excepciones predefinidas y su jerarqua de clases es la que se muestra en la figura:

160

Las siguientes son las excepciones predefinidas ms frecuentes que se pueden encontrar: ArithmeticException Las excepciones aritmticas son tpicamente el resultado de una divisin por 0: int i = 12 / 0; NullPointerException Se produce cuando se intenta acceder a una variable o mtodo antes de ser definido: class Hola extends Applet { Image img; paint( Graphics g ) { g.drawImage( img,25,25,this ); } } IncompatibleClassChangeException El intento de cambiar una clase afectada por referencias en otros objetos, especficamente cuando esos objetos todava no han sido recompilados. ClassCastException El intento de convertir un objeto a otra clase que no es vlida. y = (Prueba)x; // donde x no es de tipo Prueba NegativeArraySizeException Puede ocurrir si hay un error aritmtico al intentar cambiar el tamao de un array. OutOfMemoryException No debera producirse nunca! El intento de crear un objeto con el operador new ha fallado por falta de memoria. Y siempre tendra que haber memoria suficiente porque el garbage collector se encarga de proporcionarla al ir liberando objetos que no se usan y devolviendo memoria al sistema. NoClassDefFoundException Se referenci una clase que el sistema es incapaz de encontrar.

161

ArrayIndexOutOfBoundsException Es la excepcin que ms frecuentemente se produce. Se genera al intentar acceder a un elemento de un array ms all de los lmites definidos inicialmente para ese array. UnsatisfiedLinkException Se hizo el intento de acceder a un mtodo nativo que no existe. Aqu no existe un mtodo a.kk class A { native void kk(); } y se llama a a.kk(), cuando debera llamar a A.kk(). InternalException Este error se reserva para eventos que no deberan ocurrir. Por definicin, el usuario nunca debera ver este error y esta excepcin no debera lanzarse. 8.4 Crear Excepciones Propias Tambin podemos lanzar nuestras propias excepciones, extendiendo la clase System.exception. Por ejemplo, consideremos un programa cliente/servidor. El cdigo cliente se intenta conectar al servidor, y durante 5 segundos se espera a que conteste el servidor. Si el servidor no responde, el servidor lanzara la excepcin de time-out: Cualquier mtodo que lance una excepcin tambin debe capturarla, o declararla como parte de la interface del mtodo. Cabe preguntarse entonces, el porqu de lanzar una excepcin si hay que capturarla en el mismo mtodo. La respuesta es que las excepciones no simplifican el trabajo del control de errores. Tienen la ventaja de que se puede tener muy localizado el control de errores y no tenemos que controlar millones de valores de retorno, pero no van ms all. 8.5 Capturar Excepciones Las excepciones lanzadas por un mtodo que pueda hacerlo deben recoger en bloque try/catch o try/finally. try Es el bloque de cdigo donde se prev que se genere una excepcin. Es como si dijsemos "intenta estas sentencias y mira a ver si se produce una excepcin". El bloque try tiene que ir seguido, al menos, por una clusula catch o una clusula finally catch Es el cdigo que se ejecuta cuando se produce la excepcin. Es como si dijsemos "controlo cualquier excepcin que coincida con mi argumento". En este bloque tendremos que asegurarnos

162

de colocar cdigo que no genere excepciones. Se pueden colocar sentencias catch sucesivas, cada una controlando una excepcin diferente. No debera intentarse capturar todas las excepciones con una sola clusula. Esto representara un uso demasiado general, podran llegar muchas ms excepciones de las esperadas. En este caso es mejor dejar que la excepcin se propague hacia arriba y dar un mensaje de error al usuario. Se pueden controlar grupos de excepciones, es decir, que se pueden controlar, a travs del argumento, excepciones semejantes. La clusula catch comprueba los argumentos en el mismo orden en que aparezcan en el programa. Si hay alguno que coincida, se ejecuta el bloque. El operador instanceof se utiliza para identificar exactamente cual ha sido la identidad de la excepcin. finally Es el bloque de cdigo que se ejecuta siempre, haya o no excepcin. Hay una cierta controversia entre su utilidad, pero, por ejemplo, podra servir para hacer un log o un seguimiento de lo que est pasando, porque como se ejecuta siempre puede dejarnos grabado si se producen excepciones y nos hemos recuperado de ellas o no. Este bloque finally puede ser til cuando no hay ninguna excepcin. Es un trozo de cdigo que se ejecuta independientemente de lo que se haga en el bloque try. Cuando vamos a tratar una excepcin, se nos plantea el problema de qu acciones vamos a tomar. En la mayora de los casos, bastar con presentar una indicacin de error al usuario y un mensaje avisndolo de que se ha producido un error y que decida si quiere o no continuar con la ejecucin del programa. 8.6 Propagacion de Excepciones La clusula catch comprueba los argumentos en el mismo orden en que aparezcan en el programa. Si hay alguno que coincida, se ejecuta el bloque y sigue el flujo de control por el bloque finally (si lo hay) y concluye el control de la excepcin. Si ninguna de las clusulas catch coincide con la excepcin que se ha producido, entonces se ejecutar el cdigo de la clusula finally (en caso de que la haya). Lo que ocurre en este caso, es exactamente lo mismo que si la sentencia que lanza la excepcin no se encontrase encerrada en el bloque try. El flujo de control abandona este mtodo y retorna prematuramente al mtodo que lo llam. Si la llamada estaba dentro del mbito de una sentencia try, entonces se vuelve a intentar el control de la excepcin, y as continuamente. Veamos lo que sucede cuando una excepcin no es tratada en la rutina en donde se produce. El sistema Java busca un bloque try..catch ms all de la llamada, pero dentro del mtodo que lo trajo aqu. Si la excepcin se propaga de todas formas hasta lo alto de la pila de llamadas sin encontrar un controlador especfico para la excepcin, entonces la ejecucin se detendr dando un mensaje. Es decir, podemos suponer que Java nos est proporcionando un bloque catch por defecto, que imprime un mensaje de error y sale.

163

No hay ninguna sobrecarga en el sistema por incorporar sentencias try al cdigo. La sobrecarga se produce cuando se genera la excepcin. Hemos dicho ya que un mtodo debe capturar las excepciones que genera, o en todo caso, declararlas como parte de su llamada, indicando a todo el mundo que es capaz de generar excepciones. Esto debe ser as para que cualquiera que escriba una llamada a ese mtodo est avisado de que le puede llegar una excepcin, en lugar del valor de retorno normal. Esto permite al programador que llama a ese mtodo, elegir entre controlar la excepcin o propagarla hacia arriba en la pila de llamadas.

9. Threads y Multithreading
Considerando el entorno multithread, cada thread (hilo, flujo de control del programa) representa un proceso individual ejecutndose en un sistema. A veces se les llama procesos ligeros o contextos de ejecucin. Tpicamente, cada thread controla un nico aspecto dentro de un programa, como puede ser supervisar la entrada en un determinado perifrico o controlar toda la entrada/salida del disco. Todos los threads comparten los mismos recursos, al contrario que los procesos en donde cada uno tiene su propia copia de cdigo y datos (separados unos de otros). Grficamente, los threads se parecen en su funcionamiento a lo que muestra la figura siguiente:

9.1 Flujo en Programas Programas de flujo nico Un programa de flujo nico o mono-hilvanado (single-thread) utiliza un nico flujo de control (thread) para controlar su ejecucin. Muchos programas no necesitan la potencia o utilidad de mltiples flujos de control. Sin necesidad de especificar explcitamente que se quiere un nico flujo de control, muchos de los applets y aplicaciones son de flujo nico. Programas de flujo mltiple En nuestra aplicacin de saludo, no vemos el thread que ejecuta nuestro programa. Sin embargo, Java posibilita la creacin y control de threads explcitamente. La utilizacin de threads en Java, permite una enorme flexibilidad a los programadores a la hora de plantearse el desarrollo de aplicaciones. La simplicidad para crear, configurar y ejecutar threads, permite que se puedan implementar muy poderosas y portables aplicaciones/applets que no se puede con otros lenguajes de tercera generacin. En un lenguaje orientado a Internet como es Java, esta herramienta es vital. Si se ha utilizado un navegador con soporte Java, ya se habr visto el uso de mltiples threads en Java. Habr observado que dos applet se pueden ejecutar al mismo tiempo, o que puede desplazarla pgina del navegador mientras el applet contina ejecutndose. Esto no significa que el applet utilice mltiples threads, sino que el navegador es multithreaded.

164

Las aplicaciones (y applets) multithreaded utilizan muchos contextos de ejecucin para cumplir su trabajo. Hacen uso del hecho de que muchas tareas contienen subtareas distintas e independientes. Se puede utilizar un thread para cada subtarea. Mientras que los programas de flujo nico pueden realizar su tarea ejecutando las subtareas secuencialmente, un programa multithreaded permite que cada thread comience y termine tan pronto como sea posible. Este comportamiento presenta una mejor respuesta a la entrada en tiempo real. 9.2 Creacion y Control de threads Creacin de un Thread Hay dos modos de conseguir threads en Java. Una es implementando la interface Runnable, la otra es extender la clase Thread. La implementacin de la interface Runnable es la forma habitual de crear threads. Las interfaces proporcionan al programador una forma de agrupar el trabajo de infraestructura de una clase. Se utilizan para disear los requerimientos comunes al conjunto de clases a implementar. La interface define el trabajo y la clase, o clases, que implementan la interface realizan ese trabajo. Los diferentes grupos de clases que implementen la interface tendrn que seguir las mismas reglas de funcionamiento. Hay una cuantas diferencias entre interface y clase. Primero, una interface solamente puede contener mtodos abstractos y/o variables estticas y finales (constantes). Las clases, por otro lado, pueden implementar mtodos y contener variables que no sean constantes. Segundo, una interface no puede implementar cualquier mtodo. Una clase que implemente una interface debe implementar todos los mtodos definidos en esa interface. Una interface tiene la posibilidad de poder extenderse de otras interfaces y, al contrario que las clases, puede extenderse de mltiples interfaces. Adems, una interface no puede ser instanciada con el operador new; por ejemplo, la siguiente sentencia no est permitida: Runnable a = new Runnable(); // No se permite El primer mtodo de crear un thread es simplemente extender la clase Thread: class MiThread extends Thread { public void run() { . . . } El ejemplo anterior crea una nueva clase MiThread que extiende la clase Thread y sobrecarga el mtodo Thread.run() por su propia implementacin. El mtodo run() es donde se realizar todo el trabajo de la clase. Extendiendo la clase Thread, se pueden heredar los mtodos y variables de la clase padre. En este caso, solamente se puede extender o derivar una vez de la clase padre. Esta limitacin de Java puede ser superada a travs de la implementacin de Runnable: public class MiThread implements Runnable {

165

Thread t; public void run() { // Ejecucin del thread una vez creado } } En este caso necesitamos crear una instancia de Thread antes de que el sistema pueda ejecutar el proceso como un thread. Adems, el mtodo abstracto run() est definido en la interface Runnable tiene que ser implementado. La nica diferencia entre los dos mtodos es que este ltimo es mucho ms flexible. En el ejemplo anterior, todava tenemos oportunidad de extender la clase MiThread, si fuese necesario. La mayora de las clases creadas que necesiten ejecutarse como un thread , implementarn la interface Runnable, ya que probablemente extendern alguna de su funcionalidad a otras clases. No pensar que la interface Runnable est haciendo alguna cosa cuando la tarea se est ejecutando. Solamente contiene mtodos abstractos, con lo cual es una clase para dar idea sobre el diseo de la clase Thread. De hecho, si vemos los fuentes de Java, podremos comprobar que solamente contiene un mtodo abstracto: package java.lang; public interface Runnable { public abstract void run() ; } Y esto es todo lo que hay sobre la interface Runnable. Como se ve, una interface slo proporciona un diseo para las clases que vayan a ser implementadas. En el caso de Runnable, fuerza a la definicin del mtodo run(), por lo tanto, la mayor parte del trabajo se hace en la clase Thread. Un vistazo un poco ms profundo a la definicin de la clase Thread nos da idea de lo que realmente est pasando: public class Thread implements Runnable { ... public void run() { if( tarea != null ) tarea.run() ; } } ...

166

} De este trocito de cdigo se desprende que la clase Thread tambin implemente la interface Runnable. tarea.run() se asegura de que la clase con que trabaja (la clase que va a ejecutarse como un thread) no sea nula y ejecuta el mtodo run() de esa clase. Cuando esto suceda, el mtodo run() de la clase har que corra como un thread. Arranque de un Thread Las aplicaciones ejecutan main() tras arrancar. Esta es la razn de que main() sea el lugar natural para crear y arrancar otros threads. La lnea de cdigo: t1 = new TestTh( "Thread 1",(int)(Math.random()*2000) ); crea un nuevo thread. Los dos argumentos pasados representan el nombre del thread y el tiempo que queremos que espere antes de imprimir el mensaje. Al tener control directo sobre los threads, tenemos que arrancarlos explcitamente. En nuestro ejemplo con: t1.start(); start(), en realidad es un mtodo oculto en el thread que llama al mtodo run(). Manipulacin de un Thread Si todo fue bien en la creacin del thread, t1 debera contener un thread vlido, que controlaremos en el mtodo run(). Una vez dentro de run(), podemos comenzar las sentencias de ejecucin como en otros programas. run() sirve como rutina main() para los threads; cuando run() termina, tambin lo hace el thread. Todo lo que queramos que haga el thread ha de estar dentro de run(), por eso cuando decimos que un mtodo es Runnable, nos obliga a escribir un mtodo run(). En este ejemplo, intentamos inmediatamente esperar durante una cantidad de tiempo aleatoria (pasada a travs del constructor): sleep( retardo ); El mtodo sleep() simplemente le dice al thread que duerma durante los milisegundos especificados. Se debera utilizar sleep() cuando se pretenda retrasar la ejecucin del thread. sleep() no consume recursos del sistema mientras el thread duerme. De esta forma otros threads pueden seguir funcionando. Una vez hecho el retardo, se imprime el mensaje "Hola Mundo!" con el nombre del thread y el retardo. Suspensin de un Thread Puede resultar til suspender la ejecucin de un thread sin marcar un lmite de tiempo. Si, por ejemplo, est construyendo un applet con un thread de animacin, querr permitir al usuario la opcin de detener la animacin hasta que quiera continuar. No se trata de terminar la animacin, sino desactivarla. Para este tipo de control de thread se puede utilizar el mtodo suspend().

167

t1.suspend(); Este mtodo no detiene la ejecucin permanentemente. El thread es suspendido indefinidamente y para volver a activarlo de nuevo necesitamos realizar una invocacin al mtodo resume(): t1.resume(); Parada de un Thread El ltimo elemento de control que se necesita sobre threads es el mtodo stop(). Se utiliza para terminar la ejecucin de un thread: t1.stop(); Esta llamada no destruye el thread, sino que detiene su ejecucin. La ejecucin no se puede reanudar ya con t1.start(). Cuando se desasignen las variables que se usan en el thread, el objeto thread (creado con new) quedar marcado para eliminarlo y el garbage collector se encargar de liberar la memoria que utilizaba. En nuestro ejemplo, no necesitamos detener explcitamente el thread. Simplemente se le deja terminar. Los programas ms complejos necesitarn un control sobre cada uno de los threads que lancen, el mtodo stop() puede utilizarse en esas situaciones. Si se necesita, se puede comprobar si un thread est vivo o no; considerando vivo un thread que ha comenzado y no ha sido detenido. t1.isAlive(); Este mtodo devolver true en caso de que el thread t1 est vivo, es decir, ya se haya llamado a su mtodo run() y no haya sido parado con un stop() ni haya terminado el mtodo run() en su ejecucin. 9.3 Estados de un thread Durante el ciclo de vida de un thread, ste se puede encontrar en diferentes estados. La figura siguiente muestra estos estados y los mtodos que provocan el paso de un estado a otro. Este diagrama no es una mquina de estados finita, pero es lo que ms se aproxima al funcionamiento real de un thread .

168

Nuevo Thread Cuando un thread est en este estado, es simplemente un objeto Thread vaco. El sistema no ha destinado ningn recurso para l. Desde este estado solamente puede arrancarse llamando al mtodo start(), o detenerse definitivamente, llamando al mtodo stop(); la llamada a cualquier otro mtodo carece de sentido y lo nico que provocar ser la generacin de una excepcin de tipo IllegalThreadStateException. Ejecutable La llamada al mtodo start() crear los recursos del sistema necesarios para que el thread puede ejecutarse, lo incorpora a la lista de procesos disponibles para ejecucin del sistema y llama al mtodo run() del thread. En este momento nos encontramos en el estado "Ejecutable" del diagrama. Y este estado es Ejecutable y no En Ejecucin, porque cuando el thread est aqu no esta corriendo. Muchos ordenadores tienen solamente un procesador lo que hace imposible que todos los threads estn corriendo al mismo tiempo. Java implementa un tipo de scheduling o lista de procesos, que permite que el procesador sea compartido entre todos los procesos o threads que se encuentran en la lista. Sin embargo, para nuestros propsitos, y en la mayora de los casos, se puede considerar que este estado es realmente un estado "En Ejecucin", porque la impresin que produce ante nosotros es que todos los procesos se ejecutan al mismo tiempo. Cuando el thread se encuentra en este estado, todas las instrucciones de cdigo que se encuentren dentro del bloque declarado para el mtodo run(), se ejecutarn secuencialmente. Parado El thread entra en estado "Parado" cuando alguien llama al mtodo suspend(), cuando se llama al mtodo sleep(), cuando el thread est bloqueado en un proceso de entrada/salida o cuando el thread utiliza su mtodo wait() para esperar a que se cumpla una determinada condicin. Cuando ocurra cualquiera de las cuatro cosas anteriores, el thread estar Parado. Para cada una de los cuatro modos de entrada en estado Parado, hay una forma especfica de volver a estado Ejecutable. Cada forma de recuperar ese estado es exclusiva; por ejemplo, si el thread ha sido puesto a dormir, una vez transcurridos los milisegundos que se especifiquen, l solo se despierta y vuelve a estar en estado Ejecutable. Llamar al mtodo resume() mientras est el thread durmiendo no servira para nada.

169

Los mtodos de recuperacin del estado Ejecutable, en funcin de la forma de llegar al estado Parado del thread, son los siguientes: Si un thread est dormido, pasado el lapso de tiempo Si un thread est suspendido, luego de una llamada al mtodo resume() Si un thread est bloqueado en una entrada/salida, una vez que el comando E/S concluya su ejecucin Si un thread est esperando por una condicin, cada vez que la variable que controla esa condicin vare debe llamarse a notify() o notifyAll()

Muerto Un thread se puede morir de dos formas: por causas naturales o porque lo maten (con stop()). Un thread muere normalmente cuando concluye de forma habitual su mtodo run(). Por ejemplo, en el siguiente trozo de cdigo, el bucle while es un bucle finito -realiza la iteracin 20 veces y termina-: El mtodo isAlive() La interface de programacin de la clase Thread incluye el mtodo isAlive(), que devuelve true si el thread ha sido arrancado (con start()) y no ha sido detenido (con stop()). Por ello, si el mtodo isAlive() devuelve false, sabemos que estamos ante un "Nuevo Thread" o ante un thread "Muerto". Si nos devuelve true, sabemos que el thread se encuentra en estado "Ejecutable" o "Parado". No se puede diferenciar entre "Nuevo Thread" y "Muerto", ni entre un thread "Ejecutable" o "Parado". 9.4 Comunicacion entre threads Otra clave para el xito y la ventaja de la utilizacin de mltiples threads en una aplicacin, o aplicacin multithreaded, es que pueden comunicarse entre s. Se pueden disear threads para utilizar objetos comunes, que cada thread puede manipular independientemente de los otros threads. El ejemplo clsico de comunicacin de threads es un modelo productor/consumidor. Un thread produce una salida, que otro thread usa (consume), sea lo que sea esa salida. Vamos entonces a crear un productor, que ser un thread que ir sacando caracteres por su salida; crearemos tambin un consumidor que ira recogiendo los caracteres que vaya sacando el productor y un monitor que controlar el proceso de sincronizacin entre los threads. Funcionar como una tubera, insertando el productor caracteres en un extremos y leyndolos el consumidor en el otro, con el monitor siendo la propia tubera.

11. METODOS NATIVOS

170

Un mtodo nativo es un mtodo Java (una instancia de un objeto o una clase) cuya implementacin se ha realizado en otro lenguaje de programacin, por ejemplo, C. Vamos a ver cmo se integran mtodos nativos en clases Java. Actualmente, el lenguaje Java solamente proporciona mecanismos para integrar cdigo C en programas Java. Veamos pues los pasos necesarios para mezclar cdigo nativo C y programas Java. Recurriremos (Cmo no!) a nuestro saludo; en este caso, el programa HolaMundo tiene dos clases Java: la primera implementa el mtodo main() y la segunda, HolaMundo, tiene un mtodo nativo que presenta el mensaje de saludo. La implementacin de este segundo mtodo la realizaremos en C. 10.1 Escribir Cdigo Java En primer lugar, debemos crear una clase Java, HolaMundo, que declare un mtodo nativo. Tambin debemos crear el programa principal que cree el objeto HolaMundo y llame al mtodo nativo. Las siguientes lneas de cdigo definen la clase HolaMundo, que consta de un mtodo y un segmento esttico de cdigo: class HolaMundo { public native void presentaSaludo(); static { System.loadLibrary( "hola" ); } } Podemos decir que la implementacin del mtodo presentaSaludo() de la clase HolaMundo est escrito en otro lenguaje, porque la palabra reservada native aparece como parte de la definicin del mtodo. Esta definicin, proporciona solamente la definicin para presentaSaludo() y no porporciona ninguna implementacin para l. La implementacin la proporcionaremos desde un fichero fuente separado, escrito en lenguaje C. La definicin para presentaSaludo() tambin indica que el mtodo es un mtodo pblico, no acepta argumentos y no devuelve ningn valor. Al igual que cualquier otro mtodo, los mtodos nativos deben estar definidos dentro de una clase Java. El cdigo C que implementa el mtodo presentaSaludo() debe ser compilado en una librera dinmica y cargado en la clase Java que lo necesite. Esta carga, mapea la implementacin del mtodo nativo sobre su definicin. El siguiente bloque de cdigo carga la librera dinmica, en este caso hola. El sistema Java ejecutar un bloque de cdigo esttico de la clase cuando la cargue. Todo el cdigo anterior forma parte del fichero HolaMundo.java, que contiene la clase HolaMundo. En un fichero separado, Main.java, vamos a crear una aplicacin Java que instancie a la clase HolaMundo y llame al mtodo nativo presentaSaludo(). class Main {

171

public static void main( String args[] ) { new HolaMundo().presentaSaludo(); } } Como se puede observar, llamamos al mtodo nativo del mismo modo que a cualquier otro mtodo Java; aadimos el nombre del mtodo al final del nombre del objeto con un punto ("."). El conjunto de parntesis que sigue al nombre del mtodo encierra los argumentos que se le pasen. En este caso, el mtodo presentaSaludo() no recibe ningn tipo de argumento. 10.2 Compilar el Cdigo Java Utilizaremos ahora el compilador javac para compilar el cdigo Java que hemos desarrollado. Compilaremos los dos ficheros fuentes de cdigo Java que hemos creado, tecleando los siguientes comandos: > javac HolaMundo.java > javac Main.java 10.3 Crear el Fichero de Cabecera Ahora debemos utilizar la aplicacin javah para conseguir el fichero de cabecera .h. El fichero de cabecera define una estructura que representa la clase HolaMundo sobre cdigo C y proporciona la definicin de una funcin C para la implementacin del mtodo nativo presentaSaludo() definido en ese clase. Ejecutamos javah sobre la clase HolaMundo, con el siguiente comando: > javah HolaMundo Por defecto, javah crear el nuevo fichero .h en el mismo directorio en que se encuentra el fichero .class, obtenido al compilar con javac el cdigo fuente Java correspondiente a la clase. El fichero que crear, ser un fichero de cabecera del mismo nombre que la clase y con extensin .h. Por ejemplo, el comando anterior habr creado el fichero HolaMundo.h, cuyo contenido ser el siguiente: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <native.h> /* Header for class HolaMundo */ #ifndef _Included_HolaMundo #define _Included_HolaMundo typedef struct ClassHolaMundo {

172

char PAD; /* ANSI C requires structures to have a least one member */ } ClassHolaMundo; HandleTo(HolaMundo); #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) void HolaMundo_presentaSaludo(struct HHolaMundo *); #ifdef __cplusplus } #endif #endif Este fichero de cabecera contiene la definicin de una estructura llamada ClassHolaMundo. Los miembros de esta estructura son paralelos a los miembros de la clase Java correspondiente; es decir, los campos en la estructura corresponden a las variables de la clase. Pero como HolaMundo no tiene ninguna variable, la estructura se encuentra vaca. Se pueden utilizar los miembros de la estructura para referenciar a variables instanciadas de la clase desde las funciones C. Adems de la estructura C similar a la clase Java, vemos que la llamada de la funcin C est declarada como: extern void HolaMundo_presentaSaludo( struct HHolaMundo *); Esta es la definicin de la funcin C que deberemos escribir para implementar el mtodo nativo presentaSaludo() de la clase HolaMundo. Debemos utilizar esa definicin cuando lo implementemos. Si HolaMundo llamase a otros mtodos nativos, las definiciones de las funciones tambin apareceran aqu. El nombre de la funcin C que implementa el mtodo nativo est derivado del nombre del paquete, el nombre de la clase y el nombre del mtodo nativo. As, el mtodo nativo presentaSaludo() dentro de la clase HolaMundo es HolaMundo_presentaSaludo(). En este ejemplo, no hay nombre de paquete porque HolaMundo se considera englobado dentro del paquete por defecto. La funcin C acepta un parmetro, aunque el mtodo nativo definido en la clase Java no acepte ninguno. Se puede pensar en este parmetro como si fuese la variable this de C++. En nuestro caso, ignoramos el parmetro this. 10.4 Crear el Fichero de stubs Volvemos a utilizar la aplicacin javah para crear el fichero de stubs, que contiene todas las declaraciones de mtodos, con sus llamadas y argumentos, listos para que nosotros rellenemos el cuerpo de los mtodos con los algoritmos que necesitemos implementar. Proporciona la unin entre la clase Java y su estructura C paralela.

173

Para generar este fichero, debemos indicar el parmetro .stubs al ejecutar la aplicacin javah sobre la clase HolaMundo, de la siguiente forma: > javah -stubs HolaMundo Del mismo modo que se generaba el fichero .h; el nombre del fichero de stubs ser el nombre de la clase con la extensin .c. En nuestro ejemplo, ser HolaMundo.c, y su contenido ser el siguiente: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <StubPreamble.h> /* Stubs for class HolaMundo */ /* SYMBOL: "HolaMundo/presentaSaludo()V", Java_HolaMundo_presentaSaludo_stub */ __declspec(dllexport) stack_item *Java_HolaMundo_presentaSaludo_stub(stack_item *_P_,struct execenv *_EE_) { extern void HolaMundo_presentaSaludo(void *); (void) HolaMundo_presentaSaludo(_P_[0].p); return _P_; } 10.5 Escribir la funcion C Escribiremos la funcin C para el mtodo nativo en un fichero fuente de cdigo C. La implementacin ser una funcin habitual C, que luego integraremos con la clase Java. La definicin de la funcin C debe ser la misma que la que se ha generado con javah en el fichero HolaMundo.h. La implementacin que proponemos la guardaremos en el fichero HolaImp.c, y contendr las siguientes lnea de cdigo: #include <StubPreamble.h> #include "HolaMundo.h> #include <stdio.h> void HolaMundo_presentaSaludo( struct HHolaMundo *this ) { printf( "Hola Mundo, desde el Tutorial de Java\n" ); return; }

174

Como se puede ver, la implementacin no puede ser ms sencilla: hace una llamada a la funcin printf() para presentar el saludo y sale. En el cdigo se incluyen tres ficheros de cabecera: StubsPreamble.h Proporciona la informacin para que el cdigo C pueda interactuar con el sistema Java. Cuando se escriben mtodos nativos, siempre habr que incluir este fichero en el cdigo fuente C. HolaMundo.h Es el fichero de cabecera que hemos generado para nuestra clase. Contiene la estructura C que representa la clase Java para la que estamos escribiendo el mtodo nativo y la definicin de la funcin para ese mtodo nativo. stdio.h Es necesario incluirlo porque utilizamos la funcin printf() de la librera estndar de C, cuya declaracin se encuentra en este fichero de cabecera. 10.6 Crear la Libreria Dinmica Utilizaremos el compilador C para compilar el fichero .h, el fichero de stubs y el fichero fuente .c; para crear una librera dinmica. Para crearla, utilizaremos el compilador C de nuestro sistema, haciendo que los ficheros HolaMundo.c y HolaImp.c generen una librera dinmica de nombre hola, que ser la que el sistema Java cargue cuando ejecute la aplicacin que estamos construyendo. Vamos a ver cmo generamos esta librera en Unix y en Windows. Unix Teclearemos el siguiente comando: % cc -G HolaMundo.c HolaImp.c -o libhola.so En caso de que no encuentre el compilador los ficheros de cabecera, se puede utilizar el flag -I para indicarle el camino de bsqueda, por ejemplo: % cc -G -I$JAVA_HOME/include HolaMundo.c HolaImp.c -o libhola.so donde $JAVA_HOME es el directorio donde se ha instalado la versin actual del Java Development Kit. Windows El comando a utilizar en este caso es el siguiente: c:\>cl HolaMundo.c HolaImp.c -Fhola.dll -MD -LD javai.lib

175

Este comando funciona con Microsoft Visual C++ 2.x y posteriores. Si queremos indicar al compilador donde se encuentran los ficheros de cabecera y las libreras, tendremos que fijar dos variables de entorno: c:\>SET INCLUDE=%JAVAHOME%\include;%INCLUDE% c:\>SET LIB=%JAVAHOME%\lib;%LIB% donde %JAVAHOME% es el directorio donde se ha instalado la versin actual del Java Development Kit. 10.7 Ejecutar el Programa Y, por fin, utilizaremos el intrprete de Java, java, para ejecutar el programa que hemos construido siguiendo todos los pasos anteriormente descritos. Si tecleamos el comando: > java Main obtendremos el resultado siguiente: % Hola Mundo, desde el Tutorial de Java Si no aparece este mensaje de saludo y lo que aparece en pantalla son expresiones como UnsatisfiedLinkError, es porque no tenemos fijado correctamente el camino de la librera dinmica que hemos generado. Este camino es la lista de directorios que el sistema Java utilizar para buscar las libreras que debe cargar. Debemos asegurarnos de que el directorio donde se encuentra nuestra librera hola recin creada, figura entre ellos. Si fijamos el camino correcto y ejecutamos de nuevo el programa, veremos que ahora s obtenemos el mensaje de saludo que esperbamos. Con ello, hemos visto como integrar cdigo C en programas Java. Quedan muchas cuestiones por medio, como la equivalencia de tipos entre Java y C, el paso de parmetros, el manejo de cadenas, etc. Pero eso supondra entrar en mucha ms profundidad dentro de Java de la que aqu pretendemos (por ahora).

11. Entrada/Salida Estndar


Los usuarios de Unix, y aquellos familiarizados con las lneas de comandos de otros sistemas como DOS, han utilizado un tipo de entrada/salida conocida comnmente por entrada/salida estndar. El fichero de entrada estndar (stdin) es simplemente el teclado. El fichero de salida estndar (stdout) es tpicamente la pantalla (o la ventana del terminal). El fichero de salida de error estndar (stderr) tambin se dirige normalmente a la pantalla, pero se implementa como otro fichero de forma que se pueda distinguir entre la salida normal y (si es necesario) los mensajes de error. 11.1 La clase System Java tiene acceso a la entrada/salida estndar a travs de la clase System. En concreto, los tres ficheros que se implementan son: Stdin

176

System.in implementa stdin como una instancia de la clase InputStream. Con System.in, se accede a los mtodos read() y skip(). El mtodo read() permite leer un byte de la entrada. skip( long n ), salta n bytes de la entrada. Stdout System.out implementa stdout como una instancia de la clase PrintStream. Se pueden utilizar los mtodos print() y println() con cualquier tipo bsico Java como argumento. Stderr System.err implementa stderr de la misma forma que stdout. Como con System.out, se tiene acceso a los mtodos de PrintStream. Vamos a ver un pequeo ejemplo de entrada/salida en Java. El cdigo siguiente, miType.java, reproduce, o funciona como la utilidad cat de Unix o type de DOS: import java.io.*; class miType { public static void main( String args[] ) throws IOException { int c; int contador = 0; while( (c = System.in.read() ) != '\n' ) { contador++; System.out.print( (char)c ); } System.out.println(); // Lnea en blanco System.err.println( "Contados "+ contador +" bytes en total." ); } } 11.2 Clases comunes de Entrada/Salida Adems de la entrada por teclado y salida por pantalla, se necesita entrada/salida por fichero, como son: FileInputStream

177

DataInputStream FileOutputStream DataOutputStream Tambin existen otras clases para aplicaciones ms especficas, que no vamos a tratar, por ser de un uso muy concreto: PipedInputStream BufferedInputStream PushBackInputStream StreamTokenizer PipedOutputStream BufferedOutputStream

12. FICHEROS EN JAVA


Todos los lenguajes de programacin tienen alguna forma de interactuar con los sistemas de ficheros locales; Java no es una excepcin. Cuando se desarrollan applets para utilizar en red, hay que tener en cuenta que la entrada/salida directa a fichero es una violacin de seguridad de acceso. Muchos usuarios configurarn sus navegadores para permitir el acceso al sistema de ficheros, pero otros no. Por otro lado, si se est desarrollando una aplicacin Java para uso interno, probablemente ser necesario el acceso directo a ficheros. Antes de realizar acciones sobre un fichero, necesitamos un poco de informacin sobre ese fichero. La clase File proporciona muchas utilidades relacionadas con ficheros y con la obtencin de informacin bsica sobre esos ficheros. 12.1 Creacin de un objeto File Para crear un objeto File nuevo, se puede utilizar cualquiera de los tres constructores siguientes: File miFichero; miFichero = new File( "/etc/kk" ); o miFichero = new File( "/etc","kk" ); o

178

File miDirectorio = new File( "/etc" ); miFichero = new File( miDirectorio,"kk" ); El constructor utilizado depende a menudo de otros objetos File necesarios para el acceso. Por ejemplo, si slo se utiliza un fichero en la aplicacin, el primer constructor es el mejor. Si en cambio, se utilizan muchos ficheros desde un mismo directorio, el segundo o tercer constructor sern ms cmodos. Y si el directorio o el fichero es una variable, el segundo constructor ser el ms til. 12.2 Ficheros de Acceso Aleatorio A menudo, no se desea leer un fichero de principio a fin; sino acceder al fichero como una base de datos, donde se salta de un registro a otro; cada uno en diferentes partes del fichero. Java proporciona una clase RandomAccessFile para este tipo de entrada/salida. Creacin de un Fichero de Acceso Aleatorio Hay dos posibilidades para abrir un fichero de acceso aleatorio: Con el nombre del fichero: miRAFile = new RandomAccessFile( String nombre,String modo ); Con un objeto File: miRAFile = new RandomAccessFile( File fichero,String modo ); El argumento modo determina si se tiene acceso de slo lectura (r) o de lectura/escritura (r/w). Por ejemplo, se puede abrir un fichero de una base de datos para actualizacin: RandomAccessFile miRAFile; miRAFile = new RandomAccessFile( "/tmp/kk.dbf","rw" ); Acceso a la Informacin Los objetos RandomAccessFile esperan informacin de lectura/escritura de la misma manera que los objetos DataInput/DataOutput. Se tiene acceso a todas las operaciones read() y write() de las clases DataInputStream y DataOutputStream. Tambin se tienen muchos mtodos para moverse dentro de un fichero: long getFilePointer(); Devuelve la posicin actual del puntero del fichero void seek( long pos ); Coloca el puntero del fichero en una posicin determinada. La posicin se da como un desplazamiento en bytes desde el comienzo del fichero. La posicin 0 marca el comienzo de ese fichero.

179

long length(); Devuelve la longitud del fichero. La posicin length() marca el final de ese fichero. Actualizacin de Informacin Se pueden utilizar ficheros de acceso aleatorio para aadir informacin a ficheros existentes: miRAFile = new RandomAccessFile( "/tmp/kk.log","rw" ); miRAFile.seek( miRAFile.length() ); // Cualquier write() que hagamos a partir de este punto del cdigo // aadir informacin al fichero Vamos a ver un pequeo ejemplo, Log.java, que aade una cadena a un fichero existente: import java.io.*; // Cada vez que ejecutemos este programita, se incorporara una nueva // linea al fichero de log que se crea la primera vez que se ejecuta // // class Log { public static void main( String args[] ) throws IOException { RandomAccessFile miRAFile; String s = "Informacion a incorporar\nTutorial de Java\n"; // Abrimos el fichero de acceso aleatorio miRAFile = new RandomAccessFile( "/tmp/java.log","rw" ); // Nos vamos al final del fichero miRAFile.seek( miRAFile.length() ); // Incorporamos la cadena al fichero miRAFile.writeBytes( s ); // Cerramos el fichero miRAFile.close();

180

} }

13. COMUNICACIONES EN JAVA


13.1 Modelo de Comunicaciones con Java En Java, crear una conexin socket TCP/IP se realiza directamente con el paquete java.net. A continuacin mostramos un diagrama de lo que ocurre en el lado del cliente y del servidor:

El modelo de sockets ms simple es: El servidor establece un puerto y espera durante un cierto tiempo (timeout segundos), a que el cliente establezca la conexin. Cuando el cliente solicite una conexin, el servidor abrir la conexin socket con el mtodo accept(). El cliente establece una conexin con la mquina host a travs del puerto que se designe en puerto# . El cliente y el servidor se comunican con manejadores InputStream y OutputStream. Hay una cuestin al respecto de los sockets, que viene impuesta por la implementacin del sistema de seguridad de Java. Actualmente, los applets slo pueden establecer conexiones con el nodo desde el cual se transfiri su cdigo. Esto est implementado en el JDK y en el intrprete de Java de Netscape. Esto reduce en gran manera la flexibilidad de las fuentes de datos disponibles para los applets. El problema si se permite que un applet se conecte a cualquier mquina de la red, es que entonces se podran utilizar los applets para inundar la red desde un ordenador con un cliente Netscape del que no se sospecha y sin ninguna posibilidad de rastreo. 13.2 Clases Utiles en Comunicaciones Vamos a exponer otras clases que resultan tiles cuando estamos desarrollando programas de comunicaciones, aparte de las que ya se han visto. El problema es que la mayora de estas clases se prestan a discusin, porque se encuentran bajo el directorio sun. Esto quiere decir que son

181

implementaciones Solaris y, por tanto, especficas del Unix Solaris. Adems su API no est garantizada, pudiendo cambiar. Pero, a pesar de todo, resultan muy interesantes y vamos a comentar un grupo de ellas solamente que se encuentran en el paquete sun.net. Socket Es el objeto bsico en toda comunicacin a travs de Internet, bajo el protocolo TCP. Esta clase proporciona mtodos para la entrada/salida a travs de streams que hacen la lectura y escritura a travs de sockets muy sencilla. ServerSocket Es un objeto utilizado en las aplicaciones servidor para escuchar las peticiones que realicen los clientes conectados a ese servidor. Este objeto no realiza el servicio, sino que crea un objeto Socket en funcin del cliente para realizar toda la comunicacin a travs de l. DatagramSocket La clase de sockets datagrama puede ser utilizada para implementar datagramas o fiables (sockets UDP), no ordenados. Aunque la comunicacin por estos sockets es muy rpida porque no hay que perder tiempo estableciendo la conexin entre cliente y servidor. DatagramPacket Clase que representa un paquete datagrama conteniendo informacin de paquete, longitud de paquete, direcciones Internet y nmeros de puerto. MulticastSocket Clase utilizada para crear una versin multicast de las clase socket datagrama. Mltiples clientes/servidores pueden transmitir a un grupo multicast (un grupo de direcciones IP compartiendo el mismo nmero de puerto). NetworkServer Una clase creada para implementar mtodos y variables utilizadas en la creacin de un servidor TCP/IP. NetworkClient Una clase creada para implementar mtodos y variables utilizadas en la creacin de un cliente TCP/IP. SocketImpl Es un Interface que nos permite crearnos nuestro propio modelo de comunicacin. Tendremos que implementar sus mtodos cuando la usemos. Si vamos a desarrollar una aplicacin con requerimientos especiales de comunicaciones, como pueden se la implementacin de un cortafuegos (TCP es un protocolo no seguro), o acceder a equipos especiales (como un lector de cdigo de barras o un GPS diferencial), necesitaremos nuestra propia clase Socket.

182

14. ARQUITECTURA Modelo/Vista/Controlador


La arquitectura MVC (Model/View/Controller) fue introducida como parte de la versin Smalltalk-80 del lenguaje de programacin Smalltalk. Fue diseada para reducir el esfuerzo de programacin necesario en la implementacin de sistemas mltiples y sincronizados de los mismos datos. Sus caractersticas principales son que el Modelo, las Vistas y los Controladores se tratan como entidades separadas; esto hace que cualquier cambio producido en el Modelo se refleje automticamente en cada una de las Vistas. Adems del programa ejemplo que hemos presentado al principio y que posteriormente implementaremos, este modelo de arquitectura se puede emplear en sistemas de representacin grfica de datos, como se ha citado, o en sistemas CAD, en donde se presentan partes del diseo con diferente escala de aumento, en ventanas separadas. En la figura siguiente, vemos la arquitectura MVC en su forma ms general. Hay un Modelo, mltiples Controladores que manipulan ese Modelo, y hay varias Vistas de los datos del Modelo, que cambian cuando cambia el estado de ese Modelo.

Este modelo de arquitectura presenta varias ventajas: Hay una clara separacin entre los componentes de un programa; lo cual nos permite implementarlos por separado. Hay un API muy bien definido; cualquiera que use el API, podr reemplazar el Modelo, la Vista o el Controlador, sin aparente dificultad. La conexin entre el Modelo y sus Vistas es dinmica; se produce en tiempo de ejecucin, no en tiempo de compilacin. Al incorporar el modelo de arquitectura MVC a un diseo, las piezas de un programa se pueden construir por separado y luego unirlas en tiempo de ejecucin. Si uno de los Componentes, posteriormente, se observa que funciona mal, puede reemplazarse sin que las otras piezas se vean afectadas. Este escenario contrasta con la aproximacin monoltica tpica de muchos programas Java. Todos tienen un Frame que contiene todos los elementos, un controlador de eventos, un montn de clculos y la presentacin del resultado. Ante esta perspectiva, hacer un cambio aqu no es nada trivial. 14.1 Definicin de las partes El Modelo es el objeto que representa los datos del programa. Maneja los datos y controla todas sus transformaciones. El Modelo no tiene conocimiento especfico de los Controladores o de las Vistas, ni siquiera contiene referencias a ellos. Es el propio sistema el que tiene encomendada la responsabilidad de mantener enlaces entre el Modelo y sus Vistas, y notificar a las Vistas cuando cambia el Modelo.

183

La Vista es el objeto que maneja la presentacin visual de los datos representados por el Modelo. Genera una representacin visual del Modelo y muestra los datos al usuario. Interacta con el Modelo a travs de una referencia al propio Modelo. El Controlador es el objeto que proporciona significado a las ordenes del usuario, actuando sobre los datos representados por el Modelo. Cuando se realiza algn cambio, entra en accin, bien sea por cambios en la informacin del Modelo o por alteraciones de la Vista. Interacta con el Modelo a travs de una referencia al propio Modelo. Vamos a mostrar un ejemplo concreto. Consideremos como tal el sistema descrito en la introduccin a este captulo, una pieza geomtrica en tres dimensiones, que representamos en la figura siguiente:

En este caso, la pieza central de la escena en tres dimensiones es el Modelo. El Modelo es una descripcin matemtica de los vrtices y las caras que componen la escena. Los datos que describen cada vrtice o cara pueden modificarse (quizs como resultado de una accin del usuario, o una distorsin de la escena, o un algoritmo de sombreado). Sin embargo, no tiene nocin del punto de vista, mtodo de presentacin, perspectiva o fuente de luz. El Modelo es una representacin pura de los elementos que componen la escena. La porcin del programa que transforma los datos dentro del Modelo en una presentacin grfica es la Vista. La Vista incorpora la visin del Modelo a la escena; es la representacin grfica de la escena desde un punto de vista determinado, bajo condiciones de iluminacin determinadas. El Controlador sabe que puede hacer el Modelo e implementa el interface de usuario que permite iniciar la accin. En este ejemplo, un panel de datos de entrada es lo nico que se necesita, para permitir aadir, modificar o borrar vrtices o caras de la figura.

184

Curso de Visual Basic I


Qu es Visual Basic? La palabra "Visual" hace referencia al mtodo que se utiliza para crear la interfaz grfica de usuario (GUI). En lugar de escribir numerosas lneas de cdigo para describir la apariencia y la ubicacin de los elementos de la interfaz, simplemente puede arrastrar y colocar objetos prefabricados en su lugar dentro de la pantalla. Si ha utilizado alguna vez un programa de dibujo como Paint, ya tiene la mayor parte de las habilidades necesarias para crear una interfaz de usuario efectiva. La palabra "Basic" hace referencia al lenguaje BASIC (Beginners All-Purpose Symbolic Instruction Code), un lenguaje utilizado por ms programadores que ningn otro lenguaje en la historia de la informtica o computacin. Visual Basic ha evolucionado a partir del lenguaje BASIC original y ahora contiene centenares de instrucciones, funciones y palabras clave, muchas de las cuales estn directamente relacionadas con la interfaz grfica de Windows. Los principiantes pueden crear aplicaciones tiles con slo aprender unas pocas palabras clave, pero, al mismo tiempo, la eficacia del lenguaje permite a los profesionales acometer cualquier objetivo que pueda alcanzarse mediante cualquier otro lenguaje de programacin de Windows. El lenguaje de programacin Visual Basic no es exclusivo de Visual Basic. La Edicin para aplicaciones del sistema de programacin de Visual Basic, incluida en Microsoft Excel, Microsoft Access y muchas otras aplicaciones Windows, utilizan el mismo lenguaje. El sistema de programacin de Visual Basic, Scripting Edition (VBScript) para programar en Internet es un subconjunto del lenguaje Visual Basic. La inversin realizada en el aprendizaje de Visual Basic le ayudar a abarcar estas otras reas. Si su objetivo es crear un pequeo programa para su uso personal o para su grupo de trabajo, un sistema para una empresa o incluso aplicaciones distribuidas de alcance mundial a travs de Internet, Visual Basic dispone de las herramientas que necesita.

Las caractersticas de acceso a datos le permiten crear bases de datos y aplicaciones cliente para los formatos de las bases de datos ms conocidas, incluidos Microsoft SQL Server y otras bases de datos de mbito empresarial. Las tecnologas ActiveX le permiten utilizar la funcionalidad proporcionada por otras aplicaciones, como el procesador de textos Microsoft Word, la hoja de clculo Microsoft Excel y otras aplicaciones Windows. Puede incluso automatizar las aplicaciones y los objetos creados con la Edicin profesional o la Edicin empresarial de Visual Basic. Las capacidades de Internet facilitan el acceso a documentos y aplicaciones a travs de Internet desde su propia aplicacin. La aplicacin terminada es un autntico archivo .exe que utiliza una biblioteca de vnculos dinmicos (DLL) de tiempo de ejecucin que puede distribuir con toda libertad.

Ediciones de Visual Basic


Visual Basic se encuentra disponible en tres versiones, cada una de las cuales est orientada a unos requisitos de programacin especficos.

185

La Edicin de aprendizaje de Visual Basic permite a los programadores crear robustas aplicaciones para Microsoft Windows 95 y Windows NT. Incluye todos los controles intrnsecos, adems de los controles de cuadrcula, de fichas y los controles enlazados a datos. La documentacin que se proporciona con esta edicin incluye Learn VB Now (un CD-ROM multimedia), un Manual del programador impreso, la Ayuda en pantalla y los Libros en pantalla de Visual Basic. La Edicin profesional proporciona a los profesionales un completo conjunto de herramientas para desarrollar soluciones para terceros. Incluye todas las caractersticas de la Edicin de aprendizaje, as como controles ActiveX adicionales, incluidos controles para Internet y el Generador de informes de Crystal Reports. La documentacin que se proporciona con la Edicin profesional incluye el Manual del programador, la Ayuda en pantalla, la Gua de herramientas componentes y el Manual del usuario de Crystal Reports para Visual Basic. La Edicin empresarial permite a los profesionales crear slidas aplicaciones distribuidas en un entorno de equipo. Incluye todas las caractersticas de la Edicin profesional, as como el Administrador de automatizacin, la Galera de objetos, las herramientas de administracin de bases de datos, el sistema de control de versiones orientado a proyectos Microsoft Visual SourceSafe, etc. La documentacin impresa que se proporciona con la Edicin empresarial incluye toda la documentacin de la Edicin profesional, y la Gua para la creacin de aplicaciones cliente-servidor con Visual Basic y el Manual del usuario de SourceSafe.

1-

Introduccin:

1.1- Programacin orientada a objetos. 1.1.1- Objetos.


Un objeto es una entidad que tiene asociado un conjunto de mtodos, eventos y propiedades. Ejemplo: Una caja de texto (TextBox) en la cual podemos escribir cualquier lnea es un objeto.

1.1.2- Propiedades.
Son las caractersticas que posee un objeto o un formulario (ventana de Windows). Ejemplo: Color de fondo del formulario, Fuente de texto de un TextBox, .

1.1.3- Mtodos.
Los mtodos son funciones internas de un determinado objeto que permite realizar funciones sobre l o sobre otro objeto. Ejemplo: Deseamos poner en la ventana Windows de nuestra aplicacin "Hola mundo", por tanto pondremos el mtodo -> Ventana.Print "Hola mundo"

1.1.4- Eventos.

186

Los eventos son acciones que se pueden realizar en cualquier control: click, doble click, movimiento del ratn. A estos eventos se les puede asociar cdigo para que se ejecute al producir el evento. Un programa Visual Basic es un POE (Programa orientado a eventos). Todo lo que hacemos en un programa Visual Basic est generado por medio de eventos

1.1.5- Explicacin integrada y ejemplo de Objetos, Propiedades, Mtodos y Eventos.


Los formularios y controles de Visual Basic son objetos que exponen sus propios mtodos, propiedades y eventos. Las propiedades se pueden considerar como atributos de un objeto, los mtodos como sus acciones y los eventos como sus respuestas. Un objeto de uso diario como el globo de un nio tiene tambin propiedades, mtodos y eventos. Entre las propiedades de un globo se incluyen atributos visibles como el peso, el dimetro y el color. Otras propiedades describen su estado (inflado o desinflado) o atributos que no son visibles, como su edad. Por definicin, todos los globos tienen estas propiedades; lo que vara de un globo a otros son los valores de estas propiedades. Un globo tiene tambin mtodos o acciones inherentes que puede efectuar. Tiene un mtodo inflar (la accin de llenarlo de helio) o un mtodo desinflar (expeler su contenido) y un mtodo elevarse (si se deja escapar). De nuevo, todos los globos pueden efectuar estos mtodos. Los globos tienen adems respuestas predefinidas a ciertos eventos externos. Por ejemplo, un globo respondera al evento de pincharlo desinflndose o al evento de soltarlo elevndose en el aire. Los objetos tienen propiedades, responden a eventos y ejecutan mtodos: Si se pudiera programar un globo, el cdigo de Visual Basic podra ser como el siguiente. Para establecer las propiedades del globo: Globo.Color = Rojo Globo.Diametro = 10 Globo.Inflado = True Observe la sintaxis del cdigo: el objeto (Globo) seguido de la propiedad (Color) seguida de la asignacin del valor (Rojo). Podra modificar el color del globo desde el cdigo si repitiera esta instruccin y sustituyera el valor por otro diferente. Las propiedades tambin se pueden establecer en la ventana Propiedades mientras se est diseando la aplicacin. Los mtodos de un globo se invocan de esta forma: Globo.Inflar Globo.Desinflar Globo.Elevar 5

187

La sintaxis es similar a la sintaxis de las propiedades: el objeto (un nombre) seguido de un mtodo (un verbo). En el tercer ejemplo hay un elemento adicional, llamado argumento, que indica la distancia que se eleva. Algunos mtodos tendrn uno o ms argumentos para describir ms a fondo la accin que se va a ejecutar. El globo puede responder a un evento como se muestra a continuacin: Sub Globo_Pinchazo() Globo.Desinflar Globo.HacerRuido "Bang" Globo.Inflado = False Globo.Dimetro = 1 End Sub En este caso, el cdigo describe el comportamiento del globo cuando se produce un evento Pinchazo: invoca el mtodo Desinflar y luego invoca el mtodo HacerRuido con un argumento Bang (el tipo de ruido que se va a hacer). Como el globo ya no est inflado, la propiedad Inflado tiene el valor False y la propiedad Dimetro adopta un nuevo valor. Si bien no puede programar un globo, s puede programar un formulario o un control de Visual Basic. Como programador, tiene el control: decide qu propiedades se deben modificar, qu mtodos se deben invocar o a qu eventos hay que responder para conseguir la apariencia y el comportamiento deseados

1.1.6- Diferencias entre la programacin procedural y la programacin bajo Windows.


Un estudio profundo del funcionamiento interno de Windows necesitara un libro completo. No es necesario tener un profundo conocimiento de todos los detalles tcnicos. Una versin reducida del funcionamiento de Windows incluye tres conceptos clave: ventanas, eventos y mensajes. Una ventana es simplemente una regin rectangular con sus propios lmites. Probablemente ya sabe que hay varios tipos de ventanas: una ventana Explorador en Windows 95, una ventana de documento dentro de su programa de proceso de textos o un cuadro de dilogo que emerge para recordarle una cita. Aunque stos son los ejemplos ms comunes, realmente hay otros muchos tipos de ventanas. Un botn de comando es una ventana. Los iconos, cuadros de texto, botones de opcin y barras de mens son todos ventanas. El sistema operativo Microsoft Windows administra todas estas ventanas asignando a cada una un nico nmero identificador (controlador de ventana o hWnd). El sistema controla continuamente cada una de estas ventanas para ver si existen signos de actividad o eventos. Los eventos pueden producirse mediante acciones del usuario, como hacer clic con el mouse (ratn) o presionar una tecla, mediante programacin o incluso como resultado de acciones de otras ventanas. Cada vez que se produce un evento se enva un mensaje al sistema operativo. El sistema procesa el mensaje y lo transmite a las dems ventanas. Entonces, cada ventana puede realizar la accin apropiada, basndose en sus propias instrucciones para tratar ese mensaje en particular (por ejemplo, volverse a dibujar cuando otra ventana la ha dejado al descubierto).

188

Como puede imaginar, tratar todas las combinaciones posibles de ventanas, eventos y mensajes podra ser interminable. Afortunadamente, Visual Basic le evita tener que tratar con todos los controladores de mensajes de bajo nivel. Muchos de los mensajes los controla automticamente Visual Basic, mientras que otros se tratan como procedimientos de evento para su comodidad. Esto le permite crear rpidamente eficaces aplicaciones sin tener que tratar detalles innecesarios. En las aplicaciones tradicionales o "por procedimientos", la aplicacin es la que controla qu partes de cdigo y en qu secuencia se ejecutan. La ejecucin comienza con la primera lnea de cdigo y contina con una ruta predefinida a travs de la aplicacin, llamando a los procedimientos segn se necesiten. En una aplicacin controlada por eventos, el cdigo no sigue una ruta predeterminada; ejecuta distintas secciones de cdigo como respuesta a los eventos. Los eventos pueden desencadenarse por acciones del usuario, por mensajes del sistema o de otras aplicaciones, o incluso por la propia aplicacin. La secuencia de estos eventos determina la secuencia en la que se ejecuta el cdigo, por lo que la ruta a travs del cdigo de la aplicacin es diferente cada vez que se ejecuta el programa. Puesto que no puede predecir la secuencia de los eventos, el cdigo debe establecer ciertos supuestos acerca del "estado del mundo" cuando se ejecute. Cuando haga suposiciones (por ejemplo, que un campo de entrada debe contener un valor antes de ejecutar un procedimiento para procesar ese valor), debe estructurar la aplicacin de forma que asegure que esa suposicin siempre ser vlida (por ejemplo, deshabilitando el botn de comando que inicia el procedimiento hasta que el campo de entrada contenga un valor). El cdigo tambin puede desencadenar eventos durante la ejecucin. Por ejemplo, cambiar mediante programacin el texto de un cuadro de texto hace que se produzca el evento Change del cuadro de texto. Esto causara la ejecucin del cdigo (si lo hay) contenido en el evento Change. Si supone que este evento slo se desencadenar mediante la interaccin del usuario, podra ver resultados inesperados. Por esta razn es importante comprender el modelo controlado por eventos y tenerlo en cuenta cuando disee su aplicacin.

1.3- Proyecto. 1.3.1- Definicin de Proyecto en Visual Basic.


Para crear una aplicacin con Visual Basic se trabaja con proyectos. Un proyecto es una coleccin de archivos que se usan para generar una aplicacin. Este tema describe cmo generar y administrar proyectos. Al crear una aplicacin probablemente crear nuevos formularios; tambin puede volver a usar o modificar formularios creados en proyectos anteriores. Esto tambin se aplica a otros mdulos o archivos que pueda incluir en su proyecto. Los controles ActiveX y los objetos de otras aplicaciones tambin se pueden compartir entre proyectos. Despus de ensamblar todos los componentes de un proyecto y escribir el cdigo, puede compilar el proyecto para crear un archivo ejecutable.

1.3.2-Componentes de un Proyecto.
Cuando desarrolla un aplicacin, trabaja con un archivo de proyecto para administrar todos los diferentes archivos que crea. Un proyecto consta de lo siguiente:

189

Un archivo de proyecto que realiza el seguimiento de todos los componentes (.vbp) Un archivo para cada formulario (.frm). Un archivo de datos binario para cada formulario que contenga datos sobre propiedades de controles del formulario (.frx). Estos archivos no se pueden modificar y los genera automticamente cualquier archivo .frm que tenga propiedades en formato binario, como Picture o Icon. Opcionalmente, un archivo para cada mdulo de clase (.cls). Opcionalmente, un archivo para cada mdulo estndar (.bas). Opcionalmente, uno o ms archivos con controles ActiveX (.ocx). Opcionalmente, un nico archivo de recursos (.res).

El archivo de proyecto es simplemente una lista de todos los archivos y objetos asociados con el proyecto, as como informacin sobre las opciones de entorno establecidas. Esta informacin se actualiza cada vez que guarda el proyecto. Todos los archivos y objetos tambin se pueden compartir con otros proyectos. Cuando ha completado todos los archivos del proyecto puede convertir el proyecto en un archivo ejecutable (.exe): en el men Archivo, elija el comando Generar proyecto.exe.

1.3.2.1-Formularios.
Un formulario es una ventana. La ventana Windows de cualquier aplicacin. Podemos abrir tantas ventanas como queramos en nuestro proyecto, pero el nombre de las ventanas debe ser distinto. Por defecto como ya hemos visto, la ventana que se abre en Visual Basic tiene el nombre de Form1. Ya veremos como cambiar estas "Propiedades" ms adelante. Los mdulos de formularios (extensin de nombre de archivo .frm) pueden contener descripciones en forma de texto del formulario y sus controles, incluyendo los valores de sus propiedades. Tambin pueden contener declaraciones a nivel de formulario de constantes, variables y procedimientos externos, procedimientos de evento y procedimientos generales.

1.3.2.2-Mdulos de clase.
Los mdulos de clase (extensin de nombre de archivo .cls) son similares a los mdulos de formulario, excepto en que no tiene interfaz de usuario visible. Puede usar mdulos de clase para crear sus propios objetos, incluyendo cdigo para mtodos y propiedades.

1.3.2.3-Mdulos estndar.
Un mdulo es un archivo Visual Basic donde escribimos parte del cdigo de nuestro programa, y digo parte, porque puede haber cdigo en el formulario tambin. Las rutinas incluidas dentro de los mdulos pueden ser ejecutadas desde los formularios de la aplicacin. Los mdulos estndar (extensin de nombre de archivo .bas) pueden contener declaraciones pblicas o a nivel de mdulo de tipos, constantes, variables, procedimientos externos y procedimientos pblicos.

1.3.2.4-Archivos de Recursos.

190

Los archivos de recursos (extensin de nombre de archivo .res) contienen mapas de bits, cadenas de texto y otros datos que puede modificar sin volver a modificar el cdigo. Por ejemplo, si piensa traducir su aplicacin a un idioma extranjero, puede guardar todas las cadenas de texto de la interfaz de usuario y los mapas de bits en un archivo de recursos, y simplemente traducir el archivo de recursos en vez de la aplicacin completa. Un proyecto slo puede contener un archivo de recursos.

1.3.2.5-Controles Active X.
Los controles ActiveX (extensin de nombre de archivo .ocx) son controles opcionales que se pueden agregar al cuadro de herramientas y se pueden usar en formularios. Cuando instala Visual Basic, los archivos que contienen los controles incluidos en Visual Basic se copian a un directorio comn (el subdirectorio \Windows\System en Windows 95). Existen controles ActiveX adicionales disponibles en diversas fuentes. Tambin puede crear sus propios controles mediante las ediciones Profesional y Empresarial de Visual Basic.

1.3.2.6-Controles estndar.
Los controles estndar los proporciona Visual Basic. Los controles estndar, como CommandButton (botn de comando) o Frame (marco), siempre estn incluidos en el cuadro de herramientas, al contrario de lo que ocurre con los controles ActiveX y los objetos insertables, que se pueden agregar y quitar del cuadro de herramientas.

1.4-Entorno de Desarrollo. 1.4.1-Barra de men. y 1.4.2-Barra de Herramientas.


En la ventana del programa, podemos hacer todas las funciones normales que nos permite el compilador Visual Basic.

1.4.3-Ventana de Proyecto.

Pulse "Ctrl+R" (Ver -> Proyecto) y se abrir la ventana de proyectos (3). En esta ventana tenemos todos los ficheros del proyecto Visual Basic en el que vamos a trabajar.

1.4.4- Formulario.

191

Al principio y por defecto, el programa abre un formulario con el nombre Form1 que es la ventana Windows de nuestra aplicacin.

1.4.5- Ventana de Propiedades.

Pulsando la tecla "F4", aparecer la ventana de propiedades . Esta ventana es fundamental, ya que contiene todas las propiedades de cada objeto que insertaremos en nuestro formulario, as como las propiedades del formulario en s.

1.4.6- Caja de Herramientas.

192

La ventana caja de herramientas contiene todos los objetos que podemos incluir en nuestro formulario. Esta ventana se puede abrir en el men principal (Ver -> Caja de herramientas).

2-Programacin: 2.1- Fundamentos de la Programacin.


Cada mdulo de formulario contiene procedimientos de evento (secciones de cdigo donde se colocan las instrucciones que se ejecutarn como respuesta a eventos especficos). Los formularios pueden contener controles. Por cada control de un formulario, existe el correspondiente conjunto de procedimientos de evento en el mdulo de formulario. Adems de procedimientos de evento, los mdulos de formulario pueden contener procedimientos generales que se ejecutan como respuesta a una llamada desde cualquier procedimiento de evento. El cdigo que no est relacionado con un control o un formulario especfico se puede colocar en un tipo diferente de mdulo, un mdulo estndar (.bas). Se deben colocar en un mdulo estndar los procedimientos que se puedan utilizar como respuesta a eventos de diversos objetos, en lugar de duplicar el cdigo en los procedimientos de evento de cada objeto.

2.1.1- Como funciona una aplicacin controlada por eventos.


Un evento es una accin reconocida por un formulario o un control. Las aplicaciones controladas por eventos ejecutan cdigo Basic como respuesta a un evento. Cada formulario y control de Visual Basic tiene un conjunto de eventos predefinidos. Si se produce uno de dichos eventos y el procedimiento de evento asociado tiene cdigo, Visual Basic llama a ese cdigo. Aunque los objetos de Visual Basic reconocen automticamente un conjunto predefinido de eventos, usted decide cundo y cmo se responder a un evento determinado. A cada evento le corresponde una seccin de cdigo (un procedimiento de evento). Cuando desea que un control responda a un evento, escribe cdigo en el procedimiento de ese evento. Los tipos de eventos reconocidos por un objeto varan, pero muchos tipos son comunes a la mayora de los controles. Por ejemplo, la mayora de los objetos reconocen el evento Click: si un usuario hace clic en un formulario, se ejecuta el cdigo del procedimiento de evento Click del formulario; si un usuario hace clic en un botn de comando, se ejecuta el cdigo del procedimiento de evento Click del botn. El cdigo en cada caso ser diferente.

193

He aqu una secuencia tpica de eventos en una aplicacin controlada por eventos: 1. Se inicia la aplicacin y se carga y muestra un formulario. 2. El formulario (o un control del formulario) recibe un evento. El evento puede estar causado por el usuario (por ejemplo, por la pulsacin de una tecla), por el sistema (por ejemplo, un evento de cronmetro) o, de forma indirecta, por el cdigo (por ejemplo, un evento Load cuando el cdigo carga un formulario). 3. Si hay cdigo en el procedimiento de evento correspondiente, se ejecuta. 4. La aplicacin espera al evento siguiente.

2.2- Variables. 2.2.1- Alcance de las variables.


El alcance de una variable define qu partes del cdigo son conscientes de su existencia. Cuando declara una variable en un procedimiento, slo el cdigo de dicho procedimiento puede tener acceso o modificar el valor de la variable; tiene un alcance que es local al procedimiento. A veces, sin embargo, se necesita utilizar una variable con un alcance ms general, como aquella cuyo valor est disponible para todos los procedimientos del mismo mdulo o incluso para todos los procedimientos de toda la aplicacin. Visual Basic le permite especificar el alcance de una variable cuando la declara. Establecimiento del alcance de las variables Dependiendo de cmo se declara, una variable tiene como alcance un procedimiento (local) o un mdulo. Alcance Nivel de procedimiento Nivel de mdulo Privado Las variables son privadas del procedimiento en el que aparecen. Las variables son privadas del mdulo en el que aparecen. Pblico No es aplicable. No puede declarar variables pblicas dentro de un procedimiento. Las variables estn disponibles para todos los mdulos.

Variables utilizadas en un procedimiento Las variables a nivel de procedimiento slo se reconocen en el procedimiento en el que se han declarado. Se las conoce tambin como variables locales. Se declaran mediante las palabras clave Dim o Static. Por ejemplo: Dim intTemp As Integer o bien Static intPermanent As Integer Los valores de variables locales declaradas con Static existen mientras se ejecuta la aplicacin, mientras que las variables declaradas con Dim slo existen mientras se ejecuta el procedimiento.

194

Las variables locales resultan una eleccin apropiada para clculos temporales. Por ejemplo, puede crear una docena de procedimientos distintos que contengan una variable llamada intTemp. Como cada intTemp se ha declarado como una variable local, cada procedimiento slo reconoce su propia versin de intTemp. Cualquier procedimiento puede alterar el valor de su intTemp local sin que ello afecte a las variables intTemp de los dems procedimientos. Variables utilizadas en un mdulo De forma predeterminada, una variable a nivel de mdulo est disponible para todos los procedimientos del mdulo, pero no para el cdigo de otros mdulos. Cree variables a nivel de mdulo declarndolas con la palabra clave Private en la seccin Declaraciones al principio del mdulo. Por ejemplo: Private intTemp As Integer A nivel de mdulo, no hay diferencia entre Private y Dim, pero es preferible Private porque contrasta con Public y hace que el cdigo sea ms fcil de comprender. Variables utilizadas por todos los mdulos Para hacer que una variable a nivel de mdulo est disponible para otros mdulos, utilice la palabra clave Public para declarar la variable. Los valores de las variables pblicas estn disponibles para todos los procedimientos de la aplicacin. Al igual que todas las variables a nivel de mdulo, las variables pblicas se declaran en la seccin Declaraciones al principio del mdulo. Por ejemplo: Public intTemp As Integer Nota: No puede declarar variables pblicas en un procedimiento, slo en la seccin Declaraciones de un mdulo.

2.2.2- Declaracin
La forma de declarar las variables es la siguiente: Dim Public Static nombre_variable As tipo Dim: Al declarar una variable con esta palabra estamos diciendo que la variable sea local al mbito en que se declara. Puede ser dentro de un procedimiento o dentro de un formulario, de esta forma no sera accesible desde los dems procedimientos o formularios. Public: Las variables declaradas sern publicas y podrn estar accesibles desde todos los formularios de la aplicacin. Para conseguirlo tendremos que declararlas en un mdulo de cdigo, no en la seccin declarations de cualquier formulario de los que conste la aplicacin. Para crear un mdulo de cdigo en el men principal de Visual Basic marcamos en PROYECTO/INSETAR MDULO y aparecer junto a los dems formularios de la ventana de proyecto aunque con un icono distinto indicando que se trata de un mdulo de cdigo. Static: Con esta forma de declarar variables conseguiremos que las variables locales no se creen y se destruyan al entrar y salir de los procedimientos donde fueron declaradas sino que se mantenga su valor durante todo el periodo de ejecucin de la aplicacin. De esta forma a entrar en algn procedimiento las variables recuerdan el valor que tenan cuando se sali de l.

195

2.2.3- Tipos de variables


PRIVATE<TBODY>TIPO BOOLEAN BYTE INTEGER LONG SINGLE DOUBLE CURRENCY STRING DATE COMENTARIO Slo admite 2 valores TRUE o FALSE admite valores entre 0 y 255 admite valores entre -32768 y 32767 admite valores entre -2.147.483.648 y 2.147.483.647 admite valores decimales con precisin simple admite valores decimales de doble precisin vlido para valores de tipo moneda cadenas de caracteres fechas, permite operar con ellas</TBODY>

2.2.4- Matrices
Para declarar matrices debemos colocar entre parntesis el nmero de elementos de los que constar a continuacin del nombre de la variable: Dim medidas(9) as integer De esta forma tenemos una matriz de 10 elementos identificados del 0 al 9 Podemos obligar a que el primer elemento de una matriz tenga el ndice con valor 1. Esto lo haremos colocando la instruccin option base 1 en la seccin declarations de nuestro formulario. Tambin podemos indicar los lmites inferior y superior de la matriz: Dim medidas(5 to 14) as integer es una matriz de 10 elementos cuyos indices van del 5 al 14 Las matrices multidimensionales se declaran de la siguiente forma: Dim medidas(1 to 10, 1 to 10) as integer CONSIDERACIONES Al trabajar con Visual Basic es preferible que activemos la opcin que nos obligue a declarar todas las variables que utilicemos, de esta forma nos ahorraremos errores inesperados como el de trabajar con una variable ya utilizada anteriormente producindose un conflicto difcil de resolver. En cambio si intentamos declarar 2 variables con el mismo nombre, en el mismo formulario o procedimiento se produce un error en tiempo de edicin avisndonos de la situacin. Para activar esta opcin debemos ir a la opcin del men Herramientas y Opciones para que aparezca un cuadro de dialogo como este.

196

La opcin que nos interesa activar es Requerir declaracin de variables que en este caso ya lo est. De esta forma en cada seccin declarations de cada formulario aparecer la sentencia option explicit Otra opcin que es interesante activar, es la de Guardar los cambios en la ficha entorno, la cual te guarda una copia del cdigo antes de ejecutarlo por si acaso luego no podemos, se bloquea la aplicacin etc... no suele pasar pero nunca se sabe. De esta forma te aseguras que lo ltimo que hayas ejecutado lo tienes guardado en el disco. La opcin Comprobacin automtica de sintaxis normalmente viene activada por defecto, no conviene desactivarla puesto que te avisa de errores de sintaxis conforme vas escribiendo el cdigo: Si te falta el then despus del if, el do antes del while etc...

2.3- Procedimientos y funciones. 2.3.1-Introduccin a los Procedimientos.


Puede simplificar las tareas de programacin si divide los programas en componentes lgicos ms pequeos. Estos componentes, llamados procedimientos, pueden convertirse en bloques bsicos que le permiten mejorar y ampliar Visual Basic. Los procedimientos resultan muy tiles para condensar las tareas repetitivas o compartidas, como clculos utilizados frecuentemente, manipulacin de texto y controles, y operaciones con bases de datos. Hay dos ventajas principales cuando se programa con procedimientos: Los procedimientos le permiten dividir los programas en unidades lgicas discretas, cada una de las cuales se puede depurar ms fcilmente que un programa entero sin procedimientos.

197

Los procedimientos que se utilizan en un programa pueden actuar como bloques de construccin de otros programas, normalmente con pocas o ninguna modificacin.

En Visual Basic se utilizan varios tipos de procedimientos: Procedimientos Sub que no devuelven un valor. Procedimientos Function que devuelven un valor (normalmente conocidos como funciones).

2.3.2- Procedimientos.
Un procedimiento Sub es un bloque de cdigo que se ejecuta como respuesta a un evento. Al dividir el cdigo de un mdulo en procedimientos Sub, es ms sencillo encontrar o modificar el cdigo de la aplicacin. La sintaxis de un procedimiento Sub es la siguiente: [Private|Public][Static]Sub nombre_procedimiento (argumentos) instrucciones End Sub Cada vez que se llama al procedimiento se ejecutan las instrucciones que hay entre Sub y End Sub. Se pueden colocar los procedimientos Sub en mdulos estndar, mdulos de clase y mdulos de formulario. De forma predeterminada, los procedimientos Sub son Public en todos los mdulos, lo que significa que se les puede llamar desde cualquier parte de la aplicacin. Los argumentos de un procedimiento son como las declaraciones de variables; se declaran valores que se pasan desde el procedimiento que hace la llamada. Resulta muy til en Visual Basic distinguir entre dos tipos de procedimientos Sub, procedimientos generales y procedimientos de evento. Procedimientos generales Un procedimiento general indica a la aplicacin cmo realizar una tarea especfica. Una vez que se define un procedimiento general, se le debe llamar especficamente desde la aplicacin. Por el contrario, un procedimiento de evento permanece inactivo hasta que se le llama para responder a eventos provocados por el usuario o desencadenados por el sistema. Por qu crear procedimientos generales? Una razn es que muchos procedimientos de evento distintos pueden necesitar que se lleven a cabo las mismas acciones. Es una buena estrategia de programacin colocar las instrucciones comunes en un procedimiento distinto (un procedimiento general) y hacer que los procedimientos de evento lo llamen. Esto elimina la necesidad de duplicar cdigo y tambin hace que la aplicacin sea ms fcil de mantener. Procedimientos de evento Cuando un objeto en Visual Basic reconoce que se ha producido un evento, llama automticamente al procedimiento de evento utilizando el nombre correspondiente al evento. Como

198

el nombre establece una asociacin entre el objeto y el cdigo, se dice que los procedimientos de evento estn adjuntos a formularios y controles. Un procedimiento de evento de un control combina el nombre real del control (especificado en la propiedad Name), un carcter de subrayado (_) y el nombre del evento. Por ejemplo, si desea que un botn de comando llamado cmdPlay llame a un procedimiento de evento cuando se haga clic en l, utilice el procedimiento cmdPlay_Click. Un procedimiento de evento de un formulario combina la palabra "Form", un carcter de subrayado y el nombre del evento. Si desea que un formulario llame a un procedimiento de evento cuando se hace clic en l, utilice el procedimiento Form_Click. (Como los controles, los formularios tienen nombres nicos, pero no se utilizan en los nombres de los procedimientos de evento.)

Todos los procedimientos de evento utilizan la misma sintaxis general. Sintaxis de un evento de control Sintaxis de un evento de formulario

Private Sub nombrecontrol_nombreevento (argumentos ) instrucciones End Sub Private Sub Form_nombreevento (argumentos) instrucciones End Sub Aunque puede escribir procedimientos de evento nuevos, es ms sencillo utilizar los procedimientos de cdigo que facilita Visual Basic, que incluyen automticamente los nombres correctos de procedimiento. Puede seleccionar una plantilla en la ventana Editor de cdigo si selecciona un objeto en el cuadro Objeto y selecciona un procedimiento en el cuadro Procedimiento. Tambin es conveniente establecer la propiedad Name de los controles antes de empezar a escribir los procedimientos de evento para los mismos. Si cambia el nombre de un control tras vincularle un procedimiento, deber cambiar tambin el nombre del procedimiento para que coincida con el nuevo nombre del control. De lo contrario, Visual Basic no ser capaz de hacer coincidir el control con el procedimiento. Cuando el nombre de un procedimiento no coincide con el nombre de un control, se convierte en un procedimiento general.

2.3.3- Funciones.
La sintaxis de un procedimiento Function es la siguiente: [Private|Public][Static]Function nombre_procedimiento (argumentos) [As tipo] instrucciones End Function

199

Al igual que un procedimiento Sub, un procedimiento Function es un procedimiento diferente que puede tomar argumentos, realizar una serie de instrucciones y cambiar el valor de los argumentos. A diferencia de los procedimientos Sub, los procedimientos Function pueden devolver un valor al procedimiento que realiza la llamada. Hay tres diferencias entre los procedimientos Sub y Function: Generalmente, se llama a una funcin incluyendo el nombre y los argumentos del procedimiento en la parte derecha de una instruccin o expresin mayor (valor_retorno = funcin()). Los procedimientos Function tienen tipos de datos, al igual que las variables. Esto determina el tipo del valor de retorno. (En ausencia de la clusula As, el tipo es el tipo predeterminado Variant.) Se devuelve un valor asignndole al propio nombre_procedimiento. Cuando el procedimiento Function devuelve un valor, se puede convertir en parte de una expresin mayor.

Por ejemplo, podra escribir una funcin que calculara el tercer lado, o hipotenusa, de un tringulo rectngulo, dados los valores de los otros dos lados: Function Hipotenusa (A As Integer, B As Integer) As String Hipotenusa = Sqr(A ^ 2 + B ^ 2) End Function Se llama a un procedimiento Function de la misma forma que a las funciones incorporadas en Visual Basic: Label1.Caption = Hipotenusa(CInt(Text1.Text),CInt(Text2.Text)) strX = Hipotenusa(Width, Height)

2.3.4- Llamadas a procedimientos.


Un procedimiento Sub difiere de un procedimiento Function en que al procedimiento Sub no se le puede llamar mediante su nombre en una expresin. La llamada a un procedimiento Sub es una instruccin nica. Adems, un procedimiento Sub no devuelve un valor en su nombre como hace una funcin. Sin embargo, al igual que Function, un procedimiento Sub puede modificar los valores de las variables que se le pasan. Hay dos formas de llamar a un procedimiento Sub: Ambas instrucciones llaman a un Sub denominado MiProc. Call MiProc (PrimerArgumento, SegundoArgumento) MiProc PrimerArgumento, SegundoArgumento Observe que cuando utiliza la sintaxis Call, debe poner los argumentos entre parntesis. Si omite la palabra clave Call, deber tambin omitir los parntesis alrededor de los argumentos.

2.3.5- Llamadas a Funciones.

200

Normalmente se llama a un procedimiento de funcin que se ha escrito de la misma forma en que se llama a una funcin intrnseca de Visual Basic como Abs; es decir, utilizando su nombre en una expresin: Las instrucciones siguientes llamaran a una funcin ' llamada ToDec. Print 10 * Adec X = Adec If Adec = 10 Then Debug.Print "Fuera del intervalo" X = OtraFuncin(10 * Adec) Tambin es posible llamar a una funcin igual que se llama a un procedimiento Sub. Las instrucciones siguientes llaman a la misma funcin: Call Year(Now) Year Now Cuando llama a una funcin de esta manera, Visual Basic desecha el valor de retorno.

2.3.6- Pasaje de argumentos a los procedimientos y funciones.


Normalmente el cdigo de un procedimiento necesita cierta informacin sobre el estado del programa para realizar su trabajo. Esta informacin consiste en variables que se pasan al procedimiento cuando se le llama. Cuando se pasa una variable a un procedimiento, se llama argumento. Tipos de datos de los argumentos Los argumentos de los procedimientos que escriba tienen el tipo de dato Variant de forma predeterminada. Sin embargo, puede declarar otros tipos de datos para los argumentos. Por ejemplo, la funcin siguiente acepta una cadena y un entero: Function QuComer (DaSemana As String, Hora As Integer) As String ' Devuelve el men del almuerzo basndose en el da y la hora. If DaSemana = "Viernes" then QuComer = "Pescado" Else QuComer = "Pollo" End If

201

If Hora > 4 Then QuComer = "Demasiado tarde" End Function Paso de argumentos por valor Slo se pasa una copia de la variable cuando se pasa un argumento por valor. Si el procedimiento cambia el valor, el cambio afecta slo a la copia y no a la variable propiamente dicha. Utilice la palabra clave ByVal para indicar un argumento pasado por valor. Por ejemplo: Sub Cuentas (ByVal intNumCuenta as Integer) . . ' Ponga aqu sus instrucciones. . End Sub Paso de argumentos por referencia Pasar argumentos por referencia le da al procedimiento acceso al contenido real de la variable en su ubicacin de direccin de memoria. Como resultado, el procedimiento al que se ha pasado el valor de la variable se puede modificar de forma permanente. La forma predeterminada de pasar valores en Visual Basic es por referencia. Si especifica el tipo de dato de un argumento que se pasa por referencia, debe pasar un valor de ese tipo para el argumento. Puede eludirlo si pasa una expresin en vez de un tipo de dato como argumento. Visual Basic evala la expresin y la pasa como el tipo requerido si puede. La forma ms sencilla de convertir una variable en una expresin es ponerla entre parntesis. Por ejemplo, para pasar una variable declarada como entero a un procedimiento que espera una cadena como argumento, debera hacer lo siguiente: Sub ProcedimientoQueLlama () Dim intX As Integer intX = 12 * 3 Foo(intX) End Sub Sub Foo(Bar As String) MsgBox Bar End Sub 'El valor de Bar es la cadena "36".

202

2.4- Estructuras de Control (Repeticin y Decisin). 2.4.1-Do While Loop / Do Loop While.
Utilice el bucle Do para ejecutar un bloque de instrucciones un nmero indefinido de veces. Hay algunas variantes en la instruccin Do...Loop, pero cada una evala una condicin numrica para determinar si contina la ejecucin. Como ocurre con If...Then, la condicin debe ser un valor o una expresin que d como resultado False (cero) o True (distinto de cero). En el ejemplo de Do...Loop siguiente, las instrucciones se ejecutan siempre y cuando condicin sea True: Do While condicin instrucciones Loop Cuando Visual Basic ejecuta este bucle Do, primero evala condicin. Si condicin es False (cero), se salta todas las instrucciones. Si es True (distinto de cero), Visual Basic ejecuta las instrucciones, vuelve a la instruccin Do While y prueba la condicin de nuevo. Por tanto, el bucle se puede ejecutar cualquier nmero de veces, siempre y cuando condicin sea distinta de cero o True. Nunca se ejecutan las instrucciones si condicin es False inicialmente. Por ejemplo, este procedimiento cuenta las veces que se repite una cadena de destino dentro de otra cadena repitiendo el bucle tantas veces como se encuentre la cadena de destino: Function ContarCadenas (cadenalarga, destino) Dim posicin, contador posicin = 1 Do While InStr(posicin, cadenalarga, destino) posicin = InStr(posicin, cadenalarga, destino)

+1

contador = contador + 1 Loop ContarCadenas = contador End Function Si la cadena de destino no est en la otra cadena, InStr devuelve 0 y no se ejecuta el bucle. Otra variante de la instruccin Do...Loop ejecuta las instrucciones primero y prueba condicin despus de cada ejecucin. Esta variacin garantiza al menos una ejecucin de instrucciones:

203

Do instrucciones Loop While condicin Hace el bucle cero o ms veces Do Until condicin instrucciones Loop Hace el bucle al menos una vez Do instrucciones Loop Until condicin

2.4.2- For Next.


Los bucles Do funcionan bien cuando no se sabe cuntas veces se necesitar ejecutar las instrucciones del bucle. Sin embargo, cuando se sabe que se van a ejecutar las instrucciones un nmero determinado de veces, es mejor elegir el bucle ForNext. A diferencia del bucle Do, el bucle For utiliza una variable llamada contador que incrementa o reduce su valor en cada repeticin del bucle. La sintaxis es la siguiente: For contador = iniciar To finalizar [Step incremento] instrucciones Next [contador] Los argumentos contador, iniciar, finalizar e incremento son todos numricos. Nota: El argumento incremento puede ser positivo o negativo. Si incremento es positivo, iniciar debe ser menor o igual que finalizar o no se ejecutarn las instrucciones del bucle. Si incremento es negativo, iniciar debe ser mayor o igual que finalizar para que se ejecute el cuerpo del bucle. Si no se establece Step, el valor predeterminado de incremento es 1. Al ejecutar el bucle For, Visual Basic: 1. Establece contador al mismo valor que iniciar. 2. Comprueba si contador es mayor que finalizar. Si lo es, Visual Basic sale del bucle. (Si incremento es negativo, Visual Basic comprueba si contador es menor que finalizar.) 3. Ejecuta instrucciones. 4. Incrementa contador en 1 o en instrucciones, si se especific. 5. Repite los pasos 2 a 4. Este cdigo imprime los nombres de todas las fuentes de pantalla disponibles: Private Sub Form_Click () Dim I As Integer For i = 0 To Screen.FontCount

204

Print Screen.Fonts(i) Next End Sub En la aplicacin de ejemplo VCR, el procedimiento HighlightButton utiliza un bucle For...Next para pasar por la coleccin de controles del formulario VCR y mostrar el control Shape apropiado: Sub HighlightButton(MyControl As Variant) Dim i As Integer For i = 0 To frmVCR.Controls.Count - 1 If TypeOf frmVCR.Controls(i) Is Shape Then If frmVCR.Controls(i).Name = MyControl

Then

= True Else

frmVCR.Controls(i).Visible

frmVCR.Controls(i).Visible = False End If End If Next End Sub

2.4.3- If Else End If.


Use la estructura If...Then para ejecutar una o ms instrucciones basadas en una condicin. Puede utilizar la sintaxis de una lnea o un bloque de varias lneas: If condicin Then instruccin If condicin Then instrucciones End If Condicin normalmente es una comparacin, pero puede ser cualquier expresin que d como resultado un valor numrico. Visual Basic interpreta este valor como True o False; un valor numrico cero es False y se considera True cualquier valor numrico distinto de cero. Si condicin

205

es True, Visual Basic ejecuta todas las instrucciones que siguen a la palabra clave Then. Puede utilizar la sintaxis de una lnea o de varias lneas para ejecutar una instruccin basada en una condicin (estos dos ejemplos son equivalentes): If cualquierFecha < Now Then cualquierFecha = Now If cualquierFecha < Now Then cualquierFecha = Now End If Observe que el formato de una nica lnea de If...Then no utiliza la instruccin End If. Si desea ejecutar ms de una lnea de cdigo cuando condicin sea True, debe utilizar la sintaxis de bloque de varias lneas If...Then...End If. If cualquierFecha < Now Then cualquierFecha = Now Timer1.Enabled = False End If If...Then...Else Utilice un bloque If...Then...Else para definir varios bloques de instrucciones, uno de los cuales se ejecutar: If condicin1 Then [bloque de instrucciones 1] [Else [bloque de instrucciones n]] End If Visual Basic evala primero condicin1. Si es False, Visual Basic ejecuta el bloque de instrucciones correspondientes a Else y despus ejecuta el cdigo que sigue a End If. Por ejemplo, la aplicacin podra realizar distintas acciones dependiendo del control en que se haya hecho clic de una matriz de controles de men: Private Sub mnuCut_Click (Index As Integer) If Index = 0 Then Comando Cortar. CopyActiveControl ClearActiveControl ' ' Desactiva el control Timer.

206

Else PasteActiveControl End If End Sub

' Comando Pegar.

2.4.4- Select - Case


Visual Basic proporciona la estructura Select Case como alternativa a If...Then...Else para ejecutar selectivamente un bloque de instrucciones entre varios bloques de instrucciones. La instruccin Select Case ofrece posibilidades similares a la instruccin If...Then...Else, pero hace que el cdigo sea ms legible cuando hay varias opciones. La estructura Select Case funciona con una nica expresin de prueba que se evala una vez solamente, al principio de la estructura. Visual Basic compara el resultado de esta expresin con los valores de cada Case de la estructura. Si hay una coincidencia, ejecuta el bloque de instrucciones asociado a ese Case: Select Case expresin_prueba [Case lista_expresiones1 [bloque de instrucciones 1]] [Case lista_expresiones2 [bloque de instrucciones 2]] . . .

[Case Else

[bloque de instrucciones n]] End Select Cada lista_expresiones es una lista de uno o ms valores. Si hay ms de un valor en una lista, se separan los valores con comas. Cada bloque de instrucciones contiene cero o ms instrucciones. Si ms de un Case coincide con la expresin de prueba, slo se ejecutar el bloque de instrucciones asociado con la primera coincidencia. Visual Basic ejecuta las instrucciones de la clusula (opcional) Case Else si ningn valor de la lista de expresiones coincide con la expresin de prueba. Por ejemplo, suponga que agrega otro comando al men Edicin en el ejemplo If...Then...Else. Podra agregar otra clusula ElseIf o podra escribir la funcin con Select Case: Private Sub mnuCut_Click (Index As Integer) Select Case Index

207

Case 0 Comando Cortar. CopyActiveControl generales. ClearActiveControl Case 1

'

' Llama a procedimientos

Comando Copiar.

'

CopyActiveControl Case 2 Comando Borrar. ClearActiveControl Case 3 ' '

Comando Pegar.

PasteActiveControl Case Else frmFind.Show ' Muestra el cuadro de

dilogo

Buscar. End Select

End Sub Observe que la estructura Select Case evala una expresin cada vez al principio de la estructura.

3-

Controles

3.1- Controles bsicos


Vamos a ver los siguientes puntos: Introduccin al uso de controles Control TextBox. Control Label Control CommandButton Control OptionButton Realizacon de una pequea aplicacin de ejemplo

Antes de empezar a conocer los controles bsicos veamos cuales son sus caractersticas generales:

208

Propiedades:Todos los controles disponen de una serie de propiedades las cuales podemos cambiar al incluirlos en nuestras aplicaciones. Ejemplos de propiedades son el color, el tipo de letra, el nombre, el texto, etc... Metodos: Son procedimientos asociados a los controles, es decir, rutinas ya establecidas que podemos invocar desde nuestras aplicaciones para que se realice alguna operacin sobre el control. Por ejemplo el control ListView ( la lista de archivos que aparece en el explorador de windows) dispone del mtodo order que te ordena los datos aparecidos en la lista. Eventos: Son acciones que pueden ser motivadas por el propio usuario o por mismo sistema operativo. Ejemplos pueden ser el movimiento del ratn o hacer click sobre su botn. En Visual Basic digamos que se utiliza la programacin orientada a eventos, lo cual es una de las diferencias ms importantes respecto a la programacin lineal de MS DOS. No necesitamos detectar cuando se ha producido un evento determinado, Windows lo detecta automticamente. Los eventos ya estan definidos, son bastantes y cada control cuenta con los suyos propios, aunque son muy parecidos. Lo nico que tendremos que hacer es asociar el cdigo necesario al evento que necesitemos tratar.

TextBox

Mediante este control podremos realizar tanto la entrada como la salida de datos en nuestras aplicaciones. No hace falta que indiquemos las coordenadas de la situacin del formulario en pantalla, simplemente tendremos que marcar sobre el control de la caja de herramientas y dibujarlo con el tamao que queramos en nuestro formulario. PROPIEDADES Las propiedades de las que dispone el control son las siguientes:(para obtener el cuadro de propiedades, seleccionar el control y pulsar F4 o pulsar con el boton derecho para obtener el men contextual y marcar Propierties) Text: Aqu indicamos el texto que aparecer en el control. Podemos asignarle cualquier texto en tiempo de diseo o ejecucin. Tambin podemos tomar el texto que haya introducido el usuario para tratarlo durante la ejecucin. Name: Esta propiedad la tienen todos los controles, el nombre que viene por defecto en este caso Text1 y es el nombre con el que se conocer el control cuando lo utilicemos en el cdigo. En un mismo formulario no puede haber 2 controles con el mismo nombre. Conviene poner un nombre que represente la funcin que tiene el control en la aplicacin para que el cdigo quede ms claro. Ejemplo, si en el textbox vamos a introducir la direccin de una persona podemos asignarle a esta propiedad el valor Direccin. MultiLine: Permite que introduzcamos varias lineas de texto en el control en lugar de slo una. Alignment: Alineacin que tendr el texto dentro del control: izquierda, centro o derecha. Para que funcione la propiedad MultiLine debe estar con el valor true. Locked: Si esta con valor true bloquea el control, es decir, el usuario no puede introducir ni modificar el texto que contenga. Nos puede servir para utilizar el control como salida de datos sin que el usuario pueda modificarlos por error.

209

Otras propiedades que son comunes a la mayoria de los controles: Backcolor: color de fondo. Forecolor: color de letra. Font: tipo y tamao de letra. METODOS Recordemos que por mtodos se entienten los procedimientos o funciones asociados a un control, los cuales nos permiten realizar ciertas operaciones tiles sobre dicho control: Ej. ordenar sus elementos, buscar un dato, etc.. Pues bien, los controles bsicos que vamos a ver en este captulo nicamente contienen mtodos avanzados que no vamos a analizar por ahora, ya que son mtodos que no se suelen utilizar. Ms adelante cuando veamos otros tipos de controles estudiaremos cuales son los mtodos que nos podrn servir. Si alguien est interesado en conocer todas las caractersticas de los controles puede hacerlo mirando en la ayuda que proporciona VB, haciendo click sobre cualquier control de la caja de herramientas y pulsando a continuacin F1 obtendr ayuda referente a ese control donde aparecern todas sus propiedades, metodos y eventos. EVENTOS Los eventos son acciones que se pueden realizar en cualquier control: click, doble click, movimiento del ratn. A estos eventos se les puede asociar cdigo para que se ejecute al producir el evento. MouseMove: al mover el raton por encima del control. Mousedown: al pulsar cualquier boton del raton Change: al cambiar el contenido del control Click: al hacer click con el botn izquierdo del ratn sobre el control Doubleclick: al hacer doble click con el con el botn izquierdo del ratn sobre el control Getfocus: este evento se activa cuando el control recibe el enfoque, es decir, cuando se activa el control en tiempo de ejecucin para introducir datos en l o realizar alguna operacin. Lostfocus: Es el contrario del anterior evento, se activa cuando el control pierde el enfoque, es decir, se pasa a otro control para seguir introduciendo datos. EJEMPLO Vamos a probar el uso del control TextBox mediante un pequeo ejemplo en el que teniendo un nico control de este tipo en un formulario, lo programaremos de forma que al pasar el ratn sobre el control (evento mousemove) aparecer en el formulario el texto que contenga.

210

Observamos que al situar el control en el formulario aparece por defecto el texto Text1. Para que no aparezca ese texto al ejecutar la aplicacin, debemos cambiar la propiedad Text pulsando F4 y colocar el texto que queramos o no colocar nada. Lo que queremos hacer es que cada vez que movamos el raton por el control aparezca su contenido en el formulario. Entonces lo que habr que hacer abrir la ventana de cdigo, seleccionando el control y pulsando F7, o con el botn derecho del ratn y la opcin View code del men contextual. Este proceso nos llevar al cuadro de la imagen siguiente.

Lo que tendremos que hacer es seleccionar el evento que necesitemos de la seccin Proc, en nuestro caso mousemove y a continuacin teclear el codigo correspondiente: La instruccin print visualiza un texto en el formulario y si le ponemos text1.text le decimos que nos muestre la propiedad Text del control Text1 que ese ser el nombre que tendr el control por defecto si no lo hemos cambiado en la propiedad name. Al ejecutar esta pequea aplicacin pulsando F5 observaremos como aparece en el formulario lo que hayamos tecleado en el control cada vez que movemos el raton sobre el Textbox. Podemos modificar el programa para que responda a cualquier otro evento sin ms que seleccionarlo en la seccin Proc e introduciendo el codigo que sea necesario. Label

Este control es tambin uno de los ms utilizados, aunque su utilidad queda restringida a la visualizacin de datos en el mismo, no permitiendo la introduccin de datos por parte del usuario. La forma de utilizarlo es similar a la del control anterior, dibujar el control en el formulario con el tamao que queramos y asignarle un texto en tiempo de diseo o de ejecucin esta vez sin utilizar la propiedad text puesto que no la incorpora, sino utilizando la propiedad caption.

211

Este control sirve para mostrar mensajes en nuestro formulario que orienten al usuario sobre la utilidad de los dems controles que tengamos en la aplicacin o para indicarnos acciones que podemos realizar. En el ejemplo anterior donde apareca un textbox en el formulario, hubiera quedado mejor con un mensaje aclaratorio contenido en un control label:

PROPIEDADES Caption: Es el texto que contendr el control. Alignment: Alineacin del texto contenido en el control, no necesita que est activada ninguna otra propiedad. BorderStyle: Si queremos que aparezca un borde alrededor del control activaremos esta propiedad. Para este control no se suelen utilizar los eventos ya que su contenido suele cambiar poco a lo largo de la ejecucin de la aplicacin. De todas formas los eventos son casi los mismos del control textbox excepto que no dispone de los eventos GetFocus y LostFocus ya que a este control no se le puede dar el enfoque. En la parte final de este capitulo veremos un ejemplo donde se muestra el funcionamiento de todos los controles que vamos a ir viendo.Por ahora a ver si conseguis que ahora el mensaje no aparezca en el formulario sino en un segundo label situado en el formulario, dejando un control label que muestre el mensaje aclaratorio que hemos visto antes. CommandButton

Este control es el tpico botn que aparece en todas las aplicaciones y que al hacer click sobre l nos permite realizar alguna operacin concreta, normalmente Aceptar o Cancelar. Aunque segn el cdigo que le asociemos podremos realizar las operaciones que queramos. En el ejemplo anterior podemos aadir un control de este tipo para salir de la aplicacin sin tener pulsar sobre la equis de la esquina superior derecha.

Pero slo con introducir un control de este tipo con el texto salir que se introduce a traves de la propiedad caption no basta. Habr que asociarle un cdigo que nos permita salir de la aplicacin

212

en el evento adecuado. Y el evento por excelencia de este control es click. As pues accederemos al cdigo del control y la sentencia nos permitir salir de la aplicacin es End, simplemente tecleamos esa palabra en el evento click y comprobar que realmente finalizaremos nuestra aplicacin al pulsar sobre dicho botn. PROPIEDADES Caption: Aqui pondremos el letrero que queremos que apaezca en el botn: aceptar, cancelar, salir, etc... Enabled: Esta es una nueva propiedad, cuando su valor es true el botn funciona normalmente, cuando su valor es false el boton se encuentra desactivado, no responde a los eventos producidos sobre l y el texto aparece en un gris claro advirtiendonos de su estado. Podemos utilizar esta propiedad para activar o desactivar un boton dependiendo del estado de otros controles. Por ejemplo, en un boton Aceptar, no activarlo hasta que se haya introducido una cantidad en un control textbox, ya que ese botn nos calcular el IVA de la cantidad. EVENTOS Click: Es el evento tipico de este control y el que ms se utiliza. MouseMove: Como sabemos detecta el movimiento del raton sobre el control. Puede servir para que aparezca un mensaje en un control Label que nos aporte informacin sobre la utilidad del control ampliando el texto que hayamos colocado como caption del commandbutton. OptionButton

Este control nos permite elegir una opcin entre varias de las que se nos plantean. Cada opcin ser un control optionbutton diferente. Facilita la introduccin de datos por parte del usuario:

De todas las opciones que se nos ofrece, en este caso los 4 colores, slo podremos activar una. Si activamos cualquier otra opcin, se desactivar automticamente la ltima que tenamos activada.

213

El marco que est alrededor de los 4 controles optionbutton se trata del control Frame , es opcional, aunque es conviente colocarlo siempre que hagamos uso de las opciones. No slo por motivos de presentacin sino porque de esta manera podremos establecer grupos de controles optionbutton independientes en los que en cada grupo slo pueda haber una opcion activada a la vez. Tambin, al mover el marco se movern los controles incluidos en l facilitndonos las modificaciones. Para que los controles Optionbutton queden englobados dentro de un control Frame, primero tendremos que colocar el control Frame en el formulario con el tamao adecuado y despues ir colocando los controles Optionbutton dentro del Frame. Del control Frame la nica propiedad que nos interesar es caption, que es el texto que aparecer en el encabezado, en el ejemplo anterior: colores. PROPIEDADES DE OPTIONBUTTON Caption: El texto que aparecer al lado del control: Rojo, verde, etc... Value: Es el valor que tendr el control: True si se encuentra activado y False si no lo est. Para comprobar que opcion ha activado el usuario comprobaremos el estado de esta propiedad. Alignment: Alineacin del texto respecto al control: Left Justify: el control aparece a la izquierda del texto. Es el ejemplo anterior.Right Justify: el control aparece a la derecha del texto. Los eventos del control son los mismos que en anteriores controles, aunque no se suele asociar cdigo a los eventos de este tipo de controles, sino nicamente conocer el valor que tienen: true o false.

3.1.1- APLICACION DE EJEMPLO


Para practicar con los controles que hemos visto vamos a realizar una pequea aplicacin que consistir en realizar con 2 nmeros que introduzcamos, una operacin que seleccionemos y mostrar el resultado. El formulario donde estarn todos los controles es el siguiente:

La propiedad Caption de cada uno de los controles es la que se muestra en el formulario.

214

He modificado la propiedad Name de cada control para que al utilizarlos desde el codigo sepamos cual es el control con el que trabajamos: Los controles TextBox tienen los nombres: Num1, Num2 y Resul. Los controles Optionbutton tienen cada uno de ellos el mismo nombre que su caption Los controles CommandButton tienen los nombres: Calcular, Limpiar y Salir. A los controles Label y al Frame no have falta cambiarles el nombre.

Lo que habr que hacer ahora es asociar codigo a cada uno de los botones que es de donde se van a realizar las operaciones: Para el botn Calcular que es el que nos mostrar el resultado segn la operacin seleccionada, he utilizado la instruccin If Then Else que vimos en el captulo anterior:

El botn Limpiar Datos nos va a servir para borrar de una forma rpida los datos introducidos por el usuario y el resultado preparando los controles para introducir nuevos datos. El cdigo que tendremos que introducir es muy simple:

215

El botn Salir nicamente contendr la sentencia End.

4 -Formularios.
Los formularios tienen sus propios eventos, propiedades y mtodos con los que se puede controlar su apariencia y comportamiento. El primer paso para disear un formulario consiste en establecer sus propiedades. Puede establecer las propiedades de un formulario en tiempo de diseo en la ventana Propiedades o en tiempo de ejecucin, escribiendo cdigo. Nota En tiempo de diseo, que es cualquier momento mientras est desarrollando una aplicacin en el entorno de Visual Basic, se trabaja con formularios y controles, se establecen propiedades y se escribe cdigo para los eventos. Tiempo de ejecucin es cualquier momento mientras se ejecuta realmente la aplicacin y se interacta con ella como lo hara un usuario.

4.1- Estableciendo las Propiedades ms importantes de los formularios.


Muchas propiedades de un formulario afectan a su apariencia fsica. La propiedad Caption determina el texto que muestra la barra de ttulo del formulario y la propiedad Icon establece el icono que aparece cuando se minimiza un formulario. Las propiedades MaxButton y MinButton determinan si el formulario se puede maximizar o minimizar. Cambiando la propiedad BorderStyle puede controlar el comportamiento de cambio de tamao del formulario. Las propiedades Height y Width determinan el tamao inicial de un formulario, mientras que las propiedades Left y Top determinan la ubicacin del formulario en relacin con la esquina superior izquierda de la pantalla. Con la propiedad WindowState puede establecer si el formulario se inicia en estado maximizado, minimizado o normal. La propiedad Name establece el nombre con el que har referencia al formulario en el cdigo. De forma predeterminada, cuando se agrega un formulario por primera vez a un proyecto, su nombre es Form1, Form2, etc. Es conveniente establecer la propiedad Name a algo ms significativo, como frmEntry para un formulario de entrada de pedidos. La mejor manera de familiarizarse con las numerosas propiedades de los formularios es experimentar. Cambie algunas propiedades de un formulario en la ventana Propiedades y ejecute la aplicacin para ver su efecto. Puede aprender ms sobre cada propiedad si la selecciona y presiona F1 para ver Ayuda contextual.

4.2- Eventos y Mtodos de los formularios.


Como objetos que son, los formularios pueden ejecutar mtodos y responder a eventos. El evento Resize de un formulario se desencadena siempre que se cambia el tamao de un formulario, ya sea por una accin del usuario o a travs del cdigo. Esto permite realizar acciones como mover o cambiar el tamao de los controles de un formulario cuando han cambiado sus dimensiones. El evento Activate se produce siempre que un formulario se convierte en el formulario activo; el evento Deactivate se produce cuando otro formulario u otra aplicacin se convierte en activo. Estos eventos son adecuados para iniciar o finalizar acciones del formulario. Por ejemplo, en el evento Activate podra escribir cdigo para resaltar el texto de un determinado cuadro de texto; con el evento Deactivate podra guardar los cambios efectuados en un archivo o en una base de datos.

216

Para hacer visible un formulario se invoca el mtodo Show: Form2.Show Para descargar un formulario (cerrarlo), se invoca al mtodo Unload: Form2.Unload Unload Me Me significa el Formulario activo.

Para ocultar un formulario (pero dejarlo activo en memoria) se invoca al mtodo Hide: Form2.Hide Invocar el mtodo Show tiene el mismo efecto que establecer a True la propiedad Visible del formulario. Muchos mtodos de un formulario implican texto o grficos. Los mtodos Print, Line, Circle y Refresh son tiles para imprimir o dibujar directamente en la superficie de un formulario.

4.3- Establecer el formulario de arranque de la aplicacin.


De forma predeterminada, el primer formulario de la aplicacin es el formulario inicial. Cuando la aplicacin inicia la ejecucin, se presenta este formulario (el primer cdigo que se ejecuta es el del evento Form_Initialize de dicho formulario). Si quiere presentar un formulario diferente cuando se inicie la aplicacin, debe cambiar el formulario inicial. Para cambiar el formulario inicial 1. 2. En el men Proyecto, elija Propiedades del proyecto. Elija la ficha General.

3. En el cuadro de lista Objeto inicial, seleccione el formulario que desee que sea el nuevo formulario inicial. 4. Elija Aceptar.

Inicio sin formulario inicial Algunas veces puede desear que la aplicacin se inicie sin cargar ningn formulario. Por ejemplo, puede que desee ejecutar cdigo que cargue un archivo de datos y despus presentar uno de entre varios formularios, segn el contenido de dicho archivo. Puede hacerlo creando un procedimiento Sub llamado Main en un mdulo estndar, como en el siguiente ejemplo: Sub Main() Dim intStatus As Integer ' Llamar a un procedimiento de funcin para comprobar el estado

217

' del usuario. intStatus = GetUserStatus ' Mostrar un formulario inicial distinto segn el estado. If intStatus = 1 Then frmMain.Show Else frmPassword.Show End If Este procedimiento tiene que ser un procedimiento Sub y no puede estar en un mdulo de formulario. Para establecer el procedimiento Sub Main como objeto inicial, en el men Proyecto elija Propiedades del proyecto, seleccione la ficha General y seleccione Sub Main en el cuadro Objeto inicial.

5 -Combo Box (lista combo)


Un control ComboBox combina las caractersticas de un control TextBox y un control ListBox; los usuarios pueden introducir informacin en la parte del cuadro de texto o seleccionar un elemento en la parte de cuadro de lista del control. Para agregar o eliminar elementos en un control ComboBox, use el mtodo AddItem o RemoveItem. Establezca las propiedades List, ListCount y ListIndex para permitir a un usuario tener acceso a los elementos de un control ComboBox. Como alternativa, puede agregar elementos a la lista mediante la propiedad List en tiempo de diseo. Nota Un evento Scroll ocurrir en un control ComboBox slo cuando se desplace el contenido de la parte desplegable del ComboBox, no cada vez que cambie el contenido del ComboBox. Por ejemplo, si la parte desplegable de un ComboBox contiene cinco elementos y el elemento superior est resaltado, no ocurrir un evento Scroll hasta que presione seis veces la flecha hacia abajo (o una vez la tecla AV PG). Despus de eso, ocurrir un evento Scroll por cada pulsacin de la tecla de flecha hacia abajo. Sin embargo, si despus presiona la tecla de flecha hacia arriba, no ocurrir un evento Scroll hasta que presione seis veces la tecla de flecha hacia arriba (o una vez la tecla RE PG). Despus de eso, cada vez que presione la tecla de flecha hacia arriba se producir un evento Scroll. Eventos: Evento Change Evento Click Evento DblClick Evento DragDrop Evento LostFocus Evento OLECompleteDrag Evento OLEDragDrop Evento OLEDragOver

218

Evento DragOver Evento DropDown Evento GotFocus Eventos KeyDown y KeyUp Evento KeyPress Mtodos: Mtodo AddItem

Evento OLEGiveFeedback Evento OLESetData Evento OLEStartDrag Evento Scroll

Mtodo Refresh Mtodo RemoveItem Mtodo SetFocus Mtodo ShowWhatsThis Mtodo ZOrder

Mtodo Clear (Clipboard, ComboBox, ListBox) Mtodo Drag Mtodo Move Mtodo OLEDrag Propiedades: Propiedad Appearance Propiedades BackColor y ForeColor Propiedad Container Propiedad DataChanged Propiedad DataField Propiedad DragIcon Propiedad DragMode Propiedad Enabled Propiedad Font

Propiedad ListIndex Propiedad Locked Propiedad MouseIcon Propiedad MousePointer Propiedad Name Propiedad NewIndex Propiedad OLEDragMode Propiedad OLEDropMode Propiedad Parent

Propiedades FontBold, FontItalic, FontStrikethru y FontUnderline Propiedad FontName ActiveX) Propiedad FontSize Propiedades Height y Width Propiedades SelLength, SelStart y SelText (Controles

Propiedad Sorted Propiedad Style

219

Propiedad HelpContextID Propiedad hWnd Propiedad Index (Control Array) Propiedad IntegralHeight Propiedad ItemData Propiedades Left y Top Propiedad List Propiedad ListCount

Propiedad TabIndex Propiedad TabStop Propiedad Tag Propiedad Text Propiedad ToolTipText Propiedad TopIndex Propiedad Visible Propiedad WhatsThisHelpID

Propiedades SelLength, SelStart y SelText

6- List Box (lista).


Un control ListBox muestra una lista de elementos entre los cuales el usuario puede seleccionar uno o ms. Si el nmero de elementos supera el nmero que puede mostrarse, se agregar automticamente una barra de desplazamiento al control ListBox. Si no se selecciona ningn elemento, el valor de la propiedad ListIndex ser -1. El primer elemento de la lista es ListIndex 0 y el valor de la propiedad ListCount siempre es uno ms que el mayor valor de ListIndex. Para agregar o eliminar elementos de un control ListBox, use el mtodo AddItem o RemoveItem. Establezca las propiedades List, ListCount y ListIndex para permitir que un usuario tenga acceso a elementos del ListBox. Tambin puede agregar elementos a la lista mediante la propiedad List en tiempo de diseo. Eventos Evento Click Evento DblClick Evento DragDrop Evento DragOver Evento GotFocus Evento ItemCheck Eventos KeyDown y KeyUp Evento KeyPress Eventos MouseDown y MouseUp Evento MouseMove Evento OLECompleteDrag Evento OLEDragDrop Evento OLEDragOver Evento OLEGiveFeedback Evento OLESetData Evento OLEStartDrag

220

Evento LostFocus Mtodos Mtodo AddItem

Evento Scroll

Mtodo Refresh Mtodo RemoveItem

Mtodo Clear (Clipboard, ComboBox, ListBox) Mtodo Drag Mtodo Move Mtodo OLEDrag Propiedades Propiedad Appearance Propiedades BackColor y ForeColor Propiedad Columns (ListBox) Propiedad Container Propiedad DataChanged Propiedad DataField Propiedad DataSource Propiedad DragIcon Propiedad DragMode Propiedad Enabled Propiedad Font

Mtodo SetFocus Mtodo ShowWhatsThis Mtodo ZOrder

Propiedad MousePointer Propiedad MultiSelect Propiedad Name Propiedad NewIndex Propiedad OLEDragMode Propiedad OLEDropMode Propiedad Parent Propiedad SelCount Propiedad Selected Propiedad Sorted Propiedad Style

Propiedades FontBold, FontItalic, FontStrikethru y FontUnderline Propiedad FontName Propiedad FontSize Propiedades Height y Width Propiedad HelpContextID Propiedad hWnd Propiedad Index (Control Array) Propiedad TabStop Propiedad Tag Propiedad Text Propiedad ToolTipText Propiedad TopIndex Propiedad Visible

221

Propiedad ItemData Propiedades Left y Top Propiedad List Propiedad ListIndex

Propiedad WhatsThisHelpID Propiedad TabIndex Propiedad ListCount Propiedad MouseIcon

7- Timer (cronmetro)
Un control Timer puede ejecutar cdigo a intervalos peridicos produciendo un evento Timer. El control Timer, invisible para el usuario, resulta til para el procesamiento de fondo. No puede establecer la propiedad Enabled de un Timer para una seleccin mltiple de controles que no sean controles Timer. No existe ningn lmite prctico en cuanto al nmero de controles Timer activos que puede tener en Visual Basic 5.0 ejecutndose en Windows 95 o en Windows NT. Eventos: Evento Timer Propiedades: Propiedad Enabled Propiedad Index (Control Array) Propiedad Interval Propiedades Left y Top Propiedad Name Propiedad Parent Propiedad Tag

8- Shape (figura).
Shape es un control grfico que se muestra como un rectngulo, un cuadrado, una elipse, un crculo, un rectngulo redondeado o un cuadrado redondeado. Utilice controles Shape en tiempo de diseo en lugar de o adems de invocar los mtodos Circle y Line en tiempo de ejecucin. Puede dibujar un control Shape en un contenedor, pero no puede actuar como contenedor. El efecto de establecer la propiedad BorderStyle depende del valor de la propiedad BorderWidth. Si BorderWidth no es 1 y BorderStyle no es 0 6, BorderStyle se establece a 1. Mtodos: Mtodo Move Mtodo Refresh Mtodo ZOrder

222

Propiedades: Propiedades BackColor y ForeColor Propiedad BackStyle Propiedad BorderColor Propiedad BorderStyle Propiedad BorderWidth Propiedad Container Propiedad DrawMode Propiedad FillColor Propiedad FillStyle Propiedades Height y Width Propiedad Index (Control Array) Propiedades Left y Top Propiedad Name Propiedad Parent Propiedad Shape Propiedad Tag Propiedad Visible

9- Line (lnea).
Line es un control grfico que se muestra como una lnea horizontal, vertical o diagonal. Puede utilizar un control Line en tiempo de diseo para dibujar lneas en formularios. En tiempo de ejecucin puede utilizar un control Line en lugar del mtodo Line, o adems de l. Las lneas dibujadas con el control Line permanecen en el formulario aunque la propiedad AutoRedraw sea False. Los controles Line pueden mostrarse en formularios, en cuadros de imagen y en marcos. No puede utilizar el mtodo Move para mover un control Line en tiempo de ejecucin, pero s se puede mover o cambiar de tamao alterando sus propiedades X1, X2, Y1 e Y2. El efecto de establecer la propiedad BorderStyle depende del valor de la propiedad BorderWidth. Si BorderWidth no es 1 y BorderStyle no es 0 6, BorderStyle se establecer a 1. Mtodos: Mtodo Refresh Mtodo ZOrder Propiedades: Propiedad BorderColor Propiedad BorderStyle Propiedad BorderWidth Propiedad Container Propiedad DrawMode Propiedad Name Propiedad Parent Propiedad Tag Propiedad Visible Propiedades X1, Y1, X2 y Y2

223

Propiedad Index (Control Array)

10- Image (imagen)


Utilice el control Image para mostrar un grfico. Un control Image puede mostrar un grfico desde un mapa de bits, un icono o un metarchivo, as como un metarchivo mejorado, un archivo JPEG o archivos GIF. El control Image utiliza menos recursos del sistema y actualiza con ms rapidez que un control PictureBox, pero slo admite un subconjunto de las propiedades, los eventos y los mtodos de PictureBox. Use la propiedad Stretch para determinar si el grfico se escala para ajustarse al control o viceversa. Aunque puede colocar un control Image dentro de un contenedor, un control Image no puede actuar como contenedor. Eventos: Evento Click Evento DblClick Evento DragDrop Evento DragOver Eventos MouseDown y MouseUp Evento MouseMove Mtodos: Mtodo Drag Mtodo Move Mtodo OLEDrag Propiedades: Propiedad Appearance Propiedad BorderStyle Propiedad Container Propiedad DataChanged Propiedad DataField Propiedad DataSource Propiedad DragIcon Propiedad MouseIcon Propiedad MousePointer Propiedad Name Propiedad OLEDragMode Propiedad OLEDropMode Propiedad Parent Propiedad Picture Mtodo Refresh Mtodo ShowWhatsThis Mtodo ZOrder Evento OLECompleteDrag Evento OLEDragDrop Evento OLEDragOver Evento OLEGiveFeedback Evento OLESetData Evento OLEStartDrag

224

Propiedad DragMode Propiedad Enabled Propiedades Height y Width Propiedad Index (Control Array) Propiedades Left y Top

Propiedad Stretch Propiedad Tag Propiedad ToolTipText Propiedad Visible Propiedad WhatsThisHelpID

11- Data (acceso a bases de datos)


Proporciona acceso a datos almacenados en bases de datos mediante uno de los tres tipos de objetos Recordset. El control Data le permite desplazarse de un registro a otro, as como presentar y manipular datos de los registros en controles enlazados. Sin un control Data los controles enlazados a datos (vinculados) de un formulario no pueden tener acceso a los datos automticamente. Puede realizar la mayora de las operaciones de acceso a datos utilizando el control Data sin escribir cdigo. Los controles enlazados a datos vinculados a un control Data presentan automticamente los datos de uno o varios campos del registro actual o, en algunos casos, de un conjunto de registros situado a ambos lados del registro actual. El control Data realiza todas sus operaciones sobre el registro actual. Si el control Data recibe instrucciones para desplazarse a un registro diferente, todos los controles enlazados pasan automticamente los posibles cambios al control Data para que los guarde en la base de datos. Despus, el control Data se desplaza al registro solicitado y pasa los datos del registro actual a los controles enlazados, en los que se presentan. El control Data administra automticamente una serie de contingencias entre las que se incluyen los conjuntos de registros vacos, la insercin de nuevos registros, la modificacin y actualizacin de registros existentes, y la administracin de ciertos tipos de errores. Sin embargo, en aplicaciones ms sofisticadas es necesario interceptar algunas condiciones de error que el control Data no puede administrar. Por ejemplo, si el motor de base de datos Microsoft Jet tiene un problema al tener acceso al archivo de base de datos, no tiene el permiso adecuado o no puede ejecutar la consulta, se producir un error interceptable. Si el error se produce antes de que se inicien los procedimientos de la aplicacin o se trata de errores internos, se desencadenar el evento Error.

12- Controles enlazados


Los controles DBList, DBCombo, DBGrid y MSFlexGrid son capaces de administrar conjuntos de registros cuando estn enlazados a un control Data. Todos estos controles permiten presentar o manipular varios registros a la vez. Los controles incorporados Picture, Label, TextBox, CheckBox, Image, OLE, ListBox y ComboBox tambin son controles enlazados a datos y se pueden enlazar a un nico campo de un Recordset administrado por un control Data. En las ediciones Profesional y Empresarial se encuentran disponibles otros controles enlazados a datos como MaskedEdit y RichTextBox; otros proveedores tambin ofrecen controles adicionales.

12.1 Funcionamiento

225

Una vez iniciada la aplicacin, Visual Basic utiliza las propiedades del control Data para abrir la base de datos seleccionada, crear un objeto Database y crear un objeto Recordset. Las propiedades Database y Recordset del control Data hacen referencia a los objetos Database y Recordset recin creados, que se pueden manipular de forma independiente del control Data, con o sin controles enlazados. El control Data se inicializa antes del evento Form_Load inicial del formulario en el que se encuentra. Si se producen errores durante esta fase de inicializacin, se produce un error no interceptable. Cuando Visual Basic utiliza el motor de base de datos Jet para crear un Recordset, no se pueden producir otras operaciones o eventos de Visual Basic hasta que se termine la operacin. Sin embargo, otras aplicaciones basadas en Windows pueden seguir en ejecucin mientras se est creando el Recordset. Si el usuario presiona CTRL+INTER mientras el motor Jet est generando un Recordset, la operacin termina, se produce un error interceptable y la propiedad Recordset del control Data se establece a Nothing. En tiempo de diseo, la segunda vez que se presiona CTRL+INTER hace que Visual Basic presente la ventana Depuracin. Puede manipular el control Data con el mouse (ratn), desplazndose de un registro a otro, o al principio o al final del Recordset. Las propiedades EOFAction y BOFAction determinan lo que ocurre cuando el usuario se desplaza al principio o al final de un Recordset con el mouse. No puede establecer el enfoque en un control Data.

12.2 Validacin
El evento Validate y la propiedad DataChanged se utilizan para realizar comprobaciones de ltima hora sobre los registros que se van a escribir en la base de datos.

12.3 Objetos de acceso a datos


En los procedimientos puede utilizar los objetos de acceso a datos Database y Recordset creados por el control Data. Cada objeto Database y Recordset tiene sus propias propiedades y mtodos, y puede escribir procedimientos que utilicen dichas propiedades y mtodos para manipular sus datos. Por ejemplo, el mtodo MoveNext de un objeto Recordset desplaza el registro actual al siguiente registro dentro del Recordset. Para invocar este mtodo, podra utilizar el cdigo siguiente: Data1.Recordset.MoveNext El control Data puede tener acceso a cualquiera de los tres tipos de objetos Recordset del motor Jet versin 3.0. Si no selecciona el tipo de conjunto de registros, se crea un Recordset de tipo dynaset. En muchos casos, el tipo predeterminado y la configuracin del objeto Recordset creado son muy ineficientes. Es decir, puede que no necesite un cursor actualizable totalmente desplazable de conjunto de claves para tener acceso a los datos. Por ejemplo, un Recordset de tipo snapshot, de slo lectura y unidireccional se creara con ms rapidez que el cursor predeterminado. Asegrese de elegir el tipo ms eficiente, as como las propiedades Exclusive, Options y ReadOnly adecuadas a su situacin. Para seleccionar un tipo de Recordset especfico, establezca la propiedad RecordsetType del control Data a:

226

RecordsetType Table Dynaset Snapshot

Valor 0 1 2

Constante vbRSTypeTable vbRSTypeDynaset vbRSTypeSnapshot (Predeterminado)

12.4 Ediciones Profesional y Empresarial


En lo que concierne al acceso a datos, la principal diferencia entre las ediciones de Aprendizaje, Profesional y Empresarial de Visual Basic es la capacidad de crear nuevos objetos de acceso a datos. En la Edicin estndar no puede declarar (con la palabra clave Dim) variables como objetos de acceso a datos dentro del cdigo. Esto significa que slo el control Data puede crear objetos Database y Recordset. En las ediciones Profesional y Empresarial de Visual Basic versin 5.0 puede crear un objeto Recordset y asignarlo a la propiedad Recordset de un control Data. Cualquier control enlazado que est conectado al control Data permitir manipular los registros del Recordset que ha creado. Asegrese de que las propiedades DataField de los controles enlazados estn establecidas a nombres de campo vlidos dentro del nuevo Recordset.

12.5 Consultas almacenadas


Otra opcin importante al utilizar el control Data es la posibilidad de ejecutar consultas almacenadas. Si antes ha creado un objeto QueryDef, el control Data puede ejecutarlo y crear un Recordset mediante las propiedades SQL, Connect y otras del objeto QueryDef. Para ejecutar un QueryDef, establezca la propiedad RecordSource del control Data al nombre del QueryDef y utilice el mtodo Refresh. Si el QueryDef almacenado contiene parmetros, tiene que crear el Recordset y pasarlo al control Data.

12.6 Tratamiento de BOF/EOF


El control Data tambin puede administrar lo que ocurre cuando se encuentre con un Recordset sin registros. Modificando la propiedad EOFAction puede programar el control Data para que pase al modo AddNew de forma automtica. Puede programar el control Data para que se ajuste automticamente a la parte superior o inferior del formulario primario utilizando la propiedad Align. En cualquiera de los casos, el control Data cambia de tamao horizontalmente para llenar todo el ancho de su formulario primario, siempre que ste cambie de tamao. Eventos: Evento DragDrop Evento DragOver Evento Error Evento OLEDragOver Evento OLEGiveFeedback Evento OLESetData

227

Eventos MouseDown, MouseUp Evento MouseMove Evento OLECompleteDrag Evento OLEDragDrop Mtodos Mtodo Drag Mtodo Move Mtodo OLEDrag Mtodo Refresh Propiedades Propiedad Align Propiedad Appearance Propiedades BackColor, ForeColor Propiedad BOFAction, EOFAction Propiedad Caption Propiedad Connect Propiedad Database Propiedad DatabaseName Propiedad DefaultCursorType Propiedad DefaultType Propiedad DragIcon Propiedad DragMode Propiedad EditMode Propiedad Enabled Propiedad Exclusive Propiedad Font

Evento OLEStartDrag Evento Reposition Evento Resize Evento Validate

Mtodo ShowWhatsThis Mtodo UpdateControls Mtodo UpdateRecord Mtodo ZOrder

Propiedad FontSize Propiedades Height, Width Propiedad Index (Matriz de controles) Propiedades Left, Top Propiedad MouseIcon Propiedad MousePointer Propiedad Name Propiedad OLEDropMode Propiedad Options Propiedad Parent Propiedad ReadOnly (Aceso de datos) Propiedad Recordset Propiedad RecordsetType Propiedad RecordSource Propiedad Tag Propiedad ToolTipText

228

Propiedades FontBold, FontItalic, FontStrikethru, FontUnderline Propiedad FontName Propiedad Visible Propiedad WhatsThisHelpID

229

Curso de Visual Basic II


13 -Eventos ms importantes de los controles estndares. 13.1-Change
Aplicable a: Control ComboBox, Controles HScrollBar y VScrollBar, Control Label, Control PictureBox, Control TextBox Indica que el contenido de un control ha cambiado. Cmo y cundo ha ocurrido este evento vara segn el control: ComboBox: cambia el texto de la parte de cuadro de texto del control. Ocurre slo si la propiedad Style est establecida a 0 (Dropdown Combo) o 1 (Simple Combo) y el usuario cambia el texto o usted cambia la configuracin de la propiedad Text mediante cdigo. DirListBox: cambia el directorio seleccionado. Ocurre cuando el usuario hace doble clic en un nuevo directorio o cuando usted cambia la configuracin de la propiedad Path mediante cdigo. DriveListBox: cambia la unidad seleccionada. Ocurre cuando el usuario selecciona una nueva unidad o cuando usted cambia la configuracin de la propiedad Drive mediante cdigo. HScrollBar y VScrollBar (barras de desplazamiento horizontal y vertical): mueven la parte de cuadro de desplazamiento de la barra de desplazamiento. Ocurre cuando el usuario desplaza o cuando usted cambia la configuracin de la propiedad Value mediante cdigo. Label: cambia el contenido del control Label. Ocurre cuando un vnculo DDE actualiza los datos o cuando usted cambia la configuracin de la propiedad Caption mediante cdigo. PictureBox: cambia el contenido del control PictureBox. Ocurre cuando un vnculo DDE actualiza los datos o cuando usted cambia la configuracin de la propiedad Picture mediante cdigo. TextBox: cambia el contenido del cuadro de texto. Ocurre cuando un vnculo DDE actualiza los datos, cuando un usuario cambia el texto o cuando usted cambia la configuracin de la propiedad Text mediante cdigo.

Sintaxis Private Sub objeto_Change([ndice As Integer]) La sintaxis del evento Change consta de las siguientes partes: Parte objeto ndice Descripcin Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. Un entero que identifica nicamente a un control si est en una matriz de controles.

Comentarios

230

El procedimiento del evento Change puede sincronizar o coordinar la presentacin de datos entre controles. Por ejemplo, puede utilizar un procedimiento de evento Change de una barra de desplazamiento para actualizar la configuracin de la propiedad Value de la barra de desplazamiento de un control TextBox. O bien, puede utilizar un procedimiento de evento Change para mostrar datos y frmulas en un rea de trabajo y los resultados en otra rea. Los procedimientos de evento Change son tambin tiles para actualizar propiedades de controles del sistema de archivos (DirListBox, DriveListBox y FileListBox). Por ejemplo, puede actualizar la configuracin de la propiedad Path para que un control DirListBox refleje un cambio en la configuracin de la propiedad Drive de un control DriveListBox. Nota Un procedimiento de evento Change puede algunas veces causar un evento en cascada. Esto ocurre cuando el procedimiento de evento Change del control altera el contenido del control, por ejemplo, estableciendo una propiedad en el cdigo que determina el valor del control, como el valor de la propiedad Text para un control TextBox. Para impedir un evento en cascada: Si es posible, evite escribir un procedimiento de evento Change para un control que altere el contenido de ese control. Si escribe un procedimiento as, asegrese de establecer un indicador que impida cambios posteriores mientras el cambio actual est en curso. Evite crear dos o ms controles cuyos procedimientos de evento Change se vean afectados entre s, por ejemplo, dos controles TextBox que se actualicen entre s durante sus eventos Change. Evite utilizar una funcin o una instruccin MsgBox en este evento para los controles HScrollBar y VScrollBar.

Ejemplo del evento Change Este ejemplo muestra la configuracin numrica de la propiedad Value de una barra de desplazamiento horizontal en un control TextBox. Para probar este ejemplo, cree un formulario con un control TextBox y un control HScrollBar y despus pegue el cdigo en la seccin Declaraciones de un formulario que contenga una barra de desplazamiento horizontal (control HScrollBar) y un control TextBox. Presione F5 y haga clic en la barra de desplazamiento horizontal. Private Sub Form_Load () HScroll1.Min = 0 '

Establece Min.

Establece Max.

HScroll1.Max = 1000

'

HScroll1.LargeChange = 100 Establece LargeChange. HScroll1.SmallChange = 1 Establece SmallChange. End Sub Private Sub HScroll1_Change () Text1.Text = HScroll1.Value

'

'

231

End Sub

13.2-Click
Aplicable a: Control CheckBox, Control ComboBox, Control CommandButton, Objeto Form, Control Frame, Control Image, Control Label, Control ListBox, Control Menu, Control OptionButton, Control PictureBox, Control TextBox. Ocurre cuando el usuario presiona y suelta un botn del mouse (ratn) en un objeto. Tambin puede ocurrir cuando se cambia el valor de un control. Para un objeto Form, este evento ocurre cuando el usuario hace clic en un rea en blanco o en un control desactivado. Para un control, este evento ocurre cuando el usuario: Hace clic en un control con el botn primario o secundario del mouse. Con un control CheckBox, CommandButton, ListBox o OptionButton, el evento Click slo ocurre cuando el usuario hace clic con el botn primario del mouse. Selecciona un elemento de un control ComboBox o ListBox, ya sea presionando las teclas de direccin o haciendo clic con el botn del mouse. Presiona la BARRA ESPACIADORA cuando un control CommandButton, OptionButton o CheckBox tiene el enfoque. Presiona ENTRAR cuando un formulario tiene un control CommandButton con su propiedad Default establecida a True. Presiona ESC cuando un formulario tiene un botn Cancelar, un control CommandButton con su propiedad Cancel establecida a True. Presiona una tecla de acceso para un control. Por ejemplo, si el ttulo de un control CommandButton es "&Ir", al presionar ALT+I se desencadena este evento.

Tambin puede desencadenar el evento Click en el cdigo si: Sintaxis Private Sub Form_Click( ) Private Sub objeto_Click([ndice As Integer]) La sintaxis del evento Click consta de las siguientes partes: Parte objeto ndice Descripcin Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. Un entero que identifica nicamente a un control si est en una matriz de controles. Establece la propiedad Value de un control CommandButton a True. Establece la propiedad Value de un control OptionButton a True. Cambia el valor de la propiedad Value de un control CheckBox.

Comentarios

232

Por lo general se adjunta un procedimiento de evento Click a un control CommandButton, un objeto Menu o un control PictureBox para realizar comandos y acciones similares a comandos. Para los dems controles aplicables, utilice este evento para desencadenar acciones como respuesta a un cambio en el control. Puede utilizar la propiedad Value de un control para comprobar el estado del control desde el cdigo. Hacer clic en un control genera los eventos MouseDown y MouseUp adems del evento Click. El orden en que ocurren estos tres eventos vara de un control a otro. Por ejemplo, para los controles ListBox y CommandButton, los eventos ocurren en este orden: MouseDown, Click, MouseUp. Pero para los controles FileListBox, Label o PictureBox, los eventos ocurren en este otro orden: MouseDown, MouseUp y Click. Cuando est adjuntando procedimientos para estos eventos relacionados, asegrese de que sus acciones no entran en conflicto. Si el orden de los eventos es importante en la aplicacin, pruebe el control para determinar el orden de los mismos. Nota Para distinguir entre los botones primario, secundario y central del mouse, utilice los eventos MouseDown y MouseUp. Si hay cdigo en el evento Click, nunca se activar el evento DlbClick ya que de los dos eventos, Click es el primero que se activa. Como resultado, el evento Click intercepta el clic del mouse, por lo que DblClick nunca se producir. Ejemplo del Evento Click En este ejemplo, cada vez que se hace clic en un control PictureBox se mueve diagonalmente por un formulario. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga un control PictureBox en la esquina inferior izquierda del mismo, despus presione F5 y haga clic en el control PictureBox. Private Sub Picture1_Click () Picture1.Move Picture1.Left + 750, Picture1.Top - 550 End Sub

13.3- GotFocus
Aplicable a: Control CheckBox, Control ComboBox, Control CommandButton, , Objeto Form Controles HScrollBar y VScrollBar, Control ListBox, Control OptionButton, Control PictureBox, Control TextBox. Ocurre cuando un objeto recibe el enfoque, ya sea mediante una accin del usuario, como tabular o hacer clic en el objeto, o cambiando el enfoque en el cdigo mediante el mtodo SetFocus. Un formulario recibe el enfoque slo cuando todos los controles visibles estn desactivados. Sintaxis Private Sub Form_GotFocus( )

Private Sub objeto_GotFocus([ndice As Integer]) La sintaxis del evento GotFocus consta de las siguientes partes:

233

Parte

Descripcin

objeto Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. ndice Un entero que identifica de manera nica a un control si est en una matriz de controles. Comentarios Normalmente, el procedimiento de evento GotFocus se utiliza para especificar las acciones que ocurren cuando un control o un formulario recibe primero el enfoque. Por ejemplo, si adjunta un procedimiento de evento GotFocus a cada control de un formulario puede guiar al usuario mostrndole instrucciones breves o mensajes en la barra de estado. Tambin puede proporcionar avisos visuales activando, desactivando o mostrando otros controles que dependan del control que tiene el enfoque. Nota Un objeto puede recibir el enfoque slo si sus propiedades Enabled y Visible estn establecidas a True. Para personalizar el interfaz de teclado en Visual Basic para mover el enfoque, establezca el orden de tabulacin o especifique teclas de acceso para controles de un formulario. Ejemplo del evento GotFocus Este ejemplo muestra un mensaje en la barra de estado cuando un botn de un grupo OptionButton obtiene el enfoque. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga dos controles OptionButton y un control Label. Establezca la propiedad Name de ambos controles OptionButton a OptionGroup y, despus, presione F5 y haga clic en los controles OptionButton. Private Sub Form_Load () Label1.AutoSize = True End Sub Private Sub OptionGroup_GotFocus (Index As Integer) Select Case Index Case 0 Label1.Caption = "La opcin 1 tiene el enfoque." Case 1 Label1.Caption = "La opcin 2 tiene el

enfoque." End Select End Sub

234

Private Sub OptionGroup_LostFocus (Index As Integer) Label1.Caption = "" End Sub

13.4- KeyPress
Aplicable a: Control CheckBox, Control ComboBox, Control CommandButton, Objeto Form Controles HScrollBar y VScrollBar, Control ListBox, Control OptionButton, Control PictureBox, Control TextBox. Ocurre cuando el usuario presiona y suelta una tecla. Sintaxis Private Sub Form_KeyPress(keyascii As Integer) Private Sub objeto_KeyPress([ndice As Integer,]keyascii As Integer) La sintaxis del evento KeyPress consta de las siguientes partes: Parte Descripcin

objeto Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. ndice Un entero que identifica de manera nica a un control si est en una matriz de controles. keyascii Un entero que devuelve un cdigo de tecla numrico ANSI estndar. keyascii se pasa por referencia; al cambiarlo se enva un carcter diferente al objeto. Cambiar keyascii a 0 cancela la pulsacin de tecla, de forma que el objeto no recibe ningn carcter. Comentarios El objeto que tiene el enfoque recibe el evento. Un formulario puede recibir el evento slo si no tiene controles visibles y activados. Un evento KeyPress puede implicar a cualquier carcter imprimible del teclado, a la tecla CTRL combinada con un carcter del alfabeto estndar o uno de los caracteres especiales, y la tecla ENTRAR o RETROCESO. Un procedimiento de evento KeyPress es til para interceptar pulsaciones de teclas realizadas en un control TextBox o ComboBox. Esto le permite comprobar inmediatamente la validez de las pulsaciones o el formato de los caracteres a medida que se escriben. Cambiar el valor del argumento keyascii cambia el carcter mostrado. KeyPress interpreta las maysculas y minsculas de cada carcter como cdigos de tecla distintos y, por tanto, como caracteres diferentes. Nota El nmero ANSI para la combinacin de teclado CTRL+@ es 0. Puesto que Visual Basic reconoce un valor keyascii de 0 como una cadena de longitud cero (""), evite utilizar CTRL+@ en sus aplicaciones.

235

Ejemplo del evento KeyPress Este ejemplo convierte a maysculas el texto escrito en un control TextBox. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga un control TextBox y, despus, presione F5 y escriba algo en el control TextBox. Private Sub Text1_KeyPress (KeyAscii As Integer) Char = Chr(KeyAscii) KeyAscii = Asc(UCase(Char)) End Sub

13.5- Load
Aplicable a: Objeto Form. Ocurre cuando se carga un formulario. Para un formulario de inicio, ocurre cuando una aplicacin se inicia como resultado de una instruccin Load o como resultado de una referencia a una propiedad o control de un formulario descargado. Sintaxis Private Sub Form_Load( ) Private Sub MDIForm_Load( ) Comentarios Normalmente utiliza un procedimiento de evento Load para incluir cdigo de inicializacin para un formulario; por ejemplo, cdigo que especifica los valores predeterminados de los controles, indica el contenido que se va a cargar en controles ComboBox o ListBox e inicializa variables a nivel del formulario. El evento Load ocurre tras el evento Initialize. Nota Cuando cree procedimientos para eventos relacionados, como Activate, GotFocus, Paint y Resize, asegrese de que sus acciones no entran en conflicto y no producen eventos recursivos. Ejemplo del evento Load Este ejemplo carga elementos en un control ComboBox cuando se carga un formulario. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga un control ComboBox y despus presione F5. Private Sub Form_Load () Combo1.AddItem "Mozart" ' Agrega elementos a la

lista.

236

Combo1.AddItem "Beethoven" Combo1.AddItem "Rock 'n Roll" Combo1.AddItem "Reggae" Combo1.ListIndex = 2 predeterminada. End Sub ' Establece la seleccin

13.6- LostFocus
Aplicable a Control CheckBox, Control ComboBox, Control CommandButton, Objeto Form Controles HScrollBar y VScrollBar, Control ListBox, Control OptionButton, Control PictureBox, Control TextBox. Ocurre cuando un objeto pierde el enfoque, ya sea por una accin del usuario, como tabular o hacer clic en otro objeto, o bien mediante un cambio del enfoque en el cdigo con el mtodo SetFocus. Sintaxis Private Sub Form_LostFocus( ) Private Sub objeto_LostFocus([ndice As Integer]) La sintaxis del evento LostFocus consta de las siguientes partes: Parte Descripcin

objeto Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. ndice Un entero que identifica de manera nica a un control si est en una matriz de controles. Comentarios Un procedimiento de evento LostFocus resulta especialmente til para comprobar y validar actualizaciones. Utilizar LostFocus puede hacer que la validacin tenga lugar conforme el usuario mueve el enfoque del control. Otro uso para este tipo de procedimiento de evento es activar, desactivar, ocultar y mostrar otros objetos, como en un procedimiento de evento GotFocus. Tambin puede invertir o cambiar condiciones que estableci en el procedimiento de evento GotFocus del objeto. Ejemplo del evento LostFocus Este ejemplo cambia el color de un control TextBox cuando recibe o pierde el enfoque (se selecciona con el mouse o la tecla TAB) y muestra el texto apropiado en el control Label. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga

237

dos controles TextBox y un control Label y, despus, presione F5 y mueva el enfoque entre Text1 y Text2. Private Sub Text1_GotFocus () ' Muestra el enfoque en rojo. Text1.BackColor = RGB(255, 0, 0) Label1.Caption = "Text1 tiene el enfoque." End Sub Private Sub Text1_LostFocus () ' Muestra la prdida del enfoque en azul. Text1.BackColor = RGB(0, 0, 255) Label1.Caption = "Text1 no tiene el enfoque." End Sub

13.7- MouseMove
Aplicable a: Control CheckBox, Control CommandButton, Control Data, Objeto Form, Control Frame, Control Image, Control Label, Control ListBox, Control OptionButton, Control PictureBox, Control TextBox. Ocurre cuando el usuario mueve el mouse. Sintaxis Private Sub Form_MouseMove(botn As Integer, mays As Integer, x As Single, y As Single) Private Sub MDIForm_MouseMove(botn As Integer, mays As Integer, x As Single, y As Single) Private Sub objeto_MouseMove([ndice As Integer,] botn As Integer, mays As Integer, x As Single, y As Single) La sintaxis del evento MouseMove consta de las siguientes partes: Parte Descripcin

objeto Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. ndice Un entero que identifica de manera nica a un control si est en una matriz de controles.

238

botn Un entero que corresponde al estado de los botones del mouse en el cual un bit se establece si el botn est presionado. El argumento botn es un campo de bit con los bits correspondientes al botn primario (bit 0), al botn secundario (bit 1) y al botn central (bit 2). Estos bits corresponden a los valores 1, 2 y 4, respectivamente. Indica el estado completo de los botones del mouse; alguno, todos o ninguno de estos tres bits puede estar establecido, lo que indica que algunos, todos o ninguno de los botones est presionado. mays Un entero que corresponde al estado de las teclas MAYS, CTRL y ALT. Un bit est establecido si la tecla est presionada. El argumento mays es un campo de bits con los bits menos significativos correspondientes a la tecla MAYS (bit 0), CTRL (bit 1) y ALT (bit 2 ). Estos bits corresponden a los valores 1, 2 y 4, respectivamente. El argumento mays indica el estado de estas teclas. Alguno, todos o ninguno de los bits puede estar establecido, lo que indica que alguna, todas o ninguna de las teclas est presionada. Por ejemplo, si se presionaron las teclas CTRL y ALT, el valor de shift sera 6. x, y Un nmero que especifica la ubicacin actual del puntero del mouse. Los valores x e y siempre se expresan en trminos del sistema de coordenadas establecido por las propiedades ScaleHeight, ScaleWidth, ScaleLeft y ScaleTop del objeto. Comentarios El evento MouseMove se genera continuamente a medida que el puntero del mouse se mueve por los objetos. A menos que otro objeto haya capturado el mouse, un objeto reconoce un evento MouseMove siempre que la posicin del mouse est dentro de sus bordes. Ejemplo del evento MouseMove Este ejemplo muestra una aplicacin de dibujo simple. El procedimiento de evento MouseDown funciona con un procedimiento MouseMove relacionado para activar el dibujo cuando est presionado cualquier botn del mouse. El procedimiento de evento MouseUp desactiva el dibujo. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario, despus presione F5, haga clic en el formulario y mueva el mouse mientras est presionado el botn del mismo. Dim PaintNow As Boolean ' Declara una variable.

Private Sub Form_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single) PaintNow = True ' Activa el dibujo. End Sub Private Sub Form_MouseUp (Button As Integer, X As Single, Y As Single) PaintNow = False End Sub Private Sub Form_MouseMove (Button As Integer, Shift As Integer, X As Single, Y As Single) If PaintNow Then ' Desactiva el dibujo.

239

PSet (X, Y) punto. End If End Sub Private Sub Form_Load () DrawWidth = 10 ForeColor = RGB(0, 0, 255) dibujo. End Sub

' Dibuja un

' Utiliza un pincel ms ancho. ' Establece el color de

13.8- Timer
Aplicable a: Control Timer Ocurre cuando ha transcurrido un intervalo preestablecido para un control Timer. La frecuencia del intervalo se almacena en la propiedad Interval del control, que especifica el tiempo en milisegundos. Sintaxis Private Sub objeto_Timer([ndice As Integer]) La sintaxis del evento Timer consta de las siguientes partes: Parte Descripcin

objeto Una expresin de objeto que da como resultado un objeto de la lista Aplicable a. ndice Un entero que identifica de manera nica a un control si est en una matriz de controles. Comentarios Utilice este procedimiento de evento para indicar a Visual Basic qu hacer cada vez que se agote el intervalo de tiempo de un control Timer. Cuando est trabajando con el evento Timer: La propiedad Interval especifica el intervalo entre los eventos Timer, en milisegundos. Siempre que la propiedad Enabled del control Timer est establecida a True y la propiedad Interval sea mayor que 0, el evento Timer espera durante el periodo especificado en la propiedad Interval.

Ejemplo del evento Timer

240

Este ejemplo muestra un reloj digital. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga un control Label y un control Timer y, despus, presione F5. Private Sub Form_Load () Timer1.Interval = 1000 ' Establece el intervalo de

Timer. End Sub

Private Sub Timer1_Timer () Label1.Caption = Time ' Actualiza la presentacin de la

hora. End Sub

Este ejemplo mueve un control PictureBox por un formulario. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga un control Timer y un control PictureBox y, despus, presione F5. Si desea obtener un efecto visual mejor puede asignar un mapa de bits al control PictureBox mediante la propiedad Picture. Dim DeltaX, DeltaY As Integer Private Sub Timer1_Timer () Picture1.Move Picture1.Left + DeltaX, Picture1.Top + DeltaY If Picture1.Left < ScaleLeft Then DeltaX = 100 If Picture1.Left + Picture1.Width > ScaleWidth + ScaleLeft Then DeltaX = -100 End If If Picture1.Top < ScaleTop Then DeltaY = 100 If Picture1.Top + Picture1.Height > ScaleHeight + ScaleTop ' Declara variables.

Then

DeltaY = -100 End If End Sub Private Sub Form_Load () Timer1.Interval = 1000 ' Establece el intervalo.

241

DeltaX = 100 DeltaY = 100 End Sub

' Inicializa variables.

13.9- Unload
Aplicable a: Objeto Form. Ocurre cuando un formulario est a punto de quitarse de la pantalla. Cuando ese formulario se vuelve a cargar, el contenido de todos sus controles se reinicializa. Este evento se desencadena porque un usuario cierra el formulario mediante el comando Cerrar del men Control o una instruccin Unload. Sintaxis Private Sub objeto_Unload(cancelar As Integer) La sintaxis del evento Unload consta de las siguientes partes: Parte objeto Descripcin Una expresin de objeto que da como resultado un objeto de la lista Aplicable a.

cancelar Un entero que determina si el formulario se quita de la pantalla. Si cancelar es 0, el formulario se quita. Establecer cancelar a cualquier valor distinto de cero impide que el formulario se quite. Comentarios Establecer cancelar a un valor distinto de cero impide que el formulario se quite, pero no detiene los dems eventos, como la salida del entorno operativo Microsoft Windows. Utilice el evento QueryUnload para detener la salida de Windows. Utilice un procedimiento de evento Unload para comprobar si el formulario se debe descargar o para especificar acciones que desea que tengan lugar cuando se descargue el formulario. Tambin puede incluir cualquier cdigo de validacin a nivel del formulario que pueda necesitar para cerrar el formulario o guardar los datos en un archivo. El evento QueryUnload ocurre antes que el evento Unload. El evento Unload ocurre antes que el evento Terminate. El evento Unload puede estar causado por la utilizacin de la instruccin Unload o porque el usuario elija el comando Cerrar del men Control del formulario, salga de la aplicacin con el botn Finalizar tarea de la Lista de tareas, o salga del entorno operativo Microsoft Windows mientras la aplicacin se est ejecutando. Ejemplo del evento Unload

242

Este ejemplo muestra un procedimiento simple para cerrar un formulario mientras se avisa al usuario con varios cuadros de mensajes. En una aplicacin real, puede agregar llamadas a procedimientos Sub de propsito general que emulen el proceso de los comandos Salir, Guardar y Guardar como del men Archivo de Visual Basic. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario y, despus, presione F5. Una vez que se muestre el formulario, presione ALT+F4 para cerrar el formulario. Private Sub Form_Unload (Cancel As Integer) Dim Msg, Response ' Declara variables.

Msg = "Desea guardar los datos antes de cerrar?" Response = MsgBox(Msg, vbQuestion + vbYesNoCancel, "Dilogo

Cerrar")

Select Case Response Case vbCancel ' No se permite cerrar.

Cancel = -1 Msg = "Se ha cancelado el comando." Case vbYes ' Introduzca cdigo para guardar los datos aqu. Msg = "Datos guardados."' Case vbNo Msg = "Datos no guardados." End Select MsgBox Msg, vbOKOnly, "Confirmacin" End Sub ' Mostrar mensaje.

13.10- QueryUnload
Aplicable a: Objeto Form y Coleccin Forms, Objeto MDIForm Ocurre antes de que se cierre un formulario o una aplicacin. Sintaxis

243

Private Sub Form_QueryUnload(cancelar As Integer, modo_descarga As Integer) Private Sub MDIForm_QueryUnload(cancelar As Integer, modo_descarga As Integer) La sintaxis del evento QueryUnload consta de las siguientes partes: Parte Descripcin

cancelar Un entero. Establecer este argumento a cualquier valor distinto de 0 detiene el evento QueryUnload en todos los formularios cargados y detiene el cierre del formulario y de la aplicacin. modo_descarga Un valor o una constante que indica la causa del evento QueryUnload, tal y como se describe en Valores que se pueden obtener. Valores que se pueden obtener El argumento modo_descarga devuelve los siguientes valores: Constante vbFormControlMenu formulario. vbFormCode VbAppWindows 2 vbAppTaskManager Valor 0 Descripcin El usuario eligi el comando Cerrar del men Control del

Se invoc la instruccin Unload desde el cdigo.

La sesin actual del entorno operativo Microsoft Windows est inalizando. 3 El Administrador de tareas de Microsoft Windows est cerrando la

aplicacin. vbFormMDIForm formulario MDI 4 Un formulario MDI secundario se est cerrando porque el

tambin se est cerrando. Comentarios Normalmente este evento se utiliza para asegurarse de que no hay tareas sin finalizar en los formularios incluidos en una aplicacin antes de que esa aplicacin se cierre. Por ejemplo, si un usuario no ha guardado todava algunos datos nuevos de cualquier formulario, su aplicacin puede pedir al usuario que los guarde. Cuando una aplicacin se cierra, puede utilizar los procedimientos de evento QueryUnload o Unload para establecer la propiedad Cancel a True, deteniendo el proceso de cierre. Sin embargo, el evento QueryUnload ocurre en todos los formularios antes de que se descargue ninguno de ellos y el evento Unload ocurre conforme se descarga cada formulario.

244

Ejemplo del evento QueryUnload En este ejemplo, al cerrar un formulario se consulta al operador si realmente quiere salir o no. Tambin se chequea si est saliendo del formulario o de toda la aplicacin. ' Pegar en la seccin Declaraciones de Form1. Private Sub Form_QueryUnload (Cancel As Integer, UnloadMode As Integer) Dim Msg ' Declara la variable.

If UnloadMode > 0 Then ' Si sale de la aplicacin. Msg = "Realmente desea salir de la aplicacin?" Else ' Si slo se cierra el formulario. Msg = "Realmente desea cerrar el formulario?" End If ' Si el usuario hace clic en el botn No, se detiene QueryUnload. If MsgBox(Msg, vbQuestion + vbYesNo, Me.Caption) = vbNo Then Cancel = True End Sub

13.11- Validate
Aplicable a: Control Data Se produce antes de que otro registro se convierta en el registro actual, antes del mtodo Update (excepto cuando se guardan los datos con el mtodo UpdateRecord) y antes de una operacin Delete, Unload o Close. Sintaxis Private Sub objeto_Validate ([ndice As Integer,] accin As Integer, guardar As Integer) La sintaxis del evento Validate consta de las siguientes partes: Parte Descripcin

245

objeto Una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a ndice Identifica el control si se encuentra en una matriz de controles accin Un entero que indica la operacin que provoca el evento, como se describe en Valores guardar Una expresin booleana que especifica si los datos enlazados han cambiado, como se describe en Valores Valores Los valores de accin son: Constante vbDataActionCancel vbDataActionMoveFirst vbDataActionMovePrevious vbDataActionMoveNext vbDataActionMoveLast vbDataActionAddNew vbDataActionUpdate vbDataActionDelete vbDataActionFind vbDataActionBookmark vbDataActionClose vbDataActionUnload Los valores de guardar son: Valor True False Descripcin Los datos enlazados han cambiado Los datos enlazados no han cambiado Valor 0 1 2 3 4 5 6 7 8 9 10 11 Descripcin Cancela la operacin al salir de Sub Mtodo MoveFirst Mtodo MovePrevious Mtodo MoveNext Mtodo MoveLast Mtodo AddNew Operacin Update (no UpdateRecord) Mtodo Delete Mtodo Find Se ha establecido la propiedad Bookmark El mtodo Close Se est descargando el formulario

Comentarios El argumento guardar indica inicialmente si los datos enlazados han cambiado. Este argumento puede ser False si los datos del bfer de copia han cambiado. Si guardar es True cuando este

246

evento termina, se invocan los mtodos Edit y UpdateRecord. El mtodo UpdateRecord slo guarda los datos de controles enlazados o del bfer de copia en los que la propiedad DataChanged sea True. Este evento se produce incluso aunque no se hayan modificado los datos de los controles enlazados y aunque no existan controles enlazados. Puede utilizar este evento para cambiar valores y actualizar datos. Tambin puede decidir guardar los datos o detener cualquier accin que est provocando el evento y sustituirla por otra accin diferente. Puede cambiar el argumento accin para cambiar una accin por otra. Puede cambiar los diversos mtodos Move y el mtodo AddNew, que se pueden intercambiar libremente (cualquier Move en AddNew, cualquier Move en cualquier otro Move o AddNew en cualquier Move). Cuando utilice AddNew, puede utilizar MoveNext y despus ejecutar otro AddNew para examinar la propiedad EditMode y determinar si hay una operacin Edit o AddNew en curso. El intento de sustituir AddNew o una accin Move en cualquier otra accin se pasa por alto o genera un error interceptable. Si se establece accin a 0, se puede detener cualquier accin. Dentro del cdigo de este evento puede comprobar los datos de cada control enlazado en el que DataChanged sea True. Despus puede establecer DataChanged a False para evitar guardar dichos datos en la base de datos. Durante este evento no puede utilizar ningn mtodo (como MoveNext) en el objeto Recordset subyacente. Ejemplo de la propiedad DataChanged y del evento Validate Este ejemplo ilustra una validacin de datos sencilla. En la tabla Authors de la base de datos Biblio.mdb hay dos campos: Au_ID y Author. Como el valor de Au_ID se utiliza para identificar de forma nica al autor, este valor no se debe cambiar. El ejemplo no permite que se modifique el campo Au_ID, que est enlazado a Text1. Private Sub Data1_Validate (Action As Integer, Save As Integer) If Text1.DataChanged Then han cambiado. ' Comprueba si los datos

MsgBox "No puede cambiar el nmero de Id." Text1.DataChanged = False los datos modificados. End If ... End Sub ' No guarda

14- Mtodos ms importantes de los controles estndares. 14.1- AddItem


Aplicable a:

247

ControlComboBox, ControlListBox Agrega un elemento a un control ListBox o ComboBox. Sintaxis objeto.AddItem elemento, ndice La sintaxis del mtodo AddItem consta de las siguientes partes: Parte objeto Elemento objeto. Descripcin Requerido. Una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a. Requerido. expresin de cadena que especifica el elemento que se va a agregar al

ndice Opcional. Entero que especifica la posicin dentro del objeto donde se insertan el elemento o la fila nuevos. Para el primer elemento de un control ListBox o ComboBox, ndice es 0. Comentarios Si se especifica un valor vlido para ndice, elemento se sita en dicha posicin dentro del objeto. Si se omite ndice, elemento se agrega en la posicin que le corresponda dentro del orden apropiado (si la propiedad Sorted es True) o al final de la lista (si Sorted es False). Los controles ListBox o ComboBox que estn enlazados a un control Data no aceptan el mtodo AddItem. Ejemplo del mtodo AddItem Este ejemplo utiliza el mtodo AddItem para agregar 100 elementos a un cuadro de lista. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario con un control ListBox llamado List1, y despus presione F5 y haga clic en el formulario. Private Sub Form_Click () Dim Entry, I, Msg ' Declara variables.

Msg = "Haga clic en Aceptar para agregar 100 elementos a su cuadro de lista." MsgBox Msg ' Muestra el mensaje. ' Cuenta de 1 a 100. ' Crea la entrada. ' Agrega la entrada.

For I = 1 To 100

Entry = "Entrada " & I List1.AddItem Entry Next I

248

Msg = "Haga clic en Aceptar para quitar una de cada dos entradas." MsgBox Msg For I = 1 To 50 ' Muestra el mensaje. ' Determina cmo

quitar

List1.RemoveItem I Next I

' cada elemento

Msg = "Haga clic en Aceptar para quitar todos los elementos del cuadro de lista." MsgBox Msg List1.Clear End Sub ' Muestra el mensaje. ' Limpia el cuadro de lista.

14.2- AddNew (Objeto Recordset)


Aplicable a: Objeto Recordset. Crea un nuevo registro para un objeto Recordset de tipo Table o Dynaset. Sintaxis recordset.AddNew El marcador de posicin del recordset es una variable de objeto que representa un objeto Recordset que se puede actualizar al que puede agregar un registro nuevo. Comentarios Utilice el mtodo AddNew para crear y agregar un nuevo registro en el objeto Recordset llamado por el recordset. Este mtodo establece los campos a los valores predeterminados y si no se especifican valores predeterminados, establece los campos a Null (los valores predeterminados especificados pare el Recordset tipo Table). Despus de modificar el nuevo registro, utilice el mtodo Update para guardar los cambios y agregar el registro al Recordset. No se producirn cambios en la base de datos hasta que no se utilice el mtodo Update. Precaucin Si ejecuta un AddNew y a continuacin realiza una operacin que desplace otro registro sin usar Update, los cambios se perdern sin previo aviso. Adems, si cierra el Recordset o finaliza el procedimiento que declara el Recordset o su objeto Database, el nuevo registro y los cambios realizados se descartarn sin previo aviso.

249

Si no se ha desplazado hasta el ltimo registro de su Recordset, los registros agregados a las tablas subyacentes pueden incluirse, si se colocan ms all del registro activo. Sin embargo, si agrega un registro a un Recordset, el registro ser visible en el Recordset y se incluir en la tabla subyacente donde estar visible para todos los nuevos objetos Recordset. La posicin del nuevo registro depende del tipo de Recordset: En un objeto Recordset tipo Dynaset, los registros se insertan al final del conjunto del Recordset, independientemente de las reglas de clasificacin u orden que estuvieran en vigor cuando se abri el Recordset. En un objeto Recordset tipo Table en el que su propiedad Index se haya establecido, los registros se insertan en el lugar adecuado dentro del orden definido. Si no se ha establecido la propiedad Index, los nuevos registros se insertarn al final del Recordset.

Ejemplo de mtodo AddNew En este ejemplo se agrega un regisro nuevo a la tabla Agenda de la base de datos Clientes, tomando los datos desde un formulario que contiene 3 cajas de texto (txtCodigo, txtNombre y txtDireccion) y un botn para agregar los datos (cmdAgregar) Private Sub cmdAgregar_Click () Dim wsp as WorkSpace Dim Base as Database Dim Agenda as Recordset Set wsp = DbEngine.Workspaces(0) trabajo Set Base = wsp.OpenDatabase (Clientes.mdb) Seteo el espacio de Dimensiono las variables

Abro la base de Datos Abro

Set Agenda = BasedeDatos.OpenRecordset(SELECT * FROM Agenda) el Recordset Agenda.AddNew Agenda!Codigo = txtCodigo.Text texto a los campos Agenda!Direccion = txtDireccion.Text Agenda.Nombre = txtNombre.Text Agenda.Update Agenda.Close Base.Close Wsp.Close Cierro el Recordset Cierro la base de Datos Cierro el espacio de trabajo

Agrego un registro en blanco Asigno los valores de las cajas de

250

End Sub

14.3- CancelUpdate (Objeto Recordset)


Aplicanble a: Objeto Recordset. Cancela todas las actualizaciones pendientes del objeto Recordset. Sintaxis recordset.CancelUpdate tipo La sintaxis del mtodo CancelUpdate consta de las siguientes partes. Parte Descripcin

recordset Una variable de objeto que representa el objeto Recordset en el que se cancelan las actualizaciones pendientes. Tipo Opcional. Una constante que indica el tipo de actualizacin, como se especifica en Valores (slo espacios de trabajo ODBCDirect). Comentarios El mtodo CancelUpdate cancela todas las actualizaciones pendientes a causa de una operacin Edit o AddNew. Por ejemplo, si un usuario llama al mtodo Edit o AddNew sin haber llamado anteriormente al mtodo Update, CancelUpdate cancelar todos los cambios efectuados despus de llamar a Edit o AddNew. Ejemplo del mtodo CancelUpdate Al mismo ejemplo del Evento AddNew, le agregamos la opcin de confirmar o volver para atrs la actualizacin al operador, segn la respuesta a una caja de mensajes. Private Sub cmdAgregar_Click () Dim wsp as WorkSpace Dim Base as Database Dim Agenda as Recordset Set wsp = DbEngine.Workspaces(0) trabajo Set Base = wsp.OpenDatabase (Clientes.mdb) Seteo el espacio de Dimensiono las variables

Abro la base de Datos Abro

Set Agenda = BasedeDatos.OpenRecordset(SELECT * FROM Agenda) el Recordset

251

Agenda.AddNew registro en blanco Agenda!Codigo = txtCodigo.Text texto a los campos Agenda!Direccion = txtDireccion.Text Agenda.Nombre = txtNombre.Text If

Agrego un

Asigno los valores de las cajas de

MsgBox ("Agrega el nuevo registro, vbYesNo) = vbYes then Agenda.Update Else Agenda.CancelUpdate

End If Agenda.Close Base.Close Wsp.Close End Sub Cierro el Recordset Cierro la base de Datos Cierro el espacio de trabajo

14.4- Clear (Clipboard, Combo Box, List Box)


Aplicable a: Objeto Control ComboBox, Control ListBoxBorra el contenido de los controles ListBox o ComboBox. Sintaxis objeto.Clear El marcador de posicin objeto representa una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a. Comentarios Los controles ListBox o ComboBox que estn enlazados a un control Data no aceptan el mtodo Clear. Ejemplo del mtodo Clear

252

Este ejemplo utiliza el mtodo Clear para borrar todos los elementos de un cuadro de lista. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario con un control ListBox llamado List1, y despus presione F5 y haga clic en el formulario. Private Sub Form_Click () Dim Entry, I, Msg ' Declara variables.

Msg = "Haga clic en Aceptar para agregar 100 elementos a su cuadro de lista." MsgBox Msg For I = 1 To 100 ' Muestra el mensaje. ' Cuenta de 1 a 100. ' Crea la entrada. ' Agrega la entrada.

Entry = "Entrada " & I List1.AddItem Entry Next I

entrada."

Msg = "Haga clic en Aceptar para quitar cualquier otra

MsgBox Msg For I = 1 To 50 cmo quitar

' Muestra el mensaje. ' Determina

List1.RemoveItem I dos Next I elementos.

' uno de cada

'

Msg = "Haga clic en Aceptar para quitar todos los elementos del cuadro de lista." MsgBox Msg List1.Clear End Sub ' Muestra el mensaje. ' Limpia el cuadro de lista.

14.5- Close (Objetos Database, Recordset o Workspace)


Aplicable a: Objeto Database, Objeto Recordset, Objeto Workspace. Cierra un objeto DAO (Data Access Object).

253

Sintaxis objeto.Close El marcador de posicin objeto es una variable de objeto que representa un objeto Database, Recordset o Workspace abierto. Comentarios Si el objeto Database, Recordset o Workspace llamado por objeto est cerrado cuando utiliza Close se produce un error en tiempo de ejecucin. Precaucin Si sale de un procedimiento que declara objetos Database o Recordset y la base de datos est cerrada, los cambios no guardados se perdern, todas las transacciones pendientes se anularn y se anularn todas las modificaciones pendientes de los datos. Si intenta cerrar un objeto Connection o Database mientras hay algn objeto Recordset abierto, estos objetos Recordset se cerrarn y las actualizaciones o modificaciones pendientes quedarn anuladas. Si intenta cerrar un objeto Workspace mientras hay algn objeto Database abierto, los objetos Database se cerrarn, el cual cerrar sus objetos Recordset. La utilizacin del mtodo Close en un objeto Recordset original o duplicado no afecta al otro objeto Recordset. Una alternativa al mtodo Close es establecer el valor de una variable de objeto a Nothing (Set dbsTemp = Nothing). Ejemplo de mtodo Close Son vlidos los ejemplos de los mtodos AddNew y CancelUpdate. (donde se cierran objetos Workspace, Database y Recordset),

14.6- Cls
Aplicable a: Objeto Form, Control PictureBox. Borra los grficos y el texto generados en tiempo de ejecucin de los controles Form o PictureBox. Sintaxis

objeto.Cls
El marcador de posicin objeto representa una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a. Si se omite objeto, se supone que el objeto es el control Form que tenga el enfoque. Comentarios

254

Cls borra el texto y los grficos generados en tiempo de ejecucin por instrucciones grficas y de impresin. Los mapas de bits de fondo definidos mediante la propiedad Picture y los controles colocados en un Form en tiempo de diseo no se ven afectados por Cls. Despus de llamar a Cls, las propiedades CurrentX y CurrentY del objeto se restablecen a 0. Ejemplo del mtodo Cls Este ejemplo utiliza el mtodo Cls para eliminar la informacin impresa de un formulario. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario, y despus presione F5 y haga clic en el formulario. Private Sub Form_Click () Dim Msg ForeColor = QBColor(15) primer plano a blanco. BackColor = QBColor(1) fondo a azul. Msg = "Esta informacin se imprime en el fondo del formulario." Print Msg formulario. Msg = "Haga clic en Aceptar para borrar la informacin y el patrn de fondo " Msg = Msg & "mostrado en el formulario." MsgBox Msg Cls End Sub ' Muestra el mensaje. ' Borra el fondo del formulario. ' Imprime el mensaje en el ' Declara variable. ' Establece el color de

' Establece el color de

14.7- CompactDatabase (Objeto DBEngine)


Aplicable a: Objeto DBEngine Copia, compacta y ofrece la posibilidad de cambiar la versin, la secuencia de ordenacin y la codificacin. (slo espacio de trabajo Microsoft Jet). Sintaxis DBEngine.CompactDatabase antiguabasededatos, nuevabasededatos, escenario, opciones, contrasea

255

La sintaxis del mtodo CompactDatabase tiene los siguientes argumentos: -Antiguabasededatos: Una String que identifica una base de datos existente y cerrada. Puede ser una ruta completa y un nombre de archivo, como "C:\db1.mdb". Si el nombre de archivo tiene una extensin, deber especificarla. Si su red lo admite, tambin puede especificar una ruta de red, como "\\server1\share1\dir1\db1.mdb". -nuevabasededatos: Un tipo de datos String que es la ruta completa de la base de datos compactada que va a crear. Tambin puede especificar una ruta de acceso de red al igual que con antiguabasededatos. No puede usar el argumento nuevabasededatos para especificar el mismo archivo de base de datos que antiguabasededatos. -Escenario: Opcional. Un tipo de datos Variant que es una expresin de cadena que se utiliza para especificar la secuencia de ordenacin para crear nuevabasededatos, como se especifica en Opciones. Si omite este argumento, el escenario de la nuevabasededatos ser el mismo que el de la antiguabasededatos. Tambin puede crear una contrasea para nuevabasededatos concatenando la cadena de la contrasea (que comienza con ";pwd=") con una constante del argumento escenario, como este: dbLangSpanish & ";pwd=NuevaContrasea" Si desea utilizar el mismo escenario como antiguabasededatos (el valor predeterminado), pero especificar una contrasea nueva, simplemente escriba una contrasea en escenario: ";pwd=NuevaContrasea" opciones Opcional. Un valor entero que indica una o ms opciones, segn se especifica en Opciones. Puede combinar opciones sumando las correspondientes constantes. -Contrasea: Opcional. Un tipo de datos Variant que es una expresin de cadena que contiene una contrasea, si la base de datos est protegida con contrasea. La cadena ";pwd=" debe preceder a la propia contrasea. Si incluye una valor de contrasea en escenario, este valor se ignora. Puede utilizar una de las siguientes constantes en el argumento opciones para especificar si desea o no codificar la base de datos mientras se compacta. Constante dbEncrypt DbDecrypt Descripcin Codifica la base de datos durante la compactacin. Descodifica la base de datos durante la compactacin.

Si omite una constante de codificacin o si incluye a la vez dbDecrypt y dbEncrypt, nuevabasededatos tendr la misma codificacin que antiguabasededatos. Puede usar una de las siguientes constantes en el argumento opciones para especificar la versin del formato de los datos para la base de datos compactada. Esta constante afecta slo a la versin del formato de datos de nuevabasededatos y no afecta a la versin de ninguno de los objetos definidos por Microsoft Access, como formularios e informes. Constante Descripcin

256

dbVersion10 versin 1.0

Crea una base de datos que utiliza el motor de base de datos Microsoft Jet

durante la compactacin. DbVersion11 versin 1.1 Crea una base de datos que utiliza el motor de base de datos Microsoft Jet

durante la compactacin. DbVersion20 versin 2.0 Crea una base de datos que utiliza el motor de base de datos Microsoft Jet

durante la compactacin. DbVersion30 Crea una base de datos que utiliza el motor de base de datos Microsoft Jet versin 3.0 (compatible con la versin 3.5) durante la compactacin. Slo puede especificar una constante de versin. Si omite una constante de versin, nuevabasededatos tendr la misma versin que antiguabasededatos. Slo puede compactar nuevabasededatos a una versin igual o posterior a la de antiguabasededatos. Comentarios Al cambiar datos de una base de datos, el archivo de base de datos puede fragmentarse y utilizar ms espacio en disco del necesario. Regularmente, puede usar el mtodo CompactDatabase en la base de datos para desfragmentar el archivo de base de datos. La base de datos compactada suele ser ms pequea y ejecutarse con ms rapidez. Tambin puede cambiar la secuencia de ordenacin, la codificacin o la versin del formato de datos, mientras copia y compacta la base de datos. Tiene que cerrar antiguabasededatos antes de compactarla. En un entorno multiusuario, los dems usuarios no pueden tener abierta antiguabasededatos mientras usted la compacta. Si antiguabasededatos no est cerrada o no se encuentra disponible para su uso exclusivo, se producir un error. Puesto que CompactDatabase crea una copia de la base de datos, deber disponer de espacio suficiente en disco para la base de datos original y la duplicada. La operacin de compactacin fracasar si no hay suficiente espacio disponible en disco. La base de datos duplicada nuevabasededatos no tiene por qu estar en el mismo disco que antiguabasededatos. Despus de compactar una base de datos, puede eliminar el archivo antiguabasededatos y cambiar el nombre del archivo compactado nuevabasededatos por el nombre del archivo original. El mtodo CompactDatabase copia todos los datos y valores de permisos de seguridad de la base de datos especificada en antiguabasededatos a la base de datos especificada en nuevabasededatos. Si utiliza el mtodo CompactDatabase para convertir una base de datos versin 1.x a una versin 2.5 o 3.x, solamente las aplicaciones que utilizan las versiones de Microsoft Jet 2.5 o 3.x pueden abrir la base de datos convertida. Precaucin Debido a que el mtodo CompactDatabase no convertir objetos Microsoft Access, no es recomendable utilizar CompactDatabase para convertir una base de datos que contenga dichos

257

objetos. Para convertir una base de datos que contenga objetos Microsoft Access, en el men Herramientas, elija Utilidades de la base de datos y despus haga clic en Convertir base de datos. Ejemplo de mtodo CompactDatabase Este ejemplo utiliza el mtodo CompactDatabase compactar la base de Datos Clientes.mdb. Sub CompactDatabaseX() DBEngine.CompactDatabase "Clientes.mdb", Compact.mdb" End Sub

14.8- Delete (Objeto Recordset)


Aplicable a: Objeto Recordset. Objetos Recordset: elimina el registro activo de un objeto Recordset de tipo Dynaset o Table. Para espacios de trabajo ODBCDirect, el tipo de controlador determina si los objetos Recordset se pueden actualizar y, por tanto, admiten el mtodo Delete.

Sintaxis recordset.Delete La sintaxis del mtodo Delete utiliza los siguientes argumentos. Argumentos Descripcin

recordset Una variable de objeto que identifica un objeto Recordset de tipo Dynaset o Table abierto, que contiene el registro que desea eliminar. Comentarios Puede utilizar el mtodo Delete para eliminar un registro activo de un objeto Recordset. Recordsets Un objeto Recordset debe contener un registro activo antes de que utilice el mtodo Delete; en caso contrario se produce un error en tiempo de ejecucin. En objetos Recordset, Delete elimina el registro activo y lo hace inaccesible. Aunque no pueda modificarlo o utilizarlo, el registro eliminado permanecer activo. Sin embargo, una vez que se desplace a otro registro no podr volver a convertir en activo el registro eliminado. Las referencias subsiguientes a un registro eliminado en un Recordset no son vlidas y producen un error. Si la tabla base es la tabla principal en una relacin de eliminacin de cascada, al eliminar el registro activo tambin se eliminarn uno o ms registros de una tabla externa.

258

Nota Para agregar, modificar o eliminar un registro, debe tener un ndice nico en el registro en el origen de datos de base. Si no es as, se producir un error "Permiso denegado" en la llamada al mtodo AddNew, Delete o Edit en un espacio de trabajo Microsoft Jet. Ejemplo de mtodo Delete Se necesitan borrar todos los registros de la tabla Agenda (dentro de la base de datos Clientes) cuyos cdigos de Clientes sean menores a 10. Private Sub cmdAgregar_Click () Dim wsp as WorkSpace Dim Base as Database Dim Agenda as Recordset Set wsp = DbEngine.Workspaces(0) trabajo Set Base = wsp.OpenDatabase (Clientes.mdb) Seteo el espacio de Dimensiono las variables

Abro la base de Datos

Set Agenda = BasedeDatos.OpenRecordset(SELECT * FROM Agenda WHERE Agenda.Codigo < 10) Abro el Recordset If Not Agenda.EOF Agenda.MoveFirst Do While Not Agenda.EOF Agenda.Delete Agenda.MoveNext registro Loop End If Agenda.Close Base.Close Wsp.Close End Sub Cierro el Recordset Cierro la base de Datos Cierro el espacio de trabajo si encontr algn registro me muevo al primer registro mientras no sea fin de archivo elimino el registro me desplazo al siguiente

14.9- Edit (Objeto Recordset)


Aplicable a :

259

Objeto Recordset. Copia el registro activo de un objeto Recordset al bfer de copia para su posterior edicin. Sintaxis recordset.Edit El recordset representa el nombre de un objeto Recordset abierto y que se puede actualizar que contiene el registro que desea modificar. Comentarios Una vez que utiliza el mtodo Edit, los cambios realizados en los campos del registro activo son copiados al bfer de copia. Despus de realizar los cambios deseados en el registro, utilice el mtodo Update para guardar los cambios. El registro activo permanece activo despus de utilizar el mtodo Edit. Precaucin Si modifica un registro y a continuacin pasa a otro registro sin utilizar antes Update, los cambios se perdern sin previo aviso. Adems, si cierra recordset o finaliza el procedimiento que declara el Recordset o el objeto Database o Connection, el registro modificado se descarta sin previo aviso. La utilizacin de Edit produce un error bajo las siguientes condiciones: No hay ningn registro activo. El objeto Database o Recordset se abri de slo lectura. No hay campos que se pueden actualizar en el registro. El objeto Database o Recordset se abri para uso en modo exclusivo por otro usuario (espacio de trabajo Microsoft Jet).

Nota Para agregar, modificar o eliminar un registro, debe tener un ndice nico en el registro en el origen de datos de base. Si no es as, se producir un error "Permiso denegado" en la llamada al mtodo AddNew, Delete o Edit en un espacio de trabajo Microsoft Jet, o se producir un error "Argumento no vlido" el la llamada al mtodo Update en un espacio de trabajo ODBCDirect. Ejemplo de mtodo Edit Se necesita asignar la zona 1 a todos los registros de la tabla Agenda (dentro de la base de datos Clientes) cuyos cdigos de Clientes sean menores a 20. Private Sub cmdAgregar_Click () Dim wsp as WorkSpace Dim Base as Database Dim Agenda as Recordset Set wsp = DbEngine.Workspaces(0) trabajo Seteo el espacio de Dimensiono las variables

260

Set Base = wsp.OpenDatabase (Clientes.mdb)

Abro la base de Datos

Set Agenda = BasedeDatos.OpenRecordset(SELECT * FROM Agenda WHERE Agenda.Codigo < 20) Abro el Recordset If Not Agenda.EOF Agenda.MoveFirst Do While Not Agenda.EOF Agenda.Edit Agenda!Zona = 1 Agenda.Update Agenda.MoveNext registro Loop End If Agenda.Close Base.Close Wsp.Close End Sub Cierro el Recordset Cierro la base de Datos Cierro el espacio de trabajo me desplazo al siguiente si encontr algn registro me muevo al primer registro mientras no sea fin de archivo Edito el registro

14.10- Hide
Aplicable a: Objeto Form. Oculta un objeto Form pero no lo descarga. Sintaxis objeto.Hide El marcador de posicin objeto representa una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a. Si se omite objeto, se supone que objeto es el formulario que tenga el enfoque. Comentarios Cuando se oculta un formulario, se quita de la pantalla y su propiedad Visible queda establecida a False. Los controles de un formulario oculto no son accesibles para el usuario, pero estn

261

disponibles para la aplicacin de Visual Basic en ejecucin y para otros procesos que se estn comunicando con la aplicacin mediante DDE, as como para los eventos del control Timer. Cuando se oculta un formulario, el usuario no puede interactuar con la aplicacin hasta que el cdigo del procedimiento de evento que ha provocado la ocultacin del formulario haya terminado de ejecutarse. Si el formulario no est cargado cuando se llama al mtodo Hide, el mtodo Hide carga el formulario pero no lo presenta. Ejemplo del mtodo Hide Este ejemplo utiliza el mtodo Hide para ocultar un formulario. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario, y despus presione F5 y haga clic en el formulario. Private Sub Form_Click () Dim Msg Hide ' Declara variable. ' Oculta el formulario.

formulario."

Msg = "Haga clic en Aceptar para que vuelva a aparecer el

MsgBox Msg Show End Sub

' Muestra el mensaje.

' Muestra de nuevo el formulario.

14.11-MoveFirst - MoveLast - MoveNext MovePrevious (Objeto Recordset)


Aplicable a: Objeto Recordset. Mueven al registro primero, ltimo, siguiente o anterior de un objeto Recordset y lo convierten en el registro activo. Sintaxis recordset.{MoveFirst | MoveLast [dbRunAsync] | MoveNext | MovePrevious} El marcador de posicin recordset es una variable de objeto que representa un objeto Recordset abierto. Comentarios Precaucin Si modifica el registro activo, utilice el mtodo Update para guardar los cambios antes de ir a otro registro. Si va a otro registro sin actualizar, los cambios se perdern sin previo aviso.

262

Al abrir un Recordset, el primer registro est activo y la propiedad BOF es False. Si el Recordset no contiene registros la propiedad BOF es True y no habr ningn registro activo. Si el primer o el ltimo registro est activo cuando utiliza MoveFirst o MoveLast, el registro activo no cambia. Si utiliza MovePrevious cuando el primer registro est activo, la propiedad BOF es True y no habr ningn registro activo. Si vuelve a utilizar MovePrevious se producir un error y BOF ser True. Si utiliza MoveNext cuando el ltimo registro est activo, la propiedad EOF es True y no habr ningn registro activo. Si vuelve a utilizar MoveNext se producir un error y EOF ser True. Si recordset hace referencia a un objeto Recordset de tipo Table (slo espacios de trabajo Microsoft Jet), el movimiento seguir el ndice activo. Puede establecer el ndice activo utilizando la propiedad Index. Si no establece el ndice activo, el orden de los registros devueltos no estar definido. Importante Puede utilizar el mtodo MoveLast para llenar completamente un objeto Recordset de tipo Dynaset o Snapshot para obtener el nmero de registros activos en el Recordset. Sin embargo, si utiliza MoveLast puede hacer ms lentas sus aplicaciones. Slo debe utilizar MoveLast para obtener un total de registros si es absolutamente necesario obtener un total exacto en un Recordset abierto recientemente. No puede utilizar los mtodos MoveFirst, MoveLast y MovePrevious en un Recordset de tipo Forward-only. Para mover la posicin del registro activo en un objeto Recordset un nmero especfico de registros hacia adelante o hacia atrs, utilice el mtodo Move. Ejemplo de mtodos MoveFirst, MoveLast, MoveNext, MovePrevious Para un ejemplo de uso de los mtodos MoveFirst y MoveNext, mire el ejemplo del mtodo Edit. Se necesita asignar la zona 1 a todos los registros de la tabla Agenda (dentro de la base de datos Clientes) cuyos cdigos de Clientes sean menores a 20. Es necesario recorrer la tabla de atrs hacia adelante. Private Sub cmdAgregar_Click () Dim wsp as WorkSpace Dim Base as Database Dim Agenda as Recordset Set wsp = DbEngine.Workspaces(0) trabajo Set Base = wsp.OpenDatabase (Clientes.mdb) Seteo el espacio de Dimensiono las variables

Abro la base de Datos

Set Agenda = BasedeDatos.OpenRecordset(SELECT * FROM Agenda WHERE Agenda.Codigo < 20) Abro el Recordset

263

If Not Agenda.EOF Agenda.MoveLast Do While Not Agenda.BOF archivo Agenda.Edit Agenda!Zona = 1 Agenda.Update Agenda.MovePrevious anterior Loop End If Agenda.Close Base.Close Wsp.Close End Sub

si encontr algn registro me muevo al ltimo registro mientras no sea principio de

Edito el registro

me desplazo al registro

Cierro el Recordset Cierro la base de Datos Cierro el espacio de trabajo

14.12- OpenDatabase (Objeto Workspace)


Aplicable a: Objeto DBEngine, Objeto Workspace. Abre una base de datos especificada en un objeto Workspace y devuelve una referencia al objeto Database que la representa. Sintaxis Set basededatos = espaciodetrabajo.OpenDatabase (nombrebasededatos, opciones, slolectura, conexin) La sintaxis del mtodo OpenDatabase consta de las siguientes partes. Argumento basededatos Descripcin Una variable de objeto que representa el objeto Database que va a abrir.

264

espaciodetrabajo existente que

Opcional. Una variable de objeto que representa el objeto Workspace

contendr la base de datos. Si no incluye un valor para espaciodetrabajo, OpenDatabase utiliza el espacio de trabajo predeterminado. Nombrebasededatos Microsoft Jet Un tipo de datos String que es el nombre de un archivo de base de datos

existente o el nombre del origen de datos (DSN) de un origen de datos ODBC existente. opciones base de Opcional. Un tipo de datos Variant que establece varias opciones para la

datos, como se especifica en Valores. slolectura desea Opcional. Un valor de tipo de datos Variant (subtipo Boolean) que es True si

abrir la base de datos con acceso de slo lectura o False (predeterminado) si desea abrir la base de datos con acceso de lectura/escritura. conexin Opcional. Un tipo de datos Variant (subtipo String) que especifica informacin variada sobre la conexin, incluyendo las contraseas. Valores Para los espacios de trabajo Microsoft Jet, puede utilizar los siguientes valores para el argumento opciones: Valor True False Descripcin Abre la base de datos en modo exclusivo. (Predeterminado) Abre la base de datos en modo compartido.

Comentarios Cuando abre una base de datos, automticamente se agrega a la coleccin Databases. Estas son algunas consideraciones que debe aplicar cuando utilice nombrebasededatos:

265

Si hace referencia a una base de datos que ya est abierta para acceso en modo exclusivo por otro usuario, se produce un error. Si no hace referencia a una base de datos existente, se produce un error.

El argumento conexin se expresa en dos partes: el tipo de base de datos, seguido por punto y coma (;) y los argumentos opcionales. Primero debe proporcionar el tipo de base de datos, como "ODBC;" o "FoxPro 2.5;". A continuacin, os argumentos opcionales sin un orden concreto, separados por punto y coma. Uno de los parmetros puede ser la contrasea (si hay alguna asignada). Por ejemplo: "FoxPro 2.5; pwd=micontrasea" Para cerrar una base de datos y, de este modo, quitar el objeto Database de la coleccin Databases, utilice el mtodo Close en el objeto . Ejemplo mtodo OpenDatabase Es vlido el mismo ejemplo utilizado para los mtodos MoveFirst, MoveLast, MoveNext, MovePrevious.

14.13- OpenRecordset (Objeto Database)


Aplicable a: Objeto Database. Crea un nuevo objeto Recordset y lo aade a la coleccin Recordsets. Sintaxis Set variable = objeto.OpenRecordset (origen, tipo, opciones, bloquearmodificaciones) La sintaxis del mtodo OpenRecordset consta de las siguientes partes. Argumento variable que desea abrir. Objeto desea crear Descripcin Una variable de objeto que representa el objeto Recordset

Una variable de objeto que representa un objeto existente desde el que

el objeto Recordset nuevo. Origen nuevo Un tipo de datos String que especifica el origen de los registros para el

Recordset. El origen puede ser un nombre de tabla, un nombre de consulta o una instruccin SQL que devuelve registros.

266

Tipo como se

Opcional. Una constante que indica el tipo de objeto Recordset a abrir,

especifica en Valores. Opciones caractersticas del Opcional. Una combinacin de constantes que especifican las

objeto Recordset nuevo, como se especifica en Valores. Bloquearmodificaciones Recordset, Opcional. Una constante que determina el bloqueo para el objeto

como se especifica en Valores. Valores Puede utilizar una de las siguientes constantes para el argumento tipo. Constante dbOpenTable Microsoft Jet). dbOpenDynaset DbOpenSnapshot DbOpenForwardOnly Descripcin Abre un objeto Recordset de tipo Table (slo espacios de trabajo

Abre un objeto Recordset de tipo Dynaset (actualizable). Abre un objeto Recordset de tipo Snapshot (slo lectura) Abre un objeto Recordset de tipo Forward-only.

Nota Si abre un objeto Recordset en un espacio de trabajo Microsoft Jet y no especifica un tipo, el mtodo OpenRecordset crea una objeto Recordset de tipo Table, si es posible. Si especifica una tabla vinculada o una consulta, el mtodo OpenRecordset crea un objeto Recordset.de tipo Dynaset. Puede utilizar una combinacin de las siguientes constantes para el argumento opciones: -DbAppendOnly: Permite al usuario anexar registros nuevos al objeto Recordset, pero impide la modificacin o eliminacin de registros existentes (slo objetos Recordset de tipo Dynaset de Microsoft Jet). -DbSeeChanges: Genera un error en tiempo de ejecucin si otro usuario est cambiando los datos que usted est modificando.(Slo en objetos Recordset de tipo Snapshot de Microsoft Jet). Esto es til en aplicaciones donde varios usuarios tiene acceso de lectura/escritura simultneo a los mismos datos. -DbDenyWrite: Previene que otros usuarios puedan modificar o agregar registros (slo objetos Recordset de Microsoft Jet). -DbDenyRead: Previene que otros usuarios puedan leer datos de una tabla (slo objetos Recordset de tipo Table de Microsoft Jet).

267

-DbForwardOnly: Crea un objeto Recordset de tipo Forward-only (slo objetos Recordset de tipo Snapshot de Microsoft Jet). Se proporciona slo para compatibilidad con versiones anteriores y debe utilizar la constante dbOpenForwardOnly en el argumento tipo en vez de utilizar esta opcin. -DbReadOnly : Previene que otros usuarios puedan hacer cambios el objeto Recordset (slo Microsoft Jet). La constante dbReadOnly en el argumento bloquearmodificaciones reemplaza esta opcin, la cual se proporciona para compatibilidad con versiones anteriores. -dbInconsistent : Permite actualizaciones inconsistentes (slo objetos Recordset de tipo Dynaset de Microsoft Jet). -DbConsistent : Permite slo actualizaciones consistentes (slo objetos Recordset de tipo Dynaset de Microsoft Jet). Nota Las constantes dbConsistent y dbInconsistent se excluyen mutuamente y el uso de ambos produce un error. Proporcionar un argumento bloquearmodificaciones cuando el argumento opciones utiliza la constante dbReadOnly tambin produce un error. Puede utilizar las siguientes constantes para el argumento bloquearmodificaciones:. -dbReadOnly: Previene que los usuarios hagan cambios al Recordset. Puede utilizar dbReadOnly en el argumento opciones o en el argumento bloquearmodificaciones, pero nunca en ambos. Si lo utiliza en ambos argumentos, se produce un error en tiempo de ejecucin. -DbPessimistic: Utiliza el bloqueo pesimista para determinar cmo se pueden hacer cambios al objeto Recordset en un entorno multiusuario. La pgina que contiene el registro que est modificando est bloqueada mientras utiliza el mtodo Edit (predeterminado en espacios de trabajo Microsoft Jet). -DbOptimistic: Utiliza el bloqueo optimista para determinar cmo se pueden hacer cambios al objeto Recordset en un entorno multiusuario. La pgina que contiene el registro que est modificando est bloqueada mientras se ejecuta el mtodo Update. Comentarios En un espacio de trabajo Microsoft Jet, si objeto hace referencia a un objeto QueryDef o Recordset de tipo Dynaset o Snapshot o si origen hace referencia a una instruccin SQL o un TableDef que representa una tabla adjunta, no podr utilizar dbOpenTable para el argumento tipo y si lo hace, se producir un error interceptable. Si objeto hace referencia a un Recordset de tipo Dynaset o Snapshot, el Recordset nuevo es del mismo tipo objeto. Si objeto hace referencia a un objeto Recordset de tipo Table, el tipo del objeto nuevo es un objeto Recordset de tipo Dynaset. No puede abrir objetos Recordset nuevos desde objetos Recordset de tipo Forward-only. Utilice la constante dbSeeChanges en espacio de trabajo Microsoft Jet si desea captar los cambios realizados mientras dos o ms usuarios estn modificando o eliminando el mismo registro. Por ejemplo, si dos usuarios empiezan a modificar el mismo registro, el primer usuario que ejecute el mtodo Update consigue realizar la operacin. Cuando el segundo usuario ejecute el mtodo Update ocurre un error de tiempo de ejecucin. Del mismo modo, si el segundo usuario intenta utilizar el mtodo Delete para eliminar un registro y el primer usuario ha cambiado ya el mismo, se produce un error de tiempo de ejecucin.

268

En general, si al usuario se le presenta este error mientras est actualizando, su cdigo debe actualizar el contenido de los campos y leer los valores recientemente modificados. Si se produce el error durante el proceso de eliminacin, el cdigo puede mostrar al usuario los nuevos datos del registro junto con un mensaje que indica que se han modificado recientemente los datos. En este momento, el cdigo puede solicitar una confirmacin de que el usuario desea an eliminar el registro. Al cerrar un Recordset utilizando el mtodo Close, se eliminar automticamente de la coleccin Recordsets. Ejemplo del mtodo OpenRecordset Es vlido el mismo ejemplo utilizado para los mtodos MoveFirst, MoveLast, MoveNext, MovePrevious.

14.14- RemoveItem
Aplicable a: Control ComboBox, Control ListBox. Quita un elemento de un control ListBox o ComboBox. No acepta argumentos con nombre. Sintaxis objeto.RemoveItem ndice La sintaxis del mtodo RemoveItem consta de las siguientes partes: Parte Descripcin

objeto Requerido. Una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a. ndice Requerido. Un entero que especifica la posicin dentro del objeto del elemento o la fila que se va a quitar. Para los primeros elementos de los controles ListBox o ComboBox, ndice es 0. Comentarios Los controles ListBox o ComboBox que estn enlazados a un control Data no aceptan el mtodo RemoveItem. Ejemplo del mtodo RemoveItem Este ejemplo utiliza el mtodo RemoveItem para quitar entradas de un cuadro de lista. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario que contenga un control ListBox llamado List1, y despus presione F5 y haga clic en el formulario. Private Sub Form_Click () Dim Entry, I, Msg ' Declara variables.

269

Msg = "Haga clic en Aceptar para agregar 100 elementos al cuadro de lista." MsgBox Msg For I = 1 To 100 ' Muestra el mensaje. ' Cuenta de 1 a 100. ' Crea la

Entry = "Entrada " & I entrada. List1.AddItem Entry entrada. Next I

' Agrega la

entradas."

Msg = "Haga clic en Aceptar para quitar una de cada dos

MsgBox Msg For I = 1 To 50

' Muestra el mensaje. ' Determina cmo quitar List1.RemoveItem I ' uno de

cada dos Next I

' elementos.

Msg = "Haga clic en Aceptar para quitar todos los elementos del cuadro de lista." MsgBox Msg List1.Clear End Sub ' Muestra el mensaje. ' Borra el cuadro de lista.

14.15- RepairDatabase (Objeto DBEngine)


Aplicable a: Objeto DBEngine. Intenta reparar una base de datos daada que accede a base de datos Microsoft Jet. Sintaxis DBEngine.RepairDatabase nombrebasededatos El argumento nombrebasededatos representa un tipo de datos String que es la ruta de acceso y el nombre de un archivo de base de datos del motor Microsoft Jet existente. Si se omite la ruta, slo se buscar en el directorio activo.

270

Comentarios Debe cerrar la base de datos especificada por nombrebasededatos antes de repararla. En un entorno multiusuario, los dems usuarios no podrn tener abierto nombrebasededatos mientras usted la repara. Si no est cerrado nombrebasededatos o no est disponible para uso exclusivo, se producir un error. Este mtodo intenta reparar una base de datos marcada como posiblemente daada por una operacin de escritura incompleta. Esto puede ocurrir si una aplicacin que utiliza el motor de base de datos Microsoft Jet termina inesperadamente debido a un corte en el suministro elctrico o un problema de hardware. La base de datos no se marcar como posiblemente daada si utiliza el mtodo Close o si sale de la aplicacin de una manera normal. El mtodo RepairDatabase tambin intenta validar todas las tablas del sistema y todos los ndices. Se descartan los datos que no se puedan reparar. Si no se puede reparar la base de datos, se produce un error interceptable. Cuando intente abrir o compactar una base de datos daada, normalmente se producir un error interceptable. En algunas situaciones, sin embargo, puede que no se detecte una base de datos daada y no se produzca ningn error. Es conveniente ofrecer a los usuarios un mtodo de ejecutar el mtodo RepairDatabase en su aplicacin, si la base de datos se comporta de manera impredecible. Algunos tipos de bases de datos se pueden daar si un usuario termina una aplicacin sin cerrar los objetos Database o Recordset y el motor de base de datos Microsoft Jet, Microsoft Windows no tienen la oportunidad de vaciar las memorias de cach de datos. Para evitar que se daen las bases de datos, establezca procedimientos para cerrar las aplicaciones y apagar los sistemas que aseguren que todas las pginas de la memoria cach estn guardadas en la base de datos. En algunos casos, puede que sean necesarias fuentes de alimentacin ininterrumpida para evitar prdidas de datos por las fluctuaciones del suministro elctrico. Nota Despus de reparar una base de datos, tambin es conveniente compactar la misma utilizando el mtodo CompactDatabase para defragmentar el archivo y recuperar espacio en disco. Ejemplo del mtodo RepairDatabase Este ejemplo intenta reparar la base de datos llamada Neptuno.mdb. Sub RepairDatabaseX() Dim errBucle As Error If MsgBox("Desea reparar la base de datos Neptuno?", vbYesNo) = vbYes Then DBEngine.RepairDatabase "Neptuno.mdb" MsgBox "Fin del procedimiento reparar!" End If

271

End Sub

14.16- SetFocus
Aplicable a Control CheckBox, Control ComboBox, Control CommandButton, Objeto Form Controles HScrollBar y VScrollBar, Control ListBoxControl OptionButton, Control PictureBox, Control TextBox. Mueve el enfoque al control o formulario especificado. Sintaxis objeto.SetFocus El marcador de posicin objeto representa una expresin de objeto que da como resultado un objeto de la lista Aplicable a. Comentarios El objeto debe ser un objeto Form o un control que pueda recibir el enfoque. Despus de invocar el mtodo SetFocus, cualquier entrada del usuario se dirige al formulario o al control especificado. El enfoque slo se puede mover a un formulario o un control visible. Como un formulario y los controles de un formulario no son visibles hasta que el evento Load del formulario ha terminado, no puede usar en su propio evento Load el mtodo SetFocus para mover el enfoque al formulario que se est cargando a menos que use primero el mtodo Show para mostrar el formulario antes de que el procedimiento de evento Form_Load haya terminado. Tampoco puede mover el enfoque a un formulario o un control si su propiedad Enabled es False. Si la propiedad Enabled se ha establecido a False en tiempo de diseo, primero debe establecerla a True antes de poder recibir el enfoque mediante el mtodo SetFocus. Ejemplo del mtodo SetFocus Al hacer click en un botn de comando, setea el foco a la caja de texto txtCodigo Private Sub cmd1_Click() TxtCodigo.Setfocus End Sub

14.17- Show
Aplicable a: Objeto Form. Sintaxis

272

objeto.Show estilo, formulario_propietario La sintaxis del mtodo Show consta de las siguientes partes: Parte Descripcin

objeto Opcional. Una expresin de objeto cuyo resultado es un objeto de la lista Aplicable a. Si se omite objeto, se supone que objeto es el formulario asociado con el mdulo de formulario activo. estilo Opcional. Un entero que determina si el formulario es modal o no modal. Si estilo es 0, el formulario es no modal; si estilo es 1, el formulario es modal. formulario_propietario Opcional. Una expresin de cadena que especifica el componente que "posee" el formulario que se muestra. Para los formularios estndar de Visual Basic, utilice la palabra clave Me. Comentarios Si el formulario especificado no est cargado cuando se invoca el mtodo Show, Visual Basic lo carga automticamente. Cuando Show presenta un formulario no modal, contina con la ejecucin del cdigo que haya a continuacin. Cuando Show presenta un formulario modal, el cdigo que hay a continuacin no se ejecuta hasta que el formulario se oculta o se descarga. Cuando Show presenta un formulario modal, no hay entradas (de teclado o del mouse) excepto sobre los objetos del formulario modal. El programa debe ocultar o descargar los formularios modales (normalmente como respuesta a alguna accin del usuario) antes de que pueda producirse la entrada en otro formulario. Aunque los dems formularios de la aplicacin estn deshabilitados cuando se presenta un formulario modal, los de las dems aplicaciones no lo estn. El formulario inicial de una aplicacin se muestra automticamente despus de invocar su evento Load. Ejemplo del mtodo Show Este ejemplo utiliza el mtodo Show para mostrar un formulario oculto. Para probar este ejemplo, pegue el cdigo en la seccin Declaraciones de un formulario y despus presione F5 y haga clic en el formulario. Private Sub Form_Click () Dim Msg Hide ' Declara variable. ' Oculta el formulario.

Msg = "Haga clic en Aceptar para que vuelva a aparecer el formulario."

273

MsgBox Msg Show End Sub

' Muestra el mensaje. ' Muestra de nuevo el formulario.

14.18- Update (Objeto recordset)


Aplicable a Objeto Recordset. Guarda el contenido del bfer de copia en un objeto Recordset de tipo Dynaset o Table especificado. Sintaxis recordset.Update (tipo, obligar ) La sintaxis del mtodo Update tiene las siguientes partes. Parte Descripcin Una variable de objeto que representa un objeto Recordset abierto que se puede

Recordset actualizar. Tipo Valores

Opcional. Una constante que indica el tipo de actualizacin, como se especifica en

(slo espacios de trabajo ODBCDirect). Obligar la base Opcional. Un valor de tipo Boolean que indica si se pueden o no obligar los cambios en

de datos, sin tener en cuenta si los datos base se han cambiado por otro usuario desde la llamada al mtodo AddNew, Delete o Edit. Si es True, los cambios se fuerzan y los cambios hechos por otros usuarios se sobrescriben. Si es False (predeterminado), los cambios hechos por otros usuarios mientras la actualizacin est pendiente provocarn que falle la actualizacin para aquellos cambios conflictivos.

Comentarios

274

Utilice Update para guardar el registro activo y los cambios que haya efectuado en l. Precaucin Los cambios realizados en el registro activo se perdern si: Utiliza el mtodo Edit o AddNew y a continuacin, pasa a otro registro sin actualizarlo previamente mediante Update. Utiliza Edit o AddNew y, a continuacin, vuelve a usar Edit o AddNew sin utilizar previamente Update. Cierra el conjunto de registros a los que hace referencia recordset sin utilizar primero Update. Cancela la operacin Edit utilizando el mtodo CancelUpdate.

Para modificar un registro, utilice el mtodo Edit para copiar el contenido del registro activo al bfer de copia. Si no utiliza Edit en primer lugar, se producir un error cuando utilice Update o intente cambiar el valor de un campo. En un espacio de trabajo Microsoft Jet, cuando el objeto Recordset de la propiedad LockEdits establecida como True (bloqueo pesimista) en un entorno multiusuario, el registro permanecer bloqueado desde el momento en que se utiliza Edit hasta que se ejecuta el mtodo Update o se cancele la edicin. Si la configuracin de la propiedad LockEdits es False (bloqueo optimista), el registro se bloquea y se compara con el registro previamente modificado justo antes de se actualizado en la base de datos. Si ha cambiado el registro desde que utiliz el mtodo Edit, la operacin Update falla.. Para que la operacin Update contine con los cambios, utilice de nuevo el mtodo Update. Para volver al registro, tal como lo cambi el otro usuario, actualice el registro activo usando los mtodos Move 0. Nota Para agregar, modificar o eliminar un registro, debe haber un ndice nico en el registro del origen de datos base. Se obtiene no lo hay, se producir un error "Permiso denegado" en la llamada al mtodo AddNew, Delete o Edit en un espacio de trabajo Microsoft Jet, se producir un error "Argumento no vlido" en la llamada al mtodo Update en un espacio de trabajo ODBCDirect. Ejemplo del mtodo Update Es vlido el ejemplo del mtodo AddNew.

275

Curso de C++
Entrada/Salida de datos
<iostream.h> archivo de cabecera Salida ------> cout << " ..........."; >> i; da a i un valor ledo

Entrada ----> cin desde el teclado

cout << "su nmero es " << i << "\n"; (Suponiendo que i=100, mostrar: su nmero es 100) Se pueden usar cualquiera de las funciones de E/S de C (scanf( ), printf( ), ...) pero se considera que estos siguen ms la filosofa de C++. Comentarios En C++ los comentarios se definen de dos formas. Los comentarios del tipo C funcionan de la misma manera tanto en C++ como en C. Sin embargo se pueden definir tambin comentarios de una sola lnea usando //. Cuando comienza un comentario usando //, se ignora lo que sigue hasta el final de la lnea. Miembros public y private Los miembros de una clase pueden ser datos o funciones, que pueden definirse como pblicos (accedidos desde cualquier parte del programa), protegidos o privados (slo pueden ser accedidos por las funciones propias de la clase donde se definen) mediante las palabras public, protected y private. Declaracin de clases Una clase puede ser definida de tres formas: 1) Mediante la palabra struct: por defecto todos los miembros son pblicos. struct { double CalcularArea ( ); void Leerdatos (double Lado1, double Lado2); private : double Lado1; cuadrado

276

double Lado2; }; 2) Mediante la palabra union: por defecto los miembros son pblicos y los datos comparten espacio de memoria. union { void MuestraNombre ( ); void MuestraApellido ( ); private : char Nombre_Completo [30]; char Nombre_y_Apellido [2] [15]; }; 3) Mediante la palabra class: los miembros son privados por defecto. Es la forma usual de declarar clases. class vehiculo { int Numero_Ruedas; int Numero_Ocupantes; public : void MostrarNumeroOcupantes ( ); } Para crear un objeto en C++ primero debe definirse su forma general usando la palabra reservada class. Una clase es sintcticamente similar a una estructura. Ejemplo: esta clase define un tipo llamado cola, que se usa para crear un objeto cola: // esto crea la clase cola class cola { int c[100]; Nombre_Persona

277

int public :

ppio, fin;

void ini ( ); void meter (int i); int sacar ( ); } Se puede crear un objeto de este tipo usando el nombre de la clase. Por ejemplo, creamos un objeto llamado intcola del tipo cola: cola intcola;

Tambin se pueden crear variables cuando se est definiendo una clase, poniendo los nombres de las variables despus de la llave de cierre, igual que en una estructura. Forma general de una declaracin de clase: class nombre_clase { datos y funciones privados; public : datos y funciones publicos; } lista de nombres de objetos; Las funciones deben ser declaradas en el interior de la estructura class, mientras que su cdigo se define normalmente fuera de ella. A la hora de definir el cdigo de una funcin miembro de una clase se le debe indicar al compilador a qu clase pertenece la funcin, esto se indica precediendo al nombre de la funcin el nombre de la clase y un par de signos de dos puntos "::". Es necesario ya que en C++ est permitido que clases distintas declaren funciones distintas pero con el mismo nombre. Ejemplo: void cola::meter (int i) { if (ppio=100) { cout << "la cola est llena"; return; }

278

ppio++; c[ppio]=i; } A :: se le denomina operador de resolucin de mbito. Para llamar a una funcin de una clase desde una parte del programa que no sea parte de la propia clase, se debe utilizar el nombre del objeto y el operador punto "." Ejemplo: cola a,b;

a.ini ( ); Constructores y destructores Es normal que una parte de un objeto necesite una inicializacin antes de poder usarse. Un constructor no es ms que una funcin miembro que aglutina un conjunto de instrucciones que permiten inicializar los objetos de una clase. El nombre de esa funcin debe coincidir con el nombre de la clase. Ejemplo: //esto crea la clase cola class cola { int c[100]; int ppio,fin; public : cola (void) //constructor

void meter (int i); int sacar (void); }; El constructor cola ( ) no tiene especificado tipo de dato devuelto. En C++ las funciones constructoras no pueden devolver valores. La funcin cola se codifica como: //Funcin constructora

279

cola::cola (void) { ppio=fin=0; cout << "cola inicializada\n"; } En muchos casos un objeto debe realizar alguna accin o acciones cuando se destruye. Hay muchas razones por las que se puede necesitar un destructor. Por ejemplo, un objeto puede tener que liberar memoria que previamente se le ha asignado o reservado. Un destructor recibe el mismo nombre que la clase pero precedido por el carcter "~". El siguiente ejemplo es de la clase cola y sus funciones constructora y destructora (tenga en cuenta que la clase cola no necesita un destructor). class cola { int c[100]; int ppio,fin; public : cola (void); ~cola (void); void meter (int i); int sacar (void); } //Funcin constructora cola::cola (void) { ppio=fin=0; cout <<"cola inicializada\n"; } //Funcin destructora //constructor //destructor

280

cola::~cola (void) { cout <<"cola destruida\n"; } Funciones inline Una funcin de lnea es una funcin que se expande en el punto donde se llama en vez de ser realmente llamada. Esto lo haremos cuando dicha funcin sea muy corta y se use mucho. Nos ahorraremos el tiempo que se pierde haciendo el pase de parmetros de una llamada a funcin. Hay dos formas de declarar una funcin como inline: 1) Precediendo la definicin de una funcin con la palabra clave inline. inline <declaracin de funcin> 2) Para las funciones miembro de una clase, se puede hacer definiendo el cdigo de la funcin dentro de la definicin de la clase. class { <declaracin_de_funcin> {<cdigo de la funcin>} } Funciones friend Es posible que una funcin que no es miembro de una clase tenga acceso a la parte privada de esa clase declarndola como friend (amiga) de la clase. El formato de la declaracin de funciones friend es el siguiente: class <nombre de la clase> { public : friend <declaracin de funcin> } Calificacin de variables miembro <nombre_de_clase>

281

A veces es necesario distinguir entre variables miembro de una clase y otro tipo de variables. Esto se puede realizar mediante el operador de resolucin de mbito "::". Ejemplo: class X { int m; public : void Setm (int); void Getm (void) {cout <<m;} }; void main( ) { X x; x.Setm (5); x.Getm ( ); } void X::Setm (int m) { X::m=m; de miembro m de la clase X } Variables de clase Entre los datos que pueden ser declarados en una clase se pueden hacer dos distinciones: - Las variables de instancia representan campos con denominacin comn para todos los objetos de la clase, pero con un contenido particular para cada uno de ellos. - Una variable de clase es un campo con idntico nombre e idntico contenido para todos los objetos de una clase. Es ms, la modificacin del contenido de ese campo en un objeto, afectar a todos los dems objetos. En realidad una variable de clase no es un campo que se halle en todos los objetos de una clase y que tenga el mismo contenido para todos ellos, sino que es un mismo espacio de memoria que es compartido por todos los objetos de una clase. //para distinguir el parmetro m,

282

La forma de declarar una variable de clase en C++ es declarando un campo miembro como static. class { .............................. static <tipo> <nombre_variable> ............................... } Vectores de objetos Se pueden crear arrays de objetos de la misma manera que se crean arrays de cualquier otro tipo de datos. Por ejemplo la siguiente lnea declara un vector de 10 objetos de una clase llamada X, que deber haber sido declarada con anterioridad. X vector[10]; En este ejemplo, en caso de existir un constructor para la clase X, ste se ejecutar 10 veces, una para cada objeto del vector. De igual manera, cuando se salga del mbito de utilizacin del vector de objetos declarado, en caso de existir una funcin destructora de la clase, sta se ejecutar para cada uno de los objetos del vector. Ejemplo para pasar parmetros al constructor: class X { int static int suma; X(int i) { n=i; suma+=i; } } X::suma=0; Punteros a objetos n; X v[3] = {1,2,3} X::suma=6 <nombre_clase>

283

De forma similar en C++ puede hacerse referencia a un objeto ya sea directamente, o bien usando un puntero a ese objeto. Para acceder a un elemento de un objeto usando el objeto real, se usa el operador punto (.). Para acceder a un elemento especfico de un objeto cuando se usa un puntero al objeto, se debe usar el operador flecha (->). Un puntero a un objeto se declara con la misma sintaxis que con cualquier otro tipo de dato. <Nombree_Clase> *<Nombre_Variable>;

A un puntero se le puede asignar un objeto de dos maneras: - asignndole la direccin de un objeto existente. - localizando espacio en memoria mediante el operador new. Ejemplo (primer caso): class P_ejemplo { int public: void est_num(int val) {num=val;} void mostrar_num(); }; void P_ejemplo :: mostrar_num() { cout << num << "\n"; } main(void) { P_ejemplo ob, *p; //declarar un objeto y num;

un puntero a l

directamente

ob.est_num(1);

//acceso a ob

ob.mostrar_num();

284

p=&ob; direccin de ob p->mostrar_num(); un puntero return 0; }

//asignar a p la

// acceder a ob usando

La asignacin de espacio de memoria mediante el operador new tiene la caracterstica de que al mismo tiempo que se asigna memoria, se ejecuta la funcin constructor de la clase. De similar manera, cuando se ejecuta el operador delete para un objeto, adems de liberar el espacio ocupado, se ejecuta la funcin destructora de la clase del puntero. Ejemplo: main(void) { int *p; p=new int; if (!p) { cout << "fallo en la asignacin"; return 1; } *p=20; cout << *p; delete p; return 0; La Herencia La herencia es el proceso por el cual un objeto puede adquirir las propiedades de otro objeto. En C++, la herencia se soporta permitiendo a una clase incorporar otra clase dentro de su declaracin. Las clases que heredan propiedades se llaman clase derivada, mientras que la clase de la que se heredan se denomina clase base. La forma de declarar una clase derivada es: //libera la memoria //asigna a esa memoria el valor 20 //asigna memoria para un entero

285

class <nombre_clase_derivada> : <acceso> <nombre_clase_base> <acceso> puede ser public o private: - public: los miembros public siguen siendo public, y los private siguen siendo private en la clase derivada. - private: los miembros public y private son todos private en la clase derivada. Ejemplo: class X { char nombre[80]; public : void mostrar(void); void nombrar(char *nom); }; class Y : public X { int edad; public: void mostrarY(void); void poneredad(int edad); }; Uso de la palabra protected Se puede conceder a la clase derivada acceso a los elementos private de una clase base hacindo a estos protected. Ejemplo: class X { protected: int i; int j; public: //clase derivada de X

286

void obt_ij(void) void poner_ij(void);

287

class y : public X { int k; public: int obj_k(void); void hacer_k(void); }; Da a Y acceso a i y j aunque permanezcan inaccesibles para el resto del programa. Resumiendo: Un miembro de una clase puede ser private, protected o public. - Si es private su nombre slo puede ser usado por funciones miembro y friend de la clase en la cual es declarado. - Si es protected, su nombre slo puede ser usado por funciones miembro y friend de la clase en la cual es declarado y por funciones miembro y friend de las clases derivadas de esta clase. - Si es public, su nombre puede ser usado por cualquier funcin. Herencia mltiple Es posible que una clase herede atributos de dos o ms clases. Para realizar esto, se usa una lista de herencia, separada por comas, en la lista de las clases base de la clase derivada. La forma general es: class nombre_clase_derivada : lista de clases base { .............. }; Por ejemplo en este programa Z hereda de X y de Y. class X { protected: int a: public: void hacer_a(int i);

288

}; class Y { protected: int b; public:

289

void hacer_b(int i); }; class Z : public X, public Y { public: int hacer_ab(void); }; Constructores y destructores en clases derivadas Es posible que una clase base y una clase derivada tengan cada una su funcin de construccin. Cuando una clase derivada contiene un constructor, se ejecuta el constructor base antes de ejecutar el constructor de la clase derivada. class Base { public: Base ( ) { cout <<"\n La clase Base ha sido creada";} }; class Derivada : public Base { public: Derivada ( ) { cout << " La clase Derivada ha sido creada"; } }; main ( ) { Derivada der; return 0; } La clase Base ha sido creada La clase Derivada ha sido creada

290

La funcin de destruccin de una clase derivada se ejecuta antes que la de la clase base. Esto es as ya que la destruccin de la clase base implica la destruccin de la clase derivada, el destructor de la clase derivada debe ser ejecutado antes de ser destruido. Cuando la herencia es mltiple los constructores de las clases base se invocan por rden, de izquierda a derecha y siempre antes que el de la clase derivada. Mientras que los destructores se invocan de derecha a izquierda, y siempre despus que el destructor de la clase derivada. Clase base virtual Cuando dos objetos o ms se derivan de una clase base comn, se puede impedir que estn presentes mltiples copias de la clase base en un objeto derivado de esos objetos, declarando la clase base como virtual cuando se hereda. #include <iostream.h> class base { public: int i; }; class d1 : virtual public base { // d1 hereda base como

virtual

public: int j; }; class d2 : virtual public base { virtual public: int k; }; class d3 : public d1, public d2 { pero solo hay una public: int m; }; main (void) // d3 hereda d1 y d2 // d2 hereda base como

// copia de base en d3

291

{ d3 d; d.i=10; d.j=20; d.k=30; d.m=40; } En d3 slo hay una copia de base y d.i=10 es perfectamente vlido y no es ambiguo. Polimorfismo El fin del polimorfismo es permitir el uso de un nombre para especificar un nombre de accin general. Se ejecuta una parte especfica de la clase general dependiendo del tipo de dato con el que est tratando. Sobrecarga de funciones Una de las maneras que tiene C++ de llegar al polimorfismo es a travs de la sobrecarga de funciones. En C++, dos o ms funciones pueden compartir un mismo nombre siempre y cuando difieran en la declaracin de sus parmetros. Las funciones que comparten el mismo nombre se dice que estn sobrecargadas. int Cuadrado (int i); long Cuadrado (long l); double Cuadrado (double x); void main ( ) { int i=1; long l=3; double x=0.45; cout << "El cuadrado de un entero es: << Cuadrado (i) <<"\n"; cout << "El cuadrado de un long es: << Cuadrado (l) << "\n"; cout << "El cuadrado de un double es: << Cuadrado (x) << "\n";

292

} int Cuadrado (int i) { return i*i; } long Cuadrado (long l) { return l*l; } double Cuadrado (double x) { return x*x; } La palabra reservada this Cada vez que se invoca una funcin miembro, se pasa automticamente un puntero al objeto que la invoca. Se puede acceder a este puntero usando this. El puntero this es un parmetro implcito para todas las funciones miembro. class c1 { int i; ...... ...... } Una funcin miembro puede asignarle a i el valor 10 usando esta sentencia: i=10; En realidad, esta sentencia es la foma corta de la sentencia Sobrecarga de operadores Para sobrecargar un operador se debe definir que operacin significa con relacin a la clase a la que se aplica. Para hacer esto, hay que crear una funcin operador que defina su accin. this->i=10;

293

Forma general: tipo nombreclase :: operator { } # - es el operador que se quiere sobrecargar. tipo - es el tipo de valor devuelto por la operacin especificada, es con frecuencia, del mismo tipo que la clase para la cual se ha sobrecargado el operador. El objeto causante de una llamada a un operador sobrecargado, siempre es el situado a la izquierda del operador. En una expresin a+b donde a y b son objetos, a es el causante de la llamada (pasa su valor a travs de this), mientras que el valor de b es recibido a travs del paso de parmetros. En general, cuando se usa una funcin miembro, no se necesitan parmetros al sobrecargar una operacin monaria y solo se necesita un parmetro cuando se sobrecarga un operador binario. Funciones operadoras amigas Es posible que una funcin operador sea amiga de una clase en vez de miembro, las funciones amigas no tienen el operador implcito this por tanto cuando se usa una funcin amiga para sobrecargar un operador, se pasan los dos operandos cuando se est sobrecargando un operador binario y un solo operando cuando el operador es unario. punto a; punto b: b=a+5; Esta operacin debe retornar un punto cuyas coordenadas sern las de a incrementadas en 5. punto punto :: operator + (int i) { punto temp; temp.x=p.x+i; temp.y=p.y+i; temp.z=p.z+i; return temp; # (lista de argumentos)

294

} Esta funcin resolvera expresiones como a+5 pero no funcionara si la expresin estuviese invertida, es decir, 5+a. Para resolver este problema se usa la sobrecarga de operadores mediante funciones friend, ya que podemos escpecificar el orden de todos los parmetros, porque no existen parmetros implcitos. El problema del doble orden de los operadores de una suma, solo puede ser resuelto escribiendo dos funciones friend. punto operator + (punto p, int i) { punto temp; temp.x=p.x+i; temp.y=p.y+i; temp.z=p.z+i; return temp; } punto operator + (int i, punto p) { punto temp; temp.x=p.x+i; temp.y=p.y+i; temp.z=p.z+i; return temp; } Punteros a clases derivadas En C++ el polimorfismo se admite tanto en el momento de la compilacin, como en el de la ejecucin. La sobrecarga de operadores y funciones es un ejemplo de polimorfismo en el momento de la compilacin. Para conseguir el polimorfismo en el momento de la ejecucin C++ permite usar clases derivadas y funciones virtuales. En C++ los punteros a clases base y a clases derivadas estn relacionados. Mediante un puntero de una clase base es posible acceder a una clase derivada, aunque no es posible a la inversa. Por ejemplo:

295

clase_B base

*p;

clase_B

tipo

clase_B B_ob; derivado de clase_B clase_D p=&B_ob; p=&D_ob; D_ob;

clase_D

// p apunta a un objeto del tipo clase_B // p apunta a un objeto del tipo clase_D

Usando p, se puede acceder a todos los elementos de D_ob heredados de B_ob. Sin embargo, los elementos especficos de la D_ob no pueden ser referenciados usando p, a menos que se utilice una refundicin de tipos. class X { protected: char nombre[30]; public: void mostrar (void); void nombrar (char *nom); }; class Y : public X { int edad; public: void mostrarY (void); void poneredad (int ed); }; main ( ) { X Y X x; y; *px;

296

*py; // Accedemos a los no

((Y *) px)-> poneredad (16) miembros de X ((Y *) px)-> mostrarY ( )

puntero

// refundiendo el tipo del

Funciones virtuales Una funcin virtual es una funcin que se declara en la clase base como virtual y se redefine en una o ms clases derivadas. Las funciones virtuales son especiales, porque cuando se accede a una usando un puntero de la clase base a un objeto de una clase derivada, C++ determinada en tiempo de ejecucin a que funcin llamar en funcin del tipo de objeto apuntado. Una funcin virtual se declara como virtual dentro de la clase base usando la palabra clave virtual. Sin embargo, cuando se redefine una funcin virtual en una clase derivada no se necesita repetir la palabar clave virtual (aunque no es un error hacerlo). class Base { public: virtual void quien ( ) { cout << "Base\n"; }; }; class primera_d : public Base { public: void quien ( ) { cout << "Primera derivacin\n"; }; }; class segunda_d : public Base { };

297

main ( ) { Base Base obj_base; * p; obj_primera; obj_segunda;

primera_d segunda_d

p=&obj_base; p->quien ( ); p=&obj_primera; p->quien ( ); p=&obj_segunda; p->quien ( ); } Funciones virtuales puras y tipos abstractos Una funcin virtual pura es una funcin que se declara en una clase base y que no tiene definicin relativa a la base, por lo que todos los tipos derivados se ven obligados a definir su propia versin de esa funcin. Para declarar una funcin virtual pura se utiliza la forma general: virtual tipo nombre_funcion (lista parametros) = 0; // Se accede al quien de base // Se accede al quien de primera // Se accede al quien de Base

Si una clase tiene por lo menos una funcin virtual pura, entonces se dice que esa clase es abstracta. Las clases abstractas tienen una caracterstica importante: no puede haber objetos de esa clase, porque una o ms de sus funciones carecen de definicin. Es posible declarar punteros de una clase abstracta. class figura { protected: double x,y; public: void pon_dim (double i, double j) { x=i ; y=j; };

298

virtual void mostrar_area ( ) = 0; }; class triangulo : public figura { public: void mostrar_area ( ) { cout << "Triangulo de altura" << x << " y base" << y; }; }; class cuadrado : public figura { public: void mostrar_area ( ) { cout << "Cuadrado de dimensin << x << "," << y; }; }; main ( ) { figura triangulo cuadrado p=&trian; p->pon_dim (10.0, 5.0); p->mostrar_area ( ); p=&cuad; p->pon_dim (10.0, 5.0); *p; trian; cuad;

299

p->mostrar_area ( ); }

300

Curso de Assembler
Sistema de Cmputo.
Le llamamos sistema de cmputo a la configuracin completa de una computadora, incluyendo las unidades perifricas y la programacin de sistemas que la hacen un aparato til y funcional para un fin determinado.

Procesador Central.
Esta parte es conocida tambin como unidad central de procesamiento o UCP. formada a su vez por la unidad de control y la unidad aritmtica y lgica. Sus funciones consisten en leer y escribir contenidos de las celdas de memoria, llevar y traer datos entre celdas de memoria y registros especiales y decodificar y ejecutar las instrucciones de un programa. El procesador cuenta con una serie de celdas de memoria que se utilizan con mucha frecuencia y que, por ende, forman parte de la UCP. Estas celdas son conocidas con el nombre de registros. Un procesador puede tener una docena o dos de estos registros. La unidad aritmtica y lgica de la UCP realiza las operaciones relacionadas con los clculos numricos y simblicos. Tpicamente estas unidades slo tienen capacidad de efectuar operaciones muy elementales como: suma y resta de dos nmeros de punto fijo, multiplicacin y divisin de punto fijo, manipulacin de bits de los registros y comparacin del contenido de dos registros. Las computadoras personales pueden clasificarse por lo que se conoce como tamao de palabra, esto es, la cantidad de bits que el procesador puede manejar a la vez.

Memoria Central.
Es un conjunto de celdas (actualmente fabricadas con semiconductores) usadas para procesos generales, tales como la ejecucin de programas y el almacenamiento de informacin para las operaciones. Cada una de las celdas puede contener un valor numrico y tienen la propiedad de ser direccionables, esto es, que se pueden distinguir una de otra por medio de un nmero nico o direccin para cada celda. El nombre genrico de estas memorias es Random Access Memory (Memoria de acceso aleatorio) o RAM por sus siglas en ingls. La principal desventaja de este tipo de memoria es que los circuitos integrados pierden la informacin que tienen almacenada cuando se interrumpe la alimentacin elctrica. Esto llev a la creacin de memorias cuya informacin no se pierda cuando se apaga el sistema. Estas memorias reciben el nombre de Read Only Memory (Memoria de solo lectura) o ROM.

Unidades de Entrada y Salida.


Para que una computadora nos sea til es necesario que el procesador se comunique al exterior por medio de interfaces que permiten la entrada y la salida de datos del procesador y la memoria. Haciendo uso de estas comunicaciones es posible introducir datos para su procesamiento y la posterior visualizacin de los datos ya procesados.

301

Algunas de las unidades de entrada mas comunes son teclados, lectoras de tarjetas (ya en desuso), mouse, etc. Las unidades de salida mas comunes son las terminales de video y las impresoras.

Unidades de Memoria Auxiliar.


Como la memoria central de una computadora es costosa y, considerando las aplicaciones actuales, muy limitada, surge entonces la necesidad de crear sistemas de almacenamiento de informacin prcticos y econmicos. Adems, la memoria central pierde su contenido al apagarse la mquina, por lo que no es conveniente utilizarla para almacenamiento permanente de datos. Estos y otros incovenientes dan lugar a la creacin de unidades perifricas de memoria que reciben el nombre de memoria auxiliar o secundaria. De estas unidades perifricas las ms comunes son las cintas y los discos magnticos. La informacin almacenada en estos medios magnticos recibe el nombre de archivo. Un archivo est formado por un nmero variable de registros, generalmente de tamao fijo; los registros pueden contener datos o programas.

Unidades de informacin
Para que la PC pueda procesar la informacin es necesario que sta se encuentre en celdas especiales llamadas registros. Los registros son conjuntos de 8 o 16 flip-flops (basculadores o biestables). Un flip-flop es un dispositivo capaz de almacenar dos niveles de voltaje, uno bajo, regularmente de 0.5 volts y otro alto comunmente de 5 volts. El nivel bajo de energa en el flip-flop se interpreta como apagado o 0, y el nivel alto como prendido o 1. A estos estados se les conoce usualmente como bits, que son la unidad mas pequea de informacin en una computadora. A un grupo de 16 bits se le conoce como palabra, una palabra puede ser dividida en grupos de 8 bits llamados bytes, y a los grupos de 4 bits les llamamos nibbles.

Sistemas numricos
El sistema numrico que utilizamos a diario es el sistema decimal, pero este sistema no es conveniente para las mquinas debido a que la informacin se maneja codificada en forma de bits prendidos o apagados; esta forma de codificacin nos lleva a la necesidad de conocer el clculo posicional que nos permita expresar un nmero en cualquier base que lo necesitemos. Es posible representar un nmero determinado en cualquier base mediante la siguiente formula: Donde n es la posicin del dgito empezando de derecha a izquierda y numerando a partir de cero. D es el dgito sobre el cual operamos y B es la base numrica empleada. Convertir nmeros binarios a decimales Trabajando en el lenguaje ensamblador nos encontramos con la necesidad de convertir nmeros del sistema binario, que es el empleado por las computadoras, al sistema decimal utilizado por las personas.

302

El sistema binario est basado en unicamente dos condiciones o estados, ya sea encendido (1) o apagado (0), por lo tanto su base es dos. Para la conversin podemos utilizar la formula de valor posicional: Por ejemplo, si tenemos el numero binario 10011, tomamos de derecha a izquierda cada dgito y lo multiplicamos por la base elevada a la nueva posicin que ocupan: Binario: 1 1 0 0 1 Decimal: 1*2^0 + 1*2^1 + 0*2^2 + 0*2^3 + 1*2^4 = 1 + 2 + 0 + 0 + 16 = 19 decimal. El caracter ^ es utilizado en computacin como smbolo de potenciacin y el caracter * se usa para representar la multiplicacin.

Convertir nmeros decimales a binarios


Existen varios mtodos de conversin de nmeros decimales a binarios; aqu solo se analizar uno. Naturalmente es mucho mas fcil una conversin con una calculadora cientfica, pero no siempre se cuenta con ella, as que es conveniente conocer por lo menos una forma manual para hacerlo. El mtodo que se explicar utiliza la divisin sucesiva entre dos, guardando el residuo como dgito binario y el resultado como la siguiente cantidad a dividir. Tomemos como ejemplo el nmero 43 decimal. 43/2 = 21 y su residuo es 1 21/2 = 10 y su residuo es 1 10/2 = 5 y su residuo es 0 5/2 = 2 y su residuo es 1 2/2 = 1 y su residuo es 0 1/2 = 0 y su residuo es 1 Armando el nmero de abajo hacia arriba tenemos que el resultado en binario es 101011

Sistema hexadecimal
En la base hexadecimal tenemos 16 dgitos que van del 0 al 9 y de la letra A hasta la F (estas letras representan los nmeros del 10 al 15). Por lo tanto, contamos 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E y F. La conversin entre numeracin binaria y hexadecimal es sencilla. Lo primero que se hace para una conversin de un nmero binario a hexadecimal es dividirlo en grupos de 4 bits, empezando de derecha a izquierda. En caso de que el ltimo grupo (el que quede mas a la izquierda) sea menor de 4 bits se rellenan los faltantes con ceros. Tomando como ejemplo el nmero binario 101011 lo dividimos en grupos de 4 bits y nos queda:

303

10; 1011 Rellenando con ceros el ltimo grupo (el de la izquierda): 0010; 1011 Despus tomamos cada grupo como un nmero independiente y consideramos su valor en decimal: 0010 = 2; 1011 = 11 Pero como no podemos representar este nmero hexadecimal como 211 porque sera un error, tenemos que sustituir todos los valores mayores a 9 por su respectiva representacin en hexadecimal, con lo que obtenemos: 2BH (Donde la H representa la base hexadecimal) Para convertir un nmero de hexadecimal a binario solo es necesario invertir estos pasos: se toma el primer dgito hexadecimal y se convierte a binario, y luego el segundo, y as sucesivamente hasta completar el nmero.

Cdigo ASCII
ASCII generalmente se pronuncia "aski", es un acrnimo de American Standard Code for Information Interchange. Este cdigo asigna a las letras del alfabeto, a los dgitos decimales del 0 al 9 y a varios smbolos adicionales un nmero binario de 7 bits (ponindose el bit 8 en su estado de apagado o 0). De esta forma cada letra, dgito o caracter especial ocupa un byte en la memoria de la computadora. Podemos observar que este mtodo de representacin de datos es muy ineficiente en el aspecto numrico, ya que en formato binario nos basta un solo byte para representar numeros de 0 a 255, en cambio con el cdigo ASCII un byte puede representar unicamente un dgito. Debido a esta ineficiencia, el cdigo ASCII es principalmente utilizado en la memoria para representar texto.

Metodo BCD
BCD es un acrnimo de Binary Coded Decimal. En esta notacin se utilizan grupos de 4 bits para representar cada dgito decimal del 0 al 9. Con este mtodo podemos representar dos dgitos por byte de informacin. An cuando este mtodo es mucho mas prctico para representacin de nmeros en la memoria en comparacin al ASCII, todava se queda por debajo del binario, ya que con un byte en el mtodo BCD solo podemos representar dgitos del 0 al 99, en cambio, en formato binario podemos representar todos los dgitos desde 0 hasta 255.

304

Este formato es utilizado principalmente para representar nmeros muy grandes en aplicaciones mercantiles ya que facilita las operaciones con los mismos evitando errores de redondeo.

Representacin de punto flotante


Esta representacin esta basada en la notacin cientfica, esto es, representar un nmero en dos partes: su mantisa y su exponente. Poniendo como ejemplo el nmero 1234000, podemos representarlo como 1.123*10^6, en esta ltima notacin el exponente nos indica el nmero de espacios que hay que mover el espacio hacia la derecha para obtener el resultado original. En caso de que el exponente fuera negativo nos estara indicando el nmero de espacios que hay que recorrer el punto decimal hacia la izquierda para obtener el original.

Proceso de creacin de un programa


Para la creacin de un programa es necesario seguir cinco pasos: Diseo del algoritmo, codificacin del mismo, su traduccin a lenguaje mquina, la prueba del programa y la depuracin. En la etapa de diseo se plantea el problema a resolver y se propone la mejor solucin, creando diagramas esquemticos utilizados para el mejor planteamiento de la solucin. La codificacin del programa consiste en escribir el programa en algn lenguaje de programacin; en este caso especfico en ensamblador, tomando como base la solucin propuesta en el paso anterior. La traduccin al lenguaje mquina es la creacin del programa objeto, esto es, el programa escrito como una secuencia de ceros y unos que pueda ser interpretado por el procesador. La prueba del programa consiste en verificar que el programa funcione sin errores, o sea, que haga lo que tiene que hacer. La ltima etapa es la eliminacin de las fallas detectadas en el programa durante la fase de prueba. La correccin de una falla normalmente requiere la repeticin de los pasos comenzando desde el primero o el segundo. Para crear un programa en ensamblador existen dos opciones, la primera es utilizar el MASM (Macro Assembler, de Microsoft), y la segunda es utilizar el debugger, en esta primera seccin utilizaremos este ltimo ya que se encuentra en cualquier PC con el sistema operativo MS-DOS, lo cual lo pone al alcance de cualquier usuario que tenga acceso a una mquina con estas caracteristicas. Debug solo puede crear archivos con extensin .COM, y por las caractersticas de este tipo de programas no pueden ser mayores de 64 kb, adems deben comenzar en el desplazamiento, offset, o direccin de memoria 0100H dentro del segmento especfico.

Registros de la UCP
La UCP tiene 14 registros internos, cada uno de 16 bits. Los primeros cuatro, AX, BX, CX, y DX son registros de uso general y tambien pueden ser utilizados como registros de 8 bits, para utilizarlos como tales es necesario referirse a ellos como por ejemplo: AH y AL, que son los bytes

305

alto (high) y bajo (low) del registro AX. Esta nomenclatura es aplicable tambin a los registros BX, CX y DX. Los registros son conocidos por sus nombres especficos: AX Acumulador BX Registro base CX Registro contador DX Registro de datos DS Registro del segmento de datos ES Registro del segmento extra SS Registro del segmento de pila CS Registro del segmento de cdigo BP Registro de apuntadores base SI Registro ndice fuente DI Registro ndice destino SP Registro del apuntador de la pila IP Registro de apuntador de siguiente instruccin F Registro de banderas Es posible visualizar los valores de los registros internos de la UCP utilizando el programa Debug. Para empezar a trabajar con Debug digite en el prompt de la computadora: C:\> Debug [Enter] En la siguiente linea aparecera un guin, ste es el indicador del Debug, en este momento se pueden introducir las instrucciones del Debug. Utilizando el comando: - r [Enter] Se desplegaran todos los contenidos de los registros internos de la UCP; una forma alternativa de mostrarlos es usar el comando "r" utilizando como parametro el nombre del registro cuyo valor se quiera visualizar. Por ejemplo: - rbx Esta instruccin desplegar unicamente el contenido del registro BX y cambia el indicador del Debug de " - " a " : "

306

Estando as el prompt es posible cambiar el valor del registro que se visualiz tecleando el nuevo valor y a continuacin [Enter], o se puede dejar el valor anterior presionando [Enter] sin telclear ningn valor. Es posible cambiar el valor del registro de banderas, as como utilizarlo como estructura de control en nuestros programas como se ver mas adelante. Cada bit del registro tiene un nombre y significado especial, la lista dada a continuacin describe el valor de cada bit, tanto apagado como prendido y su relacin con las operaciones del procesador: Overflow NV = no hay desbordamiento; OV = s lo hay Direction UP = hacia adelante; DN = hacia atras; Interrupts DI = desactivadas; EI = activadas Sign PL = positivo; NG = negativo Zero NZ = no es cero; ZR = s lo es Auxiliary Carry NA = no hay acarreo auxiliar; AC = hay acarreo auxiliar Parity PO = paridad non; PE = paridad par;

307

Carry NC = no hay acarreo; CY = S lo hay

La estructura del ensamblador


En el lenguaje ensamblador las lineas de cdigo constan de dos partes, la primera es el nombre de la instruccin que se va a ejecutar y la segunda son los parmetros del comando u operandos. Por ejemplo: add ah bh Aqu "add" es el comando a ejecutar (en este caso una adicin) y tanto "ah" como "bh" son los parmetros. El nombre de las instrucciones en este lenguaje esta formado por dos, tres o cuatro letras. a estas instrucciones tambien se les llama nombres mnemnicos o cdigos de operacin, ya que representan alguna funcin que habr de realizar el procesador. Existen algunos comandos que no requieren parametros para su operacin, as como otros que requieren solo un parmetro. Algunas veces se utilizarn las instrucciones como sigue: add al,[170] Los corchetes en el segundo parmetro nos indican que vamos a trabajar con el contenido de la casilla de memoria nmero 170 y no con el valor 170, a sto se le conoce como direccionamiento directo.

Nuestro primer programa


Vamos a crear un programa que sirva para ilustrar lo que hemos estado viendo, lo que haremos ser una suma de dos valores que introduciremos directamente en el programa: El primer paso es iniciar el Debug, este paso consiste unicamente en teclear debug [Enter] en el prompt del sistema operativo. Para ensamblar un programa en el Debug se utiliza el comando "a" (assemble); cuando se utiliza este comando se le puede dar como parametro la direccin donde se desea que se inicie el ensamblado, si se omite el parametro el ensamblado se iniciar en la localidad especificada por CS:IP, usualmente 0100H, que es la localidad donde deben iniciar los programas con extensin .COM, y sera la localidad que utilizaremos debido a que debug solo puede crear este tipo especfico de programas. Aunque en este momento no es necesario darle un parametro al comando "a" es recomendable hacerlo para evitar problemas una vez que se haga uso de los registros CS:IP, por lo tanto tecleamos: - a0100 [Enter]

308

Al hacer sto aparecer en la pantalla algo como: 0C1B:0100 y el cursor se posiciona a la derecha de estos nmeros, ntese que los primeros cuatro dgitos (en sistema hexagesimal) pueden ser diferentes, pero los ltimos cuatro deben ser 0100, ya que es la direccin que indicamos como inicio. Ahora podemos introducir las instrucciones: 0C1B:0100 mov ax,0002 ;coloca el valor 0002 en el registro ax 0C1B:0103 mov bx,0004 ;coloca el valor 0004 en el registro bx 0C1B:0106 add ax,bx ;le adiciona al contenido de ax el contenido de bx 0C1B:0108 int 20 ; provoca la terminacin del programa. 0C1B:010A No es necesario escribir los comentarios que van despues del ";". Una vez digitado el ltimo comando, int 20, se le da [Enter] sin escribir nada mas, para volver al prompt del debuger. La ltima linea escrita no es propiamente una instruccin de ensamblador, es una llamada a una interrupcin del sistema operativo, estas interrupciones sern tratadas mas a fondo en un captulo posterior, por el momento solo es necesario saber que nos ahorran un gran nmero de lineas y son muy tiles para accesar a funciones del sistema operativo. Para ejecutar el programa que escribimos se utliza el comando "g", al utilizarlo veremos que aparece un mensaje que dice: "Program terminated normally". Naturalmente con un mensaje como ste no podemos estar seguros que el programa haya hecho la suma, pero existe una forma sencilla de verificarlo, utilizando el comando "r" del Debug podemos ver los contenidos de todos los registros del procesador, simplemente teclee: - r [Enter] Aparecera en pantalla cada registro con su respectivo valor actual: AX=0006BX=0004CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000 DS=0C1BES=0C1BSS=0C1BCS=0C1BIP=010A NV UP EI PL NZ NA PO NC 0C1B:010A 0F DB oF Existe la posibilidad de que los registros contengan valores diferentes, pero AX y BX deben ser los mismos, ya que son los que acabamos de modificar. Otra forma de ver los valores, mientras se ejecuta el programa es utilizando como parmetro para "g" la direccin donde queremos que termine la ejecucin y muestre los valores de los registros, en este caso sera: g108, esta instruccin ejecuta el programa, se detiene en la direccin 108 y muestra los contenidos de los registros. Tambin se puede llevar un seguimiento de lo que pasa en los registros utilizando el comando "t" (trace), la funcin de este comando es ejecutar linea por linea lo que se ensambl mostrando cada vez los contenidos de los regitros. Para salir del Debug se utiliza el comando "q" (quit).

309

Guardar y cargar los programas


No sera prctico tener que digitar todo un programa cada vez que se necesite, para evitar eso es posible guardar un programa en el disco, con la enorme ventaja de que ya ensamblado no ser necesario correr de nuevo debug para ejecutarlo. Los pasos a seguir para guardar un programa ya almacenado en la memoria son: Obtener la longitud del programa restando la direccin final de la direccin inicial, naturalmente en sistema hexadecimal. Darle un nombre al programa y extensin Poner la longitud del programa en el registro CX Ordenar a Debug que escriba el programa en el disco. Utilizando como ejemplo el programa del captulo anterior tendremos una idea mas clara de como llevar estos pasos: Al terminar de ensamblar el programa se vera as: 0C1B:0100 mov ax,0002 0C1B:0103 mov bx,0004 0C1B:0106 add ax,bx 0C1B:0108 int 20 0C1B:010A - h 10a 100 020a 000a - n prueba.com - rcx CX 0000 :000a -w Writing 000A bytes Para obtener la longitud de un programa se utiliza el comando "h", el cual nos muestra la suma y resta de dos nmeros en hexadecimal. Para obtener la longitud del nuestro le proporcionamos como parmetros el valor de la direccin final de nuestro programa (10A) y el valor de la direccin

310

inicial (100). El primer resultado que nos muestra el comando es la suma de los parmetros y el segundo es la resta. El comando "n" nos permite poner un nombre al programa. El comando "rcx" nos permite cambiar el contenido del registro CX al valor que obtuvimos del tamao del archivo con "h", en este caso 000a, ya que nos interesa el resultado de la resta de la direccin inicial a la direccin final. Por ltimo el comando w escribe nuestro programa en el disco, indicandonos cuantos bytes escribi. Para cargar un archivo ya guardado son necesarios dos pasos: Proporcionar el nombre del archivo que se cargar. Cargarlo utilizando el comando "l" (load). Para obtener el resultado correcto de los siguientes pasos es necesario que previamente se haya creado el programa anterior. Dentro del Debug escribimos lo siguiente: - n prueba.com - l - u 100 109 0C3D:0100 B80200 MOV AX,0002 0C3D:0103 BB0400 MOV BX,0004 0C3D:0106 01D8 ADD AX,BX 0C3D:0108 CD20 INT 20 El ltimo comando, "u", se utiliza para verificar que el programa se carg en memoria, lo que hace es desensamblar el cdigo y mostrarlo ya desensamblado. Los parmetros le indican a Debug desde donde y hasta donde desensamblar. Debug siempre carga los programas en memoria en la direccin 100H, a menos que se le indique alguna otra.

Condiciones, ciclos y bifurcaciones


Estas estructuras, o formas de control le dan a la mquina un cierto grado de desicin basado en la informacin que recibe. La forma mas sencilla de comprender este tema es por medio de ejemplos.

311

Vamos a crear tres programas que hagan lo mismo: desplegar un nmero determinado de veces una cadena de caracteres en la pantalla. - a100 0C1B:0100 jmp 125 ; brinca a la direccin 125H 0C1B:0102 [Enter] - e 102 'Cadena a visualizar 15 veces' 0d 0a '$' - a125 0C1B:0125 MOV CX,000F ; veces que se desplegara la cadena 0C1B:0128 MOV DX,0102 ; copia cadena al registro DX 0C1B:012B MOV AH,09 ; copia valor 09 al registro AH 0C1B:012D INT 21 ; despliega cadena 0C1B:012F LOOP 012D ; si CX>0 brinca a 012D 0C1B:0131 INT 20 ; termina el programa. Por medio del comando "e" es posible introducir una cadena de caracteres en una determinada localidad de memoria, dada como parmetro, la cadena se introduce entre comillas, le sigue un espacio, luego el valor hexadecimal del retorno de carro, un espacio, el valor de linea nueva y por ltimo el smbolo '$' que el ensamblador interpreta como final de la cadena. La interrupcin 21 utiliza el valor almacenado en el registro AH para ejecutar una determinada funcin, en este caso mostrar la cadena en pantalla, la cadena que muestra es la que est almacenada en el registro DX. La instruccin LOOP decrementa automaticamente el registro CX en uno y si no ha llegado el valor de este registro a cero brinca a la casilla indicada como parmetro, lo cual crea un ciclo que se repite el nmero de veces especificado por el valor de CX. La interrupcin 20 termina la ejecucin del programa. Otra forma de realizar la misma funcin pero sin utilizar el comando LOOP es la siguiente: - a100 0C1B:0100 jmp 125 ; brinca a la direccin 125H 0C1B:0102 [Enter] - e 102 'Cadena a visualizar 15 veces' 0d 0a '$' - a125 0C1B:0125 MOV BX,000F ; veces que se desplegara la cadena 0C1B:0128 MOV DX,0102 ; copia cadena al registro DX

312

0C1B:012B MOV AH,09 ; copia valor 09 al registro AH 0C1B:012D INT 21 ; despliega cadena 0C1B:012F DEC BX ; decrementa en 1 a BX 0C1B:0130 JNZ 012D ; si BX es diferente a 0 brinca a 012D 0C1B:0132 INT 20 ; termina el programa. En este caso se utiliza el registro BX como contador para el programa, y por medio de la instruccin "DEC" se disminuye su valor en 1. La instruccin "JNZ" verifica si el valor de B es diferente a 0, esto con base en la bandera NZ, en caso afirmativo brinca hacia la direccin 012D. En caso contrario contina la ejecucin normal del programa y por lo tanto se termina. Una tima variante del programa es utilizando de nuevo a CX como contador, pero en lugar de utilizar LOOP utilizaremos decrementos a CX y comparacin de CX a 0. - a100 0C1B:0100 jmp 125 ; brinca a la direccin 125H 0C1B:0102 [Enter] - e 102 'Cadena a visualizar 15 veces' 0d 0a '$' - a125 0C1B:0125 MOV DX,0102 ; copia cadena al registro DX 0C1B:0128 MOV CX,000F ; veces que se desplegara la cadena 0C1B:012B MOV AH,09 ; copia valor 09 al registro AH 0C1B:012D INT 21 ; despliega cadena 0C1B:012F DEC CX ; decrementa en 1 a CX 0C1B:0130 JCXZ 0134 ; si CX es igual a 0 brinca a 0134 0C1B:0132 JMP 012D ; brinca a la direcci&oaute;n 012D 0C1B:0134 INT 20 ; termina el programa En este ejemplo se us la instruccin JCXZ para controlar la condicin de salto, el significado de tal funcin es: brinca si CX=0 El tipo de control a utilizar depender de las necesidades de programacin en determinado momento.

Interrupciones

313

Definicin de interrupcin: Una interrupcin es una instruccin que detiene la ejecucin de un programa para permitir el uso de la UCP a un proceso prioritario. Una vez concluido este ltimo proceso se devuelve el control a la aplicacin anterior. Por ejemplo, cuando estamos trabajando con un procesador de palabras y en ese momento llega un aviso de uno de los puertos de comunicaciones, se detiene temporalmente la aplicacin que estabamos utilizando para permitir el uso del procesador al manejo de la informacin que est llegando en ese momento. Una vez terminada la transferencia de informacin se reanudan las funciones normales del procesador de palabras. Las interrupciones ocurren muy seguido, sencillamente la interrupcin que actualiza la hora del da ocurre aproximadamente 18 veces por segundo. Para lograr administrar todas estas interrupciones, la computadora cuenta con un espacio de memoria, llamado memoria baja, donde se almacenan las direcciones de cierta localidad de memoria donde se encuentran un juego de instrucciones que la UCP ejecutar para despues regresar a la aplicacin en proceso. En los programas anteriores hicimos uso de la interrupcion nmero 20H para terminar la ejecucin de nuestros programas, ahora utilizaremos otra interrupcin para mostrar informacin en pantalla: Utilizando Debug tecleamos: - a100 2C1B:0100 JMP 011D 2C1B:0102 [ENTER] - E 102 'Hola, como estas.' 0D 0A '$' - A011D 2C1B:011D MOV DX,0102 2C1B:0120 MOV AH,09 2C1B:0122 INT 21 2C1B:0123 INT 20 En este programa la interrupcin 21H manda al monitor la cadena localizada en la direccin a la que apunta el registro DX. El valor que se le da a AH determina cual de las opciones de la interrupcin 21H sera utilizada, ya que esta interrupcin cuenta con varias opciones. El manejo directo de interrupciones es una de las partes mas fuertes del lenguaje ensamblador, ya que con ellas es posible controlar eficientemente todos los dispositivos internos y externos de una computadora gracias al completo control que se tiene sobre operaciones de entrada y salida.

Software necesario

314

Para poder crear un programa se requieren varias herramientas: Primero un editor para crear el programa fuente. Segundo un compilador que no es mas que un programa que "traduce" el programa fuente a un programa objeto. Y tercero un enlazador o linker, que genere el programa ejecutable a partir del programa objeto. El editor puede ser cualquier editor de textos que se tenga a la mano, como compilador utilizaremos el MASM (macro ensamblador de Microsoft) ya que es el mas comn, y como enlazador utilizaremos el programa link. La extensin usada para que MASM reconozca los programas fuente en ensamblador es .ASM; una vez traducido el programa fuente, el MASM crea un archivo con la extensin .OBJ, este archivo contiene un "formato intermedio" del programa, llamado as porque an no es ejecutable pero tampoco es ya un programa en lenguaje fuente. El enlazador genera, a partir de un archivo .OBJ o la combinacin de varios de estos archivos, un programa executable, cuya extensin es usualmente .EXE aunque tambin puede ser .COM, dependiendo de la forma en que se ensambl. Este tutorial describe la forma de trabajar con la versin 5.0 o posterior del MASM, la diferencia principal de esta versin con otras anteriores es la forma en que se declaran los segmentos de cdigo, datos y la pila, pero la estructura de programacin es la misma.

Utilizacin del MASM


Una vez que se cre el programa objeto se debe pasar al MASM para crear el cdigo intermedio, el cual queda guardado en un archivo con extensin .OBJ. El comando para realizar esto es: MASM nombre_archivo; [Enter] Donde Nombre_Archivo es el nombre del programa fuente con extensin .ASM que se va a traducir. El punto y coma utilizados despues del nombre del archivo le indican al macro ensamblador que genere directamente el cdigo intermedio, de omitirse este caracter el MASM pedir el nombre del archivo a traducir, el nombre del archivo que se generar as como opciones de listado de informacin que puede proporcionar el traductor. Es posible ejecutar el MASM utilizando parmetros para obtener un fin determinado, toda la lista de los mismos se encuentra en el manual del programa. Solo recordar en este tutorial la forma de pasar dichos parmetros al MASM: Todo parmetro va despues del simbolo "/". Es posible utilizar varios parmetros a la vez. Una vez tecleados todos los parmetros se escribe el nombre del archivo a ensamblar. Por ejemplo, si queremos que el MASM ensamble un programa llamado prueba, y ademas deseamos que despliege el nmero de lineas fuente y smbolos procesados (eso lo realiza con el parametro /v), y si ocurre un error que nos diga en que linea ocurri (con el parametro /z), entonces tecleamos: MASM /v /z prueba;

Uso del enlazador (linker)


El MASM unicamente puede crear programas en formato .OBJ, los cuales no son ejecutables por si solos, es necesario un enlazador que genere el cdigo ejecutable.

315

La utilizacin del enlazador es muy parecida a la del MASM, unicamente se teclea en el indicador del DOS: LINK Nombre_Archivo ; Donde Nombre_Archivo es el nombre del programa intermedio (OBJ). Esto generara directamente un archivo con el nombre del programa intermedio y la extensin .EXE

Formato interno de un programa


Para poder comunicarnos en cualquier lenguaje, incluyendo los lenguajes de programacin, es necesario seguir un conjunto de reglas, de lo contrario no podramos expresar lo que deseamos. En este apartado veremos algunas de las reglas que debemos seguir para escribir un programa en lenguaje ensamblador, enfocandonos a la forma de escribir las instrucciones para que el ensamblador sea capaz de interpretarlas. Basicamente el formato de una linea de cdigo en lenguaje ensamblador consta de cuatro partes: Etiqueta, variable o constante: No siempre es definida, si se define es necesario utilizar separadores para diferenciarla de las otras partes, usualmente espacios, o algn smbolo especial. Directiva o instruccin: es el nombre con el que se conoce a la instruccin que queremos que se ejecute. Operando(s): la mayora de las instrucciones en ensamblador trabajan con dos operandos, aunque hay instrucciones que funcionan solo con uno. El primero normalmente es el operando destino, que es el depsito del resultado de alguna operacin; y el segundo es el operando fuente, que lleva el dato que ser procesado. Los operandos se separan uno del otro por medio de una coma ",". Comentario: como su nombre lo indica es tan solo un escrito informativo, usado principalmente para explicar que est haciendo el programa en determinada linea; se separa de las otras partes por medio de un punto y coma ";". Esta parte no es necesaria en el programa, pero nos ayuda a depurar el programa en caso de errores o modificaciones. Como ejemplo podemos ver una linea de un programa escrito en ensamblador: Etiq1: MOV AX,001AH ; Inicializa AX con el valor 001A Aqu tenemos la etiqueta "Etiq1" (Identificable como etiqueta por el smbolo final ":"), la instruccin "MOV", y los operandos "AX" como destino y "001A" como fuente, ademas del comentario que sigue despues del ";". Un ejemplo de una declaracin de una constante esta dado por: UNO EQU 0001H Donde "UNO" es el nombre de la constante que definimos, "EQU" es la directiva utilizada para usar a "UNO" como constante, y "0001H" es el operando, que en este caso sera el valor que guarde UNO.

Formato Externo de un programa

316

Ademas de definir ciertas reglas para que el ensamblador pueda entender una instruccin es necesario darle cierta informacin de los recursos que se van a utilizar, como por ejemplo los segmentos de memoria que se van a utilizar, datos iniciales del programa y tambin donde inicia y donde termina nuestro cdigo. Un programa sencillo puede ser el siguiente: .MODEL SMALL .CODE Programa: MOV AX,4C00H INT 21H .STACK END Programa El programa realmente no hace nada, unicamente coloca el valor 4C00H en el registro AX, para que la interrupcin 21H termine el programa, pero nos da una idea del formato externo en un programa de ensamblador. La directiva .MODEL define el tipo de memoria que se utilizar; la directiva .CODE nos indica que lo que esta a continuacin es nuestro programa; la etiqueta Programa indica al ensamblador el inicio del programa; la directiva .STACK le pide al ensamblador que reserve un espacio de memoria para las operaciones de la pila; la instruccin END Programa marca el final del programa.

Ejemplo prctico de un programa


Aqu se ejemplificar un programa que escriba una cadena en pantalla: .MODEL SMALL .CODE Programa: MOV AX, @DATA MOV DS, AX MOV DX, Offset Texto MOV AH, 9 INT 21H MOV AX,4C00H

317

INT 21H .DATA Texto DB 'Mensaje en pantalla.$' .STACK END Programa Los primeros pasos son iguales a los del programa anterior: se define el modelo de memoria, se indica donde inicia el cdigo del programa y en donde comienzan las instrucciones. A continuacin se coloca @DATA en el registro AX para despues pasarlo al registro DS ya que no se puede copiar directamente una constante a un registro de segmento. El contenido de @DATA es el nmero del segmento que ser utilizado para los datos. Luego se guarda en el registro DX un valor dado por "Offset Texto" que nos da la direccin donde se encuentra la cadena de caracteres en el segmento de datos. Luego utiliza la opcin 9 (Dada por el valor de AH) de la interrupcin 21H para desplegar la cadena posicionada en la direccin que contiene DX. Por ltimo utiliza la opcin 4CH de la interrupcin 21H para terminar la ejecucin del programa (aunque cargamos al registro AX el valor 4C00H la interrupcin 21H solo toma como opcin el contenido del registro AH). La directiva .DATA le indica al ensamblador que lo que est escrito a continuacin debe almacenarlo en el segmento de memoria destinado a los datos. La directiva DB es utilizada para Definir Bytes, sto es, asignar a cierto identificador (en este caso "Texto") un valor, ya sea una constante o una cadena de caracteres, en este ltimo caso deber estar entre comillas sencillas ' y terminar con el simbolo "$".

Segmentos
La arquitectura de los procesadores x86 obliga al uso de segmentos de memoria para manejar la informacin, el tamao de estos segmentos es de 64kb. La razn de ser de estos segmentos es que, considerando que el tamao mximo de un nmero que puede manejar el procesador esta dado por una palabra de 16 bits o registro, no sera posible accesar a ms de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la pc en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una direccin en un registro exclusivo para localizar cada segmento, y entonces cada direccin de una casilla especfica la formamos con dos registros, nos es posible accesar a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, ms memoria de la que veremos instalada en una PC. Para que el ensamblador pueda manejar los datos es necesario que cada dato o instruccin se encuentren localizados en el rea que corresponde a sus respectivos segmentos. El ensamblador accesa a esta informacin tomando en cuenta la localizacin del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la direccin del dato especfico. Es por ello que cuando creamos un programa empleando el Debug en cada linea que vamos ensamblando aparce algo parecido a lo siguiente: 1CB0:0102 MOV AX,BX

318

En donde el primer nmero, 1CB0, corresponde al segmento de memoria que se est utilizando, el segundo se refiere la la direccin dentro de dicho segmento, y a continuacin aparecen las instrucciones que se almacenaran a partir de esa direccin. La forma de indicarle al ensamblador con cuales de los segmentos se va a trabajar es por medio de las directivas .CODE, .DATA y .STACK. El ensamblador se encarga de ajustar el tamao de los segmentos tomando como base el nmero de bytes que necesita cada instruccin que va ensamblando, ya que sera un desperdicio de memoria utilizar los segmentos completos. Por ejemplo, si un programa unicamente necesita 10kb para almacenar los datos, el segmento de datos unicamente sera de 10kb y no de los 64kb que puede manejar.

Tabla de smbolos
A cada una de las partes de una linea de cdigo en ensamblador se le conoce como token, por ejemplo en la linea de cdigo MOV AX,Var tenemos tres tokens, la instruccin MOV, el operando AX, y el operando VAR. El ensamblador lo que hace para generar el cdigo OBJ es leer cada uno de los tokens y buscarlo en una tabla interna de "equivalencias" conocida como tabla de palabras reservadas, que es donde se encuentran todos los significados de los mnemnicos que utilizamos como instrucciones. Siguiendo este proceso, el ensamblador lee MOV, lo busca en su tabla y al encontrarlo lo identifica como una instruccin del procesador, as mismo lee AX y lo reconoce como un registro del procesador, pero al momento de buscar el token Var en la tabla de palabras reservadas no lo encuentra y entonces lo busca en la tabla de smbolos que es una tabla donde se encuentran los nombres de las variables, constantes y etiquetas utilizadas en el programa donde se incluye su direccin en memoria y el tipo de datos que contiene. Algunas veces el ensamblador se encuentra con algn token no definido en el programa, lo que hace en estos casos es dar una segunda pasada por el programa fuente para verificar todas las referencias a ese smbolo y colocarlo en la tabla de smbolos. Existen smbolos que no los va a encontrar ya que no pertenecen a ese segmento y el programa no sabe en que parte de la memoria se encontrara dicho segmento, en este momento entra en accin el enlazador, el cual crea la estructura que necesita el cargador para que el segmento y el token sean definidos cuando se cargue el programa y antes de que el mismo sea ejecutado.

Movimiento de datos
En todo programa es necesario mover datos en la memoria y en los registros de la UCP; existen diversas formas de hacer esto: puede copiar datos de la memoria a algn registro, de registro a registro, de un registro a una pila, de la pila a un registro, transmitir datos hacia dispositivos externos as como recibir datos de dichos dispositivos. Este movimiento de datos est sujeto a reglas y restricciones. Algunas de ellas son las que se citan a continuacin. No es posible mover datos de una localidad de memoria a otra directamente, es necesario primero mover los datos de la localidad origen hacia un registro y luego del registro a la localidad destino.

319

No se puede mover una constante directamente a un registro de segmentos, primero se debe mover a un registro de la UCP. Es posible mover bloques de datos por medio de las instrucciones movs, que copia una cadena de bytes o palabras; movsb que copia n bytes de una localidad a otra; y movsw copia n palabras de una localidad a otra. Las dos ltimas instrucciones toman los valores de las direcciones definidas por DS:SI como grupo de datos a mover y ES:DI como nueva localizacin de los datos. Para mover los datos tambin existen las estructuras llamadas pilas, en este tipo de estructuras los datos se introducen con la instruccin push y se extraen con la instruccin pop En una pila el primer dato introducido es el ltimo que podemos sacar, esto es, si en nuestro programa utilizamos las instrucciones: PUSH AX PUSH BX PUSH CX Para devolver los valores correctos a cada registro al momento de sacarlos de la pila es necesario hacerlo en el siguiente orden: POP CX POP BX POP AX Para la comunicacin con dispositivos externos se utilizan el comando out para mandar informacin a un puerto y el comando in para leer informacin recibida desde algun puerto. La sintaxis del comando out es: OUT DX,AX Donde DX contiene el valor del puerto que se utilizar para la comunicacin y AX contiene la informacin que se mandar. La sintaxis del comando in es: IN AX,DX Donde AX es el registro donde se guardar la informacin que llegue y DX contiene la direccin del puerto por donde llegar la informacin

Operaciones lgicas y aritmticas


Las instrucciones de las operaciones lgicas son: and, not, or y xor, stas trabajan sobre los bits de sus operandos. Para verificar el resultado de operaciones recurrimos a las instrucciones cmp y test.

320

Las instrucciones utilizadas para las operaciones algebraicas son: para sumar add, para restar sub, para multiplicar mul y para dividir div. Casi todas las instrucciones de comparacin estn basadas en la informacin contenida en el registro de banderas. Normalmente las banderas de este registro que pueden ser directamente manipuladas por el programador son la bandera de direccin de datos DF, usada para definir las operaciones sobre cadenas. Otra que tambin puede ser manipulada es la bandera IF por medio de las instrucciones sti y cli, para activar y desactivar respectivamente las interrupciones.

Saltos, ciclos y procedimientos


Los saltos incondicionales en un programa escrito en lenguaje ensamblador estn dados por la instruccin jmp, un salto es alterar el flujo de la ejecucin de un programa enviando el control a la direccin indicada. Un ciclo, conocido tambin como iteracin, es la repeticin de un proceso un cierto nmero de veces hasta que alguna condicin se cumpla. En estos ciclos se utilizan los brincos "condicionales" basados en el estado de las banderas. Por ejemplo la instruccin jnz que salta solamente si el resultado de una operacin es diferente de cero y la instruccin jz que salta si el resultado de la operacin es cero. Por ltimo tenemos los procedimientos o rutinas, que son una serie de pasos que se usarn repetidamente en el programa y en lugar de escribir todo el conjunto de pasos unicamente se les llama por medio de la instruccin call. Un procedimiento en ensamblador es aquel que inicie con la palabra Proc y termine con la palabra ret. Realmente lo que sucede con el uso de la instruccin call es que se guarda en la pila el registro IP y se carga la direccin del procedimiento en el mismo registro, conociendo que IP contiene la localizacin de la siguiente instruccin que ejecutara la UCP, entonces podemos darnos cuenta que se desva el flujo del programa hacia la direccin especificada en este registro. Al momento en que se llega a la palabra ret se saca de la pila el valor de IP con lo que se devuelve el control al punto del programa donde se invoc al procedimiento. Es posible llamar a un procedimiento que se encuentre ubicado en otro segmento, para sto el contenido de CS (que nos indica que segmento se est utilizando) es empujado tambin en la pila.

Instruccin MOV
Propsito: Transferencia de datos entre celdas de memoria, registros y acumulador. Sintaxis: MOV Destino,Fuente Donde Destino es el lugar a donde se movern los datos y fuente es el lugar donde se encuentran dichos datos. Los diferentes movimientos de datos permitidos para esta instruccin son: Destino: memoria. Fuente: acumulador

321

Destino: acumulador. Fuente: memoria Destino: registro de segmento. Fuente: memoria/registro Destino: memoria/registro. Fuente: registro de segmento Destino: registro. Fuente: registro Destino: registro. Fuente: memoria Destino: memoria. Fuente: registro Destino: registro. Fuente: dato inmediato Destino: memoria. Fuente: dato inmediato Ejemplo: MOV AX,0006h MOV BX,AX MOV AX,4C00h INT 21H Este pequeo programa mueve el valor 0006H al registro AX, luego mueve el contenido de AX (0006h) al registro BX, por ltimo mueve el valor 4C00h al registro AX para terminar la ejecucin con la opcin 4C de la interrupcin 21h.

Instruccin MOVS (MOVSB) (MOVSW)


Propsito: Mover cadenas de bytes o palabras desde la fuente, direccionada por SI, hasta el destino direccionado por DI. Sintaxis: MOVS Este comando no necesita parametros ya que toma como direccin fuente el contenido del registro SI y como destino el contenido de DI. La secuencia de instrucciones siguiente ilustran esto: MOV SI, OFFSET VAR1 MOV DI, OFFSET VAR2 MOVS Primero inicializamos los valores de SI y DI con las direcciones de las variables VAR1 y VAR2 respectivamente, despues al ejecutar MOVS se copia el contenido de VAR1 a VAR2.

322

Los comandos MOVSB y MOVSW se utilizan de la misma forma que MOVS, el primero mueve un byte y el segundo una palabra.

Instruccin LODS (LODSB) (LODSW)


Propsito: Cargar cadenas de un byte o palabra al acumulador. Sintaxis: LODS Esta instruccin toma la cadena que se encuentre en la direccin especificada por SI, la carga al registro AL (o AX) y suma o resta 1 (segun el estado de DF) a SI si la transferencia es de bytes o 2 si la transferencia es de palabras. MOV SI, OFFSET VAR1 LODS La primer linea carga la direccin de VAR1 en SI y la segunda linea lleva el contenido de esa localidad al registro AL. Los comandos LODSB y LODSW se utilizan de la misma forma, el primero carga un byte y el segundo una palabra (utiliza el registro completo AX).

Instruccin LAHF
Propsito: Transfiere al registro AH el contenido de las banderas Sintaxis: LAHF Esta instruccin es til para verificar el estado de las banderas durante la ejecucin de nuestro programa. Las banderas quedan en el siguiente orden dentro del registro: SF ZF ? AF ? PF ? CF El simbolo "?" significa que en esos bits habr. un valor indefinido.

Instruccin LDS
Propsito: Cargar el registro del segmento de datos Sintaxis: LDS destino, fuente

323

El operando fuente debe ser una palabra doble en memoria. La palabra asociada con la direccin mas grande es transferida a DS, o sea que se toma como la direccin del segmento. La palabra asociada con la direccin menor es la direccin del desplazamiento y se deposita en el registro sealado como destino.

Instruccin LEA
Propsito: Carga la direccin del operando fuente. Sintaxis: LEA destino, fuente El operando fuente debe estar ubicado en memoria, y se coloca su desplazamiento en el registro ndice o apuntador especificado en destino. Para ilustrar una de las facilidades que tenemos con este comando pongamos una equivalencia: MOV SI, OFFSET VAR1 Equivale a: LEA SI, VAR1 Es muy probable que para el programador sea mas sencillo crear programas extensos utilizando este ltimo formato.

Instruccin LES
Propsito: Carga el registro del segmento extra Sintaxis: LES destino, fuente El operando fuente debe ser un operando en memoria de palabra doble. El contenido de la palabra con la direccin mayor se interpreta como la direccin del segmento y se coloca en ES. La palabra con la direccin menor es la direccin del desplazamiento y se coloca en el registro especificado en el parmetro destino.

Instruccin POP
Propsito: Recupera un dato de la pila Sintaxis: POP destino Esta instruccin transfiere el ltimo valor almacenado en la pila al operando destino, despus incrementa en dos el registro SP.

324

Este incremento se debe a que la pila va creciendo desde la direccin mas alta de memoria del segmento hacia la mas baja, y la pila solo trabaja con palabras (2 bytes), entonces al incrementar en dos el registro SP realmente se le esta restando dos al tamao real de la pila.

Instruccin POPF
Propsito: Extrae las banderas almacenadas en la pila. Sintaxis: POPF Este comando transfiere bits de la palabra almacenada en la parte superior de la pila hacia el registro de banderas. La forma de transferencia es la siguiente: BIT BANDERA 0 CF 2 PF 4 AF 6 ZF 7 SF 8 TF 9 IF 10 DF 11 OF Estas localizaciones son las mismas para el comando PUSHF Una vez hecha la transferencia se incrementa en 2 el registro SP disminuyendo as el tamao de la pila.

Instruccin PUSH
Propsito: Coloca una palabra en la pila. Sintaxis: PUSH fuente

325

La instruccin PUSH decrementa en dos el valor de SP y luego transfiere el contenido del operando fuente a la nueva direccin resultante en el registro recin modificado. El decremento en la direccin se debe a que al agregar valores a la pila sta crece de la direccin mayor a la direccin menor del segmento, por lo tanto al restarle 2 al valor del registro SP lo que hacemos es aumentar el tamao de la pila en dos bytes, que es la nica cantidad de informacin que puede manejar la pila en cada entrada y salida de datos.

Instruccin PUSHF
Propsito: Coloca el valor de las banderas en la pila Sintaxis: PUSHF Este comando decrementa en 2 el valor del registro SP y luego se transfiere el contenido del registro de banderas a la pila, en la direccin indicada por SP. Las banderas quedan almacenadas en memoria en los mismos bits indicados en el comando POPF

Instrucciones lgicas.
Son utilizadas para realizar operaciones lgicas sobre los operandos. AND NEG NOT OR TEST XOR

Instrucciones aritmticas.
Se usan para realizar operaciones aritmticas sobre los operandos. ADC ADD DIV IDIV MUL

326

IMUL SBB SUB

Instruccin AND
Propsito: Realiza la conjuncin de los operandos bit por bit. Sintaxis: AND destino, fuente Con esta instruccin se lleva a cabo la operacin "y" lgica de los dos operandos: Fuente Destino | Destino -------------------------1 1 | 1 1 0 | 0 0 1 | 0 0 0 | 0 El resultado de la operacin se almacena en el operando destino.

Instruccin NEG
Propsito: Genera el complemento a 2 Sintaxis:

NEG destino
Esta instruccin genera el complemento a 2 del operando destino y lo almacena en este mismo operando. Por ejemplo, si AX guarda el valor de 1234H, entonces: NEG AX Nos dejara almacenado en el registro AX el valor EDCCH.

Instruccin NOT
Propsito: Lleva a cabo la negacin bit por bit del operando destino. Sintaxis:

327

NOT destino El resultado se guarda en el mismo operando destino.

Instruccin OR
Propsito: OR inclusivo lgico Sintaxis: OR destino, fuente La instruccin OR lleva a cabo, bit por bit, la disyuncin inclusiva lgica de los dos operandos: Fuente Destino | Destino -------------------------1 1 | 1 1 0 | 1 0 1 | 1 0 0 | 0

Instruccin TEST
Propsito: Comparar logicamente los operandos Sintaxis: TEST destino, fuente Realiza una conjuncin, bit por bit, de los operandos, pero a diferencia de AND esta instruccin no coloca el resultado en el operando destino, solo tiene efecto sobre el estado de las banderas.

Instruccin XOR
Propsito: OR exclusivo Sintaxis:

XOR destino, fuente


Su funcin es efectuar bit por bit la disyuncin exclusiva lgica de los dos operandos. Fuente Destino | Destino --------------------------

328

1 1 | 0 0 0 | 1 0 1 | 1 0 0 | 0

Instruccin ADC
Propsito: Adicin con acarreo. Sintaxis: ADC destino, fuente Lleva a cabo la suma de dos operandos y suma uno al resultado en caso de que la bandera CF est activada, esto es, en caso de que exista acarreo. El resultado se guarda en el operando destino.

Instruccin ADD
Propsito: Adicin de los operandos. Sintaxis: ADD destino, fuente Suma los dos operandos y guarda el resultado en el operando destino.

Instruccin DIV
Propsito: Divisin sin signo Sintaxis: DIV fuente El divisor puede ser un byte o palabra y es el operando que se le da a la instruccin. Si el divisor es de 8 bits se toma como dividendo el registro de 16 bits AX y si el divisor es de 16 bits se tomara como dividendo el registro par DX:AX, tomando como palabra alta DX y como baja AX. Si el divisor fu un byte el cociente se almacena en el registro AL y el residuo en AH, si fu una palabra el cociente se guarda en AX y el residuo en DX.

Instruccin IDIV
Propsito: Divisin con signo

329

Sintaxis: IDIV fuente Consiste basicamente en lo mismo que la instruccin DIV, solo que esta ltima realiza la operacin con signo. Para sus resultados utiliza los mismos registros que la instruccin DIV.

Instruccin MUL
Propsito: Multiplicacin sin signo Sintaxis: MUL fuente El ensamblador asume que el multiplicando sera del mismo tamao que el del multiplicador, por lo tanto multiplica el valor almacenado en el registro que se le da como operando por el que se encuentre contenido en AH si el multiplicador es de 8 bits o por AX si el multiplicador es de 16 bits. Cuando se realiza una multiplicacin con valores de 8 bits el resultado se almacena en el registro AX y cuando la multiplicacin es con valores de 16 bits el resultado se almacena en el registro par DX:AX.

Instruccin IMUL
Propsito: Multiplicacin de dos enteros con signo. Sintaxis: IMUL fuente Este comando hace lo mismo que el anterior, solo que si toma en cuenta los signos de las cantidades que se multiplican. Los resultados se guardan en los mismos registros que en la instruccin MUL.

Instruccin SBB
Propsito: Substraccin con acarreo Sintaxis: SBB destino, fuente Esta instruccin resta los operandos y resta uno al resultado si CF est activada. El operando fuente siempre se resta del destino. Este tipo de substraccin se utiliza cuando se trabaja con cantidades de 32 bits.

330

Instruccin SUB
Propsito: Substraccin Sintaxis: SUB destino, fuente Resta el operando fuente del destino.

Instrucciones de salto
Son utilizadas para transferir el flujo del proceso al operando indicado. JMP JA (JNBE) JAE (JNBE) JB (JNAE) JBE (JNA) JE (JZ) JNE (JNZ) JG (JNLE) JGE (JNL) JL (JNGE) JLE (JNG) JC JNC JNO JNP (JPO) JNS JO JP (JPE) JS

331

Instrucciones para ciclos:


LOOP Transfieren el flujo del proceso, condicional o incondicionalmente, a un destino repitiendose esta accin hasta que el contador sea cero. LOOP LOOPE LOOPNE

Instrucciones de conteo
Se utilizan para decrementar o incrementar el contenido de los contadores. DEC INC

Instrucciones de comparacin
Son usadas para comparar operandos, afectan al contenido de las banderas. CMP CMPS (CMPSB) (CMPSW)

Instrucciones de banderas
Afectan directamente al contenido de las banderas. CLC CLD CLI CMC STC STD STI

Instruccin JMP
Propsito: Salto incondicional Sintaxis:

332

JMP destino Esta instruccin se utiliza para desviar el flujo de un programa sin tomar en cuenta las condiciones actuales de las banderas ni de los datos.

Instruccin JA (JNBE)
Propsito: Brinco condicional Sintaxis: JA Etiqueta Despus de una comparacin este comando salta si est arriba o salta si no est abajo o si no es igual. Esto significa que el salto se realiza solo si la bandera CF esta desactivada o si la bandera ZF esta desactivada (que alguna de las dos sea igual a cero).

Instruccin JAE (JNB)


Propsito: salto condicional Sintaxis: JAE etiqueta Salta si est arriba o si es igual o salta si no est abajo. El salto se efectua si CF esta desactivada.

Instruccin JB (JNAE)
Propsito: salto condicional Sintaxis: JB etiqueta Salta si est abajo o salta si no est arriba o si no es igual. Se efecta el salto si CF esta activada.

Instruccin JBE (JNA)


Propsito: salto condicional Sintaxis: JBE etiqueta

333

Salta si est abajo o si es igual o salta si no est arriba. El salto se efecta si CF est activado o si ZF est activado (que cualquiera sea igual a 1).

Instruccin JE (JZ)
Propsito: salto condicional Sintaxis: JE etiqueta Salta si es igual o salta si es cero. El salto se realiza si ZF est activada.

Instruccin JNE (JNZ)


Propsito: salto condicional Sintaxis: JNE etiqueta Salta si no es igual o salta si no es cero. El salto se efectua si ZF est desactivada.

Instruccin JG (JNLE)
Propsito: salto condicional, se toma en cuenta el signo. Sintaxis: JG etiqueta Salta si es ms grande o salta si no es menor o igual. El salto ocurre si ZF = 0 u OF = SF.

Instruccin JGE (JNL)


Propsito: salto condicional, se toma en cuenta el signo. Sintaxis: JGE etiqueta Salta si es ms grande o igual o salta si no es menor que.

334

El salto se realiza si SF = OF

Instruccin JL (JNGE)
Propsito: salto condicional, se toma en cuenta el signo. Sintaxis: JL etiqueta Salta si es menor que o salta si no es mayor o igual. El salto se efecta si SF es diferente a OF.

Instruccin JLE (JNG)


Propsito: salto condicional, se toma en cuenta el signo. Sintaxis: JLE etiqueta Salta si es menor o igual o salta si no es ms grande. El salto se realiza si ZF = 1 o si SF es diferente a OF

Instruccin JC
Propsito: salto condicional, se toman en cuenta las banderas. Sintaxis: JC etiqueta Salta si hay acarreo. El salto se realiza si CF = 1

Instruccin JNC
Propsito: salto condicional, se toma en cuenta el estado de las banderas. Sintaxis: JNC etiqueta Salta si no hay acarreo. El salto se efecta si CF = 0.

335

Instruccin JNO
Propsito: salto condicional, se toma en cuenta el estado de las banderas. Sintaxis: JNO etiqueta Salta si no hay desbordamiento. El salto se efectua si OF = 0.

Instruccin JNP (JPO)


Propsito: salto condicional, toma en cuenta el estado de las banderas. Sintaxis: JNP etiqueta Salta si no hay paridad o salta si la paridad es non. El salto ocurre si PF = 0.

Instruccin JNS
Propsito: salto condicional, toma en cuenta el estado de las banderas. Sintaxis: JNP etiqueta Salta si el signo esta desactivado. El salto se efecta si SF = 0.

Instruccin JO
Propsito: salto condicional, toma en cuenta el estado de las banderas. Sintaxis: JO etiqueta Salta si hay desbordamiento (overflow). El salto se realiza si OF = 1.

Instruccin JP (JPE)

336

Propsito: salto condicional, toma en cuenta el estado de las banderas. Sintaxis: JP etiqueta Salta si hay paridad o salta si la paridad es par. El salto se efecta si PF = 1.

Instruccin JS
Propsito: salto condicional, toma en cuenta el estado de las banderas. Sintaxis: JS etiqueta Salta si el signo est prendido. El salto se efecta si SF = 1.

Instruccin LOOP
Propsito: Generar un ciclo en el programa. Sintaxis: LOOP etiqueta La instruccin loop decrementa CX en 1, y transfiere el flujo del programa a la etiqueta dada como operando si CX es diferente a 1.

Instruccin LOOPE
Propsito: Generar un ciclo en el programa considerando el estado de ZF Sintaxis: LOOPE etiqueta Esta instruccin decrementa CX en 1. Si CX es diferente a cero y ZF es igual a 1, entonces el flujo del programa se transfiere a la etiqueta indicada como operando.

Instruccin LOOPNE
Propsito: Generar un ciclo en el programa, considerando el estado de ZF Sintaxis: LOOPNE etiqueta

337

Esta instruccin decrementa en uno a CX y transfiere el flujo del programa solo si ZF es diferente a 0.

Instruccin DEC
Propsito: Decrementar el operando Sintaxis: DEC destino Esta operacin resta 1 al operando destino y almacena el nuevo valor en el mismo oeprando.

Instruccin INC
Propsito: Incrementar el operando. Sintaxis: INC destino La instruccin suma 1 al operando destino y guarda el resultado en el mismo operando destino.

Instruccin CMP
Propsito: Comparar los operandos. Sintaxis: CMP destino, fuente Esta instruccin resta el operando fuente al operando destino pero sin que ste almacene el resultado de la operacin, solo se afecta el estado de las banderas.

Instruccin CMPS (CMPSB) (CMPSW)


Propsito: Comparar cadenas de un byte o palabra. Sintaxis: CMP destino, fuente Con esta instruccin la cadena de caracteres fuente se resta de la cadena destino. Se utilizan DI como indice para el segmento extra de la cadena fuente y SI como indice de la cadena destino. Solo se afecta el contenido de las banderas y tanto DI como SI se incrementan.

Instruccin CLC

338

Propsito: Limpiar bandera de acarreo. Sintaxis: CLC Esta instruccin apaga el bit correspondiente a la bandera de acarreo, o sea, lo pone en cero.

Instruccin CLD
Propsito: Limpiar bandera de direccin Sintaxis: CLD La instruccin CLD pone en cero el bit correspondiente a la bandera de direccin.

Instruccin CLI
Propsito: Limpiar bandera de interrupcin Sintaxis: CLI CLI pone en cero la bandera de interrupciones, desabilitando as aquellas interrupciones enmascarables. Una interrupcin enmascarable es aquella cuyas funciones son desactivadas cuando IF = 0.

Instruccin CMC
Propsito: Complementar la bandera de acarreo. Sintaxis: CMC Esta instruccin complementa el estado de la bandera CF, si CF = 0 la instruccin la iguala a 1, y si es 1 la instruccin la iguala a 0. Podemos decir que nicamente "invierte" el valor de la bandera.

Instruccin STC
Propsito: Activar la bandera de acarreo. Sintaxis: STC

339

Esta instruccin pone la bandera CF en 1.

Instruccin STD
Propsito: Activar la bandera de direccin. Sintaxis: STD La instruccin STD pone la bandera DF en 1.

Instruccin STI
Propsito: Acticar la bandera de interrupcin. Sintaxis: STI La instruccin activa la bandera IF, esto habilita las interrupciones externas enmascarables (las que funcionan unicamente cuando IF = 1 ).

Interrupciones internas de hardware


Las interrupciones internas son generadas por ciertos eventos que surgen durante la ejecucin de un programa. Este tipo de interrupciones son manejadas en su totalidad por el hardware y no es posible modificarlas. Un ejemplo claro de este tipo de interrupciones es la que actualiza el contador del reloj interno de la computadora, el hardware hace el llamado a esta interrupcin varias veces durante un segundo para mantener la hora actualizada. Aunque no podemos manejar directamente esta interrupcin (no podemos controlar por software las actualizaciones del reloj), es posible utilizar sus efectos en la computadora para nuestro beneficio, por ejemplo para crear un "reloj virtual" actualizado continuamente gracias al contador del reloj interno. nicamente debemos escribir un programa que lea el valor actual del contador y lo traduzca a un formato entendible para el usuario.

Interrupciones externas de hardware


Las interrupciones externas las generan los dispositivos perifricos, como pueden ser: teclado, impresoras, tarjetas de comunicaciones, etc. Tambin son generadas por los coprocesadores. No es posible desactivar a las interrupciones externas. Estas interrupciones no son enviadas directamente a la UCP, sino que se mandan a un circuito integrado cuya funcin es exclusivamente manejar este tipo de interrupciones. El circuito, llamado PIC 8259A, si es controlado por la UCP utilizando para tal control una serie de vias de comunicacin llamadas puertos.

340

Interrupciones de software
Las interrupciones de software pueden ser activadas directamente por el ensamblador invocando al nmero de interrupcin deseada con la instruccin INT. El uso de las interrupciones nos ayuda en la creacin de programas, utilizandolas nuestros programas son ms cortos, es ms fcil entenderlos y usualmente tienen un mejor desempeo debido en gran parte a su menor tamao. Este tipo de interrupciones podemos separarlas en dos categorias: las interrupciones del sistema operativo DOS y las interrupciones del BIOS. La diferencia entre ambas es que las interrupciones del sistema operativo son ms fciles de usar pero tambin son ms lentas ya que estas interrupciones hacen uso del BIOS para lograr su cometido, en cambio las interrupciones del BIOS son mucho ms rpidas pero tienen la desventaja que, como son parte del hardware son muy especficas y pueden variar dependiendo incluso de la marca del fabricante del circuito. La eleccin del tipo de interrupcin a utilizar depender unicamente de las caracteristicas que le quiera dar a su programa: velocidad (utilizando las del BIOS) o portabilidad (utilizando las del DOS).

Interrupcin 21H
Propsito: Llamar a diversas funciones del DOS. Sintaxis: Int 21H Nota: Cuando trabajamos en MASM es necesario especificar que el valor que estamos utilizando es hexadecimal. Esta interrupcin tiene varias funciones, para accesar a cada una de ellas es necesario que el el registro AH se encuentre el nmero de funcin que se requiera al momento de llamar a la interrupcin.

Funciones para desplegar informacin al video.


02H Exhibe salida 09H Impresin de cadena (video) 40H Escritura en dispositivo/Archivo

Funciones para leer informacin del teclado.


01H Entrada desde teclado 0AH Entrada desde teclado usando buffer 3FH Lectura desde dispositivo/archivo

341

Funciones para trabajar con archivos.


En esta seccin unicamente se expone la tarea especfica de cada funcin, para una referencia acerca de los conceptos empleados refierase a la unidad 7, titulada: "Introduccin al manejo de archivos".

Mtodo FCB
0FH Abrir archivo 14H Lectura secuencial 15H Escritura secuencial 16H Crear archivo 21H Lectura aleatoria 22H Escritura aleatoria Handles 3CH Crear archivo 3DH Abrir archivo 3EH Cierra manejador de archivo 3FH Lectura desde archivo/dispositivo 40H Escritura en archivo/dispositivo 42H Mover apuntador de lectura/escritura en archivo

Funcin 02H
Uso: Despliega un caracter a la pantalla. Registros de llamada: AH = 02H DL = Valor del caracter a desplegar. Registros de retorno: Ninguno

342

Esta funcin nos despliega el caracter cuyo codigo hexagesimal corresponde al valor almacenado en el registro DL, no se modifica ningn registro al utilizar este comando. Es recomendado el uso de la funcin 40H de la misma interrupcin en lugar de esta funcin.

Funcin 09H
Uso: Despliega una cadena de carateres en la pantalla. Registros de llamada: AH = 09H DS:DX = Direccin de inicio de una cadena de caracteres Registros de retorno: Ninguno. Esta funcin despliega los caracteres, uno a uno, desde la direccin indicada en el registro DS:DX hasta encontrar un caracter $, que es interpretado como el final de la cadena. Se recomienda utilizar la funcin 40H en lugar de esta funcin.

Funcin 40H
Uso: Escribir a un dispositivo o a un archivo. Registros de llamada: AH = 40H BX = Va de comunicacin CX = Cantidad de bytes a escribir DS:DX = Direccin del inicio de los datos a escribir Registros de retorno: CF = 0 si no hubo error AX = Nmero de bytes escritos CF = 1 si hubo error AX = Cdigo de error

343

El uso de esta funcin para desplegar informacin en pantalla se realiza dandole al registro BX el valor de 1 que es el valor preasignado al video por el sistema operativo MS-DOS.

Funcin 01H
Uso: Leer un caracter del teclado y desplegarlo. Registros de llamada: AH = 01H Registros de retorno: AL = Caracter ledo Con esta funcin es muy sencillo leer un caracter del teclado, el cdigo hexadecimal del caracter ledo se guarda en el registro AL. En caso de que sea un caracter extendido el registro AL contendra el valor de 0 y ser necesario llamar de nuevo a la funcin para obtener el cdigo de este caracter.

Funcin 0AH
Uso: Leer caracteres del teclado y almacenarlos en un buffer. Registros de llamada: AH = 0AH DS:DX = Direccin del rea de almacenamiento BYTE 0 = Cantidad de bytes en el rea BYTE 1 = Cantidad de bytes ledos desde BYTE 2 hasta BYTE 0 + 2 = caracteres ledos Registros de retorno: Ninguno Los caracteres son ledos y almacenados en un espacio predefinido de memoria. La estructura de este espacio le indica que en el primer byte del mismo se indican cuantos caracteres sern ledos. En el segundo byte se almacena el nmero de caracteres que ya se leyeron, y del tercer byte en adelante se escriben los caracteres ledos. Cuando se han almacenado todos los caracteres indicados menos uno la bocina suena y cualquier caracter adicional es ignorado. Para terminar la captura de la cadena es necesario darle [ENTER].

344

Funcin 3FH
Uso: Leer informacin de un dispositivo o archivo. Registros de llamada: AH = 3FH BX = Nmero asignado al dispositivo CX = Nmero de bytes a procesar DS:DX = Direccin del rea de almacenamiento Registros de retorno: CF = 0 si no hay error y AX = nmero de bytes leidos. CF = 1 si hay error y AX contendra el cdigo del error.

Funcin 0FH
Uso: Abrir archivo FCB Registros de llamada: AH = 0FH DS:DX = Apuntador a un FCB Registros de retorno: AL = 00H si no hubo problema, de lo contrario regresa 0FFH

Funcin 14H
Uso: Leer secuencialmente un archivo FCB. Registros de llamada: AH = 14H DS:DX = Apuntador a un FCB ya abierto.

345

Registros de retorno: AL = 0 si no hubo errores, de lo contrario se regresara el cdigo correspondiente de error: 1 error al final del archivo, 2 error en la estructura del FCB y 3 error de lectura parcial. Esta funcin lo que hace es que lee el siguiente bloque de informacin a partir de la direccin dada por DS:DX, y actualiza este registro.

Funcin 15H
Uso: Escribir secuencialmente a un archivo FCB Registros de llamada: AH = 15H DS:DX = Apuntador a un FCB ya abierto Registros de retorno: AL = 00H si no hubo errores, de lo contrario contendra el cdigo del error: 1 disco lleno o archivo de solo lectura, 2 error en la formacin o especificacin del FCB. La funcin 15H despus de escribir el registro al bloque actual actualiza el FCB.

Funcin 16H
Uso: Crear un archivo FCB. Registros de llamada: AH = 16H DS:DX = Apuntador a un FCB ya abierto. Registros de retorno: AL = 00H si no hubo errores, de lo contrario contendra el valor 0FFH Se basa en la informacin proveida en un FCB para crear un archivo en el disco.

Funcin 21H
Uso: Leer en forma aleatoria un archivo FCB.

346

Registros de llamada: AH = 21H DS:DX = Apuntador a un FCB ya abierto. Registros de retorno: A = 00H si no hubo error, de lo contrario AH contendra el cdigo del error: 1 si es fin de archivo, 2 si existe error de especificacin de FCB y 3 si se ley un registro parcial o el apuntador del archivo se encuentra al final del mismo. Esta funcin lee el registro especificado por los campos del bloque actual y registro actual de un FCB abierto y coloca la informacin en el DTA (rea de transferencia de disco o Disk Transfer Area).

Funcin 22H
Uso: Escribir en forma aleatoria en un archivo FCB. Registros de llamada: AH = 22H DS:DX = Apuntador a un FCB abierto. Registros de retorno: AL = 00H si no hubo error, de lo contrario contendr el cdigo del error: 1 si el disco est lleno o es archivo de solo lectura y 2 si hay error en la especificacin de FCB. Escribe el registro especificado por los campos del bloque actual y registro actual de un FCB abierto. Escribe dicha informacin a partir del contenido del DTA (rea de transferencia de disco).

Funcin 3CH
Uso: Crear un archivo si no existe o dejarlo en longitud 0 si existe. (Handle) Registros de llamada: AH = 3CH CH = Atributo de archivo DS:DX = Apuntador a una especificain ASCIIZ Registros de retorno:

347

CF = 0 y AX el nmero asignado al handle si no hay error, en caso de haberlo CF ser 1 y AX contendra el cdigo de error: 3 ruta no encontrada, 4 no hay handles disponibles para asignar y 5 acceso negado. Esta funcin sustituye a la 16H. El nombre del archivo es especificado en una cadena ASCIIZ, la cual tiene como caracterstica la de ser una cadena de bytes convencional terminada con un caracter 0. El archivo creado contendra los atributos definidos en el registro CX en la siguiente forma: Valor Atributos 00H Normal 02H Escondido 04H Sistema 06H Escondido y de sistema El archivo se crea con los permisos de lectura y escritura. No es posible crear directorios utilizando esta funcin.

Funcin 3DH
Uso: Abre un archivo y regrese un handle Registros de llamada: AH = 3DH AL = modo de acceso DS:DX = Apuntador a una especificacin ASCIIZ Registros de retorno: CF = 0 y AX = nmero de handle si no hay errores, de lo contrario CF = 1 y AX = cdigo de error: 01H si no es vlida la funcin, 02H si no se encontr el archivo, 03H si no se encontro la ruta, 04H si no hay handles disponibles, 05H en caso de acceso negado, y 0CH si el cdigo de acceso no es vlido. El handle regresado es de 16 bits. El cdigo de acceso se especifica en la siguiente forma: BITS 7654321

348

. . . . 0 0 0 Solo lectura . . . . 0 0 1 Solo escritura . . . . 0 1 0 Lectura/Escritura . . . X . . . RESERVADO

Funcin 3EH
Uso: Cerrar archivo (Handle). Registros de llamada: AH = 3EH BX = Handle asignado Registros de retorno: CF = 0 si no hubo errores, en caso contrario CF ser 1 y AX contendr el cdigo de error: 06H si el handle es invlido. Esta funcin actualiza el archivo y libera o deja disponible el handle que estaba utilizando.

Funcin 3FH
Uso: Leer de un archivo abierto una cantdad definida de bytes y los almacena en un buffer especfico. Registros de llamada: AH = 3FH BX = Handle asignado CX = Cantidad de bytes a leer DS:DX = Apuntador a un rea de trabajo. Registros de retorno: CF = 0 y AX = nmero de bytes leidos si no hubo error, en caso contrario CF = 1 y AX = cdigo de error: 05H si acceso negado y 06H si no es vlido el handle.

Funcin 40H
Uso:

349

Escribe a un archivo ya abierto una cierta cantidad de bytes a partir del buffer designado. Registros de llamada: AH = 40H BX = Handle asignado CX = Cantidad de bytes a escribir. DS:DX = Apuntador al buffer de datos. Registros de retorno: CF = 0 y AX = nmero de bytes escritos si no hay errores, en caso de existir CF = 1 y AX = cdigo del error: 05H si el acceso es negado y 06H si el handle es invlido.

Funcin 42H
Uso: Mover apuntador al archivo (Handle) Registros de llamada: AH = 42H AL = mtodo utilizado BX = Handle asignado CX = La parte ms significativa del offset DX = La parte menos significativa del offset Registros de retorno: CF = 0 y DX:AX = la nueva posicin del apuntador. En caso de error CF ser 1 y AX = cdigo de error: 01H si la funcin no es vlida y 06H si el handle no es vlido. El mtodo utilizado se configura como sigue: Valor de AL Mtodo 00H A partir del principio del archivo 01H A partir de la posicin actual 02H A partir del final del archivo

Interrupcin 10H

350

Propsito: Llamar a diversas funciones de video del BIOS. Sintaxis: Int 10H Esta interrupcin tiene diversas funciones, todas ellas nos sirven para controlar la entrada y salida de video, la forma de acceso a cada una de las opciones es por medio del registro AH. En este tutorial unicamente veremos algunas de las funciones de esta interrupcin. Funciones comunes de la interrupcin 10H. 02H Seleccin de posicin del cursor 09H Escribe atributo y caracter en el cursor 0AH Escribe caracter en la posicin del cursor 0EH Escritura de caracteres en modo alfanumrico

Funcin 02H
Uso: Posiciona el cursor en la pantalla dentro de las coordenadas vlidas de texto. Registros de llamada: AH = 02H BH = Pgina de video en la que se posicionar el cursor. DH = Fila DL = Columna Registros de retorno: Ninguno. Las posiciones de localizacin del cursor son definidas por coordenadas iniciando en 0,0, que corresponde a la esquina superior izquierda hasta 79,24 correspondientes a la esquina inferior derecha. Tenemos entonces que los valores que pueden tomar los registros DH y DL en modo de texto de 80 x 25 son de 0 hasta 24 y de 0 hasta 79 respectivamente.

Funcin 09H
Uso: Desplegar un caracter un determinado nmero de veces con un atributo definido empezando en la posicin actual del cursor. Registros de llamada:

351

AH = 09H AL = Caracter a desplegar BH = Pgina de video en donde se desplegar BL = Atributo a usar Nmero de repeticiones. Registros de retorno: Ninguno Esta funcin despliega un caracter el nmero de veces especificado en CX pero sin cambiar la posicin del cursor en la pantalla.

Funcin 0AH
Uso: Desplegar un caracter en la posicin actual del cursor. Registros de llamada: AH = 0AH AL = Caracter a desplegar BH = Pgina en donde desplegar BL = Color a usar (slo en grficos). CX = Nmero de repeticiones Registros de retorno: Ninguno. La nica diferencia entre esta funcin y la anterior es que sta no permite modificar los atributos, simplemente usa los atributos actuales. Tampoco se altera la posicin del cursor con esta funcin.

Funcin 0EH
Uso: Deplegar un caracter en la pantalla actualizando la posicin del cursor. Registros de llamada:

352

AH = 0EH AL = Caracter a desplegar BH = Pgina donde se desplegara el caracter BL = Color a usar (solo en grficos) Registros de retorno: Ninguno

Interrupcin 16H
Propsito: Manejar la entrada/salida del teclado. Sintaxis: Int 16H Veremos dos opciones de la interrupcin 16H, estas opciones, al igual que las de otras interrupciones, son llamadas utilizando el registro AH. Funciones de la interrupcin 16H 00H Lee un caracter de teclado 01H Lee estado del teclado

Funcin 00H
Uso: Leer un caracter del teclado. Registros de llamada: AH = 00H Registros de retorno: AH = cdigo de barrido (scan code) del teclado AL = Valor ASCII del caracter. Cuando se utiliza esta interrupcin se detiene la ejecucin del programa hasta que se introduzca un caracter desde el teclado, si la tecla presionada es un caracter ASCII su valor ser guardado en el registro AH, de lo contrario el cdigo de barrido ser guardado en AL y AH contendr el valor 00H. El cdigo de barrido fu creado para manejar las teclas que no tienen una representacin ASCII como [ALT], [CONTROL], las teclas de funcin, etc.

353

Funcin 01H
Uso: Leer estado del teclado. Registros de llamada: AH = 01H Registros de retorno: Si la bandera de cero, ZF, est apagada significa que hay informacin en el buffer, si se encuentra prendida es que no hay teclas pendientes. En caso de existir informacin el registro AH contendr el cdigo de la tecla guardada en el buffer.

Interrupcin 17H
Propsito: Manejar la entrada/salida de la impresora. Sintaxis: Int 17H Esta interrupcin es utilizada para escribir caracteres a la impresora, inicializarla y leer su estado. Funciones de la interrupcin 16H 00H Imprime un caracter ASCII 01H Inicializa la impresora 02H Proporciona el estado de la impresora

Funcin 00H
Uso: Escribir un caracter a la impresora. Registros de llamada: AH = 00H AL = Caracter a imprimir DX = Puerto a utilizar Registros de retorno: AH = Estado de la impresora. El puerto a utilizar, definido en DX, se especifica as: LPT1 = 0, LPT2 = 1, LPT3 = 2 ...

354

El estado de la impresora se codifica bit por bit como sigue: BIT 1/0 SIGNIFICADO ---------------------------------------0 1 Se agot el tiempo de espera 1 2 3 1 Error de entrada/salida 4 1 Impresora seleccionada 5 1 Papel agotado 6 1 Reconocimiento de comunicacin 7 1 La impresora se encuentra libre Los bits 1 y 2 no son relevantes. La mayoria de los BIOS unicamente soportan 3 puertos paralelos aunque existen algunos que soportan 4.

Funcin 01H
Uso: Inicializar un puerto de impresin. Registros de llamada: AH = 01H DX = Puerto a utilizar Registros de retorno: AH = Status de la impresora El puerto a utilizar, definido en DX, se especifica as: LPT1 = 0, LPT2 = 1, etc. El estado de la impresora se codifica bit por bit como sigue: BIT 1/0 SIGNIFICADO ----------------------------------------

355

0 1 Se agot el tiempo de espera 1 2 3 1 Error de entrada/salida 4 1 Impresora seleccionada 5 1 Papel agotado 6 1 Reconocimiento de comunicacin 7 1 La impresora se encuentra libre Los bits 1 y 2 no son relevantes. La mayoria de los BIOS unicamente soportan 3 puertos paralelos aunque existen algunos que soportan 4.

Funcin 02H
Uso: Obtener el estado de la impresora. Registros de llamada: AH = 01H DX = Puerto a utilizar Registros de retorno: AH = Status de la impresora. El puerto a utilizar, definido en DX, se especifica as: LPT1 = 0, LPT2 = 1, etc. El estado de la impresora se codifica bit por bit como sigue: BIT 1/0 SIGNIFICADO ---------------------------------------0 1 Se agot el tiempo de espera 1 2 -

356

3 1 Error de entrada/salida 4 1 Impresora seleccionada 5 1 Papel agotado 6 1 Reconocimiento de comunicacin 7 1 La impresora se encuentra libre Los bits 1 y 2 no son relevantes. La mayoria de los BIOS unicamente soportan 3 puertos paralelos aunque existen algunos que soportan 4.

Mtodos de trabajo con archivos


Existen dos formas de trabajar con archivos, la primera es por medio de bloques de control de archivos o "FCB" y la segunda es por medio de canales de comunicacin, tambien conocidos como "handles". La primera forma de manejo de archivos se viene utilizando desde el sistema operativo CPM, antecesor del DOS, por lo mismo asegura cierta compatibilidad con archivos muy antiguos tanto del CMP como de la versin 1.0 del DOS, adems este mtodo nos permite tener un nmero ilimitado de archivos abiertos al mismo tiempo. Si se quiere crear un volumen para el disco la nica forma de lograrlo es utilizando este mtodo. An considerando las ventajas del FCB el uso de los canales de comunicacin es mucho ms sencillo y nos permite un mejor manejo de errores, adems, por ser ms novedoso es muy probable que los archivos as creados se mantengan compatibles a travs de versiones posteriores del sistema operativo. Para una mayor facilidad en las explicaciones posteriores me referir a el mtodo de bloques de control de archivos como FCBs y al mtodo de canales de comunicacin como handles. Introduccin Existen dos tipos de FCB, el normal, cuya longitud es de 37 bytes y el extendido de 44 bytes. En este tutorial unicamente se tratar el primer tipo, as que de ahora en adelante cuando me refiera a un FCB realmente estoy hablando de un FCB de 37 bytes. El FCB se compone de informacin dada por el programador y por informacin que toma directamente del sistema operativo. Cuando se utilizan este tipo de archivos unicamente es posible trabajar en el directorio actual ya que los FCB no proveen apoyo para el uso de la organizacin por directorios del DOS. El FCB est formado por los siguientes campos: POSICION LONGITUD SIGNIFICADO 00H 1 Byte Drive

357

01H 8 Bytes Nombre del archivo 09H 3 Bytes Extensin 0CH 2 Bytes Nmero de bloque 0EH 2 Bytes Tamao del registro 10H 4 Bytes Tamao del archivo 14H 2 Bytes Fecha de creacin 16H 2 Bytes Hora de creacin 18H 8 Bytes Reservados 20H 1 Byte Registro actual 21H 4 Bytes Regsitro aleatorio Para seleccionar el drive de trabajo se sigue el siguiente formato: drive A = 1; drive B = 2; etc. Si se utiliza 0 se tomar como opcin el drive que se est utilizando en ese momento. El nombre del archivo debe estar justificado a la izquierda y en caso de ser necesario se debern rellenar los bytes sobrantes con espacios, la extensin del archivo se coloca de la misma forma. El bloque actual y el registro actual le dicen a la computadora que registro ser accesado en operaciones de lectura o escritura. Un bloque es un grupo de 128 registros. El primer bloque del archivo es el bloque 0. El primer registro es el registro 0, por lo tanto el ltimo registro del primer bloque sera 127, ya que la numeracin inici con 0 y el bloque puede contener 128 registros en total. Abrir archivos Para abrir un archivo FCB se utiliza la interrupcin 21H, funcin 0FH. La unidad, el nombre y extensin del archivo deben ser inicializados antes de abrirlo. El registro DX debe apuntar al bloque. Si al llamar a la interrupcin sta regresa valor de FFH en el registro AH es que el archivo no se encontr, si todo sali bien se devolvera un valor de 0. Si se abre el archivo DOS inicializa el bloque actual a 0, el tamao del registro a 128 bytes y el tamao del mismo y su fecha se llenan con los datos encontrados en el directorio. Crear un archivo nuevo Para la creacin de archivos se utiliza la interrupcin 21H funcin 16H . DX debe apuntar a una estructura de control cuyos requisitos son que al menos se encuentre definida la unidad lgica, el nombre y la extensin del archivo. En caso de existir algun problema se devolver el valor FFH en AL, de lo contrario este registro contendr el valor de 0.

358

Escritura secuencial Antes de que podamos realizar escrituras al disco es necesario definir el rea de transferencia de datos utilizando para tal fin la funcin 1AH de la interrupcin 21H. La funcin 1AH no regresa ningn estado del disco ni de la operacin, pero la funcin 15H, que es la que usaremos para escribir al disco, si lo hace en el registro AL, si ste es igual a cero no hubo error y se actualizan los campos del registro actual y bloque. Lectura secuencial Antes que nada debemos definir el rea de transferencia de archivos o DTA. Para leer secuencialmente utilizamos la funcin 14H de la int 21H. El registro a ser leido es el que se encuentra definido por el bloque y el registro actual. El registro AL regresa el estado de la operacin, si AL contiene el valor de 1 o 3 es que hemos llegado al final del archivo. Un resultado de 2 significa que el FCB est mal estructurado. En caso de no existir error AL contendr el valor de 0 y los campos bloque actual y registro actual son actualizados. Lectura y escritura aleatoria La funcin 21H y la funcin 22H de la interrupcin 21H son las encargadas de realizar las lecturas y escrituras aleatorias respectivamente. El nmero de registro aleatorio y el bloque actual son usados para calcular la posicin relativa del registro a leer o escribir. El registro AL regresa la misma informacin que para lectura o escritura secuencial. La informacin que ser leda se regresar en el rea de transferencia de disco, as mismo la informacin que ser escrita reside en el DTA. Cerrar un archivo Para cerrar un archivo utilizamos la funcin 10H de la interrupcin 21H. Si despus de invocarse esta funcin el registro AL contiene el valor de FFH significa que el archivo ha cambiado de posicin, se cambi el disco o hay un error de acceso al disco.

Trabajando con handles


El uso de handles para manejar los archivos facilita en gran medida la creacin de archivos y el programador puede concentrarse en otros aspectos de la programacin sin preocuparse en detalles que pueden ser manejados por el sistema operativo. La facilidad en el uso de los handles consiste en que para operar sobre un archivo unicamente es necesario definir el nombre del mismo y el nmero del handle a utilizar, toda la dems informacin es manejada internamente por el DOS.

359

Cuando utilizamos este mtodo para trabajar con archivos no existe una distincin entre accesos secuenciales o aleatorios, el archivo es tomado simplemente como una cadena de bytes. Funciones para utilizar handles Las funciones utilizadas para el manejo de archivos por medio de handles son descritas en la unidad 6: Interrupciones, en la seccin dedicada a la interrupcin 21H.

Definicin de procedimiento
Un procedimiento es un conjunto de instrucciones a los que podemos dirigir el flujo de nuestro programa, y una vez terminada la ejecucin de dichas instrucciones se devuelve el control a la siguiente linea a procesar del cdigo que mando llamar al procedimiento. Los procedimientos nos ayudan a crear programas legibles y fciles de modificar. Al momento de invocar a un procedimiento se guarda en la pila la direccin de la siguiente instruccin del programa para que, una vez transferido el flujo del programa y terminado el procedimiento, se pueda regresar a la linea siguiente del programa original (el que llam al procedimiento).

Sintaxis de un procedimiento
Existen dos tipos de procedimientos, los intrasegmentos, que se encuentran en el mismo segmento de instrucciones y los intersegmentos que pueden ser almacenados en diferentes segmentos de memoria. Cuando se utilizan los procedimientos intrasegmentos se almacena en la pila el valor de IP y cuando se utilizan los intersegmentos se almacena el valor CS:IP Para desviar el flujo a un procedimiento (llamarlo) se utiliza la directiva: CALL NombreDelProcedimiento Las partes que componen a un procedimiento son: Declaracin del procedimiento Cdigo del procedimiento Directiva de regreso Terminacin del procedimiento

Por ejemplo, si queremos una rutina que nos sume dos bytes, almacenados en AH y AL cada uno y guardar la suma en el registro BX: Suma Proc Near Declaracin del procedimiento Mov Bx, 0 Contenido del procedimiento Mov Bl, Ah Mov Ah, 00 Add Bx, Ax

360

Ret Suma Endp

;Directiva de regreso ;Declaracin de final del procedimiento

En la declaracin la primera palabra, Suma, corresponde al nombre de nuestro procedimiento, Proc lo declara como tal y la palabra Near le indica al MASM que el procedimiento es intrasegmento. La directiva Ret carga la direccin IP almacenada en la pila para regresar al programa original, por ltimo, la directiva Suma Endp indica el final del procedimiento. Para declarar un procedimiento intersegmento sustituimos la palabra Near por la palabra FAR. El llamado de este procedimiento se realiza de la siguiente forma: Call Suma Las macros ofrecen una mayor flexibilidad en la programacin comparadas con los procedimientos, pero no por ello se dejarn de utilizar estos ltimos.

Definicin de una macro


Una macro es un grupo de instrucciones repetitivas en un programa que se codifican solo una vez y pueden utilizarse cuantas veces sea necesario. La principal diferencia entre una macro y un procedimiento es que en la macro se hace posible el paso de parmetros y en el procedimiento no (esto es aplicable solo para el MASM, hay otros lenguajes de programacin que si lo permiten). Al momento de ejecutarse la macro cada parmetro es sustituido por el nombre o valor especificado al momento de llamarla. Podemos decir entonces que un procedimiento es una extensin de un determinado programa, mientras que la macro es un mdulo con funciones especficas que puede ser utilizado por diferentes programas. Otra diferencia entre una macro y un procedimiento es la forma de llamar a cada uno, para llamar a un procedimiento se requiere el uso de una directiva, en cambio la llamada a las macros se realiza como si se tratara de una instruccin del ensamblador.

Sintaxis de una macro


Las partes que componen a una macro son: Declaracin de la macro Cdigo de la macro Directiva de terminacin de la macro

La declaracin de la macro se lleva a cabo de la siguiente forma: NombreMacro MACRO [parametro1, parametro2...] Aunque se tiene la funcionalidad de los parametros es posible crear una macro que no los necesite. La directiva de terminacin de la macro es: ENDM

361

Un ejemplo de macro, para colocar el cursor en alguna posicin determinada de la pantalla es: Posicion MACRO Fila, Columna PUSH AX PUSH BX PUSH DX MOV AH, 02H MOV DH, Fila MOV DL, Columna MOV BH, 0 INT 10H POP DX POP BX POP AX ENDM Para utilizar una macro solo es necesario llamarla por su nombre, como si fuera una instruccin mas del ensamblador, ya no son necesarias las directivas como en el caso de los procedimientos. Ejemplo: Posicion 8, 6

Bibliotecas de macros
Una de las facilidades que ofrece el uso de las macros es la creacin de bibliotecas, las cuales son grupos de macros que pueden ser incluidas en un programa desde un archivo diferente. La creacin de estas bibliotecas es muy sencilla, nicamente tenemos que escribir un archivo con todas las macros que se necesitarn y guardarlo como archivo de texto. Para llamar a estas macros solo es necesario utilizar la instruccin Include NombreDelArchivo, en la parte de nuestro programa donde escribiriamos normalmente las macros, esto es, al principio de nuestro programa (antes de la declaracin del modelo de memoria). Suponiendo que se guard el archivo de las macros con el nombre de MACROS.TXT la instruccin Include se utilizara de la siguiente forma: ;Inicio del programa

362

Include MACROS.TXT .MODEL SMALL .DATA ;Aqui van los datos .CODE Inicio: ;Aqui se inserta el cdigo del programa .STACK ;Se define la pila End Inicio ;Termina nuestro programa

363

Interfaz de usuario
Prlogo General
Los Avances de la Ciencia y la Tecnologa han puesto al hombre en un plano intermedio entre lo tangible e intangible computacionalmente hablando, es ahora tan comn el convivir con un computador diariamente que cada vez se hace ms imperativo la mejor interaccin hombremquina a travs de una adecuada interfaz (Interfaz de Usuario), que le brinde tanto comodidad ,como eficiencia. El presente trabajo es una introduccin al mundo de las Interfaz de Usuarios, en el estn los conceptos y nociones bsicas que permitirn en adelante adentrarnos ms en este mbito.

CONCEPTOS DE INTERFAZ
Lewis y Rieman [1993] definen las interfaces hombre computadora como: Aquellas interfaces bsicas que incluyen cosas u objetos como mens, ventanas, teclado, ratn, los beeps y algunos otros sonidos que la computadora hace, en general, todos aquellos canales por los cuales se permite la comunicacin entre el hombre y la computadora. La idea fundamental en el concepto de interfaz es el de mediacin, entre hombre y mquina. La interfaz es lo que "media", lo que facilita la comunicacin, la interaccin, entre dos sistemas de diferente naturaleza, tpicamente el ser humano y una mquina como el computador. Esto implica, adems, que se trata de un sistema de traduccin, ya que los dos "hablan" lenguajes diferentes: verbo-icnico en el caso del hombre y binario en el caso del procesador electrnico. De una manera ms tcnica se define a Interfaz de usuario, como conjunto de componentes empleados por los usuarios para comunicarse con las computadoras. El usuario dirige el funcionamiento de la mquina mediante instrucciones, denominadas genricamente entradas. Las entradas se introducen mediante diversos dispositivos, por ejemplo un teclado, y se convierten en seales electrnicas que pueden ser procesadas por la computadora. Estas seales se transmiten a travs de circuitos conocidos como bus, y son coordinadas y controladas por la unidad de proceso central y por un soporte lgico conocido como sistema operativo. Una vez que la UPC ha ejecutado las instrucciones indicadas por el usuario, puede comunicar los resultados mediante seales electrnicas, o salidas, que se transmiten por el bus a uno o ms dispositivos de salida, por ejemplo una impresora o un monitor. Resumiendo entonces podemos decir que, una interfaz de software es la parte de una aplicacin que el usuario ve y con la cual interacta. Est relacionada con la subyacente estructura, la arquitectura, y el cdigo que hace el trabajo del software, pero no se confunde con ellos. La interfaz incluye las pantallas, ventanas, controles, mens, metforas, la ayuda en lnea, la documentacin y el entrenamiento. Cualquier cosa que el usuario ve y con lo cual interacta es parte de la interfaz. Una interfaz inteligente es fcil de aprender y usar. Permite a los usuarios hacer su trabajo o desempear una tarea en la manera que hace ms sentido para ellos, en vez de tener que ajustarse al software. Una interfaz inteligente se disea especficamente para la gente que la usar.

CLASIFICACIN
Dentro de las Interfaces de Usuario se distinguir bsicamente dos tipos:

364

Una interfaz de hardware, a nivel de los dispositivos utilizados para ingresar, procesar y entregar los datos: teclado, ratn y pantalla visualizadora; y Una interfaz de software, destinada a entregar informacin acerca de los procesos y herramientas de control, a travs de lo que el usuario observa habitualmente en la pantalla.

De esta clasificacin general se puede ir desprendiendo algunas, as por ejemplo segn su evolucin tenemos que las interfaces de usuario corren en paralelo con la de los sistemas operativos; de hecho, la interfaz constituye actualmente uno de los principales elementos de un sistema operativo. A continuacin se muestran las distintas interfaces que histricamente han ido apareciendo, ejemplificndolas con las sucesivas versiones de los sistemas operativos ms populares. Interfaces de lnea de mandatos (command-line user interfaces, CUIs). Es el caracterstico del DOS, el sistema operativo de los primeros PC, y es el estilo ms antiguo de interaccin hombre-mquina. El usuario escribe rdenes utilizando un lenguaje formal con un vocabulario y una sintaxis propia (los mandatos en el caso del DOS). Se usa un teclado, tpicamente, y las rdenes estn encaminadas a realizar una accin. El usuario no suele recibir mucha informacin por parte del sistema (ejemplo: indicador del DOS), y debe conocer cmo funciona el ordenador y dnde estn los programas (nada est oculto al usuario). El modelo de la interfaz es el del programador, no el del usuario. Ejemplo del DIR-DELDIR, por la falta de informacin de respuesta del DOS. Otras veces, en cambio, es excesiva: etiqueta del volumen en el DIR. Inconveniente: carga de memoria del usuario (debe memorizar los mandatos; incluso la ayuda es difcil de leer); nombres no siempre adecuados a las funciones, significado de los mandatos mal comprendido a veces (varios mandatos con el mismo o parecido significado, como DEL y ERASE); inflexible en los nombres (DEL y no DELETE). Ventajas: potente, flexible y controlado por el usuario, aunque esto es una ventaja para usuarios experimentados. La sintaxis es estricta, y los errores pueden ser graves As: C:\TMP\>dir El volumen en unidad C es PCDOS_6 Nmero de Serie del Volumen es 1D8F-82B0 Directorio de C:\TMP . .. ABCD <DIR> <DIR> <DIR> 02-02-98 21:08 02-02-98 21:08 02-02-98 21:23

365

CARTA

DOC

1.107 22-10-96 1.107 bytes

9:51

4 archivo(s)

24.862.720 bytes libres C:\TMP\> Problema del mandato COPY En suma, un CUI es adecuado para usuarios expertos, no para noveles. Para aquellos resultan ms rpidos, por lo que se puede disear un CUI como parte de una interfaz, para que se pueda utilizar una vez que se tenga experiencia. Interfaces de mens Un men es una lista de opciones que se muestran en la pantalla o en una ventana de la pantalla para que los usuarios elijan la opcin que deseen (vase ejemplo). Los mens permiten dos cosas: navegar dentro de un sistema, presentando rutas que llevan de un sitio a otro, y seleccionar elementos de una lista, que representan propiedades o acciones que los usuarios desean realizar sobre algn objeto. Las interfaces de mens aparecen cuando el ordenador se vuelve una herramienta de usuario y no slo de programadores. Las actuales interfaces grficas u orientadas a objetos siguen utilizando este tipo de interfaces (los distintos estilos de interfaces no son mutuamente exclusivos). Existen distintos tipos de mens. Los primeros fueron los mens de pantalla completa, estructurados jerrquicamente . Veamos:

Men de pantalla completa (Norton Utilities) Los mens de barra, situados en la parte superior de la pantalla, son profusamente utilizados en las aplicaciones actuales. Contienen una lista de acciones genricas que dan paso a mens desplegables donde se concretan.

366

Men de barra y men desplegable Estos mens pueden llevar a su vez a otros: son los mens en cascada. Pueden cambiar dinmicamente, y deshabilitar opciones que no estn disponibles en un momento dado (marcndolas habitualmente en gris).

Mens en cascada de la barra de inicio de Windows 95 Las paletas o barras de herramientas son mens grficos con acciones, herramientas y opciones que se pueden colocar en la pantalla. Se utilizan mucho en programas grficos.

367

Paletas de herramientas en Microsoft Powerpoint Los mens contextuales o mens pop-up son los ms recientes. Se llaman as porque el contenido del men depende del contexto de trabajo del usuario. Contienen nicamente las opciones que son aplicables al objeto seleccionado, ms algunas de uso frecuente que tambin son accesibles desde el men de barra.

Men contextual de un icono en el escritorio de Windows 95 Las interfaces de mens, bien estructuradas, son buenas para usuarios noveles o espordicos. Son fciles de aprender y de recordar. Pueden existir mens simples y avanzados, para adaptarse al tipo de usuario. Precauciones: no ocupar demasiado espacio de la pantalla, recordar la informacin acumulada de mens precedentes, no colocar demasiados elementos en el men, agruparlos de manera lgica (no en orden alfabtico, por ejemplo; esto ayuda a recordarlos), permitir la personalizacin por parte del usuario, usar una terminologa adecuada y consistente dentro del programa y con otros programas (Exit, Quit, Escape, Close, Return, Back). Las interfaces de mens sern utilizadas normalmente en conjuncin con los otros estilos de interfaces. Interfaces grficas (graphical user interfaces, GUIs)

368

Desarrolladas originalmente por XEROX (sistema Xerox Star, 1981, sin xito comercial), aunque popularizadas por Apple (Steven Jobs se inspir en los trabajos de Xerox y cre el Apple Lisa, 1983, sin xito, y Apple Macintosh, 1984, con xito debido en gran medida a su campaa publicitaria). Los tres estilos ms comunes de interfaces grficas hombre-computadora son: Lo que t ves es lo que puedes conseguir (WYSIWYG What you see is what you get), Manipulacin directa e Interfaces de usuario basados en iconos. Un GUI es una representacin grfica en la pantalla del ordenador de los programas, datos y objetos, as como de la interaccin con ellos. Un GUI proporciona al usuario las herramientas para realizar sus operaciones, ms que una lista de las posibles operaciones que el ordenador es capaz de hacer. Caractersticas de un GUI:

1. 2. 3. 4.

Posee un monitor grfico de alta resolucin. Posee un dispositivo apuntador (tpicamente un ratn). Promueve la consistencia de la interfaz entre programas. Los usuarios pueden ver en la pantalla los grficos y textos tal como se vern impresos. 5. Sigue el paradigma de la interaccin objeto-accin. 6. Permite la transferencia de informacin entre programas. 7. Se puede manipular en la pantalla directamente los objetos y la informacin. 8. Provee elementos de interfaz estndar como mens y dilogos. 9. Existe una muestra visual de la informacin y los objetos (iconos y ventanas). 10. Proporciona respuesta visual a las acciones del usuario. 11. Existe informacin visual de las acciones y modos del usuario/sistema (mens, paletas). 12. Existen controles grficos (widgets) para la seleccin e introduccin de la informacin. 13. Permite a los usuarios personalizar la interfaz y las interacciones. 14. Proporciona flexibilidad en el uso de dispositivos de entrada (teclado/ratn).
Una caracterstica importante es que el GUI permite manipular los objetos e informacin de la pantalla, no slo presentarla. Para usar un GUI, los usuarios deben conocer (o aprender) una serie de conceptos: organizacin del sistema (ficheros, directorios en Win95), diferentes tipos de iconos y efecto de las acciones sobre ellos, elementos bsicos de una ventana, uso de los controles del GUI, uso del ratn. Los GUI usan el estilo objeto-accin, en contraposicin al accin-objeto de los CUI o las interfaces de men. El usuario selecciona un objeto, y despus la accin a realizar sobre dicho objeto. Los objetos son el principal foco de atencin del usuario, lo cual resulta ms natural y prximo a su modelo mental.

369

Metfora de la cmara Interfaces orientadas a objetos (object oriented user interfaces, OOUIs) Su aspecto es similar al de las GUIs. La diferencia estriba en el modelo subyacente: las GUIs son interfaces orientadas a la aplicacin, mientras que las OOUIs estn orientadas al objeto. La tabla siguiente muestra las principales diferencias entre ambos estilos de interfaz: Interfaces orientadas a la aplicacin La aplicacin consiste en un icono, una ventana principal y varias secundarias Los iconos representan aplicaciones o ventanas abiertas Los usuarios deben abrir una aplicacin antes de trabajar con objetos Proporciona al usuario las funciones necesarias para realizar las tareas Interfaces orientadas a objetos El producto consiste en una coleccin de objetos que cooperan y vistas de dichos objetos Los iconos representan objetos que se pueden manipular directamente Los usuarios abren objetos como vistas en el escritorio Proporciona al usuario los materiales necesarios para realizar las tareas

Se centra en la tarea principal determinada Se centra en las entradas y salidas de los objetos y por la aplicacin tareas Las tareas relacionadas son soportadas por Las tareas relacionadas son soportadas por el uso de otras aplicaciones otros objetos Estructura rgida: funcin Los usuarios pueden quedar atrapados en una tarea Los usuarios deben seguir la estructura de la aplicacin Estructura flexible: objeto Los usuarios no deben quedar atrapados en una tarea Los usuarios pueden realizar tareas a su propio gusto

Se requieren muchas aplicaciones: una por Se requieren pocos objetos, que se reutilizan en tarea muchas tareas El objetivo de la OOUI es que el usuario se concentre en sus tareas en lugar de en el ordenador y cmo utilizar las aplicaciones y ficheros necesarios para cumplir sus objetivos. Por ello se esconde la organizacin del sistema al usuario (Ejemplo de los accesos directos en Windows95-OS/2). El estilo de interaccin de los OOUIs es el de objeto-accin (tambin se da en los GUIs, aunque mezclado con el estilo accin-objeto). La ventana es un objeto ventana, no una ventana de aplicacin; desaparecen pues los mens de barra y ganan terreno los contextuales.

370

Los objetos se pueden clasificar en tres categoras: datos, contenedores y dispositivos. Sobre ellos se definen distintas vistas (por ejemplo, la ayuda constituye una vista del objeto). Definir los objetos y las vistas es lo ms complicado del diseo de la interfaz. El objeto debe ser familiar al usuario (encajar con su modelo mental, apoyado en su vida diaria), y estar relacionado con el mundo real: uso de las metforas.

Distintas vistas del objeto reloj Un ejemplo de lo que se pretende con una interfaz OOUI es el considerar un documento como un objeto sobre el cual realizar tareas tales como incorporar grficos y textos, sin necesidad de usar programas distintos para cada una de ellas. Estos programas suelen tener funciones que se solapan, con el consiguiente gasto extra en espacio y dinero. Actualmente existe una mezcla de productos orientados a la aplicacin y al objeto, aunque se est produciendo una migracin a estos ltimos. Las aplicaciones estn dejando paso a conjuntos de objetos.

CARACTERSTICAS HUMANAS DEL DISEO DE INTERFAZ


Factores Humanos Al disear interfaces de usuario deben tenerse en cuenta las habilidades cognitivas y de percepcin de las personas, y adaptar el programa a ellas. As, una de las cosas ms importantes que una interfaz puede hacer es reducir la dependencia de las personas de su propia memoria, no forzndoles a recordar cosas innecesariamente (por ejemplo, informacin que apareci en una pantalla anterior) o a repetir operaciones ya realizadas (por ejemplo, introducir un mismo dato repetidas veces). La persona tiene unas habilidades distintas de la mquina, y sta debe utilizar las suyas para soslayar las de aquella (como por ejemplo la escasa capacidad de la memoria de corto alcance). Velocidad de Aprendizaje.- Se pretende que la persona aprenda a usar el sistema lo ms pronto posible. Velocidad de Respuesta.- El tiempo necesario para realizar una operacin en el sistema. Tasa de errores.- Porcentaje de errores que comete el usuario. Retencin.- Cunto recuerda el usuario sobre el uso del sistema en un perodo. de tiempo. Satisfaccin.- Se refiere a que el usuario est a gusto con el sistema.

371

Adems de stos existen otros a considerar: Adecuacin Caractersticas Fsicas.- Cada persona tiene diferentes caractersticas fsicas. Hay algunas personas que no les gustan los teclados mientras que a otras s. Es por eso que hay teclados ergonmicos. Lo mismo sucede con el mouse. Ambiente.- El lugar donde va a ser usado el sistema. Cada interfaz tiene que adecuarse al lugar. Visibilidad.- Tomar en cuenta la cantidad de iluminacin del lugar. Se refleja el brillo en la pantalla? Personalidad.- De acuerdo a la edad, nivel socio-econmico, etc. Cultura.- Los japoneses no tienen las mismas pantallas, ventanas, etc. Este factor es importante si el mercado para el sistema es a nivel internacional.

Segn la funcin tenemos: Motivacin Sistemas Vitales.- Son de vida o muerte; muchas personas dependen de ellos. Ejemplo: un sistema para reactores nucleares. Este sistema trabaja en tiempo real, y es de suma importancia la seguridad y efectividad del mismo. Sistemas Comerciales e Industriales.- Sirven para aumentar la productividad y vender ms. Sistemas de Oficina, Hogar y Juegos.- Factor importante: el mercado a quien est dirigido; tienen que ser muy amigables y satisfacer al cliente. Sistemas de Investigacin.- Realizan tareas muy especficas y tratan de imitar el medio en el que se desenvuelve el usuario.

PASOS PARA EL DISEO DE INTERFAZ


Pasos Clsicos En el proceso de diseo de una interfaz de usuario se pueden distinguir cuatro fases o pasos fundamentales: 1. 2. 3. 4. Reunir y analizar la informacin del usuario Disear la interfaz de usuario Construir la interfaz de usuario Validar la interfaz de usuario

Reunir y analizar la informacin del usuario Es decir concretar a travs de tcnicas de requerimentacin, qu tipo de usuarios van a utilizar el programa, qu tareas van a realizar los usuarios y cmo las van a realizar, qu exigen los usuarios del programa, en qu entorno se desenvuelven los usuarios (fsico, social, cultural). Disear la interfaz de usuario Es importante dedicar tiempo y recursos a esta fase, antes de entrar en la codificacin. En esta fase se definen los objetivos de usabilidad del programa, las tareas del usuario, los objetos y acciones de la interfaz, los iconos, vistas y representaciones visuales de los objetos, los mens de

372

los objetos y ventanas. Todos los elementos visuales se pueden hacer primero a mano y luego refinar con las herramientas adecuadas. Construir la interfaz de usuario Es interesante realizar un prototipo previo, una primera versin del programa que se realice rpidamente y permita visualizar el producto para poderlo probar antes de codificarlo definitivamente. Validar la interfaz de usuario. Se deben realizar pruebas de usabilidad del producto, a ser posible con los propios usuarios finales del mismo. Es importante, en suma, realizar un diseo que parta del usuario, y no del sistema. Existen 11 pasos en el proceso de diseo centrado en las tareas, similar al anterior pero que desglosa algunas actividades implcitas en otras, as: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Entender quien usar el sistema para hacer qu. Elegir tareas representativas para el diseo. Plagiar o copiar. Bosquejar un diseo. Pensar acerca del diseo. Crear un prototipo. Evaluarla con los usuarios. Repetir. Construirla. Rastrearla. Cambiarla.

Tcnicas y pasos avanzadas para el diseo de interfaces de usuario


Presentacin de informacin No se deben colocar demasiados objetos en la pantalla, y los que existen deben estar bien distribuidos. Cada elemento visual influye en el usuario no slo por s mismo, sino tambin por su combinacin con el resto de elementos presentes en la pantalla. El nmero de elementos visuales que perciben son: en el caso a) 1 (el fondo); en b) 3 (la lnea, lo que est encima y lo que est debajo); en c) son 5 (el espacio fuera del recuadro, el recuadro, la lnea y el espacio encima y debajo de sta); finalmente, en d) el nmero se eleva a 35, siguiendo el mismo criterio. Conclusin: cada elemento nuevo que se aade influye ms de lo que se piensa en el usuario.

373

Elementos de diseo de pantalla y su percepcin visual Anlisis de Color: Es probablemente el elemento de la interfaz que con ms frecuencia es mal utilizado. El color comunica informacin, no es slo decorativo (ejemplo: reforzar mensajes de error). Deben utilizarse combinaciones adecuadas (por ejemplo, las paletas proporcionadas por los sistemas operativos). El color debe atraer la atencin, pero no cansar despus de un rato de trabajo. Es especialmente importante seguir las lneas de diseo existentes. Principio bsico: disear primero en blanco y negro, y luego aadir el color. Anlisis Audio Primero es preciso ver cundo es ms apropiado que la informacin visual. Segundo, determinar el sonido adecuado. Tercero, permitir la personalizacin (volumen y desactivacin). Como en el caso de los colores existen guas de uso. En lugares de trabajo abiertos, puede ser poco efectivo; adems, puede ser embarazoso para algunas personas. El sonido debe usarse para informar, no cuando no aade nada nuevo (por ejemplo, un mensaje de aviso de correo o de bienvenida, respectivamente, al iniciar una sesin de trabajo). Anlisis Animacin Se define como un cambio en el tiempo de la apariencia visual de un elemento grfico. Ejemplos de su uso: progreso de acciones (copia de ficheros en Windows 95, instalacin de programas), estado de procesos (iconos de impresora), acciones posibles (cambios en el cursor al desplazar el ratn). La animacin puede ayudar a subrayar iconos importantes, mostrar el estado de un objeto particular o explicar su comportamiento. Diseo internacional Debe hacerse un uso adecuado de la terminologa. Hay mucho trabajo en este campo. Debe tenerse cuidado con las diferencias culturales (gestos, terminologa, dibujos, formatos de telfonos o calendarios, etc.).

374

Anlisis y Eleccin de controles Muchas veces existe la duda de qu controles utilizar. En realidad no existe una nica forma correcta. Un aspecto a considerar es la escalabilidad (men de 10/1000 elementos; ejemplo: programas del men inicio de Windows 95). Ejemplo de alternativas Usar un men de barra o de paleta, permitir arrastrar objetos o no (problema: no existe indicacin visual de que se pueda arrastrar el objeto: qu objetos se pueden arrastrar? a dnde se pueden arrastrar? qu ocurrir cuando lleguen all? se podr deshacer la accin?).

Diferentes controles para los mismos datos Guas de Expertos Existen diversas guas de diseo sacadas de expertos y comits, que complementan a las reglas de oro estudiadas anteriormente. Por citar algunas de ellas: Demasiada simetra puede hacer las pantallas difciles de leer. Si se ponen objetos sin alinear, hacerlo drsticamente. Asimetra=activo, simetra=sereno. Elementos de tamao y color similares se perciben como pertenecientes a un grupo. Asumir errores en la entrada del usuario. Disear para el usuario, no para demostrar los propios conocimientos tecnolgicos. Unos grficos espectaculares no salvarn a una mala interfaz.

CONCLUSIONES Y RECOMENDACIONES
El conocimiento de estos puntos clave, nos permitirn enfocarnos mejor al estudio de la materia. Las Interfaces de usuario, como vnculo de inmersin del hombre en el entorno de trabajo tecnolgico actual, realzan su importancia en el desarrollo de nuevos productos, ms eficaces, eficientes e interactivos, que es lo que el mercado demanda. Puntos, cmo los histricos y evolutivos, deben ser abordados de manera ms investigativa, recordemos que conocer el pasado nos proyecta al futuro.

375

Otras puntualizaciones de clasificacin obligarn a que investiguemos y propongamos, nuevas distribuciones clasificatorias, tiles a futuro en una carrera de desarrollo de software.

376

Pruebas de programas
1. Introduccin
Una de las ltimas fases del ciclo de vida antes de entregar un programa para su explotacin, es la fase de pruebas. Una de las sorpresas con las que suelen encontrar los nuevos programadores es la enorme cantidad de tiempo y esfuerzo que requiere esta fase. Se estima que la mitad del esfuerzo de desarrollo de un programa (tanto en tiempo como en gastos) se va en esta fase. Si hablamos de programas que involucran vidas humanas (medicina, equipos nucleares, etc) el costo de la fase de pruebas puede fcilmente superar el 80%. Pese a su enorme impacto en el coste de desarrollo, es una fase que muchos programadores an consideran clasificable como un arte y, por tanto, como difcilmente conceptualizable. Es muy difcil entrenar a los nuevos programadores, que aprendern mucho ms de su experiencia que de lo que les cuenten en los cursos de programacin. An siendo una tarea abocada al fracaso, voy a intentarlo.

1.1. Qu es probar?
Como parte que es de un proceso industrial, la fase de pruebas aade valor al producto que se maneja: todos los programas tienen errores y la fase de pruebas los descubre; ese es el valor que aade. El objetivo especfico de la fase de pruebas es encontrar cuantos ms errores, mejor. Es frecuente encontrarse con el error de afirmar que el objetivo de esta fase es convencerse de que el programa funciona bien. En realidad ese es el objetivo propio de las fases anteriores (quin va a pasar a la seccin de pruebas un producto que sospecha que est mal?). Cumplido ese objetivo, lo mejor posible, se pasa a pruebas. Esto no obsta para reconocer que el objetivo ltimo de todo el proceso de fabricacin de programas sea hacer programas que funcionen bien; pero cada fase tiene su objetivo especfico, y el de las pruebas es destapar errores. Probar un programa es ejercitarlo con la peor intencin a fin de encontrarle fallos. Por poner un ejemplo duro, probar un programa es equivalente a la actividad de ciertos profesores para los que examinar a un alumno consiste en poner en evidencia todo lo que no sabe. Esto es penoso cuando se aplica a personas; pero es exactamente lo que hay que hacerle a los programas.

1.2. La Prueba Exhaustiva es Imposible


La prueba ideal de un sistema sera exponerlo en todas las situaciones posibles, as encontraramos hasta el ltimo fallo. Indirectamente, garantizamos su respuesta ante cualquier caso que se le presente en la ejecucin real. Esto es imposible desde todos los puntos de vista: humano, econmico e incluso matemtico. Dado que todo es finito en programacin (el nmero de lneas de cdigo, el nmero de variables, el nmero de valores en un tipo, etc etc) cabe pensar que el nmero de pruebas posibles es finito. Esto deja de ser cierto en cuanto entran en juego bucles, en los que es fcil introducir condiciones

377

para un funcionamiento sin fin. An en el irrealista caso de que el nmero de posibilidades fuera finito, el nmero de combinaciones posibles es tan enorme que se hace imposible su identificacin y ejecucin a todos los efectos prcticos. Probar un programa es someterle a todas las posible variaciones de los datos de entrada, tanto si son vlidos como si no lo son. Imagnese hacer esto con un compilador de cualquier lenguaje: habra que escribir, compilar y ejecutar todos y cada uno de los programas que se pudieran escribir con dicho lenguaje! Sobre esta premisa de imposibilidad de alcanzar la perfeccin, hay que buscar formas humanamente abordables y ecnomicamente aceptables de encontrar errores. Ntese que todo es muy relativo y resbaladizo en este rea.

1.3. Organizacin
Hay multitud de conceptos (y palabras clave) asociadas a las tareas de prueba. Clasificarlas es difcil, pues no son mutuamente disjuntas, sino muy entrelazadas. En lo que sigue intentaremos la siguiente estructura para la presentacin: Fases de prueba: UNIDADES Planteamientos: o CAJA BLANCA Cobertura: de segmentos de ramas de condicin/decisin de bucles o CAJA NEGRA Cobertura de requisitos INTEGRACIN ACEPTACIN

La prueba de unidades se plantea a pequea escala, y consiste en ir probando uno a uno los diferentes mdulos que constituyen una aplicacin. Las pruebas de integracin y de aceptacin son pruebas a mayor escala, que puede llegar a dimensiones industriales cuando el nmero de mdulos es muy elevado, o la funcionalidad que se espera del programa es muy compleja. Las pruebas de integracin se centran en probar la coherencia semntica entre los diferentes mdulos, tanto de semntica esttica (se importan los mdulos adecuados; se llama correctamente a los procedimientos proporcionados por cada mdulo), como de semntica dinmica (un mdulo recibe de otro lo que esperaba). Normalmente estas pruebas se van realizando por etapas, englobando progresivamente ms y ms mdulos en cada prueba. Las pruebas de integracin se pueden empezar en cuanto tenemos unos pocos mdulos, aunque no terminarn hasta disponer de la totalidad. En un diseo descendente (top-down) se empieza a probar por los mdulos ms generales; mientras que en un diseo ascendente se empieza a probar por los mdulos de base.

378

El planteamiento descendente tiene la ventaja de estar siempre pensando en trminos de la funcionalidad global; pero tambin tiene el inconveniente de que para cada prueba hay que "inventarse" algo sencillito (pero fiable) que simule el papel de los mdulos inferiores, que an no estn disponibles. El planteamiento ascendente evita tener que escribirse mdulos ficticios, pues vamos construyendo pirmides ms y ms altas con lo que vamos teniendo. Su desventaja es que se centra ms en el desarrollo que en las espectativas finales del cliente. Estas clasificaciones no son las nicas posibles. Por ejemplo, en sistemas con mucha interaccin con el usuario es frecuente codificar slo las partes de cada mdulo que hacen falta para una cierta funcionalidad. Una vez probada, se aade otra funcionalidad y as hasta el final. Esto da lugar a un planteamiento ms "vertical" de las pruebas. A veces se conoce como "codificacin incremental". Por ltimo, las pruebas de aceptacin son las que se plantea el cliente final, que decide qu pruebas va a aplicarle al producto antes de darlo por bueno y pagarlo. De nuevo, el objetivo del que prueba es encontrar los fallos lo antes posible, en todo caso antes de pagarlo y antes de poner el programa en produccin.

2. Prueba de Unidades
Cmo se prueban mdulos sueltos? Normalmente cabe distinguir una fase informal antes de entrar en la fase de pruebas propiamente dicha. La fase informal la lleva a cabo el propio codificador en su despacho, y consiste en ir ejecutando el cdigo para convencerse de que "bsicamente, funciona". Esta fase suele consistir en pequeos ejemplos que se intentan ejecutar. Si el mdulo falla, se suele utilizar un depurador para observar la evolucin dinmica del sistema, localizar el fallo, y repararlo. En lenguajes antiguos, poco rigurosos en la sintaxis y/o en la semantica de los programas, esta fase informal llega a ser muy dura, laboriosa, y susceptible de dejar pasar grandes errores sin que se note. En lenguajes modernos, con reglas estrictas, hay herramientas que permiten anlisis exhaustivos de los aspectos estticos de la semntica de los programas: tipado de las variables, mbitos de visibilidad, parmetros de llamada a procedimientos, etc etc Hay asimismo herramientas ms sofisticadas capaces de emitir "opiniones" sobre un programa y alertar de construcciones arriesgadas, de expresiones muy complicadas (que se prestan a equivocaciones), etc. etc. A veces pueden prevenir sobre variables que pueden usarse antes de tomar algn valor (no inicializadas), variables que se cargan pero luego no se usan, y otras posibilidades que, sin ser necesariamente errores en s mismas, s suelen apuntar a errores de verdad. Ms adelante, cuando el mdulo parece presentable, se entra en una fase de prueba sistemtica. En esta etapa se empieza a buscar fallos siguiendo algn criterio para que "no se escape nada". Los criterios ms habituales son los denominados de caja negra y de caja blanca. Se dice que una prueba es de caja negra cuando prescinde de los detalles del cdigo y se limita a lo que se ve desde el exterior. Intenta descubrir casos y circunstancias en los que el mdulo no hace lo que se espera de l. Por oposicin al trmino "caja negra" se suele denominar "caja blanca" al caso contrario, es decir, cuando lo que se mira con lupa es el cdigo que est ah escrito y se intenta que falle. Quizs sea ms propio la denominacin de "pruebas de caja transparente".

379

2.1. Caja blanca


Sinnimos: pruebas estructurales pruebas de caja transparente

En estas pruebas estamos siempre observando el cdigo, que las pruebas se dedican a ejecutar con nimo de "probarlo todo". Esta nocin de prueba total se formaliza en lo que se llama "cobertura" y no es sino una medida porcentual de cunto cdigo hemos cubierto? Hay diferentes posibilidades de definir la cobertura. Todas ellas intentan sobrevivir al hecho de que el nmero posible de ejecuciones de cualquier programa no trivial es (a todos los efectos prcticos) infinito. Pero si el 100% de cobertura es infinito, ningn conjunto real de pruebas pasara de un infinitsimo de cobertura. Esto puede ser muy interesante para los matemticos; pero no sirve para nada. Cobertura de segmentos A veces tambin denominada "cobertura de sentencias". Por segmento se entiende una secuencia de sentencias sin puntos de decisin. Como el ordenador est obligado a ejecutarlas una tras otra, es lo mismo decir que se han ejecutado todas las sentencias o todos los segmentos. El nmero de sentencias de un programa es finito. Basta coger el cdigo fuente e ir contando. Se puede disear un plan de pruebas que vaya ejercitando ms y ms sentencias, hasta que hayamos pasado por todas, o por una inmensa mayora. En la prctica, el proceso de pruebas termina antes de llegar al 100%, pues puede ser excesivamente laborioso y costoso provocar el paso por todas y cada una de las sentencias. A la hora de decidir el punto de corte antes de llegar al 100% de cobertura hay que ser precavido y tomar en consideracin algo ms que el ndice conseguido. En efecto, ocurre con harta frecuencia que los programas contienen cdigo muerto o inalcanzable. Puede ser que este trozo del programa, simplemente "sobre" y se pueda prescindir de l; pero a veces significa que una cierta funcionalidad, necesaria, es inalcanzable: esto es un error y hay que corregirlo. Cobertura de ramas La cobertura de segmentos es engaosa en presencia de segmentos opcionales. Por ejemplo: IF Condicion THEN EjecutaEsto; END; Desde el punto de vista de cobertura de segmentos, basta ejecutar una vez, con xito en la condicin, para cubrir todas las sentencias posibles. Sin embargo, desde el punto de vista de la lgica del programa, tambin debe ser importante el caso de que la condicin falle (si no lo fuera, sobra el IF). Sin embargo, como en la rama ELSE no hay sentencias, con 0 ejecuciones tenemos el 100%.

380

Para afrontar estos casos, se plantea un refinamiento de la cobertura de segmentos consistente en recorrer todas las posibles salidas de los puntos de decisin. Para el ejemplo de arriba, para conseguir una cobertura de ramas del 100% hay que ejecutar (al menos) 2 veces, una satisfaciendo la condicin, y otra no. Estos criterios se extienden a las construcciones que suponen elegir 1 de entre varias ramas. Por ejemplo, el CASE. Ntese que si lograramos una cobertura de ramas del 100%, esto llevara implcita una cobertura del 100% de los segmentos, pues todo segmento est en alguna rama. Esto es cierto salvo en programas triviales que carecen de condiciones (a cambio, basta 1 sla prueba para cubrirlo desde todos los puntos de vista). El criterio tambin debe refinarse en lenguajes que admiten excepciones (por ejemplo, Ada). En estos casos, hay que aadir pruebas para provocar la ejecucin de todas y cada una de las excepciones que pueden dispararse. Cobertura de condicin/decisin La cobertura de ramas resulta a su vez engaosa cuando las expresiones booleanas que usamos para decidir por qu rama tirar son complejas. Por ejemplo: IF Condicion1 OR Condicion2 THEN HazEsto; END; Las condiciones 1 y 2 pueden tomar 2 valores cada una, dando lugar a 4 posibles combinaciones. No obstante slo hay dos posibles ramas y bastan 2 pruebas para cubrirlas. Pero con este criterio podemos estar cerrando los ojos a otras combinaciones de las condiciones. Consideremos sobre el caso anterior las siguientes pruebas: Prueba Prueba Prueba Prueba 1: 2: 3: 4: Condicion1 Condicion1 Condicion1 Condicion1 = = = = TRUE FALSE FALSE TRUE y y y y Condicion2 Condicion2 Condicion2 Condicion2 = = = = FALSE TRUE FALSE TRUE

Bastan las pruebas 2 y 3 para tener cubiertas todas las ramas. Pero con ellos slo hemos probado una posibilidad para la Condicin1. Para afrontar esta problemtica se define un criterio de cobertura de condicin/decisin que trocea las expresiones booleanas complejas en sus componentes e intenta cubrir todos los posibles valores de cada uno de ellos. Ntese que no basta con cubrir cada una de las condciones componentes, si no que adems hay que cuidar de sus posibles combinaciones de forma que se logre siempre probar todas y cada una de las ramas. As, en el ejemplo anterior no basta con ejecutar las pruebas 1 y 2, pues aun cuando cubrimos perfectamente cada posibilidad de cada condicin por separado, lo que no hemos logrado es recorrer las dos posibles ramas de la decisin combinada. Para ello es necesario aadir la prueba 3. El conjunto mnimo de pruebas para cubrir todos los aspectos es el formado por las pruebas 3 y 4. An as, ntese que no hemos probado todo lo posible. Por ejemplo, si en el programa nos colamos y ponemos AND donde queramos poner OR (o viceversa), este conjunto de pruebas no lo detecta. Slo queremos decir que la cobertura es un criterio til y prctico; pero no es prueba exhaustiva.

381

Cobertura de bucles Los bucles no son ms que segmentos controlados por decisiones. As, la cobertura de ramas cubre plenamente la esencia de los bucles. Pero eso es simplemente la teora, pues la prctica descubre que los bucles son una fuente inagotable de errores, todos triviales, algunos mortales. Un bucle se ejecuta un cierto nmero de veces; pero ese nmero de veces debe ser muy preciso, y lo ms normal es que ejecutarlo una vez de menos o una vez de ms tenga consecuencias indeseables. Y, sin embargo, es extremadamente fcil equivocarse y redactar un bucle que se ejecuta 1 vez de ms o de menos. Para un bucle de tipo WHILE hay que pasar 3 pruebas 1. 2. 3. 0 ejecuciones 1 ejecucin ms de 1 ejecucin

Para un bucle de tipo REPEAT hay que pasar 2 pruebas 1. 2. 1 ejecucin ms de 1 ejecucin

Los bucles FOR, en cambio, son muy seguros, pues en su cabecera est definido el nmero de veces que se va a ejecutar. Ni una ms, ni una menos, y el compilador se encarga de garantizarlo. Basta pues con ejecutarlos 1 vez. No obstante, conviene no engaarse con los bucles FOR y examinar su contenido. Si dentro del bucle se altera la variable de control, o el valor de alguna variable que se utilice en el clculo del incremento o del lmite de iteracin, entonces eso es un bucle FOR con trampa. Tambin tiene "trampa" si contiene sentencias del tipo EXIT (que algunos lenguajes denominan BREAK) o del tipo RETURN. Todas ellas provocan terminaciones anticipadas del bucle. Estos ltimos prrafos hay que precisarlos para cada lenguaje de programacin. Lo peor son aquellos lenguajes que permiten el uso de sentencias GOTO. Tampoco conviene confiarse de lo que prometen lenguajes como MODULA-2, que se supone que prohiben ciertas construcciones arriesgadas. Los compiladores reales suelen ser ms tolerantes que lo que anuncian los libros. Si el programa contiene bucles LOOP, o simplemente bucles con trampa, la nica cobertura aplicable es la de ramas. El riesgo de error es muy alto; pero no se conocen tcnicas sistemticas de abordarlo, salvo reescribir el cdigo. Y en la prctica qu hago? Tanta definicin acaba resultando un tanto acadmica e intil. En la prctica de cada da, se suele procura alcanzar una cobertura cercana al 100% de segmentos. Es muy recomendable (aunque cuesta ms) conseguir una buena cobertura de ramas. En cambio, no suele hacer falta ir a por una cobertura de decisiones atomizadas. Qu es una buena cobertura? Pues depende de lo crtico que sea el programa. Hay que valorar el riesgo (o coste) que implica un

382

fallo si ste se descubre durante la aplicacin del programa. Para la mayor parte del software que se produce en Occidente, el riesgo es simplemente de imagen (si un juego fallece a mitad, queda muy feo; pero no se muere nadie). En estas circunstancias, coberturas del 60-80% son admisibles. La cobertura requerida suele ir creciendo con el mbito previsto de distribucin. Si un programa se distribuye y falla en algo grave puede ser necesario redistribuirlo de nuevo y urgentemente. Si hay millones de clientes dispersos por varios paises, el coste puede ser brutal. En estos casos hay que exprimir la fase de pruebas para que encuentre prcticamente todos los errores sin pasar nada por alto. Esto se traduce al final en buscar coberturas ms altas. Es an ms delicado cuando entramos en aplicaciones que involucran vidas humanas (aplicaciones sanitarias, centrales nucleares, etc) Cuando un fallo se traduce en una muerte, la cobertura que se busca se acerca al 99% y adems se presta atencin a las decisiones atmicas. Tambin se suele perseguir coberturas muy elevadas (por encima del 90%) en las aplicaciones militares. Esto se debe a que normalmente van a ser utilizadas en condiciones muy adversas donde el tiempo es inestimable. Si un programa fallece, puede no haber una segunda oportunidad de arrancarlo de nuevo. La ejecucin de pruebas de caja blanca puede llevarse a cabo con un depurador (que permite le ejecucin paso a paso), un listado del mdulo y un rotulador para ir marcando por dnde vamos pasando. Esta tarea es muy tediosa, pero puede ser automatizada. Hay compiladores que a la hora de generar cdigo mquina dejan incrustado en el cdigo suficiente cdigo como para poder dejar un fichero (tras la ejecucin) con el nmero de veces que se ha ejecutado cada sentencia, rama, bucle, etc. Limitaciones Lograr una buena cobertura con pruebas de caja blanca es un objetivo deseable; pero no suficiente a todos los efectos. Un programa puede estar perfecto en todos sus trminos, y sin embargo no servir a la funcin que se pretende. Por ejemplo, un Rolls-Royce es un coche que sin duda pasara las pruebas ms exigentes sobre los ltimos detalles de su mecnica o su carrocera. Sin embargo, si el cliente desea un todoterreno, difcilmente va a comprrselo. Por ejemplo, si escribimos una rutina para ordenar datos por orden ascendente, pero el cliente los necesita en orden decreciente; no hay prueba de caja blanca capaz de detectar la desviacin. Las pruebas de caja blanca nos convencen de que un programa hace bien lo que hace; pero no de que haga lo que necesitamos.

2.2. Caja negra


Sinnimos: pruebas de caja opaca pruebas funcionales pruebas de entrada/salida pruebas inducidas por los datos

Las pruebas de caja negra se centran en lo que se espera de un mdulo, es decir, intentan encontrar casos en que el mdulo no se atiene a su especificacin. Por ello se denominan pruebas

383

funcionales, y el probador se limita a suministrarle datos como entrada y estudiar la salida, sin preocuparse de lo que pueda estar haciendo el mdulo por dentro. Las pruebas de caja negra estn especialmente indicadas en aquellos mdulos que van a ser interfaz con el usuario (en sentido general: teclado, pantalla, ficheros, canales de comunicaciones, etc etc) Este comentario no obsta para que sean tiles en cualquier mdulo del sistema. Las pruebas de caja negra se apoyan en la especificacin de requisitos del mdulo. De hecho, se habla de "cobertura de especificacin" para dar una medida del nmero de requisitos que se han probado. Es fcil obtener coberturas del 100% en mdulos internos, aunque puede ser ms laborioso en mdulos con interfaz al exterior. En cualquier caso, es muy recomendable conseguir una alta cobertura en esta lnea. El problema con las pruebas de caja negra no suele estar en el nmero de funciones proporcionadas por el mdulo (que siempre es un nmero muy limitado en diseos razonables); sino en los datos que se le pasan a estas funciones. El conjunto de datos posibles suele ser muy amplio (por ejemplo, un entero). A la vista de los requisitos de un mdulo, se sigue una tcnica algebrica conocida como "clases de equivalencia". Esta tcnica trata cada parmetro como un modelo algebrico donde unos datos son equivalentes a otros. Si logramos partir un rango excesivamente amplio de posibles valores reales a un conjunto reducido de clases de equivalencia, entonces es suficiente probar un caso de cada clase, pues los dems datos de la misma clase son equivalentes. El problema est pues en identificar clases de equivalencia, tarea para la que no existe una regla de aplicacin universal; pero hay recetas para la mayor parte de los casos prcticos: si un parmetro de entrada debe estar comprendido en un cierto rango, aparecen 3 clases de equivalencia: por debajo, en y por encima del rango. si una entrada requiere un valor concreto, aparecen 3 clases de equivalencia: por debajo, en y por encima del rango. si una entrada requiere un valor de entre los de un conjunto, aparecen 2 clases de equivalencia: en el conjunto o fuera de l. si una entrada es booleana, hay 2 clases: si o no. los mismos criterios se aplican a las salidas esperadas: hay que intentar generar resultados en todas y cada una de las clases.

Ejemplo: utilizamos un entero para identificar el da del mes. Los valores posibles estn en el rango [1..31]. As, hay 3 clases: 1. nmeros menores que 1 2. nmeros entre 1 y 31 3. nmeros mayores que 31 Durante la lectura de los requisitos del sistema, nos encontraremos con una serie de valores singulares, que marcan diferencias de comportamiento. Estos valores son claros candidatos a marcar clases de equivalencia: por abajo y por arriba. Una vez identificadas las clases de equivalencia significativas en nuestro mdulo, se procede a coger un valor de cada clase, que no est justamente al lmite de la clase. Este valor aleatorio, har las veces de cualquier valor normal que se le pueda pasar en la ejecucin real.

384

La experiencia muestra que un buen nmero de errores aparecen en torno a los puntos de cambio de clase de equivalencia. Hay una serie de valores denominados "frontera" (o valores lmite) que conviene probar, adems de los elegidos en el prrafo anterior. Usualmente se necesitan 2 valores por frontera, uno justo abajo y otro justo encima. Limitaciones Lograr una buena cobertura con pruebas de caja negra es un objetivo deseable; pero no suficiente a todos los efectos. Un programa puede pasar con holgura millones de pruebas y sin embargo tener defectos internos que surgen en el momento ms inoportuno (Murphy no olvida). Por ejemplo, un PC que contenga el virus Viernes-13 puede estar pasando pruebas de caja negra durante aos y aos. Slo falla si es viernes y es da 13; pero a quin se le iba a ocurrir hacer esa prueba? Las pruebas de caja negra nos convencen de que un programa hace lo que queremos; pero no de que haga (adems) otras cosas menos aceptables.

3. Pruebas de Integracin
Las pruebas de integracin se llevan a cabo durante la construccin del sistema, involucran a un nmero creciente de mdulos y terminan probando el sistema como conjunto. Estas pruebas se pueden plantear desde un punto de vista estructural o funcional. Las pruebas estructurales de integracin son similares a las pruebas de caja blanca; pero trabajan a un nivel conceptual superior. En lugar de referirnos a sentencias del lenguaje, nos referiremos a llamadas entre mdulos. Se trata pues de identificar todos los posibles esquemas de llamadas y ejercitarlos para lograr una buena cobertura de segmentos o de ramas. Las pruebas funcionales de integracin son similares a las pruebas de caja negra. Aqu trataremos de encontrar fallos en la respuesta de un mdulo cuando su operacin depende de los servicios prestados por otro(s) mdulo(s). Segn nos vamos acercando al sistema total, estas pruebas se van basando ms y ms en la especificacin de requisitos del usuario. Las pruebas finales de integracin cubren todo el sistema y pretenden cubrir plenamente la especificacin de requisitos del usuario. Adems, a estas alturas ya suele estar disponible el manual de usuario, que tambin se utiliza para realizar pruebas hasta lograr una cobertura aceptable. En todas estas pruebas funcionales se siguen utilizando las tcnicas de particin en clases de equivalencia y anlisis de casos lmite (fronteras).

4. Pruebas de Aceptacin
Estas pruebas las realiza el cliente. Son bsicamente pruebas funcionales, sobre el sistema completo, y buscan una cobertura de la especificacin de requisitos y del manual del usuario. Estas pruebas no se realizan durante el desarrollo, pues sera impresentable de cara al cliente; sino una vez pasadas todas las pruebas de integracin por parte del desarrollador. La experiencia muestra que an despues del ms cuidadoso proceso de pruebas por parte del desarrollador, quedan una serie de errores que slo aparecen cuando el cliente se pone a usarlo. Los desarrolladores se suelen llevar las manos a la cabeza:

385

"Pero, a quin se le ocurre usar as mi programa?" Sea como sea, el cliente siempre tiene razn. Decir que los requisitos no estaban claros, o que el manual es ambiguo puede salvar la cara; pero ciertamente no deja satisfecho al cliente. Alegar que el cliente es un intil es otra tentacin muy fuerte, que conviene reprimir. Por estas razones, muchos desarrolladores ejercitan unas tcnicas denominadas "pruebas alfa" y "pruebas beta". Las pruebas alfa consisten en invitar al cliente a que venga al entorno de desarrollo a probar el sistema. Se trabaja en un entorno controlado y el cliente siempre tiene un experto a mano para ayudarle a usar el sistema y para analizar los resultados. Las pruebas beta vienen despues de las pruebas alfa, y se desarrollan en el entorno del cliente, un entorno que est fuera de control. Aqu el cliente se queda a solas con el producto y trata de encontrarle fallos (reales o imaginarios) de los que informa al desarrollador. Las pruebas alfa y beta son habituales en productos que se van a vender a muchos clientes. Algunos de los potenciales compradores se prestan a estas pruebas bien por ir entrenando a su personal con tiempo, bien a cambio de alguna ventaja econmica (mejor precio sobre el producto final, derecho a mantenimiento gratuito, a nuevas versiones, etc etc). La experiencia muestra que estas prcticas son muy eficaces.

5. Otros tipos de pruebas


Recorridos (walkthroughs) Quizs es una tcnica ms aplicada en control de calidad que en pruebas. Consiste en sentar alrededor de una mesa a los desarrolladores y a una serie de crticos, bajo las rdenes de un moderador que impida un recalentamiento de los nimos. El mtodo consiste en que los revisores se leen el programa lnea a lnea y piden explicaciones de todo lo que no est meridianamente claro. Puede que simplemente falte un comentario explicativo, o que detecten un error autntico o que simplemente el cdigo sea tan complejo de entender/explicar que ms vale que se rehaga de forma ms simple. Para un sistema complejo pueden hacer falta muchas sesiones. Esta tcnica es muy eficaz localizando errores de naturaleza local; pero falla estrepitosamente cuando el error deriva de la interaccin entre dos partes alejadas del programa. Ntese que no se est ejecutando el programa, slo mirndolo con lupa, y de esta forma slo se ve en cada instante un trocito del listado. Aleatorias (random testing) Ciertos autores consideran injustificada una aproximacin sistemtica a las pruebas. Alegan que la probabilidad de descubrir un error es prcticamente la misma si se hacen una serie de pruebas aleatoriamente elegidas, que si se hacen siguiendo las instrucciones dictadas por criterios de cobertura (caja negra o blanca). Como esto es muy cierto, probablemente sea muy razonable comenzar la fase de pruebas con una serie de casos elegidos al azar. Esto pondr de manifiesto los errores ms patentes. No obstante, pueden permanecer ocultos errores ms sibilinos que slo se muestran ante entradas muy precisas.

386

Si el programa es poco crtico (una aplicacin personal, un juego, ...) puede que esto sea suficiente. Pero si se trata de una aplicacin militar o con riesgo para vidas humanas, es de todo punto insuficiente. Solidez (robustness testing) Se prueba la capacidad del sistema para salir de situaciones embarazosas provocadas por errores en el suministro de datos. Estas pruebas son importantes en sistemas con una interfaz al exterior, en particular cuando la interfaz es humana. Por ejemplo, en un sistema que admite una serie de rdenes (commands) se deben probar los siguientes extremos: rdenes correctas, todas y cada una rdenes con defectos de sintaxis, tanto pequeas desviaciones como errores de bulto rdenes correctas, pero en orden incorrecto, o fuera de lugar la orden nula (lnea vacia, una o ms) rdenes correctas, pero con datos de ms provocar una interrupcin (BREAK, ^C, o lo que corresponda al sistema soporte) justo despus de introducir una orden. rdenes con delimitadores inapropiados (comas, puntos, ...) rdenes con delimitadores incongruentes consigo mismos (por ejemplo, esto] Aguante (stress testing) En ciertos sistemas es conveniente saber hasta dnde aguantan, bien por razones internas (hasta cuantos datos podr procesar?), bien externas (es capaz de trabajar con un disco al 90%?, aguanta una carga de la CPU del 90?, etc etc) Prestaciones (performance testing) A veces es importante el tiempo de respuesta, u otros parmetros de gasto. Tpicamente nos puede preocupar cunto tiempo le lleva al sistema procesar tantos datos, o cunta memoria consume, o cunto espacio en disco utiliza, o cuntos datos transfiere por un canal de comunicaciones, o ... Para todos estos parmetros suele ser importante conocer cmo evolucionan al variar la dimensin del problema (por ejemplo, al duplicarse el volumen de datos de entrada). Conformidad u Homologacin (conformance testing) En programas de comunicaciones es muy frecuente que, adems de los requisitos especficos del programa que estamos construyendo, aparezca alguna norma ms amplia a la que el programa deba atenerse. Es frecuente que organismos internacionales como ISO y el CCITT elaboren especificaciones de referencia a las que los diversos fabricantes deben atenerse para que sus ordenadores sean capaces de entenderse entre s. Las pruebas, de caja negra, que se le pasan a un producto para detectar discrepancias respecto a una norma de las descritas en el prrafo anterior se denominan de conformidad u homologacin. Suelen realizarse en un centro especialmente acreditado al efecto y, si se pasan satisfactoriamente, el producto recibe un sello oficial que dice: "homologado".

387

Interoperabilidad (interoperability tesing) En el mismo escenario del punto anterior, programas de comunicaciones que deden permitir que dos ordenadores se entiendan, aparte de las pruebas de conformidad se suelen correr una serie de pruebas, tambin de caja negra, que involucran 2 o ms productos, y buscan problemas de comunicacin entre ellos. Regresin (regression testing) Todos los sistemas sufren una evolucin a lo largo de su vida activa. En cada nueva versin se supone que o bien se corrigen defectos, o se aaden nuevas funciones, o ambas cosas. En cualquier caso, una nueva versin exige una nueva pasada por las pruebas. Si stas se han sistematizado en una fase anterior, ahora pueden volver a pasarse automticamente, simplemente para comprobar que las modificaciones no provocan errores donde antes no los haba. El mnimo necesario para usar unas pruebas en una futura revisin del programa es una documentacin muy muy clara. Las pruebas de regresin son particularmente espectaculares cuando se trata de probar la interaccin con un agente externo. Existen empresas que viven de comercializar productos que "graban" la ejecucin de una prueba con operadores humanos para luego repetirla cuantas veces haga falta "reproduciendo la grabacin". Y, obviamente, deben monitorizar la respuesta del sistema en ambos casos, compararla, y avisar de cualquier discrepancia significativa. Mutacin (mutation testing) Es una tcnica curiosa consistente en alterar ligeramente el sistema bajo pruebas (introduciendo errores) para averiguar si nuestra batera de pruebas es capaz de detectarlo. Si no, ms vale introducir nuevas pruebas. Todo esto es muy laborioso y francamente artesano.

6. Depuracin (debugging)
Casi todos los compiladores suelen llevar asociada la posibilidad de ejecutar un programa paso a paso, permitindole al operador conocer dnde est en cada momento, y cunto valen las variables. Los depuradores pueden usarse para realizar inspecciones rigurosas sobre el comportamiento dinmico de los programas. La prctica demuestra, no obstante, que su uso es tedioso y que slo son eficaces si se persigue un objetivo muy claro. El objetivo habitual es utilizarlo como consecuencia de la deteccin de un error. Si el programa se comporta mal en un cierto punto, hay que averiguar la causa precisa para poder repararlo. La causa a veces es inmediata (por ejemplo, un operador booleano equivocado); pero a veces depende del valor concreto de los datos en un cierto punto y hay que buscar la causa en otra zona del programa. En general es mala idea "correr al depurador", tanto por el tiempo que se pierde buceando sin una meta clara, como por el riesgo de corregir defectos intermedios sin llegar a la raiz del problema. Antes de entrar en el depurador hay que delimitar el error y sus posibles causas. Ante una prueba que falla, hay que identificar el dominio del fallo, averiguar las caractersticas de los datos que provoca el fallo (y comprobar experimentalmente que todos los datos con esas caractersticas provocan ese fallo, y los que no las tienen no lo provocan).

388

El depurador es el ltimo paso para convencernos de nuestro anlisis y afrontar la reparacin con conocimiento de causa.

7. Plan de Pruebas
Un plan de pruebas est constituido por un conjunto de pruebas. Cada prueba debe dejar claro qu tipo de propiedades se quieren probar (correccin, robustez, fiabilidad, amigabilidad, ...) dejar claro cmo se mide el resultado especificar en qu consiste la prueba (hasta el ltimo detalle de cmo se ejecuta) definir cual es el resultado que se espera (identificacin, tolerancia, ...) Cmo se decide que el resultado es acorde con lo esperado?

Las pruebas angelicales carecen de utilidad, tanto si no se sabe exactamente lo que se quiere probar, o si no est claro cmo se prueba, o si el anlisis del resultado se hace "a ojo". Estas mismas ideas se suelen agrupar diciendo que un caso de prueba consta de 3 bloques de informacin: 1. El propsito de la prueba 2. Los pasos de ejecucin de la prueba 3. El resultado que se espera Y todos y cada uno de esos puntos debe quedar perfectamente documentado. Las pruebas de usar y tirar ms vale que se tiren directamente, an antes de usarlas. Cubrir estos puntos es muy laborioso y, con frecuencia, tedioso, lo que hace desagradable (o al menos muy aburrida) la fase de pruebas. Es mucho mas divertido codificar que probar. Tremendo error en el que, no obstante, es fcil incurrir. Respecto al orden de pruebas, una prctica frecuente es la siguiente: 1. Pasar pruebas de caja negra analizando valores lmite. Recuerde que hay que analizar condiciones lmite de entrada y de salida. 2. Identificar clases de equivalencia de datos (entrada y salida) y aadir ms pruebas de caja negra para contemplar valores normales (en las clases de equivalencia en que estos sean diferentes de los valores lmite; es decir, en rangos amplios de valores). 3. Aadir pruebas basadas en "presuncin de error". A partir de la experiencia y el sentido comn, se aventuran situaciones que parecen proclives a padecer defectos, y se buscan errores en esos puntos. Son pruebas del tipo "Me lo tema!" 4. Medir la cobertura de caja blanca que se ha logrado con las fases previas y aadir ms pruebas de caja blanca hasta lograr la cobertura deseada. Normalmente se busca una buena cobertura de ramas (revise los comentarios expuestos al hablar de caja blanca).

8. Aspectos Sicolgicos y Organizacin del Trabajo


Parecen tonteras; pero pueden cambiar radicalmente el xito de una fase de pruebas: 1. Probar es ejercitar un programa para encontrarle fallos. Jams se debera probar un programa con el nimo de mostrar que funciona; ese no es el objetivo.

389

2. Un caso de prueba tiene xito cuando encuentra un fallo. Lo gracioso no es encontrar un caso en el que el programa funciona perfectamente. Eso es, simplemente, lo normal. Lo guai es encontrar el caso en el que falla. 3. Las pruebas debe disearlas y pasarlas una persona distinta de la que ha escrito el cdigo; es la nica forma de no ser "comprensivo con los fallos". Hacer una "obra maestra" cuesta mucho esfuerzo y requiere gran habilidad. Encontrarle fallos a una "obra maestra" cuesta an ms esfuerzo y exige otro tipo de habilidad. 4. Las pruebas no pueden esperar a que est todo el cdigo escrito para empezar a pasarlas. Deben irse pasando pruebas segn se va generando el cdigo para descubrir los errores lo antes posible y evitar que se propaguen a otros mdulos. En realidad el nombre "fase de pruebas" es engaoso, pues hay muchas actividades que se desarrollan concurrentemente o, al menos, no se necesita cerrar una fase antes de pasar a la siguiente. Algunos autores llegan al extremo de afirmar que "primero hay que probar y luego codificar". Frase graciosa que se plasma en aspectos mas concretos como que el programa se escriba pensando en que hay que probarlo. 5. Si en un mdulo (o seccin de un programa, en general) se encuentran muchos fallos, hay que insistir sobre l. Es muy habitual que los fallos se concentren en pequeas zonas. Hay mil causas para que ocurra este efecto: o cdigo escrito por un programador malo o cdigo muy difcil o cdigo mal o insuficientemente especificado o cdigo escrito en un mal da, con prisas, ... Adems, cuanto ms se parchea un trozo de cdigo, tanto ms ruinoso queda y susceptible a derrumbamientos. A la larga hay que acabar tirndolo y empezando de nuevo. 6. Si se detecta un fallo aislado, puede bastar una correccin aislada. Pero si se detectan muchos fallos en un mdulo, lo nico prctico es desecharlo, disearlo de nuevo, y recodificarlo. La tcnica de ir parcheando hasta que se pasan una serie de pruebas es absolutamente suicida y slo digna del avestruz. 7. Las pruebas pueden encontrar fallos; pero jams demostrar que no los hay. Es como las bruxas: nadie las ha visto; pero haberlas, haylas. Ningn programa (no trivial) se ha probado jams al 100%. 8. Las pruebas tambin tienen fallos. Los errores son propios de los humanos: todo el mundo se equivoca. Si una prueba falla, hay que revisar tanto lo que se prueba como lo que lo prueba. No obstante, la experiencia muestra que (casi siempre) hay ms fallos el probado que en el probador.

9. Conclusiones
Probar es buscarle los fallos a un programa. La fase de pruebas absorbe una buena porcin de los costes de desarrollo de software. Adems, se muestra renuente a un tratamiento matemtico o, simplemente, automatizado. Su ejecucin se basa en metodologa (reglas que se les dan a los encargados de probar) que se va desarrollando con la experiencia. Es tediosa, es un arte, es un trabajo que requiere una buena dosis de mala intencin, y provoca difciles reacciones humanas.

390

Aunque se han desarrollado miles de herramientas de soporte de esta fase, todas han limitado su xito a entornos muy concretos, frecuentemente slo sirviendo para el producto para el que se desarrollaron. Slo herramientas muy generales como analizadores de complejidad, sistemas de ejecucin simblica y medidores de cobertura han mostrado su utilidad en un marco ms amplio. Pero al final sigue siendo imprescindible un artista humano que sepa manejarlas.

A. Bibliografia
1. Glenford J. Myers El Arte de Probar el Software (The Art of Software Testing) El Ateneo, 1983 (John Wiley & Sons, Inc. 1979) Es "el clsico" por antonomasia. Est muy bien escrito, claro y conciso. Slo adolece de cierta vejez en cuanto los ejemplos se refieren a PL/I, y otras anticuallas. 2. Barbee Teasley Mynatt Software Engineering with Student Project Guidance Prentice-Hall International Editions, 1990 Es un libro muy pragmtico, escrito por una sicloga metida a ingeniera software. No se anda por las ramas. 3. Boris Beizer Software Testing Techniques Van Nostrand Reinhold (N.Y.) 2a ed. 1990 Es como la biblia de las pruebas. Un libro quizs algo excesivo y sin duda exhaustivo sobre el tema. 4. Roger S. Pressman Software Engineering: A Practitioner's Approach McGraw-Hill Intl. Eds. 1987 No est mal, aunque quizs se enrolla un poco y no concreta. La mayor parte de los libros tratan esta fase del desarrollo de programas de formas muy peculiares, con ms rollo que ciencia y sin dejar claro lo que hay que hacer en un caso prctico. Es muy raro que los libros que se dedican a ensear un lenguaje o a ensear a programar traten seriamente este tema. Hay que ir necesariamente a libros de ingeniera software.

B. Dictionary
Aunque he intentado utilizar traducciones razonables e intuitivas de los trminos mas habitulamente utilizados, es bien cierto que lo ms frecuente es que en la prctica nos encontremos la literatura en ingls. Esta mini-diccionario intenta cubrir la terminologa anglosajona. acceptance testing alpha testing back-box testing beta testing boundary testing branch coverage pruebas de aceptacin pruebas a nivel alfa pruebas de caja negra pruebas a nivel beta pruebas de casos lmite cobertura de ramas

391

conformance testing coverage debugging decision coverage desk checking dynamic testing equivalence partitioning error-prone modules functional tests hand execution incremental coding integration testing interoperability testing loop coverage performance tests quality regression testing robustness tests segment coverage statement coverage static testing stress tests structural tests test harness testing testing in the large testing in the small unit testing validation verification white-box testing

pruebas de homologacin cobertura depuracin cobertura de decisiones pruebas de despacho pruebas dinmicas particiones de equivalencia mdulos sospechosos pruebas funcionales ejecucin manual codificacin incremental pruebas de integracin pruebas de interoperabilidad cobertura de bucles pruebas de prestaciones calidad pruebas de regresin pruebas de robustez cobertura de segmentos cobertura de sentencias pruebas estticas pruebas de robustez pruebas estructurales banco de pruebas pruebas pruebas a escala industrial pruebas a pequea escala pruebas de unidades validacin verificacin pruebas de caja blanca

C. Caso Prctico
Los ejemplos de pruebas de programas suelen irse a uno de dos extremos: o son triviales y no se aprende nada, o son tan enormes que resultan tediosos. El ejemplo elegido para esta seccin pretende ser comedido, a costa de no contemplar mas que un reducido espectro de casos. Nos dan para probar un procedimiento PROCEDURE Busca (C: CHAR; V: ARRAY OF CHAR): BOOLEAN; A este procedimiento se le proporciona un caracter C y un array V de caracteres. El ARRAY debe estar ordenado alfabticamente, en orden ascendente. El procedimiento devuelve TRUE si C est en V, y FALSE si no. Trabajamos en Modula-2. Lo primero que hay que hacer es identificar clases de equivalencia sobre su interfaz:

392

C: CHAR El parmetro C est muy poco especificado. Slo se dice que es un caracter, lo que queda de lo ms ambiguo pues esto significa conjuntos diferentes dependiendo del ordenador. V: ARRAY OF CHAR No se dice nada del conjunto de caracteres posibles (como en C), ni de las simensiones lmite del ARRAY. Tampoco se dice nada del criterio de ordenacin. resultado: BOOLEAN ste si est perfectamente claro.

Para probar algo necesitamos saber ms. La nica forma es tener una charla con el que especific la funcin y aclarar estos extremos. Todas estas aclaraciones deben quedar recogidas por escrito en una nueva versin de la especificacin: A este procedimiento se le proporciona un caracter C y un array V de caracteres. Se admitir cualquier caracter de 8 bits de los representables en un PC con Modula-2. El ARRAY podr tener entre 0 y 10.000 caracteres y estar ordenado alfabticamente, en orden ascendente. El orden de los caracteres es el proporcionado por el Modula-2 sobre el tipo CHAR. Es admisible cualquier cadena de caracteres construida segn el convenio de Modula-2 para este tipo de datos. El procedimiento devuelve TRUE si C est en V, y FALSE si no. Trabajamos en Modula-2. Con estas explicaciones identificamos las siguientes clases de equivalencia C: CHAR 1. Cualquier caracter V: ARRAY OF CHAR 1. El ARRAY vacio. 2. Un ARRAY entre 1 y 10.000 elementos, ambos inclusive, ordenado. 3. Un ARRAY entre 1 y 10.000 elementos, ambos inclusive, desordenado. resultado: BOOLEAN 1. TRUE 2. FALSE

Por ltimo, cabe considerar combinaciones significativas de datos de entrada: que C sea el primero o el ltimo del ARRAY.

1. Pruebas de caja negra: valores lmite 1. Buscar el caracter 'k' en el ARRAY "" Debe devolver FALSE. 2. Buscar el caracter 'k' en el ARRAY "k" Debe devolver TRUE. 3. Buscar el caracter 'k' en el ARRAY "j" Debe devolver FALSE. 4. Buscar el caracter 'k' en el ARRAY "kl" Debe devolver TRUE. 5. Buscar el caracter 'k' en el ARRAY "jk" Debe devolver TRUE. 6. Buscar el caracter 'k' en el ARRAY de 10.000 "a" Debe devolver FALSE. Vamos a olvidar de momento las posibles pruebas referentes a la ordenacin del ARRAY. 2. Pruebas de caja negra: valores normales 1. Buscar el caracter 'k' en el ARRAY "abc" Debe devolver FALSE. 2. Buscar el caracter 'k' en el ARRAY "jkl" Debe devolver TRUE. Para pasar a caja blanca necesitamos conocer el cdigo interno:

393

1 2 3 4 5 6 7 8 9 10 11 12 13 14

PROCEDURE Busca (C: CHAR; V: ARRAY OF CHAR): BOOLEAN; VAR a, z, m: INTEGER; BEGIN a:= 0; z:= Str.Length (V) -1; WHILE (a <= z) DO m:= (a+z) DIV 2; IF V[m] = C THEN RETURN TRUE; ELSIF V[m] < C THEN a:= m+1; ELSE z:= m-1; END; END; RETURN FALSE; END Busca;

Es laborioso; pero si nos molestamos en ejecutar todas las pruebas anteriores marcando por dnde vamos pasando sobre el cdigo, nos encontraremos con que hemos ejecutado todas las sentencias con excepcin de la rama de la lnea 10. Para atacar este caso necesitamos un caso de prueba adicional de caja blanca 3. Pruebas de caja blanca: 1. Buscar el caracter 'k' en el ARRAY "l" Debe devolver FALSE. Con el conjunto de pruebas que llevamos hemos logrado una cobertura al 100% de segmentos y de condiciones. Respecto del bucle, la prueba 1.1 lo ejecuta 0 veces, y las dems pruebas 1 o ms veces. El conjunto de pruebas identificado se puede traducir en un banco de pruebas con el siguiente aspecto: IF IF IF IF IF IF IF IF IF Busca NOT Busca Busca NOT Busca NOT Busca Busca Busca NOT Busca Busca ('k', ('k', ('k', ('k', ('k', ('k', ('k', ('k', ('k', "") "k") "j") "kl") "jk") aaaa) "abc") "jkl") "l") THEN THEN THEN THEN THEN THEN THEN THEN THEN IO.WrStr IO.WrStr IO.WrStr IO.WrStr IO.WrStr IO.WrStr IO.WrStr IO.WrStr IO.WrStr ("falla ("falla ("falla ("falla ("falla ("falla ("falla ("falla ("falla 1.1"); 1.2"); 1.3"); 1.4"); 1.5"); 1.6"); 2.1"); 2.2"); 3.1"); END; END; END; END; END; END; END; END; END;

An podramos pasar algunas pruebas ms para comprobar la solidez del programa. Concretamente, sera bueno considerar qu ocurre si sobrepasamos el tamao mximo de 10.000 caracteres o si el ARRAY estuviera desordenado. La especificacin del mdulo no dice nada de esto, por lo que el anlisis del resultado es vidrioso. Sobre el cdigo concreto podemos apreciar que el tamao del ARRAY puede llegar hasta el mximo entero soportable por la implementacin de Modula-2 que estemos usando. Sobrepasado este lmite se puede producir un error de asignacin fuera de rango en la lnea 5. Por otra parte, si el ARRAY est desordenado, el resultado es arbitrario, aunque la funcin siempre termina devolviendo TRUE o FALSE.

394

Curso de Fortran
TEMA 1: ESTRUCTURAS Y ELEMENTOS DEL LENGUAJE FORTRAN.
CARACTERES DEL LENGUAJE FORTRAN. -El lenguaje Fortran tiene unos nmeros y signos que utiliza y que funcionan como caracteres o letras, siendo los caracteres permitidos por este lenguaje los siguientes: -Letras de la A a la Z (Tanto maysculas como minsculas). -Nmeros del 0 al 9. -Caracteres de puntuacin ., ;. -Caracteres matemticos +, *, /, -. -Caracteres especiales $, , =, <, >, (), :, , y el blanco. -El lenguaje Fortran no distingue en la sintaxis las letras maysculas y minsculas salvo en el caso de los literales, y los signos de la comparacin no los suele utilizar. -Las nuevas versiones de Fortran usan todos los cdigos relacionales pero con la excepcin de que forman parte de los literales. -Semntica, es lo que define el contenido de ciertas palabras y un conjunto de palabras reservadas cada una con un cometido especial. En este lenguaje hay un conjunto pequeo de palabras reservadas. -No existen palabras reservadas como tal, sino palabras clave, sabiendo que cualquier palabra puede ser un identificador vlido. -Con objeto de dar nombre a las cosas que se manipulan (Mdulo, ficheros estructuras, constantes, etc.) se establecen unas normas que son: -El carcter de una palabra debe ser un carcter alfabtico, que aparte de las 52 letras considera como caracteres alfabticos los smbolos $ y . -El nmero mximo de caracteres que se pueden utilizar son 31, aunque el compilador slo reconoce los seis primeros. -El nombre de los objetos debe tener un significado sobre el objeto que se est tratando (Que tenga sentido), se debe hacer una normalizacin de los nombres. FORMATO DEL LENGUAJE FORTRAN. -El lenguaje Fortran no est formateado. Las cinco primeras columnas son columnas reservadas para las etiquetas (Un nmero desde el uno hasta el 9) y referenciar una lnea.

395

-La etiqueta comprender un nmero del 1 al 99999 entero y sin signo, que deber ser nica y ocupar memoria. -No tiene por qu estar ordenada pero no se puede poner una etiqueta ms de una vez en un programa (Recomendable colocar la etiqueta para los formatos). -La sexta columna ir en blanco y slo se utiliza para indicar que la lnea que se est tratando es una lnea de continuacin (Cualquier carcter es vlido). -Como mximo se pueden continuar 19 lneas de programa y hay que hacer una indicacin. -Las columnas 7 a 72 sirven para colocar el cdigo fuente. Todas las sentencias se clasificarn en dos grupos: -Ejecutables (Especifican la accin y generan una instruccin). -No ejecutables (Informacin y naturaleza de los datos). -Las columnas 73 a 80 sirven para realizar todo tipo de comentarios, entre los cuales pueden figurar: -Nombre del programador. -Versin del programa. -Fecha del programa. -Descripcin del programa. -E/S del programa. -Variables y qu representan. -La primera columna puede llevar un conjunto de caracteres con un significado especial que sern la c, C, * y el smbolo $. -Los comentarios no pueden ir entre lneas de continuacin pero pueden estar en cualquier parte del programa excepto la anteriormente citada. TIPOS DE MODULOS. -Para referenciar un programa principal en Fortran se utilizar el siguiente formato, teniendo en cuenta que dentro del programa principal irn las acciones a cumplir: - PROGRAM Identificador accin1 accin2 .......

396

accinn END -El especificador o identificador del programa puede ser omitido y no es necesario colocarlo dentro de un programa. -FUNCTION Identificador (Parmetros) accin1 accin2 ....... accinn END -Se utilizar la estructura de Funcin cuando se necesite devolver o no un valor, teniendo en cuenta que la lista de los parmetros pasados deben ser separados por comas. -SUBROUTINE Identificador (Parmetros) accin1 accin2 ....... accinn END -La Subrutina o Procedimiento se utilizar en aquellos casos en los que se necesite establecer una relacin de ms de un tipo de dato. La lista de parmetros pasados deben ser separados por comas. -BLOCK DATA Identificador accin1 accin2 ....... accinn END

397

-Esta estructura representa un conjunto de instrucciones meramente descriptivas que sern datos comunes o aparte de los Subprogramas y se pueden inicializar. -Estos cuatro elementos se pueden colocar a partir del programa principal o se pueden ejecutar y compilar separadamente. -El lenguaje no necesita que una variable est declarada anteriormente sino que tienen una declaracin implcita (Lo distingue por la primera letra). TIPOS DE DATOS. -Como norma general el lenguaje no utiliza tipos de datos. DATOS DE TIPO ENTERO. -Son todas las variables que comiencen por una letra que est en el intervalo I-N, sern variables tomadas como enteras si no se declara explcitamente de otro tipo o tambin se pueden declarar como uno de los siguientes: -INTEGER *1 (Asigna al entero un byte de longitud). -INTEGER *2 (Asigna al entero dos bytes de longitud). -INTEGER *4 (Asigna al entero cuatro bytes de longitud). -Si despus de la palabra reservada INTEGER no se pone nada la longitud por defecto de la variable declarada sern 4 bytes. -El separador usado suele ser la coma y para definir varias variables de tipo entero se pueden usar los siguientes formatos: -INTEGER *1 Cont, Cont1, Cont2 (Declara 3 variables enteras). -Signo 2..36 0..9 A..Z . - Nmero (Indica base 16 y es perfectamente vlido). -El segundo formato define un nmero en otra base que no sea base decimal, siendo el smbolo el que indica la base en la que se va ha representar el nmero. DATOS DE TIPO REAL. -Es el nmero manipulado por excelencia en Fortran. Hay dos operaciones de tipo general que son la coma fija y la coma flotante. -Los formatos que utilizan los nmeros reales sern: -Signo parte entera. parte fraccionaria (C. fija).

398

-Signo parte entera E signo parte fraccionaria (C. flotante) -Para declarar una variable de tipo real se usar uno de los siguientes formatos: -REAL *4 (Asigna al real cuatro bytes de longitud). -REAL *8 (Asigna al real ocho bytes de longitud). -El error que se comete al usar cuatro bytes de longitud se producir en las siete u ocho cifras despus de la coma. -Cuando se utiliza un formato de cuatro bytes estamos ante un nmero de simple precisin. -El real declarado como ocho bytes abarca despus de la coma 14 o 15 cifras significativas para la parte entera del nmero que es aproximadamente el doble del formato de cuatro bytes. -Cuando se utiliza un formato de ocho bytes estamos ante un nmero de doble precisin. DATOS DE TIPO COMPLEJO. -Este tipo de datos consta de una parte real y de una parte que es imaginaria y en su representacin se asumir la parte real y se quedar con la parte imaginaria. -Los formatos que se utilizan para los nmeros complejos sern: -Complejo (Real, Imaginario). -Para declarar una variable de tipo complejo se usarn uno de los siguientes formatos: -COMPLEX *4 (Asigna al complejo cuatro bytes de longitud). -COMPLEX *8 (Asigna al complejo ocho bytes de longitud). -Despus de la palabra reservada COMPLEX puede aparecer un signo, y la parte real e imaginaria irn entre comas). -Al igual que en otros tipos de datos cuatro bytes sern simple precisin y ocho bytes sern doble precisin. DATOS DE TIPO CARACTER. -Estas variables se representan por la palabra reservada CHARACTER y el formato que utiliza Fortran para definir las variables de este tipo son: -CHARACTER *Nmero . -En este formato, nmero nos indica la longitud en caracteres de la variable, siendo el tamao mximo que puede tomar un campo carcter desde 256 hasta 32767 caracteres.

399

-Este formato define el tratamiento de cadenas. DATOS DE TIPO LOGICO. -Las variables de tipo lgico se representan mediante la palabra reservada LOGICAL con las variables separadas por comas y con un formato como el siguiente: -LOGICAL *2 (Asigna al campo dos bytes de longitud). -LOGICAL *4 (ASigna al campo cuatro bytes de longitud). -Los valores que puede tomar una variable de tipo lgico son los tpicos de True y False. DATOS DE TIPO REGISTRO. -Para este tipo de variables tenemos que definir previamente una estructura y sus correspondientes campos que se van ha tomar, siendo dicha estructura del tipo: -STRUCTURE Identificador CHARACTER *25 Campo1 INTEGER Campo2 REAL Campo3 LOGICAL Campo4 ..................... END STRUCTURE -La implementacin del registro que podra usarse con la estructura representada podra ser la siguiente: -RECORD Identificador1 Identificador2 (Nmero). -Donde identificador1 representa el nombre de la estructura creada, identificador2 ser el nombre del registro y Nmero ser el nmero de elemento del registro. -Si por ejemplo deseamos referenciar a un real del registro nmero 25 tendremos que utilizar la siguiente forma: -Identificador2 (25).Campo3. CONSTANTES. -Para definir una constante en Fortran deberemos utilizar el siguiente formato: -PARAMETER (Identificador=constante, ...).

400

-La lista de constantes ir separada por comas. Las constantes podrn ser de tipo numrico o de tipo Hollerith (Cadena de caracteres con cualquier carcter imprimible). Tambin podrn ser expresiones. -Las constantes numricas podrn tener los siguientes formatos: -Sin punto decimal ni coma. -Precedida por un signo, en los enteros. -Coma fija con punto decimal, en los reales. -Coma flotante con exponente entero, en los reales. -Simple precisin (n.m, n., .m, n.mEe, n.E+e, nE+e). -Doble precisin (n.mD+e, n.De, mDe, nDe). -Compleja como un par ordenado entre parntesis. -Lgicas .TRUE. y .FALSE.. -De caracteres. -La constante Hollerith podr tener el siguiente formato: -Nmero H carcter. -Una constante simblica definida en PARAMETER puede aparecer como una expresin o como un valor de DATA. VARIABLES. -Para definir una variable el compilador conoce ya de antemano si la variable va a ser de tipo real o de tipo entera, siendo por defecto de tipo real. -La estructura que debern seguir las declaraciones de las variables en un programa Fortran ser la siguiente: -PROGRAM Identificador PARAMETER (Lista de constantes) *VARIABLES INTEGER Lista de variables REAL Lista de variables COMPLEX Lista de variables

401

CHARACTER Lista de variables LOGICAL Lista de variables RECORD Tipos *INICIO ....... -Un atributo es una palabra reservada que acompaa a la variable y sirve para que el programa realice un mejor aprovechamiento de memoria, mejor dimensionamiento de arrays dinmicos, etc. -Si la variable comienza con una letra que se encuentre en el intervalo I-N se tratar de una variable de tipo entera. Anlogamente si comienza con una letra del intervalo A-H, O-Z se tratar de una variable real. -Como norma general, una variable debe ser declarada antes de que pueda utilizarse. Para inicializar una lista de variables a determinados valores se usar el siguiente formato: -DATA Lista de variables, lista de constantes. -Esta sentencia debe preceder a cualquier sentencia ejecutable. Una constante seguida de un asterisco indicar repeticin. EXPRESIONES. -En las expresiones aritmticas sabemos que si se realiza una operacin de un entero con un real, siempre se codificar el entero como real y luego se realiza la operacin. -Se convierte el tipo que contiene al otro, realizndose siempre con la mxima precisin. Los operandos que se van a usar en las expresiones aritmticas sern los siguientes: -** (Operador potencia). -/ (Operador divisin real). -* (Operador producto). -- (Operador resta). -+ (Operador suma). -() (Parntesis). -Para el uso de operadores relacionales, Fortran no admite como unos operadores relacionales los signos siguientes: -<=, >=, <, >, <>. -Los operadores relacionales que estn permitidos en este lenguaje sern los siguientes:

402

-.LT. (Operador menor que). -.LE. (Operador menor o igual que). -.NE. (Operador distinto de). -.GT. (Operador mayor que). -.GE. (Operador mayor o igual que). -.EQ. (Operador igual que). -Una expresin relacional compara los valores de dos expresiones de tipo aritmtico o de caracteres y el resultado es de tipo lgico. Los formatos que puede tomar una sentencia relacional sern: -Variable operador variable. -Variable operador constante. -Las variables no han de ser necesariamente variables, tambin pueden ser expresiones de todo tipo. -En cuanto a los operadores lgicos que estn permitidos por Fortran se encuentran los siguientes: -.NOT. (Operador negacin). -.AND. (Operador producto lgico). -.OR. (Operador suma lgica). -.XOR. (Operador suma exclusiva lgica). -.EQV. (Operador de equivalencia lgica). -.NEQV. (Operador de no equivalencia lgica). -El resultado de la evaluacin de una expresin de tipo lgico ser un valor de tipo lgico, pudiendo ser el formato de la expresin: -Operando1 operador operando2. -El orden de prioridad establecido para la creacin de variables en lenguaje Fortran ser por tanto el siguiente: -Expresiones aritmticas. -Expresiones de caracteres. -Expresiones relacionales.

403

-Expresiones lgicas. -Para la declaracin de variables se utiliza la palabra reservada IMPLICIT, que toma una variable cualquiera a no ser que se indique lo contrario. El formato que utiliza la instruccin es el siguiente: -IMPLICIT REAL (A-Z). -El formato anterior indica que todas las variables que empiecen desde la a hasta la z sean tratadas como reales de forma implcita. Una variable suele ser tratada siempre como un real. FINAL DE UN PROGRAMA. -Hay dos formas de terminar la ejecucin de un programa, lgicamente o fsicamente. -Para terminar la ejecucin de un programa lgicamente se utiliza el siguiente formato: -STOP Nmero. -Nmero es la constante de caracteres o constante entera sin signo de cinco dgitos, o puede ser un mensaje de error encerrado entre dos comillas. -Para terminar la ejecucin de un programa fsicamente se utiliza el siguiente formato: -END. ENTRADA Y SALIDA DE DATOS. -Para la introduccin de datos se utiliza la palabra reservada READ con el formato siguiente: -READ (Nmero, Etiqueta). -Donde nmero es el nmero que indica la unidad de entrada, colocando el valor * si no se quiere poner nada. Etiqueta indica la etiqueta que va ha definir un formato, si no se quiere poner nada se usa el valor *. -Para la lectura de elementos con READ las variables se separan por un blanco o por comas, si se ha de introducir una frase o algo de tipo carcter, ir encerrado entre comillas. -Si no hay suficientes datos para esta instruccin se produce un error y el programa aborta. -Para la salida de datos se utilizan dos mtodos igualmente vlidos, que son dos palabras reservadas y que tienen el siguiente formato: -PRINT *, Lista de variables (Separadas por comas). -WRITE (*,*), Lista de variables (Separadas por comas). -La lista de variables pueden ser tanto variables, como constantes como expresiones de todo tipo. El cursor avanza a la siguiente lnea. Este tipo de sentencias se suelen incluir antes de una sentencia READ.

404

TEMA 2: FUNCIONES INTRINSECAS.


CONCEPTO DE FUNCION. -Son aquellas funciones incorporadas al compilador, que son accesibles desde un programa. Pueden ser llamadas en un programa dando el identificador de la Funcin seguido por sus argumentos entre parntesis. -Estas funciones pueden tener uno o varios argumentos y se caracterizan porque: -El nombre y sus valores de entrada son uno mismo. -Nunca puede ser usada en el lado izquierdo de la asignacin. -El nombre determina el tipo de salida de la Funcin. -Los argumentos son del mismo tipo que la Funcin. -Los argumentos pueden ser expresiones de todo tipo. -El formato de la Funcin ser el siguiente: -Nombre (Identificador1, identificador2, ..., identificadorn). FUNCIONES ARITMETICAS. -Son aquellas que realizan algn tipo de operacin matemtica. -Calcula el valor absoluto de un argumento real y devuelve un resultado real y positivo: -ABS (Expresin numrica). -Convierte un real a un entero truncndolo o eliminando la parte decimal devuelve un entero: -Var=INT (Expresin numrica). -INT (10**VarN*VarX)/10**VarN (N son los decimales a truncar). -INT (10**VarN*VarX+0.5)/10**VarN (N son los decimales deseados). -NINT (Expresin numrica) (Realiza un redondeo). -Convierte un nmero entero a un real: -FLOAT (Expresin numrica). -Eleva un nmero real a la ensima potencia donde e es la base del logaritmo natural, devuelve un real: -EXP (Nmero).

405

-Var=EXP (Nmero). -DEXP (Nmero) (Donde nmero es de doble precisin). -CEXP (Nmero) (Donde nmero es un nmero complejo). -Calcula el logaritmo natural de base e y es la inversa de la Funcin anterior, devuelve un real: -Var=LOG (Expresin numrica). -Calcula el logaritmo decimal o de base diez del argumento indicado, devuelve un real: -Var=LOG10 (Expresin numrica). -Calcula la raz cuadrada del argumento indicado, devuelve un real: -Var=SQRT (Expresin numrica). -Devuelve el mayor valor de una serie de argumentos indicados: -MAX0 (Lista de identificadores) (Devuelve un entero). -AMAX1 (Lista de identificadores) (Devuelve un real). -DMAX1 (Lista de identificadores) (Devuelve un doble precisin). -Devuelve el menor valor de una serie de argumentos indicados: -MIN0 (Lista de identificadores) (Devuelve un entero). -AMIN1 (Lista de identificadores) (Devuelve un real). -DMIN1 (Lista de identificadores) (Devuelve un doble precisin). -Calcula el mdulo o resto de dos nmeros, devuelve un entero: -MOD (Nmero1, nmero2). -Transfiere el signo del segundo identificador al primero: -SIGN (Expresin1, expresin2). -Convierte un nmero de doble precisin a un nmero real: -SNGL (Nmero). FUNCIONES TRIGONOMETRICAS. -Son aquellas que realizan alguna operacin con las razones de tipo trigonomtrico. -Calcula el seno de una expresin dada, devuelve un real y debe estar entre 1 y -1:

406

-Var=SIN (Expresin numrica) (Devuelve un real). -DSIN (Expresin numrica) (Devuelve un doble precisin). -CSIN (Expresin numrica) (Devuelve un complejo). -Calcula el coseno de una expresin dada, devuelve un real y debe estar entre 1 y -1: -Var=COS (Expresin numrica) (Devuelve un real). -DCOS (Expresin numrica) (Devuelve un doble precisin). -CCOS (Expresin numrica) (Devuelve un complejo). -Calcula la tangente de una expresin dada, devuelve un real: -Var=TAN (Expresin numrica) (Devuelve un real). -DTAN (Expresin numrica) (Devuelve un doble precisin). -Calcula el arcoseno de una expresin dada, devuelve un real: -Var=ASIN (Expresin numrica). -Calcula el arcocoseno de una expresin dada, devuelve un real: -Var=ACOS (Expresin numrica). -Calcula el arcotangente de una expresin dada, devuelve un real: -Var=ATAN (Expresin numrica). -Calcula el seno hiperblico de una expresin dada, devuelve un real: -Var=SINH (Expresin numrica). -Calcula el coseno hiperblico de una expresin dada, devuelve un real: -Var=COSH (Expresin numrica). -Calcula la tangente hiperblico de una expresin dada, devuelve un real: -Var=TANH (Expresin numrica). FUNCIONES DE NUMEROS COMPLEJOS. -Realiza diferentes funciones con los nmeros complejos. -Toma la parte real de un argumento complejo: -REAL (Expresin compleja).

407

-Toma la parte imaginaria de un argumento complejo: -AIMAG (Expresin compleja). -Calcula el conjugado de un argumento complejo: -CONJ (Expresin compleja). -Representa un argumento complejo tomado de nmeros reales: -COMPLEX (Nmero1, nmero2).

TEMA 3: ESTRUCTURAS DE CONTROL SELECTIVAS Y REPETITIVAS.


ESTRUCTURAS DE BIFURCACION INCONDICIONAL. -Este tipo de estructuras son unas herramientas que sirven para confeccionar las estructuras selectivas. No son recomendables y slo se mantienen por compatibilidad con otras versiones. -La siguiente estructura es un IF aritmtico, en el que se realiza la eleccin de tres posibles etiquetas. Se evala la condicin y en Funcin de esa condicin se tomarn los siguientes valores: -Condicin <0 toma Etiqueta1. -Condicin =0 toma Etiqueta2. -Condicin >0 toma Etiqueta3. -En este formato se pueden colocar dos etiquetas iguales, siendo el formato de la instruccin: -IF (Expresin aritmtica) Etiqueta1, Etiqueta2, Etiqueta3. -La siguiente instruccin se usar en el caso de que haya sentencias repetitivas que no estn implementadas en el compilador, siendo su misin la de transferir el control a la sentencia especicada en la etiqueta: -GOTO Etiqueta. -La siguiente instruccin transfiere el control a la etiqueta ensima de la lista segn un valor entero. Es el GOTO calculado. -En esta sentencia se evala la expresin entera entre uno y el nmero de etiquetas, y se bifurcar a la etiqueta indicada para ejecutar un conjunto de sentencias segn la expresin entera. Su formato es: -GOTO (Etiqueta1, etiqueta2, ..., etiquetan) Expresin entera. -La instruccin GOTO asignado tiene como formato el siguiente: -ASSIGN Constante entera TO Variable entera.

408

-GOTO Variable entera (Etiqueta1, etiqueta2, ..., etiquetan). -Esta instruccin produce una estructura multibifurcacin. La variable debe tener una de las etiquetas de GOTO y su funcionamiento es igual que la anterior. -Para llevar la variable a la etiqueta se usa la opcin de ASSIGN. Si la variable no est asignada el GOTO no es vlido. ESTRUCTURAS ALTERNATIVAS. -Dentro de las estructuras alternativas hay tres tipos. -La estructura alternativa simple, realiza un conjunto de acciones si la condicin es verdadera y sigue el flujo de control secuencial si la condicin es falsa. -En las estructuras alternativas todas las condiciones se colocan siempre entre parntesis. El formato de la alternativa simple es: -IF (Expresin lgica) Sentencia. -La expresin lgica est formada por una expresin con relacionales y la sentencia a continuacin no pueden ser las palabras reservadas DO, ELSEIF, ELSE, ENDIF, END o IF. -La estructura alternativa doble, selecciona una de dos opciones. Si la condicin es verdadera se ejecutan unas acciones y si es falsa otras acciones. El formato de esta estructura sera: -IF (Condicin) THEN accin1 accin2 ....... accinn ENDIF -Esta ltima estructura se puede anidar con varias sentencias IF, y su estructura quedara: -IF (Condicin) THEN accin1 accin2 ....... accinn ELSE

409

accinn+1 accinn+2 ......... accinn+n .............. ENDIF -Si se quieren anidar varios niveles de sentencias IF-ELSE usaremos la siguiente estructura: -IF (Condicin) THEN accin1 accin2 ....... accinn ELSEIF (Condicin2) THEN accinn+1 accinn+2 ......... accinn+n .................... ELSE accinm+1 accinm+2 ......... accinm+n ENDIF -La estructura alternativa mltiple se utiliza cuando la condicin tiene ms de dos valores con seleccin mltiple. El valor de su expresin a de ser un entero, carcter o lgico.

410

-Si el carcter utilizado es numrico no ir entre comillas, mientras que si es de tipo carcter ir entre comillas. Esta ltima medida exige que el cdigo ASCII del primer carcter sea menor que el segundo. -El formato de la estructura ser el siguiente: -SELECT CASE (Expresin) CASE (Caso1) accin1 accin2 ....... accinn CASE (Caso2) accinn+1 accinn+2 ......... accinn+n ............... CASE DEFAULT accinm+1 accinm+2 ......... accinm+n END SELECT CONCEPTO DE BUCLE. -Consiste en una estructura de control que gobierna los procesos de tipo repetitivo dentro de un programa. Repite una secuencia de acciones mientras o hasta una condicin sea verdadera o falsa. -Iteracin de un bucle es la repeticin de sentencias interiores que hay dentro de un bucle. ESTRUCTURA REPETITIVA. SENTENCIA "DO".

411

-Esta estructura contiene dos formatos. El primer formato se utiliza cuando se conoce el nmero de iteraciones y su formato es: -DO Etiqueta VarA=Inicio, Final, Incremento . -Las variables se separan por comas y el incremento puede ser positivo o negativo y es opcional. Normalmente inicio debe ser menor que la variable final. -El otro formato es lo mismo que una estructura de tipo repetir. Se usa cuando se desea repetir una condicin un nmero de veces siendo una por defecto. Su formato es: -DO Etiqueta VarA=Inicio, Final, Incremento accin1 accin2 ....... accinn Etiqueta CONTINUE ENDDO -Las variables pueden ser enteras, constantes o expresiones, y es ms rpida y potente con variables y constantes con lo que se evitan los errores de redondeo. -Como mnimo se ejecuta una vez. Ejecuta un conjunto de instrucciones hasta que se encuentra a la etiqueta, y si no se usa la etiqueta se deber cerrar el bucle con ENDDO. -Por defecto el incremento del bucle es uno. Si se usa la etiqueta habr que tener en cuenta una serie de restricciones: -No se puede usar un GOTO incondicional o asignado. -No se puede usar ELSE, IF, SELECT CASE, ENDIF. -No puede seguirle un ENDSELECT, EXIT, RETURN o STOP. -Para evitar estas restricciones bastar con colocar una sentencia en blanco. Si se quiere usar una variable ndice despus de salir la variable ndice la variable tendr el valor final ms el incremento. ESTRUCTURA REPETITIVA. SENTENCIA "WHILE". -Utiliza una condicin que puede ser una expresin lgica o relacional. Esta condicin se evala antes y despus de cada ejecucin del bucle. Si la condicin es falsa no se ejecuta nunca. -Los bucles deben terminar siempre y pueden ser controlados por:

412

-Contador, necesita una variable dentro del bucle que se debe inicializar, comprobar e incrementar. -Centinela, es un valor especial para el final de una lista y es un valor que jams se procesa, pero debe ser del mismo tipo que los datos. -Interruptor, es una variable lgica. -El formato de la instruccin ser la siguiente: -DO Etiqueta WHILE (Expresin lgica) accin1 accin2 ....... accinn Etiqueta CONTINUE ENDDO -Todas las restricciones anteriormente indicadas para la otra estructura repetitiva sirven para DO WHILE. -Si por cualquier circunstancia el compilador no reconoce este tipo de estructuras se podr implementar de la siguiente: -Etiqueta IF (Condicin) THEN accin1 accin2 ....... accinn GOTO Etiqueta ENDIF -Hay que tener en cuenta que GOTO slo se usar en el caso de este tipo de circunstancias y slamente para ellas. -Cuando se usen etiquetas, una sla etiqueta valdr para cerrar varios bucles que estn anidados. Pero si se usan ENDDO hay que colocar tantos como DO hayan.

TEMA 4: ENTRADAS Y SALIDAS CON FORMATOS.

413

INSTRUCCION CON FORMATO DE ENTRADA. SENTENCIA "READ". -Este tipo de instrucciones no es muy potente porque slo estn pensadas para introducir y sacar datos. La unidad estndar en el teclado est denominado como un asterisco (*). -La lectura mediante la instruccin READ se realiza con la siguiente sintaxis: -READ (Lista de descriptores), Lista de variables. -La Lista de variables siempre ir separada por comas. -Con esta sintaxis la lectura se realiza del dispositivo predefinido que puede ser uno de los siguientes: -Constante entera (Que referencia otra sentencia). -Variable carcter. -Array de caracteres. -Expresin carcter (Entre parntesis y apstrofes). -Variable entera (Con una etiqueta FORMAT). -Dentro de la lista de descriptores podemos encontrarnos las siguientes opciones: -Unidad, que sirve para que a cada dispositivo se le asigne una unidad o un entero (Sin signo) y la palabra UNIT es opcional. Si no se coloca UNIT debe ir en primer lugar, si se pone la palabra reservada UNIT podr ir en otro lugar. Su sintaxis es: - UNIT= Entero. -Formato, en el que podrn colocarse los siguientes parmetros: -Etiqueta, asignada a la sentencia especial que ser FORMAT. -Variable, entera a la que previamente se le halla asignado una Etiqueta vlida. -Expresin, de tipo cadena que contenga los cdigos de los formateos entre parntesis que estarn cerrados entre unas comillas. -Variable o array, de caracteres a los cuales se le hallan asignado los cdigos de formato. -Asterisco (*), que indicar una entrada sin formato. sabiendo que el formato estar definido por la sintaxis: - FMT= Formato. -Nmero de registro, que slo se usar en el tratamiento de los archivos de acceso directo, donde se le indica que lea el nmero relativo al registro siendo su estructura o sintaxis:

414

-REC=Nmero registro. -Cdigo chequeador, de la operacin al cual deposita el resultado de analizar la operacin siendo su sintaxis: -IOSTAT=Variable entera. -Control del fin de archivo, que se especifica con la sintaxis: -END=Etiqueta. -Transferencia de control, que se produce cuando hay un error y que contiene la siguiente sintaxis: -ERR=Etiqueta. -Si se admiten UNIT y FMT, sus correspondientes valores debern estar en el primer y segundo lugar de la instruccin READ respectivamente. -Los dos primeros formatos se han de usar obligatoriamente, mientras que los otros parmetros slo se usarn para ficheros externos. Un ejemplo de uso de esta sentencia sera: -READ (UNIT=3, FMT=20, REC=10, IOSTAT=Cod, END=100, ERR=Error). -Si la lectura es desde teclado el fin de fichero debe ser tecleado por el usuario con la combinacin CTRL+Z. INSTRUCCION CON FORMATO DE SALIDA. SENTENCIA "WRITE". -Para este tipo de instrucciones nos encontramos dos formatos que estn diferenciados: -PRINT (Lista de descriptores), Lista de salida. -WRITE (Lista de descriptores), Lista de salida. -El primer formato siempre se utilizar cuando el dispositivo de salida est predefinido por el sistema, mientras que el segundo formato se usar para una salida a una unidad especfica. -La lista de descriptores que se podr usar sern los mismos que en la instruccin READ. UNIT y FMT podrn no figurar si se pone primero el nmero de la unidad y luego el formato. INSTRUCCION DE FORMATOS. SENTENCIA "FORMAT". -Esta instruccin facilita la informacin necesaria para que se haga un reconocimiento de la representacin que los datos van a tomar en la memoria principal. La sintaxis de FORMAT ser la siguiente: -Etiqueta FORMAT (Lista de cdigo de formato). -Etiqueta ser el nmero de etiqueta (Obligatorio), aunque se podr referenciar tambin con una variable entera a la cual se ha asignado una etiqueta con ASSIGN.

415

-La lista de cdigos puede estar vaca si la lista de sentencias de E/S est vaca con un salto de lnea. -Esta sentencia no es ejecutable, puede escribirse en cualquier parte y conviene agruparlas todas al principio o al final del programa. -La Lista de cdigo de formato podr contener uno o varios de los siguientes tipos: -Cdigos numricos o de datos (Repetibles) entre los cuales tenemos: -Cdigo I. -Cdigo F. -Cdigo E. -Cdigo D. -Cdigo G. -Cdigo P. -Cdigo L. -Cdigo A. -Cdigos de posicionamiento (No repetibles) entre los cuales tenemos: -Cdigo X. -Cdigos T, Tl, Tr. -Cdigo /. -Cdigos especiales (No repetibles) entre los cuales tenemos: -Cdigos S, Sp, Ss. -Cdigos Bn, Bz. -Cdigo H. -Cdigo :. -Cdigo . -Cdigos de control de carro. CODIGO NUMERICO "I".

416

-En los cdigos de datos o numricos siempre hay que diferenciar tres partes que son fundamentales: -Tipo de dato a representar. -Tamao para la representacin. -Puntos y tamaos de la parte fraccionaria. -Entre los cdigos numricos existen una serie de caractersticas: -Los blancos de relleno son ignorados. -Si el signo es negativo se genera el signo -. -Tiene prioridad el punto decimal de entrada ante la posicin decimal especificada. -Los campos se ajustan a la derecha. -Si se produce error de salida salen asteriscos. -Para la entrada de este cdigo los datos deben ser de tipo entero, siendo I el identificador de cdigo y w el ancho del campo y la sintaxis: -Iw. -Transfiere datos enteros desde el soporte externo a variables, tantos caracteres como valor tenga w contando dgitos, blancos y signos y asocia variables de izquierda a derecha con las normas siguientes: -Blancos entre dgitos o al final se interpretan como ceros. -No usan cdigo de formato cuando la lectura es por teclado. -Con Bn los blancos se ignoran y con Bz se toman como ceros. -Para la salida los datos van desde la memoria interna al soporte externo con un valor w de ancho ajustndose a la derecha y si es negativo se coloca un signo delante del dgito ms significativo. -Si el nmero de caracteres es mayor que el ancho de salida el campo de salida se llena de asteriscos. -Si se utiliza un coeficiente de repeticin n deber ser una constante entera sin signo mayor que cero con la sintaxis: -nIw. -Si se quiere indicar el nmero mnimo de dgitos que aparecern en la salida se usar m, una constante entera sin signo con la sintaxis: -nIw.m.

417

-Se debe contemplar la opcin de que el nmero sea negativo con lo que se deber aumentar el formato en una unidad. -Si en la salida se colocan menos dgitos que en el formato indicado se suprimirn todos los ceros que no sean significativos. CODIGO NUMERICO "F". -Este cdigo transfiere datos de tipo real desde un dispositivo externo hasta la memoria o a la inversa, siendo su sintaxis la siguiente: -Fw.d. -En este cdigo, F ser el carcter del cdigo, w ser la longitud total del campo incluyendo los blancos, signos, puntos y dgitos, y d ser el nmero de decimales de la parte real. -Si no se introduce un punto decimal se sitan los decimales de forma automtica. El cdigo F permite ser un cdigo repetible con lo que su sintaxis ser la siguiente: -nFw.d. -Se pueden introducir nmeros reales en notacin exponencial, siendo el exponente que sigue al nmero real de dos formas: -Constante entera con signo + o -. -Carcter E o D seguido de un signo y una constante entera. -Los caracteres de las variables reales se ajustan a la derecha del campo de anchura. El punto decimal se genera con d posiciones a la derecha. -En la salida no se producen ceros a la izquierda a no ser que la mantisa sea menor que uno, con lo que habr un cero a la izquierda del punto decimal. -Si la parte fraccionaria tiene ms anchura que d habr un redondeo del valor antes de ajustarse y si el dato no cabe en w se producir una salida de w asteriscos. -Entre los ejemplos que se pueden citar para este formato estarn: CODIGO NUMERICO "E". -Este formato lee o escribe datos reales en simple o doble precisin en notacin exponencial. E es el cdigo del exponente, w el ancho del campo y d los dgitos decimales con una sintaxis del tipo: -Ew.d. -La opcin w contar la mantisa, el dgito que precede al punto decimal el punto decimal de la mantisa y el exponente que tendr tres dgitos de forma que la representacin ser del tipo: -(-)0.E(-)nn. -Si el punto no figura en la entrada, la parte decimal sern los d

418

dgitos ms a la derecha de la mantisa. Si aparece el punto decimal en la entrada no se tendr en cuenta la especificacin d en el formato. -El signo del exponente se omite si es positivo y E o D pueden omitirse si el exponente tiene signo. Es recomendable especificar el punto decimal en la entrada. -Para la salida han de reservarse una posicin para el punto decimal y el dgito cero que precede al punto decimal si hay posicin para l. Si la mantisa es negativa otra posicin y el exponente cuatro posiciones. -Se ejecuta la anchura del campo a la derecha y si w es menor que las posiciones para la salida se muestran asteriscos. -En Funcin del desplazamiento del punto decimal se calcula el exponente y la mantisa se puede redondear, pudindose omitir el cero antecedente al punto decimal si no hay posicin. -El cdigo E puede ser repetible con la siguiente sintaxis: -nEw.d. -Otro formato en el que una constante entera e nos indica el nmero de dgitos del exponente sera: -Ew.dEe. -Entre los ejemplos que se pueden citar para este formato estarn: CODIGO NUMERICO "D". -Describe nmeros reales en simple o doble precisin en forma exponencial con el mismo efecto que el cdigo E siendo su sintaxis: -Dw.d. -Para la entrada de datos este cdigo es tratado de igual manera que el cdigo E. Normalmente se utiliza este cdigo para enfatizar que el nmero que se trata es de doble precisin. -El exponente para la salida cambia la letra E por la letra D y tiene la siguiente sintaxis: -D(+/-)nn. -Este cdigo numrico es repetible siendo su sintaxis: -nDw.d. CODIGO NUMERICO "G". -Se utiliza este cdigo para entrada y salida de datos de simple y doble precisin siendo sus sintaxis ms generales las siguientes:

419

-Gw.d. -Gw.dEe. -La entrada de datos reales tiene el mismo significado que los cdigos F, E y D. -En la salida acta como formato F cuando el valor de salida est en el rango entre 0.1 y 10**d, mientras que acta como formato E cuando el nmero es ms pequeo de 0.1 o mayor que 10**d. -Si acta como formato F entonces los ltimos cuatro caracteres sern blancos y el valor se imprimir en un ancho de campo de w-4 caracteres. -Si se usan formatos a la entrada y se coloca un punto decimal cuando el formato es distinto, habr disyuncin entre el formato del teclado y el establecido en el programa. -Siempre se da prioridad al formato introducido por teclado. -Entre los ejemplos que se pueden citar para este formato estarn: CODIGO DE DATOS "P". -Es un factor de escala y sirve para ver los valores exponenciales con un entero delante del punto decimal, aplicable slamente a los cdigos F, E, D y G, afectando a la entrada de datos y a su salida. -El valor n ser un entero que puede llevar signo y su sintaxis ser: -nP. -Al aplicar el cdigo P en la entrada slo es aplicable si el valor externo carece de exponente con lo que se expresar mediante la relacin: -Valor interno=(Valor externo)/10n. -El factor de escala afecta a los cdigos de formato que aparecen a continuacin hasta encontrar otro factor de escala. -Cuando se usa para el cdigo F en la salida se produce desplazamiento del punto en el valor verdadero siendo su relacin: -Valor externo=Valor interno*10n. -Si se usa en concexin con D o E la salida no cambia pero se desplaza el punto de la mantisa al hacer el producto por 10**n, y el exponente se decrementa en n, al igual que con la forma mantisaexponente. -Con el formato G no se usa el factor de escala porque da problemas. -Si tenemos las siguientes instrucciones:

420

-100 FORMAT (2PE7.2, F6.3, -1P8.4) READ (5, 100) VarA, VarB, VarC WRITE (*, '(1X, 3F12.5)') VarA, VARB, VarC y le introducimos la siguiente entrada: -b17.E0157.132-453261.7. tendremos la siguiente tabla de referencia: CODIGO DE DATOS "L". -Este cdigo se usa para la edicin de datos lgicos siendo w el ancho del campo y siendo su sintaxis la siguiente: -Lw. -Para la entrada busca en el campo de forma que si el primer carcter es T (True) o F (False) los dems caracteres del campo son ignorados. -Para la salida se produce una conversin de TRUE a FALSE o T a F ajustndose a la derecha del campo. -El cdigo L es un cdigo de tipo repetible siendo su sintaxis: -nLw. CODIGO DE DATOS "A". -Este cdigo transfiere datos de tipo carcter de la memoria al soporte externo y a la inversa, permitiendo el manejo, entrada y salida de cualquier variable de tipo carcter, siendo su sintaxis: -Aw. -Tambin tiene formato repetible siendo su sintaxis: -nAw. -En las entradas se debe colocar apstrofes que delimiten la cadena si es con lista directa, pero con el cdigo A puede evitarse y tomar la longitud de la variable asociada teniendo en cuenta: -Si w=n todo el dato es asignado a la variable carcter. -Si w>n los ltimos n caracteres se almacenan en la variable. -Si w<n los caracteres se ajustan a la izquierda rellenndose los n-w caracteres de la derecha a blancos.

421

-En las salidas existen otras consideraciones: -Si w=n tendr de longitud n. -Si w>n los n caracteres se ajustan a la derecha y los w-n primeros caracteres se rellenan a blancos. -Si w<n los w primeros caracteres de la cadena salen a la salida perdindose los restantes. CODIGO DE POSICIONAMIENTO "X". -Los cdigos de posicionamiento determinan la posicin dentro de una lnea. La posicin puede ser relativa a la actual del cursor o una posicin absoluta. -El cdigo X salta n caracteres tanto para la salida como para la entrada en el medio externo siendo su sintaxis: -nX. -La opcin n indica los caracteres a saltar a partir de la posicin actual del cursor. -Para la entrada salta n posiciones hacia adelante desde la posicin del cursor. Para la salida en la lnea o registro de salida genera n blancos y mueve el cursor n posiciones a la derecha. -Este cdigo no es repetible. CODIGOS DE POSICIONAMIENTO "T", "Tl" Y "Tr". -Estos cdigos producen una tabulacin en el registro y T especifica una posicin absoluta dentro del registro de entrada o salida siendo su sintaxis: -Tc. -La opcin c indica la columna dentro del registro desde donde se va a posicionar el cursor y donde comienzan las transferencias de datos. -El tabulador Tl mueve la posicin del cursor hacia la izquierda y Tr mueve la posicin del cursor hacia la derecha con las siguientes sintaxis: -Tls. -Trs. -La opcin s indica el nmero de posiciones que se han de desplazar el cursor desde la posicin actual. -Si tenemos las siguientes instrucciones: -50 FORMAT (2A, I2, T1, A, T18, A, I2) CHARACTER *6 Nombre1, Nombre2, Apellido1*8, Apellido2*8

422

INTEGER Edad1, Edad READ (1, 50) Nombre1, Apellido1, Edad1, Nombre2, Apellido2, Edad y le introducimos la siguiente entrada: -Jos-Martinez8Torralva25. obtendremos la siguiente lista de variables en la salida: -Nombre1 - Jos-. -Apellido1 - Martnez. -Edad1 - 0. -De modo anlogo, si tenemos las siguientes instrucciones: -100 FORMAT (T8, I5, Tl2, I4, Tl6, A6) CHARACTER *8 Cadena INTEGER VarA, VarB READ (*, 100) VarA, VarB, Cadena y le introducimos la siguiente entrada: -Visita12345678. obtendremos la siguiente lista de variables en la salida: -M - 23456. -N - 5678. -Cadena - 345678. -Estos cdigos no son repetibles. CODIGO DE POSICIONAMIENTO "/". -Este es un cdigo de posicionamiento vertical que da por terminado un registro o deja registros o lneas vacas y su sintaxis es: -/. -Puede separase por comas o no y sita el puntero en el primer carcter de un nuevo registro. -En la salida causa n-1 registros o lneas vacas y no es un cdigo de tipo repetible. CODIGOS ESPECIALES "S", "Sp" Y "Ss".

423

-Estos cdigos slo son vlidos para formatos de salida, para nmeros y controlan la salida del signo + en los nmeros positivos de forma: -S no imprime el signo +. -Sp imprime el signo +. -Ss no imprime el signo +. -El formato Sp slo valdr hasta que sea el final de los especificadores de formato o hasta un cdigo S o Ss. -Estos cdigos no son repetibles. CODIGOS ESPECIALES "Bn" Y "Bz". -Estos cdigos dirigen la interpretacin de los caracteres en blanco en los formatos numricos de forma que: -Bn ignora los blancos. -Bz toma los blancos como ceros. -Si un campo de entrada est en blanco se considera como cero. CODIGO ESPECIAL "HOLLERITH". -Este cdigo slo puede formar parte de un formato de salida y no es vlido para entradas, siendo su sintaxis: -wHc. -Este cdigo indica que habr una salida de w caracteres que son los que van a aparecer en c. Los apstrofes se consideran como cualquier otro carcter. -El nmero de caracteres de la constante carcter determina el ancho del campo de salida. CODIGO ESPECIAL ":". -Este cdigo provoca la terminacin de la operacin de salida si no hay ms elementos en la lista de la sentencia de salida. -Si el cdigo es encontrado durante una entrada de datos o si quedan elementos en la lista de salida es ignorado. CODIGO ESPECIAL " ". -Este cdigo facilita formatear la salida de datos en la pantalla. Este cdigo se aplica en formatos de sentencias de salida.

424

-Causa que el procesador suprima en la salida de acceso secuencial la separacin de registros actual y el siguiente registro. CODIGOS ESPECIALES DE CONTROL DE CARRO. -Exigen que se escriban los cdigos como primer carcter dentro de la sentencia WRITE que emplea la impresora como salida siendo sus cdigos los siguientes: -Blanco avanza una lnea. -0 avanza dos lneas. -1 sita en la primera lnea de la siguiente pgina. -+ imprime sobre la misma lnea y no avanza. -Si el primer carcter no es un signo de estos se toma como blanco. En la mayora de las sentencias FORMAT tiene especificado en la columna 1 o 1X. REGISTROS MULTIPLES. -Si el nmero de elementos de la lista de entrada y salida es menor que el nmero de cdigos de datos, los cdigos sobrantes se ignoran. -Si el nmero de elementos es mayor que el nmero de cdigos de formato se van asociando los cdigos de datos con los datos de izquierda a derecha. -Cuando se alcanza el parntesis de cierre de FORMAT se empieza un nuevo registro repitindose los cdigos a partir del parntesis de apertura precedente.

TEMA 5: SUBPROGRAMAS Y FUNCIONES EN FORTRAN.


UNIDADES DE PROGRAMA. -Ante la necesidad de una organizacin jerrquica Fortran permite dividir el programa en mdulos llamados unidades de programa. Hay dos clases de unidades de programa: -Programa principal. -Subprograma. -Cada programa tiene un slo programa principal que puede contener cero o ms Subprogramas que pueden ser: -Funciones. -Subrutinas. -Las Funciones pueden ser de varios tipos:

425

-Externas, es un mdulo independiente. -Intrnsecas, es un mdulo independiente. -Unilneas, son locales al mdulo o unidad de programa donde estn definidas. -Las Subrutinas son Subprogramas que pueden ser usados para devolver un conjunto de cero a n datos y suelen ser de propsito general. -Los mdulos pueden estar uno a continuacin del otro y no estarn separados a nivel lgico aunque s lo estarn a nivel fsico. -La Funcin se usa cuando se necesita devolver un slo valor, mientras que las Subrutinas se utilizan para devolver ms valores o en su defecto ninguno. FUNCIONES SENTENCIA (UNILINEA). -Es un procedimiento especificado en una sentencia simple, con forma similar a una sentencia de asignacin aritmtica, lgica o carcter. Este tipo de Funciones representan una frmula. -Se escribe en la misma unidad de programa que va a ser usada, son locales a la unidad de programa en la que est definida. -Es una sentencia de tipo no ejecutable y ha de ser escrita antes de ser invocada, su sintaxis suele ser: - Tipo Identificador (Lista de parmetros actuales)=Expresin. -Si no se colocan parmetros, habr que poner parntesis igualmente. -El tipo de dato del identificador de la Funcin sentencia y los argumentos ficticios estn determinados por el tipo implcito del identificador. -La invocacin se realiza escribiendo su identificador y entre parntesis los argumentos verdaderos o actuales que sustituyen a los argumentos que son ficticios. -Los argumentos actuales pueden ser expresiones y han de corresponderse en nmero, orden y tipo con los argumentos ficticios. La llamada a la Funcin debe formar parte de una sentencia ejecutable. -Se puede invocar el paso de varios parmetros separados por una coma o no ser invocados. FUNCIONES INTRINSECAS. -Son las Funciones propias del lenguaje Fortran, que ya fueron descritas en el tema 2. -Estas Funciones se usan con el identificador de la Funcin seguido de los valores de los parmetros.

426

-Habr que tener en cuenta que los argumentos sean del mismo tipo y que correspondan dentro del rango. FUNCIONES EXTERNAS. -Este tipo de Funciones devuelve un valor a travs del identificador de la Funcin aunque puede devolver otros valores. Su sintaxis es: - Tipo FUNCTION Identificador (Lista de parmetros formales) accin1 accin2 ....... accinn Identificador=Expresin ....... RETURN END -El tipo es opcional y especifica el tipo de datos del valor que se devuelve. Si no se especifica se siguen las normas del tipo implcito de los identificadores. -Esta sentencia debe ser la sentencia inicial de una Subrutina FUNCTION. El tipo de la Funcin est determinado por la especificacin tipo en la cabecera. -Puede tener cualquier sentencia excepto las definiciones de otras Subrutinas. El valor asignado debe ser del mismo tipo que el de la Funcin. -El fn lgico de una Funcin es RETURN que puede aparecer ms veces. La ltima sentencia de cdigo fuente de definicin de una Funcin es END que ser el fn fsico. -Desde una Funcin se puede invocar otra Subrutina pero no a la misma Funcin y no se permite la recursividad. LLAMADA A FUNCIONES EXTERNAS. -La llamada a una Funcin ha de formar parte de una sentencia siendo la sintaxis de la llamada la siguiente: -Identificador (Lista de parmetros actuales).

427

-La referencia a la Funcin se puede realizar desde cualquier otra unidad de programa. La lista de parmetros deber estar separada por comas. -Los parmetros se pueden pasar por valor colocando la palabra reservada VALUE delante de las variables. No se debe modificar los valores de las variables de la lista. Es mejor duplicarlos o protegerlos. -Los parmetros actuales deben coincidir con los argumentos ficticios con las siguientes especificaciones: -Constantes, variables o expresiones, excepto la concatenacin de operandos. -Nombre de array. -Funciones intrnsecas o externas. -Subrutinas. PASO DE ARGUMENTOS A LAS SUBRUTINAS. -Las llamadas por valor realizan unas copias del argumento verdadero con lo que ste no cambia durante la ejecucin de la Subrutina. -Las llamadas por referencia no realizan esa copia y el argumento verdadero puede cambiar durante la ejecucin de la Subrutina. -Para saber cuando se deben pasar valores por valor o referencia nos fijaremos en la siguiente tabla: -Por referencia si es una variable, array, elemento de array o caracteres. -Por valor si es una constante o una expresin. SUBRUTINAS. -Las Subrutinas empiezan con la sentencia SUBROUTINE y puede tener cualquier sentencia excepto las usadas para definir otros mdulos o unidades de programa. -Puede devolver cero, uno o ms valores siendo la transmisin por parmetros de cabecera. La ejecucin termina con un fn lgico o RETURN siendo la ltima sentencia el fn fsico o END. -Si slo hay un RETURN se puede omitir porque END funcionar como si fuera RETURN. La sintaxis de las Subrutinas es: -SUBROUTINE Identificador (Lista de parmetros formales) accin1 accin2 .......

428

accinn RETURN ....... END -El identificador del procedimiento ha de ser nico, el primer carcter debe ser una letra. -En la Subrutina no se asocia un valor al identificador de la misma, devuelve los datos de salida modificando sus argumentos si son: -Nombres de variable. -Nombres de array. -Subrutinas ficticias. -Asteriscos. -El tipo de los argumentos se especifica explcita o implcitamente. El argumento array se define con un tamao fijo, anidado o ajustable. -El argumento asterisco se usa para la salida mltiple de una Subrutina no permitiendo la autollamada o la recursin. LLAMADA A SUBRUTINAS. -Una Subrutina puede ser invocada desde otra Subrutina o unidad de programa principal con la siguiente sintaxis: -CALL Identificador (Lista de parmetros actuales). -Los argumentos deben coincidir en orden y tipo con los argumentos ficticios de la Subrutina referenciada. La ejecucin de CALL causa que el control de la ejecucin pase a la Subrutina referenciada. -Los argumentos de CALL pueden ser: -Expresiones. -Arrays. -Subrutinas. -El argumento correspondiente al asterisco ha de ser del tipo *N siendo N una etiqueta de una sentencia ejecutable. Los valores se transmiten por referencia. -Un ejemplo de la utilizacin de Subrutinas podra ser:

429

-CHARACTER *20 Cab REAL Gwr PARAMETER (Cab='Especificacin de la gravedad') Gwr=9.82337 WRITE (*, *) Cab, Gwr CALL Lista (Cab, Gwr) WRITE (*, *) Cab, Gwr SUBROUTINE Lista (Ttulo, Datos) CHARACTER *20 Ttulo REAL Datos Ttulo='Densidad Kg/cm' Datos=0.57975 WRITE (*, *) Ttulo, Datos RETURN END -El resultado que proporcionar el anterior trozo de cdigo ser un resultado imprevisible, puesto que no se puede modificar una constante a una variable. -La relacin general para el paso de parmetros ser la siguiente: Parmetros actuales Parmetros formales Variables Nombre variable Elementos de array Nombre variable Estructuras Nombre variable Expresiones Nombre variable Arrays Array *Etiqueta Etiqueta Subrutinas Nombre nico

430

Funciones Nombre nico SENTENCIAS "EXTERNAL" E "INTRINSIC". -Estas sentencias se usan para cuando el argumento es el identificador simblico de un Subprograma o Funcin. -La sentencia EXTERNAL declara que un identificador es un nombre de una Funcin externa o de una Subrutina. No es una sentencia ejecutable y debe aparecer antes del cdigo de sentencias ejecutables. -La sintaxis de EXTERNAL es: -EXTERNAL Identificador1, Identificador2, ..., Identificadorn. -La sentencia INTRINSIC declara que un identificador es nombre de una Funcin intrnseca, apareciendo el identificador en la sintaxis: -INTRINSIC Funcin1, Funcin2, ..., Funcinn. -Las Funciones intrnsecas que no pueden ser argumentos actuales de las Subrutinas son los siguientes: -De conversin de tipos, INT, IFIX, IDINT, FLOAT y CHAR. -Lexicogrficas, LGE, LGT, LLE y LLT. -De mximos y mnimos. ENTRADA MULTIPLE A UNA SUBRUTINA. SENTENCIA "ENTRY". -Sirve para que el comienzo de una Subrutina sea en una sentencia especfica contenida en la Subrutina. Puede estar en cualquier punto del programa excepto dentro de un bloque IF o DO. -Esta sentencia es de tipo no ejecutable siendo su sintaxis: -ENTRY Identificador (Lista de parmetros actuales). -Los identificadores simblicos de varios puntos de entrada tienen que ser diferentes entre s y del nombre del procedimiento. -Un punto ENTRY se referencia desde otro mdulo. El proceso prosigue hasta encontrar un RETURN. SALIDA MULTIPLE DE UNA SUBRUTINA. SENTENCIA "RETURN N". -Esta sentencia causa que el control de la ejecucin retorne a la unidad de programa desde donde se invoc. La sintaxis es la siguiente: -RETURN n.

431

-La especificacin n se usar en una Subrutina, para devolver el control a una sentencia especfica, sin ser la siguiente a la llamada. -El valor de n ha de estar comprendido entre uno y el nmero de asteriscos de la cabecera de la Subrutina.

TEMA 6: VECTORES Y MATRICES (ARRAYS).


ARRAYS UNIDIMENSIONALES. -Consiste en una lista de un nmero finito de datos del mismo tipo que se referencian por un identificador comn y un nmero de orden que son consecutivos. -Las variables que representan los arrays se denominan variables de subndice. El tamao de un vector es el nmero de elementos que componen el vector. Una variable de subndice tiene el formato: -Variable (Subndice). -La variable puede ser un array de los siguientes tipos: -Numrico. -Cadena. -Lgico. -Complejo. -El subndice puede ser: -Constante numrica. -Variable. -Expresin matemtica. -Para saber una determinada posicin de un elemento se deben cumplir las siguientes caractersticas: -Todos los elementos del array son del mismo tipo. -El vector tiene un nombre nico y los elementos estn ordenados por el subndice. DECLARACION DE UN ARRAY. SENTENCIA "DIMENSION". -Hay dos formas de definir un vector o matriz, utilizando la forma comn a todas las versiones cuyo formato ser: -DIMENSION Identificador1 (Mnimo:Mximo), ..., Identificadorn

432

(Mnimo:Mximo). -El nmero de elementos que obtendremos vendr dado por la frmula Mximo-Mnimo+1, sabiendo que mximo deber ser mayor o igual que el valor de mnimo. -El identificador es una variable con las mismas reglas. Opcionalmente se puede colocar despus del Identificador un alias o ALLOCATE que se usar cuando estemos en tiempo de ejecucin. Como ejemplos: -ALLOCATE Array (Valor). -DIMENSION Array (11:25). -DIMENSION (14). -Array (:). -Lo que indica la opcin ALLOCATE es que el valor de sus subndices se va a dimensionar durante la fase de ejecucin. -El subndice izquierdo puede tomar los valores cero, negativo o positivo al igual que el subndice derecho, pero ste ltimo debe ser igual o mayor que el subndice izquierdo. -No se puede modificar la dimensin una vez definida, y es necesario colocar corchetes para definir un array en tiempo de ejecucin. -En el ejemplo siguiente se declara un array de una dimensin que se dimensionar posteriormente: -DIMENSION Array ALLOCATE (:). -Para desasignar el array y liberar la memoria se utilizar la opcin DEALLOCATE como sigue: -DEALLOCATE Array (Valor). -Las dimensiones se separan por comas y se pueden definir como mximo siete dimensiones, aunque pueden ser ms dependiendo de la memoria del ordenador. -Si el lmite es uno se puede omitir el valor de mnimo, quedando la sintaxis siguiente: -DIMENSION Identificador1 (Mximo), ..., Identificadorn (Mximo). -La segunda forma de definir vectores es con la especificacin de tipos de sintaxis: -Tipo Identificador (Mnimo:Mximo). -Tipo es cualquiera de los tipos definidos en Fortran. Mediante esta forma se asocia el tipo de dato al identificador y definirlo como un array con tantos elementos como haya. -Los lmites inferiores y superiores son expresiones enteras siempre con constantes nunca variables.

433

-Otros atributos que podemos colocar despus del identificador son: -REFERENCE. -C. -PASCAL. -VALUE. -Se usarn estas opciones cuando se va a pasar el array por parmetro a otros lenguajes, por lo que habr que normalizar el lenguaje. -Los arrays se almacenan en la memoria de una forma distinta que en otros lenguajes, ya que se almacenan columnas a columnas unas a continuacin de otras. -Se almacenan en orden creciente de sus subndices a partir de una posicin determinada. Para sacar los elementos por filas se usar el DO implcito. OPERACIONES CON ARRAYS Y ELEMENTOS DE UN ARRAY. -Hay dos tipos de operaciones bsicas, con los elementos que sern las mismas que permiten los tipos de datos o con estructuras como el recorrido de un array. -Para hacer el recorrido de un array se usar una estructura del tipo FOR como: -DO Etiqueta VarA=Izquierdo, Derecho accin1 accin2 ....... accinn Etiqueta CONTINUE ENDDO -Se puede leer o escribir un array completo con slo poner el Identificador del array en la sentencia de E/S. -Se puede inicializar un array completo con slo poner su identificador y los valores que se inicializan en la sentencia DATA. -Consiste en obtener datos de un dispositivo externo y almacenarlos en un vector o escribir en un dispositivo los datos de un array. -La E/S puede realizarse con el identificador del array y por tanto

434

se procesa en total o elemento a elemento con un DO implcito o explcito. ENTRADA Y SALIDA DE ARRAYS CON DO "IMPLICITO". -Se utiliza en vez de la estructura de tipo FOR. Esta estructura est ya definida y hace sencilla la manipulacin de los arrays en la lectura y escritura. -Con este tipo de DO se ejecuta una sla vez la sentencia READ de modo que se lee un slo registro fsico. Su sintaxis es: -READ (*, *) (Identificador (VarA), VarA=Inicio, Final, Incremento). -Cuando el incremento es uno se puede omitir. Tambin puede emplearse para acceder a los elementos de un array de ms de una dimensin para leer o escribir e inicializar. -El DO implcito se puede anidar con tantos niveles como sea necesario o tantos niveles como dimensiones tenga el array. -Un ejemplo del uso del DO implcito sera: -READ (*, *) (Array (VarA), VarA=1, 20). -El ejemplo anterior lee veinte variables establecindose un bucle. Otro ejemplo: -READ (*, *) (Array (VarA), VarA=Izdo, Dcho, Paso). -En el ejemplo anterior Paso indica que la variable toma el valor de izdo y sus sucesivos valores repitiendo el proceso hasta que dcho tiene mayor valor que izdo. -Pero si paso es negativo entonces dcho ha de ser menor forzosamente. El siguiente ejemplo tendra como representacin con DO la siguiente: -READ (*, 100) (Array (VarA), VarB=1) READ (*, *) ((Array (VarA, VarB), VarB=1, VarC), VarA=1, VarD) -DO VarA=1, VarD DO VarB=1, VarC READ (*, *) (Array (VarA, VarB)) ENDDO ENDDO PASO DE ARRAYS POR PARAMETRO. -El paso de un array se realiza por referencia y cuando un array es pasado, en realidad se pasa la direccin en memoria del primer elemento ahorrndose memoria y espacio.

435

-Un ejemplo de ello es el siguiente: -REAL Array (100, 200), VarA, VarB CALL Lista (Array, VarA, VarB) SUBROUTINE Lista (Arrayauxiliar, VarC, VarD) -En la definicin de los argumentos ficticios para los arrays en las Subrutinas o Funciones, no es necesario que sean iguales los lmites superior e inferior de cada dimensin con los lmites del argumento actual del array transmitido. -Siempre se exige que una variable sea dimensionada por lo que dentro de la Subrutina se colocar la siguiente declaracin: -REAL Arrayauxiliar (100, 200). -En esta Subrutina no se crea la variable arrayauxiliar sino que se define dicha variable. El array en esa declaracin se puede ajustar siempre que la dimensin sea menor que la declarada anteriormente. -En todo caso el tamao del argumento ficticio para el array no puede ser mayor que el del argumento actual. -Se puede poner un asterisco que es la opcin por defecto y que indica que toma el valor de la dimensin iniciada en el programa principal. ARRAYS DE TAMAO AJUSTABLE Y TAMAO ASUMIDO. -El argumento ficticio array en la Funcin Mximo su tamao se ajusta a N elementos que es un dato transmitido, y por tanto la definicin de arrays ajustables. -La definicin de un argumento ficticio array de tamao ajustable en una Funcin o en una Subrutina es la nica situacin en la que una definicin de array puede incluir una variable en la especificacin del rango de cada dimensin. -Para definir un argumento ficticio array como asumido se especifica el lmite superior de la ltima dimensin del array con un asterisco. El nmero de elementos del array ficticio es el mismo que el array pasado por parmetro. -Slo se puede especificar el lmite superior de la ltima dimensin con un asterisco, aunque para los caracteres tambin es vlido. -Para las Funciones carcter externas usaremos la siguiente sintaxis: -CHARACTER * (*) FUNCTION Identificador (Lista de parmetros).

TEMA 7: SENTENCIAS ESPECIALES DE FORTRAN.


SENTENCIA "EQUIVALENCE".

436

-Esta sentencia declara que dos o ms identificadores son equivalentes y al menos dos. Hace que compartan la misma posicin de almacenamiento que puede ser referenciado de ms de una forma. -La sintaxis de esta orden es la siguiente: -EQUIVALENCE (Lista de Identificadores). -En una misma sentencia EQUIVALENCE pueden haber ms de una lista de identificadores, cada una de las cuales se refiere a la misma posicin de almacenamiento: -EQUIVALENCE (Lista de Identificadores), + (Lista de Identificadores). -Las variables de distinto tipo pueden hacerse equivalentes, de forma que habr almacenamiento compartido pero no una posible conversin de tipos. -La sentencia de almacenamiento de los identificadores de la lista comienza con la primera unidad de almacenamiento de las entidades de la lista. -Se pueden hacer equivalentes los arrays y los elementos de los arrays. Los ndices de las variables array escritos en una lista de la sentencia deben ser constantes o expresiones formadas por constantes. -En una sentencia EQUIVALENCE no puede provocarse que una misma variable ocupe dos posiciones de memoria distintas. -Esta sentencia puede utilizarse entre variables o arrays de tipo carcter aunque sus longitudes no sean las mismas. -Las variables carcter equivalentes tendrn el mismo primer carcter ubicado en la misma posicin de memoria. -En Fortran 77 no est permitido hacer equivalentes variables de tipo carcter con variables numricas, y los nombres de Funcin no pueden hacerse equivalentes con otra entidad. -Esta sentencia se suele utilizar cuando la memoria de que se dispone es muy pequea, pero produce que la lgica del programa pueda acceder a la variable correcta. SENTENCIA "COMMON". -Una forma de comunicarse entre el programa o Unidad de programa que llama a una Subrutina o Funcin, y la Subrutina o Funcin llamada, es a travs de la lista de argumentos. -Con estos argumentos se referencian zonas de memoria comunes desde distintas Unidades de programa, la Unidad de programa que llama y la Unidad llamada. -Con esta sentencia se pueden definir zonas comunes de memoria entre diversas Unidades de que forman parte un programa, entre la Unidad principal y una o varias Subrutinas o Funciones.

437

-Es una sentencia no ejecutable que debe aparecer en la Unidad de programa que llama y en el Subprograma llamado antes de todas las sentencias ejecutables. -En esta sentencia se listan los nombres de las variables y los nombres de los arrays con su dimensin y es una forma alternativa de comunicar datos a la lista de argumentos de los Subprogramas. -Si un array va ha estar en una zona comn se puede definir su lista de identificadores en la lista COMMON o definir el array y ponerlo en la lista de la sentencia COMMON. -Las variables y los arrays son asignados a un almacenamiento comn en el orden en que aparecen en la sentencia COMMON, siendo la sintaxis de esta: -COMMON Identificador1, Identificador2, ..., Identificadorn. -La sintaxis anterior pertenece a una sentencia COMMON sin nombre o en blanco, pero existe una sentencia COMMON etiquetada o con nombre. -El orden de los identificadores especificados en una sentencia COMMON determina la equivalencia de identificadores simblicos entre varias Unidades de un programa (El bloque comn de memoria es lo global). -La lista de identificadores deben ser de igual tipo en todas las sentencias COMMON establecindose las equivalencias por el orden en que estn en la lista. -Un identificador de una lista COMMON no puede figurar como argumento en una Subrutina o Funcin ni como parmetro en las llamadas (Puede haber un solapamiento de memoria). -El bloque comn de memoria es nico para todo el programa. La memoria comn se establece de forma contigua. -En un bloque comn de memoria no puede haber variables carcter y no carcter mezcladas, siendo todo de variables carcter o variables no carcter. -Las variables o arrays que figuran en la lista de COMMON sin nombre no pueden ser inicializadas con DATA, slo pueden inicializarse con sentencias de asignacin. SENTENCIA "COMMON" CON NOMBRE. USO CONJUNTO DE "COMMON" Y "EQUIVALENCE". -Esta sentencia etiquetada permite definir varias zonas comunes de memoria, cada una con su nombre o etiqueta. -Se forman de igual manera que las anteriores pero se escribe su nombre o etiqueta con dos barras (Slash) antes de la lista de variables. -El nombre de COMMON debe ser un identificador vlido y pueden definirse dos o ms zonas comunes. Es conveniente usar tantas sentencias COMMON como zonas comunes haya. -Se pueden mezclar en una zona comn variables numricas con variables o arrays lgicos. -En el siguiente ejemplo tendremos slamente dos bloques COMMON uno con nombre y otro sin nombre:

438

-COMMON /Nombre-1/ VarA, VarB, VarC COMMON VarH, VarI, VarJ COMMON /Nombre-1/ VarK, VarL COMMON VarM, VarN -Una comparacin entre un trozo de programa con COMMON y otro sin l podra ser el siguiente: -REAL VarD, VarE, VarF, VarG, VarZ READ *, VarD, VarE, VarF, VarG VarZ=2 VarT=Funcin (VarD, VarE, VarF, VarG) STOP END FUNCTION Funcin (VarA, VarB, VarC, VarX) VarI=VarA*VarX**2+VarB*VarX+VarC RETURN END -REAL VarD, VarE, VarF, VarG, VarZ COMMON /Coeficientes/ VarD, VarE, VarF VarZ=2 VarT=Funcin (VarZ) STOP END FUNCTION Funcin (VarX) COMMON /Coeficientes/ VarA, VarB, VarC VarF=VarA*VarX**2+VarB*VarX+VarC RETURN END

439

-Si en una sentencia COMMON se precede a los identificadores de dos barras o slash indicar que son zonas comunes sin nombre. Es indiferente el orden de definicin de las zonas comunes. -Los arrays o variables de COMMON con etiqueta pueden ser inicializados con la sentencia DATA, pero no las variables de un COMMON sin nombre. -Un COMMON con etiqueta tiene que tener el mismo tamao en todas las Unidades de programa. -Las variables o arrays pueden aparecer en ambas sentencias COMMON y EQUIVALENCE siempre que no causen conflicto en el orden en el que se almacenan. -Dos variables que estn en una zona comn no pueden ser equivalentes entre s. SENTENCIA "SAVE". -Esta sentencia se utiliza para almacenar estticamente los valores o datos de una invocacin a otra. -SAVE declara que las variables locales y los arrays sean retenidos despus de ejecutar RETURN o la siguiente llamada al Subprograma. -Las variables o arrays locales contendrn el ltimo valor adquirido en la ejecucin anterior al Subprograma. Su sintaxis es: -SAVE (Lista de identificadores). -La lista de identificadores, que podrn ser variables, arrays o bloques COMMON, retendr el ltimo valor adquirido antes de la ejecucin de RETURN. -Las excepciones en las que las variables no quedan indefinidas al salir de un Subprograma son: -Sentencias SAVE del Subprograma. -Bloques COMMON en blanco o sin nombre. -Bloques COMMON etiquetados y definidos en la Unidad de programa principal y uno o ms Subprogramas. -Las variables en bloques COMMON etiquetados en Subprogramas quedan indefinidos slo cuando hay una salida desde un Subprograma pero no se pueden retener con SAVE y el nombre del bloque COMMON. -En una lista de SAVE no pueden aparecer parmetros de Subrutinas o Funciones, ni nombres de Funcin y Subrutinas ni variables o arrays de bloques COMMON. -Puede haber ms de un SAVE o escribir la lista en un slo SAVE. Si hay ms de un SAVE no podrn repetirse nombres de variables o arrays. INICIALIZACION DE VARIABLES. SENTENCIA "DATA". -Esta sentencia permite inicializar variables con la siguiente sintaxis:

440

-DATA Lista1 /Constantes/, ..., Listan /Constantes/. -La lista contendr los nombres de las variables o arrays a inicializar separados por comas e inicializa las constantes que vendrn separadas por comas. -Puede especificarse ms de una lista y sus constantes o agrupar todas las variables en una sla lista. La inicializacin se realiza en el orden en el que aparecen, de izquierda a derecha. -Si hay constantes consecutivas iguales se podr poner: -Nmero*Constante. -En el formato anterior Nmero es el nmero de repeticiones que se especificar de dicha forma. -Las reglas ms importantes de las sentencias DATA son las siguientes: -El nmero de constantes ha de ser igual al nmero de elementos de la lista, variables o arrays. -No pueden aparecer argumentos ficticios de Subprogramas, nombres de Funcin y elementos de bloque COMMON en blanco. -Slo pueden estar los bloques COMMON etiquetados en los bloques DATA. -El tipo de variable o array a inicializar debe corresponderse con el tipo de la constante. Lo mismo para las variables de tipo carcter (Si hay exceso se ignora y si hay defecto se completa con blancos a la derecha). -Esta sentencia no es ejecutable y puede aparecer despus de las especificaciones de datos pero es mejor colocarlas antes de las sentencias ejecutables. -Las sentencias DATA pueden utilizar el DO implcito para inicializar el array a los datos que se quieran o slo definirlo. Un ejemplo de ello sera: -INTEGER VarA, Orden, Alfa, Lista (100) REAL Coeficiente (4), Epsilon (2), Pi (5), VarX (5, 5) CHARACTER *15 Ayuda DATA VarA /0/, Orden /3/ DATA Coeficiente /1.0, 2*3.0, 1.0/, Epsilon (1) /0.0001/ DATA ((VarX (VarI, VarJ), VarI=1, VarJ), VarJ=1.5) /15*1.0/ DATA Lista /100*0/ DATA Ayuda /'Ayuda'/ -El siguiente formato puede ir en cualquier parte del programa y tiene como misin hacer una llamada al camino o ruta especificado:

441

-$INCLUDE 'Path Nombre.For'. SENTENCIA "PARAMETER". -Esta sentencia identifica constantes mediante nombres identificadores o simblicos para que despus se pueda hacer referencia a la constante por el identificador. La sintaxis de la orden es: -PARAMETER (Identificador=Expresin constante). -Dentro de los parntesis se pueden especificar tantos parmetros como se quiera separndolos por comas. -La expresin debe coincidir en su tipo con el identificador y el valor que va ha representar el identificador al evaluarse la expresin. Debe ajustarse a las reglas establecidas para las sentencias de asignacin. -El tipo de dato del identificador puede definirse de forma implcita o explcita, describiendo la sentencia de definicin antes. -La expresin constante puede hacer referencia a otro parmetro pero ha de estar definida antes en otro PARAMETER o en la misma sentencia PARAMETER. -Un identificador de constante no puede cambiar despus el valor que se le ha impuesto. -El mbito de los parmetros es la Unidad de programa en que estn definidos y una vez que el parmetro es definido puede ser referenciado en los sitios en que pueden referenciarse las constantes excepto: -No se pueden usar los parmetros en una especificacin de un formato. -Un parmetro no puede usarse como parte de otra constante. -Esta sentencia no es ejecutable y puede aparecer despus de cualquier sentencia de especificacin de tipo o antes de que se haga uso del parmetro. -Cuando un valor aparece varias veces en una Unidad de programa se debe asociarle un nombre simblico y usar dicho nombre para despus hacer la referencia a dicho nombre. INICIALIZACION DE UN COMMON CON NOMBRE. SUBPROGRAMA "BLOCK DATA". -Este Subprograma asigna valores iniciales a variables y a arrays de un COMMON etiquetado, puesto que los COMMON en blanco se inicializan en las sentencias DATA. -La sentencia BLOCK DATA que puede tener un identificador termina con la sentencia END y las sentencias que se pueden especificar son todas las no ejecutables para la inicializacin de la lista de COMMON etiquetados y que son: -IMPLICIT. -PARAMETER.

442

-DIMENSION. -SAVE. -COMMON. -EQUIVALENCE. -DATA. -La sintaxis de este Subprograma ser: -BLOCK DATA Identificador sentencia1 sentencia2 .......... sentencian END -Si el identificador se coloca es considerado como un identificador global y no puede coincidir con el nombre de una Funcin o el de una Subrutina. -El COMMON etiquetado al inicializarlo hay que especificarlo en el Subprograma BLOCK DATA de forma completa aunque haya variables no inicializables. -En un programa ejecutable pueden haber ms de un BLOCK DATA pero slo uno puede ser sin nombre y todos los dems nombres distintos. Un COMMON slo puede estar en un slo BLOCK DATA. -Un ejemplo del uso de este Subprograma sera: -BLOCK DATA Nombre COMPLEX VarA, VarB LOGICAL VarC, VarD INTEGER VarI, VarJ, VarK, Lista REAL VarX COMMON /Bloque1/ VarX (10), VarI, VarJ, VarA COMMON /Bloque2/ Lista (6), VarC, VarD, VarB DATA VarX /10*0.0/, VarI, VarJ /1, 0/, VarC /False/

443

DATA VarA, VarB, /2*(0, 1)/ END -Normalmente este tipo de Subprogramas se suelen colocar cuando acaba el programa principal. TIPO DE DATO COMPLEJO. -Este tipo de datos se representa por un par ordenado de nmeros reales de doble precisin, enteros o una combinacin de ellos encerrados entre parntesis y separados por comas. -Para definir un identificador de tipo complejo contamos con COMPLEX, y pueden haber variables, arrays complejos y Funciones complejas. -La memoria que ocupa una variable compleja es el doble de una variable real. Se puede asignar una constante compleja, otra variable compleja o una expresin compleja inicializndose con DATA. -Cuando a la variable compleja se le quiere asignar un nmero complejo que tiene la parte real, la parte imaginaria o ambas debe usarse la Funcin intrnseca COMPLEX. -Los nmeros complejos pueden sumarse, restarse, multiplicarse, elevarse a una potencia y dividirse. No puede usarse en la expresin aritmtica de la sentencia IF aritmtico, y no puede usarse como subndice de un array. -Otras Funciones que tienen los nmeros complejos son: -AIMAG (Expresin numrica) (Parte imaginaria como nmero real). -CONJ (Expresin numrica) (Devuelve el complejo conjugado). -Otras Funciones internas que tienen un nombre especfico para el argumento complejo y el valor que devuelven es tambin complejo son: -CSQRT (Expresin numrica) (Raz cuadrada de un complejo). -CABS (Expresin numrica) (Mdulo del complejo). -CEXP (Expresin numrica) (Funcin exponencial de un complejo). -CLOG (Expresin numrica) (Logaritmo natural de un complejo). -CSIN (Expresin numrica) (Seno de un complejo). -CCOS (Expresin numrica) (Coseno de un complejo). SENTENCIA "PAUSE". -Esta sentencia hace una parada temporal en la ejecucin de un programa para detener la salida hasta que el usuario haya podido leer toda la informacin. Su sintaxis es:

444

-PAUSE. -PAUSE 'Cadena de caracteres'. -PAUSE Nmero (Constante de hasta cinco dgitos). -Al producirse la parada se visualiza un mensaje propio regido por el nmero indicado o la cadena si ha sido especificada. ASIGNACION DE ETIQUETAS A VARIABLES ENTERAS. SENTENCIA "ASSIGN TO". -Esta sentencia permite asignar un nmero de etiquetas por una constante entera a una variable entera siendo su sintaxis: -ASSIGN Etiqueta TO Variable. -Etiqueta es la etiqueta de una sentencia ejecutable o de una sentencia FORMAT, siendo variable el identificador de una variable entera. -Despus de la ejecucin de ASSIGN el valor de la variable no puede ser considerada como un dato entero. -Si la etiqueta asignada a la variable es la de una sentencia FORMAT, la variable puede ser usada como un identificador de formato. -Si la etiqueta asignada es ejecutable la variable puede ser usada en un GOTO asignado como: -GOTO Variable. -GOTO Variable (Etiqueta1, Etiqueta2, ..., Etiquetan). -Cuando se ejecuta una sentencia GOTO asignada el control del programa es transferido a la sentencia con la etiqueta del ltimo valor asignado a la variable con ASSIGN.

TEMA 8: TRATAMIENTO DE CADENAS EN FORTRAN.


CADENAS DE CARACTERES. -Una cadena es un conjunto de caracteres encerrados entre apstrofes. Si se quiere representar un apstrofe dentro de una cadena se deber representar por dos apstrofes consecutivos. -Una cadena se declara con la siguiente sintaxis: -CHARACTER *Nmero Lista de variables. -Aqu nmero representa el nmero de caracteres de las variables de una cadena. Un array que contiene caracteres se define con una sentencia CHARACTER y declarada de dos modos distintos. LAS CADENAS COMO ARGUMENTO DE SUBPROGRAMAS.

445

-Un Subprograma puede especificar una cadena de caracteres sin darle una longitud especfica y equivale a la longitud de una array con una variable entera. -Se puede definir en un Subprograma un array de n variables cada una con su cadena de caracteres sin especificar la longitud de cadena en la sentencia CHARACTER. -La longitud de la cadena es siempre positiva nunca igual a cero, y dicha longitud no se puede alterar aunque s asignar cadenas cuya longitud es diferente. -Una cadena con longitud ms corta que la de la variable, rellena a blancos por la derecha y si es ms larga la trunca. ASIGNACION DE VALORES A LAS CADENAS. -Se realiza con la sentencia de asignacin y una constante de caracteres usndose una variable cadena para inicializar otra variable de cadena. -Si los caracteres asignados no coinciden con la longitud se rellenan a blancos y si es mayor que la longitud se trunca por la derecha. COMPARACION DE CADENAS. -Esta comparacin se realiza carcter a carcter de izquierda a derecha con las siguientes reglas: -Si las cadenas tienen igual longitud y los caracteres son los mismos, las cadenas son iguales. -Si una cadena es ms corta que la otra se aaden blancos a la derecha de la otra cadena, de modo que pueda proceder a la evaluacin como si las cadenas fueran iguales. -Las reglas de ordenacin tpicas son las siguientes: -Las letras maysculas estn ordenadas de A a Z. -Los dgitos ordenados de 0 a 9. -El carcter blanco es menor que cualquier letra o nmero. SUBCADENAS. -Es cualquier cadena que representa un subconjunto de la cadena original y mantiene el orden original. Para especificar una subcadena de una variable de carcter o un elemento de un array de carcter se usa: -Nombrecadena ( Expresin1 : Expresin2 ). -Expresin1 es la posicin en nombrecadena del primer carcter de la subcadena y expresin2 es la posicin en nombrecadena del ltimo carcter de la subcadena. -Expresin1 y expresin2 deben ser del tipo entero y cumplir: -1<=Expresin1<=Expresin2<=Longitud de la cadena.

446

-Si se omite la expresin1 se toma por defecto uno. Si se omite la expresin2 se toma el valor de la longitud de la cadena original, siendo la subcadena: -Expresin2-Expresin1+1. CONCATENACION DE CADENAS. -Consiste en combinar dos o ms cadenas de caracteres en una nica cadena, siendo el operador que realiza la concatencacin o unin de cadenas el siguiente: -//. FUNCION LONGITUD. SENTENCIAS "LEN" Y "GETLEN". -LEN determina la longitud de la cadena de caracteres argumento siendo su sintaxis: -LEN (Cadena de caracteres). -Si la cadena de caracteres es una constante de carcter su longitud es el nmero de caracteres. Si es una variable de cadena o elemento de array la longitud es la definida en la declaracin. -Si cadena es una subcadena con el formato (Expresin1:Expresin2) su longitud es la siguiente: -Expresin2-Expresin1+1. -GETLEN calcula la longitud de una cadena de caracteres excluyendo a los caracteres en blanco siendo su formato: -GETLEN (Cadena de caracteres). FUNCIONES DE TRATAMIENTO DE CARACTERES. SENTENCIAS "CHAR" E "ICHAR". -CHAR determina el carcter de la cadena que ocupa la posicin relativa en la secuencia de caracteres ASCII siendo su sintaxis: -CHAR (Posicin). -El valor de posicin debe estar entre 0 y 255 caracteres de la cadena. -ICHAR es la Funcin inversa de CHAR. El argumento es un carcter y la Funcin devuelve un entero que es la posicin del carcter en la secuencia ordenada de caracteres ASCII con el formato: -ICHAR (Carcter). FUNCION DE BUSQUEDA. SENTENCIA "INDEX". -Esta Funcin localiza una subcadena dentro de otra. Devuelve un valor entero que indica la posicin inicial de la cadena de caracteres destino dentro de la cadena original siendo su sintaxis: -INDEX (Cadena fuente, Cadena destino).

447

-Si la cadena destino no existe el formato devuelve el valor cero. OTRAS FUNCIONES. -La Funcin LEN_TRIM devuelve la longitud de la cadena dada sin los espacios en blanco siendo su sintaxis: -LEN_TRIM (Cadena de caracteres). -La Funcin SCAN busca una subcadena en una cadena dada y muestra la primera posicin en la que coinciden ambas cadenas, buscando carcter a carcter, siendo su sintaxis: -SCAN (Cadena1, Cadena2). -La Funcin VERIFY devuelve un entero y verifica que una cadena est includa en otra, devolviendo la posicin del carcter que sea distinto de los dems y siendo su sintaxis: -VERIFY (Cadena1, Cadena2). -Otras Funciones que devuelven un valor lgico y que sirven para la comparacin son: -LGE (Cadena1, Cadena2), verifica si cadena1 es mayor o igual que cadena2. -LGT (Cadena1, Cadena2), verifica si cadena1 es mayor que cadena2. -LLE (Cadena1, Cadena2), verifica si cadena1 es menor o igual que cadena2. -LLT (Cadena1, Cadena2), verifica si cadena1 es menor que cadena2. -En estas cuatro ltimas Funciones el argumento debe ser siempre un carcter.

TEMA 9: FICHEROS.
INTRODUCCION. ESTRUCTURA DE UN FICHERO. -Un fichero es una coleccin de datos organizados de alguna manera y almacenados generalmente en disco o cinta. -Un fichero est formado por registros y estos constan de campos que pueden ser numricos o de caracteres. -Los ficheros formateados pueden ser editados, imprimirse o visualizarse mientras que los ficheros no formateados no pueden hacer esas acciones. Aunque la lectura y la escritura son ms rpidas y ocupan poca memoria. ORGANIZACION DE FICHEROS. -Se consideran dos tipos de acceso a los registros de un fichero que son los siguientes:

448

-Acceso Secuencial. -Acceso Directo. -El acceso Secuencial implica el acceso a un fichero segn el orden de almacenamiento de sus registros, uno a uno. -El acceso Directo implica situarse en un registro determinado sin que ello implique la consulta de los registros precedentes. -La Organizacin de un fichero define la forma en que los registros se disponen sobre el soporte de almacenamiento: -Organizacin Secuencial. -Organizacin Directa. -Organizacin Indexada. -Un fichero con organizacin Secuencial es una sucesin de registros almacenados consecutivamente sobre un soporte externo de tal modo que para acceder al registro n, necesariamente hay que pasar por los n-1 registros precedentes. -Un fichero con organizacin Directa exige soporte direccionable. Cada posicin se localiza por su direccin absoluta, que en el caso del disco suele venir definida por nmero de pista y de sector. -El lenguaje Fortran no es muy fuerte es la manipulacin de archivos aunque tiene capacidad para manipularlos. -Los archivos suelen ser pequeos y la informacin a tratar suele estar en sentencias DATA. APERTURA DE UN FICHERO. SENTENCIA "OPEN". -Esta sentencia es la encargada de la apertura de un fichero, conectando un fichero a un nmero de Unidad, de forma que para referirse despus al fichero se har con el nmero de Unidad establecido en la apertura. -Cuando se quiere crear un fichero en un programa se utilizar la sentencia OPEN para que quede en una Unidad. -La sentencia OPEN tambin es usada para declarar las propiedades del fichero, si es Secuencial o Directo, si es Formateado o no Formateado y otras propiedades. -La sintaxis de la sentencia ser la siguiente: -OPEN ( UNIT= N, FILE='Nombre' , ACCESS=Tipo , FORM=Formato , STATUS=Estado , IOSTAT=N , ERR=Etiqueta , BLANK=Tipo , RECL=N , BLOCKSIZE=N , MODE=Tipo ). -La sentencia OPEN es la primera sentencia que debe aparecer al utilizar un fichero.

449

-La opcin UNIT siempre debe figurar en la sentencia OPEN y se podr omitir, en cuyo caso N deber figurar como primer parmetro. -Esta opcin se encarga de asignar un canal de comunicacin para ejecutar el archivo. -La opcin FILE indica el nombre del fichero que va a estar conectado a la unidad N. Si no se coloca el nombre del fichero se podr pasar por parmetro el nombre de dicho fichero. -La opcin ACCESS, donde Tipo es una expresin de tipo carcter e indica el tipo de fichero que se va a utilizar, siendo por defecto Secuencial y puediendo tomar los valores: -SEQUENTIAL. -DIRECT. -La opcin FORM, donde Formato es una expresin de tipo carcter en que el valor por defecto es Secuencial, asigna un formato al fichero, siendo los valores que puede tomar: -FORMATTED. -UNFORMATTED. -BINARY. -Si la opcin es omitida OPEN asumir FORMATTED si el fichero es de tipo Secuencial y UNFORMATTED si es un fichero Directo. BINARY se utiliza en Fortran 90 y representa a los ficheros binarios. -La opcin STATUS, donde Estado es una expresin de tipo carcter y se usa para saber si el fichero ya existe o es nuevo y por tanto va a ser creado. Sus opciones son las siguientes: -OLD (Si el fichero existe, sino produce error). -NEW (Crea el nuevo fichero y FILE no debe existir en la Unidad). -SCRATCH (Si crea un fichero temporal y no debe darse nombre al fichero). -UNKNOWN (Si no se sabe si existe o no el fichero siendo la opcin por defecto). -Un fichero SCRATCH desaparece cuando se cierra la Unidad o cuando termina la ejecucin. -La opcin IOSTAT, donde N es un identificador de una variable entera. Almacena un cdigo numrico de cmo se ejecuta la sentencia OPEN. Si OPEN se ejecuta sin error se almacena un cero en N. -La opcin ERR, donde Etiqueta es la sentencia donde se bifurca incondicionalmente si el fichero no puede ser abierto al producirse un error. -La opcin BLANK, donde Tipo es una expresin de tipo carcter que puede tomar los siguientes valores:

450

-NULL (Los blancos son ignorados y es opcin por defecto). -ZERO (Los blancos son interpretados como ceros). -Esta opcin se usa para especificar la interpretacin de los espacios en blanco en los datos de entrada. -La opcin RECL, donde N es una expresin entera, indica la longitud en caracteres de los registros en un fichero de acceso Directo. Slo debe aparecer en los ficheros Directos. -La opcin BLOCKSIZE, donde N es un nmero entero, asigna un tamao al buffer de lectura intermedio. -La opcin MODE, donde Tipo es una cadena de caracteres, es el modo de apertura del fichero. La opcin por defecto es la de lectura escritura siendo las opciones: -READ. -WRITE. -READWRITE. -Todas las opciones son opcionales excepto UNIT que es obligatorio, y si el fichero es de acceso Directo es obligatorio especificar RECL. ESCRITURA DE UN FICHERO SECUENCIAL. -Consiste en transferir informacin desde la memoria principal al soporte externo donde est el fichero. -Para escribir en un fichero Secuencial se usa WRITE especificando la Unidad a la que est conectada el fichero indicando que el fichero es Secuencial. -La sentencia de escritura tiene la siguiente sintaxis: -WRITE ( UNIT= N , FORM=Formato , ERR=Etiqueta , IOSTAT=N , ENDFILE=Unidad ). -La nica opcin obligatoria sera UNIT, todas las dems han quedado explicadas. -Al crear un fichero los registros se escriben uno a continuacin del otro, escribiendo un registro especial EOF al final de los datos escritos. -Normalmente con CLOSE el registro EOF es automticamente escrito al cerrar el fichero, aunque se puede usar la siguiente sentencia: -ENDFILE N. -ENDFILE ( UNIT= N , ERR=Etiqueta , IOSTAT=N ).

451

-Donde N es el nmero de la Unidad a la que se conect el fichero Secuencial. La sintaxis ms empleada es la primera. Todas las dems opciones ya han quedado explicadas. -Si se ejecuta ENDFILE, el nombre de la Unidad a la que est conectado un fichero nuevo, sin datos, crear un fichero vaco. SENTENCIA DE CIERRE DE UN FICHERO "CLOSE". -Esta sentencia rompe la conexin de un fichero con la Unidad a la que se conect un OPEN siendo su sintaxis: -CLOSE ( UNIT= N , STATUS=Tipo , IOSTAT=N , ERR=Etiqueta ). -La opcin UNIT, siendo N un entero, es el nmero de la Unidad donde se encuentra el fichero que se va a desconectar y puede omitirse, en cuyo caso, ser el primer parmetro de CLOSE. -La opcin STATUS, siendo Tipo una expresin tipo carcter indica el estado del fichero en ese momento, puediendo tener los valores: -KEEP (El fichero se guarda despus de ser cerrado). -DELETE (El fichero es borrado perdindose despus del cierre). -Por defecto toma el valor KEEP, excepto para los ficheros que son de tipo SCRATCH. -Los posibles valores que se pueden sacar de las opciones FORM y ACCESS son los siguientes: -Registros Secuencial-Formateados. -Registros Secuencial-No formateados. -Registros Directo-Formateados. -Registros Directo-No formateados. -Registros Binario-Formateado. -Registros Binario-No formateado. -Los registros de tipo Secuencial-Formateados se almacenan en cdigo ASCII y permiten verse con cualquier editor de texto y manipularlos. -Si tenemos las siguientes instrucciones: -VarI=4 OPEN (33, FILE='FSEQ') WRITE (33, '(A, I3)') 'RECORD', I/3 WRITE (33, 'C3')

452

WRITE (33, '(11H El Reg-N3)') CLOSE (33) -La salida que dar este ejemplo sern tres segmentos de longitud variable cuya estructura de registros ser la siguiente: -Se puede observar como despus de la primera marca de fichero OA, el segundo registro no aparece por ser de 0 bytes, mientras que el primero tendr 9 bytes y el tercero tendr 11 bytes. -Los registros de tipo Secuencial-No formateados tienen longitud variable y no estn en cdigo ASCII, sino otro cdigo que lo introduce el programa y lo organiza en bloques fsicos de 128 bytes como mximo. -Si tenemos las siguientes instrucciones: -CHARACTER Array (3) INTEGER *4 Datos (35) DATA Datos /35*-1/, Array / 'X', 'Y', 'Z'/ OPEN (33, FILE='UFSEQ', FORM='UNFORMATTED') WRITE (33) Datos WRITE (33) Array CLOSE (33) -La salida que dar este ejemplo ser la siguiente: -Como el bloque tiene un tamao mximo de 128 bytes por registro, al sacar todos los elementos del Array Datos (Que necesitar 140 bytes) se particiona en dos registros uno de 128 bytes y otro de 12 bytes. -Los registros de tipo Directo-Formateados tienen la misma longitud, que se deber especificar en la longitud del registro. -A cada registro siempre se le aade el carcter de control de carro (OD) y el carcter de avance de lnea (OA). Si tenemos las siguientes instrucciones: -OPEN (33, FILE='FDIR', FORM='FORMATTED', ACCESS='DIRECT', RECL=10) WRITE (33, '(A)', REC=1) 'Registro 1' WRITE (33, '(I5)', REC=3) 30303 CLOSE (33)

453

-La salida que dar este ejemplo ser la siguiente: -Se puede ver que del registro primero se pasa al tercero. En los ficheros Directos, se reserva espacio para los registros. -De esa forma si luego viene un registro segundo se insertara entre los registros primero y tercero. -Los registros de tipo Directo-No formateados es la opcin por defecto, en la que la longitud del registro es la misma para todos pero el compilador no introduce los caracteres OD y OA. LECTURA DE UN FICHERO SECUENCIAL. -Transfiere la informacin contenida en los registros del fichero a la memoria del ordenador representada por las variables que aparecen en la lista de la sentencia de lectura. -La sentencia READ tiene la sintaxis para ficheros Secuenciales siguiente: -READ ( UNIT= N, FORM=Formato , END=Etiqueta , ERR=Etiqueta , IOSTAT=N , REC=N ) Lista de variables. -La opcin que siempre debe figurar es el nmero de Unidad perteneciente al de apertura del fichero Secuencial. -La opcin END, donde Etiqueta es un nmero entero indica que al detectar el carcter de fin de fichero transfiera el control de ejecucin del programa a la etiqueta indicada. -Todas las dems opciones ya se han explicado. -Para leer un fichero la primera opcin a realizar es abrirlo en la opcin STATUS con el valor OLD. POSICIONAMIENTO EN UN FICHERO SECUENCIAL. -La lectura y escritura de registros se realiza uno a uno y en serie. Hay dos sentencias para regresar un registro (BACKSPACE) y para posicionarse al principio del fichero (REWIND). -La sintaxis de BACKSPACE es la siguiente: -BACKSPACE N. -BACKSPACE ( UNIT= N, IOSTAT=N , ERR=Etiqueta ). -N es el nmero de la Unidad a la cual est conectado el fichero. Si no hay registro anterior porque el fichero se acaba de abrir no tiene efecto. -Todas las dems opciones ya se han explicado. -La sentencia REWIND tiene la siguiente sintaxis:

454

-REWIND N. -REWIND ( UNIT= N, IOSTAT=N , ERR=Etiqueta ). -El efecto es rebobinar el fichero al comienzo, apuntando al primer registro. Todas las dems opciones ya se han explicado. CREACION DE UN FICHERO DE ACCESO DIRECTO. -Cada registro de un fichero de acceso Directo es identificado nicamente por su posicin lgica en el fichero o nmero de registro que es un entero de 1 a n. -El acceso a los registros se hace siempre a partir del nmero de registro con el que fue creado. En un fichero de acceso Directo no tiene sentido el registro EOF. -Por ello ENDFILE no se debe ejecutar sobre estos ficheros, porque no tiene efecto. -En un fichero de acceso Directo se reserva espacio de almacenamiento para todos los posibles registros. -En los ficheros de acceso Directo los datos de entrada o de salida deben ir siempre con formato. Este tipo de ficheros es creado al abrirlo, especificando acceso directo y dndole una longitud. -Los ficheros no formateados guardan la informacin en cdigo binario, siendo el acceso a los registros ms rpidos porque se elimina el tiempo de conversin a binario. -Los registros pueden ser escritos o ledos en cualquier orden. Siempre hay que indicar en las sentencias READ y WRITE un parmetro REC que indicar el nmero de registro a acceder. -El proceso para leer un registro es similar al de escribir. Hay que especificar el nmero de registro que se quiere leer. SENTENCIA "INQUIRE". -Con esta sentencia durante la ejecucin de un programa puede obtenerse informacin sobre las caractersticas de una Unidad o de un fichero. -Puede ejecutarse antes de que un fichero haya sido abierto, conectado a una Unidad. -La informacin que puede requerirse de un fichero o de una Unidad es muy variada. La sintaxis para esta sentencia es la siguiente: -INQUIRE (UNIT=N, Lista de especificadores). -Donde N es un nmero de unidad sobre el que se va a preguntar siendo la lista de especificadores opcionales los siguientes: -ACCESS=Var*10 ('SEQUENTIAL' o 'DIRECT'). -BLANK=Var*4 ('NULL' o 'ZERO'). -DIRECT=Var*7 ('YES', 'NO' o 'UNKNOWN').

455

-EXIST=Var ('.TRUE.' o '.FALSE.'). -FORM=Var ('UNFORMATTED' o 'FORMATTED). -FORMATTED=Var ('YES', 'NO' o 'UNKNOWN'). -NAME=Var*3 ('.TRUE.', '.FALSE.' o 'Nombre'). -NEXTREC=N (Registro despus del ltimo accedido). -NUMBER=N (Nmero de Unidad igual a UNIT). -OPENED=Var ('.TRUE.' o '.FALSE.'). -RECL=N (Longitud del registro). -SEQUENTIAL=Var ('YES', 'NO' o 'UNKNOWN'). -UNFORMATTED=Var ('YES', 'NO' o 'UNKNOWN'). -IOSTAT=N (Cdigo de estado). -ERR=Etiqueta (Bifurcaciones de error). -BINARY=Var*10 ('YES', 'NO' o 'UNKNOWN'). -BLOKSIZE=N (Tamao del buffer). -Si se produce error en INQUIRE todas las variables que figuran en la sentencia queda un valor indefinido excepto la variable entera de STATUS y las variables de los especificadores pueden ser elementos de un Array. -Otra posibilidad de INQUIRE es preguntar por el fichero con la siguiente sintaxis: -INQUIRE (FILE='Nombre', Lista de especificadores). -La lista de especificadores es la misma que la anterior. Si el nombre del fichero es un identificador vlido para el sistema y si el fichero existe se aadirn los siguientes: -DIRECT, FORMATTED, NAME, SEQUENTIAL y UNFORMATTED. -Si el fichero est abierto puede aplicarse: -ACCESS, BLANK, FORM, NEXTREC, NUMBER, BELL, IOSTAT y ERR. -La ventaja es que provee de informacin muy valiosa para abrir ficheros o evitar errores que abortan la ejecucin de un programa. FICHEROS INTERNOS.

456

-Cuando el programa ejecuta READ o WRITE con un fichero o dispositivo externo se realizan las dos operaciones conjuntamente. -En los ficheros internos la transferencia de informacin se produce entre dos reas de memoria interna. Un fichero interno es un rea de almacenamiento interno. -Las entradas o salidas con ficheros internos deben ser siempre formateadas con los cdigos de formato deseados por el programador. -Con los ficheros internos hay que especificar siempre los cdigos de formato en las sentencias WRITE y READ. -Para acceder a ms de un registro en un fichero interno hay que ejecutar una sla vez la sentencia READ o WRITE. -Las opciones que no se permiten usar son las siguientes: -OPEN, CLOSE, INQUIRE, REWIND, BACKSPACE y ENDFILE. FICHEROS BINARIOS. -Es un fichero de tipo Secuencial aunque tambin podemos tener ficheros Directos, pero de esta forma permite recibir o escribir ms de un registro a la vez. -No se separan los registros y se lee de la misma manera pero en el Directo hay que poner en la opcin de formato lo siguiente: -FORM='BINARY'. -Si tenemos las siguientes instrucciones: -INTEGER *1 VarA (4) CHARACTER VarB (3) CHARACTER *4 VarC DATA VarA /4*7/ DATA VarC /'Esto'/, VarB /'A', 'B', 'C'/ OPEN (33, FILE='FBIN', FORM='BINARY') WRITE (33) VarB, VarC WRITE (33) 'Que', 'Quieres' WRITE (33) VarA CLOSE (33)

457

-La salida que producir en el registro ser la siguiente: -Notar que la separacin es imaginaria. Siempre en un fichero Directo se han de inicializar las variables normalmente.

458

Curso de Pascal
Tema 0. Introduccin.
Hay distintos lenguajes que nos permiten dar instrucciones a un ordenador. El ms directo es el propio del ordenador, llamado "lenguaje de mquina" o "cdigo mquina", formado por secuencias de ceros y unos. Este lenguaje es muy poco intuitivo para nosotros, y difcil de usar. Por ello se recurre a otros lenguajes ms avanzados, ms cercanos al propio lenguaje humano (lenguajes de alto nivel), y es entonces el mismo ordenador el que se encarga de convertirlo a algo que pueda manejar directamente. Se puede distinguir dos tipos de lenguajes, segn se realice esta conversin: 1. En los intrpretes, cada instruccin que contiene el programa se va convirtiendo a cdigo mquina antes de ejecutarla, lo que hace que sean ms lentos. 2. En los compiladores, se convierte todo el programa en bloque a cdigo mquina y despus se ejecuta. As, hay que esperar ms que en un intrprete para comenzar a ver trabajar el programa, pero despus ste funciona mucho ms rpido. La mayora de los lenguajes actuales son compiladores, y suelen incluir: Un editor para escribir o revisar los programas. El compilador propiamente dicho, que los convierte a cdigo mquina. Otros mdulos auxiliares, como enlazadores (linkers) para unir distintos subprogramas, y depuradores (debuggers) para ayudar a descubrir errores.

Algunos de los lenguajes ms difundidos son: BASIC, que durante mucho tiempo se ha considerado un buen lenguaje para comenzar a aprender, por su sencillez, aunque se poda tender a crear programas poco legibles. A pesar de esta "sencillez" hay versiones muy potentes, incluso para programar en entornos grficos como Windows (es el caso de Visual Basic). COBOL, que fue muy utilizado para negocios, aunque ltimamente est bastante en desuso. FORTRAN, concebido para ingeniera, operaciones matemticas, etc. Tambin va quedando desplazado. Ensamblador, muy cercano al cdigo mquina, pero sustituye las secuencias de ceros y unos (bits) por palabras ms fciles de recordar, como MOV, ADD, CALL o JMP. C, el mejor considerado actualmente, porque no es difcil y permite un grado de control del ordenador muy alto, combinando caractersticas de lenguajes de alto y bajo nivel. Adems, es muy transportable: existe un estndar, el ANSI C, lo que asegura que se pueden convertir programas en C de un ordenador a otro o de un sistema operativo a otro con bastante menos esfuerzo que en otros lenguajes. PASCAL, el lenguaje estructurado (ya se ir viendo esto ms adelante) por excelencia, y que en algunas versiones tiene una potencia comparable a la del lenguaje C, como es el caso de Turbo Pascal en programacin para DOS y Windows. Frente al C tiene el inconveniente de que es menos portable, y la ventaja de que en el caso concreto de la

459

programacin para DOS, Turbo Pascal no tiene nada que envidiar la mayora de versiones del lenguaje C, pero resulta ms fcil de aprender, es muy rpido, crea ficheros EXE ms pequeos, etc.

Dos conceptos que se mencionan mucho al hablar de programacin son "programacin estructurada" y "programacin orientada a objetos". La programacin estructurada consiste en dotar al programa de un cierto orden, dividindolo en bloques independientes unos de otros, que se encargan de cada una de las tareas necesarias. Esto hace un programa ms fcil de leer y modificar. La programacin orientada a objetos se tratar ms adelante, cuando ya se tenga una buena base de programacin. De momento, anticipemos que "Object Pascal" es el nombre que se suele dar a un lenguaje Pascal que permita programacin orientada a objetos (como es el caso de Turbo Pascal), y que "C++" es una ampliacin del lenguaje C, que tambin soporta P.O.O. En lo que sigue vamos a ver los fundamentos de la programacin en Pascal, primero intentando ceirnos al Pascal estndar, y luego ampliando con las mejoras que incluye Turbo Pascal, la versin ms difundida.

Tema 1. Generalidades del Pascal.


Como lenguaje estructurado que es, muchas veces habr que dividir en bloques las distintas partes que componen un programa. Estos bloques se denotan marcando su principio y su final con las palabras begin y end. La gran mayora de las palabras clave de Pascal (palabras con un significado especial dentro del lenguaje) son palabras en ingls o abreviaturas de stas. No existe distincin entre maysculas y minsculas, por lo que "BEGIN" hara el mismo efecto que "begin" o "Begin". As, lo mejor ser adoptar el convenio que a cada uno le resulte ms legible: algunos autores emplean las rdenes en maysculas y el resto en minsculas, otros todo en minsculas, otros todo en minsculas salvo las iniciales de cada palabra... Yo emplear normalmente minsculas, o a veces maysculas y minsculas combinadas cuando esto haga ms legible algn comando "ms enrevesado de lo habitual" (por ejemplo, si estn formados por dos o ms palabras inglesas como OutText o SetFillStyle.) Cada sentencia (u orden) de Pascal debe terminar con un punto y coma (;), salvo el ltimo "end", que lo har con un punto. Tambin hay otras tres excepciones: no es necesario un punto y coma despus de un "begin", ni antes de una palabra "end" o de un "until" (se ver la funcin de esta palabra clave ms adelante), aunque no es mala tcnica terminar siempre cada sentencia con un punto y coma, al menos hasta que se tenga bastante soltura. Cuando definamos variables, tipos, constantes, etc., veremos que tampoco va punto y coma despus de las cabeceras de las declaraciones. Pero eso ya llegar... Con poco ms que lo visto hasta ahora ya se podra escribir un pequeo programa que hiciera aparecer el mensaje "Hola" en la pantalla: program Saludo;

460

begin write('Hola'); end.

La palabra program no es necesaria en muchos compiladores actuales, pero s lo era inicialmente en Pascal estndar, y el formato era program NombrePrograma (input, output);

(para indicar que el programa iba a manejar los dispositivos de entrada y salida). Por ejemplo, como este programa escribe en la pantalla, si se usa el Pascal de GNU, deber poner: program Saludo(output);

Aunque para nosotros no sea necesaria la lnea de "program", su empleo puede resultar cmodo si se quiere poder recordar el objetivo del programa con slo un vistazo rpido a su cabecera. Saludo es un identificador que nos va a servir para indicar el nombre del programa. Los "identificadores" son palabras que usaremos para referirnos a una variable, una constante, el nombre de una funcin o de un procedimiento, etc. Una variable equivale a la clsica incgnita "x" que todos hemos usado en matemticas, que puede ser cualquier nmero. Ahora nuestras "incgnitas" podrn tener cualquier valor (no slo un nmero: tambin podremos guardar textos, fichas sobre personas o libros, etc.) y nombres ms largos (y que expliquen mejor su contenido). Estos nombres de "identificadores" sern combinaciones de letras y nmeros, junto con algunos (pocos) smbolos especiales, como el de subrayado (_). No podrn empezar con un nmero, sino por un carcter alfabtico (A a Z, sin ni acentos) o un subrayado, y no podrn contener espacios. As, seran identificadores correctos: Nombre_De_Programa, programa2, _SegundoPrograma pero no seran admisibles 2programa, 2prog, tal&tal, Prueba de programa, ProgramaParaM (unos por empezar por nmeros, otros por tener caracteres no aceptados, y otros por las dos cosas). Las palabras "begin" y "end" marcan el principio y el final del programa, que esta vez slo se compone de una lnea. Ntese que, como se dijo, el ltimo "end" debe terminar con un punto. "Write" es la orden que permite escribir un texto en pantalla. El conjunto de todo lo que se desee escribir se indica entre parntesis. Cuando se trata de un texto que queremos que aparezca "tal cual", ste se encierra entre comillas (una comilla simple para el principio y otra para el final, como aparece en el ejemplo).

461

El punto y coma que sigue a la orden "write" no es necesario (va justo antes de un "end"), pero tampoco es un error; as que podemos dejarlo, por si despus aadimos otra orden entre "write" y "end". La orden "write" aparece algo ms a la derecha que el resto. Esto se llama escritura indentada, y consiste en escribir a la misma altura todos los comandos que se encuentran a un mismo nivel, algo ms a la derecha los que estn en un nivel inferior, y as sucesivamente. Se ir viendo con ms detalle a medida que se avanza. En un programa en Pascal no hay necesidad de conservar una estructura tal que aparezca cada orden en una lnea distinta. Se suele hacer as por claridad, pero realmente son los puntos y coma (cuando son necesarios) lo que indica el final de una orden, por lo que el programa anterior se podra haber escrito: program Saludo; begin write('Hola') end.

Una ltima observacin: si se compila este programa desde Turbo Pascal 5.0 o una versin superior, aparentemente "no pasa nada". No es as, sino que se ejecuta y se vuelve al editor tan rpido que no nos da tiempo a verlo. La solucin es pulsar Alt+F5 para que nos muestre la pantalla del DOS.

Tema 2. Introduccin al manejo de variables.


Las variables son algo que no contiene un valor predeterminado, una posicin de memoria a la que nosotros asignamos un nombre y en la que podremos almacenar datos. En el primer ejemplo que vimos, puede que no nos interese escribir siempre el mensaje "Hola", sino uno ms personalizado segn quien ejecute el programa. Podramos preguntar su nombre al usuario, guardarlo en una variable y despus escribirlo a continuacin de la palabra "Hola", con lo que el programa quedara program Saludo2; var nombre: string; begin writeln('Introduce tu nombre, por favor'); readln(nombre); write('Hola ',nombre); end.

Aqu ya aparecen ms conceptos nuevos. En primer lugar, hemos definido una variable, para lo que empleamos la palabra var, seguida del nombre que vamos a dar a la variable, y del tipo de datos que va a almacenar esa variable.

462

Los nombres de las variables siguen las reglas que ya habamos mencionado para los identificadores en general. Con la palabra string decimos que la variable nombre va a contener una cadena de caracteres (letras o nmeros). Un poco ms adelante, en esta misma leccin, comentamos los principales tipos de datos que vamos a manejar. Pasemos al cuerpo del programa. En l comenzamos escribiendo un mensaje de aviso. Esta vez se ha empleado "writeln", que es exactamente igual que "write", con la nica diferencia de que despus de visualizar el mensaje, el cursor (la posicin en la que se seguira escribiendo, marcada normalmente por una rayita o un cuadrado que parpadea) pasa a la lnea siguiente, en vez de quedarse justo despus del mensaje escrito. Despus se espera a que el usuario introduzca su nombre, que le asignamos a la variable "nombre", es decir, lo guardamos en una posicin de memoria cualquiera, que el compilador ha reservado para nosotros, y que nosotros no necesitamos conocer (no nos hace falta saber que est en la posicin 7245 de la memoria, por ejemplo) porque siempre nos referiremos a ella llamndola "nombre". De todo esto se encarga la orden "readln". Finalmente, aparece en pantalla la palabra "Hola" seguida por el nombre que se ha introducido. Como se ve en el ejemplo, "writeln" puede escribir ms de un dato, pero eso lo estudiaremos con detalle un poco ms adelante... Tipos bsicos de datos En Pascal debemos declarar las variables que vamos a usar. Esto puede parecer incmodo para quien ya haya trabajado en Basic, pero en la prctica ayuda a conseguir programas ms legibles y ms fciles de corregir o ampliar. Adems, evita los errores que puedan surgir al emplear variables incorrectas: si queremos usar "nombre" pero escribimos "nombe", la mayora de las versiones del lenguaje Basic no indicaran un error, sino que consideraran que se trata de una variable nueva, que no tendra ningn valor, y normalmente se le asignara un valor de 0 o de un texto vaco. En Pascal disponemos de una serie de tipos predefinidos, y de otros que podemos crear nosotros para ampliar el lenguaje. Los primeros tipos que veremos son los siguientes: Byte. Es un nmero entero, que puede valer entre 0 y 255. El espacio que ocupa en memoria es el de 1 byte, como su propio nombre indica. Integer. Es un nmero entero con signo, que puede valer desde -32768 hasta 32767. Ocupa 2 bytes de memoria. Char. Representa a un carcter (letra, nmero o smbolo). Ocupa 1 byte. String. Es una cadena de caracteres, empleado para almacenar y representar mensajes de ms de una letra (hasta 255). Ocupa 256 bytes. El formato en Pascal estndar (y en Turbo Pascal, hasta la versin 3.01) era string[n], donde n es la anchura mxima que queremos almacenar en esa cadena de caracteres (de 0 a 255), y entonces ocupar n+1 bytes en memoria. En las ltimas versiones de Turbo Pascal podemos usar el formato "string[n]" o simplemente "string", que equivale a "string[255]", como apareca en el ejemplo anterior.

463

Real. Es un numero real con signo. Puede almacenar nmeros con valores entre 2.9e-39 y 1.7e38 (en notacin cientfica, e5 equivale a multiplicar por 105), con 11 o 12 dgitos significativos, y que ocupan 6 bytes en memoria. Boolean. Es una variable lgica, que puede valer TRUE (verdadero) o FALSE (falso), y se usa para comprobar condiciones. Array. Se emplea para definir vectores o matrices. Se deber indicar el ndice inferior y superior, separados por dos puntos (..), as como el tipo de datos.

Ejemplo: un vector formado por 10 nmeros enteros sera vector: array[1..10] of integer

y una matriz de dimensiones 3x2 que debiera contener nmeros reales: matriz1: array[1..3,1..2] of real

Para mostrar en pantalla el segundo elemento del vector se usara write(vector[2]);

y para ver el elemento (3,1) de la matriz, writeln(matriz1[3,1]);

Record. La principal limitacin de un array es que todos los datos que contiene deben ser del mismo tipo. Pero a veces nos interesa agrupar datos de distinta naturaleza, como pueden ser el nombre y la edad de una persona, que seran del tipo string y byte, respectivamente. Entonces empleamos los records o registros, que se definen indicando el nombre y el tipo de cada campo (cada dato que guardamos en el registro), y se accede a estos campos indicando el nombre de la variable y el del campo separados por un punto:

program Ejemplo_de_registro; var dato: record nombre: string[20];

464

edad: byte; end;

begin dato.nombre:='Jos Ignacio'; dato.edad:=23; write('El nombre es ', dato.nombre ); write(' y la edad ', dato.edad, ' aos.'); end.

La nica novedad en la definicin de la variable es la aparicin de una palabra end despus de los nombres de los campos, lo que indica que hemos terminado de enumerar stos. Ya dentro del cuerpo del programa, vemos la forma de acceder a estos campos, tanto para darles un valor como para imprimirlo, indicando el nombre de la variable a la que pertenecen, seguido por un punto. El conjunto := es la sentencia de asignacin en Pascal, y quiere decir que la variable que aparece a su izquierda va a tomar el valor que est escrito a la derecha (por ejemplo, x := 2 ). Puede parecer engorroso el hecho de escribir "dato." antes de cada campo. Tambin hay una forma de solucionarlo: cuando vamos a realizar varias operaciones sobre los campos de un mismo registro (record), empleamos la orden with, con la que el programa anterior quedara program Ejemplo_de_registro; var dato: record nombre: string[20]; edad: byte; end; begin with dato do begin nombre:='Jos Ignacio'; edad:=23; write('El nombre es ',nombre); write(' y la edad ',edad,' aos.'); end; end.

En este caso tenemos un nuevo bloque en el cuerpo del programa, delimitado por el "begin" y el "end" situados ms a la derecha, y equivale a decir "en toda esta parte del programa me estoy refiriendo a la variable dato". As, podemos nombrar los campos que queremos modificar o escribir, sin necesidad de repetir a qu variable pertenecen.

465

Ejemplos. Ejemplo 1: Cambiar el valor de una variable. program NuevoValor; var numero: integer; begin numero := 25; writeln('La variable vale ', numero); numero := 50; writeln('Ahora vale ', numero); numero := numero + 10; writeln('Y ahora ', numero); writeln('Introduce ahora t el valor'); readln( numero ); writeln('Finalmente, ahora vale ', numero); end. Ejemplo 2: Sumar dos nmeros enteros. program SumaDosNumeros; var numero1, numero2, suma: integer; begin writeln('Introduce el primer nmero'); readln( numero1 ); writeln('Introduce el segundo nmero'); readln( numero2 ); suma := numero1 + numero2; writeln('La suma de los dos nmeros es: ', suma); end. Ejemplo 3: Media de los elementos de un vector. Este es un programa nada optimizado, para que se adapte a los conocimientos que tenemos por ahora y se vea cmo se manejan los Arrays. Admite muchas mejoras, que iremos viendo ms adelante. Como novedades sobre la leccin, incluye la forma de dejar una lnea de pantalla en blanco (con writeln), o de definir de una sola vez varias variables que sean del mismo tipo, separadas por comas. Las operaciones matemticas se vern con ms detalle en la prxima leccin. program MediadelVector; var vector: array [1..5] of real; suma, media: real; begin writeln('Media de un vector con 5 elementos.'); writeln; writeln('Introduce el primer elemento'); readln(vector[1]); writeln('Introduce el segundo elemento');

466

readln(vector[2]); writeln('Introduce el tercer elemento'); readln(vector[3]); writeln('Introduce el cuarto elemento'); readln(vector[4]); writeln('Introduce el quinto elemento'); readln(vector[5]); suma := vector[1] + vector[2] + vector[3] + vector[4] + vector[5]; media := suma / 5; writeln('La media de sus elementos es: ', media); end. Como todava llevamos pocos conocimientos acumulados, la cosa se queda aqu, pero con la siguiente leccin ya podremos realizar operaciones matemticas algo ms serias, y comparaciones lgicas.

Tema 3. Entrada/Salida bsica.


Ya hemos visto por encima las dos formas ms habituales de mostrar datos en pantalla, con "write" o "writeln", y de aceptar la introduccin de datos por parte del usuario, con "readln" (o "read", que no efecta un retorno de carro despus de leer los datos). Veamos ahora su manejo y algunas de sus posibilidades con ms detalle: Para mostrar datos, tanto en pantalla como en impresora, se emplean write y writeln. La diferencia entre ambos es que "write" deja el cursor en la misma lnea, a continuacin del texto escrito, mientras que "writeln" baja a la lnea inferior. Ambas rdenes pueden escribir tipos casi de cualquier clase: cadenas de texto, nmeros enteros o reales, etc. No podremos escribir directamente arrays, records, ni muchos de los datos definidos por el usuario. Cuando se desee escribir varias cosas en la misma lnea, todas ellas se indican entre un mismo parntesis, y separadas por comas. Se puede especificar la anchura de lo escrito, mediante el smbolo de dos puntos (:) y la cifra que indique la anchura. Si se trata de un nmero real y queremos indicar tambin el nmero de decimales, esto se hace tambin despus de los dos puntos, con el formato ":anchura_total:decimales". Como ejemplos: write ('Hola, ',nombre,' qu tal ests?'); writeln (resultado:5:2); writeln('Hola,',nombre:10,'. Tu edad es:',edad:2); En el caso de una cadena de texto, la anchura que se indica es la que se tomar como mnima: si el texto es mayor no se "parte", pero si es menor, se rellena con espacios por la izquierda hasta completar la anchura deseada. Igual ocurre con los nmeros: si es ms grande que la anchura indicada, no se "parte", sino que se escribe completo. Si es menor, se rellena con espacios por la izquierda. Los decimales s que se redondean al nmero de posiciones indicado: var num: real; begin num := 1234567.89; writeln(num); (* La lnea anterior lo escribe con el formato por defecto:

467

exponencial *) writeln(num:20:3); writeln(num:7:2); writeln(num:4:1); writeln(num:3:0); writeln(num:5); end.

(* (* (* (* (*

Con tres decimales *) Con dos decimales *) Con un decimal *) Sin decimales *) Qu har ahora? *)

La salida por pantalla de este programa sera: 1.2345678900E+06 1234567.890 .ej1234567.89 .ej1234567.9 .ej1234568 1.2E+06

Aqu se puede observar lo que ocurre en los distintos casos: Si no indicamos formato, se usa notacin cientfica (exponencial). Si la anchura es mayor, aade espacios por la izquierda. Si es menor, no se trunca el nmero. Si el nmero de decimales es mayor, se aaden ceros. Si ste es menor, se redondea. Si indicamos formato pero no decimales, sigue usando notacin exponencial, pero lo ms compacta que pueda, tratando de llegar al tamao que le indicamos.

En este programa ha aparecido tambin otra cosa nueva: los comentarios. Un comentario es algo que no se va a ejecutar, y que nosotros incluimos dentro del programa para que nos resulte ms legible o para aclarar lo que hace una lnea o un conjunto de lneas. En Pascal, los comentarios se encierran entre (* y *). Tambin est permitido usar { y }, tanto en Turbo Pascal como en SURPAS. Como se ve en el ejemplo, pueden ocupar ms de una lnea. En la prctica, es muy importante que un programa est bien documentado. Cuando se trabaja en grupo, la razn es evidente: a veces es la nica forma de que los dems entiendan nuestro trabajo. En estos casos, el tener que dar explicaciones "de palabra" es contraproducente: Se pierde tiempo, las cosas se olvidan... Tampoco es cmodo distribuir las indicaciones en ficheros aparte, que se suelen extraviar en el momento ms inoportuno. Lo ideal es que los comentarios aclaratorios estn siempre en el texto de nuestro programa. Pero es que cuando trabajamos solos tambin es importante, porque si releemos un programa un mes despus de haberlo escrito, lo habitual es que ya no nos acordemos de lo que haca la variable X, de por qu la habamos definido como "Record" y no como "Array", por qu dejbamos en blanco la primera ficha o por qu empezbamos a ordenar desde atrs.

468

Para tomar datos del usuario, la forma ms directa es empleando readln, que toma un texto o un nmero y asigna este valor a una variable. No avisa de lo que est haciendo, as que normalmente convendr escribir antes en pantalla un mensaje que indique al usuario qu esperamos que teclee: writeln('Por favor, introduzca su nombre'); readln(nombre); "Readln" tiene algunos inconvenientes: No termina hasta que pulsemos RETURN. La edicin es incmoda: para corregir un error slo podemos borrar todo lo que habamos escrito desde entonces, no podemos usar las flechas o INICIO/FIN para desplazarnos por el texto. Si queremos dar un valor a una variable numrica y pulsamos " 23" (un espacio delante del nmero) le dar un valor 0. ...

Ms adelante, veremos que existen formas mucho ms verstiles y cmodas de leer datos a travs del teclado, en el mismo tema en el que veamos cmo se maneja la pantalla en modo texto desde Pascal... Tema 4. Operaciones matemticas. En Pascal contamos con una serie de operadores para realizar sumas, restas, multiplicaciones y otras operaciones no tan habituales. En operaciones como +, - y * no debera haber ninguna duda. Los problemas pueden venir con casos como el de 10/3. Si 10 y 3 son nmeros enteros, qu ocurre con su divisin? En otros lenguajes como C, el resultado sera 3, la parte entera de la divisin. En Pascal no es as: el resultado sera 3.333333, un nmero real. Si queremos la parte entera de la divisin, deberemos utilizar div. Finalmente, mod nos indica cual es el resto de la divisin. El signo - se puede usar tambin para indicar negacin. All van unos ejemplillos: program operaciones; var e1, e2: integer; r1, r2, r3: real;

(* Nmeros enteros *) (* Nmeros reales *)

begin e1:=17; e2:=5; r1:=1; r2:=3.2; writeln('Empezamos...'); r3:=r1+r2; writeln('La suma de r1 y r2 es :', r3); writeln(' o tambin ', r1+r2 :5:2); (* Indicando el formato *) writeln('El producto de r1 y r2 es :', r1 * r2); writeln('El valor de r1 dividido entre r2 es :', r1 / r2); writeln('La diferencia de e2 y e1 es : ', e2 - e1); writeln('La divisin de e1 entre e2 : ', e1 / e2);

469

writeln(' Su divisin entera : ', e1 div e2); writeln(' Y el resto de la divisin : ', e1 mod e2); writeln('El opuesto de e2 es :', -e2); end.

El operador + (suma) se puede utilizar tambin para concatenar cadenas de texto, as: var texto1, texto2, texto3: string; begin texto1 := 'Hola '; texto2 := 'Cmo ests?'; texto3 := texto1 + texto2; writeln(texto3); (* Escribir "Hola Cmo ests?" *) end.

Cuando tratemos tipos de datos ms avanzados, veremos que +, - y * tambin se pueden utilizar para conjuntos, e indicarn la unin, diferencia e interseccin. Operadores lgicos Vimos de pasada que en el tema que haba unos tipos de datos llamados "boolean", y que podan valer TRUE (verdadero) o FALSE (falso). En la prxima leccin veremos cmo hacer comparaciones del estilo de "si A es mayor que B y B es mayor que C", y empezaremos a utilizar variables de este tipo, pero vamos a mencionar ya eso del "y". Podremos encadenar proposiciones de ese tipo (si A y B entonces C) con: and (y), or (), not (no) y los operadores relacionales, que se usan para comparar y son los siguientes: Operador Operacin = <> < > <= >= Igual a No igual a (distinto de) Menor que Mayor que Menor o igual que Mayor o igual que

Igual que antes, algunos de ellos (>=, <=, in) los utilizaremos tambin en los conjuntos, ms adelante. Los operadores "and", "or" y "not", junto con otros, se pueden utilizar tambin para operaciones entre bits de nmeros enteros. Lo comento de pasada para no liar a los que empiezan. De momento, ah va resumido y sin ms comentarios. Quien quiera saber ms, lo podr ver en las ampliaciones al curso bsico. Operador Operacin not and or Negacin Producto lgico Suma lgica

470

xor shl shr

Suma exclusiva Desplazamiento hacia la izquierda Desplazamiento a la derecha

Queda como ejercicio hallar (y tratar de entender) el resultado de este programita: begin writeln('All vamos... '); writeln( 5+3+4*5*2 ); writeln( (5+3)*4+3*5-8/2+7/(3-2) ); writeln( 5 div 3 + 23 mod 4 - 4 * 5 ); writeln( 125 and 6 ); (* Este para los ms osados *) end.

Tema 5: Condiciones.
Vamos a ver cmo podemos evaluar condiciones desde Pascal. La primera construccin que trataremos es if ... then. En espaol sera "si ... entonces", que expresa bastante bien lo que podemos hacer con ella. El formato es "if condicin then sentencia". Veamos un ejemplo breve antes de seguir: program if1; var numero: integer; begin writeln('Escriba un nmero'); readln(numero); if numero>0 then writeln('El nmero es positivo'); end.

La "condicin" debe ser una expresin que devuelva un valor del tipo "boolean" (verdadero/falso). La sentencia se ejecutar si ese valor es "cierto" (TRUE). Este valor puede ser tanto el resultado de una comparacin como la anterior, como una propia variable booleana. As, una forma ms "rebuscada" (pero que a veces resultar ms cmoda y ms legible) de hacer lo anterior sera: program if2; var numero: integer; esPositivo: boolean; begin writeln('Escriba un nmero'); readln(numero); esPositivo := (numero>0); if esPositivo then writeln('El nmero es positivo'); end.

471

Cuando veamos en el prximo tema las rdenes para controlar el flujo del programa, seguiremos descubriendo aplicaciones de las variables booleanas, que muchas veces uno considera "poco tiles" cuando est aprendiendo. La "sentencia" puede ser una sentencia simple o una compuesta. Las sentencias compuestas se forman agrupando varias simples entre un "begin" y un "end": program if3; var numero: integer; begin writeln('Escriba un nmero'); readln(numero); if numero<0 then begin writeln('El nmero es negativo. readln end; end.

Pulse INTRO para seguir.');

En este ejemplo, si el nmero es negativo, se ejecutan dos acciones: escribir un mensaje en pantalla y esperar a que el usuario pulse INTRO (o ENTER, o RETURN, o <-+, segn sea nuestro teclado), lo que podemos conseguir usando "readln" pero sin indicar ninguna variable en la que queremos almacenar lo que el usuario teclee. Tambin podemos indicar lo que queremos que se haga si no se cumple la condicin. Para ello tenemos la construccin if condicin then sentencia1 else sentencia2: program if4; var numero: integer; begin writeln('Escriba un nmero'); readln(numero); if numero<0 then writeln('El nmero es negativo.') else writeln('El nmero es positivo o cero.') end.

Un detalle importante que conviene tener en cuenta es que antes del "else" no debe haber un punto y coma, porque eso indicara el final de la sentencia "if...", y el compilador nos avisara con un error.

472

Las sentencias "if...then...else" se pueden encadenar: program if5; var numero: integer; begin writeln('Escriba un nmero'); readln(numero); if numero<0 then writeln('El nmero es negativo.') else if numero>0 then writeln('El nmero es positivo.') else writeln('El nmero es cero.') end. Si se deben cumplir varias condiciones a la vez, podemos enlazarlas con "and" (y). Si se pueden cumplir varias, usaremos "or" (o). Para negar, "not" (no): if if if if ( opcion = 1 ) and ( terminado = true ) then [...] ( opcion = 3 ) or ( teclaPulsada = true ) then [...] not ( preparado ) then [...] ( opcion = 2 ) and not ( nivelDeAcceso < 40 ) then [...]

Pero cuando queremos comprobar entre varios posibles valores, sera muy pesado tener que hacerlo con muchos "if" seguidos o encadenar muchos con "and" u "or". Hay una alternativa que resulta mucho ms cmoda: la orden case. Su sintaxis es case expresin of caso1: sentencia1; caso2: sentencia2; ... casoN: sentenciaN; end; o bien, si queremos indicar lo que se debe hacer si no coincide con ninguno de los valores que hemos enumerado, usamos else: case expresin of caso1: sentencia1; caso2: sentencia2; ... casoN: sentenciaN; else otraSentencia; end;

En Pascal estndar, esta construccin se empleaba con otherwise en lugar de "else" para significar "en caso contrario", as que si alguien no usa TP/BP, sino un compilador que protesta con el "else", slo tiene que probar con "otherwise".

473

Con un ejemplo se ver ms claro cmo usar "case": program case1; var letra: char; begin WriteLn('Escriba un letra'); ReadLn(letra); case letra of ' ': WriteLn('Un espacio'); 'A'..'Z', 'a'..'z': WriteLn('Una letra'); '0'..'9': WriteLn('Un dgito'); '+', '-', '*', '/': WriteLn('Un operador'); else WriteLn('No es espacio, ni letra, ni dgito, ni operador'); end; end. Como ltimo comentario: la "expresin" debe pertenecer a un tipo de datos con un nmero finito de elementos, como "integer" o "char", pero no "real". Y como se ve en el ejemplo, los "casos" posibles pueden ser valores nicos, varios valores separados por comas, o un rango de valores separados por .. (como los puntos suspensivos, pero slo dos).

Tema 6: Bucles.
Vamos a ver cmo podemos crear bucles, es decir, partes del programa que se repitan un cierto nmero de veces. Segn cmo queramos que se controle ese bucle, tenemos tres posibilidades, que vamos a empezar a ver ya por encima: for..to: La orden se repite desde que una variable tiene un valor inicial hasta que alcanza otro valor final (un cierto NUMERO de veces). while..do: Repite una sentencia MIENTRAS que sea cierta la condicin que indicamos. repeat..until: Repite un grupo de sentencias HASTA que se d una condicin.

La diferencia entre estos dos ltimos es que "while" comprueba la condicin antes de ejecutar las otras sentencias, por lo que puede que estas sentencias ni siquiera se lleguen a ejecutar, si la condicin de entrada es falsa. En "repeat", la condicin se comprueba al final, de modo que las sentencias intermedias se ejecutarn al menos una vez. Vamos a verlos con ms detalle... For. El formato de "for" es

474

for variable := ValorInicial to ValorFinal do Sentencia;

Vamos a ver algunos ejemplos. Primero, un miniprograma que escriba los nmeros del uno al diez: var contador: integer; begin for contador := 1 to 10 do writeln( contador ); end.

Los bucles "for" se pueden enlazar uno dentro de otro, de modo que un segundo ejemplo que escribiera las tablas de multiplicar del 1 al 5 podra ser var tabla, numero: integer; begin for tabla := 1 to 5 do for numero := 1 to 10 do writeln( tabla, 'por ', numero ,'es', tabla * numero ); end.

Hasta ahora hemos visto slo casos en los que despus de "for" haba un nica sentencia. Qu ocurre si queremos repetir ms de una orden? Basta encerrarlas entre "begin" y "end" para convertirlas en una sentencia compuesta. As, vamos a mejorar el ejemplo anterior haciendo que deje una lnea en blanco entre tabla y tabla: var tabla, numero: integer; begin for tabla := 1 to 5 do begin for numero := 1 to 10 do writeln( tabla, 'por ', numero ,'es', tabla * numero ); writeln; (* Lnea en blanco *) end; end.

475

Conviene recordar que es muy conveniente usar la escritura indentada, que en este caso ayuda a ver dnde empieza y termina lo que hace cada "for". Una observacin: para "contar" no necesariamente hay que usar nmeros: var letra: char; begin for letra := 'a' to 'z' do write( letra ); end.

Como ltimo comentario: con el bucle "for", tal y como lo hemos visto, slo se puede contar en forma creciente y de uno en uno. Para contar de forma decreciente, se usa "downto" en vez de "to". Para contar de dos en dos (por ejemplo), hay usar "trucos": multiplicar por dos o sumar uno dentro del cuerpo del bucle, etc... Eso s, sin modificar la variable que controla el bucle (usar cosas como "write(x*2)" en vez de "x := x*2", que pueden dar problemas en algunos compiladores). Como ejercicios propuestos: 1. 2. 3. 4. 5. 6. Un programa que escriba los nmeros 2, 4, 6, 8 ... 16. Otro que escriba 6, 5, 4,..., 1. Otro que escriba 3, 5, 7,..., 21. Otro que escriba 12, 10, 8,..., 0. Otro que multiplique dos matrices. Para los que conozcan el problema, uno de resolucin de sistemas de ecuaciones por Gauss.

While. Vimos como podamos crear estructuras repetitivas con la orden "for", y comentamos que se poda hacer tambin con "while..do", comprobando una condicin al principio, o con "repeat..until", comprobando la condicin al final de cada repeticin. Vamos a verlas con ms detalle: La sintaxis de "while" while condicin do sentencia;

Se podra traducir como "MIENTRAS condicin HAZ sentencia", o sea, que la sentencia se va a repetir mientras la condicin sea cierta. Un ejemplo que nos diga la longitud de todas las frases que queramos es: var

476

frase: string; begin writeln('Escribe frases, y deja una lnea en blanco para salir'); write( 'Primera frase?' ); readln( frase ); while frase <> '' do begin writeln( 'Su longitud es ', length(frase) ); write( 'Siguiente frase?' ); readln( frase ) end end.

En el ejemplo anterior, slo se entra al bloque begin-end (una sentencia compuesta) si la primera palabra es correcta (no es una lnea en blanco). Entonces escribe su longitud, pide la siguiente frase y vuelve a comprobar que es correcta. Como comentario casi innecesario, length es una funcin que nos dice cuantos caracteres componen una cadena de texto. Si ya de principio la condicin es falsa, entonces la sentencia no se ejecuta ninguna vez, como pasa en este ejemplo: while (2<1) do writeln('Dos es menor que uno');

Repeat .. Until. Para "repeat..until", la sintaxis es repeat sentencia; ... sentencia; sentencia until condicin;

Es decir, REPITE un grupo de sentencias HASTA que la condicin sea cierta. Cuidado con eso: es un grupo de sentencias, no slo una, como ocurra en "while", de modo que ahora no necesitaremos "begin" y "end" para crear sentencias compuestas. El conjunto de sentencias se ejecutar al menos una vez, porque la comprobacin se realiza al final.

477

Como ltimo detalle, de menor importancia, no hace falta terminar con punto y coma la sentencia que va justo antes de "until", al igual que ocurre con "end". Un ejemplo clsico es la "clave de acceso" de un programa, que iremos mejorando cuando veamos distintas formas de "esconder" lo que se teclea, bien cambiando colores o bien escribiendo otras letras, como *. program ClaveDeAcceso; var ClaveCorrecta, Intento: String; begin ClaveCorrecta := 'PascalForever'; repeat WriteLn( 'Introduce la clave de acceso...' ); ReadLn( Intento ) until Intento = ClaveCorrecta (* Aqu ira el resto del programa *) end.

Como ejercicios propuestos: 1. Mejorar el programa de la clave de acceso para que avise de que la clave no es correcta. 2. Mejorar ms todava para que slo haya tres intentos. 3. Adaptar la primera versin (el ejemplo), la segunda (la mejorada) y la tercera (re-mejorada) para que empleen "while" y no "until"

Por cierto, si alguien ha programado en Basic puede que se pregunte por la orden goto. Existe en Pascal, pero su uso va a en contra de todos los principios de la Programacin Estructurada, solo est "medio-permitido" en casos muy concretos, as que lo veremos ms adelante. Soluciones a los ejercicios. Vamos a empezar a resolver los ejercicios que habamos ido proponiendo. Antes que nada, hay que puntualizar una cosa: el que as se resuelva de una forma no quiere decir que no se pueda hacer de otras. Ni siquiera que la ma sea la mejor, porque tratar de adaptarlos al nivel que se supone que tenemos. 1.- Un programa que escriba los nmeros 2, 4, 6, 8 ... 16. program del2al16; var i: integer;

478

begin for i := 1 to 8 do writeln( i*2 ); end.

2.- Otro que escriba 6, 5, 4,..., 1. program del6al1; var i: integer; begin for i := 6 downto 1 do writeln( i ); end.

3.- Otro que escriba 3, 5, 7,..., 21. program del3al21; var i: integer; begin for i := 1 to 10 do writeln( i*2 +1 ); end.

4.- Otro que escriba 12, 10, 8,..., 0. program del12al0; var i: integer; begin for i := 6 downto 0 do writeln( i*2 ); end.

5.- Otro que multiplique dos matrices.

479

Saltamos la parte que declara las variables y que pide los datos. La parte de la multiplicacin sera, para matrices cuadradas de 10 por 10, por ejemplo: for i := 1 to 10 do for j := 1 to 10 do c[i,j] := 0; for i :=1 to for j := 1 for k := c[i,j]

(* limpia la matriz destino *)

10 do to 10 do 1 to 10 do := c[i,j] + a[k,j] * b[i,k];

6.- Resolucin de sistemas de ecuaciones por Gauss. La nica dificultad es conocer el problema: basta hacer 0 por debajo de la diagonal principal (y encima tambin, si se quiere, pero no es necesario) e ir despejando cada variable.

7.- Mejorar el programa de la clave de acceso para que avise de que la clave no es correcta. program ClaveDeAcceso2; var ClaveCorrecta, Intento: String; begin ClaveCorrecta := 'PascalForever'; repeat WriteLn( 'Introduce la clave de acceso...' ); ReadLn( Intento ) if Intento <> ClaveCorrecta then writeln( ' Esa no es la clave correcta! '); until Intento = ClaveCorrecta (* Aqu ira el resto del programa *) end.

2.- Mejorar ms todava para que slo haya tres intentos. program ClaveDeAcceso3; var ClaveCorrecta, Intento: String; NumIntento: integer;

(* nmero de intento *)

480

begin ClaveCorrecta := 'PascalForever'; NumIntento := 0; (* an no hemos probado *) repeat NumIntento := NumIntento + 1; (* siguiente intento *) WriteLn( 'Introduce la clave de acceso...' ); ReadLn( Intento ) if Intento <> ClaveCorrecta then begin writeln( ' Esa no es la clave correcta! '); if NumIntentos = 3 then exit (* sale si es el 3 *) end until Intento = ClaveCorrecta (* Aqu ira el resto del programa *) end.

3.- Adaptar la primera versin (el ejemplo), la segunda (la mejorada) y la tercera (remejorada) para que empleen "while" y no "repeat-until" Slo ponemos una de ellas, porque las dems son muy similares. program ClaveDeAcceso4; var ClaveCorrecta, Intento: String; begin ClaveCorrecta := 'PascalForever'; Intento := ''; (* cadena vaca *) while Intento <> ClaveCorrecta do (* mientras no acertemos *) begin WriteLn( 'Introduce la clave de acceso...' ); ReadLn( Intento ) if Intento <> ClaveCorrecta then writeln( ' Esa no es la clave correcta! '); end; (* fin del "while" *) (* Aqu ira el resto del programa *) end. (* equivale a ClaveDeAcceso2*)

Tema 7: Constantes y tipos.


Definicin de constantes. Cuando desarrollamos un programa, nos podemos encontrar con que hay variables que realmente "no varan" a lo largo de la ejecucin de un programa, sino que su valor es constante.

481

Hay una manera especial de definirlas, que es con el especificador "const", que tiene el formato const Nombre = Valor;

Veamos un par de ejemplos antes de seguir const MiNombre = 'Nacho Cabanes'; const PI = 3.1415926535; const LongitudMaxima = 128;

Estas constantes se manejan igual que variables como las que habamos visto hasta hora, slo que no se puede cambiar su valor. As, es valido hacer Writeln(MiNombre); if Longitud > LongitudMaxima then ... OtraVariable := MiNombre; LongCircunf := 2 * PI * r;

pero no podramos hacer PI := 3.14; MiNombre := 'Nacho'; LongitudMaxima := LongitudMaxima + 10;

Las constantes son mucho ms prcticas de lo que puede parecer a primera vista (especialmente para quien venga de lenguajes como Basic, en el que no existen -en el Basic "de siempre", puede que s exista en los ltimas versiones del lenguaje). Me explico con un ejemplo : Supongamos que estamos haciendo nuestra agenda en Pascal (ya falta menos para que sea verdad), y estamos tan orgullosos de ella que queremos que en cada pantalla de cada parte del programa aparezca nuestro nombre, el del programa y la versin actual. Si lo escribimos de nuevas cada vez, adems de perder tiempo tecleando ms, corremos el riesgo de que un da queramos cambiar el nombre (ya no se llamar "Agenda" sino "SuperAgenda") pero lo hagamos en unas partes s y en otras no, etc., y el resultado tan maravilloso quede estropeado por esos "detalles". O si queremos cambiar la anchura de cada dato que guardamos de nuestros amigos, porque el espacio para el nombre nos haba quedado demasiado escaso, tendramos que recorrer todo el programa de arriba a abajo, con los mismos problemas, pero esta vez ms graves an, porque puede que intentemos grabar una ficha con una tamao y leerla con otro distinto... La solucin ser definir todo ese tipo de datos como constantes al principio del programa, de modo que con un vistazo a esta zona podemos hacer cambios globales: const

482

Nombre = 'Nacho'; Prog = 'SuperAgenda en Pascal'; Versin = 1.95; LongNombre = 40; LongTelef = 9; LongDirec = 60; ...

Las declaraciones de las constantes se hacen antes del cuerpo del programa principal, y generalmente antes de las declaraciones de variables: program MiniAgenda; const NumFichas = 50; var Datos: array[ 1..NumFichas ] of string; begin ...

El identificador "const" tiene tambin en Turbo Pascal otro uso menos habitual: definir lo que se llaman constantes con tipo, que son variables normales y corrientes, pero a las que damos un valor inicial antes de que comience a ejecutarse el programa. Se usa const variable: tipo = valor;

As, volviendo al ejemplo de la clave de acceso, podamos tener una variables "intentos" que dijese el nmero de intentos. Hasta ahora habramos hecho var intentos: integer; begin intentos := 3; ...

483

Ahora ya sabemos que sera mejor hacer, si sabemos que el valor no va a cambiar: const intentos = 3; begin ...

Pero si se nos da el caso de que vemos por el nombre que es alguien de confianza, que puede haber olvidado su clave de acceso, quiz nos interese permitirle 5 o ms intentos. Ya no podemos usar "const" porque el valor puede variar, pero por otra parte, siempre comenzamos concediendo 3 intentos, hasta comprobar si es alguien de fiar. Podemos hacer const intentos: integer = 3; begin ...

Recordemos que una "constante con tipo" se manejar exactamente igual que una variable, con las ventajas de que est ms fcil de localizar si queremos cambiar su valor inicial y de que el compilador optimiza un poco el cdigo, haciendo el programa unos bytes ms pequeo. Definicin de tipos. El tipo de una variable es lo que indicamos cuando la declaramos: var PrimerNumero: integer;

indica que vamos a usar una variable que se va a llamar PrimerNumero y que almacenar valores de tipo entero. Si queremos definir una de las fichas de lo que ser nuestra agenda, tambin haramos: var ficha: record nombre: string; direccion: string; edad: integer; observaciones: string end;

Tampoco hay ningn problema con esto, verdad? Y si podemos utilizar variables creando los tipos "en el momento", como en el caso anterior, para qu necesitamos definir tipos? Vamos a verlo con un ejemplo. Supongamos que vamos a tener ahora dos variables: una "ficha1" que

484

contendr el dato de la ficha actual y otra "ficha2" en la que almacenaremos datos temporales. Veamos qu pasa... program PruebaTipos; var ficha1: record nombre: string; direccion: string; edad: integer; observaciones: string end; ficha2: record nombre: string; direccion: string; edad: integer; observaciones: string end; begin ficha1.nombre := 'Pepe'; ficha1.direccion := 'Su casa'; ficha1.edad := 65; ficha1.observaciones := 'El mayor de mis amigos...'; ficha2 := ficha1; writeln( ficha2.nombre); end.

Veamos qu hara este programa: define dos variables que van a guardar la misma clase de datos. Da valores a cada uno de los datos que almacenar una de ellas. Despus hacemos que la segunda valga lo mismo que la primera, e imprimimos el nombre de la segunda. Aparecer escrito "Pepe" en la pantalla... No. Aunque a nuestros ojos "ficha1" y "ficha2" sean iguales, para el compilador no es as, por lo que protesta y el programa ni siquiera llega a ejecutarse. Es decir: las hemos definido para que almacene la misma clase de valores, pero no son del mismo tipo. Esto es fcil de solucionar: var ficha1, ficha2: record nombre: string; direccion: string; edad: integer; observaciones: string end;

485

begin ...

Si las definimos a la vez, SI QUE SON DEL MISMO TIPO. Pero surge un problema que se entender mejor ms adelante, cuando empecemos a crear funciones y procedimientos. Qu ocurre si queremos usar en alguna parte del programa otras variables que tambin sean de ese tipo? Las definimos tambin a la vez? En muchas ocasiones no ser posible. As que tiene que haber una forma de indicar que todo eso que sigue a la palabra "record" es un tipo al que nosotros queremos acceder con la misma comodidad que si fuese "integer" o "boolean", queremos definir un tipo, no simplemente declararlo, como estbamos haciendo. Pues es sencillo: type NombreDeTipo = DeclaracionDeTipo;

o en nuestro caso type TipoFicha = record nombre: string; direccion: string; edad: integer; observaciones: string end; var ficha1: TipoFicha; ... var ficha2: TipoFicha; ...

Ahora s que podremos asignar valores entre variables que hayamos definido en distintas partes del programa, podremos usar esos tipos para crear ficheros (que tambin veremos ms adelante), etc.

Tema 8: Procedimientos y funciones.


Conceptos bsicos. La programacin estructurada trata de dividir el programa el bloques ms pequeos, buscando una mayor legibilidad, y ms comodidad a la hora de corregir o ampliar. Por ejemplo, si queremos crear nuestra, podemos empezar a teclear directamente y crear un programa de 2000 lneas que quizs incluso funcione, o dividirlo en partes, de modo que el cuerpo del programa sea algo parecido a

486

begin InicializaVariables; PantallaPresentacion; Repeat PideOpcion; case Opcion of '1': MasDatos; '2': CorregirActual; '3': Imprimir; ... end; Until Opcion = OpcionDeSalida; GuardaCambios; LiberaMemoria end.

As resulta bastante ms fcil de seguir. En nuestro caso, estos bloques sern de dos tipos: procedimientos (procedure) y funciones (function). La diferencia entre ellos es que un procedimiento ejecuta una serie de acciones que estn relacionadas entre s, y no devuelve ningn valor, mientras que la funcin s que va a devolver valores. Vemoslo con un par de ejemplos: procedure Acceso; var clave: string; (* Esta variable es local *) begin writeln(' Bienvenido a SuperAgenda '); writeln('=========================='); (* Para subrayar *) writeln; writeln; (* Dos lneas en blanco *) writeln('Introduzca su clave de acceso'); readln( clave ); (* Lee un valor *) if clave <> ClaveCorrecta then (* Compara con el correcto *) begin (* Si no lo es *) writeln('La clave no es correcta!'); (* avisa y *) exit (* abandona el programa *) end end;

Primeros comentarios sobre este ejemplo: El cuerpo de un procedimiento se encierra entre "begin" y "end", igual que las sentencias compuestas. Un procedimiento puede tener sus propias variables, que llamaremos variables locales, frente a las del resto del programa, que son globales. Desde dentro de un procedimiento podemos acceder a las variables globales (como ClaveCorrecta del ejemplo anterior), pero no podemos acceder a las locales desde fuera del procedimiento en el que las hemos definido.

487

La funcin exit, que no habamos visto an, permite interrumpir la ejecucin del programa (o de un procedimiento) en un determinado momento.

Veamos el segundo ejemplo: una funcin que eleve un nmero a otro (no existe en Pascal), se podra hacer as, si ambos son enteros: function potencia(a,b: integer): integer; (* a elevado a b *) var i: integer; (* para bucles *) temporal: integer; (* para el valor temporal *) begin temporal := 1; (* inicializacin *) for i := 1 to b do temporal := temporal * a; (* hacemos "b" veces "a*a" *) potencia := temporal; (* y finalmente damos el valor *) end;

Comentemos cosas tambin: Esta funcin se llama "potencia". Tiene dos parmetros llamados "a" y "b" que son nmeros enteros (valores que "se le pasan" a la funcin para que trabaje con ellos). El resultado va a ser tambin un nmero entero. "i" y "temporal" son variables locales: una para el bucle "for" y la otra almacena el valor temporal del producto. Antes de salir es cuando asignamos a la funcin el que ser su valor definitivo.

Pero vamos a ver un programa que use esta funcin, para que quede un poco ms claro: program PruebaDePotencia; var numero1, numero2: integer;

(* Variable globales *)

function potencia(a,b: integer): integer; (* Definimos la funcin *) var i: integer; (* Locales: para bucles *) temporal: integer; (* y para el valor temporal *) begin temporal := 1; (* incializacin *) for i := 1 to b do

488

temporal := temporal * a; potencia := temporal; end;

(* hacemos "b" veces "a*a" *) (* y finalmente damos el valor *)

begin (* Cuerpo del programa *) writeln('Potencia de un nmero entero'); writeln; writeln('Introduce el primer nmero'); readln( numero1 ); writeln('Introduce el segundo nmero'); readln( numero2 ); writeln( numero1 ,' elevado a ', numero2 ,' vale ', potencia (numero1, numero2) ) end.

Un procedimiento tambin puede tener "parmetros", igual que la funcin que acabamos de ver: program ProcConParametros; procedure saludo (nombre: string); (* Nuestro procedimiento *) begin writeln('Hola ', nombre, ' qu tal ests?'); end; begin writeln; saludo( 'Eva' ); end. (* (* (* (* Comienzo del programa *) Lnea en blanco *) Saludamos a Eva *) Y se acab *)

En el prximo apartado veremos la diferencia entre pasar parmetros por valor (lo que hemos estado haciendo) y por referencia (para poder modificarlos), y jugaremos un poco con la recursividad. Pero antes, unos ejercicios propuestos de lo que hemos visto: Adaptar la funcin "potencia" que hemos visto para que trabaje con nmeros reales, y permita cosas como 3.2 ^ 1.7 Hacer una funcin que halle la raz cbica del nmero que se le indique. Definir las funciones suma y producto de tres nmeros y hacer un programa que haga una operacin u otra segn le indiquemos (con "case", etc). Un programa que halle la letra (NIF) que corresponde a un cierto DNI.

489

Parmetros. Ya habamos visto, sin entrar en detalles, qu es eso de los parmetros: una serie de datos extra que indicbamos entre parntesis en la cabecera de un procedimiento o funcin. Es algo que estamos usando, sin saberlo, desde el primer tema, cuando empezamos a usar "WriteLn": writeln( 'Hola' );

Esta lnea es una llamada al procedimiento "WriteLn", y como parmetros le estamos pasando lo que queremos que escriba, en este caso el texto "Hola". Pero vamos a ver qu ocurre si hacemos cosas como sta: program PruebaDeParametros; var dato: integer; procedure modifica( variable : integer); begin variable := 3 ; writeln( variable ); end; begin dato := 2; writeln( dato ); modifica( dato ); writeln( dato ); end.

Vamos a ir siguiendo cada instruccin: Declaramos el nombre del programa. No hay problema. Usaremos la variable "dato", de tipo entero. El procedimiento "modifica" toma una variable de tipo entero, le asigna el valor 3 y la escribe. Lgicamente, siempre escribir 3. Empieza el cuerpo del programa: damos el valor 2 a "dato". Escribimos el valor de "dato". Todos de acuerdo en que ser 2. Llamamos al procedimiento "modifica", que asigna el valor 3 a "dato" y lo escribe. Finalmente volvemos a escribir el valor de "dato"... 3?

490

No: Escribe un 2. Las modificaciones que hagamos a "dato" dentro del procedimiento modifica slo son vlidas mientras estemos dentro de ese procedimiento. Lo que modificamos es la variable genrica que hemos llamado "variable", y que no existe fuera del procedimiento. Eso es pasar un parmetro por valor. Si realmente queremos modificar el parmetro, lo que hacemos es simplemente aadir la palabra "var" delante de cada parmetro que queremos permitir que se pueda modificar. El programa quedara: program PruebaDeParametros2; var dato: integer; procedure modifica( var variable : integer); begin variable := 3 ; writeln( variable ); end; begin dato := 2; writeln( dato ); modifica( dato ); writeln( dato ); end.

Esta vez la ltima lnea del programa s que escribe un 3 y no un 2, porque hemos permitido que los cambio hechos a la variable salgan del procedimiento. Esto es pasar un parmetro por referencia. El nombre "referencia" alude a que no se pasa realmente al procedimiento o funcin el valor de la variable, sino la direccin de memoria en la que se encuentra, algo que ms adelante llamaremos un "puntero". Una de las aplicaciones ms habituales de pasar parmetros por referencia es cuando una funcin debe devolver ms de un valor. Habamos visto que una funcin era como un procedimiento, pero adems devolva un valor (pero slo uno). Si queremos obtener ms de un valor de salida, una de las formas de hacerlo es pasndolos como parmetros, precedidos por la palabra "var". Y como ejercicio queda un caso un poco ms "enrevesado". Qu ocurre si el primer programa lo modificamos para que sea as: program PruebaDeParametros3; var dato: integer; procedure modifica( dato : integer);

491

begin dato := 3 ; writeln( dato ); end; begin dato := 2; writeln( dato ); modifica( dato ); writeln( dato ); end.

Recursividad. La idea en s es muy sencilla: un procedimiento o funcin es recursivo si se llama a s mismo. Para buscar un utilidad, vamos a verlo con un ejemplo clsico: el factorial de un nmero. Partimos de la definicin de factorial: n! = n (n-1) (n-2) ... 3 2 1 Por otra parte, (n-1)! = (n-1) (n-2) (n-3) ... 3 2 1 Luego podemos escribir cada factorial en funcin del factorial del siguiente nmero: n! = n (n-1)! Acabamos de dar la definicin recursiva del factorial. Ahora slo queda ver cmo se hara eso programando: program PruebaDeFactorial; var numero: integer; function factorial( num : integer) : integer; begin if num = 1 then factorial := 1 (* Aseguramos que tenga salida siempre *) else factorial := num * factorial( num-1 ); (* Caso general *) end; begin writeln( 'Introduce un nmero entero (no muy grande) ' );

492

readln(numero); writeln( 'Su factorial es ', factorial(numero) ); end.

Dos comentarios sobre este programa: Atencin a la primera parte de la funcin recursiva: es muy importante comprobar que hay salida de la funcin, para que no se quede dando vueltas todo el tiempo y deje el ordenador colgado. No conviene poner nmeros demasiado grandes. Los enteros van desde -32768 hasta 32767, luego si el resultado es mayor que este nmero, tendremos un desbordamiento y el resultado ser errneo. En cuanto a qu es "demasiado grande": el factorial de 8 es cerca de 40.000, luego slo podremos usar nmeros del 1 al 7. Si este lmite del tamao de los enteros parece preocupante, no hay por qu darle muchas vueltas, porque en el prximo tema veremos que hay otros tipos de datos que almacenan nmeros ms grandes o que nos permiten realizar ciertas cosas con ms comodidad.

Un par de ejercicios: - Una funcin recursiva que halle el producto de dos nmeros enteros. - Otra que halle la potencia (a elevado a b), tambin recursiva. Soluciones. Pues aqu van unas soluciones (insisto en que no tienen por qu ser las nicas ni las mejores) a los ejercicios propuestos en el tema 8: 1.- Adaptar la funcin "potencia" que hemos visto para que trabaje con nmeros reales, y permita cosas como 3.2 ^ 1.7 Partimos del programa propuesto como ejemplo, que era: function potencia(a,b: integer): integer; (* a elevado a b *) var i: integer; (* para bucles *) temporal: integer; (* para el valor temporal *) begin temporal := 1; (* inicializacin *) for i = 1 to b do temporal := temporal * b; (* hacemos "b" veces "a*a" potencia := temporal; (* y finalmente damos el valor *) end;

493

No basta con cambiar los tipos de las variables, poniendo "real" en vez de "integer". Si hacemos esto, ni siquiera podremos compilar el programa, porque una variable de tipo "real" no se puede usar para controlar un bucle. Una forma de hacerlo es empleando nmeros exponenciales y logaritmos: como son operaciones inversas, tenemos que exp( log( x )) = x, donde "log" y "exp" s que son funciones predefinidas en Pascal. Pero, por otra parte, sabemos que log (a^b) = b log a. As, una forma (no la mejor) de hacerlo sera simplemente function PotReal(a,b: real): real; begin PotReal := exp ( b * log ( a )) ; end; (* a elevado a b, reales *)

2.- Hacer una funcin que halle la raz cbica del nmero que se le indique. Mirando la funcin anterior, ya es fcil: una raz cbica es lo mismo que elevar a 1/3, as que se puede hacer; function RaizCubica(n: real): real; begin RaizCubica := exp ( 0.33333333 * log ( n )) ; end;

3.- Definir las funciones suma y producto de tres nmeros y hacer un programa que haga una operacin u otra segn le indiquemos (con "case", etc.). Trabajando con nmeros reales, sera: program Suma_Y_Producto; var num1, num2: real; opcion: char; function suma( a,b : real): real; begin suma := a + b ; end; (* Definicin de variables *) (* Esta es la funcin "suma" *)

function producto( a,b : real): real; begin producto := a * b ; end; begin

(* y el producto *)

494

write( 'Introduzca el primer nmero: '); readln( num1 ); write( 'Introduzca el segundo nmero: '); readln( num2 ); writeln( 'Qu operacin desea realizar?' ); writeln( 's = Suma p = Producto' ); readln( opcion ); case opcion of 's': writeln( 'Su suma es ', suma(num1,num2) ); 'p': writeln( 'Su producto es ', producto(num1,num2) ); else writeln( 'Operacin desconocida!' ); end; (* Fin del case *) end. (* y del programa *)

4.- Un programa que halle la letra que corresponde a un cierto DNI. La forma de calcular la letra que corresponde a un cierto DNI es muy sencilla: basta con dividir el nmero del DNI entre 23 y coger el resto de esa divisin. Segn el valor de ese resto, se asigna una letra u otra: 0=T 1=R 2=W 3=A 4=G 5=M 6=Y 7=F 8=P 9=D 10=X 11=B 12=N 13=J 14=Z 15=S 16=Q 17=V 18=H 19=L 20=C 21=K 22=E As, un programa que halle la letra (implementando esta parte como funcin, para que quede "ms completo") puede ser simplemente: program Nif; var numero:longint; function LetraNif ( dni: longint ): char; const valores: string[24]='TRWAGMYFPDXBNJZSQVHLCKE'; begin LetraNif := valores [( dni mod 23 ) + 1]; end; begin writeln('Cual es el DNI cuyo NIF quiere hallar?'); readln(numero); writeln('La letra es ', LetraNif( Numero ) ,'.'); end. (* Letra del NIF. Nacho Cabanes, Jun. 92 *)

495

5.- Qu ocurre si el primer programa del tema 8.2 lo modificamos para que sea as: program PruebaDeParametros3; var dato: integer; procedure modifica( dato : integer); begin dato := 3 ; writeln( dato ); end; begin dato := 2; writeln( dato ); modifica( dato ); writeln( dato ); end.

Slo ha cambiado el nombre de la variable global, que ahora se llama "dato", igual que la local. Pero de cualquier modo, ambas variables SON DISTINTAS, de modo que el programa sigue funcionando igual que antes, cuando los nombres de las variables eran distintos. Es aconsejable, para evitar problemas, no utilizar variables globales y locales con el mismo nombre. 6.- Hallar una funcin recursiva que halle el producto de dos nmeros enteros positivos. Este es un caso con poco sentido en la prctica, pero que se puede resolver simplemente recordando que un producto no son ms que varias sumas: function producto( a,b : integer) : integer; var temporal: integer; begin if b = 1 then producto := a else producto := a + producto (a, b-1 ); end;

Y un programa que la utilizase sera: var num1, num2: integer; begin write( 'Introduzca el primer nmero: '); readln( num1 ); write( 'Introduzca el segundo nmero: '); readln( num2 ); writeln( 'Su producto es ', producto(num1,num2) ); end.

496

7.- Otra que halle la potencia (a elevado a b), tambin recursiva. La idea es la misma que antes: simplificar el problema, esta vez escribiendo la potencia como varios productos: function potencia( a,b : integer) : integer; var temporal: integer; begin if b = 1 then potencia := a else potencia := a * potencia (a, b-1 ); end;

Tema 9: Otros tipos de datos.


Comenzamos a ver los tipos de datos que podamos manejar en el tema 2. En aquel momento tratamos los siguientes: Byte. Entero, 0 a 255. Ocupa 1 byte de memoria. Integer. Entero con signo, -32768 a 32767. Ocupa 2 bytes. Char. Carcter, 1 byte. String[n]. Cadena de n caracteres (hasta 255). Ocupa n+1 bytes. Real. Real con signo. De 2.9e-39 a 1.7e38, 11 o 12 dgitos significativos, ocupa 6 bytes. Boolean. TRUE o FALSE. Array. Vectores o matrices. Record. Con campos de distinto tamao.

Esta vez vamos a ampliar con otros tipos de datos que podemos encontrar en Turbo Pascal (aunque puede que no en otras versiones del lenguaje Pascal). Como resumen previo, vamos a ver lo siguiente: Enteros. Correspondencia byte-char. Reales del 8087. Tipos enumerados. Ms detalles sobre Strings. Registros variantes. Conjuntos.

Vamos all:

497

Comencemos por los dems tipos de nmeros enteros. Estos son: Shortint. Entero con signo, de -128 a 127, ocupa 1 byte. Word. Entero sin signo, de 0 a 65535. Ocupa 2 bytes. Longint. Sin signo, de -2147483648..2147483647. Ocupa 4 bytes.

Estos tipos, junto con "char" (y "boolean" y otros para los que no vamos a entrar en tanto detalle) son tipos ordinales, existe una relacin de orden entre ellos y cada elemento est precedido y seguido por otro que podemos conocer (cosa que no ocurre en los reales). Para ellos estn definidas las funciones: pred - Predecesor de un elemento : pred(3) = 2 succ - Sucesor: succ(3) = 4 ord - Nmero de orden (posicin) dentro de todo el conjunto.

El uso ms habitual de "ord" es para convertir de "char" a "byte". Los caracteres se almacenan en memoria, de tal forma que a cada uno se le asigna un nmero entre 0 y 255, su "cdigo ASCII" (ej: A=65, a=96, 0=48, =224). La forma de hallar el cdigo ASCII de una letra es simplemente ord(letra), como ord(''). El paso contrario, la letra que corresponde a cierto nmero, se hace con la funcin "chr". As, podemos escribir los caracteres "imprimibles" de nuestro ordenador sabiendo que van del 32 al 255 (los que estn por debajo de 32 suelen ser caracteres de control, que en muchos casos no se podrn mostrar en pantalla): var bucle: byte; begin for bucle := 32 to 255 do write( chr(bucle) ); end.

Si tenemos coprocesador matemtico, (vamos a suponer que no es el caso y no vamos a dar ms detalles de su uso), podemos utilizar tambin los siguientes tipos de nmeros reales del 8087: Nombre Rango Dgitos Bytes ---------+--------------------------+---------+-----single 1.5e-45 a 3.4e38 7-8 4 double 5.0e-324 a 1.7e308 15-16 8 extended 3.4e-4932 a 1.1e4932 19-20 10 comp -9.2e18 a 9.2e18 19-20 8

498

El tipo "comp" es un entero de 8 bytes, que slo tenemos disponible si usamos el coprocesador.

Nosotros podemos crear nuestros propios tipos de datos enumerados: type DiasSemana = (Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo);

Declaramos las variables igual que hacamos con cualquier otro tipo: var dia: DiasSemana

Y las empleamos como siempre: podemos darles un valor, utilizarlas en comparaciones, etc. begin dia := Lunes; [...] if dia = Viernes then writeln( 'Se acerca el fin de semana!' ); [...]

Los tipos enumerados tambin son tipos ordinales, por lo que podemos usar pred, succ y ord con ellos. As, el en ejemplo anterior pred(Martes) = Lunes, succ(Martes) = Miercoles, ord(Martes) = 1

(el nmero de orden de Martes es 1, porque es el segundo elemento, y se empieza a numerar en cero).

Volvamos a los strings. Habamos visto que se declaraban de la forma "string[n]" y ocupaban n+1 bytes (si escribimos slo "string", es vlido en las ltimas versiones de Pascal y equivale a "string[255]"). Ocupa n+1 bytes porque tambin se guarda la longitud de la cadena (el espacio que ocupa realmente). Vamos a ver cmo acceder a caracteres individuales de una cadena. La definicin anterior, indicando el tamao entre corchetes le recuerda a la de un Array. As es. De hecho, la definicin original en Pascal del tipo String[x] era "Packed Array[1..x] of char" ("packed" era para que el compilador intentase "empaquetar" el array, de modo que ocupase menos; esto no es necesario en Turbo Pascal). As, con nombre[1] accederamos a la primera letra del nombre, con nombre[2] a la segunda, y as sucesivamente.

499

Una ltima curiosidad: habamos dicho que se guarda tambin la longitud de la cadena. Esto se hace en la posicin 0. Veamos un programa de ejemplo: var linea: string [20]; pos: byte; (* Cadena inicial: limitada a 20 letras *) (* Posicin que estamos mirando *)

begin writeln( 'Introduce una lnea de texto...' ); readln( linea ); for pos := 1 to ord(linea[0]) do writeln(' La letra nmero ', pos,' es una ', linea[pos]); end.

Comentarios: "linea[0]" da la longitud de la cadena, pero es un carcter, luego debemos convertirlo a byte con "ord". Entonces, recorremos la cadena desde la primera letra hasta la ltima. Si tecleamos ms de 20 letras, las restantes se desprecian.

Tambin habamos visto ya los registros (records), pero con unos campos fijos. No tiene por qu ser necesariamente as. Tenemos a nuestra disposicin los registros variantes, en los que con un "case" podemos elegir unos campos u otros. La mejor forma de entenderlos es con un ejemplo. program RegistrosVariantes; type TipoDato = (Num, Fech, Str); Fecha = record D, M, A: Byte; end; Ficha = record Nombre: string[20]; case Tipo: TipoDato of Num: (N: real); Fech: (F: Fecha); Str: (S: string); end; var UnDato: Ficha; begin UnDato.Nombre := 'Nacho'; UnDato.Tipo := Num;

(* Campo fijo *) (* Campos variantes *) (* Si es un nmero: campo N *) (* Si es fecha: campo F *) (* Si es string: campo S *)

(* Campo normal de un record *) (* Vamos a almacenar un nmero *)

500

UnDato.N UnDato.Nombre UnDato.Tipo UnDato.F.D UnDato.F.M UnDato.F.A

:= 3.5; := := := := := 'Nacho2'; Fech; 7; 11; 93;

(* que vale 3.5 *) (* (* (* (* (* Campo normal *) Ahora almacenamos una fecha *) Da: 7 *) Mes: 11 *) Ao: 93 *)

UnDato.Nombre := 'Nacho3'; UnDato.Tipo := Str; UnDato.S := 'Nada'; end.

(* Campo normal *) (* Ahora un string *) (* el texto "Nada" *)

Finalmente, tenemos los conjuntos (sets). Un conjunto est formado por una serie de elementos de un tipo base, que debe ser un ordinal de no ms de 256 valores posibles, como un "char", un "byte" o un enumerado. type Letras = set of Char; type DiasSemana = (Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo); Dias = set of DiasSemana;

Para construir un "set" utilizaremos los corchetes ([ ]), y dentro de ellos enumeramos los valores posibles, uno a uno o como rangos de valores separados por ".." : var LetrasValidas : Letras; Fiesta : Dias; begin LetrasValidas = ['a'..'z', 'A'..'z', '0'..'9', '', ''] Fiesta = [ Sabado, Domingo ] end.

Un conjunto vaco se define con [ ]. Las operaciones que tenemos definidas sobre los conjuntos son:

501

Operac Nombre ---------+---------------+ Unin Diferencia * Interseccin in Pertenencia

As, podramos hacer cosas como VocalesPermitidas := LetrasValidas * Vocales;

if DiaActual in Fiesta then writeln( 'No me dirs que ests trabajando...' );

En el primer ejemplo hemos dicho que el conjunto de vocales permitidas (que deberamos haber declarado) es la interseccin de las vocales (que tambin debamos haber declarado) y las letras vlidas. En el segundo, hemos comprobado si la fecha actual (que sera de tipo DiasSemana) pertenece al conjunto de los das de fiesta.

Tema 10: Pantalla en modo texto.


Este tema va a ser especfico de Turbo Pascal para DOS. Algunas de las cosas que veremos aparecen en otras versiones de Turbo Pascal (la 3.0 para CP/M, por ejemplo), pero no todas, o en otros compiladores (como TMT Pascal Lite) y de cualquier modo, nada de esto es Pascal estndar. Nos centraremos primero en cmo se hara con las versiones 5.0 y superiores de Turbo Pascal. Luego comentaremos cmo se hara con Turbo Pascal 3.01. En la mayora de los lenguajes de programacin, existen "bibliotecas" (en ingls, "library") con funciones y procedimientos nuevos, que permiten ampliar el lenguaje. En Turbo Pascal, estas bibliotecas reciben el nombre de "unidades" (UNIT), y existen a partir de la versin 5. Veremos cmo crearlas un poco ms adelante, pero de momento nos va a interesar saber cmo acceder a ellas, porque Turbo Pascal incorpora unidades que aportan mayores posibilidades de manejo de la pantalla en modo texto o grfico, de acceso a funciones del DOS, de manejo de la impresora, etc. As, la primera unidad que trataremos es la encargada de gestionar (entre otras cosas) la pantalla en modo texto. Esta unidad se llama CRT. Para acceder a cualquier unidad, se emplea la sentencia "uses" justo despus de "program" y antes de las declaraciones de variables: program prueba; uses crt;

502

var [...]

Vamos a mencionar algunos de los procedimientos y funciones ms importantes. Para ver los dems, basta pasearse por la ayuda de Turbo Pascal: escribir crt y pulsar Ctrl+F1 encima de esa palabra, que nos muestra informacin sobre esa palabra clave (o la que nos interesase). ClrScr : Borra la pantalla. GotoXY (x, y) : Coloca el cursor en unas coordenadas de la pantalla. TextColor (Color) : Cambia el color de primer plano. TextBackground (Color) : Cambia el color de fondo. WhereX : Funcin que informa de la coordenada x actual del cursor. WhereY : Coordenada y del cursor. Window (x1, y1, x2, y2) : Define una ventana de texto.

Algunos "extras" no relacionados con la pantalla son: Delay(ms) : Espera un cierto nmero de milisegundos. ReadKey : Funcin que devuelve el carcter que se haya pulsado. KeyPressed : Funcin que devuelve TRUE si se ha pulsado alguna tecla. Sound (Hz) : Empieza a generar un sonido de una cierta frecuencia. NoSound: Deja de producir el sonido.

Comentarios generales: X es la columna, de 1 a 80. Y es la fila, de 1 a 25. El cursor es el cuadrado o raya parpadeante que nos indica donde seguiramos escribiendo. Los colores estn definidos como constantes con el nombre en ingls. As Black = 0, de modo que TextColor ( Black ) es lo mismo que TextColor(0). La pantalla se puede manejar tambin accediendo directamente a la memoria de vdeo, pero eso es bastante ms complicado.

Aqu va un programa de ejemplo que maneja casi todo esto: program PruebaDeCRT; { Acceso a pantalla en modo texto } { Exclusivo de Turbo Pascal } { -- Comprobado con: TP 7.0 -- } uses crt;

503

var bucle : byte; tecla : char; begin ClrScr; TextColor( Yellow ); TextBackground( Red ); GotoXY( 40, 13 ); Write(' Hola '); Delay( 1000 ); Window ( 1, 15, 80, 23 ); TextBackground ( Blue ); ClrScr; for bucle := 1 to 100 do WriteLn( bucle ); WriteLn( 'Pulse una tecla..'); tecla := ReadKey; Window( 1, 1, 80, 25 ); GotoXY( 1, 24 ); Write( 'Ha pulsado ', tecla ); Sound( 220 ); Delay( 500 ); NoSound; Delay( 2000 ); TextColor( LightGray ); TextBackground( Black ); ClrScr; end.

{ { { { { { { { {

Borra la pantalla } Color amarillo } Fondo rojo } Vamos al centro de la pantalla } Saludamos } Esperamos un segundo } Ventana entre las filas 15 y 23 } Con fondo azul } La borramos para que se vea }

{ Escribimos del 1 al 100 } { { { { { { { { { { Esperamos que se pulse una tecla } Restauramos ventana original } Vamos a la penltima lnea } Pos eso } Sonido de frecuencia 220 Hz } Durante medio segundo } Se acab el sonido } Pausa antes de acabar } Colores por defecto del DOS } Y borramos la pantalla }

Finalmente, veamos los cambios para Turbo Pascal 3.01: En TP3 no existen unidades, por lo que la lnea "uses crt;" no existira. La otra diferencia es que para leer una tecla se hace con "read(kbd, tecla);" (leer de un dispositivo especial, el teclado, denotado con kbd) en vez de con "tecla := readkey". Con estos dos cambios, el programa anterior funciona perfectamente. Para Surpas la cosa cambia un poco: GotoXY empieza a contar desde 0. No existen ClrScr, TextColor ni TextBackground (entre otros), que se pueden emular como hemos hecho en el prximo ejemplo. No existen Window, Delay, Sound, y no son tan fciles de crear como los anteriores.

Con estas consideraciones, el programa (que aun as se parece) queda: program PruebaDeCRT; { Versin para SURPAS }

{ ----- Aqu empiezan las definiciones que hacen que Surpas maneje la pantalla de forma similar a Turbo Pascal ------ }

504

{ Para comprender de donde ha salido esto, consulta el fichero IBMPC.DOC que acompaa a SURPAS } const Black = Blue = Green = Cyan = Red = Magenta = Brown = LightGray = DarkGray = LightBlue = LightGreen = LightCyan = LightRed = LightMagenta= Yellow = White = 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15;

procedure ClrScr; begin gotoxy(0,0); write( CLREOS ); end; procedure TextColor(n: byte); begin write( chr(27), 'b', chr(n) ); end; procedure TextBackground(n: byte); begin write( chr(27), 'c', chr(n) ); end; { Se podran aadir otras posibilidades, como TextMode, HighVideo y LowVideo, etc, siguiendo este esquema, si se cree necesario } { ----- Final del aadido ----- } var bucle : byte; tecla : char; begin ClrScr; { Borra la pantalla } TextColor( Yellow ); { Color amarillo } TextBackground( Red ); { Fondo rojo } GotoXY( 30, 13 ); { Vamos al centro de la pantalla } Write(' Hola. Pulse una tecla... '); { Saludamos } read(kbd,tecla); { Esperamos que se pulse una tecla } TextBackground ( Blue ); { Con fondo azul }

505

ClrScr; { for bucle := 1 to 100 do Write( bucle, ' ' ); { WriteLn; { WriteLn( 'Pulse otra tecla..'); read(kbd,tecla); { GotoXY( 0, 12 ); { Write( 'Ha pulsado ', tecla ); { TextColor( LightGray ); { TextBackground( Black ); { GotoXY( 0, 23 ); { end.

La borramos para que se vea } Escribimos del 1 al 100 } Avanzamos una lnea } Esperamos que se pulse una tecla } Vamos al centro de la pantalla } Pos eso } Colores por defecto del DOS } Y borramos la pantalla } Vamos a la penltima lnea }

Tema 11: Manejo de ficheros.


Ahora vamos a ver cmo podemos leer ficheros y crear los nuestros propios. Voy a dividir este tema en tres partes: primero veremos como manejar los ficheros de texto, luego los ficheros "con tipo" (que usaremos para hacer una pequea agenda), y finalmente los ficheros "generales". Las diferencias entre las dos ltimas clases de fichero pueden parecer poco claras ahora e incluso en el prximo apartado, pero en cuanto hayamos visto todos los tipos de ficheros se comprender bien cuando usar unos y otros. Ficheros de texto. Vayamos a lo que nos interesa hoy: un fichero de texto. Es un fichero formado por caracteres ASCII normales, que dan lugar a lneas de texto legible. Si escribimos TYPE AUTOEXEC.BAT desde el DOS, veremos que se nos muestra el contenido de este fichero: una serie de lneas de texto que, al menos, se pueden leer (aunque comprender bien lo que hace cada una ya es ms complicado). En cambio, si hacemos TYPE COMMAND.COM, aparecern caracteres raros, se oir algn que otro pitido... es porque es un fichero ejecutable, que no contiene texto, sino una serie de instrucciones para el ordenador, que nosotros normalmente no sabremos descifrar. Casi salta a la vista que los ficheros del primer tipo, los de texto, van a ser ms fciles de tratar que los "ficheros en general". Hasta cierto punto es as, y por eso es por lo que vamos a empezar por ellos. Nos vamos a centrar en el manejo de ficheros con Turbo Pascal. Para acceder a un fichero, hay que seguir unos cuantos pasos: 1. 2. 3. 4. 5. Declarar el fichero junto con las dems variables. Asignarle un nombre. Abrirlo, con la intencin de leer, escribir o aadir. Trabajar con l. Cerrarlo.

La mejor forma de verlo es con un ejemplo. Vamos a imitar el funcionamiento de la orden del DOS anterior: TYPE AUTOEXEC.BAT.

506

program MuestraAutoexec; var fichero: text; linea: string;

(* Fichero de texto *) (* Lnea que leemos *)

*) *)

begin assign( fichero, 'C:\AUTOEXEC.BAT' ); reset( fichero ); while not eof( fichero ) do begin readln( fichero, linea ); writeln( linea ); end; close( fichero ); end.

(* Le asignamos el nombre *) (* Lo abrimos para lectura (* Mientras que no se acabe (* Leemos una lnea *) (* y la mostramos *) (* Se acab: lo cerramos *)

Eso es todo. Debera ser bastante autoexplicativo, pero aun as vamos a comentar algunas cosas: text es el tipo de datos que corresponde a un fichero de texto. assign es la orden que se encarga de asignar un nombre fsico al fichero que acabamos de declarar. reset abre un fichero para lectura. El fichero debe existir, o el programa se interrumpir avisando con un mensaje de error. eof es una funcin booleana que devuelve TRUE (cierto) si se ha llegado ya al final del fichero (end of file). Se leen datos con read o readln igual que cuando se introducan por el teclado. La nica diferencia es que debemos indicar desde qu fichero se lee, como aparece en el ejemplo. El fichero se cierra con close. No cerrar un fichero puede suponer no guardar los ltimos cambios o incluso perder la informacin que contiene.

Este fichero est abierto para lectura. Si queremos abrirlo para escritura, empleamos "rewrite" en vez de "reset", pero esta orden hay que utilizarla con cuidado, porque si el fichero ya existe lo machacara, dejando el nuevo en su lugar, y perdiendo los datos anteriores. Ms adelante veremos cmo comprobar si el fichero ya existe. Para abrir el fichero para aadir texto al final, usaramos "append" en vez de "reset". En ambos casos, los datos se escribiran con writeln( fichero, linea );

Una limitacin que puede parecer importante es eso de que el fichero debe existir, y si no el programa se interrumpe. En la prctica, esto no es tan drstico. Hay una forma de comprobarlo, que es con una de las llamadas "directivas de compilacin". Obligamos al compilador a que

507

temporalmente no compruebe las entradas y salidas, y lo hacemos nosotros mismos. Despus volvemos a habilitar las comprobaciones. Ah va un ejemplo de cmo se hace esto: program MuestraAutoexec2; var fichero: text; linea: string;

(* Fichero de texto *) (* Lnea que leemos *)

begin assign( fichero, 'C:\AUTOEXEC.BAT' ); (* Le asignamos el nombre *) {$I-} (* Deshabilita comprobacin de entrada/salida *) reset( fichero ); (* Lo intentamos abrir *) {$I+} (* La habilitamos otra vez *) if ioResult = 0 then (* Si todo ha ido bien *) begin while not eof( fichero ) do (* Mientras que no se acabe *) begin readln( fichero, linea ); (* Leemos una lnea *) writeln( linea ); (* y la mostramos *) end; close( fichero ); (* Se acab: lo cerramos *) end; (* Final del "if" *) end.

De modo que {$I-} deshabilita la comprobacin de las operaciones de entrada y salida, {$I+} la vuelve a habilitar, y "ioresult" devuelve un nmero que indica si la ltima operacin de entrada/salida ha sido correcta (cero) o no (otro nmero, que indica el tipo de error). Como ltimo ejemplo sobre este tema, un programa que lea el AUTOEXEC.BAT y cree una copia en el directorio actual llamada AUTOEXEC.BAN. La orden del DOS equivalente sera COPY C:\AUTOEXEC.BAT AUTOEXEC.BAN program CopiaAutoexec; var fichero1, fichero2: text; linea: string;

(* Ficheros de texto *) (* Lnea actual *)

begin assign( fichero1, 'C:\AUTOEXEC.BAT' ); (* Le asignamos nombre *) assign( fichero2, 'AUTOEXEC.BAN' ); (* y al otro *) {$I-} (* Sin comprobacin E/S *) reset( fichero1 ); (* Intentamos abrir uno *) {$I+} (* La habilitamos otra vez *)

508

if ioResult = 0 then (* begin rewrite( fichero2 ); (* while not eof( fichero1 ) do (* begin readln( fichero1, linea ); writeln( fichero2, linea ); end; writeln( 'Ya est '); close( fichero1 ); close( fichero2 ); end else writeln(' No he encontrado el fichero! end.

Si todo ha ido bien *) Abrimos el otro *) Mientras que no acabe 1 *) (* Leemos una lnea *) (* y la escribimos *) (* (* (* (* '); Se acab: avisamos, *) cerramos uno *) y el otro *) Final del "if" *) (* Si no existe *)

Como ejercicios propuestos: Un programa que vaya grabando en un fichero lo que nosotros tecleemos, como haramos en el DOS con COPY CON FICHERO.EXT Un programa que copie el AUTOEXEC.BAT excluyendo aquellas lneas que empiecen por K o P (maysculas o minsculas).

Ficheros con tipo. Ya hemos visto cmo acceder a los ficheros de texto, tanto para leerlos como para escribir en ellos. Hoy nos centraremos en lo que vamos a llamar "ficheros con tipo". Estos son ficheros en los que cada uno de los elementos que lo integran es del mismo tipo (como vimos que ocurre en un array). En los de texto se podra considerar que estaban formados por elementos iguales, de tipo "char", pero ahora vamos a llegar ms all, porque un fichero formado por datos de tipo "record" sera lo ideal para empezar a crear nuestra propia agenda. Pues una vez que se conocen los ficheros de texto, no hay muchas diferencias a la hora de un primer manejo: debemos declarar un fichero, asignarlo, abrirlo, trabajar con l y cerrarlo. Pero ahora podemos hacer ms cosas tambin. Con los de texto, el uso habitual era leer lnea por lnea, no carcter por carcter. Como las lneas pueden tener cualquier longitud, no podamos empezar por leer la lnea 4, por ejemplo, sin haber ledo antes las tres anteriores. Esto es lo que se llama ACCESO SECUENCIAL. Ahora s que sabemos lo que va a ocupar cada dato, ya que todos son del mismo tipo, y podremos aprovecharlo para acceder a una determinada posicin del fichero cuando nos interese, sin necesidad de pasar por todas las posiciones anteriores. Esto es el ACCESO ALEATORIO (o directo).

509

La idea es sencilla: si cada ficha ocupa 25 bytes, y queremos leer la nmero 8, bastara con "saltarnos" 25*7=175 bytes. Pero Turbo Pascal nos lo facilita ms an, con una orden, seek, que permite saltar a una determinada posicin de un fichero sin tener que calcular nada nosotros mismos. Veamos un par de ejemplos... Primero vamos a introducir varias fichas en un fichero con tipo: program IntroduceDatos; type ficha = record nombre: string [80]; edad: byte end; var fichero: file of ficha; bucle: byte; datoActual: ficha;

(* Nuestras fichas *)

(* Nuestro fichero *) (* Para bucles, claro *) (* La ficha actual *)

begin assign( fichero, 'basura.dat' ); (* Asignamos *) rewrite( fichero ); (* Abrimos (escritura) *) writeln(' Te ir pidiendo los datos de cuatro personas...' ); for bucle := 1 to 4 do (* Repetimos 4 veces *) begin writeln(' Introduce el nombre de la persona nmero ', bucle); readln( datoActual.nombre ); writeln(' Introduce la edad de la persona nmero ', bucle); readln( datoActual.edad ); write( fichero, datoActual ); (* Guardamos el dato *) end; close( fichero ); (* Cerramos el fichero *) end. (* Y se acab *)

La nica diferencia con lo que ya habamos visto es que los datos son de tipo "record" y que el fichero se declara de forma distinta, con "file of TipoBase". Entonces ahora vamos a ver cmo leeramos slo la tercera ficha de este fichero de datos que acabamos de crear: program LeeUnDato; type

510

ficha = record nombre: string [80]; edad: byte end; var fichero: file of ficha; bucle: byte; datoActual: ficha; begin assign( fichero, 'basura.dat' ); reset( fichero ); (* Abrimos (lectura) *) seek( fichero, 2 ); (* <== Vamos a la ficha 3 *) read( fichero, datoActual ); (* Leemos *) writeln(' El nombre es: ', datoActual.nombre ); writeln(' La edad es: ',datoActual.edad ); close( fichero ); (* Y cerramos el fichero *) end.

El listado debe ser autoexplicativo. La nica cosa que merece la pena comentar es eso del "seek( fichero, 2 )": La posicin de las fichas dentro de un fichero de empieza a numerar en 0, que corresponder a la primera posicin. As, accederemos a la segunda posicin con un 1, a la tercera con un 2, y en general a la "n" con "seek(fichero,n-1)". Y ya que como mejor se aprende es practicando, queda propuesto elaborar una agenda: En primer lugar, va a ser una agenda que guarde una sola ficha en memoria y que vaya leyendo cada ficha que nos interese desde el disco, o escribiendo en l los nuevos datos (todo ello de forma "automtica", sin que quien maneje la agenda se de cuenta). Esto hace que sea ms lenta, pero no tiene ms limitacin de tamao que el espacio libre en nuestro disco duro. Las posibilidades que debe tener sern: Mostrar la ficha actual en pantalla (automtico tambin). Modificar la ficha actual. Aadir fichas nuevas. Salir del programa.

Ms adelante ya le iremos introduciendo mejoras, como buscar, ordenar, imprimir una o varias fichas, etc. El formato de cada ficha ser: Nombre: 20 letras. Direccin: 30 letras. Ciudad: 15 letras. Cdigo Postal: 5 letras. Telfono: 12 letras. Observaciones: 40 letras.

511

Ficheros generales. Hemos visto cmo acceder a los ficheros de texto y a los fichero "con tipo". Pero en la prctica nos encontramos con muchos ficheros que no son de texto y que tampoco tienen un tipo de datos claro. Muchos formatos estndar como PCX, DBF o GIF estn formados por una cabecera en la que se dan detalles sobre el formato de los datos, y a continuacin ya se detallan los datos en s. Esto claramente no es un fichero de texto, y tampoco se parece mucho a lo que habamos llamado ficheros con tipo. Quizs, un fichero "de tipo byte", pero esto resulta muy lento a la hora de leer ficheros de un cierto tamao. Como suele ocurrir, "debera haber alguna forma mejor de hacerlo..." La hay: declarar un fichero sin tipo, en el que nosotros mismos decidimos qu tipo de datos queremos leer en cada momento. Ahora leeremos bloques de bytes, y los almacenaremos en un "buffer" (memoria intermedia). Para ello tenemos la orden "BlockRead", cuyo formato es: procedure BlockRead(var F: Fichero; var Buffer; Cuantos: Word [; var Resultado: Word]);

donde F es un fichero sin tipo (declarado como "var fichero: file" ). Buffer es la variable en la que queremos guardar los datos ledos. Cuantos es el nmero de datos que queremos leer. Resultado (opcional) almacena un nmero que indica si ha habido algn error.

Hay otra diferencia con los ficheros que hemos visto hasta ahora, y es que cuando abrimos un fichero sin tipo con "reset", debemos indicar el tamao de cada dato (normalmente diremos que 1, y as podemos leer variables ms o menos grandes indicndolo con el "cuantos" que aparece en BlockRead). As, abriramos el fichero con reset( fichero, 1 );

Los bloques que leemos con "BlockRead" deben tener un tamao menor de 64K (el resultado de multiplicar "cuantos" por el tamao de cada dato). El significado de "Resultado" es el siguiente: nos indica cuantos datos ha ledo realmente. De este modo, si vemos que le hemos dicho que leyera 30 fichas y slo ha ledo 15, podremos deducir que hemos llegado al final del fichero. Si no usamos "resultado" y tratamos de leer las 30 fichas, el programa se interrumpir, dando un error.

Para escribir bloques de datos, utilizaremos "BlockWrite", que tiene el mismo formato que BlockRead, pero esta vez si "resultado" es menor de lo esperado indicar que el disco est lleno.

512

Esta vez, es en "rewrite" (cuando abrimos el fichero para escritura) donde deberemos indicar el tamao de los datos (normalmente 1 byte).

Como las cosas se entienden mejor practicando, ah va un primer ejemplo, tomado de la ayuda en lnea de Turbo Pascal y ligeramente retocado, que es un programa que copia un fichero leyendo bloques de 2K: program CopiaFichero; { Sencillo y rpido programa de copia de ficheros, SIN comprobacin de errores } var Origen, Destino: file; CantLeida, CantEscrita: Word; NombreOrg, NombreDest: String; Buffer: array[1..2048] of Char; begin Write( 'Introduzca el nombre del fichero ORIGEN... ' ); ReadLn( NombreOrg ); Write( 'Introduzca el nombre del fichero DESTINO... ' ); ReadLn( NombreDest ); Assign( Origen, NombreOrg ); Reset( Origen, 1 ); { Tamao = 1 } Assign( Destino, NombreDest ); Rewrite( Destino, 1 ); { Lo mismo } WriteLn( 'Copiando ', FileSize(Origen), ' bytes...' ); repeat BlockRead( Origen, Buffer, SizeOf(Buffer), CantLeida); BlockWrite( Destino, Buffer, CantLeida, CantEscrita); until (CantLeida = 0) or (CantEscrita <> CantLeida); Close( Origen ); Close( Destino ); WriteLn( 'Terminado.' ) end.

Una mejora: es habitual usar "SizeOf" para calcular el tamao de una variable, en vez de calcularlo a mano y escribir, por ejemplo, 2048. Es ms fiable y permite modificar el tipo o el tamao de la variable en la que almacenamos los datos ledos sin que eso repercuta en el resto del programa.

Y un segundo ejemplo, que muestra parte de la informacin contenida en la cabecera de un fichero GIF, leyendo un "record": program GifHeader; Type Gif_Header = Record

{ Primeros 13 Bytes de un Gif }

513

Firma, NumVer Tam_X, Tam_Y _Packed, Fondo, Aspecto end;

: Array[1..3] of Char; : Word; : Byte;

Var Fich : File; Cabecera : GIF_Header; Nombre: String; begin Write( 'Nombre del fichero GIF (con extensin)? '); ReadLn( Nombre ); Assign( Fich, Nombre ); Reset( Fich, 1 ); { Tamao base: 1 byte } Blockread( Fich, Cabecera, SizeOf(Cabecera) ); Close( Fich ); With Cabecera DO begin Writeln('Versin: ', Firma, NumVer); Writeln('Resolucin: ', Tam_X, 'x', Tam_Y, 'x', 2 SHL (_Packed and 7)); end; end.

Ejemplo: agenda. { ======================================================== Ejemplo de Agenda, con lo que hemos visto hasta ahora. Caractersticas: - Nmero de fichas ilimitado, pero mayor lentitud, porque los datos estn permanentemente en disco, y en memoria se almacena slo la ficha actual. - Muestra un ficha. - Se puede ver la siguiente, la anterior o la nmero "x" - Se pueden aadir fichas nuevas. Posibles mejoras: - Muuuuuchas. Por ejemplo... - Mejorar la presentacin. - Mejorar la entrada de datos y su modificacin. - Buscar un determinado texto en las fichas. - Imprimir una o varias fichas. - Ordenar el fichero alfabticamente. - Mantener todas o parte de las fichas en memoria para mayor velocidad. - Borrar fichas. - Clave de acceso.

514

- Datos encriptados. - Etc, etc, etc... Nacho Cabanes, Marzo 93. ======================================================== } program MiniAg; uses crt; { Manejo de la pantalla y el teclado } { Nombre del fichero } { Nuestro tipo de datos }

const nombref: string[12]='agenda.dat'; type tipoagenda = record nombre: string[20]; direccion: string[30]; ciudad: string[15]; cp: string[5]; telef: string[12]; observ: string[40] end; var

FichAgenda: file of tipoagenda; ficha: TipoAgenda; { Guarda la NumFicha: word; { El nmero de Ultima: word; { Nmero de la opcion: char; { La opcin del men

{ Fichero ficha actual ficha actual ltima ficha que se elige

} } } } }

procedure Pausa; var tecla: char; begin tecla := readkey; end;

{ ----- Espera a que se pulse una tecla }

procedure Saludo; { ----- Cartelito de presentacin } begin TextBackground(Black); TextColor(LightGray); ClrScr; window(23,3,80,25); writeln;writeln;writeln; TextColor(LightCyan); writeln('+------------------------------+'); writeln(' __'); writeln(' __'); writeln(' A G E N D A __'); writeln(' __'); writeln(' __'); writeln('+------------------------------+__'); writeln(' ________________________________'); TextColor(LightGray); window(1,1,80,25); gotoxy(1,25); { Para que el cursor no moleste } pausa;

515

end; procedure Escribe; { ----- Escribe los datos en pantalla var i: byte; { i: para bucles begin ClrScr; TextColor(White); TextBackground(Blue); gotoxy(1,2);write(' Agenda (ficha actual: ', NumFicha,'/',ultima,')'); clrEol; { borra hasta el final de la lnea en azul gotoxy(1,3); for i:= 1 to 80 do write('-'); { 80 guiones TextBackground(Black); TextColor(LightGray); seek( FichAgenda, NumFicha-1 ); { se coloca en la ficha que toca read( FichAgenda, ficha ); { y la lee with ficha do begin { Escribe cada dato gotoxy(1,6); writeln('Nombre: ', nombre); writeln; writeln; writeln('Direccin: ', direccion); writeln; writeln('Ciudad: ', ciudad); writeln; writeln('Cdigo Postal: ', cp); writeln; writeln('Telfono: ', telef); writeln; writeln; writeln('Observaciones: ',observ); end; TextColor(White); TextBackground(Blue); gotoxy(1,23); { Abajo: escribe las opciones for i:= 1 to 80 do write('-'); gotoxy(1,24);write(' 1-Anterior 2-Posterior 3-Nmero' + ' 4-Aadir 5-Corregir 0-Terminar'); clrEol; TextBackground(Black); TextColor(LightGray); end; } }

} } } } }

procedure FichaNueva; { ----- Aade una ficha nueva } begin ClrScr; TextColor(Yellow); NumFicha := Ultima + 1; { Hay que escribir al final } writeln('Aadiendo la ficha ',NumFicha,'.'); TextColor(LightGray); with ficha do { Pide cada dato } begin writeln; writeln(' Nombre ?'); readln( nombre ); writeln; writeln(' Direccin ?'); readln( direccion ); writeln; writeln(' Ciudad ?'); readln( ciudad );

516

writeln; writeln(' Cdigo Postal ?'); readln( cp ); writeln; writeln(' Telfono ?'); readln( telef ); writeln; writeln(' Observaciones ?'); readln( observ ); end; seek( FichAgenda, NumFicha-1 ); write( FichAgenda,ficha ); Ultima := Ultima + 1; end;

{ Se sita } { y escribe la ficha } { Ahora hay una ms }

procedure Modifica; { ----- Modifica la ficha actual var temporal:string[100]; { Almacena cada valor temporalmente begin ClrScr; TextColor(Yellow); writeln('Corrigiendo la ficha ',NumFicha,'.'); TextColor(LightGray); with ficha do begin writeln; writeln(' Nombre (', nombre,') ?'); { Muestra el valor anterior readln(temporal); { y pide el nuevo if temporal<>'' { Si tecleamos algo, then nombre:=temporal; { lo modifica (si no, no cambia) writeln; writeln(' Direccin (',direccion,') ?'); readln(temporal); if temporal<>'' then direccion:=temporal; writeln; writeln(' Ciudad (',ciudad,') ?'); readln(temporal); if temporal<>'' then ciudad:=temporal; writeln; writeln(' Cdigo Postal (',cp,') ?'); readln(temporal); if temporal<>'' then cp:=temporal; writeln; writeln(' Telfono (',telef,') ?'); readln(temporal); if temporal<>'' then telef:=temporal; writeln; writeln('Observaciones (',observ,') ?'); readln(temporal); if temporal<>'' then observ:=temporal; end; seek( FichAgenda, NumFicha-1 ); { Como siempre... } write( FichAgenda, ficha ) end;

} }

} } } }

procedure NumeroFicha; { ----- Va a la ficha con un cierto numero } var numero: word; begin ClrScr; TextColor(Yellow); writeln('Saltar a la ficha con un determinado nmero ');

517

TextColor(LightGray); writeln; writeln(' Qu nmero de ficha ?'); readln( numero ); if numero>0 then { comprueba que sea vlido } if numero<=ultima then NumFicha:=numero { si es <= que la ltima, salta } else NumFicha:=ultima; { si es mayor, se queda en la ltima } end; procedure Prepara; { ----- Inicializacin de las variables } begin { y apertura/creacin del fichero } NumFicha := 1; Ultima := 1; assign( FichAgenda, nombref ); {$I-} { Desactiva errores de E/S } reset( FichAgenda ); { e intenta leer } {$I+} if ioresult <>0 then {Si no existen los datos} begin ClrScr; writeln(' No existen datos.', ' Pulse una tecla para introducirlos.'); pausa; rewrite( FichAgenda ); { los crea } FichaNueva; { y obliga a aadir una ficha } end; Ultima := FileSize( FichAgenda ); { Nmero de fichas } end; begin { ----- Cuerpo del programa ----- } Saludo; prepara; repeat Escribe; opcion:='a'; while not (opcion in ['0'..'8']) do opcion := readkey; case opcion of '1': { Ficha anterior } if NumFicha>1 then NumFicha := NumFicha - 1; '2': { Ficha posterior } if NumFicha<ultima then NumFicha := NumFicha + 1; '3': { Nmero de ficha } NumeroFicha; '4': { Aadir una ficha } FichaNueva; '5': { Corregir la ficha actual } Modifica; end; until opcion='0'; { Terminar } Close( FichAgenda ); ClrScr; end.

518

Tema 12: Creacin de unidades.


Comentamos en el tema 10 que en muchos lenguajes de programacin podemos manejar una serie de bibliotecas externas (en ingles, library) de funciones y procedimientos, que nos permitan ampliar el lenguaje base. En Turbo Pascal, estas bibliotecas reciben el nombre de "unidades" (unit), y existen a partir de la versin 5. En su momentos, empleamos la unidad CRT, que nos daba una serie de facilidades para manejar la pantalla en modo texto, el teclado y la generacin de sonidos sencillos. Iremos viendo otras unidades estndar cuando accedamos a la pantalla en modo grfico, a los servicios del DOS, etc. Pero por ahora vamos a ver cmo podemos crear las nuestras propias. Para qu? Nos podra bastar con teclear en un programa todas las funciones que nos interesen. Si creamos otro programa que las necesite, pues las copiamos tambin en ese y ya est... No. Las unidades nos ayudan a conseguir dos cosas: La primera ventaja es que los programas sean ms modulares. Que podamos dejar aparte las funciones que se encargan de batallar con el teclado, por ejemplo, y en nuestro programa principal slo est lo que realmente tenga este programa que lo diferencie de los otros. Esto facilita la legibilidad y con ello las posibles correcciones o ampliaciones. La segunda ventaja es que no tenemos distintas versiones de los mismos procedimientos o funciones. Esto ayuda a ganar espacio en el disco duro, pero eso es lo menos importante. Lo realmente interesante es que si se nos ocurre una mejora para un procedimiento, todos los programas que lo usen se van a beneficiar de l automticamente.

Por ejemplo: imaginemos que estamos haciendo un programa de rotacin de objetos en tres dimensiones. Creamos nuestra biblioteca de funciones, y la aprovechamos para todos los proyectos que vayamos a hacer en tres dimensiones. No solo evitamos reescribir en cada programa el procedimiento RotaPunto, p.ej., que ahora tomar de nuestra unidad "Graf3D" sino que si de repente descubrimos una forma ms rpida de rotarlos, todos los programas que utilicen el procedimiento RotaPunto se vern beneficiados slo con recompilarlos. Pero vamos a lo prctico: Una "unit" tiene dos partes: una pblica, que es aquella a la que podremos acceder, y una privada, que es el desarrollo detallado de esa parte pblica, y a esta parte no se puede acceder desde otros programas. La parte pblica se denota con la palabra "interface", y la privada con "implementation". Debajo de interface basta indicar los nombres de los procedimientos que queremos "exportar", as como las variables, si nos interesase crear alguna. Debajo de implementation escribimos realmente estos procedimientos o funciones, tal como haramos en un programa normal.

519

Un ejemplo para que se entienda mejor... unit miCrt1; { Unidad que "mejora" la CRT }

interface

{ Parte "pblica", que se exporta }

procedure AtXY( X, Y: byte ; texto: string ); { Escribe un texto en ciertas coordenadas }

implementation

{ Parte "privada", detallada }

uses crt;

{ Usa a su vez la unidad CRT }

procedure AtXY( X, Y: byte ; texto: string ); begin gotoXY( X, Y); { Va a la posicin adecuada } write( texto ); end; end. { Final de la unidad }

Este ejemplo declara un procedimiento "AtXY" que hace un GotoXY y un Write en un solo paso. Un programa que lo emplease podra ser simplemente: program PruebaDeMiCrt1; uses miCrt1; begin AtXY( 7, 5, 'Texto en la posicin 7,5.' ); end.

520

Este programa no necesita llamar a la unidad CRT original, sino que nuestra unidad ya lo hace por l. Vamos a mejorar ligeramente nuestra unidad, aadindole un procedimiento "pausa": unit miCrt2; { Unidad que "mejora ms" la CRT }

{-------------------} interface

{ Parte "pblica", que se exporta }

procedure AtXY( X, Y: byte ; texto: string ); procedure Pausa; {-------------------} implementation

{ Parte "privada", detallada }

uses crt;

{ Usa a su vez la unidad CRT }

var tecla: char;

{ variable privada: el usuario no puede utilizarla }

procedure AtXY( X, Y: byte ; texto: string ); begin gotoXY( X, Y); { Va a la posicin adecuada } write( texto ); end; procedure Pausa; begin tecla := ReadKey; end; {-------------------} end. { Pausa, llamando a ReadKey } { El valor de "tecla" se pierde }

{ Final de la unidad }

521

y un programa que usase esta unidad, junto con la CRT original podra ser: program PruebaDeMiCrt2; uses crt, miCrt2; begin ClrScr; atXY( 7, 5, 'Texto en la posicin 7,5.' ); pausa; end.

{ De Crt } { de miCrt2 } { de miCrt2 }

Finalmente, las unidades pueden contener ms cosas adems de funciones y procedimientos: pueden tener un "trozo de programa", su cdigo de inicializacin, como por ejemplo: unit miCrt3; { Unidad que "mejora ms" la CRT }

{-------------------} interface

{ Parte "pblica", que se exporta }

var EraMono: boolean;

{ Variable pblica, el usuario puede acceder a ella }

procedure AtXY( X, Y: byte ; texto: string ); procedure Pausa; {-------------------} implementation

{ Parte "privada", detallada }

uses crt;

{ Usa a su vez la unidad CRT }

var tecla: char;

{ variable privada: el usuario no puede utilizarla }

procedure AtXY( X, Y: byte ; texto: string ); begin gotoXY( X, Y); { Va a la posicin adecuada }

522

write( texto ); end; procedure Pausa; begin tecla := ReadKey; end; {-------------------} begin if lastmode = 7 then EraMono := true else EraMono := false; end. { Pausa, llamando a ReadKey } { El valor de "tecla" se pierde }

{ Aqu va la inicializacin } { { { { Si el modo de pantalla era monocromo } EraMono ser verdadero } si no => falso } Final de la unidad }

y el programa podra usar la variable EraMono sin declararla: program PruebaDeMiCrt3; uses crt, miCrt3; begin ClrScr; atXY( 7, 5, 'Texto en la posicin 7,5.' ); if not EraMono then atXY ( 10, 10, 'Modo de color ' ); pausa; end.

{ De Crt } { de miCrt3 } { de miCrt3 }

Se podra hablar mucho ms sobre las unidades, pero resumiremos: Al compilar una unidad se crea un fichero .TPU, al que se puede acceder desde nuestros programas con dos condiciones: que empleemos la misma versin de Turbo Pascal (el formato de las TPU vara en cada versin), y que sepamos cmo es la parte pblica (interface). Cada unidad tiene su propio segmento de cdigo (esto va para quien conozca la estructura de la memoria en los PC), as que una unidad pueda almacenar hasta 64k de procedimientos o funciones. Los datos son comunes a todas las unidades, con la limitacin 64k en total (un segmento) para todos los datos (estticos) de todo el programa.

523

Si queremos almacenar datos de ms de 64k en el programa, tenga una o ms unidades, deberemos emplear variables dinmicas, distintas en su manejo de las que hemos visto hasta ahora (estticas), pero eso ya lo veremos en el prximo tema...

Tema 13: Variables dinmicas.


En Pascal estndar, tal y como hemos visto hasta ahora, tenemos una serie de variables que declaramos al principio del programa o de cada mdulo (funcin o procedimiento, unidad, etc.). Estas variables, que reciben el nombre de estticas, tienen un tamao asignado desde el momento en que se crea el programa. Esto es cmodo para detectar errores y rpido si vamos a manejar estructuras de datos que no cambien, pero resulta poco eficiente si tenemos estructuras cuyo tamao no sea siempre el mismo. Es el caso de una agenda: tenemos una serie de fichas, e iremos aadiendo ms. Si reservamos espacio para 10, no podremos llegar a aadir la nmero 11, estamos limitando el mximo. En este caso, la solucin que vimos fue la de trabajar siempre en el disco. No tenemos lmite en cuanto a nmero de fichas, pero es muchsimo ms lento. Lo ideal sera aprovechar mejor la memoria que tenemos en el ordenador, para guardar en ella todas las fichas o al menos todas aquellas que quepan en memoria. Una solucin "tpica" es sobredimensionar: preparar una agenda contando con 1000 fichas, aunque supongamos que no vamos a pasar de 200. Esto tiene varios inconvenientes: se desperdicia memoria, obliga a conocer bien los datos con los que vamos a trabajar, sigue pudiendo verse sobrepasado, y adems en Turbo Pascal tenemos muy poca memoria disponible para variables estticas: 64K (un segmento, limitaciones heredadas del manejo de memoria en el DOS en modo real). Por ejemplo, si en nuestra agenda guardamos lo siguientes datos de cada persona: nombre (40 letras), direccin (2 lneas de 50 letras), telfono (10 letras), comentarios (70 letras), que tampoco es demasiada informacin, tenemos que cada ficha ocupa 235 bytes, luego podemos almacenar menos de 280 fichas en memoria, incluso suponiendo que las dems variables que empleemos ocupen muy poco espacio. Todas estas limitaciones se solucionan con el uso de variables dinmicas, para las cuales se reserva espacio en el momento de ejecucin del programa, slo en la cantidad necesaria, se pueden aadir elementos despus, y se puede aprovechar toda la memoria convencional (primeros 640K) de nuestro equipo. Si adems nuestro compilador genera programas en modo protegido del DOS, podremos aprovechar toda la memoria real de nuestro ordenador (4 Mb, 8 Mb, etc.). Si crea programas para sistemas operativos que utilicen memoria virtual (como OS/2 o Windows, que destinan parte del disco duro para intercambiar con zonas de la memoria principal, de modo que aparentemente tenemos ms memoria disponible), podremos utilizar tambin esa memoria de forma transparente para nosotros. As que se acab la limitacin de 64K. Ahora podremos tener, por ejemplo, 30 Mb de datos en nuestro programa y con un acceso muchsimo ms rpido que si tenamos las fichas en disco, como hicimos antes. Ahora "slo" queda ver cmo utilizar estas variables dinmicas. Esto lo vamos a ver en 3 apartados. El primero (ste) ser la introduccin y veremos cmo utilizar arrays con elementos que

524

ocupen ms de 64K. El segundo, manejaremos las "listas enlazadas". El tercero nos centraremos en los "rboles binarios" y comentaremos cosas sobre otras estructuras. Introduccin. La idea de variable dinmica est muy relacionada con el concepto de puntero (pointer). Un puntero es una variable que "apunta" a una determinada posicin de memoria, en la que se encuentran los datos que nos interesan. Como un puntero almacena una direccin de memoria, slo gastar 4 bytes de esos 64K que tenamos para datos estticos. El resto de la memoria (lo que realmente ocupan los datos) se asigna en el momento en el que se ejecuta el programa y se toma del resto de los 640K. As, si nos quedan 500K libres, podramos guardar cerca de 2000 fichas en memoria, en vez de las 280 de antes. De los 64K del segmento de datos slo estaramos ocupando cerca de 8K (2000 fichas x 4 bytes). Vemoslo con un ejemplo (bastante intil, "puramente acadmico") que despus comentar un poco. program Dinamicas; type pFicha = ^Ficha;

(* Puntero a la ficha *)

Ficha = record nombre: string[40]; edad: byte end; var fichero: file of ficha; datoLeido: ficha; indice: array [1..1000] of pFicha; contador: integer;

(* Datos almacenados *)

(* (* (* (*

El fichero, claro *) Una ficha que se lee *) Punteros a 1000 fichas *) N de fichas que se lee *)

begin assign( fichero, 'Datos.Dat' ); (* Asigna el fichero *) reset( fichero ); (* Lo abre *) for contador := 1 to 1000 do (* Va a leer 1000 fichas *) begin read( fichero, datoleido ); (* Lee cada una de ellas *) new( indice[contador] ); (* Le reserva espacio *) indice[contador]^ := datoLeido; (* Y lo guarda en memoria *) end; close( fichero ); (* Cierra el fichero *) writeln('El nombre de la ficha 500 es: '); writeln(indice[500]^.nombre); for contador := 1 to 1000 do (* Liberamos memoria usada *) dispose( indice[contador] ); end.

525

El acento circunflejo (^) quiere decir "que apunta a" o "apuntado por". As, pFicha = ^Ficha;

indica que pFicha va a "apuntar a" datos del tipo Ficha, y indice[500]^.nombre

ser el campo nombre del dato al que apunta la direccin 500 del ndice. El manejo es muy parecido al de un array que contenga records, como ya habamos visto, con la diferencia de el carcter ^, que indica que se trata de punteros. Antes de asignar un valor a una variable dinmica, hemos de reservarle espacio con "new", porque si no estaramos escribiendo en una posicin de memoria que el compilador no nos ha asegurado que est vaca, y eso puede hacer que "machaquemos" otros datos, o parte del propio programa, o del sistema operativo... esto es muy peligroso, y puede provocar desde simples errores muy difciles de localizar hasta un "cuelgue" en el ordenador o cosas ms peligrosas... Cuando terminamos de utilizar una variable dinmica, debemos liberar la memoria que habamos reservado. Para ello empleamos la orden "dispose", que tiene una sintaxis igual que la de new: new( variable ); dispose( variable ); { Reserva espacio } { Libera el espacio reservado }

Ya hemos visto una forma de tener arrays de ms de 64K de tamao, pero seguimos con la limitacin en el nmero de fichas. En el prximo apartado veremos cmo evitar tambin esto. Arrays de punteros. Vimos una introduccin a los punteros y comentamos cmo se manejaran combinados con arrays. Antes de pasar a estructuras ms complejas, vamos a hacer un ejemplo prctico (que realmente funcione). Tomando la base que vimos, vamos a hacer un lector de ficheros de texto. Algo parecido al README.COM que incluyen Borland y otras casas en muchos de sus programas. Es un programa al que le decimos el nombre de un fichero de texto, lo lee y lo va mostrando por pantalla. Podremos desplazarnos hacia arriba y hacia abajo, de lnea en lnea o de pantalla en pantalla. Esta vez, en vez de leer un registro "record", leeremos "strings", y por comodidad los

526

limitaremos a la anchura de la pantalla, 80 caracteres. Tendremos una capacidad, por ejemplo, de 2000 lneas, de modo que gastaremos como mucho 80*2000 = 160 K aprox. Hay cosas que se podran hacer mejor, pero me he centrado en procurar que sea lo ms legible posible... program Lector; uses crt; const MaxLineas = 2000; kbEsc = #27; kbFuncion = #0; kbArr = kbPgArr kbAbj = kbPgAbj #72; = #73; #80; = #81; { Lee ficheros de texto } { Unidades externas: } { Pantalla de texto y teclado } { Para modificarlo fcilmente } { Cdigo ASCII de la tecla ESC } { Las teclas de funcin devuelven 0 + otro cdigo } { Cdigo de Flecha Arriba } { Pgina Arriba } { Flecha Abajo } { Pgina Abajo }

type LineaTxt = string [80]; PLineaTxt = ^LineaTxt; lineas = array[1..maxLineas] of PLineaTxt; var nomFich: string; fichero: text; datos: lineas; lineaActual: string; TotLineas: word; Primera: word;

{ Una lnea de texto } { Puntero a lna de texto } { Nuestro array de lneas }

{ { { { { {

El nombre del fichero } El fichero en s } Los datos, claro } Cada lnea que lee del fichero } El nmero total de lneas } La primera lnea en pantalla }

Procedure Inicio; { Abre el fichero } begin textbackground(black); { Colores de comienzo: fondo negro } textcolor(lightgray); { y texto gris } clrscr; { Borramos la pantalla } writeln('Lector de ficheros de texto.'); writeln; write('Introduzca el nombre del fichero: '); readln(nomFich); end; Procedure Pantalla; begin textbackground(red); textcolor(yellow); { Pantalla del lector } { Bordes de la pantalla } { Amarillo sobre rojo }

527

clrscr; { ... } gotoxy(2,1); write('Lector de ficheros de texto. Nacho Cabanes, 95.' +' Pulse ESC para salir'); gotoxy(2,25); write('Use las flechas y AvPag, RePag para moverse.'); window(1,2,80,24); { Define una ventana interior } textbackground(black); { Con distintos colores } textcolor(white); clrscr; end; Procedure EscribeAbajo(mensaje: string); { Escribe en la lnea inferior } begin window(1,1,80,25); { Restaura la ventana } textbackground(red); { Colores de los bordes: } textcolor(yellow); { Amarillo sobre rojo } gotoxy(60,25); { Se sita } write(mensaje); { y escribe } window(1,2,80,24); { Redefine la ventana interior } textbackground(black); { y cambia los colores } textcolor(white); end; procedure salir; var i: word; begin for i := 1 to TotLineas do dispose(datos[i]); window(1,1,80,25); texto, } textbackground(black); textcolor(white); clrscr; writeln('Hasta otra...'); end; Procedure Pausa; tecla } var tecla:char; begin tecla:=readkey; end; Function strs(valor:word):string; var cadena: string; begin str(valor,cadena); strs := cadena; end; function min(a,b: word): word; { Antes de abandonar el programa } { Para cada lnea leda, } { libera la memoria ocupada } { Restablece la ventana de { { { { el color de fondo, } el de primer plano, } borra la pantalla } y se despide }

{ Espera a que se pulse una

{ Convierte word a string }

{ Halla el mnimo de dos nmeros }

528

begin if a<b then min := a else min := b; end; procedure Lee; begin; clrscr; TotLineas := 0; Primera := 0; while (not eof(fichero)) and (TotLineas < MaxLineas) do begin readln( fichero, LineaActual ); TotLineas := TotLineas + 1 ; new(datos[TotLineas]); datos[TotLineas]^ := LineaActual; end; if TotLineas > 0 } then Primera := 1; close(fichero); end;

{ Inicializa variables } { Mientras quede fichero } { y espacio en el array } { { { { Lee una lnea } Aumenta el contador } Reserva memoria } y guarda la lnea }

{ Si realmente se han ledo lneas { empezaremos en la primera } { Al final, cierra el fichero }

procedure Muestra; { Muestra el fichero en pantalla } var i: word; { Para bucles } tecla: char; { La tecla que se pulsa } begin; repeat for i := Primera to Primera+22 do begin gotoxy(1, i+1-Primera ); { A partir de la primera lnea } if datos[i] <> nil then { Si existe dato correspondiente, } write(datos[i]^); { lo escribe } clreol; { Y borra hasta fin de lnea } end; EscribeAbajo('Lneas:'+strs(Primera)+'-'+ strs(Primera+22)+'/'+strs(TotLineas)); tecla := readkey ; if tecla = kbFuncion then begin { Si es tecla de funcin } tecla := readkey; { Mira el segundo cdigo } case tecla of kbArr: { Flecha arriba } if Primera>1 then Primera := Primera -1; kbAbj: { Flecha abajo } if Primera<TotLineas-22 then Primera := Primera + 1; kbPgArr: { Pgina arriba } if Primera>22 then Primera := Primera - 22 else Primera := 1; kbPgAbj: { Pgina Abajo } if Primera< (TotLineas-22) then Primera := Primera + min(22, TotLineas-23) else Primera := TotLineas-22;

529

end; end; until tecla = kbEsc; end; begin Inicio; { Pantalla inicial } assign(fichero, nomFich); { Asigna el fichero } {$I-} { desactiva errores de E/S } reset(fichero); { e intenta abrirlo } {$I+} { Vuelve a activar errores } if IOresult = 0 then { Si no ha habido error } begin Pantalla; { Dibuja la pantalla } Lee; { Lee el fichero } Muestra; { Y lo muestra } end else { Si hubo error } begin writeln(' No se ha podido abrir el fichero ! '); { Avisa } pausa; end; salir { En cualq. caso, sale al final } end.

Listas enlazadas. Habamos comentado cmo podamos evitar las limitaciones de 64K para datos y de tener que dar un tamao fijo a las variables del programa. Despus vimos con ms detalle como podamos hacer arrays de ms de 64K. Aprovechbamos mejor la memoria y a la vez seguamos teniendo acceso directo a cada dato. Como inconveniente: no podamos aadir ms datos que los que hubiramos previsto al principio (2000 lneas en el caso del lector de ficheros que vimos como ejemplo). Pues ahora vamos a ver dos tipos de estructuras totalmente dinmicas (frente a los arrays, que eran estticos). En esta leccin sern las listas, y en la prxima trataremos los rboles binarios. Hay otras muchas estructuras, pero no son difciles de desarrollar si se entienden bien estas dos. Ahora "el truco" consistir en que dentro de cada dato almacenaremos todo lo que nos interesa, pero tambin una referencia que nos dir dnde tenemos que ir a buscar el siguiente. Sera algo as como: (Posicin: 1023). Nombre : 'Nacho Cabanes' DireccionFido : '2:346/3.30' SiguienteDato : 1430

530

Este dato est almacenado en la posicin de memoria nmero 1023. En esa posicin guardamos el nombre y la direccin (o lo que nos interese) de esta persona, pero tambin una informacin extra: la siguiente ficha se encuentra en la posicin 1430. As, es muy cmodo recorrer la lista de forma secuencial, porque en todo momento sabemos dnde est almacenado el siguiente dato. Cuando lleguemos a uno para el que no est definido cual es el siguiente, quiere decir que se ha acabado la lista. Hemos perdido la ventaja del acceso directo: ya no podemos saltar directamente a la ficha nmero 500. Pero, por contra, podemos tener tantas fichas como la memoria nos permita. Para aadir un ficha, no tendramos ms que reservar la memoria para ella, y el Turbo Pascal nos dira "le he encontrado sitio en la posicin 4079". As que nosotros iramos a la ltima ficha y le diramos "tu siguiente dato va a estar en la posicin 4079". Esa es la idea "intuitiva". Vamos a empezar a concretar cosas en forma de programa en Pascal. Primero cmo sera ahora cada una de nuestras fichas: type pFicha = ^Ficha; Ficha = record nombre: string[30]; direccion: string[50]; edad: byte; siguiente: pFicha; end; { Puntero a la ficha } { Estos son los datos que guardamos: } { Nombre, hasta 30 letras } { Direccion, hasta 50 } { Edad, un numero < 255 } { Y direccin de la siguiente }

La nomenclatura ^Ficha ya la habamos visto. Se refiere a que eso es un "puntero al tipo Ficha". Es decir, la variable "pFicha" va a tener como valor una direccin de memoria, en la que se encuentra un dato del tipo Ficha. La diferencia est en el campo "siguiente" de nuestro registro, que es el que indica donde se encuentra la ficha que va despus de la actual. Un puntero que "no apunta a ningn sitio" tiene el valor NIL, que nos servir despus para comprobar si se trata del final de la lista: todas las fichas "apuntarn" a la siguiente, menos la ltima, que "no tiene siguiente". Entonces la primera ficha la definiramos con var dato1: pFicha; { Va a ser un puntero a ficha }

y la crearamos con

531

new (dato1); dato1^.nombre := 'Pepe'; dato1^.direccion := 'Su casa'; dato1^.edad := 45; dato1^.siguiente := nil;

{ Reservamos memoria } { Guardamos el nombre, } { la direccin } { la edad } { y no hay ninguna ms }

Ahora podramos aadir una ficha detrs de ella. Primero guardamos espacio para la nueva ficha, como antes: var dato2: pFicha; { Va a ser otro puntero a ficha }

new (dato2); dato2^.nombre := 'Juan'; dato2^.direccion := 'No lo s'; dato2^.edad := 35; dato2^.siguiente := nil;

{ Reservamos memoria } { Guardamos el nombre, } { la direccin } { la edad } { y no hay ninguna detrs }

y ahora enlazamos la anterior con ella: dato1^.siguiente := dato2;

Si quisiramos introducir los datos ordenados alfabticamente, basta con ir comparando cada nuevo dato con los de la lista, e insertarlo donde corresponda. Por ejemplo, para insertar un nuevo dato entre los dos anteriores, haramos: var dato3: pFicha; { Va a ser otro puntero a ficha }

new (dato3); dato3^.nombre := 'Carlos'; dato3^.direccion := 'Por ah'; dato3^.edad := 14; dato3^.siguiente := dato2;

{ enlazamos con la siguiente }

dato1^.siguiente := dato3;

{ y con la anterior }

532

La estructura que hemos obtenido es la siguiente Dato1 - Dato3 - Dato2 - nil o grficamente: +------+ +------+ +------+ Dato1 +->-Dato3 +->--Dato2 +------ +------ +------ +---------+ +--------+ +-----------+ -----------

nil

Es decir: cada ficha est enlazada con la siguiente, salvo la ltima, que no est enlazada con ninguna (apunta a NIL). Si ahora quisiramos borrar Dato3, tendramos que seguir dos pasos: 1.- Enlazar Dato1 con Dato2, para no perder informacin. 2.- Liberar la memoria ocupada por Dato3. Esto, escrito en "pascalero" sera: dato1^.siguiente := dato2; dispose(dato3); { Enlaza Dato1 y Dato2 } { Libera lo que ocup Dato3 }

Hemos empleado tres variables para guardar tres datos. Si tenemos 20 datos, necesitaremos 20 variables? Y 3000 variables para 3000 datos? Sera tremendamente ineficiente, y no tendra mucho sentido. Es de suponer que no sea as. En la prctica, basta con dos variables, que nos indicarn el principio de la lista y la posicin actual, o incluso slo una para el principio de la lista. Por ejemplo, un procedimiento que muestre en pantalla toda la lista se podra hacer de forma recursiva as: procedure MuestraLista ( inicial: pFicha ); begin

533

if inicial <> nil then { Si realmente hay lista } begin writeln('Nombre: ', inicial^.nombre); writeln('Direccin: ', inicial^.direccion); writeln('Edad: ', inicial^.edad); MuestraLista ( inicial^.siguiente ); { Y mira el siguiente } end; end;

Lo llamaramos con "MuestraLista(Dato1)", y a partir de ah el propio procedimiento se encarga de ir mirando y mostrando los siguientes elementos hasta llegar a NIL, que indica el final. Aqu va un programilla de ejemplo, que ordena los elementos que va insertando... y poco ms: program EjemploDeListas; type puntero = TipoDatos numero: sig: end;

^TipoDatos; = record integer; puntero

function CrearLista(valor: integer): puntero; {Crea la lista, claro} var r: puntero; { Variable auxiliar } begin new(r); { Reserva memoria } r^.numero := valor; { Guarda el valor } r^.sig := nil; { No hay siguiente } CrearLista := r { Crea el puntero } end;

procedure MuestraLista ( lista: puntero ); begin if lista <> nil then { Si realmente hay lista } begin writeln(lista^.numero); { Escribe el valor } MuestraLista (lista^.sig ) { Y mira el siguiente } end; end;

procedure InsertaLista( var lista: puntero; valor: integer); var

534

r: puntero; begin if lista <> nil then if lista^.numero<valor then InsertaLista(lista^.sig,valor) else begin new(r); r^.numero := valor; r^.sig := lista; lista := r end else begin new(r); r^.numero := valor; r^.sig := nil; lista := r end end;

{ Variable auxiliar } { Si hay lista } { y todava no es su sitio } { hace una llamada recursiva: } { mira la siguiente posicin } { Caso contrario: si hay lista } { pero hay que insertar ya: } { Reserva espacio, } { guarda el dato } { pone la lista a continuac. } { Y hace que comience en } { el nuevo dato: r } { Si no hay lista } { deber crearla } { reserva espacio } { guarda el dato } { no hay nada detrs y } { hace que la lista comience } { en el dato: r }

var l: puntero; begin l := CrearLista(5); InsertaLista(l, 3); InsertaLista(l, 2); InsertaLista(l, 6); MuestraLista(l) end.

{ Variables globales: la lista }

{ { { { {

Crea una lista e introduce un 5 } Inserta un 3 } Inserta un 2 } Inserta un 6 } Muestra la lista resultante }

Ejercicios propuestos: 1- Se podra quitar de alguna forma el segundo "else" de InsertaLista? 2- Cmo sera un procedimiento que borrase toda la lista? 3- Y uno de bsqueda, que devolviera la posicin en la que est un dato, o NIL si el dato no existe? 4- Cmo se hara una lista "doblemente enlazada", que se pueda recorrer hacia adelante y hacia atrs? rboles binarios.

535

Hemos visto cmo crear listas dinmicas enlazadas, y cmo podamos ir insertando los elementos en ellas de forma que siempre estuviesen ordenadas. Hay varios casos particulares. Slo comentar un poco algunos de ellos: Una pila es un caso particular de lista, en la que los elementos siempre se introducen y se sacan por el mismo extremo (se apilan o se desapilan). Es como una pila de libros, en la que para coger el tercero deberemos apartar los dos primeros (salvo los malabaristas). Este tipo de estructura se llama LIFO (Last In, First Out: el ltimo en entrar es el primero en salir). Una cola es otro caso particular, en el que los elementos se introducen por un extremo y se sacan por el otro. Es como se supone que debera ser la cola del cine: los que llegan, se ponen al final, y se atiende primero a los que estn al principio. Esta es una estructura FIFO (First In, First Out).

Estas dos son estructuras ms sencillas de programar de lo que sera una lista en su caso general, pero que son tambin tiles en muchos casos. Finalmente, antes de pasar con los "rboles", comentar una mejora a estas listas enlazadas que hemos visto. Tal y como las hemos tratado, tienen la ventaja de que no hay limitaciones tan rgidas en cuanto a tamao como en las variables estticas, ni hay por qu saber el nmero de elementos desde el principio. Pero siempre hay que recorrerlas desde DELANTE hacia ATRS, lo que puede resultar lento. Una mejora relativamente evidente es lo que se llama una lista doble o lista doblemente enlazada: si guardamos punteros al dato anterior y al siguiente, en vez de slo al siguiente, podremos avanzar y retroceder con comodidad. Pero tampoco profundizaremos ms en ellas.

ARBOLES.
En primer lugar, veamos de donde viene el nombre. En las listas, despus de cada elemento vena otro (o ninguno, si habamos llegado al final). Pero tambin nos puede interesar tener varias posibilidades despus de cada elemento, 3 por ejemplo. De cada uno de estos 3 saldran otros 3, y as sucesivamente. Obtendramos algo que recuerda a un rbol: un tronco del que nacen 3 ramas, que a su veces se subdividen en otras 3 de menor tamao, y as sucesivamente hasta llegar a las hojas. Pues eso ser un rbol: una estructura dinmica en la que cada nodo (elemento) puede tener ms de un "siguiente". Nos centraremos en los rboles binarios, en los que cada nodo puede tener un hijo izquierdo, un hijo derecho, ambos o ninguno (dos hijos como mximo). Para puntualizar a un ms, aviso que trataremos los rboles binarios de bsqueda, en los que tenemos prefijado un cierto orden, que nos ayudar a encontrar un cierto dato dentro de un rbol con mucha rapidez. Y como es este "orden prefijado"? Sencillo: para cada nodo tendremos que: la rama de la izquierda contendr elementos menores. la rama de la derecha contendr elementos mayores.

Como ejemplo, vamos a introducir en un rbol binario de bsqueda los datos 5,3,7,2,4,8,9 Primer nmero: 5 (directo)

536

Segundo nmero: 3 (menor que 5) 5

/ 3

Tercer nmero: 7 (mayor que 5) 5 / \

Cuarto: 2 (menor que 5, menor que 3) 5 / \ / 2 3 7

Quinto: 4 (menor que 5, mayor que 3) 5 / \ 4

3 / \

Sexto: 8 (mayor que 5, mayor que 7) 5 / \ 3 / \ 2 4 7 \ 8

Sptimo: 9 (mayor que 5, mayor que 7, mayor que 8) 5 / \

537

3 / \

7 4 \ 8 \ 9

Y qu ventajas tiene esto? Pues la rapidez: tenemos 7 elementos, lo que en una lista supone que si buscamos un dato que casualmente est al final, haremos 7 comparaciones; en este rbol, tenemos 4 alturas => 4 comparaciones como mximo. Y si adems hubiramos "equilibrado" el rbol (irlo recolocando, de modo que siempre tenga la menor altura posible), seran 3 alturas. Esto es lo que se hace en la prctica cuando en el rbol se va a hacer muchas ms lecturas que escrituras: se reordena internamente despus de aadir cada nuevo dato, de modo que la altura sea mnima en cada caso. De este modo, el nmero mximo de comparaciones que tendramos que hacer sera log2(n), lo que supone que si tenemos 1000 datos, en una lista podramos llegar a tener que hacer 1000 comparaciones, y en un rbol binario, log2(1000) => 10 comparaciones como mximo. La ganancia es evidente. No vamos a ver cmo se hace eso de los "equilibrados", que sera propio de un curso de programacin ms avanzado, o incluso de uno de "Tipos Abstractos de Datos" o de "Algortmica", y vamos a empezar a ver rutinas para manejar estos rboles binarios de bsqueda. Recordemos que la idea importante es todo dato menor estar a la izquierda del nodo que miramos, y los datos mayores estarn a su derecha. Ahora la estructura de cada nodo (dato) ser: type TipoDato = string[10]; { Vamos a guardar texto, por ejemplo }

Puntero = ^TipoBase; TipoBase = record dato: TipoDato; hijoIzq: Puntero; hijoDer: Puntero; end;

{ El puntero al tipo base } { El tipo base en s: } { - un dato } { - puntero a su hijo izquierdo } { - puntero a su hijo derecho }

Y las rutinas de insercin, bsqueda, escritura, borrado, etc., podrn ser recursivas. Como primer ejemplo, la de escritura de todo el rbol (la ms sencilla) sera:

538

procedure Escribir(punt: puntero); begin if punt <> nil then { Si no hemos llegado a una hoja } begin Escribir(punt^.hijoIzq); { Mira la izqda recursivamente } write(punt^.dato); { Escribe el dato del nodo } Escribir(punt^.hijoDer); { Y luego mira por la derecha } end; end;

Si alguien no se cree que funciona, que coja lpiz y papel y lo compruebe con el rbol que hemos puesto antes como ejemplo. Es muy importante que este procedimiento quede claro antes de seguir leyendo, porque los dems sern muy parecidos. La rutina de insercin sera: procedure Insertar(var punt: puntero; valor: TipoDato); begin if punt = nil then { Si hemos llegado a una hoja } begin new(punt); { Reservamos memoria } punt^.dato := valor; { Guardamos el dato } punt^.hijoIzq := nil; { No tiene hijo izquierdo } punt^.hijoDer := nil; { Ni derecho } end else { Si no es hoja } if punt^.dato > valor { Y encuentra un dato mayor } Insertar(punt^.hijoIzq, valor) { Mira por la izquierda } else { En caso contrario (menor) } Insertar(punt^.hijoDer, valor) { Mira por la derecha } end;

Y finalmente, la de borrado de todo el rbol, casi igual que la de escritura: procedure BorrarArbol(punt: puntero); begin if punt <> nil then { Si queda algo que borrar } begin BorrarArbol(punt^.hijoIzq); { Borra la izqda recursivamente } dispose(punt); { Libera lo que ocupaba el nodo } BorrarArbol(punt^.hijoDer); { Y luego va por la derecha } end; end;

539

Un comentario: esta ltima rutina es peligrosa, porque indicamos que "punt" est libre y despus miramos cual es su hijo izquierdo (despus de haber borrado la variable). Esto funciona en Turbo Pascal , porque marca esa zona de memoria como disponible pero no la borra fsicamente, pero puede dar problemas con otros compiladores o si se adapta esta rutina a otros lenguajes (como C). Una forma ms segura de hacer lo anterior sera: procedure BorrarArbol2(punt: puntero); var derecha: puntero; { Aqu guardaremos el hijo derecho } begin if punt <> nil then { Si queda algo que borrar } begin BorrarArbol2(punt^.hijoIzq); { Borra la izqda recursivamente } derecha := punt^.hijoDer; { Guardamos el hijo derecho <=== } dispose(punt); { Libera lo que ocupaba el nodo } BorrarArbol2(derecha); { Y luego va hacia por la derecha } end; end;

O bien, simplemente, se pueden borrar recursivamente los dos hijos antes que el padre (ahora ya no hace falta ir "en orden", porque no estamos leyendo, sino borrando todo): procedure BorrarArbol(punt: puntero); begin if punt <> nil then { Si queda algo que borrar } begin BorrarArbol(punt^.hijoIzq); { Borra la izqda recursivamente } BorrarArbol(punt^.hijoDer); { Y luego va hacia la derecha } dispose(punt); { Libera lo que ocupaba el nodo } end; end;

Ejercicios propuestos: Implementar una pila de strings[20]. Implementar una cola de enteros. Implementar una lista doblemente enlazada que almacene los datos ledos de un fichero de texto (mejorando el lector de ficheros que vimos). Hacer lo mismo con una lista simple, pero cuyos elementos sean otras listas de caracteres, en vez de strings de tamao fijo. Aadir la funcin "buscar" a nuestro rbol binario, que diga si un dato que nos interesa pertenece o no al rbol (TRUE cuando s pertenece; FALSE cuando no). Cmo se borrara un nico elemento del rbol?

Apndice 1: Otras rdenes no vistas.

540

A lo largo del curso ha habido rdenes que no hemos tratado, bien porque no encajasen claramente en ningn tema, o bien porque eran demasiado avanzadas para explicarlas junto con las ms parecidas. En primer lugar vamos a ir viendo las que podan haber formado parte de temas anteriores, y despus las que faltan. Tampoco pretendo que esto sea una recopilacin exhaustiva, sino simplemente mencionar algunas rdenes interesantes que parecan haberse quedado en el tintero. Los temas que se van a comentar son: Bucles: break, continue. Saltos: goto, label. Punteros: getmem, freemem, pointer. Fin del programa: exit, halt. Nmeros aleatorios: rnd, randomize. Funciones: abs, sin, cos, arctan, round, trunc, sqr, sqrt, exp, ln, odd, potencias.

Bucles: break, continue. En Turbo Pascal 7.0, tenemos dos rdenes extra para el control de bucles, tanto si se trata de "for", como de "repeat" o "until". Estas rdenes son: Break: sale del bucle inmediatamente. Continue: pasa a la siguiente iteracin.

Un ejemplo "poco til" que use ambas podra ser escribir los nmeros pares hasta el 10 con un for: for i := 1 to 1000 do { begin if i>10 then break; { if i mod 2 = 1 then continue; { writeln( i ); { end; Nos podemos pasar } Si es as, sale } Si es impar, no lo escribe } Si no, lo escribe }

Esto se puede imitar en versiones anteriores de TP con el temible "goto", que vamos a comentar a continuacin: Goto. Es una orden que se puede emplear para realizar saltos incondicionales. Su empleo est bastante desaconsejado, pero en ciertas ocasiones, y se lleva cuidado, puede ser til (para salir de bucles fuertemente anidados, por ejemplo, especialmente si no se dispone de las dos rdenes anteriores. El formato es goto etiqueta

541

y las etiquetas se deben declarar al principio del programa, igual que las variables. Para ello se usa la palabra "label". Vamos a verlo directamente con un ejemplo: label uno, dos; var

donde: byte;

begin writeln('Quiere saltar a la opcin 1 o a la 2?'); readln (donde); case donde of 1: goto uno; 2: goto dos; else writeln('Nmero incorrecto'); end; exit; uno: writeln('Esta es la opcin 1. Vamos a seguir...'); dos: writeln('Esta es la opcin 2.'); end.

Punteros: getmem, freemem, pointer. Habamos hablado de "new" y "delete", en las que el compilador decide la cantidad de memoria que debe reservar. Pero tambin podemos obligarle a reservar la que nosotros queramos, lo que nos puede interesar, por ejemplo, para leer cadenas de caracteres de longitud variable. Para esto usamos "getmem" y "freemem", en los que debemos indicar cunta memoria queremos reservar para el dato en cuestin. Si queremos utilizarlos como new y dispose, reservando toda la memoria que ocupa el dato (ser lo habitual), podemos usar "sizeof" para que el compilador lo calcule por nosotros: type TipoDato = record Nombre: string[40]; Edad: Byte; end; var p: pointer; begin if MaxAvail < SizeOf(TipoDato) then

542

Writeln('No hay memoria suficiente') else begin GetMem(p, SizeOf(TipoDato)); { Trabajaramos con el dato, y despus... } FreeMem(p, SizeOf(TipoDato)); end; end.

Por cierto, "pointer" es un puntero genrico, que no est asociado a ningn tipo concreto de dato. No lo vimos en la leccin sobre punteros, porque para nosotros lo habitual es trabajar con punteros que estn relacionados con un cierto tipo de datos. Exit, halt. En el tema 8 (y en los ejemplos del tema 6) vimos que podamos usar exit para salir de un programa. Esto no es exacto del todo: exit sale del bloque en el que nos encontremos, que puede ser un procedimiento o una funcin. Por tanto, slo sale del programa si ejecutamos exit desde el cuerpo del programa principal. Si estamos dentro de un procedimiento, y queremos abandonar el programa por completo, deberamos hacer ms de un "exit". Pero tambin hay otra opcin: la orden "halt". Halt s que abandona el programa por completo, estemos donde estemos. Como parmetro se le puede pasar el "Errorlevel" que se quiere devolver al DOS, y que se puede leer desde un fichero batch. Por ejemplo: "halt" o "halt(0)" indicara una salida normal del programa (sin errores), "halt(1)" podra indicar que no se han encontrado los datos necesarios, etc.

Nmeros aleatorios.
Si queremos utilizar nmeros aleatorios en nuestros programas, podemos emplear la funcin "random". Si la usamos tal cual, nos devuelve un nmero real entre 0 y 1. Si usamos el formato "random(n)", lo que devuelve es un nmero entero entre 0 y n-1. Para que el ordenador comience a generar la secuencia de nmeros aleatorios, podemos usar "randomize", que toma como semilla un valor basado en el reloj, lo que supone que sea suficientemente aleatorio: var i:integer; begin Randomize; for i := 1 to 50 do Write (Random(1000), ' '); end.

543

Funciones.
La mayora de las que vamos a ver son funciones matemticas que estn ya predefinidas en Pascal. Muchas de ellas son muy evidentes, pero precisamente por eso no podamos dejarlas sin mencionar al menos: Abs: valor absoluto de un nmero. Sin: seno de un cierto ngulo dado en radianes. Cos: coseno, anlogo. ArcTan: arco tangente. No existe funcin para la tangente, que podemos calcular como sin(x)/cos(x). Round: redondea un nmero real al entero ms cercano. Trunc: trunca los decimales de un nmero real para convertirlo en entero. Int: igual que trunc, pero el resultado sigue siendo un nmero real. Sqr: eleva un nmero al cuadrado. Sqrt: halla la raz cuadrada de un nmero. Exp: exponencial en base e, es decir, eleva un nmero a e. Ln: calcula el logaritmo neperiano (base e) de un nmero. Odd: devuelve TRUE si un nmero es impar. Potencias: no hay forma directa de elevar un nmero cualquiera a otro en pascal, pero podemos imitarlo con "exp" y "ln", as:

function elevado(a,b: real): real; begin elevado := exp(b *ln(a) ); end; begin writeln(elevado(2,3)); end.

La deduccin de esta frmula es fcil, conociendo las propiedades de los logaritmos y las exponenciales. a^b = exp ( ln ( a^b )) = exp ( b * ln ( a ))

Apndice 2: Palabras reservadas de Turbo Pascal (ordenadas alfabticamente):


Son las palabras clave que no se pueden redefinir: and: Para encadenar comparaciones ("y"), u operador de bits (producto). array: Matriz o vector a partir de un tipo base. asm: Accede al ensamblador integrado. begin: Comienzo de una sentencia compuesta. case: Eleccin mltiple segn los valores de una variable.

544

const: Comienzo de la declaracin de constantes. constructor: Inicializa un objeto que contiene mtodos virtuales. destructor: Elimina un objeto que contiene mtodos virtuales. div: Divisin entera. do: Ordenes a realizar en un bucle "for" o en un "while". downto: Indica el final de un bucle "for" descendente. else: Accin a realizar si no se cumple la condicin dada en un "if". end: Fin de una sentencia compuesta. file: Tipo de datos: fichero. for: Bucle repetitivo. Se usa con "to" o "downto", y con "do". function: Definicin de una funcin. goto: Salta a una cierta etiqueta dentro del programa. if: Comprobacin de una condicin. Se usa con "then" y "else". implementation: Parte privada de una unidad: detalle que los procedimientos y funciones que la forman, junto con los datos no accesibles. in: Comprueba la pertenencia de un elemento en un conjunto. inherited: Accede al mtodo de igual nombre del objeto padre. inline: Inserta instrucciones en cdigo mquina dentro del programa. interface: Parte pblica de una unidad: datos accesibles y cabeceras de los procedimientos y funciones. label: Etiqueta a la que se puede saltar con "goto". mod: Resto de la divisin entera. nil: Puntero nulo. not: Operador lgico ("no") y de bits. object: Declaracin de un objeto. of: Indica el tipo base de un "array", "string", "set" o "file". or: Operador lgico ("") y de bits (suma).

545

packed: Tipo de array. No necesario en Turbo Pascal. procedure: Definicin de un procedimiento. program: Nombre de un programa. record: Tipo de datos: registro. repeat: Repite una serie de rdenes hasta ("until") que se cumple una condicin. set: Tipo conjunto. shl: Desplazamiento de bits hacia la izquierda. shr: Desplazamiento de bits hacia la derecha. string: Tipo de datos: cadena de caracteres. then: Accin a realizar si se cumple una condicin dada por "if". to: Indica el final de un bucle "for" ascendente. type: Comienzo de la declaracin de tipos. unit: Comienzo (y nombre) de la definicin de una unidad. until: Condicin de fin de un "repeat". uses: Lista las unidades que va a utilizar un programa u otra unidad. var: Comienzo de la declaracin de variables. while: Repite una orden mientras se d una condicin. with: Para acceder a los campos de un "record" sin tener que nombrarlo siempre. xor: Operacin de bits (suma exclusiva).

546

Curso de Perl
1- Introduccin 1.1- PERL
1.1.1- Que es PERL Perl (Practical Extraction and Report Languaje) es un lenguaje de programacin surgido a inicios de los noventas, que busca antes que nada el facilitar la elaboracin de tareas comunes en sistemas tipo UNIX, donde tradicionalmente las tareas de administracin y proceso de datos se realiza con herramientas muy rudimentarias y por dems hostiles al usuario o administrador. Pero que se aplican sobre grandes cantidades de informacin (por lo regular texto) por lo que se requiere que sean de alto rendimiento. Su autor, Larry Wall ([email protected]) realiz Perl casi como una obra altruista, de modo que hasta PERL 5.X su distribucin es gratuita, sin que por eso tenga menos poder o consistencia. 1.1.2- Para que sirve Perl surgi como una opcin para una gran cantidad de herramientas de UNIX en las cuales basa su propia sintaxis, buscando el mnimo sacrificio de su desempeo por una mxima facilidad de programacin e integracin, sigue la filosofa de mantener un ambiente que sea capaz de detectar y corregir pequeas omisiones del programador, y de proporcionarle una forma abreviada de realizar mltiples tareas. En una palabra, es una utilera que pretende facilitar el proceso de grandes volmenes de informacin sin sacrificar el rendimiento. 1.1.3- Donde Puede Usarse Las plataformas donde Perl se ha desarrollado mas son los servidores UNIX, por sus necesidades de administracin y lo robusto de su manejo de memoria y de procesos (requisitos de PERL hacia el S.O.) adems de la facilidad de Perl para realizar los as llamados CGIs, interfaces para comunicar recursos del servidor con un servicio de internet particular (como podra ser WWW o gopher), En otras plataformas, PC en particular, se han desarrollado versiones que mantienen un razonable grado de funcionalidad, pero en realidad, el sistema DOS no tiene un manejo lo bastante bueno de los procesos o de la memoria para permitir a PERL dar un buen desempeo, adems de que no es comn ver en PC necesidades de administracin de la magnitud de un servidor institucional. Sin embargo, puede practicarse la programacin en PERL de PC, o incluso elaborar programas de reporteo en el , sin embargo, es algo que no se ha popularizado hasta hoy. 1.1.4- Que fuentes de informacin existen Los libros que son ya clsicos sobre Perl son los libros del camello y la llama de la editorial Nutshell, adems, existen magnficas introducciones y manuales de referencia que se pueden obtener va internet. Aun cuando es imposible mencionar con precisin las fuentes de informacin de un medio tan dinmico con algo tan esttico como este documento. Debe notarse, adems que estas referencias estn en ingls. Para buscar informacin, Yahoo! por supuesto: http://www.yahoo.com/Computers_and_Internet/Languajes/Perl/

547

Fuente de informacin general y referencia de documentos: http://pubweb.nexor.co.uk/public/perl/perl.html http://www.khoros.unm.edu:80/staff/neilb/perl/perl5.html Una buena formada introduccin: http://www.khoros.unm.edu:80/staff/neilb/perl/introduction/home.html Documentacin: http://www-cgi.cs.cmu.edu/cgi-bin/perl-man http://www.perl.com/perl/info/documentation.html ftp://ftp.cdrom.com/pub/perl/CPAN/doc/manual/html/index.html ftp://ftp.uoknor.edu/mirrors/CPAN/doc/manual/html/index.html Debo recalcar que por la misma naturaleza de Perl, los recursos disponibles y las herramientas que se pueden utilizar cambian muy a menudo, por lo que es indispensable dedicar algn esfuerzo a mantenerse al da para evitar un desperdicio mayor de esfuerzo por no utilizar los nuevos recursos disponibles.

1.2- Filosofa de Perl


"Hay mas de una forma de hacerlo" -Larry Wall, autor del lenguaje de programacin Perl. Perl no establece ninguna filosofa de programacin (de hecho, no se puede decir que sea orientado a objetos, modular o estructurado aun cuando soporta directamente todos estos paradigmas), los objetivos que se tuvieron en cuenta al disear la sintaxis de Perl fueron la facilidad de aprendizaje y de uso y la claridad de cdigo, las cuales, considero que son necesarias (aunque pueden escribirse programas en Perl complejos e inteligibles si as se desea). Por si fuese poco, Perl no es ni un compilador ni un interprete, esta en un punto intermedio, cuando mandamos a ejecutar un programa en Perl, se compila el cdigo fuente a un cdigo intermedio en memoria, se le optimiza (como si fusemos a elaborar un programa ejecutable) pero es ejecutado por un motor, como si se tratase de un interprete. El resultado final, es que utilizamos algo que se comporta como un interprete pero que tiene un rendimiento comparativo al de programas compilados. Sin embargo, ya existen compiladores de Perl con la versin 5. En fin, Perl no nos forza a nada, pero como es lgico hay ciertas reglas que recomiendo seguir para facilitar nuestro trabajo: Claridad. En la mecnica de programacin actual, los programas deben de ser entendibles por la persona que nos suceda en tareas de mantenimiento, de lo contrario perjudicamos tanto a nuestros compaeros de trabajo como a nuestra propia libertad para progresar y mantenernos libres de preocupaciones.

548

Indentacin. Una costumbre ya clsica de la programacin, en lo personal, y a lo largo de los ejemplos de este documento, indento el cdigo dos espacios hacia adelante al abrir cada bloque, y termino la indentacin al terminar el bloque, de modo que las llaves de apertura y cierre quedan a la vista y en la misma columna, solas en sus renglones (esto incrementa algo el numero de lneas, pero facilita sobremanera la bsqueda y correccin de los diversos bloques de control). Nombres de variables y dems. En lo personal, procuro dar la mxima claridad a los nombres de las variables sin hacerlos demasiado grandes, el nombre de los contadores y variables que guardan valores concernientes a un pequeo segmento de cdigo por lo regular son de un par de letras (c1, c2, ... cx para los contadores, s1, s2, etc para cadenas de entrada etc.) mientras que las variables que afectan a diversos segmentos (a modo de regla, que tienen su definicin en una pantalla distinta a donde se usan) tienen nombres explicativos que procuro no excedan los 12 caracteres. Adems, los nombres de archivos se usan con maysculas (ARCHENT, ARCHSAL, etc) y las clases tienen su primera letra mayscula. Comentarios. Para facilitar la comprensin de un programa no hay como explicarlo, y los comentarios son el medio ideal para hacerlo, hay por lo menos tres comentarios que considero que siempre deben incluirse en un programa: Que hace el programa, Quien lo escribi y Cuando inicio y termino de escribirlo, sobretodo en el contexto de una organizacin, estos tres simples comentarios pueden hacer la diferencia entre desechar un programa como indescifrable o dedicarle algn tiempo para revisarlo. Adems, considero prudente comentar dentro del cdigo la forma en que el programa deber ejecutarse, parmetros, y su sintaxis, as como comentar las estructuras de control como un modo de explicar la funcionalidad al detalle y recalcar con comentarios las funciones que cumplen las variables. Sencillez. Es cmodo en ocasiones el comprimir una serie de instrucciones en una sola lnea, queda al criterio decidir cuando se gana en claridad con un cdigo mas o menos extenso, pero no debe titubearse en comentar el cdigo que sea "comprimido".

1.3- Diferencias entre Perl 4.3 y 5.X, Como elegir versin


Actualmente existen dos versiones altamente populares de Perl, la 4.3 y la 5.0X, de hecho hay diferencias importantes entre una versin y otra. Seguramente el lector se pregunta porque surge la duda entre usar una versin vieja y una nueva, por regla general las nuevas versiones son mejores que las anteriores de modo que las opacan en todo sentido, Perl no es la excepcin a esta regla, el nico factor que impide una transicin inmediata es que no son 100% compatibles. La versin 5 de Perl es una reescritura total que ya incluye un manejo de estructuras abstractas de datos mucho mas poderoso, incluso, soporta la orientacin a objetos a su manera (tema que no trato en esta introduccin). De modo que las libreras, por ejemplo para creacin de CGIs no funcionan de una funcin a otra por lo que la migracin es poco practica. As pues, la decisin sobre que versin utilizar depende del trabajo que haya sido realizado con anterioridad, si ya se tiene un sistema completo o grande en Perl 4, es recomendable mantenerlo en Perl 4 por el resto de su vida til, pero para desarrollos nuevos es por mucho, mas recomendable iniciarlos con Perl 5 o en su caso, la versin mas reciente que este disponible, por experiencia se que la capacitacin para adaptarse a la nueva versin es extraordinariamente corta dada la importancia de las mejoras. Una tercera opcin (que es por mucho la mas recomendable si se tienen los recursos) consiste en instalar las dos versiones de modo que convivan en el sistema.

2- Programacin bsica

549

En este capitulo daremos una rpida revisin a los conceptos mas usuales que se encuentran en un programa en Perl, trataremos de ver la implementacin para ambas versiones 4 y 5 cuando sea posible, especificando siempre en que versin esta el ejemplo original (dando preferencia a la versin 5) y al menos las alternativas para implementarlo en la otra versin. Los ejemplo requerirn para funcionar, que se tenga Perl instalado en la maquina en que se practique y deber conocerse la ruta completa al binario de Perl. Mas que sentirse en libertad de experimentar con los diversos ejemplos, deben sentirse obligados a experimentar, modificar y explorar por su cuenta cada que sea posible, tomando los ejemplo solo como un punto seguro de partida. Para este fin, recomiendo que se tenga acceso desde el inicio a alguna referencia del lenguaje (ya sea el libro Programming Perl (del Camello) de la Nutshell o alguna referencia disponible por WWW), este documento pretende explicar lo bsico de un modo accesible para que despus pueda el nuevo programador de Perl abordar sin temor los materiales mas detallados.

2.1- Estructura Bsica de un programa, programa Holamundo.


Como se menciona en la introduccin, Perl no obliga a casi nada, as pues, lo que planteo como estructura bsica es mas bien una convencin que un requisito del lenguaje, a diferencia de Pascal (por ejemplo) Perl no tiene una plantilla para sus programas y si se adoptan algunos protocolos es solo por comodidad. Los programas de Perl, por lo regular, inician con la lnea: #!/usr/bin/perl Esta lnea, indica al SO que lo que sigue es un script de Perl, y que "Perl" (el programa con el cual debe ejecutarse) esta en el directorio "/usr/bin", la secuencia "#!" es una secuencia que UNIX reconoce, no Perl. Un mtodo alterno, que funciona para otras plataformas, es: en lugar de colocar esa primera lnea ejecutamos: Perl nombre_del_script.pl de modo que directamente se ejecuta el interprete de Perl pasndole como primer parmetro el script a ejecutar (los dems parmetros se tomaran como parmetros al programa). Si se requiere deber sustituirse "Perl" por la ruta completa del programa y el nombre que el programa tenga. Para los ejemplos sucesivos, tomare la suposicin de que se trabaja en un sistema UNIX con el interprete de Perl en el directorio "/usr/bin". Y eso es toda la estructura bsica!. Programa Hola mundo: #!/usr/bin/perl print "Hola Mundo\n"; #Saluda #Programa Hola Mundo, Daniel Sol Llaven, 1996, como parte del tutorial de Perl

550

Este programa, se escribe como un archivo de texto comn, (al que recomiendo se le ponga extensin ".pl") y se cambian sus permisos para que pueda ser ejecutado (por lo regular con un "chmod 700 nombre_programa.pl" en sistemas UNIX), para ejecutarlo simplemente se invoca el nuevo script "nombre_programa.pl", hay que recordar que para el sistema particular en que se este trabajando es muy probable que deba modificarse "/usr/bin/" por otra ruta. As, la ejecucin en un sistema UNIX podra verse como: >chmod 700 Hola.pl >Hola.pl Hola Mundo >Hola.pl Hola Mundo > Ejecutando dos veces el script pretendo mostrar que no es necesario cambiar el modo de ejecucin del script sino solo una vez. Expliquemos las tres lneas que constituyen nuestro primer script: #!/usr/bin/perl Esta lnea, propiamente dicho, no es parte del script de Perl, sino una instruccin al SO para indicar que las siguientes lneas debern interpretarse por el programa "/usr/bin/Perl", si se omite, o no funciona en el sistema operativo, podemos ejecutar el programa de la siguiente forma: >/usr/bin/Perl Hola.pl Hola Mundo >print "Hola Mundo\n"; #Saluda Esta es la nica instruccin del programa y esta acompaada por un comentario, la instruccin es: print "Hola Mundo\n"; que pudo ser escrita como: print("Hola Mundo\n"); sin que esto implicara el mnimo cambio en funcionalidad. a continuacin tiene el comentario: #Saluda La sintaxis de C para comentarios no funciona en Perl, pues se usa a "/" para expresiones regulares. #Programa Hola Mundo, Daniel Sol Llaven, 1996, como parte del tutorial de Perl Esta lnea es otro comentario, el SO y Perl consideran todo lo que este en una lnea que inicie con "#" como comentario. (excepto en el caso de que sea una primera lnea, el SO lo interpreta como la indicacin del interprete, pero Perl lo ignora como a cualquier otro comentario).

551

2.2- Estructuras de Datos Bsicas


En nuestro programa Hola.pl no utilizamos ninguna variable, por lo que para explicar estos conceptos necesitaremos un nuevo programa, HolaP.pl que es una versin personalizada de Hola.pl. #!/usr/bin/perl #Programa Hola Mundo personalizado, Daniel Sol Llaven 1996, Tutorial Perl. print "Hola ?como te llamas?:"; $nombre=<STDIN>; chop($nombre); print "$nombre!, bonito nombre, cuantos aos tienes?:"; $edad=<STDIN>; print "sabias que te faltan ".(100-$edad)." para tener 100?\nAdios!\n"; Su ejecucin genera resultados similares a los siguientes: >HolaP.pl Hola ?como te llamas?:Daniel Daniel!, bonito nombre, cuantos aos tienes?:22 sabias que te faltan 78 para tener 100? Adis! > En este programa, aparecen muchos elementos novedosos que iremos revisando poco a poco a lo largo de este documento, por el momento, revisemos que hace lnea por lnea: #!/usr/bin/perl #Programa Hola Mundo personalizado, Daniel Sol Llaven 1996, Tutorial Perl. Cabecera normal del programa, incluido comentario indispensable. print "Hola ?como te llamas?:"; Esta lnea escribe "Hola ?como te llamas?:" pero ntese que no escribe una vuelta de carro al final (omite el "\n"). $nombre=<STDIN>; Aqu asignamos a $nombre un rengln escrito por la entrada estndar del programa. Perl define al "archivo" STDIN como su entrada estndar, y el operador <> que indica la lectura de una lnea de un archivo de texto, de modo que <STDIN> indica la lectura de un rengln de la entrada estndar (al menos para el ejemplo!), este tema se tratara con cuidado en la seccin 2.5 y 3.3 de este documento que tratan de operaciones con archivos. Para fines prcticos, usaremos <STDIN> como la expresin que lee una lnea de entrada del usuario desde teclado. chop($nombre);

552

Perl, al leer la lnea escrita por el usuario, tambin toma el enter que el usuario da, de modo que en el ejemplo, la entrada que di en vez de ser "Daniel" se puede escribir como "Daniel\n", (siendo "\n" el carcter de vuelta de carro), la funcin "chop()" tiene la funcin de eliminar el ultimo carcter a nuestra cadena, de modo que despus de esta instruccin $nombre solo tiene "Daniel". Por las caractersticas de la lectura de renglones de archivos, chop() es un elemento casi constante en programas de Perl. (Prubalo!, revisa que sucede si comentas esta lnea, veras que al imprimir el nombre imprime tambin el retorno de carro avanzando una lnea hacia abajo). print "$nombre!, bonito nombre, cuantos aos tienes?:"; Esta lnea nos muestra como se imprime el contenido de nuestras variables de forma sencilla; notamos que en la cadena a imprimir se encuentra $nombre, el compilador lo interpreta y reemplaza por su valor, (para imprimir "$" se pone "\$"), el resto de la cadena es completamente convencional. $edad=<STDIN>; Esta lnea, lee otra lnea de TEXTO y la coloca en la variable $edad (con todo y retorno de carro). print "sabias que te faltan ".(100-$edad)." para tener 100?\nAdios!\n"; Esta lnea imprime un mensaje que esta dividido en tres partes, la primera y la ultima son cadenas convencionales, que estn concatenadas (operador .) con una operacin aritmtica. Hay que notar que edad era un rengln de la entrada estndar, y no un numero, sin embargo no hay conflicto de tipo al operarlo con un entero, porque? porque los dos, para Perl, son del mismo tipo, son escalares, despus discutiremos que son los escalares y que mecanismo de conversin se utiliza para extraer el entero del texto contenido en $edad. (Experimenta!, prueba que pasa si los valores que se dan a edad no son enteros, que pasa si son reales o cadenas!) Despus de nuestro primer encuentro con las variables de Perl, el sentimiento mas probable es el de perplejidad, como maneja sus variables?, a que me refera conque enteros, reales y cadenas son del mismo tipo?, y si es as, como sern los dems tipos?. En realidad, las variables de Perl pretenden simplificarnos la vida, pero debemos comprender como pretenden facilitar las cosas para no terminar peleando contra ellas. 2.2.1- Clases de Datos Perl reconoce tres clasificaciones bsicas de datos, y dos especiales, por claridad, llamaremos clases a estas diversas clasificaciones, y tipos a las formas usuales de datos. Las diversas clases se distinguen entre si por el smbolo que antecede al nombre de la variable (por ejemplo $nombre es una variable de tipo escalar que se llama "nombre"), debe notarse que no hay relacin entre variables del mismo nombre si son de clases distintas. A continuacin, pongo una tabla de las clases de datos y los tipos que contienen: Clase Smbolo Tipos

553

Escalar Arreglo Hash Archivo Type Glob

$ @ % (ninguno) *

Entero, Real, Cadena, Referencia* Arreglo de escalares Arreglo Asociativo de escalares Identificador de Archivo Cualquiera**

* Las referencias son exclusivas de Perl 5, son el equivalente a apuntadores. ** Las variables tipo Glob se usaban como sustituto de las referencias en Perl 4, son obsoletas para Perl 5 y en lo personal no recomiendo su uso sino en casos muy particulares. 2.2.1.1- Escalares Nota: Algunas caractersticas de las conversiones entre tipos son exclusivas de Perl 5, pero la sintaxis y caractersticas generales son las mismas para ambas versiones. Por lo que en general, el tratamiento expuesto es valido para las dos. El escalar, es la clase de datos que engloba los tipos convencionales de datos, de modo que podemos decir: $v1="Daniel Sol Llaven"; $v1=100; $v1=89.12; Sin que esto implique ningn cambio en la naturaleza de $v1, en todo momento es un escalar. Aun cuando la compatibilidad de datos enteros y reales es fcil de imaginar, la conversin implcita en el caso de las cadenas no lo es, sin embargo la regla es bastante simple. por ejemplo: $v1="123y321"; crea un escalar $v1, que contiene la cadena "123y312", pero que pasa si le deseamos sumar 4?. Perl, interpreta $v1 como entero (o punto flotante) para esto, toma todos los caracteres del inicio que forman un numero correcto y ese numero es el que interpreta; de modo que: print $v1+1; imprimir: 124 Del mismo modo, como ejemplo: $v2="123.123.123"+1

554

da el valor 124.123 a la variable $v1 (punto flotante). Otro punto importante de las variables en general es que, aunque en ningn momento se declaran como de un tipo o de otro, si se pueden "destruir", o revisar que hayan recibido algn valor en la ejecucin del programa, esto se logra mediante las funciones defined() y undef(). defined nos indica si la variable que le pasamos como argumento ha sido definida (es decir, existe en el programa) o no. undef toma una variable y la "elimina" de modo que ya no tiene valor y defined la reporta como no utilizada. Los valores lgicos de verdadero y falso se manejan de modo similar al de C, cualquier valor escalar no nulo o cero se considera verdadero. Debe tenerse cuidado con las cadenas "0" pues si se evalan como nmero resultaran en falso aun cuando no son cadenas nulas. Los escalares son los constituyentes de las dems estructuras de datos, por lo que al explicar los arreglos, hashes, referencias y archivos haremos muchas referencias a ellos. 2.2.1.2- Arreglos Los arreglos en Perl son simplemente, arreglos dinmicos de escalares, es decir, se pueden usar cualesquiera elementos en el arreglo y Perl se encargar de hacer al arreglo de la dimensin adecuada. La definicin de un arreglo con valores constantes es: @a1=("hola",123,43.2,"adios"); Esta definicin, crea el arreglo @a1 con cuatro elementos, dos cadenas, un entero y un real, en realidad, cuatro escalares, para hacer referencia a los elementos escalares de un arreglo se usan los corchetes [] indicando el ndice del elemento (de cero al numero de elementos menos uno) de modo que: print "$a1[0]\n$a1[1]\n$a1[2]\n$a3[3]\n"; resulta para el arreglo anterior: hola 123 43.2 adis Ntese que el elemento de un arreglo ya no es un arreglo, sino un escalar, y debe especificarse como tal, con $ en vez de @. No es lo mismo el escalar a1 que un elemento del arreglo a1. $a1 Es un elemento distinto que $a1[] Por claridad, recomiendo que no se dupliquen nombres de arreglos (o cualquier otra cosa) con escalares, aunque Perl da un manejo independiente a las diversas entidades, de modo que si se hace no habr quejas de parte de Perl. Es importante sealar que los arreglos y hashes no pueden ser elementos de un arreglo, si se intenta esto, los arreglos o hashes sern "aplanados" a elementos que se agregan al arreglo. Por ejemplo:

555

(a,b,(c,d),e)==(a,b,c,d,e) Es decir, el arreglo constante a,b, arregl con c,d , e es equivalente al arreglo que contiene a,b,c,d,e. Pues al formar el primer arreglo, el sub-arreglo c,d es "aplanado" a elementos que se agregan al arreglo principal. Algo similar sucede a los hashes. Para hacer arreglos de arreglos o arreglos como elementos de hashes, se utilizan las referencias. 2.2.1.3- Hash o Arreglos Asociativos El Hash, o arreglo asociativo es una ampliacin del arreglo, en la que en vez de ubicar los elementos por posicin se les ubica por una "llave" es pues un arreglo de parejas ordenadas que se accesa por el primer elemento, el smbolo de los hashes es %, y la forma de declarar un hash con constantes es muy similar a la forma para declarar un arreglo: %h1=("ll1",1,"ll2",2,"ll3",3); Esta declaracin crea un hash con tres parejas ordenadas, las llaves son "ll1", "ll2", "ll3", para usarlo se pueden usar expresiones del tipo: $ndx="ll3"; print "$h1{ll1}\n$h1{"ll2"}\n$h1{$ndx}\n"; resultando: 1 2 3 Al igual que en los arreglos, cada elemento de un hash es un escalar, de modo que debe anteponerse $ en vez de % (pues no estamos haciendo referencia a todo el hash, sino a un elemento escalar del hash), pero a diferencia del los arreglos, en vez de usar [] para indicar el ndice se usan las llaves {}. Dentro de las llaves, en el ejemplo, usamos las tres formas principales de dar el ndice: ll1 - En esta forma Perl adivina correctamente que ll1 es una cadena y que esta es la llave. "ll2" - En esta forma decimos explcitamente el valor de la llave deseada. $ndx- como $ndx="ll3" Perl puede determinar el valor de la llave correcta. Para ndices, al igual que para hashes, tambin puede usarse el valor de variables o de expresiones para especificar el ndice. 2.2.1.4- Equivalencias de Clases Como ya revisamos, al hacer referencia a un elemento particular de un arreglo o hash obtenemos un escalar (en ves de todo el arreglo o hash). Este tipo de "cambios" de clase son el propsito de esta seccin, pues pueden resultar algo confusos aunque, una vez comprendidos, dan una buena parte del sabor peculiar de Perl.

556

Bsicamente, Perl distingue dos tipos de contextos para evaluar una expresin: como escalar y como arreglo. El primero se refiere a las expresiones que han de regresar un escalar (del tipo que sea) como resultado, y el segundo a aquellas expresiones que han de regresar un arreglo o conjunto de escalares como resultado. Muchas expresiones pueden ser evaluadas en ambos contextos y obtener, segn el contexto, un resultado distinto, esto lo revisaremos con cuidado conforme vayamos revisando estas expresiones. Revisemos los cambios que ocurren a los datos de una cierta clase cuando los evaluamos en otro contexto: Contexto Clase escalar escalar escalar arreglo arreglo hash

escalar El valor Se convierte en Vaco, no definido original el nico elemento del arreglo arreglo Numero de El arreglo de Los elementos pares elementos valores (0,2,4,...) forman (usualmente) originales las llaves y los nones los datos del nuevo hash. cociente que arreglo con las El hash original representa la parejas eficiencia del llave1,valor1,lla hash ve2,valor2,...

arreglo

hash

Las transiciones mas utilizadas son las de arreglo a escalar, las de arreglo a hash y de hash a arreglo, la primera porque permite conocer el tamao de los arreglos y las segundas porque proveen los mecanismos para inicializar los hashes con arreglos constantes y para "aplanarlos" con fines de almacenamiento e impresin. A menudo, se representa un arreglo con una sola cadena que contiene separadores para los diversos elementos, Perl implementa la funcin "split" que divide estas cadenas en arreglos, su sintaxis bsica es: split($delim,$cadena) donde $delim es la expresin que representa los delimitadores de elementos y $cadena es la cadena a dividir. Como ya se mencion, generar un arreglo de cadenas donde los elementos son las subcadenas de $cadena que estn separadas por cadenas $delim. por ejemplo, una cadena como:

557

$registro="Daniel:22:daniel@simba"; @reg=split(":",$registro); print "Nombre:$reg[0]\nEdad:$reg[1]\nEmail:$reg[2]\n"; genera un resultado similar a: Nombre:Daniel Edad:22 Email:daniel@simba Cuidado! no todas las funciones de Perl convierten de igual forma los arreglos en escalares, por lo que debe provarse o investigar primero que efectivamente en el contexto en que hagamos la conversin el resultado sea el deseado. Ejemplos: Arreglo constante @arreglo=(1,2,"hola",3,"adios"); inicializa el arreglo @arreglo con los elementos 1,2,"hola",3,"adios", (todos escalares). la notacin de los elementos entre parntesis define un arreglo constante, el equivalente a un numero o cadena constante cuyo valor asignramos a una variable pero en el contexto de los arreglos. "Hash" constante %hash=("llave1","dato1","llave2","dato2); #arreglo constante a hash

Inicializa el %hash con las llaves "llave1" y "llave2" ponindole como contenidos "dato1" y "dato2" respectivamente. De hecho, no especificamos un "hash constante" como en el caso del arreglo, sino que especificamos un arreglo constante el cual pasa por una transformacin de clase para asignarse a un hash, de modo que la expresin es equivalente a: @arreglo=("llave1","dato1","llave2","dato2); #arreglo constante a arreglo %hash=@arreglo; #arreglo a hash Cardinalidad de un arreglo @arreglo=(1,2,3,4); $elementos=@arreglo; En este caso $elemento recibe el valor 4, pues son 4 los elementos del arreglo, ntese que el ndice mximo en el arreglo es de solo tres, pues el primer elemento es el elemento 0 y Perl 4 regresa, con esta misma expresin el ndice mximo en lugar del nmero de elementos como Perl 5. Asignacin a arreglo constante ($a,$b,$c)=(1,2,3); Esta expresin es equivalente a:

558

$a=1; $b=2; $c=3; Pero debe tenerse cuidado, el siguiente cdigo: ($a,$b,$c)=(1,2,3); @a=($a,$b,$c); $a=7; $b=8; $c=9; Da al arreglo @a el valor del arreglo constante (1,2,3), y no, como podra pensarse. (7,8,9). Cuando se genera un arreglo constante se evalan los elementos que lo forman como constantes escalares y se le asigna despus, para obtener resultados diferentes se deber usar un arreglo de referencias. Arreglos con arreglos @a=(1,2,3); @b=(5,6,7); @c=(@a,4,@b,8); Estas expresiones generan tres arreglos, (1,2,3), (5,6,7) y (1,2,3,4,5,6,7,8), y no, como podra pensarse un arreglo de arreglos, cuando incluimos un arreglo dentro de otro, Perl "aplana" el arreglo insertado como si insertara uno a uno todos sus elementos en la posicin indicada del arreglo que ha de contenerlos, para hacer arreglos de arreglos deberemos usar referencias a estos. 2.2.3- Tipos Especiales de Datos Llamo a estos "tipos especiales" porque nos permiten hacer cosas inposibles con otros tipos, por ejemplo, el evitar ser "aplanado" en los arreglos. Adems, incluyo en esta seccin a los Archivos, pues aunque su sintaxis se parece a las de las variables, su funcionalidad es bien distinta. Sin embargo, por tratarse de solo una introduccin omito discutir sobre los "type globs" que son la aproximacin a las referencias que perl 4 implementa. 2.2.3.1- Referencias Nota: Este tipo de datos es exclusivo de Perl 5, Perl 4 usaba los type globs para realizar algunas de estas funciones, pero el proceso es mucho mas complicado y obscuro. Las referencias son el equivalente lgico de los apuntadores, son un tipo de dato que no contiene informacin en si misma, sino que permite manejar indirectamente los datos contenidos en otra entidad. Las referencias, sin embargo, no forman una clase nueva de datos, sino que son tratados como un tipo mas de escalar. La definicin de referencias se realiza por el operador referencia "\" (backslash) , funciona de modo similar a "&" en C, y aunque a diferencia de C no hay un operador de derreferencia la sintaxis para acceder al dato original es muy sencilla.

559

A diferencia de C, los objetos que creemos mediante referencias no quedan como cadveres en memoria, Perl lleva un registro de las diversas referencias a cada elemento en memoria y automticamente lo destruye cuando descubre que nadie hace ya referencia a l, de modo que pueden usarse las referencias sin miedo, aunque con la precaucin necesaria para no perder los datos que almacenamos en ellas. 2.2.3.1.1- Creacin Podemos crear referencias de varias formas, las que considero mas sencillas y tiles son: Usando el operador de referenciacin en una variable, o valor, en el caso de una variable, es crear un medio alterno de acceder al valor de la variable. $rescalar=\$escalar; $rarreglo=\@arreglo; $rhash=\%hash; $rrutina=\&subrutina; #la programacin de subrutinas la revisaremos mas adelante. $globref=\*ARCHIVO; #solo revisaremos los type globs para usarlos como referencias a archivos. Creando objetos "annimos" que solo pueden ser accesados por medio de la referencia. Escalares $rescalar=\"hola"; Arreglos: $rarreglo=[1,2,3]; Hashes $rhash={"llave1" => "dato1","llave2" => "dato2"}; Ntese que en esta representacin usamos adems el operador "=>" que indica una pareja de llave y dato, las que se separan por comas; esta notacin tambin es valida para hashes convencionales, pero la claridad que agrega es mejor utilizada al declarar hashes annimos. Como elementos de arreglos y/o hashes para formar estructuras de datos complejas, al ser escalares, son elementos del arreglo sin mas complicacin, de modo que los arreglos a los que hacen referencia se mantienen intctos. Por ejemplo, para formar un hash que contenga arreglos de nombres de letras: %rhletras={ "latinas" => ["a","b","c"], "gregas" => ["alfa","beta","gamma"]}; Esta sentencia forma un hash donde las llaves son cadenas y los datos son referencia a arreglos. Pueden convinarse las referencias a arreglos y a hashes annimos a voluntad para formar una estructura de datos tan compleja como se desee. # referencia al arreglo annimo (1,2,3) #referencia a la cadena annima "hola"

560

2.2.3.1.2- Uso De nada sirven las referencias si no podemos extraer y modificar los datos contenidos en los elementos sealados por la referencia, en Perl la forma para obtener el valor y para modificarlo es casi la misma, solo las abreviaturas de las referencias para obtener el valor funcionaran de modo distinto cuando tratemos de asignarles valor. Aun cuando Perl tiene varias formas de "derreferenciar" la informacin, solo discutir dos por considerarlas las mas sencillas, sin embargo, recomiendo una revisin al capitulo PERLREF de la referencia de Perl 5.X para una explicacin mas detallada y a consciencia del manejo de las referencias. Uso "simblico" de las referencias. Por regla general, podemos usar las referencias como si se sustituyeran antes de ejecutar el cdigo (aunque en realidad, no sea as), de modo que el cdigo: $nombre="entero"; $entero=5; $rentero=\$entero; $$nombre=6; $$rentero=7; efectivamente cambia el valor de $entero de 5 a 6 y despues de 6 a 7, del mismo modo podemos usar las referencias refirindonos a cualquier otro tipo. por ejemplo, para arreglos: $rarreglo=[1,2,3,4] $$rarreglo[2]="tres"; @$rarreglo=(); #crea arreglo annimo (1,2,3,4) #modifica el arreglo annimo a (1,2,"tres",4) #limpia el arreglo annimo

Uso con el operador de derreferencia. Como una forma de abreviar el proceso para referencias a arreglos y hashes, se aadi el operador -> que indica que el escalar es una referencia a hash o arreglo y espera el ndice despus. por ejemplo: $rarreglo->[5]="algo"; coloca "algo" como el 6o elemento del arreglo al que hace referencia $rarreglo. $rhash->{"alfa"}="uno"; coloca la pareja ("alfa" => "uno") en el hash al que hace referencia $rhash. Uso abreviado del operador de derreferencia. Se pueden obtener referencias a referencias, y se pueden hacer arreglos de referencias, de modo que los arreglos multidimencionales se pueden elaborar con la misma mecnica que en C.

561

$ra3d->[0]->[0]->[0]=1; #pone un "0" en la primera celda de un arreglo tridimencional Ntese que al crear perl los arreglos de las dimensiones adecuadas automticamente no hay problemas de invasin de memoria no reservada (aunque un uso descuidado puede ocupar cantidades demasiado grandes de memoria). Y esta aplicacin se considero lo bastante frecuente para implementar una abreviatura, de modo que la expresin anterior es equivalente a: $ra3d[0][0][0]=1; Lo cual le da mas claridad de lectura al cdigo (pero debe tenerse cuidado de no confundirlo con un elemento de un arreglo, $ra3d es una referencia a un arreglo de referencias y no un arreglo normal. Por claridad podramos usar: $ra3d->[0][0][0]=1; Ejemplo: A continuacin, coloco el listado de un programa que usa las estructuras de datos que mencionamos anteriormente usando su potencia lo mas posible sin que pierdan claridad, recomiendo que no solo se pruebe el programa, sino que se elabore una versin en la que se exploren los puntos que particularmente les resulten mas interesantes. 2.2.3.2- Archivos Uno de los usos mas probados y comunes de Perl es el procesamiento de archivos de texto para generar otros archivos (por lo regular de texto) que implican cierto proceso de los datos ledos originalmente. Adems, Perl no conoce mas entrada que la proveniente de archivos (asociando la entrada, salida y salida de errores a archivos "de ambiente") por lo que en cierta medida ya hemos revisado el uso bsico de archivos, solo que ahora le daremos la explicacin correspondiente a las entidades que vimos antes. Como en cualquier otro lenguaje, el ciclo de uso normal de un archivo es: Apertura Uso Cerrado Mediante "open" inicializa una variable de archivo Lectura secuencial por lneas Mediante close

Perl tiene implementacines para manejar archivos binarios y archivos aleatorios, pero el uso de archivos de texto secuenciales es lo mas comn en UNIX, por lo que considero a este manejo como bsico y le explico aqu. Bsicamente, se puede decir que hay tres tipos de archivos que se manejan de modo muy similar: Archivos comunes Programas de los que capturamos la entrada o la salida La entrada, salida o salida estndar de errores.

Estos tres tipos de archivos se manejan igual (con la limitacin de que algunos solo reciben lectura o escritura) y el momento en que se determina el tipo verdadero del archivo, es en el momento de

562

la apertura, por lo que ha dedicado una seccin exclusivamente a la apertura mientras que al uso y cierre los he concentrado en la seccin que siguiente a esta. 2.2.3.2.1- Apertura La entrada, salida y salida de errores estndar, estn abiertas por default, pero en cualquier otro sentido se utilizan igual que cualquier otro archivo, debe tenerse cuidado al planear los entubamientos de informacin para evitar que nuestro programa espere por siempre una entrada que no le ha de llegar, sin embargo no es necesario ser paranoico al respecto. Si tratamos de usarlas despus de cerrarlas el resultado de las lecturas ser nulo y los intentos de salida no tendrn efecto. Los archivos convencionales se abren con la instruccin "open", la cual es, por decir poco, muy interesante, en esta seccin solo revisaremos su uso bsico. open VARCHIVO,EXPRESION VARCHIVO es la variable mediante la que usaremos el archivo, no tiene indicador de clase y por convencin la manejamos en maysculas. EXPRESIN es, digamos, el nombre y modo en que habr de abrirse el archivo. Los archivos, se darn con los nombres nativos del S.O., y los modos se especifican con las siguientes cadenas al inicio del nombre (que imitan al shell). <archivo >archivo abre "archivo" para lectura abre "archivo" para escritura, borrndolo si existe.

>>archivo abre "archivo" para escritura, agregando la nueva informacin al final +<archivo abre "archivo" para lectura y escritura +>archivo abre "archivo" para lectura y escritura borrando "archivo" al abrirlo. |programa ejecuta "programa" y reasigna su entrada estndar a lo que escribamos en el programa| ejecuta "programa" y reasigna su salida estndar para lectura de nuestro programa

Debe notarse que a diferencia de los archivos no se puede ejecutar programas para lectura Y escritura. Es importante notar que esta forma de ejecutar programas desde perl es muy poderosa, pero no es la nica, por lo regular se usa "system" para ejecutar programas si la salida no nos importa o encerrando con las comillas ` al comando si la salida es sencilla (un rengln) lo que evala al comando como si fuese una expresin resultando en la salida del programa (las comillas ' funcionan de distinta forma entre Perl 4 y 5 al ejecutar el programa).

563

Tambin se pueden abrir la salida y entrada estndar mediante open, pero estas funciones as como la de cerrarlas puede tener consecuencias en la ejecucin del programa que son algo complicadas de explicar, por lo que recomiendo que de desear manejar estas operaciones se use la referencia de Perl. 2.2.3.2.2- Uso y Cerrado Bsicamente, se puede usar un archivo de dos formas, por renglones, (terminados por el carcter de vuelta de carro) o por carcter (byte). Revisare solo la primera opcin, porque el manejo por carcter tiene implicaciones sobre la naturaleza del sistema de explicacin muy larga y porque son mas usuales los archivos de texto en las aplicaciones de Perl. El operador que nos permite leer de un archivo es "<>" teniendo la variable de archivo dentro de el, as por ejemplo: $reng=<STDIN>; Que, como ya hemos mencionado; lee un rengln de la entrada estndar y lo coloca en la variable $reng, incluida la vuelta de carro que termine el rengln. Si el rengln termina con carcter de fin de archivo, este no se incluye en el rengln, y lecturas sucesivas de STDIN esperaran indefinidamente por una nueva entrada (as que debe tenerse cuidado al usar la entrada estndar o confiar en el usuario). STDIN es la variable de archivo que identifica la entrada estndar, as como STDOUT es la variable que identifica la salida estndar y STDERR es la que identifica la salida estndar de errores. @contenidos=<STDIN>; Esta vez, estamos evaluando al operador <> en un contexto de arreglo, y su comportamiento varia, como arreglo <> regresa un arreglo de cadenas, donde cada cadena es un rengln del archivo, de modo que la expresin anterior leer todo lo que se introduzca en la entrada estndar y lo coloca en el arreglo @contenido. Esta es una forma mas segura de usar la entrada estndar, pues ahora cada elemento del arreglo equivale a una lectura sucesiva de STDIN en contexto escalar. Si sustituimos STDIN por cualquier otra variable de archivo (o valor de variable de referencia a una variable de archivo) estaremos cargando el archivo ya sea rengln por rengln o todo completo a variables de memoria. Como caso especial, si evaluamos la expresin: <ARCHIVO>; cargaremos un rengln del archivo al que ARCHIVO hace referencia y lo coloca en la variable $_ de la cual hablaremos mas adelante. A continuacin, esta el listado de un programa sencillo realizado en perl que demuestra la potencialidad de las estructuras de datos y lo bsico del manejo de archivos, este programa esta hecho para perl 4, pero no creo que tenga problemas para correr bajo perl 5, sin embargo, debe

564

recordarse que la ruta hacia perl muy probablemente deber cambiarse o deber ejecutarse perl dndole el programa como parmetro. #!/usr/bin/perl # Programa hecho en perl4 que crea un archivo de parejas de nombre y # direccin IP tomando las direcciones de un archivo de la forma: # #131.178.80.32 #206.103.64.98 #ppp16-07.rns.tamu.edu #130.178.80.20 # # Etc, entiendase que los "#" no estn en el archivo de entrada que # tiene el nombre "salida" y que esta en el directorio activo. open(DIR,"<salida"); # Abre el archivo de entrada while($dir=<DIR>) # Lee todas sus lneas una a una { chop $dir; # les corta el fin de lnea open(NS,"nslookup $dir|"); # Invoca nslookup y recibe el resultado undef(@inf,$nombre,$inf); # Destruye datos viejos while($reng=<NS>) # revisa el resultado del nslookup { chop $reng; # quita fin de lnea $reng=~s/ //g; # quita espacios @inf=split(":",$reng); # Hace un arreglo con los resultados # que vienen como "Name: 132.248.100.100" por ejemplo. if($inf[0] eq "Name") {$nombre=$inf[1]}; #Toma el nombre del servidor if($inf[0] eq "Address") {$dir=$inf[1]}; #Toma su direccin IP } close NS; # Cierra los resultados y termina la ejecucin de nslookup if(defined($nombre)) # Si asigno valor a $nombre... { $hres{$dir}=$nombre; # Llena un Hash de la direccin y el nombre print "%\n"; # Indica el xito de la resolucin } else { print "#\n"; #Indica el fracaso de la resolucin } } close(DIR); #Cierra su archivo de entrada open(DIRS,">direcciones"); # Abre archivo de salida de resultados print "\nimprimiendo resutlados...\n"; foreach $dir (keys %hres) # Para todas las llaves de %hres { print DIRS "$dir $hres{$dir}\n"; #imprime la pareja llave-valor } close(DIRS); # cierra su archivo de salida. servidor.unam.mx" # "Address:

2.3- Operaciones Bsicas

565

Como se habr podido distinguir en los ejemplos vistos hasta ahora, Perl cuenta con una amplia y muy interesante gama de operadores, en esta seccin daremos una rpida revisin a los mas importantes, confiando en que el uso de la mayora de los operadores ser casi intuitivo. Para obtener mayor informacin sobre los operadores, la seccin PERLOP de la referencia es la mejor fuente de informacin detallada. Las variables, comillas, parntesis y funciones con parntesis tienen la precedencia mas alta en perl, de hecho, Perl los interpreta mas como operadores unarios que se comportan como funciones por la agrupacin de los parmetros sobre los que actan. Despus de estos, la precedencia de los operadores mas usuales es: Mas alta: Posicin no asocia derecha izquierda izquierda izquierda no asocia no asocia izquierda izquierda izquierda izquierda no asocia derecha derecha izquierda izquierda izquierda izquierda Operador ++ y -! ~ \ y la versin unaria de + y =~ !~ */%x +-. < > <= >= lt gt le ge == != <=> eq ne cmp & |^ && || .. ?: = += -= *= etc. , => not and or xor

Mas baja.

566

2.3.1- Operaciones Aritmticas Estas operaciones interpretaran al escalar como un numero (ya sea entero o real) y operaran aritmticamente sobre el, toda cadena que no inicie con una cifra ser interpretada como un cero por estos operadores. En orden de Precedencia tenemos: ++ y -- Son los operadores de auto incremento y auto decremento tan conocidos para los programadores de C. Su uso se puede resumir en la siguiente tabla: Posicin ++ --

Antepuesto Incrementa 1 antes de evaluar Decrementa 1 Antes de evaluar Pospuesto Incrementa 1 despus de evaluar evaluar Decrementa 1 despus de

As pues de las expresiones: $var=5; print $var++; print "\n$var\n"; obtenemos impresos los nmeros: 5 6 Debido a que en el primer print, incrementamos $var, pero al evaluar antes del incremento obtenemos su valor original, sin embargo, para evaluaciones posteriores su valor ha sido incrementado. ** Es el operador de Exponenciacion, as 5**2 resulta en 25 (5 al cuadrado) * Es el operador de Multiplicacin / Es el operador de Divisin % Es el operador de Modulo + Es el operador de Suma - Es el operador de Resta A estos no les dedicare mayores explicaciones por ser tpicos en todos los lenguajes de programacin y comunes a la matemtica cotidiana. Sin embargo, hay interesantes derivados de estos que vale la pena revisar. Estos son los operadores de automodificacin, estos se implementan al observar que en muchos programas se

567

requiere asignar a una variable el resultado de una operacin que involucra el antiguo valor de la misma variable. Estos operadores proporcionan una forma resumida de operar una variable con otra y asignar a la primera el resultado, por ejemplo: Si queremos hacer un $total, que sea el $total mas un $cargo particular podramos escribir: $total=$total+$cargo; Y en Perl (al igual que en C) tenemos la opcin de resumirlo como: $total+=$cargo; Ntese que el operador empleado fue "+=" que indica que al valor actual de la variable, se le asignara su propio valor mas el resultado de la evaluacin de la expresin a al derecha del operador. As como se puede formar el operador de asignacin con incremento, se pueden utilizar la mayora de los otros operadores susceptibles a actuar sobre una variable, la nica excepcin no intuitiva es el de sustitucin de patrones en la variable, que usa una sintaxis distinta "=~" que revisaremos al final de esta seccin. 2.3.2- Operaciones Lgicas Indispensables para las condiciones de todo ciclo de control son las operaciones lgicas en los lenguajes de programacin, y en este contexto me permitir incluir las funciones de comparacin. Las operaciones de comparacin entre valores se dividen en aritmticas y de cadenas, y hay que tener especial cuidado en utilizar la familia correcta so pena de obtener resultados no deseados. Operacin Mayor que Menor que Aritmtica > < gt lt ge le eq ne cmp Cadena

Mayor o igual >= Menor o igual <= Igual Diferente Compara == != <=>

La operacin "Compara" regresa un valor de -1,0 o 1 dependiendo si el valor a la izquierda es menor, igual o mayor (respectivamente) que el de la derecha del operador, ntese que si deseramos evaluar esto de forma lgica seria equivalente al operador "Diferente". Debemos recordar que Perl considera como verdaderos los valores no nulos (cadenas) y diferentes de cero.

568

Adems, tenemos los operadores lgicos que permiten cambiar el valor de verdad de una expresin: Operador Funcin ! ~ && || & | ^ .. Negacin Lgica Inverso aditivo (cambia el signo de un numero) Negacin Bit a bit de un valor Conjuncin lgica de los operandos Disyuncin lgica de los operandos Conjuncin bit a bit de los operandos Disyuncin bit a bit de los operandos Or exclusiva bit a bit Si evaluado como escalar regresa alternativamente verdadero y falso. Como arreglo genera un arreglo a partir de los limites de un rango. Negacin lgica de la expresin a su izquierda (muy baja precedencia) Conjuncin lgica de baja precedencia Disyuncin lgica de baja precedencia Disyuncin exclusiva lgica (precedencia mnima)

not and or xor

2.3.3- Operaciones con Cadenas As como los operadores aritmticos forzan a la variable a comportarse como de tipo entero o real, as los operadores de cadena forzan a los escalares a comportarse como cadenas, de modo que los nmeros son automticamente convertido en cadenas (son su precisin completa) al aplicarles estos operadores. Las operaciones a realizar con cadenas, por lo regular se manejan como funciones en vez de como operadores, con la excepcin de las siguientes: Operador Funcin =~ !~ Especifica la expresin a afectar por bsqueda de patrones regulares (ver seccin siguiente). Especifica la expresin a afectar por bsqueda de patrones regulares, pero evala negando el resultado lgico de la operacin.

569

. x

Concatenacin, todas las cadenas operadas por "." forman una sola cadena. Es el operador de repeticin, en contexto escalar toma la cadena de la izquierda y la repite el numero de veces que el numero de la derecha indica. En contexto de lista, toma la lista a su izquierda y la repite las veces que indica el operador a su derecha en una nueva lista.

++

Autoincremento, modifica el valor de los elementos de una cadena de modo que se incrementen sin cambiar su naturaleza, por ejemplo ++"zzz" -> "aaaa"; ++"99" -> "100" , ++"a9" -> "b0", etc.

2.3.4- Operaciones Regulares Este seccin se dedica a dar una rpida revisin a las operaciones de sustitucin y reconocimiento de patrones (definidos por lenguajes regulares) que son de las caractersticas mas utilizadas y tiles de Perl, aun cuando no sean tan sencillos de comprender en un primer acercamiento, recomiendo al lector que dedique algn tiempo extra al estudio de la construccin de las expresiones para describir los patrones a buscar por su cuenta, pues puede obtener grandes beneficios de este esfuerzo, la referencia es hasta donde se, la mejor fuente de informacin a este respecto, en las secciones de operadores PERLOP y de expresiones regulares PERLRE. Los operandos para utilizar las expresiones regulares son: Operado Funcin r =~ !~ Especifica que la cadena a la izquierda del operando es la que pasara por el proceso ya sea de sustitucin, o de bsqueda. Especifica que la cadena a la izquierda del operando ser la afectada, y hace que la expresin en su conjunto regrese el negado del valor lgico resultante. Delimita un patrn regular.

//

Por defecto, las operaciones de bsqueda de patrones y sustitucin actan sobre el parmetro default $_ , y al evaluarlas modifican su valor y regresan el numero de sustituciones u ocurrencias encontradas. La formacin y manejo de las expresiones regulares escapa a los alcances de este documento, as que solo mencionare como utilizar estos operadores para realizar substituciones y su derivacin para buscar algn patrn. Para realizar bsquedas, la sintaxis bsica es: s/PATRN/CADENA/g

570

Que hace la substitucin de todas las ocurrencias de PATRN en CADENA. En esta forma, actuara sobre el parmetro default $_ y regresara el numero de substituciones que haga o cero si no encuentra el patrn. As, para cambiar todas la ocurrencias de "Hola" por "Saludos" en una cadena llamada $cadena (en vez de en $_) deberemos escribir: $cadena=~s/Hola/Saludos/g; Por el contrario, si deseamos saber cuantas veces aparece "Hola" en cadena y colocar este valor en $cuenta deberemos escribir: $cuenta=( $cadena=~s/Hola/Hola/g ); Ntese que en realidad no realizo la operacin de bsqueda (que si existe) sino la substitucin por la misma cadena, para evitar introducir nuevas operaciones. Por ultimo, si deseamos determinar si no se encuentra la cadena, (pues para determinar si se encuentra bastara usar el valor de veces que se encontr la cadena como condicin lgica, pues evaluara como verdadero si es distinto de cero y falso si es cero) usaremos el operador !~ if($cadena!~s/Hola/Hola/g) { print "No se encontraron \"Hola\"s\n"; } Como ya se menciono, para un tratamiento serio de las cadenas regulares y los operadores para utilizarlas hay que consultar la referencia de perl en las secciones de PERLOP y PERLRE. 2.3.5- Operaciones Miscelneas A continuacin describo brevemente la funcin de otros operadores que resultan tiles en condiciones particulares. Operador Funcin ?: El operador condicional, es una forma de if resumida en una sola expresin, tiene tres operandos, op1?op2:op3 donde op1 es una expresin que evala falsa o verdadera, Si evala verdadera, se evaluara y la expresin regresara el valor de op2, si por el contrario, op1 evala falso, es op3 la que determina el valor final de toda la expresin. , Como el operador , de C, si se usa entre dos expresiones, evala la de la izquierda, desecha el valor obtenido y evala la expresin de la derecha regresando el valor as obtenido. Este operador funciona primordialmente igual que la coma, pero es exclusivo de Perl 5, forzando en algunas versiones que el operando de la izquierda sea una cadena (recurdese su uso para declarar arreglos asociativos).

=>

571

Adems, tenemos los signos de puntuacin, que a diferencia de otros lenguajes son considerados operadores de Perl. Bsicamente, tenemos 4 tipos de smbolos que nos sirven para agrupar otros smbolos para darles una interpretacin especial, son: Smbolo Funcin '' "" `` // Especifica valores literales (cadenas), no hace substituciones. Especifica valores literales, haciendo substituciones. Ejecucin, ejecuta el comando contenido entre ` despus de hacer las substituciones indicadas. Suelen usarse para delimitar expresiones regulares, hace substituciones con la operacin s//.

Entiendase por substitucin el reemplazar el nombre de variables por los valores de estas, as como los caracteres de escape; por ejemplo: $saludo="echo hola"; print '$saludo'."\n"; print "$saludo"."\n"; print `$saludo`."\n"; print "adios\n"; Genera como resultados algo similar a: $saludo echo hola hola adis Como se puede ver , en todos los casos imprime un fin de lnea "\n" despus de cada cadena (o expresin), la primera, es, literalmente la cadena dada; la segunda, es la cadena donde se ha substituido el nombre de la variable $saludo por su valor; la tercera, es el resultado de la ejecucin del comando especificado en la cadena, donde se substituy el nombre de una variable por su valor. Perl 4 no regresa valor cuando utilizamos las comillas para ejecutar un programa o comando, es decir, lo ejecuta sin capturar su salida, de modo que esta ir a la misma salida estandar del script que lo invoc. Alguna experimentacin con el uso de estos operadores es altamente recomendable, pues las convenciones de cada programa para entregar sus resultados a algn dispositivo o a alguna salida distinta de la standard pueden ocasionar resultados poco previsibles.

2.4- Estructuras de Control


Las estructuras de control bsicas de Perl y su sintaxis son:

572

IF: if (EXPRESIN) BLOQUE if (EXPRESIN) BLOQUE else BLOQUE if (EXPRESIN) BLOQUE elseif (EXPRESIN) BLOQUE ... else BLOQUE WHILE: WHILE (EXPRESIN) BLOQUE ETIQUETA WHILE (EXPRESIN) BLOQUE FOR for (EXPRESION;EXPRESION;EXPRESION) BLOQUE ETIQUETA for (EXPRESION;EXPRESION;EXPRESION) BLOQUE FOREACH foreach VARIABLE (LISTA) BLOQUE EXPRESIN es toda instruccin de PERL que evala en un solo valor, bsicamente se emplean como inicializaciones y condiciones de las estructuras. BLOQUE es un conjunto de expresiones encerradas en {}, ntese que todos los bloques, sin excepcin inician con { y terminan con }. ETIQUETA se refiere a etiquetas en el cdigo, estas son nombres seguidos de ":", que permiten identificar a algn bloque en particular. Para modificar la ejecucin de los ciclos tenemos las siguientes instrucciones: next Esta interrumpe el flujo del ciclo iniciando de inmediato ETIQUETA la siguiente iteracin. Si no se especifica la ETIQUETA afecta al ciclo mas interno. last Esta interrumpe el flujo del ciclo y pasa el control a la ETIQUETA primera instruccin despus del bloque del ciclo. redo Esta reinicia la ejecucin del ciclo, sin reevaluar la condicin.

Los programas a lo largo de este documento son ejemplos del uso de estas estructuras de control, por lo que no pongo ningn ejemplo especifico en esta seccin. 2.4.1- Manejo de subrutinas Como en cualquier lenguaje estructurado, Perl tiene la capacidad de generar nuevas funciones o procedimientos, pero perl tiene varias peculiaridades en la forma de definir sus funciones,Ademas de ser estas las que sufrieron mas cambios entre la versin 4 y 5. Caractersticas de las funciones en la versin 4 de Perl Para su ejecucin, se les tiene que sealar explcitamente, anteponiendo el smbolo de & al nombre de la funcin (algo similar a la clase de una variable), las referencias a una funcin eran siempre a travs de typeglobs por lo que es incomodo e inusual usarlas. Reciben siempre un nico parmetro, el arreglo default @_ en el cual deban ir todos los parmetros que de desearan, sin posibilidad forzar el paso de ciertos parmetros a la funcin, para obtener los valores se suele hacer un $parametro=shift @_; por parmetro.

573

Caractersticas de las funciones en la versin 5.002 y superiores Debe notarse que en versiones anteriores de perl no todas estn implementadas, en particular, los prototipos de funcin no estn implementadas sino hasta la versin 5.002. Si un procedimiento o funcin ya ha sido declarado y se ejecuta como instruccin en una lnea puede omitirse el smbolo & al inicio de su nombre. Es mas, siendo que perl no requiere el uso de parntesis al inicio y final de una funcin, los parmetros pueden seguir al nombre de la funcin como para cualquier otra funcin nativa de Perl. La verdadera utilidad de declarar nuestras funciones es el reducir la cantidad y complejidad de la revisin debe hacer de los valores que recibe como parmetros (dejando a Perl una mayor parte del trabajo), por ejemplo, si construimos una funcin que forzosamente requiera una clase de valor como parmetro (por ejemplo, una referencia) podemos definirla de modo que solo acepte parmetros que cumplan con esta caracterstica, de modo que perl genere un error en caso de que traten de pasarle un dato de tipo distinto, en vez de que nosotros tengamos que verificar si el escalar recibido corresponde o no a una referencia. A continuacin revisaremos la definicin de las subrutinas y funciones, Perl no hace mayor diferencia entre estas, de hecho se manejan igual y usaremos los trminos indistintamente a lo lago de esta seccin. Sintaxis bsica: Declaraciones; especifican que habr una funcin de este nombre pero no especifican su cdigo, requieren que posteriormente se haga la definicin. sub NOMBRE; Con parmetros indeterminados (estilo Perl4) Especificando clase de parmetros, Perl5.002 o

sub NOMBRE(PROTOTIPO);

Definiciones Se especifica el cdigo de una funcin, y si se desea, tambin su nombre y prototipo (tipo y numero de parmetros). sub NOMBRE BLOQUE; Definicin de la funcin, parmetros indeterminados sub NOMBRE (PROTOTIPO) BLOQUE Definicin con parmetros

$subref=sub BLOQUE Especifica cdigo y regresa una referencia a el, en este caso $subref es una referencia a una funcin de parmetros indeterminados cuyo cdigo esta contenido en BLOQUE. No lo discutiremos a detalle por considerarlo una aplicacin especial de referencias y subrutinas.

Los elementos que empleo en esta definicin son: NOMBRE Es un identificador valido para perl (palabra formada de

574

nmeros, letras y bajo-guion con las mismas reglas que C, Pascal y dems. Es el nombre con el que se podr invocar la funcin, si se omite, en vez de definir o declarar la funcin se generara una referencia a una funcin annima. BLOQUE Es un bloque de cdigo, al igual que en los ciclos de control, siempre inician con "{" y terminan con "}", dentro, puede haber cualquier serie de sentencias de perl, y determina un espectro de validez para las variables que se declaren locales con my. PROTOTIPO Es una cadena que indica la clase y una caracterstica de cada parmetro que la funcin recibir.

Comencemos revisando como crear un procedimiento que no tome parmetros. sub sencillito { print "Yo soy un procedimiento sencillito\n"; } Este procedimiento se invocara con el identificador "sencillito" o "&sencillito" , ejecutara su cdigo y terminara sin regresar un valor. Una versin de este que ahora es funcin (pues regresa un escalar) ser: sub sencillo { print "Yo soy una funcin sencilla\n"; return "sencilla"; die("no me muero\n"); } Este procedimiento, regresa la cadena "sencillo" despus de imprimir el mensaje especificado en el print, ntese que despues del return no continua su ejecucin y que al igual que "sencillito" no pone atencin a los parmetros, que podran de hecho, pasrsele o no. return es la palabra reservada que hace que un procedimiento o funcin termine y regrese un valor, si se usa el return sin especificar valor o si se llega al final del cdigo sin encontrar return regresa un nulo. Si deseamos tomar parmetros, pero no aun hacer uso de los prototipos, debemos tomar en cuenta que todos los parmetros vienen empacados en @_ el arreglo default, y que para obtenerlos podemos usar las siguientes expresiones: ($primero,$segundo,$tercero)=@_; #Obtiene tres escalares del arreglo (ntese que si tercero fuese arreglo, absorbera el resto de los escalares en @_) $primero=shift @_; $segundo=shift @_; $tercero=shift @_; #Tambin obtiene tres parmetros de @_, pero este tambin los retira de @_, pues el operador shift toma el primer elemento de una lista y lo elimina, regresndolo como resultado de la expresin.

575

La ventaja del segundo mtodo es que se retiran de @_ los elementos que eran parmetros para la funcin que hacemos, de modo que @_ puede contener mas informacin til para algn otro propsito. Las referencias son de gran utilidad para manejar todo tipo de datos como parmetros a una funcin, de hecho, la nica posibilidad de pasar un arreglo a un procedimiento sin que el arreglo pierda su identidad requiere el uso de referencias, aunque como veremos, este requisito puede esconderse al invocar la funcin (no as al implementarla). De cualquier modo, si recibimos referencias como parmetros solo deberemos darles el manejo de escalares para extraerlas de @_ y usarlas como siempre. La manera de "ocultar" la necesidad de referencias en el momento de invocar la funcin implica el uso de prototipos. Los prototipos pueden ser de gran utilidad para evitar errores de programacin al invocar funciones con los parmetros equivocados, por lo que a pesar de no ser un tema bsico lo incluyo en este documento. Las cadenas del prototipo se componen de los siguientes smbolos: $ @ % \ ; Escalar Arreglo (toma todos los parmetros siguientes en el arreglo) Arreglo Asociativo (tambin absorbe todos los parmetros) El identificador del siguiente parmetro deber iniciar cn ($,@ o %). Los siguientes parmetros son opcionales

As por ejemplo, las siguientes definiciones especifican: sub biescalar ($$); #recive dos escalares sub escarreglo ($@); # toma el primer parmetro individual y todos los dems en un arreglo. sub tomahash (%); #los parmetros recibidos forman un hash sub dosotresesc ($$;$); #dos escalares obligatorios y un tercero opcional. sub arreglos (\@$\@); #un arreglo, un escalar y despus otro arreglo, sin que se mezclen. Debe notarse que si se utilizan los parmetros de clase arreglo y hash sin "\" estos sern el ultimo parmetro a recibir, pues tomaran el resto de los parmetros dentro de si, de modo que si se desea tomar mas de un arreglo o hash como parmetro se deber especificar la clase con "\". Ntese que el uso de "\" tiene como efecto colateral que se recibe una referencia al arreglo, hash o escalar, la que esta garantizada a ser una referencia al tipo especificado; ntese que no por eso el parmetro que acepta la funcin es una referencia, por el contrario, debe ser una variable de la clase especificada. Este mecanismo, nos permite controlar la clase de la variable que nos d el velor que recibimos como parmetro.

576

A continuacin, veremos un ejemplo sencillo del uso de prototipos y funciones en un programa que especifica una funcin que debe recibir dos escalares e imprime un saludo con ellos. #!/usr/bin/perl print "hola\!\n"; sub rutina ($$) { $nombre=shift @_; $direccion=shift @_; print "Hola $nombre\nVivies en $direccion\n"; return 345; } print "Dame tu nombre\n"; $res[0]=<STDIN>;chop $res[0]; print "Dame tu direccion\n"; $res[1]=<STDIN>; chop $res[1]; print rutina $res[0],$res[1] ."\n"; Revsese con cuidado este programa y experimntese con el, de modo que el siguiente programa (una modificacin del anterior que explota las caractersticas de prototipos y de referencias para recibir dos arreglos y un escalar como parmetro) pueda comprenderse con facilidad. #!/usr/bin/perl # Definicin de la subrutina "rutina" sub rutina(\@\$\@) { ($runo,$rsaluda,$rdos)=@_; #(@uno+0>@dos+0)?print "uno mayor que dos\n":print "dos mayor o igual a uno\n"; print "$$runo[0],\n"; print "$$rsaluda\n"; print "$$rdos[0],$$rdos[1],\n"; return 345; } #Programa principal #Primer nombre @res=("Daniel","Sol","Llaven"); #Segundo nombre @re=("David","Jonathan","Sol","Llaven"); $algo="hola"; print rutina(@res,$algo,@re) ."***\n"; Este segundo programa define una funcin con prototipos que utilizan extensivamente "\", de modo que no solo requiere que sus parmetros sean un arreglo, un escalar y un segundo arreglo, sino que estos deben ser variables de las clases @, $ y @ respectivamente (por ejemplo, no aceptara una cadena constante en vez de $algo en la ultima lnea). Observando el cdigo, vemos que los parmetros se reciben como referencias y se usan como tales, pero que en el programa principal se pasan los parmetros como variables de los tipos especificados en el prototipo (de hecho, "rutina" no aceptar ninguna otra cosa). Espero que este ejemplo ayude a entender los conceptos de prototipos y de subrutinas, en realidad, no suele hacerse hincapi en estos temas por no ser necesarios, dada la libertad que perl

577

nos da, pero en el contexto de desarrollos medianos a grandes pueden ser muy valiosos para evitar errores de difcil deteccin. En este capitulo, como en todos los dems, es indispensable que el lector haga programas de ejercicio de su propia inspiracin con el tiempo y la libertad de probar y experimentar los temas que revisamos aqu.

2.5- Operaciones con archivos


Al menos en la UNAM, Perl se utiliza principalmente con dos propsitos, el primordial es para implementar interfaces para servicios de red, y el segundo, es el proceso sistemtico de grandes archivos de texto (bitcoras de operacin, control de usuarios, etc.) debido a su facilidad para el manejo de archivos y si flexibilidad en el manejo de datos capturados de textos (formato muy comn de la informacin en UNIX, que tiene la nica desventaja de hacer crecer un poco mas los archivos, con las ventajas de ser interpretables con herramientas mas variadas, e incluso a simple vista, con la posibilidad de comprimirse con gran eficiencia). Debo aclarar desde ahora, que el manejo de archivos binarios, no es uno de los puntos fuertes de perl, y que aunque puede hacerlo, no solo no es comn, sino que resulta mejor utilizar programas hechos en C para proveer la manipulacin de este tipo de archivos, es por esto, que este tema solo lo tocaremos brevemente, y nos enfocaremos al uso de archivos de texto. Las operaciones bsicas para trabajar con archivos son: open(VARCH,ARCHIVO); <VARCH>; close(VARCH); Que respectivamente abren, leen, y cierran el archivo. Donde: VARCH Es la variable que identificara al archivo, no se le pone identificador de clase y por convencin, se utiliza con maysculas. ARCHIVO Es el nombre del archivo a utilizar, precedido de smbolos que indican el modo de uso del archivo.

2.5.1- Apertura y cerrado Los smbolos con los que especificamos el modo de operacin de un archivo son: Smbolo Significado > < >> +< +> El archivo se abrir para escritura (borrndolo si existe) El archivo se abre para lectura El archivo se abrir para escritura agregando al final El archivo se abrir de lectura escritura El archivo se abre para lectura y escritura vaciandolo primero

578

El uso de archivos de lectura y escritura lo revisaremos mas adelante, pues involucra algunos procesos adicionales al manejo convencional. Los "archivos" que no requieren ni abrirse (ni cerrarse) son, como es de esperarse, la entrada estndar, la salida estndar y la salida de errores, sin embargo, si pueden ser cerrados y abiertos con open y close, para esto se utilizan como nombre de los archivos (sin mas smbolos): >Abre la salida estndar Abre la entrada estndar.

Si no se utiliza ningn smbolo sino solo el nombre del archivo a utilizar, este archivo se abre para lectura. La funcin open regresa al evaluarse el valor de la variable de archivo, o nulo si no se puede abrir el archivo en el modo deseado, de modo que si se utiliza como condicin lgica resultara verdadera en caso de lograr abrir el archivo. Algunos ejemplos de apertura de archivos son: open(ETCP,"</etc/passwd"); Apertura de lectura nicamente del /etc/passwd asociado a la variable de archivo ETCP open(SAL,">salida.txt"); Creacin del archivo "salida.txt" en el directorio desde donde se invoque el programa, solo de escritura y asociado a la variable SAL. El cerrado de los archivos en una operacin recomendable, pero no necesaria, en el caso de los archivos, garantiza que todos los buffers han sido vaciados (o regresa falso en caso de que haya algn error). 2.5.2- Lectura de datos La lectura de la informacin contenida en los archivos, como ya habamos mencionado antes, se realiza con la operacin <>, en esta ocasin la veremos mas a detalle. El operador <> realiza la lectura de texto de el archivo, cuya variable se coloca entre < y >. Ah es evaluado en un contexto de escalar, regresa un rengln, incluyendo el carcter de fin de lnea. Si se evala en contexto de lista, regresa un arreglo de todos los renglones del archivo incluyendo sus terminadores de lnea. A manera de ejemplos:

579

$reng=<ENTRADA>; Carga una lnea del archivo asociado a ENTRADA (que debe ser de lectura). @contenido=<ENTRADA>; Toma todas las lneas del archivo y las coloca como elementos consecutivos del arreglo @contenido. Debe mencionarse, que cuando se evala el operador <> para un archivo, el puntero de posicin en el archivo va avanzando, de modo que en aplicaciones sucesivas (de contexto escalar) recibiremos renglones sucesivos. Y si se aplica en contexto de arreglo, una evaluacin sucesiva no regresara mas renglones. Adems, el operador <> tiene la propiedad de tomar valor por defecto cuando no se especifica que archivo debe usar, esta caracterstica tiene dos modalidades: Usar el ultimo archivo abierto: El operador <> continua trabajando con el ultimo archivo al que reavisamos, por ejemplo: open(ENT,"<entrada.txt"); while(<>) { print; } #trabaja con el archivo entrada.txt #para toda lnea del ultimo archivo abierto # se imprime.

En este programa utilizamos varios parmetros default de las funciones: en el while(<>) vemos que el operador <> no tiene ningn identificador de archivo, y sin embargo funciona como si se hubiese puesto <ENT> en vez de <>, esto es debido a que <> toma por default al ultimo archivo abierto como archivo default. En segundo lugar, vemos que ni asignamos el valor que regrese <> a ninguna variable ni especificamos que variable debemos imprimir en el print. En ambos casos se esta utilizando el parmetro default $_, <> por defecto asigna el valor que resulte a $_ y print por defecto imprime a la salida estndar el valor de $_. El otro modo de obtener los valores de archivos por defecto a trabajar con <>; supongamos un programa de nombre imprime.pl con el siguiente cdigo: while(<>) { print; } #para toda lnea del archivo default #imprimirla

Aparentemente este programa carece de funcin pues no especifica ni abre ningn archivo para leer, el modo de especificar que archivo debe usarse es por medio de parmetros, de modo que el programa debe ejecutarse como: imprime.pl entrada.txt

580

Para que imprima todas las lneas del archivo entrada.txt. En resumen, si no se especifica cual archivo deber de usarse para leer informacin con la operacin <> Perl tratara de utilizar: 1. El ultimo archivo abierto por open 2. El siguiente parmetro como nombre de archivo a abrir y procesar como de lectura. Ntese que si al segundo programa, imprime.pl, se le da mas de un parmetro, recorrer todos los parmetros como nombres de archivos a abrir para usar con <>. Esto provee un mtodo muy breve para hacer programas que procesen los archivos cuyos nombres se pasen como parmetros. Deba tenerse cuidado al emplear las variables de default en un programa, y no recomiendo que se usen en programas grandes o de lgica compleja, pues estos valores son fcilmente afectables por otras operaciones y se requiere un conocimiento muy profundo de perl para conocer todas las peculiaridades del lenguaje en cuanto a estas variables especiales. As pues, recomiendo usarlas solo para hacer mas simples los programas que ya sean sencillos por si mismos. 2.5.3- Escritura Una vez que hemos abierto un archivo para escritura, la forma mas comn de escribir en el, es mediante la funcin print, de hecho, print esta diseada para escribir a archivos, pero el archivo que utiliza por defecto para escribir es el de la salida estndar STDOUT, la sintaxis para especificar que archivo debe utilizarse es: print ARCHIVO LISTA donde: ARCHIVO es la variable de tipo archivo donde se dirigir la salida LISTA es una lista con los valores a imprimir, (si solo es un escalar se interpreta como una lista de un solo elemento, pero evita que al imprimir listas se obtengan los nmeros de su cardinalidad en Perl 5). Si se desea emplear las variables de archivo guardadas en arreglos, hashes o como resultado de expresiones se les deber poner en un bloque de cdigo (entre {}) para mas informacin consultar la funcin print en la referencia de Perl (seccin PERLFUNC). As pues, para crear y llenar un archivo con los primeros 100 nmeros enteros: open(AS,">enteros.100"); for($c1=0;$c1<100;$c1++) { print AS "$c1\n"; } Ntese que si se usan parntesis alrededor de los parmetros del print muy probablemente Perl pensara que toda la expresin entre parntesis especifica el valor a imprimir resultando en un error, por lo que recomiendo se evite el uso de parntesis cuando se usa print para escribir a archivos.

# de 0 a 99 (perdn si 0 no es entero) #escribe al archivo de AS el numero.

#contenido en $c1 y fin de lnea.

581

2.5.4- Saltos y proceso binario Antes que nada debo indicar que las funciones que revisaremos aqu las incluyo, no por considerarlas bsicas para la programacin, sino por su gran utilidad, y para que se conozca el modo en que perl puede lidiar con archivos binarios. Sin embargo, si se pretende desarrollar algo usando estas herramientas recomiendo ampliamente una saludable dosis de experimentacin y prototipaje para evitar demoras posteriores, adems, ser indispensable tener la referencia a su lado. As que sintase el lector con la libertad de saltar esta seccin y continuar con la 2.6, que le resultar mas til al corto y mediano plazo. Comencemos por la forma en que perl 5 (todo lo que tratare en esta seccin no funciona o funciona de modo muy distinto en perl 4) puede leer, escribir y manejar informacin binaria, que llamaremos empaquetada: Perl maneja la informacin en tipos abstractos, que nos ahorran gran trabajo de programacin al momento de usarlos, desafortunadamente, esto no ayuda en el momento de querer leer tipos de datos que vienen como una serie de bits dentro de bytes. La solucin que perl da a este problema es mas o menos sencilla, pero requiere algn entendimiento de ,que es una cadena. Una cadena es una serie de caracteres, como el lector debe ya saberlo, pero cada carcter es en s mismo representado por un byte; as que si no somos muy exigentes con los caracteres que pueden o no ser incluidos en una cadena, podramos decir que cualquier serie de valores representados en bytes pueden interpretarse como una cadena. Pues bien, este es el concepto que Perl explota. Un empacado de informacin lo manejaremos como un escalar que para fines prcticos es de tipo cadena. Pero que en vez de generar con el operador "." y contener texto comn y corriente, contiene una serie de caracteres (que no necesariamente producirn resutlados coherentes si tratamos de imprimirlos) que en si mismos contienen informacin que nos interesa recuperar. El proceso de desempacado de la informacin consiste en indicar a perl que a partir de una cierta cadena empacada debemos extraer un arreglo de valores a partir de un formato que deberemos especificar. La contraparte de este proceso es el generar nuestra cadena empacada a partir de un arreglo de valores y la misma informacin de formato que pretendemos leer despus. Bajo este enfoque, Perl puede manejar perfectamente bien un archivo basado en registros, pero tendra serias dificultades para manejar un archivo que no codifique toda su informacin en un patrn fijo, para esto ser muy importante la capacidad de brincar en la posicin del archivo. La sintaxis de las funciones que empaquetan y desempaquetan la informacin son las siguientes: pack($FORMATO,@VALORES); Esta funcin regresa la cadena empacada con el formato descrito por la cadena $FORMATO que contiene los valores presentes en el arreglo @VALORES. unpack($FORMATO,EXPRESION);

582

Esta es la contraparte de pack, usando el formato especificado por $FORMATO extrae los valores de la cadena que resulte de EXPRESIN (probablemente la variable que contiene la variable) y regresa al evaluarse un arreglo de valores de los tipos especificados. Para especificar el formato en el que se empaquetara o con el cual desempaquetar la informacin se usa la siguiente sintaxis: Simbolo Significado A a b B h H c C s S i I l L f d x X @ Cadena ASCII rellenada con espacios a la derecha. Cadena ASCII rellenada de nulos a la derecha. Cadena de bits (Orden ascendente de bits) Cadena de bits (Orden descendente de bits) Cadena de nibbles (en hexadecimal con el nibble bajo primero) Cadena de nibbles (en hexadecimal con el nibble alto primero) Un valor de carcter con signo Un valor de carcter sin signo Un entero corto con signo Un entero corto sin signo Un entero con signo Un entero sin signo Un entero largo con signo Un entero largo sin signo Un punto flotante en formato nativo de Perl Un doble en formato nativo de Perl Un byte nulo Regresar un byte Llenar de nulos hasta la posicin absoluta

Despus de cada uno de estos se puede colocar un numero que indicara cuantos elementos de este tipo esperamos, a excepcin de los del primer bloque (cadenas) donde el numero indicara la longitud de la cadena a interpretar como un solo elemento. Todos estos especificadores funcionan tanto a la entrada como a la salida (e idealmente usamos la misma cadena de formato en el empaquetado y en el desempaquetado.

583

As por ejemplo, para leer de una cadena empaquetada los bits, suponiendo que estos estar ordenados del menos significativo (al principio) al mas significativo (al final), y que buscamos leer 32 bits ser: ($cadbit)=unpack("b32",$paquete); Ntese que al escribir ($cadbit) estamos asignando al escalar $cadbit el primer elemento del arreglo generado por unpack, es decir, el primer elemento generado. Por otro lado, para empaquetar y desempaquetar en una cadena 4 enteros podemos escribir: $paquete=pack("i4",(34,-43,127,512)); @enteros=unpack("i4",$paquete); Debe recordarse que con las cadenas el numero que se pone a continuacin del carcter (aAbBhH) indica la longitud de las cadenas, pero con los dems tipos indica cuantos elementos de ese tipo esperamos. Ahora, para leer byte por byte un archivo (los cuales podemos concatenar con los el acostumbrado operador ".") utilizamos la funcin getc getc ARCHIVO; Esta funcin regresa el siguiente carcter del archivo al que ARCHIVO hace referencia, o una cadena vaca (falso lgico) si llegamos al fin de archivo. Por ejemplo, hagamos un diminuto programa que nos muestre los valores hexadecimales y binarios de todos los bytes de un archivo: #!/usr/bin/perl #programa de prueba de las capacidades de desempaquetado #y proceso binario de archivos en Perl5 open(AE,"<$ARGV[0]"); $contador=0; print "offset\thexa\tbinario\t\tcaracter\n"; while($ch=getc(AE)) { $binario=unpack("B8",$ch); #deseamos el bit mas significativo a la izquierda $hexa=unpack("H2",$ch); #nibble mas significativo primero print "$contador\t$hexa\t$binario\t\"$ch\"\n"; #$ch es el carcter original $contador++; } close(AE); Este programa tambin intenta imprimir el carcter ledo con fines didcticos, si se emplea con archivos autnticamente binarios recomiendo que se elimine $ch de las variables a imprimir. La caracterstica comnmente utilizada del proceso de archivos binarios que resta por revisar es la lectura aleatoria de archivos; esto se logra cambiando arbitrariamente la posicin del apuntador de archivo mediante la funcin search. Esta funcin trabaja por bytes, de modo que es de muy poca utilidad cuando trabajamos con archivos de texto. La sintaxis de la funcin es:

584

search ARCHIVO,POSICION,MODO donde: ARCHIVO POSICIN MODO es la variable con la que hacemos referencia a un archivo Especifica el avance desde la posicin que indique el modo

1-Desde el inicio, 2-Desde la posicin actual, 3-Desde el final.

2.6- Operaciones con recursos del sistema


Una de las grandes ventajas de Perl sobre otros lenguajes es la facilidad con la que interacta con otros programas y con el sistema (UNIX en particular pero cualquiera en lo general). De hecho, una de las grandes mejoras del Perl5 sobre el Perl4 es que tiene un proceso mucho mejor coordinado de programas concurrentes invocados desde Perl, por lo que recomiendo ampliamente se considere el desarrollar programas que usen intensivamente el sistema con Perl5 (o superior). Por considerar esta solo una introduccin planteare solo las formas mas bsicas de interaccin con el sistema, recomiendo ampliamente, que para las tareas especificas para las que se suelen usar llamadas al sistema se consulte la referencia de Perl en la seccin PERLFUNC para averiguar si estn presentes y cual es la manera de usarlas. En esta seccin revisaremos tan solo los mtodos de ejecucin de programas comandos del sistema operativo que dividiremos en tres categoras: Como expresiones a evaluar: Ya lo hamos revisado en la seccin de operadores, pero conviene repetirlo en este contexto. Sin interrelacin: Invocacin de comandos y programas esperando su culminacin antes de terminar, o de modo que sean la ultima accin de nuestro programa. Entubados: Principalmente mediante el uso de open y close, nos permiten alimentar de informacin otros procesos o alimentar nuestro programa del resultado de ellos.

2.6.1 Como expresiones Como ya vimos, con el uso de las comillas `` podemos tratar un comando del S.O. como si fuese una expresin la cual nos regresa los resultados que dicho comando entrega a su salida estndar. algunos ejemplos de esto son: `date` evalua como "Thu Sep 12 18:49:47 CST 1996\n" suponiendo que esa fuese la fecha `wc -l Hola.pl` evalua como " 3 Hola.pl" suponiendo que Hola.pl tuviese tres lneas.

585

Es la forma mas sencilla de ejecutar comandos desde nuestro programa en Perl. Debe aclararse que Perl 4 solo ejecuta el comando dejando la salida estandar de este a la salida del script de Perl; de modo que no regresa ningun valor a nuestro script de Perl 4. 2.6.2 Sin interrelacin Por interrelacin entiendase que: Ya sea que la entrada estndar del proceso ejecutado sea tomada desde nuestro programa. O que nuestro programa tome la salida del comando, de modo que en estos mtodos la salida estndar del programa ejecutado ser la salida estndar que nuestro propio programa tiene (consola por lo regular) y lo mismo para la entrada estndar. Hay dos mtodos para invocar programas con estas caractersticas "system" y "exec", y la diferencia entre estos radica en el ambiente que el programa invocado ocupara. La sintaxis de estas es: system(PROGRAMA); Ejecuta PROGRAMA como un nuevo proceso, espera a que acabe y continua exec(PROGRAMA); Ejecuta PROGRAMA, y termina nuestro script siendo reemplazado por el PROGRAMA que ejecutamos. donde PROGRAMA es una cadena conde esta el comando (como lo daramos en S.O.) a ejecutar, puede ser un comando compuesto, por ejemplo: $comando="cat Hola.pl |grep daniel"; system($comando);#equivalente a system("cat Hola.pl |grep daniel"); efectivamente escribir en la salida estndar las lneas del script Hola.pl en las que aparezca la palabra "daniel". Cuando un programa se ejecuta, las variables de ambiente, el camino actual y otras condiciones del sistema operativo suelen ser determinantes para su desempeo, cuando utilizamos system, el programa a ser ejecutado se ejecuta en un nuevo ambiente (que no tiene mas relacin con el nuestro que gozar de los mismos privilegios), mientras que cuando lo ejecutamos con exec el programa de hecho substituye a nuestro script de perl en el ambiente, de modo que hereda todas las caractersticas que nuestro script haya determinado del ambiente. De este modo, una posible aplicacin de exec es cuando debemos forzar que el programa que ejecutamos tome variables de ambiente que colocamos con el script de perl, en cambio system es necesario cuando debemos ejecutar mas de un programa, o cuando debemos realizar mas acciones con nuestro script una vez que el programa invocado termine. 2.6.3 Entubados En esta seccin, veremos como podemos capturar la salida que los programas que ejecutemos generan y procesar esta como si fuese un archivo y por otra parte, como podemos asignar una variable de archivo a la entrada estndar de un programa que ejecutemos para que procese lo que le alimentemos.

586

Una nota importante: Cuando ejecutamos programas de este modo Perl no espera a que el programa termine para continuar, si deseamos que lo haga debemos usar close con la variable de archivo que les asociemos para indicar que el programa debe terminar (y perl efectivamente esperara a que lo haga). La ejecucin de comandos o programas asocindolos a archivos se hace aadiendo el carcter "|" al comando a ejecutar y utilizando el comando open. Por ejemplo, supongamos que deseamos procesar el resultado de un programa llamado "fuente". Si el comando para su ejecucin desde sistema operativo fuese: fuente saluda 3 deberemos "Abrirlo" de la siguiente forma: open(RESF,"fuente saluda 3|"); while(<RESF>) { #procesar los renglones que va poniendo en $_ } close(RESF); #espera a que termine y cierra el flujo de datos Ntese como se coloco el carcter "|" al final del comando usual. Y que RESF funciona como un archivo abierto para lectura. Tambin es conveniente recalcar que si deseamos ejecutar un comando que per se utilice entubamientos se puede hacer, por ejemplo: open(ALGO,"fuente hola 3|prcesa_hola |"); Asociara a ALGO lo que el programa procesa_hola genere en su salida estndar, mientras que el S.O. ha entubado la salida de "fuente" a la entrada de "procesa_hola". Del mismo modo, si deseamos poner informacin a la entrada estndar de un comando deberemos abrirlo colocando el smbolo "|" como primer carcter del comando. Y el archivo que asociemos se deber usar como una archivo abierto para escritura. De nuevo, cuando indiquemos el close, Perl enviara un EOF al archivo al que estamos ejecutando y esperara a que este le indique el termino de su proceso.

3- PROGRAMACIN ESPECIAL
En la Seccion 2, revisamos la programacin bsica en Perl, pero la programacin en este lenguaje se basa, sobretodo, en gran cantidad de optimizaciones y formas alternativas de realizar tareas comunes lo que le d una funcionalidad adicional que lo hace en extremo valioso en la realizacin de estas tareas o aquellas que las requieran. Por eso es que dedico toda una seccin de este tutorial a tratar estas caractersticas y el modo de emplearlas, para que el nuevo programador, no solo pueda elaborar programas en Perl, sino que pueda explotarlo para facilitar la realizacin de sus tareas mas usuales.

3.1- Uso de Perl en lnea, Tareas Comunes

587

Una de las caractersticas de Perl dada su semi naturaleza de interprete es que puede ser ejecutado y actuar como un interprete de comandos (aunque los ejecuta hasta el momento de terminar todas las instrucciones con un fin de archivo desde teclado. Adems, Perl tiene varias opciones en lnea que son de extraordinaria utilidad, por ejemplo, para substituir cadenas a lo largo de todo un archivo, o indicar que los errores se expresen en un formato mas entendible (en caso de que no este como default desde que lo compilamos), etc. Esta seccin, se dedica a revisar algunos de estos parmetros de Perl enfocado a usos especficos. 3.1.1- Formas de Especificar el programa Existen tres mtodos de indicar a Perl cual ha de ser el script a ejecutar: 1- Ejecutando un script directamente que especifique la colocacin de Perl con una lnea que inicie con "#!" 2- Pasando el script por la entrada estndar a Perl (completo con todo y fin de archivo), Pero esta opcin no admitir parmetros para el script. 3- Especificado las instrucciones en lnea de comando con el interruptor -e. Esta la revisaremos con cuidado cuando veamos los interruptores. 3.1.2- Interruptores En esta seccin no intento revisar todos los interruptores descritos en la seccin PERLRUN de la referencia, sino solo algunos de ellos enfocados a comprender mejor el como se realizan algunas tareas con Perl en lnea de comando, y el orden tambin ha sido modificado tratando de hacer mas fcil su comprensin. -e comando Este interruptor se usa para indicar una lnea de cdigo en Perl a ejecutar (nota solo una, completa con ";"), si se da este interruptor, Perl no buscara un nombre de script en la lista de argumentos ,ni la recibir por la entrada estndar. Para formar un script de varias lneas, se puede dar varias veces este interruptor con lneas distintas. -n Hace que Perl asuma que existe el ciclo siguiente alrededor del script que le damos: while(<>) { #aqui va el script } -p Al igual que "-n" hace que Perl asuma un ciclo alrededor del script, pero adems, hace que imprima automticamente el ultimo valor evaluado de cada ciclo del script, lo que equivale al siguiente ciclo:

588

while(<>) { #El script va aqu } continue { print; } El uso del interruptor -p inhibe al interruptor -n. -a Al estar usando un interruptor "-e" o "-p" hace que los renglones que se van leyendo en el ciclo del "-n" o "-p" pasen por un "split" con el delimitador especificado con el interruptor -F y sean asignados, ya como arreglos a @F, resultando en la siguiente estructura del while: while(<>){ @f=split(' '); #aqui va el script } -F Con este interruptor determina una expresin regular la cual se usara como delimitado para el split del switch "-a", se puede poner el patrn entre "/" siendo estos ignorados. Nota: los interruptores -a y -F son exclusivos de Perl 5. -iextencion Especifica que los archivos procesados por "<>" sean editados en su lugar, esto es, que el archivo de entrada sea movido a otro archivo con la extensin especificada, y que en el archivo original se vayan escribiendo los resultados que generemos. Si no se especifica ninguna extensin, al final del proceso no habr ningn archivo con la informacin original. -v Imprime la versin y nivel de parches del ejecutable de Perl. -w Imprime advertencias sobre identificadores que se usen solo una vez, escalares que se usan sin haber recibido valor, subrutinas indefinidas, archivos sobre los que intentemos de realizar operaciones para las que no fueron abiertos, etc. Se recomienda para diagnosticas problemas en los programas. Habiendo revisado algunos interruptores, ahora vamos a ver algunos ejemplos de las cosas que podemos hacer con ellos. 3.1.3- Tareas Comunes de Perl en Lnea de Comando Entindase por Lnea de Comando, que desde nuestro interprete de comandos, invocamos Perl con los parmetros adecuados para que realice una funcin sin necesidad de haber escrito un script, por supuesto, no son tareas muy complejas, pero llegan a ser deslumbrantemente tiles.

589

3.1.3.1- Reemplazar una cadena en un archivo por otra Es muy comn que, por ejemplo, deseemos reemplazar todas las ocurrencias de, digamos, un camino y nombre de programa, por otro en un archivo de texto que podra ser el cdigo fuente de un programa o alguna otra cosa, muchos procesadores de palabra soportan este tipo de procesos, pero con Perl no se requieren herramientas extra y podemos trabajar de un solo golpe grandes cantidades de archivos. el comando seria de la forma: Perl -pi.bak -e "s/CADENA1/CADENA2/g;" archivo1 archivo2 archivo3 Recordando lo que significan los interruptores: -p indica que alrededor de la instruccin tenemos un ciclo que revisara todas las lneas de todos los archivos que se pasen como parmetros a script, adems, que los resultados se imprimirn (a la salida estndar). Como este interruptor no recibe valores, puede acompaarse de otro interruptor (en este caso, "-p -i" es equivalente a "-pi"). -i Los archivos procesados sern editados en lnea, generando respaldos de estos con la extensin .bak, la salida estndar del bloque de "-p" se redirecciona a los archivos origianles, de modo que los resultados del script sern el nuevo contenido de los archivos originales. -e El comando a ejecutar dentro del ciclo, y cuyo resultado ser el nuevo contenido de los archivos es una substitucin (usamos implcitamente el argumento default $_ para indicar sobre que se hace la substitucin), el comando, es una substitucin del patrn en toda la lnea leda. archivo1 archivo2 archivo3 Son los archivos que usa el ciclo de "-p" que, como en el caso de cualquier otro script que usa "<>" son recibidos como parmetros. El resultado final es que generamos archivo1.bak, archivo2.bak, archivo3.bak y archivo1, archivo2 y archivo3 y en los ltimos la CADENA1 ha sido reemplazada por CADENA2. 3.1.3.2- Imprimir algn campo de un archivo En UNIX, gran parte de la informacin del sistema se almacena en archivos de texto con separadores para indicar campos, siendo cada rengln un registro, adems, como un ejemplo sencillo, planteemos una ejecucin en lnea de Perl que imprima todos los logins del etc/passwd que pertenezcan a un cierto grupo, digamos, el 100. Perl -anF: -e 'if($F[3]==100){print "$F[0]\n";}' /etc/passwd

590

Curso de Clipper
A principio de los aos ochenta, DBASE II hizo su aparicin de la mano de George Tate (19431984) y su empresa Ashton-Tate. Esta nueva herramienta se presentaba en el emergente mundo de los microordenadores con la intencin de facilitar la gestin de las bases de datos. Evidentemente, los sistemas de gestin de bases de datos existan desde mucho antes, sobre todo, desarrollados para grandes sistemas, pero la cuestin estaba en cubrir una carencia que ms tarde o ms temprano deba ser atendida por los ingenieros de software y que era esperada ansiosamente por el creciente nmero de usuarios de los ordenadores personales. El sistema de gestin de bases de datos haba que disearse no exclusivamente como un entorno de programacin, semejante a otros entornos o lenguajes con capacidad de tratamiento de grandes masas de datos. Este deba posibilitar la ejecucin interactiva de instrucciones, ser amigable, accesible por usuarios no programadores, y deba estar formado por unas instrucciones potentes y fciles de memorizar. (LA PRIMERA DE LAS VERSIONES DE DBASE II SE UTILIZ CON EL SISTEMA OPERATIVO CP/M, SIGUINDOLE OTRAS COMO LA 2.4 DE SEPTIEMBRE DE 1983 BAJO DOS 1.1 Y 2.0). Tambin, a principio de los ochenta se comienza a utilizar entre los usuarios de micros una nueva terminologa informtica de bases de datos, sta era ms familiar en otros ambientes informticos y defina con precisin los conceptos ms bsicos: Una base de datos puede definirse como la agrupacin til y organizada de informacin. Bases de datos relacionales. Este tipo de estructura define relaciones entre los datos en una base de datos. Un modelo simple organiza la base de datos de igual forma que podemos definir una tabla de dos dimensiones (filas y columnas). Los datos de una fila (registro) se subdividen en columnas (campos). A cada fila se la asigna un nmero (n de registro) que representa el orden en que ser almacenado el registro en la base de datos. A las distintas columnas se le asignar un nombre de campo. Con esta estructura bsica de base de datos era fcil manipular y actualizar gran cantidad de informacin.

Es fcil distinguir los componentes bsicos de una base de datos: Su estructura es descrita por un conjunto de nombres de campos, estos campos pueden ser de varios tipos en funcin del dato a almacenar (nmeros, fechas, etc) y de longitud definible. Otro componente son los datos propiamente dichos. Los gestores de bases de datos permiten la organizacn y el tratamiento eficaz de grandes masas de datos proporcionndonos gran variedad de herramientas.

DBASE II proporciona un gestor de base de datos de tipo relacional con capacidad para gestionar las bases de datos, interpretar interactivamente instrucciones y ejecutar bloques de sentencias (programas). DBASE II tambin contribuy a la filosofa de la programacin estructurada, mejor sus prestaciones y evolucion en varias versiones (DBASE III, DBASE III+ y DBASE IV).

591

George Tate fallecido tempranamente nunca pudo comprobar la revolucin que ocasionara este producto, an en constante evolucin. El xito obtenido entre los usuarios de micros, principalmente atrados por su versatilidad y potencia, y los grandes beneficios producidos en su comercializacin, hizo que muchas empresas de software se adherieran a la idea de desarrollar nuevos productos anlogos, una gama de dialectos que hoy se les agrupa con el sobrenombre de entorno xBase (Clipper, Quicksilver, Foxbase, etc). La difusin de estos productos han desbancado a muchos lenguajes de programacin, como al Cobol que aunque propicia una fcil lectura de sus fuentes, la programacin resulta lenta y laboriosa. En los ochenta, en pleno boom informtico DBASE sustituye a muchos lenguajes por la potencia de sus rdenes y facilidad de uso. Por entonces, hubo que estar muy despierto a la hora de seleccionar una herramienta de trabajo con futuro. CLIPPER es un dialecto creado como otros tantos con la intencin de mejorar las prestaciones de DBASE. Su primera versin se cre en 1985 en los laboratorios de Natuncket. CLIPPER est escrito en lenguaje C y Ensamblador y se present como un lenguaje atrevido que ha dado muchos quebraderos de cabeza en Ashthon-Tate. En el primer contacto que se tiene con l es dificil encontrar muchas diferencias con respecto a DBASE, ya que CLIPPER es un lenguaje formado por un conjunto de comandos y funciones similares a las usadas con DBASE, incluso la mayora con igual formato sintctico. Pero no tardaremos demasiado tiempo en percartarnos de las diferencias. La principal de ellas, est en que todos los programas escritos en Clipper pueden compilarse y enlazarse. El resultado obtenido es un fichero ejecutable que puede utilizarse de forma independiente al gestor de base de datos y sin necesidad de incluir mdulo runtime. Esto repercute en la velocidad de ejecucin de los programas. Muchos programadores recordarn que cuando entregaban un proyecto a un cliente desarrollado en DBASE II o III se vean con la fatalidad de entregar los ficheros fuentes, ya que DBASE lo que haca era interpretarlos. CLIPPER salvaguard estos intereses. CLIPPER aport ms comandos y funciones y prescindi de muchos de DBASE. CLIPPER es ahora sin duda el compilador ms utilizado en aplicaciones de gestin de datos para microordenadores. La ltima versin aparecida en el mercado es la CLIPPER 5.01 versin reparada de la CLIPPER 5.0. Hasta el momento, la versin ms utilizada quizs por su largo tiempo de vigencia es la CLIPPER SUMMER '87. Anteriores a sta eran la CLIPPER AUTUMN '86 y la versin de 1985. De todas la versiones detalladas la SUMMER '87 ha sido la ms difundida. Muchas aplicaciones se han desarrollado con esta versin, por ello, an, muchos programadores se resisten al cambio a versiones ms actuales. Otras prestaciones de CLIPPER SUMMER '87 a destacar son las siguientes: Provee un conjunto de funciones para el tratamiento de ficheros en redes de area local. Permite manejar ficheros de bajo nivel. Posibilita la creacin de funciones de usuarios y agruparlas en libreras. Permite el uso de arrays unidimensionales. Proporciona un depurador avanzado.

592

La presente gua est dividida en doce captulos. Cada captulo describe comandos y/o funciones de Clipper referentes a temas concretos. El primero de ellos describe aspectos tcnicos iniciales que es preciso conocer de este producto.

I. Caractersticas tcnicas.
1. Capacidades. N. mximo de registros por base de datos, 1000.000.000 N. mximo de caracteres por registro, RAM disponible N. mximo de campos por registro, RAM disponible N. mximo de caracteres por campo, 32 kb N. de dgitos de precisin en operaciones de clculo, 18 N. mximo de caracteres en una clave de indexacin, 250 N. mximo de variables de memoria, 2048 Tamao mximo de una variable de memoria, 64 kb N. mximo de dgitos en una variable numrica, 19 N. mximo de tablas, 2048 2. Requerimiento hardware. Ordenador : IBM PC, XT, AT, 386 o compatible Memoria RAM : 256 kb Disco duro : Necesario para funcionamiento ptimo Coprocesador: Si existe se aprovecha automticamente 3. Requerimiento software. Sistema Operativo : DOS 2.0 o superior (monousuario) DOS 3.1 o superior (multiusuario) LAN : Bajo DOS. No requiere LAN Pack. Bloqueo manual. 4. Instalacin . La instalacin de CLIPPER es muy fcil, basta con copiar el contenido de todos los disquetes a un directorio o ejecutar el fichero CLIPCOPY.BAT que se encuentra en el disco de Sistema. 5. Config.sys. Para el funcionamiento ptimo de CLIPPER conviene incluir las siguientes lneas en el fichero de configuracin CONFIG.SYS. FILES = 20 BUFFERS = 8 Si se posee DOS 3.3 o superior es posible trabajar hasta con 255 ficheros abiertos simultneamente. Para ello se debe indicar, en lugar de FILES = 20: FILES = 255 (Es importante ajustar el nmero de ficheros para aprovechar al mximo la memoria).

593

6. Autoexec.bat. En el fichero AUTOEXEC.BAT resulta de gran utilidad incluir una lnea de PATH. Esto permitir ejecutar el compilador desde otros directorios de trabajo. PATH C:\CLIPPER 7. Ficheros. A los distintos ficheros que maneja CLIPPER podremos diferenciarlos por su extensin. Si hemos utilizado anteriormente DBASE, la mayora nos resultarn familiares. Bases de datos (.DBF) Datos memo (.DBT) Indices (.NTX) en DBASEIII (.NDX) Etiquetas (.LBL) Informes (.FRM) Texto (.TXT) Variables de memoria (.MEM) Fuentes (.PRG) Objetos (.OBJ) Compilacin (.CLP) Enlace (.LNK) Overlays (.OVL) Ejecutables (.EXE) 8. Compatibilidad con DBASE. La posibilidad de compilar DBASE con el compilador de CLIPPER est limitada por un grupo de comandos y funciones de DBASE. A continuacin se muestra una relacin de estos comandos y funciones: APPEND LIST FILES SET CATALOG ASSIST LIST HISTORY SET COLOR ON/OFF BROWSE LIST STRUCTURE SET DEBUG CHANGE LOAD SET DOHISTORY CLEAR FIELDS LOGOUT SET ECHO CREATE LABEL MESSAGE() SET ENCRYPTION CREATE REPORT MODIFY COMMAND SET FIELDS CREATE QUERY MODIFY LABEL SET HEADING CREATE SCREEN MODIFY QUERY SET HELP CREATE VIEW MODIFY REPORT SET HISTORY DISPLAY FILES MODIFY SCREEN SET MEMOWIDTH DISPLAY MEMORY MODIFY STRUCTURE SET MENUS DISPLAY STATUS MODIFY VIEW SET SAFETY DISPLAY STRUCTUR ON ERROR SET STATUS DISPLAY USERS ON ESCAPE SET STEP EDIT ON KEY SET TALK ERROR() RESUME SET TITLE EXPORT TO RETRY SET TYPEHEAD HELP RETURN TO MASTER SET VIEW IMPORT TO SET INSERT SET CARRY

594

Comandos y funciones no compatibles. Otras distinciones a considerar son las referentes a las macros (en Clipper no pueden usarse para sustituir a una palabra del sistema) y los ficheros ndices (en Clipper estn optimizados). Clipper proporciona un manejador de bases de datos (DBU), un emulador del punto de peticin de orden de Dbase (DOT), un generador de informes y etiquetas (RL) y un generador de ficheros ndices. Todo estas opciones son semejantes a las proporcionadas por Dbase.

II. Entorno de desarrollo.


1. Entorno. Para desarrollar con CLIPPER tendremos que disponer de las siguientes herramientas bsicas: - Un editor que genere cdigo ASCII standard. - El compilador CLIPPER.EXE. - Las libreras CLIPPER.LIB, EXTEND.LIB, OVERLAY.LIB, etc. - Un enlazador PLINK86.EXE ,LINK.EXE ,TLINK.EXE. - Un depurador de programas DEBUG.OBJ. 2. Escritura de programas. Los requisitos bsicos a cumplir para la correcta escritura de los fuentes son: a) Los ficheros fuentes se nombrarn especificando la extensin .PRG. b) La longitud de una lnea es de 256 caracteres. c) Una lnea slo admitir una instruccin. d) Las instrucciones pueden escribirse desde la primera lnea en el editor. e) Cuando sea necesario escribir lneas de instrucciones muy largas, podemos hacerlo en lneas independiente escribiendo un punto y coma al final de la lnea. f) Puede escribirse en minsculas o maysculas, indistintamente. g) El asterisco '*' se utilizar para hacer comentarios. h) El doble '&' se utilizar para comentar lneas con instrucciones. 3. Compilacin. La compilacin es una traduccin del fichero fuente (.PRG) para obtener un fichero objeto (.OBJ). Consiste en transcribir cada instruccin desde el lenguaje simblico en que est escrito el cdigo (CLIPPER) a cdigo comprensible por el enlazador del sistema operativo (DOS). El fichero del compilador que proporciona CLIPPER se llama CLIPPER.EXE. Sintaxis:

595

CLIPPER <Fprg> [-<opcin> {-<opcin>}] <Fprg> Programa fuente que se compila -<opcin> Opciones de compilacin -l El mdulo objeto no almacena el n de lnea del fuente. -m Hace que las llamadas DO o SET PROCEDURE no se compi- len. -o Especificar el directorio donde se depositar el fichero objeto. -p La compilacin no comienza hasta que no se pulsa una tecla. -q Suprime la visin en pantalla de los nmeros de lneas. -s Hace que no se genere mdulo objeto. Verifica slo sintaxis. -t Especificar la unidad donde se crear el fichero temporal .$$$ Es imprescindible que haya al menos un espacio en blanco entre <Fprg> y la primera opcin as como entre cada una de ellas. Es obligatorio que la opcin se exprese en minsculas. Nuestro programa puede contener asimismo diversas llamadas DO a otros mdulos .PRG o a procedimientos del mismo programa. Si no le especificamos lo contrario, CLIPPER compila de forma automtica los ficheros llamados por DO. 4. Enlace. El fin de un enlazador es el de asociar los mdulos objeto obtenidos mediante el compilador con las libreras donde se contienen las traducciones mquina de cada una de las sentencias,llamadas,etc. que aparecen en el mdulo objeto. a) Enlazadores * PLINK86 (Phoenix Tec. Clipper Summer '87) Sintaxis: PLINK86 FI <Fobj> {,<Fobj>} [OUTPUT <Fexe>] LIB <Flib> {,<Flib>} | [@<Flnk>] * LINK (Microsoft) Sintaxis: LINK <Fobj> {<Fobj>},<Fexe>,<mapa>,<Flib> {<Flib>} * TLINK (Borland) Sintaxis: TLINK <Fobj> {<Fobj>},<Fexe>,<mapa|/x>,<Flib> {<Flib>} * RTLINK (Pocket Soft. Clipper 5) Sintaxis: RTLINK [FI <Fobj> [OUTPUT <Fexe>] [LIB [<Flib>] [<opciones>]] | [@<Flnk>]

596

b) Overlay * Ficheros de enlace .LNK Todas las clasulas que deban indicarse al enlazador pueden situarse en un fichero de enlace .LNK. El enlazador usa uno de estos ficheros conforme a la siguiente sintaxis: PLINK86 @<via><Flnk> Ejemplo_1: PRUEBA.LNK FILE prueba LIB clipper,extend ; (';'Indica el final del fichero .LNK) * Librerias CLIPPER.LIB EXTEND.LIB OVERLAY.LIB * Overlays El mayor problema con el que nos podemos encontrar, cuando estamos realizando una aplicacin en Clipper, es que sta no nos quepa fsicamente en la memoria de trabajo de nuestro ordenador. El nico modo que tenemos de solucionar este problema es proceder a lo que denominamos segmentacin, programacin por capas, solapas u overlays. Cuando programamos usando esta tcnica, lo que hacemos es dividir la memoria RAM en dos o ms reas de trabajo. En la primera de ellas (rea principal) se carga el mdulo ejecutable, y en las reas de solape se cargan y descargan, conforme se van usando, los diferentes mdulos overlay que hayamos definido. Ejemplo_1: PRUEBA.LNK (2 reas) FILE prgprin LIB clipper,extend OVERLAY CODE, $CONSTANTS BEGINAREA SECTION FILE modulo1 SECTION FILE modulo2 SECTION FILE modulo3 ENDAREA Mandatos para compilar y linkar CLIPPER CLIPPER CLIPPER CLIPPER PLINK86 prgprin -m modulo1 modulo2 modulo3 @prueba

597

Ejemplo_2: PRUEBA.LNK (3 reas) FILE prgprin LIB clipper,extend OVERLAY CODE, $CONSTANTS BEGINAREA SECTION FILE modulo1 SECTION FILE modulo2 ENDAREA BEGINAREA SECTION FILE modulo3 ENDAREA Mandatos para compilar y linkar CLIPPER CLIPPER CLIPPER CLIPPER PLINK86 prgprin -m modulo1 modulo2 modulo3 @prueba

(Para que Clipper produzca un fichero .EXE y tantos ficheros .OVL como mdulos para overlays tengamos definidos, slo hay que cambiar la instruccin: SECTION FILE <Fobj> {,<Fobj>} por SECTION INTO <Fovl> FILE <Fobj> {,<Fobj>} (Esto ltimo es til para trabajar con disquetes) * Mandatos del enlazador PLINK86 #. Sirve para poner un comentario en un fichero de enlace. BATCH. Por defecto, cuando PLINK86 no encuentra un fichero .OBJ o .LIB de los especificados, la operacin de enlace contina adelante. BEGINAREA. Determina el comienzo de un rea. ENDAREA. Determina el final de un rea. DEBUG. Proporciona informacin adicional para ayudar a la depuracin de una aplicacin en el caso de overlay. FILE. Especificar los mdulos objetos separados por coma (,). HEIGHT. N lneas/pgina del informe (MAP). LIBRARY. Especificar las libreras que sern enlazadas con los .OBJ.

598

LOWERCASE. Convierte en minsculas todos los identificadores y smbolos. MAP=<Fmap>. Especificar fichero .MAP. NOBELL. Elimina el sonido que aparece con los mensajes del PLINK86. OUTPUT. Especificar fichero .EXE. SEARCH. Hace una segunda pasada por las libreras si tras terminar el enlace alguno de los smbolos ha quedado sin definir. SECTION. Determina que los mdulos objeto que se relacionan tras la palabra FILE estarn en el rea de overlay abierta, pero no en un fichero independiente en disco. SECTION INTO. Igual que el anterior, pero en un fichero en disco. UPPERCASE. Convierte a maysculas todos los identificadores y smbolos. VERBOSE. Nos da informacin en pantalla de lo que est haciendo PLINK86. WIDTH. Determina el ancho en columnas del informe (MAP). WORKFILE. Sirve para direccionar el archivo temporal que usa el enlazador.

III. Bases de datos.


1. Creacin de una base de datos. Para crear un fichero de estructura vacia se usar el mandato CREATE. Para definir los distintos campos de la futura base de datos emplearemos APPEND BLANK (para aadir un registro en blanco) y REPLACE (para almacenar el contenido). CREATE <Fstr> Ejemplo_1: CREATE clientes USE clientes APPEND BLANK REPLACE FIELD_NAME WITH "CODIGO" REPLACE FIELD_TYPE WITH "C" REPLACE FIELD_LEN WITH 5 APPEND BLANK REPLACE FIELD_NAME WITH "NOMBRE" REPLACE FIELD_TYPE WITH "C" REPLACE FIELD_LEN WITH 30 CLOSE RETURN Las variables de entorno FIELD_NAME, FIELD_TYPE, FIELD_LEN y FIELD_DECIMALS tomarn el nombre de campo, el tipo de campo, la longitud de campo y las posiciones decimales, respectivamente.

599

Una vez creada la estructura pasaremos a generar la base de datos propiamente dicha con CREATE FROM. CREATE <Fdbf> FROM <Fstr> Ejemplo_2: CREATE CLIENTES FROM CLIENTES Tanto en la utilidad DOT porporcionada por Clipper como en el entorno Dbase podemos crear bases de datos sin necesidad de escribir programas. 2. Tipos y longitud de campos. Los distintos tipos de campos que podemos definir en una base de datos son: C - Caracter (1-254 caracteres alfanumricos) N - Numrico (1-19 dgitos de entero.) (0-15 dgitos decimal y dos dgitos menor que entero) D - Fecha (8 dd-mm-aa) L - Lgico (1 carcter para valores lgicos: T,F,Y,N) M - Memo (10) Almacena direccin para acceder a fichero .DBT. 3. Usar una base de datos. Para usar una base de datos emplearemos la sentencia USE especificando el fichero de base de datos. Si existe un fichero memo asociado se abrir, y si se indic uno o ms ficheros .NTX se activarn los ndices correspondientes. Tambin proporciona el alias adecuado. USE <Fdbf> [INDEX <Fntx1> {,<Fntx2>}] [EXCLUSIVE] [ALIAS] <alias> El nmero mximo de ficheros ndices asociados es 15. EXCLUSIVE se emplea para redes y posibilita la apertura de ficheros con uso exclusivo a un usuario. USE sin ms, cierra el fichero del rea activa. Ejemplo_1: USE CLIENTES 4. Modificar estructura. Para modificar la estructura de una base de datos se recomienda el uso de la sentencia MODIFY STRUCTURE propia de Dbase. Posibilita renombrar, suprimir y aadir campos, as como modificar el tipo y la longitud de los mismos. Con LIST STRUCTURE de Dbase listaremos la estructura de una base de datos. MODIFY STRUCTURE Ejemplo_1:

600

USE CLIENTES MODIFY STRUCTURE Hay que tener precaucin si existen registros en la base de datos ya que algunas modificaciones pueden vaciarnos el contenido de uno o ms campos. 5. Aadir registros. APPEND BLANK aade un registro vacio a nuestro fichero en uso. El puntero de la base de datos se sita en el registro aadido. La sentencia REPLACE nos servir para reemplazar el contenido de los campos. REPLACE [<ambito>] [<alias1>] <campo1> WITH <expr1> {,[<alias2>]<campo2> WITH <expr2>} [FOR <expL>][WHILE <exprL>] Ejemplo_1: USE CLIENTES APPEND BLANK REPLACE CODIGO WITH "00001" REPLACE NOMBRE WITH "Federico Torres" 6. Listar registros. LIST y DISPLAY sirven para visualizar, imprimir o enviar a un fichero de texto, un registro o conjuto de registros. LIST [OFF] [<mbito>] [<Lcam>] [FOR <expL>] [WHILE <expL>] [TO PRINT/TO FILE <ftxt>] DISPLAY [OFF] [<mbito>] [<Lcam>] [FOR <expL>] [WHILE <expL>] [TO PRINT/TO FILE <ftxt>] Ejemplo_1: USE CLIENTES LIST Ejemplo_2: USE CLIENTES LIST CODIGO Ejemplo_3: USE CLIENTES DISPLAY FOR CODIGO > "50000" TO PRINT 7. Puntero de registro. Clipper mantiene un puntero que indica el registro activo en cada momento. Tanto en Clipper con en Dbase podemos conocer la posicin del puntero con la funcin RECNO(). En el ejemplo anterior

601

al aadir el registro vacio el puntero se desplaza a la posicin que ocupa este registro dentro de la base de datos. Podemos deducir que las sustituciones se efectuarn ah. Existen mandatos que afectan nicamente al registro activo. El puntero se puede desplazar usando la sentencias GO y SKIP en sus distintas modalidades: GO <Reg> (ir al registo indicado) GO TOP (ir al registro nmero 1) GO BOTTOM (ir ltimo registro) SKIP (ir al siguiente registro) SKIP -1 (ir al anterior) etc. 8. Editar un registro. La edicin de registros es posible realizarla con varias sentencias. No es posible usar EDIT de Dbase III. En Clipper la edicin de un registro puede realizarse con un grupo de GET's, aunque existen otras sentencias ms avanzada como DBEDIT, MEMOEDIT, etc. @ <Fila>,<Col> [SAY <ExpC> [PICTURE <msc> ]] [GET <Vmen> [PICTURE <masc>] [RANGE <expN1>, <expN2>] [VALID <expL>]] PICTURE expresa un formato para la entrada/salida de informacin. RANGE sirve para validar datos numricos entre los dos lmites especificados. VALID se emplea para expresiones genricas de validacin. <expL> ser la condicin de validacin. Ejemplo_1: USE CLIENTES GO 3 @ 1,1 SAY " Modifique codigo: " GET CODIGO @ 2,1 SAY " Modifique nombre: " GET NOMBRE READ Ejemplo_2: USE ALUMNOS GO TOP @ 1,1 SAY NOMBRE @ 2,1 SAY " Modifique edad: " GET EDAD RANGE 1,7 @ 3,1 SAY " Modifique sexo: " GET SEXO PICTURE "!"; VALID(SEXO$"VH") READ READ lee las variables GET's 9. Marcar un registro. Clipper igual que Dbase permite marcar registros para posteriormente, si procede, borrarlos definitivamente. Esto se har con la sentencia DELETE que marca con un asterisco el registro activo. Puede marcarse ms de un registro usando la clasulas FOR o WHILE.

602

DELETE [mbito] [FOR <ExpL>] [WHILE <ExpL>] [mbito] RECORD <nm> Marcar el registro especificado. ALL Marcar todos los registros Ejemplo_1: USE CLIENTES GO 1 DELETE Ejemplo_2: USE CLIENTES DELETE ALL Ejemplo_3: USE CLIENTES DELETE RECORD 10 Ejemplo_4: USE CLIENTES DELETE FOR NOMBRE = "Mara" 10. Borrar registros. Una vez marcado un registro es posible borrarlo con PACK PACK Ejemplo_1: USE CLIENTES DELETE RECORD 10 PACK 11. Desmarcar registros. La sentencia RECALL suprime las marcas puestas con DELETE RECALL [<mbito>] [FOR <ExpL>] [WHILE <ExpL>] Ejemplo_1: USE CLIENTES RECALL RECORD 10 12. Borrar todos los registros. ZAP borra todos los registros marcados o no de una base de datos manteniendo su estructura.

603

13. Localizar registros. LOCATE permite localizar uno o ms registros. En el momento que encuentra un registro el puntero de registro se coloca en l, esperando a un CONTINUE para continuar con la bsqueda. La bsqueda es secuencial por lo que si el tamao de la base de datos es considerable puede resultar lento este proceso. LOCATE [<mbito>] [FOR <expL>] [WHILE <expL>] CONTINUE Ejemplo_1: USE CLIENTES LOCATE FOR CODIGO > "10000" .AND. NOMBRE = "JOSE" 14. Operaciones con bases de datos. En una base de datos es posible contar registros, y realizar operaciones de suma y media aritmtica. COUNT nos servir para contar, SUM para sumar el contenido de campos numricos y AVERAGE para calcular la media aritmtica. COUNT [<mbito>] [FOR <expL>] [WHILE <expL>] TO <vmen> COUNT cuenta el nmero de registros que cumplen una determinada condicin especificada. Dicha informacin ha de depositarse obligatoriamente en una variable numrica de memoria. <mbito> es por defecto ALL SUM [<mbito>] TO <vmen> SUM suma uno o ms campos depositando el resultado en una variable. AVERAGE [<mbito>} TO <vmen> AVERAGE calcula la media aritmtica de uno o ms campos. 15. Exportar. COPY TO Copia toda la base de datos en curso o slo una parte a un nuevo archivo. COPY TO <archivo> [<mbito> [FIELDS <lista campos>] [FOR <condicin>] [WHILE <condicin>] [SDF/DELIMITED/DELIMITED WITH <delimitador>] <archivo> - Es el nombre del nuevo archivo. <mbito> - Determina la porcin del archivo a copiar, por defecto es ALL (todo). FIELDS <lista campos> - Son los campos a copiar a la nueva base de datos. FOR/WHILE <condicin> - Especifican la condicin a cumplir.

604

SDF - Especifica que el archivo de salida ser con formato ASCII, con campos de longitud fija. DELIMITED - Formato para el archivo de salida ASCII, con campos de longitud variable y separados por comas. Si se desea pueden separase con espacios (BLANK), o con cualquier otro delimitador. Ejemplo_1: USE HELP COPY TO HELP.TXT SDF 16. Importar. APPEND FROM aade datos a la base en uso a partir de otro archivo que puede ser que no sea (.DBF). Podemos seleccionar loa datos a aadir mediante cualificadores. APPEND [<registros> [FIELDS <campos>] FROM <fichero> [FOR <condicin>] [WHILE <condicin>] [SDF/DELIMITED [WITH BLANK/<delimitador>]] <registros> - Registros a agregar por defecto son todos. <campos> - Lista de campos a agregar. <fichero> - Nombre del archivo origen. Por defecto, (.DBF), FOR/WHILE <condicin> - Indican las condiciones que han de cumplir los registros para ser agregados. SDF - Identifca archivos ASCII. DELIMITED - Archivos ASCII con separacin de campos con comas. DELIMITED WITH BLANK - Campos separados por un espacio DELIMITED WITH <delimitador> - Podemos especificarlo. Ejemplo_1: USE CLIENTES APPEND FROM VENTAS FOR PEDIDO > 5000

IV. Indices.
1. Crear ficheros ndices. INDEX indexa un fichero de datos por el campo que le indiquemos. Crea en disco un fichero con la extensin .NTX. Pueden usarse tambin claves mltiples formada por la suma de varios campos, de partes de campos, expresiones y campos, etc, pero recuerde que el mximo nmero de caracteres de una clave ser de 250. Para sumar campos hemos de tener siempre la precaucin de convertirlos previamente a cadena. Los ficheros ndices no son compatibles con los de Dbase III. Cuando un ndice est abierto con su correspondiente base de datos se actualiza de forma

605

automtica. Una base de datos puede tener asociados como mximo 15 ficheros ndices. Los registros que se encuentran marcados para ser borrados tambin forman parte del ndice. INDEX ON <campo1> {+<campo2>} TO <Fntx> Ejemplo_1: USE CLIENTE INDEX ON NOMBRE TO NOMCLI Ejemplo_2: USE CLIENTE INDEX ON NOMBRE+DTOS(FECHA) TO FECCLI 2. Activar fichero ndice. Como vimos anteriormente en el captulo I, la activacin de ndices se realiza con USE. Se pueden especificar uno o ms ficheros ndices. Con SET ORDER TO se establecer el ndice activo. Esta sentencia altera el ordenamiento de la declaracin inicial de ndices hecha con USE...INDEX. Si indicamos SET ORDER TO 0 se desactivan todos los ficheros ndices. No obstante, la importancia de este mandato estriba en que no tenemos necesidad de abrirlos de nuevo para activarlos. SER ORDER TO <expN> <expN> es el nmero de ndice activo. Puede valer de 0 a 15. Ejemplo_1: NOMBRE = SPACE(20) FECHA = CTOD(SPACE(8)) USE CLIENTES INDEX NOMCLI,FECCLI,DOMCLI SET ORDER TO 2 LIST NOMBRE,FECHA TO PRINT 3. Bsqueda por ndice. SEEK busca una expresin en una clave ndice. SEEK <expr> Ejemplo_1: USE CLIENTES INDEX NOMCLI SEEK "LUIS MARIN" IF FOUND() @ 4,4 SAY FECHA @ 5,4 SAY VENTAS ELSE @ 10,1 SAY "No existe CLIENTE" ENDIF 4. Area de trabajo.

606

SELECT selecciona las diferentes reas de trabajo en que vamos a situar nuestros ficheros de datos. El ltimo SELECT que enunciemos es aquel que contendr el fichero activo. SELECT <rea>/<alias> <rea> es un nmero comprendido entre 0 y 254. <alias> es el nombre de un rea de trabajo existente si hay un fichero abierto en ese rea. Se puede hacer referencia a las 10 primeras reas de trabajo con las letras A a J. En Clipper se pueden utilizar 255 reas de trabajo. En cada rea de trabajo se pueden abrir un fichero de base de datos y 15 ficheros ndices como mximo asociados a l. Ejemplo_1: SELECT 1 USE CLIENTES SELECT 2 USE DIARIOVTAS Ejemplo_2: SELECT 1 USE CLIENTES INDEX NOMCLI ALIAS CLI SELECT 2 USE DIARIOVTAS INDEX TOTALVTAS ALIAS DIA ... ... SELECT CLI SEEK "LUIS PEREZ" IF FOUND() CODCLI = CODIGO SELECT DIA SEEK CODCLI IF FOUND() @ 10,10 SAY PTASVENTAS ENDIF ENDIF 5. Cierre de ficheros. CLOSE cierra el fichero de base de datos abierto en el rea activa as como sus ndices asociados. CLOSE DATABASES cierra todos los ficheros de todas las reas de trabajo, as como sus correspondientes ndices. CLOSE INDEX cierra todos los ndices del rea de trabajo activa. CLOSE ALL cierra todos los ficheros abiertos.

V. Variables de memoria.
1. Tipos de variables.

607

Variable es un nombre asignado a una posicin de memoria que se puede utilizar para almacenar un dato concreto. Los tipos de variables por el tipo de dato que contienen son: -numricas -alfanumricas -lgicas -fechas 2. Nombrar una variable de memoria. Independientemente del tipo a que pertenezca una variable, debe asignrsele un nombre, que puede ser de uno a diez caracteres pueden ser una combinacin de letras, dgitos o signo de subrayado. El primer carcter de una variable de memoria debe ser una letra. Los siguientes nombres son nombres de variables de memoria permitidos. COMPRAS Precio I_V_A MES_1_A_6 No debe utilizarse el mismo nombre para una variable y para un campo en la misma aplicacin. 3. Introduccin de datos en una variable. Las instrucciones STORE y el signo igual (=) pueden emplearse indistintamente para la asignacin de datos a variables de memoria. STORE <dato> TO <var> <var> = <dato> Ejemplo_1: PTAS = 0 FECHA_ALTA = CTOD(SPACE(8)) STORE "enero" TO MES 4. Visualizacin de variables. Para visualizar el contenido de una variable puede usarse la interrogacin (?) con los siguientes formatos: ? <expr> ?? <expr> Ejemplo_1: ? MES ?? "HOLA" 5. Expresiones.

608

Adems de servir como depsito temporal, las variables de memoria pueden utilizarse en procesosde operaciones. Una variable de memoria puede ser incluida en una expresin para definir un procedimiento, para describir una condicin en una instruccin o para servir como elemento de salida (resultado de una operacin). Pueden utilizarse diferentes tipos de expresin. Una expresin puede incluir un campo de datos, una variable de memoria, una constante o una combinacin de todo ello. Sin embargo, todos los elementos de una expresin deben ser del mismo tipo. La expresin ms corriente es la expresin aritmtica, que puede contener un valor, una variable de memoria, un campo numrico y una combinacin de stos unidos por uno o ms operadores aritmticos. Las expresiones son tiles para realizar clculos matemticos. Puede utilizarse una expresin para asignar un valor a una variable de memoria o para reemplazar el contenido de un campo numrico con un nuevo valor. Cuando se incluye ms de un operador aritmtico en una expresin, sta se valora de izquierda a derecha de acuerdo con siguiente sistema de prioridades: Prioridad mxima : ** ^ Prioridad secundaria: * / Baja prioridad : + Se pueden utilizar parntesis en una expresin para definir la secuencia de evaluacin y suprimir el sistema normal de prioridades. El material dentro de los parntesis siempre es evaluado previamente. Cuando haya parntesis anidados es una expresin aritmtica, la expresin del parntesis interno es evaluado en primer lugar, luego se evala el parntesis externo. Dentro de un parntesis, los operadores se evalan segn el sistema de prioridades, de izquierda a derecha. 6. Declaracin pblica y privada. PUBLIC declara variables de memoria como globales o pblicas. Estas pueden modificar su valor en cualquier parte del programa. PUBLIC <Lvar> PRIVATE declara de uso privado la variables de memoria especificadas. Ests pueden modificar su valor en partes de un programa. PRIVATE <Lvar> 7. Salvar y restaurar variables de memoria. SAVE TO salva en un fichero variables de memoria. SAVE TO <fmem> [ALL [LIKE <masc>/EXCEPT <masc>]] <fmem> es el nombre del fichero donde se almacenarn las variables. Si no se especifica la extensin por defecto es .MEM. ALL salva en el fichero todas las variables existentes. LIKE <masc> salva en el fichero todas las variables cuya estructura sea semejante a la especificada en <masc>. Recuerde que puede hacer uso de los smbolos comodines: * y ?.

609

EXCEPT <masc> salva todas las variables que no tengan una estructura semejante a <masc>. RESTORE FROM restaura desde disco el fichero de variables de memoria <Fmem>. Si se usa ADDITIVE no se borra el entorno de variables activo al restaurar. RESTORE FROM <Fmem> [ADDITIVE] Al restaurar las variables de memoria, stas son privadas, a no ser que se especifiquen como pblicas antes de restaurarlas y se utilice la clasula ADDITIVE. Ejemplo_1: conf_cla = "1234" conf_dis = "A" conf_dir = "C:\GESTION\" conf_mar = 20 conf_col = "S" SAVE TO CONFIG ALL LIKE conf_* Ejemplo_2: RESTORE FROM CONFIG ADDITIVE clave = SPACE(4) @ 1,1 SAY "Teclear Clave: " GET clave READ IF clave = conf_cla ... ... ENDIF 8. Eliminar variables de memoria. RELEASE elemina de la vemoria las variables especificadas. RELEASE [<vmem1> {,<vmem2>}] [ALL [LIKE<masc>] EXCEPT <masc>]] <vmem1>..<vmem2> es la lista de variables que se desean eliminar. ALL indica que sean eliminadas todas las variables existentes. ALL LIKE <masc> indica que sean eliminadas todas las variables cuya estructura sea semejante a la expresada en <masc>. Se pueden usar los comodines: * y ?. ALL EXCEPT <masc> indica que sean borradas todas las variables que no concuerden con la estructura expresada en <masc>. Ejemplo_1: conf_cla conf_dis conf_dir conf_mar = = = = "1234" "A" "C:\GESTION\" 20

610

conf_col = "S" RELEASE ALL 9. Macros. Las macros sirven en CLIPPER para forzar la sustitucin de una variable por su valor en aquellos puntos de programa donde por si misma la variable no se traducira. Cuando tras una macro se sigue algn tipo de expresin hemos de indicar al sistema que la macro termina con un punto (.). &<vmem> Ejemplo_1: nombre = "lpiz" ? "Artculo: &nombre" Ejemplo_2: base = "CLIENTES" USE &base Ejemplo_3: filtro = "EDAD > 18 .AND. SEXO = 'V'" USE CLIENTES LIST NOMBRE FOR &filtro 10. Operadores y valores lgicos. a) Operadores lgicos. .AND. (Y adems) .OR. (O adems) .NOT. (Negacin) ! (Negacin) Ejemplo_1: IF EDAD > 18 .AND. EDAD < 65 PAGAR = 10000 ENDIF Ejemplo_2: IF !FILE("CLIENTES.DBF") @ 1,1 SAY " Error no encuentra base de datos " ENDIF b) Valores lgicos. Representan pares de valores. .T. (Verdadero) .F. (Valso)

611

.Y. (Si) .N. (No) Ejemplo_1: JUBILADO = .Y. IF JUBILADO .. ENDIF 11. Operadores relacionales. = (Igual que) == (Exactamente igual que) > (Mayor que) < (Menor que) >= (Mayor igual que) <= (Menor igual que) <> # (Distinto) Ejemplo_1: DO CASE CASE MES = 1 .... .... CASE MES >= 2 .... .... ENDIF

VI. Operaciones de entrada y salida.


1. Entrada, mscara, validacin y rango. @...SAY/GET muestra en las coordenadas reseadas el contenido de la expresin que sigue a SAY, carga valores a los campos o las variables de memoria que siguen a GET (hasta ser ledos por READ.) Las variables usadas han de ser declaradas previamente. @ <fila>.<col> [SAY <expC> [PICTURE <masc>]] [GET <expC> [PICTURE <masc>] [RANGE <expN1>,<expN2>] [VALID <expL>]] PICTURE expresa un formato para la entrada/salida de informacin. Este formato puede estar controlado por plantillas o funciones. Las primeras se aplican carcter a carcter y las segundas afectan a toda la clasula. Las funciones irn precedidas del smbolo @. RANGE sirve para validar datos numricos, indicando un lmite inferior y un superior. Entre estos lmites deber estar comprendido en dato numrico para que sea vlido.

612

VALID se emplea para expresiones genricas de validacin. <expL> ser la condicin de validacin. Ejemplo_1: nombre = SPACE(20) @ 2,1 SAY "Teclear nombre: " GET nombre PICTURE "@!" READ Ejemplo_2: edad = 0 @ 3,3 SAY "Teclear edad: " GET edad PICTURE "999" RANGE 19,125 READ Ejemplo_3: resp = SPACE(1) @ 5,5 say " GRABAR ? " GET resp "!" VALID(resp$"SN") READ Ejemplo_4: importe = 0 @ 5,5 SAY " TECLEAR IMPORTE: " GET importe PICTURE "@E9,999.99" READ Smbolos usados por PICTURE A Hace que un GET slo admita letras L Idem slo para valores lgicos. Y Permite slo "Y" o "N" N Idem slo letras y caracteres X Idem cualquier carcter 9 Permite que slo se visualicen dgitos # Idem slo letras, espacios y signos ! Idem slo letras maysculas , Representa los miles en los datos numricos $ Hace que se muestren $ para rellenar una cifra por la izquierda. * Idem con * Smbolos utilizados como funciones <"@<smbolo>") C Indica CR despus de un nmero positivo X Indica DB despus de un nmero negativo ( Encierra con parntesis nmeros negativos con espacios a la izquierda. ) Idem sin espacios a la izquierda B Justifica los nmeros por la izquierda A Hacen que slo se puedan captar caracteres alfabticos ! Hace que slo se permitan letras maysculas R Permite insertar caracteres que aparecern solamente en pantalla, no almacenndose en la variable E Convierte los nmeros al formato europeo D Visualiza las fechas en el formato especificado con SET DATE

613

K Borra el contenido de la variable si no se pulsa primero un carcter de control del cursor S Hace scroll horizontal con la variable Z Hace que los valores cero en un campo numrico se representen como blancos. ACCEPT acepta datos alfanumricos por pantalla y los carga en <mven>. No es necesario haber declarado previamente <Vmen>. ACCEPT [<expC>] TO <vmen> Ejemplo_1: ACCEPT "Escribe tu nombre" TO nombre INPUT Acepta datos por pantalla. Los datos han de ser identificados con sus correspondientes indicadores, as, por ejemplo, una cadena de carcteres deber escribirse entrecomillada, mientras que esto no ser preciso con un nmero. INPUT [<expC>] TO <vmen> Ejemplo_1: INPUT " Edad " TO edad WAIT detiene la ejecucin del programa y espera la pulsacin de una tecla. WAIT [<expC>] [TO <vmen>] <expC> es una cadena de caracteres que se visualizarn a modo de informacin. Si se omite, aparecer en pantalla: Press any key to continue... <vmen> es una variable que contendr el carcter qu se ha pulsado 2. Pausa. INKEY() detiene por un tiempo el flujo del programa y devuelve el valor de la tecla que se est pulsando. INKEY([<expN>]) <expN> indica el nmero de segundos de espera. Si es igual a cero detiene el programa y espera que pulsemos una tecla cuyo valor ASCII toma. Ejemplo_1: tecla = INKEY(0) Pulsando [enter], tecla = 13 3. Conocer la ltima tecla pulsada.

614

LASTKEY() devuelve el valor de la ltima tecla pulsada. Dicho valor es un nmero que se corresponde con el valor ASCII del carcter. Ejemplo_1: INKEY(0) DO CASE CASE LASTKEY() = 27 RETURN CASE CHR(LASTKEY()) = "+" ..... CASE LASTKEY() = 13 ..... ENDCASE 4. Salida. ?, ??, @ SAY, TEXT/ENDTEXT se emplean generalmente como instrucciones de salida (pantalla/impresora) para expresiones, cadenas, bloques de texto, etc. Ejemplo_1: TEXT ********************* ERROR ********************* ENDTEXT Ejemplo_2: a=4 b=5 c=3 ? (a*b)**c 5. Borrar pantalla. CLEAR borra la pantalla, manteniendo los atributos de color vigente, y libera todos los GET pendientes. Asimismo, posiciona el cursor en la posicin 0,0 (posiciones verticales 0 a 24 / posiciones horizontales 0 a 79). @..CLEAR TO borra un rea de pantalla. Ejemplo_1: @ 3,3 CLEAR TO 9,9 6. Dibujar un marco. @..TO dibuja un marco de lnea sencilla en las coordenadas especificadas. Si se emplea la opcin DOUBLE, el marco dibujado ser de lnea doble. @ <fila sup>,<col izq> TO <fila inf>,<col dcha> [DOUBLE]

615

7. Dibujar una caja. @..BOX construye una caja entre las coordenadas indicadas y con los cdigos ASCII especificados en <expC>. El orden de los caracteres es: 1. Esquina superior izquierda 2. Lnea horizontal superior 3. Esquina superior derecha 4. Lnea vertical derecha 5. Esquina inferior derecha 6. Lnea horizontal inferior 7. Esquina inferior izquierda 8. Lnea vertical izquierda 9. Carcter de relleno @ <fila sup>,<col izq>,<fila inf>,<col dcha> BOX <expC> Ejemplo_1: cadena = "+-++-+" @ 1,1,10,10 BOX cadena 8. Hacer un men. @..PROMPT facilita la creacin de mens en nuestros programas. Cada opcin se muestra con un PROMPT en una posicin especifica de la pantalla y se le acompaa opcionalmente de un mensaje aclaratorio. @ <fila>,<col> PROMPT <expC> [MESSAGE <expC>] SET WRAP ON/OFF posibilita la rotacin al alcanzar la primera o ltima opcin. SET MESSAGE determina el nmero de fila donde aparecern los mensajes de las distintas opciones. SET MESSAGE TO [<fila> [CENTER/CENTRE]] CENTER/CENTRE muestra el mensaje en la fila especificada centrndolo. MENU TO sirve para leer el valor numrico que representa a la opcin seleccionada. Dicho valor se asigna automticamente y representa el nmero de orden de cada PROMPT. Ejemplo_1: SET WRAP ON SET MESSAGE TO 23 CENTER @ 1,1 PROMPT "ALTA " MESSAGE "Alta de usuarios " @ 2,1 PROMPT "BAJA " MESSAGE "Baja de usuarios " @ 3,1 PROMPT "LISTADO " MESSAGE "Listado DESDE/HASTA" MENU TO opcion DO CASE CASE opcion = 1 .....

616

CASE opcion = 2 ..... CASE opcion = 3 ..... ENDCASE 9. Salvar/Restaurar pantallas. SAVE SCREEN salva la pantalla actual as como su estructura de variables ledas y pendientes de leer. SAVE SCREEN [TO <vmem>] TO <vmem> indica que la pantalla ser almacenada en la variable de memoria <vmem>. Esta variable ser de tipo carcter. RESTORE SCREEN restaura una pantalla almacenada previamente RESTORE SCREEN [FROM <vmem>] SAVESCREEN() almacena una parte de la pantalla en una variable de memoria <vmen> = SAVESCREEN(<fil sup>,<col izq>,<fil inf>,<col dcha>) RESTSCREEN() restaura una rea de una pantalla salvada previamente. RESTSCREEN(<fil sup>,<col izq>,<fil inf>,<col dcha>,<vmem>) Ejemplo_1: @ 2,3 SAY " -------- " @ 3,3 SAY " CLIENTES " @ 4,3 SAY " -------- " SAVE SCREEN TO panta CLEAR INKEY(0) RESTORE FROM panta RETURN Ejemplo_2: @ 2,3 SAY " -------- " @ 3,3 SAY " CLIENTES " @ 4,3 SAY " -------- " panta = SAVESCREEN(3,3,4,12) CLEAR INKEY(0) RESTSCREEN(5,5,6,14,panta)

VII. Bifurcacin y bucles.


1. IF (Si cumple condicin...).

617

Bifurca un programa entre una condicin y su opuesta. Puede usarse como mandato o como funcin. En el primer caso, lo que hace es ejecutar alternativamente unas instrucciones u otras y en el segundo devolver alternativamente un valor u otro. La sintaxis de la funcin puede ser IF() o IIF(). Mandato: IF <expL> <instrucciones> [ELSEIF <expL>] <instrucciones> [ELSE <instrucciones>] ENDIF Funcin: IIF/IF(<expL>,<.T.>,<.F.>) <expL> es la condicin que se desea establecer ELSEIF reconoce rdenes cuando se cumple la que condicin expresada. ELSE realiza las distintas rdenes que se indican cuando la condicin es falsa. <.T.> Indica la expresin a evaluar para el valor verdadero de la condicin. <.F.> Indica la expresin a evaluar para el valor falso de la condicin. Ejemplo_1: IF sexo = "V" peso = 20 ELSE peso = 12 ENDIF Ejemplo_2: IF porcentaje > 10 porcentaje = porcentaje - 2 ENDIF Ejemplo_3: salario = salario + IIF(ho>80,80000+1500*(ho-80),80000) 2. DO CASE (En caso de cumplir condicin...). Bifurca la ejecucin de un programa segn las diferentes condiciones especificadas. OTHERWISE representa todos los casos que no cumplen ninguna condicin.

618

DO CASE CASE <expL> <instrucciones> CASE <expL> <instrucciones> OTHERWISE <instrucciones> ENDCASE <expL> son las diferentes condiciones. Ejemplo_1: DO CASE CASE velocidad > 180 consumo = 4 CASE velocidad > 120 consumo = 3 CASE velocidad > 80 consumo = 2 OTHERWISE consumo = 1 ENDCASE 3. FOR..NEXT (Desde un valor hasta alcanzar otro). Permite la creacin de una estructura de bucle que se ejecuta para un rango de valores determinados de forma ascendente o descendente. FOR <expN1> TO <expN2> [STEP <expN3>] <instrucciones> [EXIT] <instrucciones> [LOOP] NEXT <expN1> es el valor inicial. Este valor se asignar a una variable de control. <expN2> es el valor final del bucle. STEP <expN3> indica el incremento o decremento de la variable. Por defecto incrementa en 1. EXIT detiene el bucle pasando el control a la sentencia posterior a NEXT. LOOP pasa de nuevo el control al comienzo del bucle, sin necesidad de que se llegue a NEXT. Ejemplo_1: FOR N=1 TO 10 CUADRADO = N**N ? CUADRADO NEXT

619

4. DO WHILE (Hacer mientras que cumpla condicin...). DO WHILE realiza una estructura de bucle mientras se cumpla la condicin especificada. DO WHILE comienza y contina el bucle si se cumple la condicin. ENDDO devuelve el control al principio. DO WHILE <expL> <instrucciones> [EXIT] [LOOP] ENDDO <expL> es la condicin que se debe cumplir para que se ejecute el bucle. LOOP manda todo el proceso de nuevo al comienzo del bucle, sin necesidad de que se llegue al final, es decir a ENDDO. EXIT fuerza a que se pare el proceso y sale del bucle aunque la condicin no haya cesado de darse. Ejemplo_1: DO WHILE .T. @ 2,2 PROMPT "CLIENTES" @ 3,2 PROMPT "PROVEEDORES" MENU TO opcion DO CASE CASE opcion = 1 DO CLI CASE opcion = 2 DO PRO CASE LASTKEY() = 27 CLEAR RETURN ENDCASE ENDDO Ejemplo_2: C=0 DO WHILE C<100 C=C+1 ? C ENDDO

VIII. Fin.
1. Retornar. RETURN termina un procedimiento, programa, o funcin, devolviendo el control al procedimiento de llamada o al DOS. Ejemplo_1:

620

DO BORRAR WITH 2,2,20,20 .. .. .. QUIT PROCEDURE BORRAR PARAMETERS X1,Y1,X2,Y2 @ X1,Y1 CLEAR TO X2,Y2 RETURN 2. Terminar. QUIT termina la ejecucin de un programa devolviendo el control al DOS. Este mandato realiza la misma funcin que CANCEL o que RETURN en el procedimiento de ms alto nivel. Ejemplo_1: USE FICHERO INDEX INDICE SEEK CLAVE IF FOUND() DO PROCESO ELSE QUIT ENDIF 3. Cancelar. CANCEL cancela la ejecucin de un programa o procedimiento, devolviendo el control al sistema operativo. Ejemplo_1: clave = SPACE(4) @ 4,4 SAY "Clave: " GET clave PICTURE "@!" READ IF clave # "9876" CANCEL ELSE ENDIF

IX. Mantenimiento de ficheros.


1. Renombrar fichero. RENAME renombra ficheros. Es el equivalente al RENAME del DOS ,aunque su sintaxis es algo distinta. RENAME <fich1> TO <fich2>

621

<fich1> es el nombre inicial del fichero y <fich2> es el nuevo nuevo. Tanto <fich1> como <fich2> deben incluir la extesin del fichero. Ejemplo_1: RENAME CLIENTES.DBF TO CLIENTES.DAT USE CLIENTES.DAT 2. Copiar ficheros. COPY FILE copia el contenido de <fich1> en <fich2>. No sirven con este mandato los comodines para copiar varios ficheros en bloque. Salvo esta excepcin funciona igual que el COPY del DOS. Es importante recordar que siempre hemos de proporcionarle las vas donde buscar los ficheros a copiar y donde queremos copiarlos. Si no se le especifica esta ltima el fichero se deposita en el directorio de trabajo. COPY FILE <fich1> TO <fich2> <fich1> es el fichero origen y <fich2> el fichero destino. Ejemplo_1: COPY FILE CLIENTES.DAT TO CLIENTES.DBF 3. Borrar ficheros. DELETE FILE y ERASE borran ficheros. Al especificar el nombre del fichero a borrar debe figurar tambin su extensin. Antes de usar este comando es necesario cerrar el fichero a borrar con el comando CLOSE. ERASE/DELETE FILE <fich> Ejemplo_1: USE CLIENTES .. .. CLOSE DATABASES DELETE FILE CLIENTES.DBF 4. LLamada al Dos. Adems de las rdenes elementales de mantenimiento de ficheros vistas anteriormente, existe la posibilidad de invocar cualquiera del DOS con RUN o !. Por ejemplo, para salir temporalmente de un programa podemos incluir un RUN COMMAND.COM y regresar con EXIT. RUN <sent> ! <sent> Ejemplo_1:

622

RUN CHKDSK > CHEQDIS.TXT 5. Comprobar la existencia de un fichero. Antes de realizar cualquier operacin con un fichero podemos comprobar su existencia con la funcin FILE() que nos retornar un verdadero (.T.) o un falso (.F.). FILE(<fich>) Ejemplo_1: IF FILE("CLIENTES.DBF") SORT ON NOMBRE TO CLISORT DELETE FILE CLIENTES.DBF ENDIF

X. Procedimientos y funciones.
1. Procedimiento. PROCEDURE indica el principio de un procedimiento. PROCEDURE <nombre procedimiento> <rdenes> [RETURN] <nombre procedimiento> - Debe de empezar con una letra y slo evala los 10 primeros caracteres. RETURN - Es aconsejable su uso para determinar el fin de un procedimiento, aunque no necesario, ya que detecta el fin al encontrar otro procedure o una marca de fin de archivo. Ejemplo_1: CLEAR DO FONDO INKEY(0) RETURN PROCEDURE Fondo FOR I=0 TO 24 @ I, 0 SAY REPLICATE("", 80 ) NEXT RETURN 2. LLamada a un procedimiento. DO ejecuta un procedimiento escrito en Clipper, C o ensamblador, pasndole parmetros (hasta 128) con WITH. DO <procedimiento> [WITH <lista de parmetros>]

623

Ejemplo_1: PROCEDURE LISTACURSOS IF !ISPRINTER() DO MSGIMPRESORA ENDIF * rdenes ... RETURN PROCEDURE MSGIMPRESORA CLEAR @ 9, 28 TO 12, 51 @ 10,30 SAY "CONECTE LA IMPRESORA" @ 11,32 SAY "Y PULSE UNA TECLA" INKEY(0) RETURN 3. Creacin de un fichero de procedimientos. SET PROCEDURE Activa los archivos de procedimientos especificados. SET PROCEDURE TO [ <archivo> ] <archivo> - Si se omite la extensin, se asume que es (.PRG). Ejemplo_1: SET PROCEDURE TO CLIENTES SET PROCEDURE TO PROVEED SET PROCEDURE TO MATERIAL 4. Nombre del procedimiento y nmero de lnea. PROCNAME() indica el nombre del procedimiento o programa que estamos ejecutando. PROCNAME() Ejemplo_1: ? "Procedimiento en uso : ", procname() PROCLINE() Devuelve el nmero de la lnea del cdigo fuente en curso del programa. Siempre que no le hayamos indicado al compilador que no numere las lneas. PROCLINE() Ejemplo_1: ? procline(), "Linea ", cLinea 5. Creacin de una funcin.

624

FUNCTION Declara una funcin definida por el usuario escrita en Clipper. FUNCTION <nombre funcin> <instrucciones> RETURN <valor de respuesta> <nombre de funcin> - Solo acepta los diez primeros caracteres. <valor de respuesta>- Es obligatorio la devolucin de un valor. Para llamar a una funcin de usuario, proceda del siguiente modo: funcin( <lista de parmetros> ) Los parmetros se pasan por valor, exceptuando los arrays, o si el parmetros es precedido por una arroba (@), entonces es pasado por referencia. Ejemplo_1: CLEAR @ 24,0 SAY ISBISIESTO( DATE() ) RETURN FUNCTION ISBISIESTO PARAMETERS DFECHA PRIVATE DANY, CCADENA, LDEVUELVE DANY = YEAR( DFECHA ) CCADENA = CTOD( "29-02-" + STR(DANY)) IF DOW(CCADENA)=0 LDEVUELVE = .F. ELSE LDEVUELVE = .T. ENDIF RETURN LDEVUELVE 6. Conocer el nmero de parmetros. PCOUNT() Determina el nmero de parmetros pasados a un procedimiento o funcin definida por el usuario. PCOUNT() Ejemplo_1: PROCEDURE EDITOR PARAMETERS CFICHERO IF PCOUNT() = 0 @ 24,0 SAY "INDIQUE EL FICHERO: " GET CFICHERO READ ENDIF

XI. Tablas.

625

1 Declaracin tablas. Una tabla es un rea de memoria que puede reservarse para contener un grupo de datos. Una tabla consta de un identificativo o nombre y un nmero definible de posiciones (de 1 a 1024 en Clipper '87). Estas posiciones pueden contener datos numricos, alfabticos, fechas, etc. Para acceder a uno de los datos contenido en una tabla se har indicando el nmero de posicin que ocupa. Existen varias funciones que posibilitan realizar operaciones en una tabla tales como aadir nuevos datos, eliminar datos, rellenar, etc. Este tipo de estructuras de memoria se utilizan como soporte temporal de los datos. DECLARE declara una o ms reas de memoria (arrays) con una longitud especfica. Antes de poder realizar cualquier operacin con una tabla debemos declararla. DECLARE <array>[<expN>]{,<array>[<expN>]...} <array> es el nombre de la tabla <expN> es la longitud de la tabla (1-1024)

Ejemplo_1: DECLARE PROVINCIA[8] PROVINCIA[1] = "ALMERIA" PROVINCIA[2] = "CADIZ " PROVINCIA[3] = "CORDOBA" .... Ejemplo_2: numero = 8 DECLARE PROVINCIA[numero] Ejemplo_3: tipo = "FICHA" numero = "01" tabla = tipo+numero DECLARE &tabla[4] &tabla[1] = "ANDALUCIA" &tabla[2] = 8 &tabla[3] = .T. &tabla[4] = CTOD("01/01/92") 2 Longitud. LEN es una funcin que devuelve el nmero de elementos que tiene una tabla, o lo que es lo mismo la longitud de la tabla indicada. LEN(<array>) <array> - Nombre de la tabla Ejemplo_1: DECLARE PROVINCIA[8] ? LEN(PROVINCIA)

626

3 Insertar. La insercin de nuevos elementos en una tabla es posible mediante la funcin AINS indicndose el nombre de la tabla y la posicin donde se desea insertar el nuevo elemento. Automticamente, el elemento insertado desplazar a los posteriores en una posicin y el ltimo se perder. AINS(<array>,<expN>) <array> - Nombre de la tabla <expN> - Posicin elemento Ejemplo_1: DECLARE NOMBRE[3] NOMBRE[1] = "LUIS" NOMBRE[2] = "MARIA" NOMBRE[3] = "CARLOS" AINS(NOMBRE,2) NOMBRE[2] = "MANUEL" ? NOMBRE[1] ? NOMBRE[2] ? NOMBRE[3] 4 Suprimir. ADEL suprime elementos en una tabla redimensionndola. ADEL(<array>,<expN>) <array> - Nombre de la tabla <expN> - Posicin elemento Ejemplo_1: DECLARE NOMBRE[3] NOMBRE[1] = "LUIS" NOMBRE[2] = "MARIA" NOMBRE[3] = "CARLOS" ADEL(NOMBRE,2) ? NOMBRE[1] ? NOMBRE[2] 5 Copiar. La copia de un elemento o grupo de elementos de una tabla a otra tabla la realiza la funcin ACOPY, debiendo indicarse la tabla origen, la tabla destino, la posicin inicial de la tabla origen, el n de elementos a copiar y el elemento de la tabla destino donde ha de comenzarse la copia. ACOPY(<array1>,<array2>[,<expN1> [,<expN2>[,<expN3>]]]) <array1> - Nombre de la tabla origen <array2> - Nombre de la tabla destino <expN1> - Posicin origen en tabla origen a copiar

627

<expN2> - Nmero de elementos a copiar desde <expN1> <expN3> - Elemento destino a comenzar copia Ejemplo_1: DECLARE TABLA_A[2],TABLA_B[2] TABLA_A[1] = "LUIS" TABLA_A[2] = "MARIA" ACOPY(TABLA_A,TABLA_B) ? TABLA_B[1] ? TABLA_B[2] Ejemplo_2: DECLARE TABLA_A[2],TABLA_B[3] TABLA_A[1] = "A" TABLA_A[2] = "B" ACOPY(TABLA_A,TABLA_B,1,1,3) ? TABLA_B[3] 6 Rellenar. AFILL rellena uno o ms elementos con la expresin indicada. AFILL(<array1>,<expr>[,<expN1>[,<expN2>]]) <array1> - Nombre de la tabla <expr> - Expresin con la que se rellenar la tabla <expN1> - Posicin donde comenzar a rellenar <expN2> - Nmero de elementos a rellenar desde <expN1> Ejemplo_1: DECLARE TLF[2] TLF[1] = "433-23-23" TLF[2] = "433-23-24" AFILL(TLF,"000-00-00",2,1) ? TLF[1] ? TLF[2] 7 Directorio. ADIR accede al directorio del disco almacenado en tablas informacin relativa a los ficheros y directorios. ADIR(<masc> [,<array1> [,<array2> [,<array3> [,<array4> [,<array5>]]]]]) <masc> - mscaras posibles en DOS (*/?) o nombre fichero. <array1> - Es la tabla que se rellenar con los nombres de ficheros reseados en <masc>. Tipo C. <array2> - Idem. para tamao en bytes de fichero. Tipo N. <array3> - Idem. para fechas. Tipo D <array4> - Idem. para horas. Tipo C <array5> - Idem. para atributos. Tipo C

628

Atributos: A - Fichero archivo D - Directorio H - Oculto R - Slo Lectura S - Sistema Ejemplo_1: fil_prg = ADIR("*.PRG") Ejemplo_2: DECLARE TABLA[ADIR("*.PRG")] Ejemplo_3: fil_sec = ADIR("*.sec") DECLARE NOMBRE[fil_sec],FECHA[fil_sec] ADIR("*.sec",NOMBRE,"",FECHA) FOR n=1 TO fil_sec fil_del = NOMBRE[n] IF FECHA[n] < CTOD("01/01/92") DELETE FILE &fil_del ENDIF NEXT 8 Estructura. La estructura de una base de datos puede conocerse mediante la funcin AFIELDS. Los nombres de campos, tipo, longitud, etc. pueden almacenarse en tablas para el posterior tratamiento. AFIELDS(<array1> [,<array2> [,<array3> [,<array4> ]]]]) <array1> - Tabla que contendr nombre de campos. <array2> - Tabla que contendr tipo de campos. <array3> - Tabla que contendr longitud de campos. <array4> - Tabla que contendr nmero posiciones decima les. Ejemplo_1: USE base num_cam = FCOUNT() DECLARE NOMBRE[num_cam],TIPO[num_cam] AFIELDS(NOMBRE,TIPO) FOR n=1 TO num_cam ? NOMBRE[n] ? TIPO[n] NEXT 9 Men. ACHOICE es una funcin que permite generar un men de persiana con los elementos de una tabla en las posiciones de pantalla que se indiquen. Devuelve un valor de tipo numrico que se corresponde con el nmero de posicin del elemento seleccionado. Si el valor es 0 no se seleccion ningn elemento. ACHOICE(<fila sup>,<colizq>,<fila inf>,<col dcha>,<array1> [,<array2>[,<func>[,<expN1>[,<expN2>]]]])

629

<fila sup>- Coordenada X1 de pantalla <col izq> - Coordenada Y1 de pantalla <fila inf>- Coordenada X2 de pantalla <col dcha>- Coordenada Y2 de pantalla <array1> - tabla que contendr elementos <array2> - tabla que contendr valores lgicos <func> - Funcin de usuario. Pasa 3 parmetros: 1-modalidad: 0 Perodo de inactividad 1 Se intenta sobrepasar el principio 2 Se intenta sobrepasar el final 3 Espera de tecla especfica 4 No se puede escoger una opcin 2-elemento actual de la tabla 3-posicin que ocupa el elemento en la ventana Valores retorno: 0 Suspende seleccin 1 Devuelve elemento cursor 2 Contin#a proceso seleccin 3 Va al elemento cuyo primer carcter corresponde a la ltima tecla oprimida. Ejemplo_1: CLEAR SET SCOREBOARD OFF SET COLOR TO W+/N,,,,BG/N DECLARE MEN[3],LOG[3] MEN[1] = "ALTA " MEN[2] = "BAJA " MEN[3] = "MODIFICACION" LOG[1] = .T. LOG[2] = .T. LOG[3] = .T. clave = SPACE(2) @ 1,01 SAY "Clave: " GET clave PICTURE "XX" READ DO CASE CASE clave = "11" LOG[3] = .F. CASE clave = "22" LOG[2] = .F. OTHERWISE RETURN ENDCASE @ 4,1 TO 8,19 opcion = ACHOICE(5,2,7,18,MEN,LOG) 10 Base Datos DBEDIT visualiza el contenido de una base de datos en pantalla. Es una potente funcin que permite la edicin de los datos sobre una ventana definida en pantalla. DBEDIT([<fila sup>[,<colizq>,[<fila inf>,[<col dcha>]]]] [,<array1>], [,<func>][,<array2]/<expC>] [,<array3]/<expC>][,<array4]/<expC>] [,<array5]/<expC>][,<array6]/<expC>] [,<array7]/<expC>]) <fila sup> <col izq> <fila inf> <col dcha> posiciones. <array1> - Tabla de nombres de los campos. <func> - Funcin de usuario.

630

<array2> - Tabla de modelos de visualizacin. <array3> - Tabla de encabezados de columnas. <array4> - Tabla de separacin de encabezados. <array5> - Tabla de separacin de columnas. <array6> - Tabla de separacin de pies. <array7> - Tabla de pies. Cuando se utiliza una funcin de usuario, DBEDIT() pasa de forma automtica dos parmetros: 1-Estado actual de DBEDIT() dependiendo de la ltima tecla pulsada antes de llamar a la funcin. Las diferentes modalidades del estado son: 0 Inactividad 1 Se ha intentado sobrepasar el primer 2 Se ha intentado sobrepasar el ltimo registro 3 El fichero de datos se encuentra vacio 4 Se ha pulsado una tecla especfica 2-Posicin que ocupa en la tabla el campo sobre el que nos encontramos posicionados. Valores de retorno: 0 Para salir de DBEDIT() 1 Para continuar la ejecucin de DBEDIT() 2 Se vuelven a leer los datos nuevamente y se contina DBEDIT() 3 Se activa la posibilidad de aadir nuevos registros Ejemplo_1: DECLARE TAB1[3],TAB2[3],TAB3[3],TAB4[3],TAB5[3],TAB6[3],TAB7[3] * Nombre campos TAB1[1]= "BAS_LOC" TAB1[2]= "BAS_PRO" TAB1[3]= "BAS_HAB" * Mscaras de visualizacin TAB2[1]= "XXXXXXX" TAB2[2]= "XXXXXXX" TAB2[3]= "999,999,999" * Encabezados de columna TAB3[1]= "LOCALIDAD" TAB3[2]= "PROVINCIA" TAB3[3]= "HABITANTES" * Separadores de encabezados TAB4[1]= "D" TAB4[2]= "D" TAB4[3]= "D"

631

* Separadores de columnas TAB5[1]= "3" TAB5[2]= "3" TAB5[3]= "3" * Separadores de pies de pgina TAB6[1]= "D" TAB6[2]= "D" TAB6[3]= "D" * Pies de pgina TAB7[1]= "DPIE_1D" TAB7[2]= "DPIE_2D" TAB7[3]= "DPIE_3D" CLEAR USE BASE DBEDIT(1,1,7,40,TAB1,"",TAB2,TAB3,TAB4,TAB5,TAB6,TAB7)

XII. Impresora.
1. Salida. SET DEVICE redirecciona las salidas por pantalla o por impresora. Por defecto es por pantalla. SET DEVICE TO SCREEN/PRINTER Ejemplo_1: PROCEDURE LISTACURSOS IF !ISPRINTER() && Impresora no conectada DO MSGIMPRESORA && Mensaje que conecte INKEY(0) ENDIF SET DEVICE TO PRINT USE CURSOS INDEX CURSOS GO TOP NPAGINA = 1 && Contador de pginas NFILA = 5 && Contador de filas @ 0,0 SAY CHR(15) && Impresin comprimida * CABECERA @ @ @ @ @ @ @ 1,0 SAY "LISTADO CURSOS" 1,115 SAY "PAGINA.: " + LTRIM(STR(NPAGINA)) 2,115 SAY "FECHA..: " + DTOC(DATE()) 3,0 SAY "N" 3,10 SAY "NOMBRE CURSO" 3,90 SAY "PRECIO" 4,0 SAY REPLICATE(CHR(196),132)

632

* FIN CABECERA DO WHILE .NOT. EOF() @ NFILA, 0 SAY RECNO() PICTURE "9999" @ NFILA,10 SAY NOMCURSO @ NFILA,90 SAY PRECIO NFILA = NFILA + 1 IF NFILA = 50 EJECT && Salto de pgina NPAGINA = NPAGINA + 1 * REPETICION DE LA CABECERA NFILA = 5 ENDIF SKIP && Incrementamos registro ENDDO CLOSE EJECT @ 0,0 SAY CHR(18) && Desactivamos comprimido SET DEVICE TO SCREEN RETURN 2. Salto de pgina. EJECT realiza un salto de pgina en la impresora, y pone a cero los valores de la fila y la columna de la impresora. Use SETPRC() si necesita poner a cero los valores internos de fila y columna de la impresora sin enviar un salto de pgina. EJECT (Ver SET DEVICE, se incluye un ejemplo completo) 3. Conocer la situacin del cabezal de impresin. PCOL() devuelve la columna en que se halla el cabezal de impresin. Retorna un nmero entero. Un EJECT (salto de pgina) coloca PCOL() a cero. PCOL() Ejemplo_1: SET DEVICE TO PRINTER @ 10, PCOL() + 10 SAY "BANCO : " + BANCO PROW() Devuelve la fila en que se haya el cabezal de impresin. Un salto de pgina, EJECT, coloca PROW() a cero. PROW()

633

Ejemplo_1: SET DEVICE @ PROW() + @ PROW() + @ PROW() + TO 1, 2, 3, PRINTER 5 SAY "NOMBRE...: " + NOMBRE 5 SAY "DIRECCION: " + DIRECCION 5 SAY "POBLACION: " + POBLACION

4. Conocer si est preparada la impresora. ISPRINTER() Comprueba si la impresora esta lista para imprimir. Devuelve un valor lgico. ISPRINTER() Ejemplo_1: IF !ISPRINTER() && impresora no conectada @ 24,0 SAY "* CONECTE LA IMPRESORA, PULSE TECLA *" INKEY(0) ENDIF 5. Conocer si tiene papel. DOSERROR() determina el error producido por el DOS. Devuelve un valor numrico correspondiente a un error. Para la lista de errores consulte el manual de Nantucket. El error que genera la impresora por falta de papel es el nmero 28. DOSERROR() Ejemplo_1: IF DOSERROR() = 28 ? "Falta papel" ENDIF

XIII. Funciones predefinidas.


1. De bases de datos. ALIAS() Devuelve el alias del rea de trabajo. DELETED() Devuelve el estado de borrado del registro actual. EOF() Indica si se alcanza el final de un archivo. BOF() Indica si se alcanza el principio de un archivo. DBFILTER() Determina la expresin del filtro. FIELDNAME() Devuelve el nombre del campo especificado. HEADER() Determina la longitud de cabecera.

634

RECSIZE() Determina la longitud del registro. FCOUNT() Devuelve el nmero de campos de la base. USED() Determina la base de datos en uso. FOUND() Devuelve verdadero si se encontr registro. RECNO() Devuelve el nmero de registro actual. LASTREC() Devuelve el nmero total de registros. 2. Numricas. ABS() Devuelve el valor absoluto de una expresin. EXP() Calcula la exponencial. INT() Convierte cualquier expresin numrica en entero. LOG() Devuelve el logaritmo natural de un nmero. MIN() Devuelve el valor mnimo de dos nmeros o dos fechas. MAX() Devuelve el valor mximo de dos nmeros o dos fechas. SQRT() Devuelve la raiz cuadrada de un nmero positivo. ROUND() Devuelve el nmero redondeando con la cantidad de decimales especificados. VAL() Convierte una tira de caracteres a un valor numrico. 3. Cadenas. ASC() Devuelve el cdigo ASCII del carcter izquierdo. AT() Devuelve un nmero que indica la posicin de comienzo de una cadena de caracteres dentro de otra. CHR() Devuelve el carcter del cdigo ASCII especificado. EMPTY() Devuelve verdad si la expresin est vacia. ISALPHA() Devuelve verdadero si el primer carcter es alfabtico. ISLOWER() Determina si el carcter ms a la izquierda de la cadena est en minsculas. ISUPPER() Determina si el carcter ms a la derecha de la cadena est en maysculas. LEN() Devuelve el nmero de caracteres que hay en una cadena

635

LEFT() Devuelve el nmero de caracteres especificados desde la izquierda RIGHT() Devuelve el nmero de caracteres especificados desde la derecha. LTRIM() Elimina los espacios de la izquierda de una cadena. REPLICATE() Repite una expresin de caracteres. SPACE() Crea una cadena de espacios. STR() Convierte un valor numrico en cadena. STRTRAN() Busca y reemplaza dentro de una cadena de caracteres. SUBSTR() Extrae una parte especfica de una cadena. TRANSFORM() Devuelve la tira de caracteres con el formato especificado. TRIM() Elimina los espacios de una cadena. 4. Fechas. CDOW() Devuelve el nombre de da de la semana de una fecha. CMONTH() Devuelve el nombre del mes de una fecha. CTOD() Convierte a fecha una cadena DATE() Devuelve la fecha del sistema DAY() Devuelve el nmero de da del mes de una fecha. DOW() Devuelve el nmero que representa el da de la semana de un valor fecha. DTOC() Convierte una fecha a cadena DTOS() Convierte una fecha a cadena tipo ndice. MONTH() Devuelve un nmero que representa el mes. YEAR() Devuelve el valor nmero completo del ao dada una fecha. 5. Hora. SECONDS() Devuelve la hora del sistema como segundos y centsimas. TIME() Devuelve la hora del sistema. SECS() Devuelve hora como segundos y centsimas. TSTGRING() Dada una cantidad de segundos nos devuelve dicha cantidad en formato hora.

636

6. Otras funciones de inters. FILE() Devuelve verdadero si existe el fichero especificado. GETE() Recupera el contenido de una variable de entorno DOS. TYPE() Devuelve el tipo de dato de la variable, expresin o campo. COL() Devuelve la columna actual del cursor. ROW() Devuelve la fila actual del cursor. CURDIR() Determina el directorio actual. DISKSPACE() Determina el nmero de bytes disponible en una unidad. MEMORY() Devuelve el espacio de memoria libre. READVAR() Devuelve el nombre de la variable de un GET/MENU.

XIV. Ordenes SET.


1. Ordenes SET TO... SET ALTERNATE TO <fichero> Crea un fichero de protocolo. SET COLOR TO <expresin> Fija los colores de pantalla. SET DECIMALS TO <expN> Fija el nmero de los decimales a mostrar en los resultados de las funciones numricas y clculos. SET DEFAULT TO <unidad>[:<rutra>] Especifica la unidad y directorio por defecto para la creacin de ficheros. SET DATE AMERICAN/ANSI/BRITISH/ITALIAN/FRENCH/GERMAN Fija el formato de los campos de fecha. AMERICAN mm/dd/aa ANSI aa.mm.dd BRITISH dd/mm/aa ITALIAN dd-mm-aa FREMCH dd/mm/aa GERMAN dd.mm.aa. SET DELIMITERS TO <expr> Especifica los caracteres empleados como delimitadores. SET DEVICE TO SCREEN/PRINTER Dirige el resultado de la instruccin @ al dispositivo elegido. SET FILTER TO <condicin> Hace que la base de datos se vea como si slo contuviese los registros que cumplen la condicin.

637

SET INDEX TO <lista ficheros> Abre el ndice indicado y cierra los anteriores abiertos con la misma base de datos. SET KEY <expN> TO <procedimiento> Asigna a una tecla un procedimiento. SET MARGIN TO <expN> Fija el margen izquierdo de la impresora. SET MESSAGE TO <expN>/CENTER Establece la lnea donde se muestran los mensajes asociados a PROMPT. SET ORDER TO [<expN>] Establece que fichero ndice es el principal. SET PATH TO [<lista de rutas>] Especifica la ruta de bsqueda que Clippersigue en el acceso a ficheros. SET PRINTER TO [<dispositivo>/<fichero>] Determina la salida de la impresora. SET PROCEDURE TO [<fichero>] Activa fichero de procedimientos. 2. Ordenes SET ON/OFF. SET ALTERNATE on/OFF Determina cuando la salida se enva al fichero. SET BELL on/OFF Determina cundo suena la alarma durante la entradade datos. SET CENTURY on/OFF Determina si una fecha debe mostrar los dgitos del siglo o no. SET CONFIRM on/OFF Determina si se requiere pulsar return para cada GET. SET CONSOLE on/off Determina si la ejecucin de los comandos utilizarn la pantalla como salida.. SET CURSOR on/off Muestra u oculta el cursor en la pantalla. SET DELETED on/OFF Oculta/procesa los registros marcados para borrar. SET DELIMITERS on/OFF Determina si se muestran los delimitadores SET ESCAPE ON/off Activa/Desactiva el desvo producido al pulsar la tecla ESC. SET INTENSITY ON/off Muestra los campos de entrada durante los GETs en color o en vdeo inverso. SET PRINT on/OFF Determina si la salida de los comandos @...SAY se mandarn a la impresora. SET SCOREBOARD ON/off Determinan si los mensajes de clipper aparecen en la lnea 0. SET SOFTSEEK on/OFF Permite acceder al registro ms prximo si el buscado no se encuentra. SET UNIQUE on/OFF Determina si slo los registros con clave no repetida aparecern en el ndice.

638

SET WRAP on/OFF Permite el movimiento circular entre opciones de mens.

XV. Redes locales.


1. Bloqueo de registro. RLOCK() Bloquea/desbloquea el registro actual del rea de trabajo en curso. Para utilizar en redes locales. RLOCK() / LOCK() 2. Bloqueo de ficheros. FLOCK() Bloquea/desbloquea un archivo abierto de base de datos dependiendo de su estado anterior. Slo se utiliza en redes locales. FLOCK() 3. Desbloqueo. UNLOCK Desactiva el bloqueo de los archivos o registros bloqueados por el #ltimo usuario. UNLOCK [ALL] ALL - Quita todos los bloqueos en curso de todas las reas de trabajo. 4. Uso exclusivo de ficheros. SET EXCLUSIVE Permite el uso exclusivo o no de archivos de base de datos, ndices y campos memos, en redes locales. Por defecto esta en ON. SET EXCLUSIVE ON/off

639

También podría gustarte