Captulo 1
tomarn en el proceso. Nota: Estos han sido normalizados por el Instituto Norteamericano de Normalizacin (ANSI).
Programacin estructurada
La programacin estructurada signica escribir un programa de acuerdo a las siguientes reglas:
itiva.
El programa tiene un diseo modular. Los mdulos son diseados de modo descendente. Cada modulo se codica utilizando las tres estructuras de control bsicas: secuencial, alternativa y repet-
El termino programacin estructurada se reere a un conjunto de tcnicas que aumentan considerablemente la productividad del programa reduciendo en elevado grado el tiempo requerido para escribir, vericar, depurar y mantener los programas. La programacin estructurada utiliza un nmero limitado de estructuras de control que minimizan la complejidad de los programas y por consiguiente reducen los errores. La programacin estructurada es el conjunto de tcnicas que incorporan:
Recursos abstractos
Descomponer un programa en trminos de recursos abstractos segn Dijkstra consiste en descomponer una determinada accin compleja en trminos de un nmero de acciones ms simples.
Diseo descendente
El diseo descendente es el proceso mediante el cual un problema se descompone en una serie de niveles o pasos sucesivos de renamiento. La metodologa descendente consiste en efectuar una relacin entre las sucesivas etapas de estructuracin, de modo que se relacionen unas con otras mediante entradas y salidas de informacin. Es decir, se descompone el problema en etapas o estructuras jerrquicas de forma que se puede considerar cada estructura desde dos puntos de vista: Qu hace? Y Cmo lo hace?. Si se considera un nivel n de renamiento, las estructuras se consideran de la siguiente manera:
Estructuras bsicas
Cualquier programa con un solo punto de entrada y un solo punto de salida puede resolverse con tres tipos de estructuras bsicas de control: Secuencial, alternativa y repetitiva.
- Secuencial
Es aquella que ejecuta las acciones sucesivamente unas a continuacin de otras sin posibilidad
de omitir ninguna, y naturalmente sin bifurcaciones (saltos a subrutinas). Todas estas estructuras tendrn una entrada y una salida.
- Alternativa
Es aquella en la que nicamente se realiza una alternativa dependiendo del valor de una
determinada condicin o predicado. Las estructuras alternativas tambin llamadas condicionales pueden ser de tres tipos: Simple, doble o mltiple Alternativa simple: Son aquellas en donde la existencia o cumplimiento de la condicin implica la ruptura de la secuencia y la ejecucin de una determinada accin.
Alternativa doble
Es aquella que permite la eleccin entre dos acciones o tratamientos en funcin de que se cumpla o no determinada condicin
Alternativa mltiple
Se adopta cuando la condicin puede tomar n valores enteros distintos. Segn se elija uno de estos valores en la condicin se realizara una de las n acciones. Esta estructura propuesta por Hoare, es la case del lenguaje Pascal o case de los Basic estructurados (Case = estructura de casos).
Repetitiva o iterativa
Son aquellas en las que las acciones se ejecutan un nmero determinado de veces y dependen de un valor predenido o el cumplimiento de una determinada expresin lgica. Un bucle o lazo es el conjunto de acciones a repetir. En consecuencia es preciso disponer de estructuras algortmicas que permitan describir una iteracin de forma cmoda. Las tres estructuras mas usuales dependiendo de que la condicin se encuentre al principio o al nal de la iteracin son: Estructura mientras, repetir hasta y estructura para.
veces hasta que la condicin no se cumpla. Esta condicin se ejecuta al menos una vez. A esta estructura tambin
Estructura Para
esta se cumple se ejecuta el cuerpo del ciclo y posteriormente se incrementa la variable de control, lo anterior se repite hasta que la condicin no se cumpla.
Secuencia Programa
Se le llama programa a la serie de instrucciones escritas en alguno de los lenguajes, por medio de los
cuales se logra que la computadora realice todas las operaciones o decisiones sealadas en dichas instrucciones. Podemos distinguir dos tipos de programa: Programa fuente: es el conjunto de instrucciones escritas en algn lenguaje de computadora, las cuales han sido transcritos para ser interpretadas por algn dispositivo de lectura. Programa objeto: es el conjunto de instrucciones que componen un programa fuente y que han sido traducidas al lenguaje maquina por medio del compilador correspondiente.
diente se requiere para cada lenguaje de programacin. ste efecta slo la traduccin, no ejecuta el programa. Una vez compilado el programa, el resultado en forma de programa objeto ser directamente ejecutable.
Pseudocdigo
pseudocdigo no es realmente un cdigo sino una imitacin y una versin abreviada de instrucciones reales para las computadoras.
Programa Entorno
Mayor2Numeros;
numero1,numero2;
numero2;
10
11
Entero numero;
numero;
Escribe Fin Si
si;
Algoritmo que lea dos nmeros por teclado y muestre un mensaje por pantalla indicando si el cuadrado el primero es mayor que el cubo del segundo. Programa Entorno Entero Entero Inicio Escribe Lee Lee Escribe
Introduce el primer nmero ; num1, num2; // Ns leidos por teclado cuadrado, cubo; // Vars. Temporales CoparaConOperaciones;
num2; = n1 * n1;
cuadrado cubo Si
= n2 * n2 * n2;
12
Algoritmo que lean un nmero entero entre 1 y 10, y nos muestre por pantalla el nmero en letra (el literal). Si el nmero ledo no esta comprendido entre 1 y 10 mostrar dicha incidencia. Programa Entorno Entero Inicio
n; Escribe Introduce un nmero: ; NumeroALiteral;
Lee n; /* Aunque se cumpla una condicin el ordenador debe evaluarlas todas, con el coste de rendimiento que
esto representa */
Si
n = 1 entonces
Escribe
Uno ;
Fin si; Si n = 2 entonces Escribe Dos ; Fin si; Si n = 3 entonces Escribe Tres ; Fin si; Si n = 4 entonces Escribe Cuatro ; Fin si; Si n = 5 entonces Escribe Cinco ; Fin si; Si n = 6 entonces Escribe Seis ; Fin si; Si n = 7 entonces Escribe Siete ; Fin si; Si n = 8 entonces Escribe Ocho ; Fin si; Si n = 9 entonces Escribe Nueve ; Fin si; Si n = 10 entonces Escribe Diez ; Fin si; /* Comprobamos si el nmero no est entre 1 y 10 para mostrar mensaje */ Si n < 1 o n> 10 entonces Escribe El nmero no est entre 1 y 10 ; Fin si; Fin;
13
Realizar un algoritmo que lea tres nmeros por teclado y nos indique cual de ellos es el mayor.
Entero n1, n2, n3; // Ns leidos por teclado
14
Fin; Algoritmo que lee una calicacin numrica entre 0 y 10 y la transforma en calicacin alfabtica, escribiendo el resultado
0<=Nota<3 3<=Nota<5 5<=Nota<6 6<=Nota<7 7<=Nota<9 9<=Nota<=10 MD INS SUF BIE NOT SOB
NumeroALiteral;
Escribe Introduce un nmero: ; Lee n; /* No sangraremos todas las sentencias incluidas dentro del Sino pues no hay espacio en el papel
y adems est forma es ms clara. No hay que olvidar poner tantos Fin si como Si hayamos abierto */
Si
n = 1 entonces
Escribe Sino Sino Sino Sino Sino Sino Sino Sino Escribe Escribe Escribe Escribe Escribe Escribe Escribe Escribe
Uno ;
Si n = 2 entonces Dos ;
Si n = 3 entonces Tres ;
Si n = 4 entonces Cuatro ;
Si n = 5 entonces Cinco ;
Si n = 6 entonces Seis ;
Si n = 7 entonces Siete ;
Si n = 8 entonces Ocho ;
Si n = 9 entonces Nueve ;
15
Escribe
Diez ;
Sino /* El nmero no est entre 1 y 10 para mostrar mensaje */ Escribe El nmero no est entre 1 y 10 ; Fin si; Fin Si; Fin Si; . . . Fin Si; /* A diferencia de la implementacin del ejercicio 3 este algoritmo
evala el resto innecesariamente */
Fin; Algoritmo que lee tres nmeros cualesquiera y nos indica todas sus relaciones de igualdad Programa Entorno Entero Inicio Escribe Introduce tres nmeros: ; Lee n1, n2, n3; Si n1 = n2 y n1 = n3 entonces Escribe Los tres nmeros son iguales ; Sino Si n1 = n3 entonces Escribe El 1 y el 2 son iguales ; Sino Si n2 = n3 entonces Escribe El 2 y el 3 son iguales ; Sino Escribe Todos son distintos ; Fin si; Fin si; Fin si; Fin; Algoritmo que recibe como datos de entrada una hora expresada en horas, minutos y segundos que nos calcula y escribe la hora, minutos y segundos que sern transcurrido un segundo. Programa Entorno Entero Inicio Escribe Introduce hora: ; Lee hora;
hora, min, seg; UnSegundoDespues; n1, n2, n3; RelacionesIgualdad;
16
Fin; Algoritmo que lee como dato de entrada un ao y nos dice si se trata de un ao bisiesto o no. Se sabe que son bisiestos todos los aos mltiplos de 4, excepto los que sean mltiplos de 100 sin ser mltiplos de 400.
Programa EsBisiesto;
Lee anyo; Si (anyo % 4 = 0) y No ( anyo % 100 = 0 y no ( anyo % 400 =0) ) entonces Escribe El ao , anyo , es bisiesto ; Sino Escribe El ao , anyo , NO es bisiesto ; Fin si; Fin; En un determinado comercio se realiza un descuento dependiendo del precio de cada producto. Si el precio es inferior a S/. 6 , no se hace descuento; si es mayor o igual a S/. 6 y menor que S/.60 se hace un 5 % de desceunto, y si es mayor o igual a S/. 60 se hace un 10 % de descuento. Realizar el algoritmo que lee el precio de un producto y nos calcula y escribe su precio nal. Programa Entorno Entero Inicio Escribe Introduce precio Inicial: ; Lee PrecioInicial;
PrecioInicial, Dto; Calculo_Precio_Final;
17
Dto
= 0;
Sino Si PresioInical < 60 entonces Dto = 5; Sino Dto = 10; Fin si; Escibe Fin; Algoritmo que lee el precio nal pagado por un producto y su precio de tarifa y nos calcula el porcentaje de descuento que le ha sido aplicado. Programa Entorno Inicio Escribe Introduce precio Tarifa: ; Lee PrecioTarifa; Escribe Introduce precio Final: ; Lee PrecioFinal; /* La dicultad de este problema es
plejidad algortmica */ Calculo_Descuento; Entero PrecioFinal, PrecioTarifa; Precio nal = , PrecioInicial*(1 + Dto/100), Descuento = , Dto, % ;
Dto = (1-PrecioFinal/PrecioTarifa)*100; Escibe Descuento = , Dto, % ; Fin; Algoritmo que lee 3 nmeros distintos y nos dice cual de ellos es el menor. Programa Entorno Inicio Escribe Introduce 3 nmeros ; Lee n1, n2, n3; Si n1=n2 o n1=n3 o n2=n3 entonces Escribe Los nmeros no son distintos ; Sino Si n1 < n2 entonces // El menor ser n1 o n3 Si n1 < n3 entonces Escribe El menor es , n1; Sino // n1 >= n3 Escribe El menor es , n3; Fin si;
NumeroMenor; Entero n1, n2, n3; // Ns leidos por teclado
18
Fin 12.4 Algoritmo que lee como datos de entrada una fecha expresada en da (1..31), mes (1..12, y ao y nos dice la fecha que ser el da siguiente. Se supondr que febrero tiene siempre 28 das. Programa Entorno Entero Entero Inicio Escribe Introduce Dia: ; Lee dia; Escribe Introduce Mes: ; Lee mes; Escribe Introduce Ao: ; Lee Ao; Si mes = 2 entonces numDiasMes = 28; Sino Si mes=4 o mes=6 o mes=9 o mes=11 entonces // Meses de 30 das: Abrir, Junio, Sept. Nov. NumDiasMes = 30; Sino // El resto tiene 31 das NumDiasMes = 31; Fin si; Fin si; Si dia > numDiasMes entonces mes = mes + 1; dia = 1; Si mes > 12 entonces anyo = anyo + 1; mes = 1; Fin si; Fin si; Escribe Un da despus es: , dia, / , mes, / , anyo; Fin;
dia, mes, anyo; numDiasMes; UnDiaDespues;
19
numDiasMes;
= 29;
= 28;
mes=4 o mes=6 o mes=9 o mes=11 entonces // Meses de 30 das: Abrir, Junio, Sept. Nov.
NumDiasMes = 30; Sino // El resto tiene 31 das NumDiasMes = 31; Fin si; Fin si; // Filtramos si la fecha es correcta Si dia>=1 y dia<=numDiasMes y Mes >= 1 y mes <= 12 entonces Si
dia > numDiasMes entonces
mes = mes + 1; dia = 1; Si mes > 12 entonces anyo = anyo + 1; mes = 1; Fin si; Fin si; Escribe Sino Escribe Fin Fin;
Si; La fecha es incorrecta ; Un da despus es: , dia, / , mes, / , anyo;
20
Algoritmo que lee dos nmeros enteros, positivos y distintos y nos dice si el mayor es mltiplo del menor o, lo que es lo mismo, si el menor es divisor del mayor. Programa Entorno Entero Entero Inicio Escribe Introduce 2 nmeros ; Lee n1, n2; // Buscamos el mayor y el menor Si n1 > n2 entonces mayor = n1; menor = n2; Sino mayor = n2; menor = n1; Fin si; Si mayor % menor = 0 entonces Escribe Mayor, es mltiplo de , menor; Sino Escribe No existe ninguna relacin entre los nmeros ; Fin si; Fin Algoritmo que calcula la edad de una persona recibiendo como datos de entrada la fecha de nacimiento, y la fecha actual, ambas en tres variables. Programa Entorno Entero Entero Entero Inicio Escribe Introduce Fecha nacimiento: ; Lee d_n, m_n, a_n; Escribe Fecha actual: ; Lee d_a, m_a, a_a; /* La edad de una persona ser igual al ao
d_n, m_m, a_n; // Fecha de nacimiento d_a, m_a, a_a; // Fecha de actual Edad; // variable temporal EdadEnAnyos; n1, n2; // Ns ledos por teclado mayor, menor; // Variables temporales EsDivisorElMenor;
Edad = a_a a_n; /*Comprobamos si ya ha cumplido los aos o no */ Si m_a < m_n o (m_a = m_n y dia_a < d_n) entonces // Aun no ha cumplido los aos este ao Edad = Edad - 1; Fin si; Escribe Edad = , Edad; Fin;
21
n<100 hacer
ParesMenoresQue100;
n = 1; Mientras Si
n<100 hacer
n % 2 = 0 entonces
n;
Realizar un algoritmo que muestre por pantalla los nmeros mltiplos de 5 menores que 10000. Programa Entorno Entero Inicio n = 5; Mientras
n; MultDe5Menor1000;
n<1000 hacer
22
16.3 Algoritmo que lea un nmero por teclado e imprima por pantalla todos los nmeros positivos menores que N Programa Entorno Entero Entero Inicio Escribe Introduce un nmero: ; Lee n; i = 1; Mientras i < n hacer Escribe i; i = i + 1; Fin Mientras; Fin
Otra forma n; i; // Contador ListaNumeros;
ListaNumeros;
Entero n;
Escribe Introduce un nmero: ; Lee n; Mientras n>1 hacer n = n - 1; Escribe n; Fin Mientras; Fin Algoritmo que lee una secuencia de 10 nmeros y obtiene e imprime cuantos hay positivos, negativos y nulos. Programa Entorno Entero Entero Entero Inicio nPos=0; nNeg=0; nNulos=0; i = 0; Mientras i<10 hacer
n; i; // Variable control del bucle nPos, nNeg, nNulos; // Contadores CuentaNumeros;
23
Algoritmo que lee una secuencia de nmeros no nulos, terminada con la introduccin de un 0, y obtiene e imprime el mayor, visualizando un mensaje de si ha ledo algn nmero negativo. Programa Entorno Entero Entero Inicio hayNegativos = false; // suponemos que no hay negativos /* El menor de los posibles, cualquier nmero de los ledos por teclado ser mayor que este */ mayor = -9999999; hacer Escribe Introduce un nmero: ; Lee n; Si n <> 0 entonces // Procesamos el nmero ledo Si n < 0 entonces hayNegativos = true; // Se ha encontrado un negativo FinSi; Si n > mayor entonces /* Se ha encontrado un nmero mayor que el actual, lo seleccionamos como mayor */ mayor = n Fin si; Fin si; Mientras n <> 0; Escribe El mayor es : , mayor; Si hay Negativos entones
n; mayor; // Guarda el valor del mayor hayNegativos; // switch CuentaNumeros;
Booleano
24
Fin
Otra forma, no presuponemos ningn nmero como mayor
CuentaNumeros;
hayNegativos = false; esPrimeraVez = true; hacer Escribe Introduce un nmero: ; Lee n; Si n <> 0 entonces // Procesamos el nmero ledo Si n < 0 entonces hayNegativos = true; // Se ha encontrado un negativo FinSi; Si esPrimeraVez o n > mayor entonces /* Se ha encontrado un nmero mayor
cionamos como mayor */
= false;
25
Algoritmo que lee un nmero entero positivo N y calcula e imprime el factorial de N!. Si lee un nmero negativo indica con un mensaje que no es posible la operacin. Programa Entorno Entero Entero Entero Inicio Escribe Introduce un nmero: ; Lee n; Si n < 0 entonces Escribe No existe factorial de un nmero negativo ; Sino resultado = 1; // Inicializamos el acumulador i = 1; Mientras i < n hacer resultado = resultado * i; Fin Mientras; Escribe El factorial de , n, es , resultado; Fin si; Fin Algoritmo que lee un nmero X y otro entero positivo N y calcula la N-sima potencia de X (sin utilizar el operador de potencia). Programa Entorno Entero Entero Entero Inicio Escribe Introduce un nmero: ; Lee x; Escribe Introduce la potencia: ; Lee n; Si n < 0 entonces Escribe No se calcular potencias negativas ; Sino resultado = 1; // Inicializamos el acumulador i = 1; Mientras i < n hacer resultado = resultado * x; Fin Mientras; Escribe x, elevado a , n, es , resultado; Fin si; Fin
x, n; i; // Contador resultado; Potencia; n; i; // Contador resultado; Factorial;
26
Algoritmo que obtenga el producto de dos nmeros enteros positivos mediante sumas sucesivas.
Programa Entorno
Producto;
Introduce un nmero 1: ;
Escribe
n2;
Introduce un nmero 2: ;
Debemos considerar el caso de que alguno de los nmeros sea negativo, lo mejor es poner los dos nmeros en positivo y luego considerar el signo. Vamos a evitar usar el operador de producto * /
resultadoNeg // Si
n1 Finsi Si
= -n1;
n2 < 0 entonces
n2 Finsi
= -n2;
resultado // i
= 1;
= 0;
Inicializamos el acumulador
Mientras Fin Si
resultado
Mientras;
resultadoNeg entonces
Escribe
si;
27
Algoritmos que obtenga el cociente y el resto de dos nmeros enteros positivos mediante restas. Programa Entorno Entero dividendo, divisor; Entero resto, cociente; Booleano resultadoNeg; Inicio Escribe Introduce dividendo: ; Lee dividendo; Escribe Introduce divisor: ; Lee divisor; /* Debemos considerar el caso de
Division;
que alguno de los nmeros sea negativo, lo mejor es poner los dos
nmeros en positivo y luego considerar el signo. Vamos a evitar usar el operador de producto * */
resultadoNeg = (dividendo < 0 o divisor < 0) y no (dividendo < 0 y divisor < 0); // Convertimos los nmeros a positivo Si dividendo < 0 entonces dividendo = - dividendo; Finsi Si divisor < 0 entonces divisor = - divisor; Finsi cociente = 0; // Inicializamos el acumulador resto = dividendo; Mientras divisor < resto hacer resto = resto = divisor; cociente = cociente + 1; Fin Mientras; Si resultadoNeg entonces cociente = -cociente; // Cambiamos de signo Fin si; Escribe dividendo, entre , divisor, da , cociente , con resto , resto; Fin Algoritmo que lee tres nmeros A, B, C, y los imprime en orden creciente e indica si fueron introducidos en ese orden. /*
Este problema es casi idntico a uno resulto con anterioridad */ NumerosOrdenAscendente;
Programa Entorno
Entero
28
Inicio
Fin; Algoritmo que lea un nmero por teclado y nos diga si es primo. /*
Un nmero es primo cuando solo es divisible por si mismo y por el 1. Para saber si un nmero es primo debemos dividirlo sucesivamente por sus posibles divisores (nmeros menor que l) y si encontramos algn divisor diremos que el nmero NO es primo, es caso contrario ser un nmero primo. */
Programa Entorno
NumeroPrimo;
Entero n, cont; Booleano esPrimo; // Variable que indicar si es primo o no Inicio Escribe Introduce un nmero: ; Lee n; esPrimo = true; // suponemos que el nmero es primo cont = 2; // Empezamos dividiendo por 2 /* Iremos dividiendo el nmero por sus posibles divisores hasta que encontremos un divisor, y por tanto
el nmero deje de ser primo o hasta que acabemos la lista de posibles divisores (los menores que l) */
29
Algoritmo que genera la lista de los N primeros nmeros primos, siendo N el dato de entrada. /*
Para resolver este algoritmo es necesario realizar una aproximacin descendente. Partiendo del hecho de que ya sabemos calcular si un nmero es o no primo, damos por supuesta esta operacin e intentamos resolver el problema. La aproximacin primera sera
Lee i
n;
= 1; // Contador que avanza por los nmeros = 0; // N de primos que hemos visualizado i < n hacer
nPrimos mientras Si
Fijarse que est fuera del si, se incrementa siempre independiente de que el nmero i sea primo o no */
= i + 1; Mientras;
Fin
Una vez tenemos la 1 aproximacin tan solo tenemos que introducir el cdigo que nos indica si un nmero es primo donde proceda. Cuando veamos funciones, veremos que est tarea es trivial. */
Programa Entorno
NPrimerosPrimos;
// Variables utilizadas para ver si el nmero i es primo Entero i, cont; Booleano esPrimo; // Variable que indicar si es primo o no // Variables utilizadas para ir probando los nmeros Entero nPrimos; Entero n;
30
Inicio Escribe Introduce un nmero: ; Lee n; i = 1; // Contador que avanza por los nmeros nPrimos = 0; // N de primos que hemos visualizado mientras i < n hacer /* Si i es un numero primo entonces */ esPrimo = true; // suponemos que el nmero es primo cont = 2; // Empezamos dividiendo por 2 Mientras cont < i y esPrimo hacer // Mientras cont < i y esPrimo=true hacer Hace los mismo Si n % cont entonces esPrimo = false; // Se ha encontrado un divisor Fin si; Fin mientras; Si esPrimo entonces Escribe i; nPrimos = nPrimos + 1; Fin Si; /* Fijarse que est fuera del si, se incrementa siempre independiente
o no */
i Fin Fin;
= i + 1;
Mientras;
Algoritmo lea un nmero N y muestre por pantalla los nmeros primos menores que N. /*
Para resolver este algoritmo es necesario realizar una aproximacin descendente. Partiendo del hecho de que ya sabemos calcular si un nmero es o no primo, damos por supuesta esta operacin e intentamos resolver el problema. La aproximacin primera sera
Lee
Para
Si
i;
Una vez tenemos la 1 aproximacin tan solo tenemos que introducir el cdigo que nos indica si un nmero es primo donde proceda. Cuando veamos funciones, veremos que est tarea es trivial. Este algoritmo a diferencia del anterior saca los nmeros primos menores que N, mientras que el otro sacaba los N primeros primos. La diferencia es la siguiente, para N=5 N primeros : 1,2,3,5,7 < N : 1,2,3,5 */
31
Algoritmo que calcula e imprime los nmeros perfectos menores que 1000. (Un nmero es perfecto si la suma de sus divisores, excepto l mismo, es igual al propio nmero.). /*
Para resolver este algoritmo es necesario realizar una aproximacin descendente. Damos por supuesta la operacin es numero perfecto que ms adelante desarrollaremos La aproximacin primera sera
Para
Si
n;
sumaDivisores Para Si
= 0;
sumaDivisores
= sumaDivisores + n;
32
Fin Si
sumaDivisores = n entonces
// Fin De */
si;
N es un nmero perfecto
Programa Entorno
NumerosPerfectos;
Entero Inicio
n i, sumaDivisores;
Para n=1 hasta 999 hacer /* Si n es numero perfecto entonces */ sumaDivisores = 0; Para i=1 hasta n-1 hacer Si n % i = 0 entonces sumaDivisores = sumaDivisores + n; Fin si; Fin Para Si sumaDivisores = n entonces // N es un nmero perfecto Escribe n; Fin Si; Fin Para; Fin; Algoritmo que evala un polinomio de grado N. Los datos de entrada son el valor de la variable y los coecientes. Programa Entorno Entero Entero Inicio
coef, grado, x, i; valorPol; EvaluaPolinomio;
Lee grado; Escribe Valor de X: ; Lee x; // Acumulador en el que guardaremos el valor del polinomio valorPol = 0; Para i=x hasta 0 incremento -1 Escribe Introduce coeciente de grado ( , i, ): ; Lee coef; valorPol = valorPol + coef * x ^ i; Fin para; Escribe El valor del polinomio es: , valorPol; Fin
33
Entero n, i;
Entorno Inicio
i;
Algoritmo que lee un nmero entero y positivo y calcula y escribe la suma de sus divisores. Programa Entorno
SumaDivisores;
Entero n, i; suma;
Entero Inicio
Suma Fin
Si;
= suma + i;
34
Algotimo que lee un nmero entero y positivo N y escribe los N primeros trminos de la sucesin de Fibonacci. La sucesin se caracteriza porque cada trmino es igual a la suma de sus dos anteriores, dandose por denicin el primero (0) y el segundo (1).
a1 = 0 a2= 1 an = an-1 + an-2
Programa Entorno
Fibonacci;
Escribe Introduce un nmero: ; Lee n; // Casos base, triviales Si n > 0 entonces Escribe 0 ; Fin si; Si n > 1 entonces Escribe 0 ; Fin si; // Comenzamos bucle an_1 = 0; an = 1; /* Los dos casos anteriores ya se han visualizado, por eso empezamos en 3 */ Para cont=3 hasta n Suma = an + an_1; // Calculamos el trmino siguiente Escribe suma; // Los dos ltimos trminos avanzan una posicin an_1 = an; an = suma; Fin Para; Fin
Captulo 2
main() {
35
36
/*este }
TIPOS DE DATOS
Cuando usamos un programa es muy importante manejar datos. En C podemos almacenar los datos en variables. El contenido de las variables se puede ver o cambiar en cualquier momento. Estas variables pueden ser de distintos tipos dependiendo del tipo de dato que queramos guardar en ellas. No es lo mismo guardar un nombre que un nmero. Hay que recordar tambin que la memoria del ordenador es limitada, as que cuando guardamos un dato, debemos usar slo la memoria necesaria Identicadores Son nombres dados a constantes, variables y funciones. El identicador esta formado por una secuencia de una o ms letras, dgitos y el caracter de subrayado. El primer caracter de un identicador siempre debe ser una letra o el caracter de subrayado, en estos identicadores (nombres de variables) se debe tener gran cuidado, ya que el C hace diferencia entre maysculas y minsculas. Por ejemplo, Max, max y MAX son tres identicadores diferentes. Constantes Las constantes son un valor que una vez jado por el compilador, no cambia durante la ejecucin del programa. El C indica al programa que se trata de una constante usando la directiva #dene. Por ejemplo. #dene pi 3.1416 Esta linea dene a la variable pi como una constante con el valor 3.1416, este valor de las constantes puede ser un numero o bien una cadena de caracteres.
Tipos de Datos
Tipo Int
En una variable de este tipo se almacenan nmeros enteros (sin decimales). El rango de valores que admite es -32767 a 32767. Cuando denimos una variable lo que estamos haciendo es decirle al compilador que nos reserve una zona de la memoria para almacenar datos de tipo int. Hay que decirle al compilador que queremos crear una variable y hay que indicarle de qu tipo. Si esta declaracin se hace antes del main(), esto indica que la variable ser una variable global, o sea que se usar en todo el main() y en el resto de funciones. Mientras que si se declara dentro del main(), esto indica que la variable ser una variable local, o sea, slo para la funcin main(). Por ejemplo. int valor; main()... Esta expresin declara una variable global de nombre "valor" y de tipo entero. main() { oat valor; } Esta expresin declara una variable local de nombre "valor" y de tipo oat (coma otante)
Tipo Char
Las variables de tipo char sirven para almacenar caracteres. Los caracteres se almacenan en realidad como nmeros del 0 al 255. Los 128 primeros (0 a 127) son el ASCII estndar. El resto es el ASCII extendido y depende del idioma y del ordenador. Para declarar una variable char solo escribimos: "char nombre;" y listo, ya existe una variable "nombre" que almacenar datos de tipo caracter.
Tipo Float
En este tipo de variable podemos almacenar nmeros decimales, no slo enteros como en los anteriores. El rango de posibles valores es del 3.4 E-38 al 3.4 E+38. Para declarar un oat, de forma similar, escribimos: "oat prom;" y con esto hemos declarado una variable de nombre "prom" que tendr un dato de tipo oat o coma otante.
37
Tipo double
En las variables tipo double se almacenan nmeros reales, estos tiene el rango de 1.79769 E-308 a 1.79769 E+308. Al igual que las anteriores para declararla escribimos: "double num;" y tenemos una variable de nombre "num" que aceptar datos tipo double. Modicador Unsigned Este modicador (que signica sin signo) modica el rango de valores que puede contener una variable. Slo admite valores positivos. Este modicador se usa as: "unsigned int total;" esto declara un variable de nombre "total", de tipo entero positivo.
scanf
Al utilizar la palabra scanf, nosotros podemos hacer que el usuario introduzca datos por el teclado. El uso de scanf es de la siguiente manera: "scanf(" %i", &num);" esta ltima expresin indica al programa que se debe solicitar un nmero entero ( %i, es una especicacin de formato y esto lo puedes ver en la seccin de operadores y manipuladores), luego se indica el nombre de la variable en la que se va a guardar el dato, ste (el nombre) precedido por el signo &, que indica al programa la direccin de sta variable.
38
printf
De esta manera podemos imprimir en pantalla, ya sea un mensaje o el valor contenido en una variable. La sintaxis de printf es as: "printf(" %d", num);" esta sentencia lo que hace es imprimir el valor de la variable num en el lugar donde aparece el " %d". El printf nos sirve tambin para mandar a imprimir en pantalla un mensaje as: "printf("Bienvenidos al mundo de C")" esto impirmir en pantalla: Bienvenidos al mundo de C Nota: Para hacer uso de estas funciones, es necesario incluir la directiva o archivo de cabecera stdio.h, esto se hace de la siguiente manera: "#include ", as queda incluida ste archivo y se podr proceder con el programa. Ahora haremos uso de lo que hemos aprendido para hacer un programa.
# {
include stdio.h
main()
int x; printf("Bienvenidos al mundo de C\n"); printf("Introduzca un valor entero "); scanf(" %i", &x); printf("\nEl valor que introdujo fue %i",x); return 0; }
Ahora explicaremos lo que hace: Primero se declara una variable de tipo entero y de nombre x. Despus se manda a imprimir en pantalla el mensaje "Bienvenidos al mundo de C", el "\n" que sigue es para dar un salto de lnea en el programa, si no lo pusieramos el siguiente mensaje aparecera pegado al anterior. Luego se imprime "Introduzca un valor entero", para que el usuario pueda introducir un valor por medio del teclado y se manda a guardar este valor a la variable antes declarada. Despues se imprime un mensaje y el valor de la variable. Como pueden ver, no es tan difcil. La expresin "return 0" es una instruccin. Como vern, toda funcin debe retornar un valor, como no queremos que nuestra funcin main devuelva nada, entonces le decimos que retorne 0. La salida del programa anterior sera: Bienvenidos al mundo de C Introduzca un valor entero 25 (por ejemplo 25) El valor que introdujo fue 25
getch y getche
Si lo que queremos es que el usuario introduzca un caracter por el teclado usamos las funciones getch y getche. Estas esperan a que el usuario introduzca un carcter por el teclado. La diferencia entre getche y getch es que la primera saca por pantalla la tecla que hemos pulsado y la segunda no. Por ejemplo.
# int {
char letra; printf( "Introduzca una letra: " ); letra = getche(); printf( "\nLa letra que introdujo es: %c", letra ); return 0; }
39
La salida del programa anterior sera: Introduzca una letra:N La letra que introdujo es: N Ahora hagamos el mismo programa con getch():
# int {
char letra; printf( "Introduzca una letra: " ); letra = getch(); printf( "\nLa letra que introdujo es: %c", letra ); return 0; }
La salida del programa anterior sera: Introduzca una letra: La letra que introdujo es: N Se debe tener en cuenta, aunque no se haga en estos ejemplos, que el archivo de cabecera stdio.h debe estar entre los signos <>, de lo contrario se producira un error en el programa.
OPERADORES (MANIPULADORES)
Los operadores son smbolos que indican como son manipulados los datos. Estos operadores se pueden clasicar en: aritmticos, lgicos, relacionales, de asignacin, de manejo de bits y otros.
Operadores de Asignacin
Estos operadores son los que nos sirven para darle (asignarle) un valor a una variable. Este valor lo podemos teclear manualmente o bien puede ser el valor de otra variable. Por ejemplo. x=5; /*Esto mete un valor directamente*/ x=a; /*Esto asigna el valor de otra variable*/
Operadores Aritmticos
Los operadores aritmticos son aquellos que sirven para realizar operaciones tales como suma, resta, divisin y multiplicacin. Con ellos podemos calcular el resto o mdulo de una divisin entera, o bien podemos incrementar o disminuir el valor de las variables. Por ejemplo.
# {
include stdio.h
main() int x=7; int y; int z=2; /*Como vern es posible declarar e iniciar la variable al mismo tiempo*/ printf("El valor de x es: %i\n", x); y=x+z; /*Asigna el valor de una suma*/ x++; /*Incrementa el valor de x en uno*/ printf("Los valores de x, y, z son %i, %i, %i", x,y,z); return 0; }
La salida a este programa sera: El valor de x es: 7 Los valores de x, y, z son 8, 9, 2
40
Operadores Lgicos
son los que nos permitirn decidir en una sentencia condicional que veremos ms adelante. Por ejemplo. x<7&&t==4; /*Esta lnea devuelve 1, si x es menor que 7 "y" t es igual a 4*/
Operadores Binarios
Estos permiten ver el contenido de las variables como valores binarios. Utiles en el acceso a bits individuales en memoria, tales como la memoria de pantalla para mostrar grcos. Las funciones de estos operadores se describen en la tabla, y se ocupan similarmente en los programas.
Operador Sizeof
Existe un operador muy utilizado que es el sizeof. Este operador nos devuelve el tamao en bytes de una variable. De esta manera no tenemos que preocuparnos en recordar o calcular cuanto ocupa. Adems el tamao de una variable cambia de un compilador a otro, es la mejor forma de asegurarse. Se usa poniendo el nombre de la variable despus de sizeof y separado de un espacio.
Por int {
ejemplo. main()
OPERADORES DE ASIGNACION
Operador + * / % ++ Operacin Suma dos numeros, pueden ser enteros o reales Resta dos numeros, pueden ser enteros o reales Multiplica dos numeros, pueden ser enteros o reales Divisin, el resultado depende de los tipos de datos Mdulo o resto de una divisin entera, los operandos tienen que ser enteros Incrementa en uno la variable Decrementa en uno la variable
OPERADORES LOGICOS
Operador && || ! Operacin AND , en espaol Y. Da como resultado el valor lgico 1, si ambos operadores son distintos de cero OR, en espaol O. El resultado es cero, si ambos operandos son cero NOT, en espaol NO. El resultado es cero si el operando tiene un valor distinto de cero
OPERADORES DE RELACION
Operador < > <= >= == != Operacin Primer operando menor que el segundo Primer operando mayour que el segundo Primer operando menor o igual que el segundo Primer operando mayor o igual que el segundo Primer operando igual que el segundo Primer operando distinto del segundo
41
OPERADORES BINARIOS
Operador & ! ^ ~ >> << Operacin AND, compara los bits dos a dos, si ambos son 1 el resultado es 1 OR, compara los bits dos a dos, si alguno de ellos es 1, se obtien 1 XOR, compara los bits dos a dos, si son distintos el resultado es 1 Complemento, convierte los ceros a uno y los unos a cero Desplazamiento a la derecha, esto equivale a dividir por 2 Desplazamiento a la izquierda, esto equivale a multiplicar por 2
OPERADORES DE ASIGNACION
Operador = *= /= %= += -= <<= >>= &= != ^= Operacin Asignacin simple Multiplicacin ms asignacin Divisin ms asignacin Mdulo ms asignacin Suma ms asignacin Resta ms asignacin Desplazamiento a la izquierda ms asignacin Desplazamiento a la derecha ms asignacin Operacin AND sobre bits ms asignacin Operacin OR sobre bits ms asignacin Operacin XOR sobre bits ms asignacin
NIVELES DE PRECEDENCIA
Operador ( ) [ ] ~ ! & ++ * /% + << >> < <= > >= == != ^ ! && || = *= /= %= += -= &= ^= != <<= >>=
SENTENCIAS Y BUCLES
Hasta ahora los programas que hemos visto eran lineales. O sea que las instrucciones se ejecutaban en orden y una sola vez. Pero resulta que muchas veces no es esto lo que queremos que ocurra. Lo que nos suele interesar es que dependiendo de los valores de los datos se ejecuten unas instrucciones y no otras. O tambin puede que queramos repetir unas instrucciones un nmero determinado de veces. Para esto estn las sentencia de control de ujo.
Bucles
Los bucles nos ofrecen la solucin cuando queremos repetir una tarea un nmero determinado de veces. Supongamos que queremos escribir 100 veces la palabra Bienvenido. Con lo que sabemos, tendramos que escribir 100 veces la expresin "printf("Bienvenido\n");", sin embargo un ciclo for nos facilitara en grande esta tarea. Estos bucles pueden ser: For (Para), While (Mientras), Do while(Hacer mientras).
El Bucle For:
Este bucle nos permite hacer un nmero nito de tareas, cuantas veces necesitemos. La sintaxis del for es la siguiente:
42
for([valor {
...sentencias ...sentencias }
La parte del valor inicial, es el valor con el que empieza la variable contadora, o la variable que nos va a decir cuando salir. La condicin, es la que nos determinar el nmero de veces que se llevarn a cabo las instrucciones que siguen. Estas sentencias se realizarn mientras la condicin estipulada se cumpla. El incremento, es el numero en que vamos a incrementar la variable contadora.
El Bucle While:
La sintaxis del bucle while es la siguiente:
while {
( condicin )
bloque }
de instrucciones a ejecutar
La condicin de este bucle, al igual que el for, es la que indica cuantas veces se ejecutarn las instrucciones. Estas (las instrucciones) se ejecutarn mientras la condicin estipulada se cumpla.
El Bucle Do While
La sintaxis de este bucle es:
do { instrucciones } while
( condicin ); a ejecutar
En este bucle,las instrucciones se llevan a cabo al menos una vez, puesto que la condicin se evala al nal del mismo; a diferencia del while o el for, que si la condicin inicial no se cumple, entonces las instrucciones no se cumplen ni una sola vez. La condicin es similar a las antes descritas.
Sentencias Condicionales
Hasta aqu hemos visto cmo podemos repetir un conjunto de instrucciones las veces que deseemos. Pero ahora vamos a ver cmo podemos controlar totalmente el ujo de un programa. Dependiendo de los valores de alguna variable se tomarn unas acciones u otras.
Sentencia if
La palabra if signica si (condicional), su sintaxis es como sigue:
if {
( condicin )
instrucciones }
a ejecutar
43
Cuando se cumple la condicin entre parntesis se ejecuta el bloque inmediatamente siguiente al if (instrucciones a ejecutar). Por ejemplo.
# {
include stdio.h
es 2");
Sentencia if-else
En el if, si la condicin no se cumpla, entonces las instrucciones no se ejecutaban y el programa nalizaba o segua con las siguientes instrucciones. En el if-else, (si-sino) si no se cumple la condicin entonces se realizarn otras instrucciones. Su sintaxis es la siguiente:
if
( condicin )
int {
main()
int a; printf( "Introduce un nmero " ); scanf( " %i", &a ); if ( a==8 ) { printf ( "El nmero que introdujo es ocho.\n" ); } else { printf ( "El nmero que introdujo no es ocho"\n" ); } }
44
En este ejemplo se manda a solicitar un nmero, si el nmero introducido es ocho, entonces manda un mensaje positiv, mientras que si el numero no es ocho manda un mensaje negativo. A esto se le llama if anidados. A la vez es posible analizar ms de una condicin siempre y cuando estas vayan ligadas con su respectivo operador. Por ejemplo.
int {
main()
int a; printf( "Introduce un nmero " ); scanf( " %i", &a ); if ( a<10 ) { printf ( "El nmero introducido era menor de 10.\n" ); } else if ( a>10 && a<100 ) { printf ( "El nmero est entre 10 y 100"\n" ); } else if ( a>100 ) { printf( "El nmero es mayor que 100\n" ); }
En este ejemplo lo que se hace es pedir un numero, se analiza una condicin y si se cumple se mand un mensaje, sino, se analiza otra condicin y se manda a imprimir un mensaje para cada caso.
Sentencia switch
La sentencia switch, analiza una condicin y dependiendo del valor escoge entre varias opciones para realizar distintas instrucciones y si la opcin no est establecida, simplemente ejecuta una opcin por defecto. La sintaxis para el switch es:
switch {
( variable )
case case
opcin 1:
cdigo break;
Ahora miremos, el switch analiza el valor de "variable" luego si variable tiene el valor "opcion 1" entonces se ejecutan las instrucciones que siguen, y as sucesivamente. Es importante poner al nal de cada grupo de instrucciones la palabra "break", la que indica al programa que ah terminan las sentencias. Se pueden poner cuantas opciones se desee y al nal es recomendable (aunque no exigido) que se ponga un grupo de instrucciones que se ejecuten si la variable toma un valor que no se ha denido. Esta ltima se pone despues de la palabra "default".
45
Sentencia goto
La sentencia goto (ir a) nos permite hacer un salto a la parte del programa que deseemos. En el programa podemos poner etiquetas, estas etiquetas no se ejecutan. Es como poner un nombre a una parte del programa. Estas etiquetas son las que nos sirven para indicar a la sentencia goto dnde tiene que saltar. Por ejemplo.
int {
main()
printf( "Lnea 1\n" ); goto linea3; /* Le decimos al goto que busque la etiqueta linea3 */ printf( "Lnea 2\n" ); linea3: /* Esta es la etiqueta */ printf( "Lnea 3\n" ); }
Como resultado tenemos: Lnea 1 Lnea 3 O sea que el printf de la linea 2 no se ejecuta, puesto que lo saltamos con el goto.
ARREGLOS O MATRICES
Un array (arreglo) es un conjunto de variables del mismo tipo, las cuales se diferencian entre s por su posicin (ubicacin). Para ilustrarlo, supongamos una tabla dividida en celdas y cada celda es una variable en particular, por supuesto que todas las celdas son del mismo tipo y pertenecen a la misma tabla. Los arreglos se declaran as: tipo_dato nombre_arreglo [tamao]; El tipo de dato, puede ser uno de los denidos anteriormente. El nombre del arreglo es cualquier identicador vlido y el tamao es el tamao que deseamos que tenga el arreglo, este siempre debe ir entre corchetes. Los datos del arreglo empiezan a introducirse por el dato [0]. Por ejemplo. int temp[24]; /* Con esto tenemos declaradas 24 variables de tipo entero*/ Para poder introducir los datos en un arreglo debemos recorrerlo, o sea ir a cada celda del arreglo y ah escribir el dato. Para esto se usa frecuentement un ciclo for de la siguiente manera:
for( {
printf( "Temperatura de las %i: ", hora ); scanf( " %i", &temp[hora] ); media += temp[hora]; }
Los arreglos al igual que las otras variables se pueden inicializar, para hacerlo se prosigue de la siguiente manera: int temperaturas[24] = { 15, 18, 20, 23, 22, 24, 22, 25, 26, 25, 24, 22, 21, 20, 18, 17, 16, 17, 15, 14, 14, 14, 13, 12 }; De esta manera hemos inicializado el arreglo con los valores correspondientes. Asi temperatura[0] tiene el valor 15, temperatura[1] tiene el valor 18 y as sucesivamente. Punteros a Arreglos Aqu tenemos otro de los importantes usos de los punteros, los punteros a arrays. Estos estn ntimamente relacionados. Para que un puntero apunte a un array se puede hacer de dos formas, una es apuntando al primer elemento del array: Por ejemplo. int *puntero; int temperaturas[24]; puntero = &temperaturas[0];
46
Un array se guarda en posiciones seguidas en memoria, de tal forma que el segundo elemento va inmediatamente despus del primero en la memoria. Ejemplo.
#include {
int main()
int i; int temp[24]; for( i=0; i<24; i++ ) { printf( "La direccin del elemento %i es %p.\n", i, &temp[i] ); } }
Nota: Recuerda que %p sirve para imprimir en pantalla la direccin de una variable en hexadecimal. El resultado es: La direccin del elemento 0 es 4c430. La direccin del elemento 1 es 4c434. La direccin del elemento 2 es 4c438. La direccin del elemento 3 es 4c43c. ... La direccin del elemento 21 es 4c484. La direccin del elemento 22 es 4c488. La direccin del elemento 23 es 4c48c Paso de un Arreglo a una funcin En C no podemos pasar un array entero a una funcin. Lo que tenemos que hacer es pasar un puntero al array. Con este puntero podemos recorrer el array, as:
int {
sumar( int *m )
int suma, i; suma = 0; for( i=0; i<10; i++ ) { suma += m[i]; } return suma; } int { int contador; int matriz[10] = { 10, 11, 13, 10, 14, 9, 10, 18, 10, 10 }; for( contador=0; contador<10; contador++ ) printf( " %3i\n", matriz[contador] ); printf( "+ \n" ); printf( " %3i", sumar( matriz ) ); }
Este programa tiene alguna cosilla adicional como que muestra toda la matriz en una columna. Adems se usa para imprimir los nmeros %3i. El 3 indica que se tienen que alinear los nmeros a la derecha, as queda ms elegante. Como he indicado no se pasa el array, sino un puntero a ese array. main()
47
Por int {
ejemplo. main()
char
nombre[20]; "Introduzca su nombre (20 letras mximo): " ); " %s", nombre ); "\nEl nombre que ha escrito es: %s\n", nombre );
Vemos cosas curiosas como por ejemplo que en el scanf no se usa el smbolo &. No hace falta porque es un array, y ya sabemos que escribir el nombre del array es equivalente a poner &nombre[0]. Tambin puede llamar la atencin la forma de imprimir el array. Con slo usar %s ya se imprime todo el array. Ya veremos esto ms adelante.
de una Cadena
nombre[] = "Gorka"; "Texto: %s\n", nombre ); "Tamao de la cadena: %i bytes\n", sizeof nombre );
printf( printf( }
Como vemos la inicializacin es parecida a la de un arreglo, solo que aqui se escribe toda la frase para inicializarla. Cuando inicializamos una cadena el C automticamente introduce como ultimo elemento el "\0" que indica n de la cadena. Esto sirve para que a la hora de imprimir el C, sepa cuando debe parar de imprimir. Es importante no olvidar que la longitud de una cadena es la longitud del texto ms el smbolo de n de cadena. Por eso cuando denamos una cadena tenemos que reservarle un espacio adicional.
strlen
Su sintaxis es: strlen(char *s) Esta funcin nos devuelve el nmero de caracteres que tiene la cadena (sin contar el '\0'). Si no utilizaramos esta funcin, para poder saber cuantos elementos tiene una cadena tendramos que recorrerla con un puntero.
48
strcpy
(por ejemplo "hola"). Debemos tener cuidado de que la cadena destino (cadena1) tenga espacio suciente para albergar a la cadena origen (cadena2).
strcat
Su sintaxis es. char *strcat(char *cadena1, const char *cadena2); Esta funcin copia la cadena2 al nal de la cadena1. Como siempre tenemos que asegurarnos que la variable en la que metemos las dems cadenas tenga el tamao suciente.
sprintf
Su sintaxis es: int sprintf(char *destino, const char *format, ...); Funciona de manera similar a printf, pero en vez de mostrar el texto en la pantalla lo guarda en una variable (destino). El valor que devuelve (int) es el nmero de caracteres guardados en la variable destino.
strcmp
Su sintaxis es: int strcmp(const char *cadena1, const char *cadena2); Compara cadena1 y cadena2. Si son iguales devuelve 0. Un nmero negativo si cadena1 va antes que cadena2 y un nmero positivo si es al revs.
Ejemplo. #include #include int { char nombre1[]="Gorka"; char nombre2[]="Pedro"; char nombre_completo[50]; char nombre[]="Gorka"; char apellido[]="Urrutia"; sprintf( nombre_completo, " %s %s", nombre, apellido ); printf( "El nombre completo es: %s.\n", nombre_completo ); printf( " %i", strcmp(nombre1,nombre2)); }
En este ejemplo vemos claramente el uso de las funciones antes mencionadas, para la salida es mejor probarlo. Entrada de Cadenas por el Teclado main() stdio.h string.h
scanf
Hemos visto en captulos anteriores el uso de scanf para nmeros, ahora es el momento de ver su uso con cadenas. Scanf almacena en memoria (en un buer) lo que vamos escribiendo. Cuando pulsamos ENTER lo analiza, comprueba si el formato es correcto y por ltimo lo mete en la variable que le indicamos. Scanf toma una palabra como cadena. Usa los espacios para separar variables. Asi si introducimos una palabra, damos espacio y escribimos otra, scanf solo almacenar la primea palabra. Es importante siempre asegurarse de que no vamos a almacenar en cadena ms letras de las que caben. Para ello debemos limitar el nmero de letras que le va a introducir scanf.
49
gets
Esta funcin nos permite introducir frases enteras, incluyendo espacios. Su sintaxis es: char *gets(char *buer); Almacena lo que vamos tecleando en la variable buer hasta que pulsamos ENTER. Si se ha almacenado algn caracter en buer le aade un '\0' al nal y devuelve un puntero a su direccin. Si no se ha almacenado ninguno devuelve un puntero NULL.
Por int {
ejemplo. main()
char cadena[30]; char *p; printf( "Escribe una palabra: " ); ush( stdout ); p = gets( cadena ); if (p) printf( "He guardado: \" %s\" \n", cadena ); else printf( "No he guardado nada!\n" ); }
Qu son los buer?
#include int {
main()
stdio.h
char ch; char nombre[20], apellido[20],telefono[10]; printf( "Escribe tu nombre: " ); scanf( " %[A-Z]s", nombre ); printf( "Lo que recogemos del scanf es: %s\n", nombre ); printf( "Lo que haba quedado en el buer: " ); while( (ch=getchar())!='\n' ) printf( " %c", ch ); }
En este ejemplo el [A-Z] lo que hace es que solamente lee las letras maysculas. Y la salida sera: Escribe tu nombre: GORka Lo que recogemos del scanf es: GOR Lo que haba quedado en el buer: ka
Arreglos de Cadenas
Un array de cadenas puede servirnos para agrupar una serie de mensajes. Por ejemplo todos los mensajes de error de un programa. Luego para acceder a cada mensaje basta con usar su nmero. Por ejemplo.
stdio.h string.h
50
} int
main()
{ error( }
2 );
La salida a este programa sera: Error nmero 2: No hay espacio en disco. Un array de cadenas es en realidad un array de punteros a cadenas. El primer elemento de la cadena ("No se ha producido ningn error") tiene un espacio reservado en memoria y errores[0] apunta a ese espacio.
FUNCIONES
Las funciones son de una gran utilidad en los programas. Nos ayudan a que sean ms legibles y ms cortos. Con ellos estructuramos mejor los programas. Una funcin sirve para realizar tareas concretas y simplicar el programa. Nos sirve para evitar tener que escribir el mismo cdigo varias veces. Ya hemos visto en el curso algunas funciones como printf, gotoxy, scanf y clrscr. Estas funciones estn denidas en una biblioteca (la biblioteca estndar de C). No tenemos que preocuparnos de ella porque el compilador se encarga de ella automticamente. Sin embargo nosotros tambin podemos denir nuestras propias funciones. Pocas veces se ve un programa un poco complejo que no use funciones. Una de ellas, que usamos siempre, es la funcin main. Una funcin tiene la siguiente estructura: tipo_de_variable nombre_de_la_funcin( argumentos ) { denicin de variables; cuerpo de la funcin; return 0; } El nombre de la funcin debe ser un nombre de identicador vlido. Este se utiliza para llamar la funcin dentro del programa. El tipo_de_variable: Cuando una funcin se ejecuta y termina debe devolver un valor. Este valor puede ser cualquiera de los tipos de variables que hemos visto en el captulo de Tipos de datos (int, char, oat, double), o un tipo de dato denido por el usuario. El valor que devuelve la funcin suele ser el resultado de las operaciones que se realizan en la funcin, o si han tenido exito o no. Aqui tambin podemos usar el tipo void, el cual nos permite que podamos devolver cualquier tipo de variable o ninguna. Denicin de variables: Dentro de la funcin podemos denir variables que slo tendrn validez dentro de la propia funcin. Si declaramos una variable en una funcin no podemos usarla en otra. Cuerpo de la funcin: Aqu es donde va el cdigo de la funcin. Return: Antes hemos indicado que la funcin devuelve un valor. La sentencia return se usa para esto. El dato que se pone despues de return es el dato que se devuelve. Puede ser una constante o una variable. Debe ser del mismo tipo que tipo_de_variable. Argumentos: Estos son variables que se pasan como datos a una funcin. Deben ir separados por una coma. Cada variable debe ir con su tipo de variable. Las funciones deben denirse antes de ser llamadas. Ejemplo.
#include #include
stdio.h conio.h
51
void {
clrscr(); printf( "La pantalla est limpia\n" ); return; /* No hace falta devolver ningn valor, mucha gente ni siquiera pone este return */ } int { prepara_pantalla();/* }
Este ejemplo aunque no devuelve ningn valor, nos muestra como se dene y llama una funcin. Ejemplo. Llamamos a la funcin */ main()
stdio conio.h
int mayor; /* Esta funcin dene su propia variable, esta variable slo se puede usar aqu */ if ( a>b ) mayor else mayor } int { int num1, num2; int resultado; printf( "Introduzca dos nmeros: " ); scanf( " %i %i", num1, num2 ); resultado = compara( num1, num2 );/* Recogemos el valor que devuelve la funcin en resultado */ printf( "El mayor de los dos es %i\n", resultado ); }
En este ejemplo vemos la denicin y llamada de la funcin, adems que podemos ver que devuelve un valor. Este valor es la comparacin de dos numeros. Las variables las podemos denir en cualquier parte del programa, aunque es recomendable que se denan antes de que se llamen. Hay quienes, primero las declaran al inicio del programa y al nal del mismo las denen, pero esto se puede hacer de la manera que nosotros querramos. main() = b; return mayor; = a;
52
Argumentos de un Programa
Ya sabemos cmo pasar argumentos a una funcin. La funcin main tambin acepta argumentos. Sin embargo slo se le pueden pasar dos argumentos. Veamos cules son y cmo se declaran: int main( int argc, char *argv[] ) El primer argumento es argc (argument count). Es de tipo int e indica el nmero de argumentos que se le han pasado al programa. El segundo es argv (argument values). Es un array de strings (o puntero a puntero a char). En el se almacenan los parmetros. Normalmente (como siempre depende del compilador) el primer elemento (argv[0]) es el nombre del programa con su ruta. El segundo (argv[1]) es el primer parmetro, el tercero (argv[2]) el segundo parmetro y as hasta el nal. A los argumentos de main se les suele llamar siempre as, no es necesario pero es costumbre. Ejemplo. #include stdio.h int main(int argc,char *argv[]) { int i; for( i=0 ; i printf( "Argumento %i: %s\n", i, argv[i] ); } Si llamamos al programa argumentos.c y lo compilamos (argumentos.exe) podramos teclear "c:\programas> argumentos hola amigos" y tendramos como salida: Argumento 0: c:\programas\argumentos.exe Argumento 1: hola Argumento 2: amigos
ESTRUCTURAS
Supongamos que queremos hacer una agenda con los nmeros de telfono de nuestros amigos. Necesitaramos un array de Cadenas para almacenar sus nombres, otro para sus apellidos y otro para sus nmeros de telfono. Esto puede hacer que el programa quede desordenado y difcil de seguir. Y aqu es donde vienen en nuestro auxilio las estructuras. Para denir una estructura usamos el siguiente formato: struct nombre_de_la_estructura { campos de estructura; }; Por ejemplo. struct cliente { char nombre[30]; char apellido[40]; char telefono[10]; char edad; }; Con esto hemos creado una estructura llamada cliente la que almacena 4 datos de tipo cadena de caracteres que son: el nombre y el apellido del cliente, telefono y edad del mismo. Ahora que tenemos la estructura denida, es necesario declarar una variable del tipo de la estructura, esto se hace as: struct cliente cliente1; Ahora tenemos una variable del tipo cliente. Para acceder a cada uno de sus miembros usamo un punto ".". As para acceder al nombre simplemente escribimos "cliente1.nombre". Ejemplo.
53
include stdio.h amigo { /* Denimos la estructura estructura_amigo */ nombre[30]; apellido[40]; telefono[10]; edad;
struct
amigo amigo;
main()
printf( "Escribe el nombre del amigo: " ); ush( stdout ); scanf( " %s", &amigo.nombre ); printf( "Escribe el apellido del amigo: " ); ush( stdout ); scanf( " %s", &amigo.apellido ); printf( "Escribe el nmero de telfono del amigo: " ); ush( stdout ); scanf( " %s", &amigo.telefono ); printf( "El amigo %s %s tiene el nmero: %s.\n", amigo.nombre, amigo.apellido, amigo.telefono ); }
En este ejemplo se puede usar un gets, en vez del scanf, debido a que alguien puede introducir un nombre separado por espacios, el cual no sera aceptado por scanf.
Arreglos de Estructuras
Supongamos ahora que queremos guardar la informacin de varios clientes. Con una variable de estructura slo podemos guardar los datos de uno. Para manejar los datos de ms gente (al conjunto de todos los datos de cada persona se les llama REGISTRO) necesitamos declarar arrays de estructuras. Esto se hace de la siguiente manera: struct cliente cliente1[NUMERO_ELEMENTOS]; Ahora para acceder a un elemento determinado simplemente hacemos cliente1[0].nombre. Ejemplo.
stdio.h
ELEMENTOS 3
estructura_amigo
54
int {
int num_amigo; for( num_amigo=0; num_amigo { printf( "\nDatos del amigo nmero %i:\n", num_amigo+1 ); printf( "Nombre: " ); ush( stdout ); gets(amigo[num_amigo].nombre); printf( "Apellido: " ); ush( stdout ); gets(amigo[num_amigo].apellido); printf( "Telfono: " ); ush( stdout ); gets(amigo[num_amigo].telefono); printf( "Edad: " ); ush( stdout ); scanf( " %i", &amigo[num_amigo].edad ); while(getchar()!='\n'); } /*
Ahora imprimimos sus datos */
for( {
num_amigo=0; num_amigo
"El amigo %s ", amigo[num_amigo].nombre ); " %s tiene ", amigo[num_amigo].apellido ); " %i aos ", amigo[num_amigo].edad ); "y su telfono es el %s.\n" , amigo[num_amigo].telefono );
En este ejemplo debemos introducir los datos de 3 amigos y podemos ver la manera que se imprimen los datos.
55
struct cliente cliente1[] = { "Juan", "Lopez", "220-9632", 30, "Pedro", "Gonzalez", "520-1258", 42, "Ana", "Martinez", "713-5694", 20 }; En este ejemplo cada lnea es un registro. Como suceda en los arrays si damos valores iniciales al array de estructuras no hace falta indicar cuntos elementos va a tener. En este caso la matriz tiene 3 elementos, que son los que le hemos pasado.
Punteros a Estructuras
Cmo no, tambin se pueden usar punteros con estructuras. Vamos a ver como funciona esto de los punteros con estructuras. Primero de todo hay que denir la estructura de igual forma que hacamos antes. La diferencia est en que al declara la variable de tipo estructura debemos ponerle el operador '*' para indicarle que es un puntero. Es importante recordar que un puntero no debe apuntar a un lugar cualquiera, debemos darle una direccin vlida donde apuntar. No podemos por ejemplo crear un puntero a estructura y meter los datos directamente mediante ese puntero, no sabemos dnde apunta el puntero y los datos se almacenaran en un lugar cualquiera. Ejemplo.
#include struct
stdio.h
estructura_amigo {
"Juanjo", "Lopez", "592-0483", 30 }; struct int { p_amigo = &amigo; printf( " %s tiene ", p_amigo->apellido ); printf( " %i aos ", p_amigo->edad ); printf( "y su telfono es el %s.\n" , p_amigo->telefono ); }
Ahora que denimos el puntero y le indicamos que apunte a amigo, podemos accesar los datos de la estructura mediante el puntero. La diferencia es que ahora no se accesa con ".", sino que ahora se utiliza "->". Accesando a estos datos podemos leer e imprimirlos. estructura_amigo *p_amigo;
main()
56
# # {
struct
"Juan", "Lopez", "305-4342", 30, "Marcos", "Gomez", "635-4823", 42, "Ana", "Martinez", "713-5694", 20 }; struct int { int num_amigo; p_amigo = amigo; /* apuntamos al primer elemento del array */ /* Ahora imprimimos sus datos */ for( num_amigo=0; num_amigo { printf( "El amigo %s ", p_amigo->nombre ); printf( " %s tiene ", p_amigo->apellido ); printf( " %i aos ", p_amigo->edad ); printf( "y su telfono es el %s.\n" , p_amigo->telefono ); /* y ahora saltamos al siguiente elemento */ p_amigo++; } }
Ahora que sabemos accesar a los datos de un arreglo de estructuras por medio de punteros, entonces podemos introducir datos y tambin mandarlos a imprimir. amigo *p_amigo;
main()
57
Estructuras Anidadas
Es posible crear estructuras que tengan como miembros otras estructuras. Esto tiene diversas utilidades, por ejemplo tener la estructura de datos ms ordenada. Por ejemplo, si queremos una estructura de clientes de un banco con sus datos y tambien la fecha del ltimo pago, se podra hacer de la siguiente manera: struct ultimopago{ int dia; int mes; int ao; }; Y ahora la estructura total, incluyendo la de la fecha del ltimo pago. struct cliente{ char nombre[25]; char apellido[25]; oat saldo; struct ultimopago pago; } cliente1; De esta manera tenemos una variable "cliente1" del tipo cliente con una estructura anidada.
PUNTEROS
Los punteros son una de las ms potentes caractersticas de C, pero a la vez uno de sus mayores peligros. Los punteros nos permites acceder directamente a cualquier parte de la memoria. Esto da a los programas C una gran potencia. Sin embargo son una fuente ilimitada de errores. Un error usando un puntero puede bloquear el sistema y a veces puede ser difcil detectarlo. Otros lenguajes no nos dejan usar punteros para evitar estos problemas, pero a la vez nos quitan parte del control que tenemos en C.
Memoria
Cuando hablamos de memoria nos estamos reriendo a la memoria RAM del ordenador. Son unas pastillas que se conectan a la placa base y nada tienen que ver con el disco duro. El disco duro guarda los datos permanentemente (hasta que se rompe) y la informacin se almacena como archivos. Nosotros podemos decirle al ordenador cundo grabar, borrar, abrir un documento, etc. La memoria RAM en cambio, se borra al apagar el ordenador. La memoria Ram la usan los programas sin que el usuario de stos se de cuenta.
Direcciones de Variables
Al declarar una variable estamos diciendo al ordenador que nos reserve una parte de la memoria para almacenarla. Cada vez que ejecutemos el programa la variable se almacenar en un sitio diferente, eso no lo podemos controlar, depende de la memoria disponible y otros factores misteriosos. Puede que se almacene en el mismo sitio, pero es mejor no arse. Dependiendo del tipo de variable que declaremos el ordenador nos reservar ms o menos memoria. Como vimos en el captulo de tipos de datos cada tipo de variable ocupa ms o menos bytes. Cuando naliza el programa todo el espacio reservado queda libre. Existe una forma de saber qu direcciones nos ha reservado el ordenador. Se trata de usar el operador & (operador de direccin). Para mostrar la direccin de la variable usamos %p en lugar de %i, sirve para escribir direcciones de punteros y variables. El valor se muestra en hexadecimal. No hay que confundir el valor de la variable con la direccin donde est almacenada la variable. La variable 'a' est almacenada en un lugar determinado de la memoria, ese lugar no cambia mientras se ejecuta el programa. El valor de la variable puede cambiar a lo largo del programa, lo cambiamos nosotros. Ese valor est almacenado en la direccin de la variable. El nombre de la variable es equivalente a poner un nombre a una zona de la memoria. Cuando en el programa escribimos 'a', en realidad estamos diciendo, "el valor que est almacenado en la direccin de memoria a la que llamamos 'a'.
QUE ES UN PUNTERO?
Un puntero es una variable un tanto especial. Con un puntero podemos almacenar direcciones de memoria. En un puntero podemos tener guardada la direccin de una variable.
58
Datos que cada tipo de variable ocupaba un espacio distinto. Por eso cuando declaramos un puntero debemos especicar el tipo de datos cuya direccin almacenar. Para declarar un puntero se utiliza un asterisco, para indicar que se trata de un puntero. Por ejemplo. char *punt; Esto declara una variable puntero que almacenara la direccion de una variable de tipo char. Cuando un puntero contiene la direccion de una variable, se dice que ese puntero apunta a esa variable. La sintaxis general para declarar un puntero es como sigue: tipo_de_dato *nombre_del_puntero;
Utilidad de un Puntero
Los punteros tienen muchas utilidades, por ejemplo nos permiten pasar argumentos (o parmetros) a una funcin y modicarlos. Tambin permiten el manejo de cadenas y de arrays. Otro uso importante es que nos permiten acceder directamente a la pantalla, al teclado y a todos los componenetes del ordenador. Nos deben dejar tambin la posibilidad de acceder a esas posiciones de memoria. Para acceder a ellas se usa el operador *, que no hay que confundir con el de la multiplicacin. Ejemplo.
# int {
int numero; int *punt; numero = 43; punt = № printf( "Direccin de numero = %p, valor de numero = %i\n", &numero, *punt ); }
Se puede observar en el ejemplo que para accesar al valor de numero usamos *punt en vez de numero. Esto es porque punt apunta a numero y *punt nos permite accesar al valor al que apunta punt. Usando un puntero podemos apuntar a una variable y con *puntero vemos o cambiamos el contenido de esa variable. Un puntero no slo sirve para apunta a una variable, tambin sirve para apuntar una direccin de memoria determinada. Punteros como argumentos de funciones Hemos visto en el captulo de funciones cmo pasar parmetros y cmo obtener resultados de las funciones (con los valores devueltos con return). Pero tiene un inconveniente, slo podemos tener un valor devuelto. Ahora vamos a ver cmo los punteros nos permites modicar varias variables en una funcin. Ejemplo.
# int {
59
int var1, var2; var1 = 5; var2 = 8; printf( "La suma es: %i y a vale: %i\n", suma(&var1, var2), var1 ); }
En este ejemplo podemos ver como con un puntero podemos cambiar el valor de una variable, a traves de ellos podemos accesar y cambiar los datos. Tambien los podemos utilizar como argumentos de funciones, tal es el caso del ejemplo anterior.
malloc y free
Existen varias funciones para la asignacin dinmica de la memoria, pero las dos funciones bsicas son malloc y free. La funcin malloc sirve para reservar una parte de la memoria, y devuelve la direccin del comienzo de esa parte. Esta direccin podemos almacenarla en un puntero y as podemos acceder a la memoria reservada. La sintaxis de malloc, es como sigue: puntero = (tipo_de_variable *) malloc( nmero de bytes a reservar ); puntero: es una variable tipo puntero que almacena la direccin del bloque de memoria reservado. Puede ser un puntero a char, int, oat,... (tipo_de_variable *): es lo que se llama un molde. La funcin malloc nos reserva una cierta cantidad de bytes y devuelve un puntero del tipo void (que es uno genrico). Con el molde le indicamos al compilador que lo convierta en un puntero del mismo tipo que la variable puntero. Esto no es necesario en C, ya que lo hace automticamente, aunque es aconsejable acostumbrarse a usarlo. Una vez reservada la memoria y guardada su direccin en un puntero podemos usar ese puntero como hemos visto hasta ahora. Si no haba suciente memoria libre malloc devolver el valor NULL. El puntero por tanto apuntar a NULL. Es muy importante comprobar siempre si se ha podido reservar memoria o no comprobando el valor de puntero. Esto se puede hacer de la siguiente manera: if(puntero) Si hay memoria suciente entonces se cumple la condicion, de lo contrario es falso. Cuando ya no necesitemos ms el espacio reservado debemos liberarlo, es decir, indicar al ordenador que puede destinarlo a otros nes. Si no liberamos el espacio que ya no necesitamos corremos el peligro de agotar la memoria del ordenador. Para ello usamos la funcin free. La sintaxis de free es: free( puntero ); Ejemplo.
# int {
int bytes; char *texto; printf("Cuantos bytes quieres reservar: "); scanf(" %i",&bytes); texto = (char *) malloc(bytes); /* Comprobamos si ha tenido xito la operacin */
60
} else }
printf("Memoria reservada: %i bytes = %i kbytes = %i Mbytes\n", bytes, bytes/1024, bytes/(1024*1024)); printf("El bloque comienza en la direccin: %p\n", texto); /* Ahora liberamos la memoria */ free( texto );
printf("No
En este ejemplo vemos claramente la asignacion y liberacion de la memoria, despues de preguntar cuanta memoria se desea reservar, se comprueba si se reservo y luego se procede a las instrucciones de lo contrario inmediatamente manda el mensaje "No se ha podido reservar memoria".
ARCHIVOS
Lectura de un archivo Todas las funciones de entrada/salida estndar usan el puntero *FILE para conseguir informacin sobre el archivo abierto. Este puntero no apunta al archivo sino a una estructura que contiene informacin sobre l. Esta estructura incluye entre otras cosas informacin sobre el nombre del archivo, la direccin de la zona de memoria donde se almacena el chero, tamao del buer. Para poder utilizar este puntero se debe incluir el archivo de cabecera stdio.h. Para abrir el archivo utilizamos la funcion fopen. La sintaxis de esta funcion es asi: FILE *fopen(const char *nombre_chero, const char *modo); El nombre del archivo se puede indicar directamente o usando una variable. El archivo se puede abrir de distintas formas, estas se muestran en la tabla. Parametro r w a Modicador b t + Modo Abre un archivo existente para lectura Crea un archivo nuevo y lo abre para escritura Si el archivo existe, borra su contenido Abre un archivo (si no existe lo crea) para escritura El puntero se situa al nal del archivo, de forma que Accion Abre el archivo en modo binario Abre el archivo en modo texto Abre el archivo para lectura y escritura
Estos modicadores se pueden combinar con los parametros dados anteriormente para abrir el archivo en la forma deseada. Ejemplo. wb+ Crea el archivo (o lo borra si existe) en modo binario para lectura y escritura. rt Abre un archivo existente en modo texto para lectura. Una cosa muy importante despus de abrir un chero es comprobar si realmente est abierto. El sistema no es infalible y pueden producirse fallos: el chero puede no existir, estar daado o no tener permisos de lectura. Si intentamos realizar operaciones sobre un puntero tipo FILE cuando no se ha conseguido abrir el chero puede haber problemas. Por eso es importante comprobar si se ha abierto con xito. Si el chero no se ha abierto el puntero chero (puntero a FILE) tendr el valor NULL, si se ha abierto con xito tendr un valor distinto de NULL. Por ejemplo.
if {
(chero==NULL)
61
Leer un archivo
Para leer un archivo podemos utilizar la funcin getc, que lee los caracteres uno a uno. Se puede usar tambin la funcin fgetc. Adems de estas dos existen otras funciones como fgets, fread que leen ms de un carcter. La sintaxis de la funcion getc y fgetc es asi: int getc(FILE *archivo); Ejemplo. letra = getc( chero ); En este ejemplo se toma un caracter de archivo, lo almacena en letra y el puntero se coloca en el siguiente caracter.
Comprobar el n de un Archivo
Cuando entramos en el bucle while, la lectura se realiza hasta que se encuentre el nal del chero. Para detectar el nal del chero se pueden usar dos formas: con la funcin feof() comprobando si el valor de letra es EOF. Esta funcin comprueba si se ha llegado al nal de chero en cuyo caso devuelve un valor distinto de 0. Si no se ha llegado al nal de chero devuelve un cero. Ejemplo.
# int {
FILE *chero; char letra; chero = fopen("origen.txt","r"); if (chero==NULL) { printf( "No se puede abrir el chero.\n" ); exit( 1 ); } printf( "Contenido del chero:\n" ); letra=getc(chero); while (feof(chero)==0) { printf( " %c",letra ); letra=getc(chero); } if }
En este ejemplo se aplica todo lo que hemos dicho anteriormente, se abre el archivo para lectura. (fclose(chero)!=0)
printf(
Cerrar un Archivo
Una vez realizadas todas las operaciones deseadas sobre el archivo hay que cerrarlo. Es importante no olvidar este paso pues el archivo podra corromperse. Al cerrarlo se vacan los buers y se guarda el archivo en disco. Un archivo se cierra mediante la funcin fclose(chero). Si todo va bien fclose devuelve un cero, si hay problemas devuelve otro valor.
62
Escritura de un Archivo
para escritura. Para la escritura en un archivo usamos la funcion putc, la cual tiene la siguiente sintaxis: int putc(int c, FILE *chero); Aqui c contiene el carcter que queremos escribir en el chero y el puntero chero es el chero sobre el que trabajamos. De esta forma vamos escribiento en un chero el contenido de otro chero. Ejemplo.
# int {
FILE *origen, *destino; char letra; origen=fopen("origen.txt","r"); destino=fopen("destino.txt","w"); if (origen==NULL || destino==NULL) { printf( "Problemas con los cheros.\n" ); exit( 1 ); } letra=getc(origen); while (feof(origen)==0) { putc(letra,destino); printf( " %c",letra ); letra=getc(origen); } if (fclose(origen)!=0) printf( "Problemas al cerrar el chero origen.txt\n" ); if (fclose(destino)!=0) printf( "Problemas al cerrar el chero destino.txt\n" ); }
Aqui se puede observar la apertura del archivo, luego la lectura del origen y la escritura en el destino.
fread y fwrite
Estas funciones nos permiten tratar con datos de cualquier tipo, incluso con estructuras dentro de un archivo, al contrario de las pasadas en las que solo sirven para manejar caracteres y cadenas.
63
fwrite
Fwrite nos permite escribir en un chero. Esta funcin tiene la siguiente sintaxis: size_t fwrite(void *buer, size_t tamano, size_t numero, FILE *pchero); buer: variable que contiene los datos que vamos a escribir en el chero. tamano: el tamao del tipo de dato a escribir. Puede ser un int, un oat, una estructura, ... Para conocer su tamao usamos el operador sizeof. numero: el nmero de datos a escribir. pchero: El puntero al chero sobre el que trabajamos. Ejemplo.
#include struct
{ nombre[20]; apellido[20]; telefono[15];
int
FILE *chero; chero = fopen( "nombres.txt", "a" ); do { printf( "Nombre: " ); ush(stdout); gets(registro.nombre); if (strcmp(registro.nombre,"")) { printf( "Apellido: " ); ush(stdout); gets(registro.apellido); printf( "Telfono: " ); ush(stdout); gets(registro.telefono); fwrite( registro, sizeof(registro), 1, chero ); } } while (strcmp(registro.nombre,"")!=0); fclose( chero ); }
En este ejemplo guardamos los datos personales mediante fwrite usando la estructura registro. Abrimos el chero en modo 'a', para que los datos que introducimos se aadan al nal del chero. Una vez abierto entramos en un bucle do-while mediante el cual introducimos los datos. Los datos se van almacenando en la variable registro (que es una estructura). Cuando ya tenemos todos los datos de la persona los escribimos en el archivo con la funcion fwrite, la que tiene la siguiente sintaxis: fwrite( registro, sizeof(registro), 1, chero ); En donde: registro - es la variable (en este caso una estructura) que contiene la informacin a meter al chero. sizeof(registro) - lo utillizamos para saber cul es el nmero de bytes que vamos a guardar, el tamao en bytes que ocupa la estructura. 1 - indica que slo vamos a guardar un elemento. Cada vez que se recorre el bucle guardamos slo un elemento. chero - el puntero FILE al chero donde vamos a escribir. fread
64
size_t tamano, size_t numero, FILE *pchero); funcin indica el nmero de elementos de tamao 'tamano' que ha conseguido leer. Ejemplo.
# {
include stdio.h
struct char char char } { FILE *chero; chero = fopen( "nombres.txt", "r" ); while (!feof(chero)) { if (fread( registro, sizeof(registro), 1, chero )) { printf( "Nombre: %s\n", registro.nombre ); printf( "Apellido: %s\n", registro.apellido); printf( "Telfono: %s\n", registro.telefono); } } fclose( chero ); }
En este ejemplo observamos la utilidad de las funciones antes mencionadas. registro; main()
int
Estas tres constantes estn denidas en el chero . Como curiosidad se indican a continuacin sus deniciones: #dene SEEK_SET 0 #dene SEEK_CUR 1 #dene SEEK_END 2 Si se produce algn error al intentar posicionar el puntero, la funcin devuelve un valor distinto de 0. Si todo ha va bien el valor devuleto es un 0. Ejemplo.
65
ftell
La funcin ftell es complementaria a fseek, devuelve la posicin actual dentro del chero. Su sintaxis es la siguiente: long ftell(FILE *pchero); El valor que nos da ftell puede ser usado por fseek para volver a la posicin actual.
fprintf y fscanf
Estas dos funciones trabajan igual que sus equivalentes printf y scanf. La nica diferencia es que podemos especicar el chero sobre el que se desea operar. Las sintaxis de estas dos funciones son: int fprintf(FILE *pchero, const char *formato, ...); int fscanf(FILE *pchero, const char *formato, ...);
66
La calicacin de Fsica se obtiene de la sig. Manera: Examen 80 % Promedio de tareas 20 % En esta materia se pidi un total de dos tareas.
La calicacin de Qumica se obtiene de la sig. Manera: Examen 85 % Promedio de tareas 15 % En esta materia se pidi un promedio de tres tareas
18. Realizar un Programa que lea la entrada de 02 nmeros y muestre el doble producto del primero menos la mitad del segundo. 19. Desarrollar un programa que permita capturar preguntas como (Edad, sueldo, y mascota favorita) y al nal muestre los datos capturados en conjunto 20. Se desea obtener el precio total de la venta de un producto, conociendo el nmero de cajas adquiridas por un cliente, el nmero de unidades que tiene caca caja, del precio por unidad y de la tasa de impuesto. 21. Disee un programa que guarde y muestre la nota del examen nal de 3 alumnos
67
68
Captulo 3
Programacin en C#
C# (lase, en ingls C sharp, y en espaol C almohadilla) es un lenguaje de programacin que permite el desarrollo de aplicaciones para Internet, para mviles y aplicaciones de propsito general. Inicialmente se desarroll para programar en la plataforma .NET, pero dadas las caractersticas de esta y la estandarizacin que se ha hecho de su estructura por parte de las principales entidades de estndares internacionales, se han desarrollado otras plataformas que cumplen con dicha estructura y por lo tanto C# puede ser utilizado como lenguaje de programacin en ellas. Entre estas plataformas se destaca el Proyecto MONO, desarrollado para Linux y Mac. El lenguaje C# es orientado a objetos y se ha creado basndose en la estructura de C y C++, especialmente su sintaxis y potencia, y adoptando el estilo y metodologa de la programacin en Visual Basic. Sin embargo es importante aclarar que C# no es el resultado de la evolucin directa de ninguno de estos lenguajes, sino que ha sido creado desde cero, para programar sobre la plataforma .NET. Es un lenguaje que fue concebido con el objetivo de programar esta plataforma y por lo tanto se puede decir que es el lenguaje natural de .NET. La empresa Microsoft, creadora de C#, en un intento de superar a otras plataformas que estn imponindose en el soporte a aplicaciones que trabajan en red, especialmente sobre Internet, decidi estandarizar la plataforma .NET y con ella el lenguaje base de la misma, C#. Con esta decisin se ha logrado que Microsoft de a conocer las especicaciones tanto de la plataforma como del lenguaje de programacin, y de esta manera permitir que terceros desarrollen implementaciones de .NET para el mismo Windows o para sistemas operativos diferentes.
69
70
CAPTULO 3. PROGRAMACIN EN C#
La plataforma .NET
71
73
Para comenzar no entraremos en muchos detalles sobre la plataforma .NET, y evitar de esta manera confundir al lector poco experimentado, con tecnicismos que cuando se tenga una mejor familiaridad se pueden abordar con mayor propiedad. .NET, en esencia es una librera de clases que contienen o encapsulan una gran cantidad de funciones que trabajan sobre el sistema operativo. La caracterstica fundamental de este aspecto, es que dichas clases tienen una estructura comn para todos los lenguajes que trabajen sobre esta plataforma. Esto trae como consecuencia que una clase que sea programada en C#, podr ser heredada o utilizada en cualquier lenguaje de la plataforma, como pueden ser Visual Basic .NET o JScript, para comenzar. Desde la perspectiva del programador el aspecto ms importante, es que .NET pone a su disposicin un marco o entorno de trabajo, llamado .NET Framework, el cual le permite acceder a una infraestructura dotada con lenguajes de programacin como C#, Visual Basic .NET, C++ y JScript, y con la posibilidad de acceder a innidad de servicios tiles para desarrollar cualquier tipo de aplicacin.
74
CAPTULO 3. PROGRAMACIN EN C#
75
77
El objetivo inicial de este curso es conocer los fundamentos bsicos de la programacin C# para aplicaciones de propsito general en .NET, y no el manejo de una herramienta especica de software. Sin embargo, en el transcurso del mismo, y para hacer ms fcil la tarea de programacin, se recomienda utilizar software asistente que se encargue de administrar los detalles tcnicos repetitivos y nos permite concentrarnos en los detalles de nuestro inters. Para iniciar a programar en C# y .NET, tan solo se necesita el entorno de trabajo, conocido como .NET Framework, el cual incluye la plataforma de desarrollo y ejecucin de las aplicaciones .NET. Actualmente est a disposicin del pblico la versin 4.0 de este entorno, que puede ser descargada gratuitamente desde la pgina de Microsoft, pero para efectos de este curso se utilizar la versin 2.0, para la cual existe mayor soporte y documentacin. Sin embargo, se sugiere descargar el kit de desarrollo de software de Microsoft .NET 2.0, que contiene adems del .NET Framework una serie de herramientas y archivos de ayuda tiles a la hora de programar. Se sugiere que las prcticas iniciales se programen utilizando nicamente un editor de texto sencillo, como el Bloc de notas, y se realice la compilacin mediante la lnea de comandos. Esto con el n de que tengamos la posibilidad de comprender al detalle aquellos aspectos que un entorno de desarrollo integrado, como el Visual Studio, no nos permite observar, pero cuya comprensin y familiaridad resultan muy importantes a la hora de desarrollar aplicaciones que requieren programacin avanzada.
78
CAPTULO 3. PROGRAMACIN EN C#
La lnea de comandos
79
81
Para compilar nuestras aplicaciones a travs de la lnea de comandos, lo primero que se debe hacer es congurar adecuadamente este entorno. Cualquier aplicacin de .NET depende para su ejecucin de una serie de libreras propias de la plataforma, las cuales le suministran la comunicacin necesaria con el sistema operativo. Por lo tanto para compilar un archivo es necesario poner a disposicin del compilador las direcciones donde posiblemente pueda encontrar algn componente que requiera o que se est utilizando en la aplicacin. Para facilitar esta tarea vamos a denir algunas variables de entorno que facilitan el trabajo de indicar las direcciones bsicas del framework .NET. Lo primero que se debe crear es una carpeta, donde se guardarn los archivos fuente y sus correspondientes compilados. Para facilitar la coherencia entre este escrito y las prcticas que se desarrollen se sugiere crear la carpeta C:\CursoLP.
Para
evitar tener que crear manualmente las variables de entorno con las direcciones necesarias para el compilador, vamos a crear un archivo de procesamiento por lotes, *.BAT, que realice este trabajo en forma rpida y automtica. Ejecute el Bloc de notas, copie las lneas siguientes y guarde el archivo en el directorio C:\CursoLP, nombrndolo como cmdSharpLP.bat:
@echo o Echo Lnea de comandos para el compilador de .NET Framework 2.0 Echo. rem Ejecute este archivo con la instruccin: %comspec % /k cmdSharpLP.bat @SET DirFramework=C:\WINDOWS\Microsoft.NET\Framework @SET VerFramework=v2.0.50727 @set PATH= %DirFramework %\v2.0; %DirFramework %\ %VerFramework %; %PATH % @set LIBPATH= %DirFramework %\v2.0; %DirFramework %\ %VerFramework %; %LIBPATH % cd C:\cursoLP
A continuacin abra la ventana Ejecutar (puede hacerse mediante la combinacin de teclas WINDOWS+R) y ejecute la siguiente instruccin: %comspec % /k c:\cscurso\cursoLP.bat
A continuacin debe cargarse la ventana de lnea de comandos congurada con las direcciones necesarias para trabajar con los compiladores instalados con el framework .NET. Se debe repetir este ltimo paso siempre que se desee abrir una nueva lnea de comandos para compilar un programa desarrollado en C#
public {
class PrimerPrograma
static
void Main()
82
CAPTULO 3. PROGRAMACIN EN C#
{ System.Console.WriteLine("Hola }
mundo C#...!");
}
Guarde el archivo en la carpeta de trabajo,CursoLP, y asigne el nombre ejemplo01.cs. El compilador de C# se identica como csc.exe (C Sharp Compiler), y tiene una diversidad de opciones, dependiendo del tipo de compilacin que se desee realizar. Para este caso, basta con ejecutar el compilador seguido del nombre del archivo fuente que se desea compilar. Por defecto se genera un archivo ejecutable, *.EXE. La instruccin de compilacin es la siguiente: > csc ejemplo01.cs El siguiente grco muestra el resultado de la compilacin de ejemplo01.cs y posterior ejecucin del programa generado.
83
84
CAPTULO 3. PROGRAMACIN EN C#
86
CAPTULO 3. PROGRAMACIN EN C#
Como se ha dicho, C# es un lenguaje de programacin orientada a objetos y todo en l son clases. La clase
es el concepto fundamental de esta metodologa de programacin y es quin identica a los componentes que constituyen un programa. En un sencillo programa como el del ejemplo 1, el programador solo escribe una clase, pero en la prctica existen muchas clases trabajando para permitir que el programa se ejecute. Todo programa C# est constituido por ms de una clase, aunque el programador tan solo deba escribir una de ellas. El ejemplo 1 muestra una estructura bsica de un sencillo programa que se ejecutar en una consola de comandos, o al menos la parte que debe construir el programador, pero permite visualizar con detalle los elementos esenciales que soportan a cualquier aplicacin de software, sin importar el entorno donde vaya a ejecutarse.
public {
class PrimerPrograma
static { } }
void Main()
//
Instrucciones
La clase es algo as como la estructura o molde de un componente de software, y se dene con la palabra clave class. El trmino public le informa al sistema que dicha clase y sus componentes estn disponibles para ser vistos desde afuera del componente de software que la contiene, en este caso el propio programa. El programa en s, observe, es una clase, pero no se puede perder de vista que en la prctica el programa necesita otros componentes de software para poder ejecutarse, y lo ms seguro es que estos ltimos de alguna manera dependan de una o ms clases. Cuando se ejecuta el programa, el sistema operativo a travs de la plataforma de ejecucin, .NET, crea una instancia (para comenzar entindase, un componente de software basado en ese molde) de esta clase e interpreta las ordenes contenidas en ella. En este caso el nombre que se ha colocado a la clase, PrimerPrograma, es una cadena de texto tomado arbitrariamente e indiferente a como se lo escriba, lo importante es tener en cuenta las reglas que la mayora de lenguajes imponen a los nombres de sus elementos. Como regla general se ha establecido que los nombres de los elementos de programacin deben iniciar por un carcter alfabtico (letra) o por una raya abajo (_). Sin embargo, se sugiere no utilizar esta ltima forma de iniciar el nombre de un elemento, ya que le hace perder esttica al contenido y sobre todo diculta su lectura por parte del programador. Adems, aunque no es un requisito, es importante tener en cuenta las recomendaciones hechas por la documentacin del .NET Framework, sobre la nomenclatura de los nombres asignados a los diferentes elementos que se utilizan en el desarrollo de un programa, ya que permiten estandarizar los nombres utilizados en nuestras aplicaciones y en los componentes que vayamos a agregar al mismo entorno de desarrollo. As, por ejemplo, para nombrar las clases se sugiere identicadores que inicien por una letra mayscula, y cuando se requiera utilizar palabras compuestas, cada palabra debe iniciar por mayscula, tales como: MiPrograma, ProgramaDibujo, NominaTrabajadoresEmpresa, etc. No es correcto llamar a una clase, como: 5ProgramaDibujo, Programa#dibujo, programa-dibujo. Adems, un buen programador debe manejar un estilo de escritura de cdigo que haga clara su interpretacin, no solo por l mismo, sino por otras personas a quienes les puede interesar revisarlo. Como sugerencia de estilo, la cual manejaremos en este escrito, se sugiere utilizar identicadores lo ms explicativos posible. Es mejor evitar el uso de abreviaturas, ya que con el tiempo podemos olvidar su signicado y cuando el programador deba volver a revisar su cdigo, despus de algunos meses o aos, le complicar su interpretacin. Talvez, despus de algn tiempo, sea ms fcil interpretar para que sirva una clase llamada IdentidadTrabajador, que una con el nombre CITra. Todo programa desarrollado en C# debe incluir un mtodo Main(), el cual le informa al compilador por donde debe iniciar y tambin terminar un programa. Este mtodo o funcin siempre se dene antecedida de la palabra clave static, la cual permite utilizar la clase directamente, sin necesidad de instanciar un objeto de ella. Esto debe ser as por que en el momentos de iniciar la ejecucin de un programa, an no se ha montado en el sistema todos los componentes necesarias para manejar objetos y por lo tanto el procesador no sabe que hacer con ellos. La palabra clave void, que antecede a Main, le dice al sistema que la funcin que viene en seguida no retornar ningn valor y que por lo tanto no espere nada. En C#, esta funcin tambin se puede denir como int La funcin Main() puede ir como se mostr en el ejemplo 1, o tambin incluir argumentos de tipo cadena de texto. Dichos argumentos se identican por un arreglo o vector del tipo string (cadena de texto), como en la siguiente forma:
87
static {
// }
Instrucciones
En apariencia, los argumentos de inicio de ejecucin solo son vlidos para programas de consola, y no para programas que manejan un sistema grco de ventanas, como las aplicaciones tipo Windows. Pero esto no es muy exacto, los programas tipo Windows, o en general que manejan ventanas, tambin pueden requerir argumentos de entrada en el instante en que inician su ejecucin. Un buen ejemplo son los programas que manejan algn formato de archivo especico, como puede ser el Bloc de notas que genera archivos de texto que se identican con la extensin *.TXT y los cuales, generalmente, el sistema operativo asocia con este editor. Esto trae como consecuencia dos formas de iniciar la ejecucin del Bloc de notas: una, a travs de su acceso directo en el men de Programas, y la otra haciendo doble clic en el archivo de texto. En este ltimo caso el sistema operativo enva un argumento al programa informndole que su ejecucin la inicio un archivo y no el acceso directo del men de programas. El argumento que se enva contiene el nombre completo del archivo que lo llam, y de esta forma el programa se ejecuta y realiza su apertura.
public {
class Bienvenida
" + nombre[0]);
En seguida realice la ejecucin del programa llamando a ejemplo02 seguido de un nombre, como por ejemplo: > ejemplo02 Homero Si todo ha salido bien, la salida que muestra el programa despus de teclear ENTER es, Bienvenido(a) Homero Pruebe a ejecutar el programa sin enviarle un argumento. Observar que se produce un error, el cual es informado por la plataforma de ejecucin del .NET.
88
CAPTULO 3. PROGRAMACIN EN C#
La consola
Este es el nombre como en Windows, e incluso en otros sistemas operativos, se conoce a la interfaz que permite enviar rdenes al sistema operativo a travs de comandos escritos. Tales comandos, no son otra cosa sino programas desarrollados para este entorno de ejecucin. Para .NET la consola de Windows se manipula a travs de un objeto que se identica con el nombre Console, el cual incluye todas las funciones bsicas para manejar este elemento del sistema operativo. Una de esas funciones es WriteLine que se encarga de enviar a la pantalla, o tambin a otros dispositivos de salida, el argumento que se le asigne, ya sea una cadena de texto o un valor numrico. En la instruccin que hemos utilizado en el anterior ejemplo, System.Console.WriteLine("Bienvenido(a) " + nombre[0]); se observa que el objeto Console esta antecedido por el identicador System. Este en realidad es lo que se denomina espacio de nombres, que no es ms que el nombre de un conjunto que agrupa a una serie de clases, que por lo general el programador considera guardan alguna relacin entre s. En este caso la clase Console pertenece al conjunto o espacio de nombres llamado System. Miembro Black Blue Cyan DarkBlue DarkCyan DarkGray DarkGreen DarkMagenta DarkRed DarkYellow Gray Green Magenta Red White Yellow Color Negro Azul Aguamarina Azul marino Verde azulado Gris oscuro Verde oscuro Fucsia oscuro Rojo oscuro Amarillo oscuro Gris Verde Fucsia Rojo Blanco Amarillo
public {
class LectorDatos
static {
void Main()
System.Console.ForegroundColor = System.ConsoleColor.Green; System.Console.Write("Nombre del usuario: "); System.Console.ReadLine(); System.Console.Write("Nmero de indenticacin: "); System.Console.ReadLine(); System.Console.WriteLine("Acceso permitido");
89
System.Console.ResetColor(); // } }
Inicie un nuevo archivo de texto en el Bloc de notas, incluya las lneas de codigo sugeridas y gurdelo con el nombre ejemplo03.cs. En los ejemplos anteriores la compilacin se realiz utilizando la opcin bsica que ofrece el compilador de C#, llamando al compilador y asignndole el nombre del archivo fuente que se desea compilar. Por defecto, el compilador asigna al archivo compilado el mismo nombre del archivo fuente, pero en un momento dado el programador puede desear asignar un nombre conveniente al archivo ejecutable. Como cualquiera de los compiladores antecesores a C#, este cuenta con una serie de opciones que permiten obtener diferentes salidas en el proceso de compilacin. Para este caso en particular, se puede utilizar el parmetro out, que permite asignar un nombre al archivo compilado, diferente al de su fuente. Se aplica siguiendo la sintaxis, csc /out:ArchivoCompilado ArchivoFuente Teniendo en cuenta lo anterior compile el programa con la instruccin, > csc /out:LectorDatos.exe ejemplo03.cs y ejectelo mediante la llamada, > LectorDatos En la programacin de este ejemplo nos hemos dado cuenta que las instrucciones pueden ser un tanto complejas de escribir, dada la necesidad de tener que incluir el espacio de nombres en las llamadas a las clases y sus mtodos. Si tenemos en cuenta que la programacin en .NET se hace a base de objetos, cuyas clases hacen parte de espacios de nombres, y que muchos de estos espacios de nombres a su vez hacen parte de otros espacios de nombres, el grado de dicultad parece aumentarse innecesariamente para el programador de C#, a causa de estos mtodos de asignacin de nombres. Sin embargo, esta forma de identicar las clases y sus objetos tiene una poderosa razn de ser y, en vez de perjudicar, ms bien es un benecio para el programador. En seguida se describe la importancia de esta metodologa de agrupamiento de clases y la forma como podemos evitar las dicultades que ofrece el manejo de los espacios de nombres. Reestablece las opciones de color
Espacios de nombres
Un espacio de nombres es un nombre que identica a un conjunto de clases y que ayuda a distinguirlas de otras que pueden llevar el mismo nombre base. Por ejemplo, la clase Console que hemos utilizado en todos los ejemplos, hace parte del espacio de nombres System, que agrupa a todas las clases bsicas de la plataforma .NET. Si un programador necesita denir otra clase con el nombre Console, puede hacerlo sin ningun problema siempre y cuando incluya un espacio de nombres diferente, como por ejemplo CursoCSharp.Console. Ambas clases pueden ser utilizadas en una misma aplicacin sin ningn problema, pero para referenciarlas se deber utilizar su nombre compuesto, espacio de nombres y nombre de la clase, tal como System.Console y CursoCSharp.Console.
Tambin se puede decir que un espacio de nombres es algo as como un directorio que agrupa a un conjunto de clases, las cuales el programador las agrupa dependiendo de su criterio. La plataforma .NET posee muchos espacios de nombres, que agrupan a clases cuya funcionalidad tiene alguna caracterstica que las relaciona a unas con otras. La siguiente tabla muestra algunos de los espacios de nombres bsicos que se han denido en .NET, y los archivos DLL que los contienen junto a sus clases (o tambin llamadas tipos):
90
CAPTULO 3. PROGRAMACIN EN C#
Es importante destacar que estos son solo algunos de los espacios de nombres que existen en .NET. Pero todos los espacios de nombre que conforman la plataforma .NET se encuentran denidos dentro del espacio de nombres System, y una gran cantidad de ellos estn ubicados dentro de los anteriores espacios de nombres. Otro aspecto interesante, es observar la forma como han sido nombrados los archivos DLL que contienen a estos espacios de nombres, como por ejemplo el espacio de nombres System.Drawing es guardado en el archivo compilado System.Drawing.dll. No signica esto que se trate de una regla obligatoria que vaya a tener algn efecto en el proceso de compilacin, sino ms bien una cuestin de organizacin que facilita mucho la identicacin del espacio de nombres y sus clases. Por lo general los entornos de desarrollo integrado, como Sharpdevelop y VisualStudio .NET generan estos nombres en forma automtica. El objetivo fundamental de los espacios de nombres, adems de ayudar en la organizacin, es impedir las incompatibilidades creadas por la posible duplicidad de nombres asignados a las clases. Es tanta la cantidad de clases que se encuentran denidas dentro de la plataforma de .NET y las que vaya a utilizar el programador de aplicaciones, que bien podra entrar en conicto a la hora de dar un nombre a una clase, lo cual aunque puede ser detectado por el compilador, de todas maneras puede perjudicar el diseo y sobre todo la posibilidad de trabajar un proyecto con componentes de software asignados a diferentes grupos de desarrollo. Adems se corre el riesgo que los nombres de las clases de un programador coincidan con las de otro, lo cual creara inconsistencias al sistema. Supongamos un caso: un equipo de desarrollo encarga a dos grupos diferentes de programadores el diseo de algunos de los componentes de software para la aplicacin de software que est desarrollando. Los dos grupos, debido a la dicultad que tienen de comunicacin, implementan, cada uno de ellos, una clase llamada ColorTexto para colorear las lneas de texto de la salida, pero con funcionalidades un tanto diferentes. Por ejemplo, el primer grupo dota a esta clase con la funcionalidad de colorear el texto con rojo, mientras que el segundo grupo le asigna funcionalidades para colorear con amarillo y azul. Si el equipo de desarrollo base necesita utilizar los tres colores, est obligado a utilizar las dos clases por que cuentan con las funcionalidades que se necesitan y no se puede obviar una de ellas, pero puede verse en un serio problema si ambas fueron compiladas con el mismo nombre. La solucin al anterior problema es utilizar los espacios de nombres para identicar claramente a cada clase y evitar conicto de nombres. Supongamos que los equipos de programadores se identican con los nombres de Alfa y Beta y hacen parte de la organizacin MiEmpresa, entonces podran utilizar como nombres para sus respectivas clases,
MiEmpresa.EquipoAlfa.ColorTexto
y
MiEmpresa.EquipoBeta.ColorTexto
Con esto queda perfectamente solucionado el problema de la duplicidad de clases y permite la utilizacin de ambas clases sin ningn problema. Para establecer un espacio de nombres se utiliza la palabra clave namespace, que tiene la siguiente estructura:
namespace {
Nombre
91
// // }
Se puede anidar cualquier cantidad de espacios de nombres para permitir alargar la identidad de las clases. De esta manera la clase de nuestro ejemplo puede denirse, por parte del Equipo Alfa as:
namespace {
MiEmpresa
namespace {
MiEmpresa
//
using {
namespace
92
CAPTULO 3. PROGRAMACIN EN C#
public static class ColorTexto { public static void Rojo(string cadena) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(cadena); Console.ResetColor(); } } }
Observe que, se ha utilizado la instruccin using System; para direccionar al espacio de nombres System. De esta manera se le informa al compilador donde debe buscar la denicin de la clase Console. Aunque la programacin orientada a objetos establece que las clases son plantillas, o moldes, que describen en forma general como es un objeto, hasta el momento no la hemos utilizado en este contexto, que en la prctica es su verdadera y ms importante nalidad, sino a travs del uso directo de las clases y sus mtodos. C# le permite al programador, en cierta manera, evadir el concepto de programacin orientada a objetos deniendo clases estticas. Este tipo de clases se comportan como plantillas que nicamente sirven para agrupar funciones, como aquellas que se utilizan en cualquier lenguaje de programacin estructurada y que le sirven al programador para realizar alguna tarea concreta. Este tipo de clases se denen con la palabra clave static. Todos los mtodos o funciones de una clase esttica deben denirse tambin como static. Adems, estas clases se utilizan directamente ya que no permiten denir objetos a partir de ellas. Siguiendo el esquema de nombres sugerido anteriormente, guarde el archivo anterior con el nombre EquipoAlfa.ColorTexto.cs y complelo utilizando el parmetro /target:library, como lo muestra la siguiente instruccin tecleada en la lnea de comandos: > csc /t:library EquipoAlfa.ColorTexto.cs Si todo sale bien, al revisar la carpeta de trabajo se observa que se ha compilado en el ensamblado EquipoAlfa.ColorTexto.dll.La siguiente parte del ejemplo consiste en utilizar esta librera y su clase en un programa ejecutable. El programa que se va a desarrollar le pide al usuario ingresar un mensaje desde el teclado y lo escribe a continuacin en color rojo. Inicie un nuevo archivo en el Bloc de notas y teclee el siguiente cdigo:
//
Archivo: Ejemplo04.cs
using System; using EquipoAlfa; public class MensajeDeColor { static void Main() { string sMensaje; Console.Write("Escriba su mensaje: "); sMensaje = Console.ReadLine(); ColorTexto.Rojo(sMensaje); Console.WriteLine("Terminado..."); } }
Para cambiar a color rojo el mensaje ingresado por el usuario, el programa utiliza la clase ColorTexto denida en el ensamblado EquipoAlfa.ColorTexto.dll. En el proceso de compilacin de este programa, el compilador debe establecer donde se encuentra la clase y jar est informacin en el archivo ejecutable que se va a generar. Para ello es necesario indicarle al compilador de C# el nombre del recurso con el cual debe realizar el enlace, mediante la opcin /resources seguido del nombre del archivo DLL, que en forma simplicada se escribe como /r.
93
En consecuencia la compilacin se debe realizar mediante la siguiente instruccin: >csc /r:EquipoAlfa.ColorTexto.dll ejemplo04.cs En denitiva hemos desarrollado una aplicacin que consta de dos ensamblados, al menos desde la perspectiva del programador: un archivo ejecutable obtenido en la ltima compilacin y un archivo DLL que contiene a la clase que se encarga de ponerle color al texto. Esta estructura es la que utilizan la mayora de aplicaciones modernas, donde cada componente se compila en un archivo independiente, en el caso de .NET en un ensamblado, lo cual trae una ventaja principal, es que permite hacer cambios a la aplicacin sin necesidad de tener que volver a compilar todo, sino nicamente el componente que sea necesario.
//
Archivo: EquipoBeta.ColorTexto.cs
using System; namespace EquipoBeta { public static class ColorTexto { public static void Amarillo(string cadena) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(cadena); Console.ResetColor(); } public static void Azul(string cadena) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(cadena); Console.ResetColor(); } } }
Guarde el archivo con el nombre EquipoBeta.ColorTexto.cs y complelo con la instruccin de lnea de comandos, > csc /t:library EquipoBeta.ColorTexto.cs Con esto ya tenemos los ensamblados entregados por los equipos Alfa y Beta y los vamos a utilizar en nuestro proyecto de software. El cdigo del programa que nos permite calcular las funciones trigonomtricas es el siguiente:
//
Archivo: Ejemplo05.cs
94
CAPTULO 3. PROGRAMACIN EN C#
double angulo = 0; Console.Clear(); Console.Title = "Funciones trigonomtricas"; Console.Write("Valor del ngulo en grados = "); angulo = Convert.ToDouble(Console.ReadLine()); angulo = angulo * Math.PI / 180; // Convierte de grados a radianes EquipoBeta.ColorTexto.Amarillo("\nSeno = " + Math.Sin(angulo)); EquipoBeta.ColorTexto.Azul("Coseno = " + Math.Cos(angulo)); EquipoAlfa.ColorTexto.Rojo("Tangente = " + Math.Tan(angulo)); Console.ReadLine(); // Detiene la ejecucin hasta presionar ENTER } }
Este programa, para empezar limpia e inicializa la pantalla mediante el mtodo clear de la clase Console y asigna un ttulo a la barra de ttulos de la consola, mediante la propiedad Title. La lectura de datos se realiza mediante el mtodo ReadLine. Pero este mtodo captura el dato como una cadena de texto de tipo string, lo cual implica que para poderse manipular como un valor numrico se debe convertir primero a nmero. En este caso se utiliza la Clase esttica Convert y su mtodo ToDouble, que lo convierte de cadena a un valor de tipo double (numero real de precisin doble). Tambin, es necesario realizar una conversin del dato ingresado, pasndolo de grados a radianes, ya que este es el formato que utilizan las funciones trigonomtricas de .NET. Tanto el valor de la constante PI como las funciones trigonomtricas, y otras funciones matemticas, se encuentran denidas e implementadas en al clase esttica Math. Finalmente, hay que compilar este archivo fuente, para generar el ensamblado de nuestro programa, teniendo en cuenta que este depende de dos ensamblados ms, EquipoAlfa.ColorTexto.dll y EquipoBeta.ColorTexto.dll. En el llamado al compilador se debe pasar el listado de las libreras que se necesitan para la compilacin, utilizando la opcin /r: seguida de los nombres de los archivos DLL, separados por comas (pero sin espacios). Debe tenerse en cuenta que para llamar a la clase ColorTexto, fue necesario incluir el espacio de nombres al cual perteneca, dado que si no se hacia as se creara una ambigedad que no permitira al sistema saber de cual clase se est hablando. La compilacin se realiza ejecutando la siguiente instruccin en la lnea de comandos: > csc /r:EquipoAlfa.ColorTexto.dll,EquipoBeta.ColorTexto.dll ejercicio05.cs Con este sencillo ejemplo se ha mostrado la forma como se puede organizar una aplicacin de software y la reutilizacin de algunos de sus componentes para ahorrar trabajo y ganar tiempo en el proceso de desarrollo. Cuando abordemos el concepto de programacin orientada a objetos, propiamente dicho, se dar mayor nfasis a este tipo de diseo.