You are on page 1of 44

REPUBLICA BOLIVARIANA DE VENEZUELA INTRODUCCION A LA PROGRAMACIN EN C++

FUNCIONES
Las funciones son un conjunto de instrucciones que realizan una tarea especfica. En general toman ciertos valores de entrada, llamados parmetros y proporcionan un valor de salida o valor de retorno; aunque en C++, tanto unos como el otro son opcionales, y pueden no existir.

Prototipos de funciones
En C++ es obligatorio usar prototipos. Un prototipo es una declaracin de una funcin. Consiste en una presentacin de la funcin, exactamente con la misma estructura que la definicin, pero sin cuerpo y terminada con un ";". La estructura de un prototipo es: [extern|static] <tipo_valor_retorno> <identificador>(<lista_parmetros>); En general, el prototipo de una funcin se compone de las siguientes secciones: Opcionalmente, una palabra que especifique el tipo de almacenamiento, puede ser extern o static. Si no se especifica ninguna, por defecto ser extern. El tipo del valor de retorno, que puede ser void, si no necesitamos valor de retorno. En C, si no se establece, ser int por defecto, aunque en general se considera una mala tcnica de programacin omitir el tipo de valor de retorno de una funcin. En C++ es obligatorio indicar el tipo del valor de retorno. El identificador de la funcin. Es costumbre, muy til y muy recomendable, poner nombres que indiquen, lo ms claramente posible, qu es lo que hace la funcin, y que permitan interpretar qu hace el programa con slo leerlos. Cuando se precisen varias palabras para conseguir este efecto se puede usar alguna de las reglas ms usuales. Una consiste en separar cada palabra con un "_". Por ejemplo, si hacemos una funcin que busque el nmero de telfono de una persona en una base de datos, podramos llamarla "busca_telefono" o "BuscaTelefono". Una lista de declaraciones de parmetros entre parntesis. Los parmetros de una funcin son los valores de entrada (y en ocasiones tambin de salida). Para la funcin se comportan exactamente igual que variables, y de hecho cada parmetro se declara igual que una variable. Una lista de parmetros es un conjunto de declaraciones de parmetros separados con comas. Puede tratarse de una lista vaca. En C es preferible usar la forma "func(void)" para listas de parmetros vacas. En C++ este procedimiento se considera obsoleto, se usa simplemente "func()".

Por ejemplo: int Mayor(int a, int b); Un prototipo sirve para indicar al compilador los tipos de retorno y los de los parmetros de una funcin, de modo que compruebe si son del tipo correcto cada vez que se use esta funcin dentro del programa, o para hacer las conversiones de tipo cuando sea necesario.

En el prototipo, los nombres de los parmetros son opcionales, y si se incluyen suele ser como documentacin y ayuda en la interpretacin y comprensin del programa. El ejemplo de prototipo anterior sera igualmente vlido si se escribiera como: int Mayor(int, int); Esto slo indica que en algn lugar del programa se definir una funcin "Mayor" que admite dos parmetros de tipo int y que devolver un valor de tipo int. No es necesario escribir nombres para los parmetros, ya que el prototipo no los usa. En otro lugar del programa habr una definicin completa de la funcin. Normalmente, los prototipos de las funciones se declaran dentro del fichero del programa, o bien se incluyen desde un fichero externo, llamado fichero de cabecera. Ya lo hemos dicho ms arriba, pero las funciones son extern por defecto. Esto quiere decir que son accesibles desde cualquier punto del programa, aunque se encuentren en otros ficheros fuente del mismo programa. En contraposicin las funciones declaradas static slo son accesibles dentro del fichero fuente donde se definen.

Definicin de funciones
Al igual que hemos visto con las variables, las funciones deben declararse, para lo que usaremos los prototipos, pero tambin deben definirse. Una definicin contiene adems las instrucciones con las que la funcin realizar su trabajo, es decir, su cdigo. La sintaxis de una definicin de funcin es: [extern|static] <tipo_valor_retorno> <identificador>(<lista_parmetros>) { [sentencias] } Como vemos, la sintaxis es idntica a la del prototipo, salvo que se elimina el punto y coma final, y se aade el cuerpo de funcin que representa el cdigo que ser ejecutado cuando se llame a la funcin. El cuerpo de la funcin se encierra entre llaves "{}". La definicin de la funcin se hace ms adelante o ms abajo, segn se mire, es decir, se hace despus que el prototipo. Lo habitual es hacerlo despus de la funcin main. Una funcin muy especial es la funcin main. Se trata de la funcin de entrada, y debe existir siempre, ya ser la que tome el control cuando se ejecute el programa. Los programas Windows usan la funcin WinMain como funcin de entrada, aunque en realidad esta funcin contiene en su interior la definicin de una funcin main. Existen reglas para el uso de los valores de retorno y de los parmetros de la funcin main, pero de momento la usaremos como int main() o int main(void), con un entero como valor de retorno y sin parmetros de entrada. El valor de retorno indicar si el programa ha terminado sin novedad ni errores retornando cero, cualquier otro valor de retorno indicar un cdigo de error.

Invocacin a funciones
Una invocacin llamada a una funcin implica pasarle el control de la ejecucin del programa, as como los argumentos parmetros que requiere para realizar su tarea. Por ejemplo se tienen las lneas: saludo(); precio = calcula(costo); //INVOCACION A LA FUNCION saludo() //INVOCACION A LA FUNCION calcula()

En la primera, se invoca a la funcin saludo() y no se le pasa ningn argumento. En la segunda, se invoca a la funcin calcula(), pasndosele como argumento una copia del valor que tiene la variable costo. El valor retornado por calcula() se asigna a la variable precio. El programa seria: #include <iostream.h> #include <conio.h> // DECLARACION DE FUNCIONES void saludo(); float calcula(float); // DEFINICION DE LA FUNCION PRINCIPAL void main() { float costo, precio; clrscr(); cout << "COSTO : $ "; cin>> costo; saludo(); //INVOCACION A LA FUNCION saludo() precio = calcula(costo); //INVOCACION A LA FUNCION calcula() cout << "PRECIO : $ " << precio; getch(); } // DEFINICION DE LA FUNCION saludo() void saludo() { clrscr(); cout << "!! BIENVENIDO A LA VENTA ESPECIAL !!"; } // DEFINICION DE LA FUNCION calcula() float calcula(float x) { return( x * 1.6); }

Paso de parmetros
Cuando se invoca a una funcin, en ocasiones es necesario enviarle algunos elementos indispensables para realizar su tarea. Estos elementos, enviados en la invocacin, se llaman parmetros actuales.

Dependiendo de la naturaleza de los parmetros, clasificaremos el paso de ellos en: Paso por valor Se realiza un paso de parmetros por valor cuando se enva una copia de los valores de los parmetros actuales. En este caso, la funcin invocada no podr modificar los valores de las variables utilizadas como parmetros actuales, sino que trabajar con las copias que se le envan. Estas copias son recibidas en los parmetros formales, los cuales se comportan como variables locales pertenecientes a la funcin invocada. Al igual que las variables locales, una vez que termina la ejecucin de la funcin invocada, las variables pertenecientes a los parmetros formales se destruyen; y por lo tanto, se pierden los valores que envi el mdulo invocador. El siguiente programa se muestra el paso de parmetros por valor. #include <iostream.h> void precio(double); void main(void) { double costo; cout << "COSTO : $ "; cin>> costo; precio(costo); // SE INVOCA A precio() Y SE LE ENVIA UNA // COPIA DEL VALOR DE costo cout << "\n" cout << costo; // EL VALOR DE costo SE CONSERVA DESPUES // DE LA INVOCACION A precio() } void precio(double recibido) { recibido="recibido" * 1.5; // SE MODIFICA EL VALOR DE LA // COPIA GURDADA EN recibido cout << "\n" cout << recibido; }

Paso por referencia A diferencia del paso por valor, el paso por referencia permite que la funcin invocada modifique el valor original de las variables usadas como argumentos. En C++, el paso de parmetros por referencia se realiza creando un alias del identificador de la variable que forma el parmetro actual, como se muestra en el siguiente programa. #include <iostream.h> #include <conio.h> void oferta(float &);

void main(void) { float precio; clrscr(); gotoxy(20,12); cout << " CUAL ES EL PRECIO ?. N$ " ; cin>> precio; oferta(precio); gotoxy(20,14); cout << " DIJO USTED N$ " << precio << " ?"; gotoxy(20,16); cout << " ESO ES CON EL 20 % DE DESCUENTO"; getch(); } void oferta(float & bajo) { bajo *=0.8 ; gotoxy(20,24); cout << "PRECIO REBAJADO: N$ " << bajo; }

En el programa se puede decir que bajo es otro identificador asociado a la variable precio, por lo que la funcin oferta() realmente modifica el valor contenido en dicha variable. Segn lo anterior, podemos decir que es preferible utilizar una variable existente en lugar crear una nueva, y eliminar el paso de parmetros por valor. El paso de parmetros por valor puede sustituirse por un paso de parmetros por referencia donde se utilice el modificador const, como se muestra en el listado. #include <iostream.h> #include <conio.h> void seudoferta(const float &); void main(void) { float precio; clrscr(); gotoxy(20,12); cout << " CUAL ES EL PRECIO ?. N$ " ; cin>> precio; seudoferta(precio); gotoxy(20,14); cout << " DIJO USTED N$ " << precio << " ?"; gotoxy(20,16); cout << " ESO DIJE.... NO HAY DESCUENTO"; } void seudoferta(const float & bajo) { float rebajado; rebajado= bajo * 0.8 ; gotoxy(20,24); cout << "PRECIO REBAJADO: N$ " << rebajado; }

Las funciones cuando reciben parmetros de entrada por valor no modifican las variables de entrada. Ejemplo de ello supongamos que queremos cambiar el valor de dos variables mediante una funcion que recibe parmetros de entrada por valor: #include <iostream.h> #include <conio.h> void cambio(int a, int b); void main(void) { int a, b; cout << "Ingrese valor de a: " ; cin>> a; cout << "Ingrese valor de b: " ; cin>> b; cambio(a, b); cout << " El valor de a: " << a << endl; cout << " El valor de b: " << a << endl; getch(); } void cambio(int a, int b) { int aux; aux = a; a = b; b = aux; } Al mostrar los valores de la variables se mantienen sin ser modificadas a pesar de que se cambian los valores enviados en la funcion, pero estos valores reciben solo su contenido. A diferencia de los los parmetros de entrada por referencia o la direccion de un arreglo en estos casos si se modifican los valores de entrada. Por ejemplo si leemos una frase y transformamos los caracteres minusculas en mayusculas. #include <iostream.h> #include <conio.h> void mayuscula(char cad[100]); void main(void) { char frase[100]: cout << "Ingrese una frase: " ; gets(frase); mayuscula(frase); cout << " La frase en mayuscula: " << frase << endl; getch(); } void mayuscula(char cad[100]) { int i; for( i=0; cad[i]!=\0; i++) if( cad[i]>=a && cad <=z) cad[i]= cad[i] - 32; }

Funciones recursiva
Se dice que una funcin es recursiva cuando puede invocarse a s misma. En cada invocacin se crean las variables locales, por lo que es indispensable incluir una condicin de salida, ya que de no hacerlo se agotar la memoria disponible en la pila. Como un primer acercamiento al manejo de funciones recursivas en C++, se presenta el listado #include <iostream.h> #include <conio.h> void recursiva(void); void main(void) { clrscr(); recursiva(); cout << "\n PULSE CUALQUIER TECLA PARA CONTINUAR."; getch(); } void recursiva(void) { int x; cout << "TECLEE UN NUMERO ENTERO... ( 0=SALIR): "; cin>> x ; if(x) recursiva(); } En el listado podemos observar que, para que la funcin recursiva se invoque a si misma, es necesario que el valor almacenado en x sea diferente de 0. El clculo del factorial de un nmero es un problema clsico en la aplicacin de las funciones recursivas. En el listado se presenta un programa en C++ que calcula el factorial de un nmero dado. #include <iostream.h> #include <conio.h> long factorial(unsigned short int); void main() { unsigned short int num; long result; clrscr(); do { gotoxy(20,11); cout << "El FACTORIAL del nmero: " ; clreol(); cin>> num ; } while(num <0 || num> 19 ); result = factorial(num);

gotoxy(20,13); cout << " es : " << result; getch(); } long factorial(unsigned short int n) { if(n==0) return 1; else return n*(factorial(n-1)) ; } En el listado, si el nmero dado por el usuario es 4, el proceso realizado por el programa se podra representar de la manera siguiente. Numero de Valor de n Resultado invocacin 1 2 3 4 5 4 3 2 1 0 4*(factorial(3)) 3*(factorial(2)) 2*(factorial(1)) 1*(factorial(0)) 1

Resultados de invocar a la funcin factorial() pasndole como parmetro el nmero 4 . En cada invocacin, se crea en la pila una variable cuyo identificador es n y su valor cambiar como se muestra en la tabla. Como en las invocaciones 1,2,3 y 4 el valor de n es diferente de 0, la funcin vuelve a invocarse a s misma, quedando sin resolver el resultado. Cuando el valor de n es igual a 0 (invocacin 5), la funcin retorna el valor 1 la la invocacin 4, por lo que el resultado en sta se calcula as : 1 * (factorial(0)) = 1 * 1 = 1 La invocacin 4 retorna a la invocacin 3 el valor 1 y el resultado en la invocacin 4 es : 2 * (factorial(1)) = 2 * 1 = 2 A su vez, la invocacin 3 retorna a la invocacin 2 el valor 2. El resultado en la invocacin 2 es: 3 * (factorial(2)) = 3 * 2 = 6 Posteriormente, la invocacin 2 retorna el valor 6 a la invocacin 1. El resultado en la invocacin 1 es: 4 * (factorial(3)) = 4 * 6 = 24 Finalmente, la invocacin 1 retorna el valor 24 a la funcin invocadora main(), la cual asigna a la variable result el valor recibido ( 24 ).

APUNTADORES
Las referencias de memoria
Las referencias son novedades absolutas del C++ (no se encuentran disponibles en C). Una referencia es un nuevo tipo de datos que nos va a permitir utilizar las caractersticas de los punteros pero tratndolos como variables ordinarias. Podis imaginaros una referencia como un "alias" de una variable o, mejor dicho, como la misma variable disponible pero con un nombre distinto. Vaya lo no? ;). La inicializacin de una referencia es bien fcil ya que slo tendremos que asociar la referencia que deseemos a otra variable que ya est creada por nosotros. Una vez que hemos realizado tal inicializacin, la referencia va a estar continuamente asociada con su variable correspondiente. Cabe aadir que, si quisiramos hacer que nuestra referencia fuese el "alias" de otra variable o lo que es lo mismo que referenciase a otra variable no podramos, tendramos un error de compilacin. La declaracin de una referencia es bien sencilla: // dato es una variable definida por // nosotros y es de tipo entero. int dato; // referenciaDato es la referencia que hemos creado. int& referenciaDato = dato; Como podis observar para crear una referencia no necesitamos ms que la variable a la que queremos referenciar, que en el ejemplo es dato, junto la referencia en s que se va a definir con el smbolo &. De este modo, referenciaDato es la referencia o alias de la variable dato. Una vez hechas las dos operaciones anteriores cualquier cambio que hagamos sobre dato se ver reflejado en referenciaDato y viceversa, es decir, si realizamos una modificacin en referenciaDato, esta tambin se va a ver reflejada en la variable dato. Ejemplo: #include <iostream.h> void main() { int dato = 50; int& refDato = dato; cout << \n; cout << "La variable dato vale " << dato << \n; cout << "La variable refDato vale " << refDato << \n; // multiplicamos la variable // dato por 2, ahora dato = 100 dato *= 2; cout << "La variable dato vale " << dato << /n; cout << "La variable refDato vale " << refDato << \n; // incrementamos el valor de la

// referenicia, ahora refDato = 101; refDato ++; cout << "La variable dato vale " << dato << \n; cout << "La variable refDato vale " << refDato; } Si ejecutis este programita veris que la salida que se obtiene sera: 50 50 100 100 101 101 Con lo que deducimos que los cambios efectuados en dato y refDato se ven involucrados. Debis de saber que tanto dato como refDato comparten la misma direccin de memoria y por eso, cualquier cambio que efectuemos sobre dato afectar a refDato del mismo modo que si lo hiciramos al revs. Para cercioraros de que tanto la variable como la referencia poseen la misma direccin tan slo tenis que ejecutar este listado que os muestro a continuacin y comprobar que la direccin de dato y refDato es la misma. void main() { int dato = 50; int& refDato = dato; cout << \n << "La direccin de la variable dato es " << &dato << /n; cout << \n << "La direccin de la referencia refDato es " << &refDato << \n; } Si lo ejecutis veris que salen los mismos valores. Una de las cosas que tenemos que tener bien claro es la utilizacin del operador unario & con una variable y con una referencia. Cuando declaramos una referencia estamos definiendo un tipo de datos que es exclusivo del C++, en este caso, el operador & va junto al tipo de datos que estamos definiendo como referencia as, si nosotros queremos crear una referencia que va a referenciar a una variable de tipo entero pondremos algo como int& referencia. Otro uso bien distinto es cuando lo que queremos obtener es la direccin en memoria de cierta variable o dato. En este caso tanto en C como en C++ el operador unario & se comporta de igual modo dndonos la direccin en memoria de tal variable o dato. Tan slo tenis que ejecutar el ltimo de los listados expuestos el cual nos mostrar, por pantalla, la direccin en memoria de una variable de tipo entero y la de una referencia que tiene con alias precisamente tal variable de tipo entero.

Apuntadores
Los punteros en el Lenguaje C, son variables que " apuntan " , es decir que poseen la direccin de las ubicaciones en memoria de otras variables, y por medio de ellos tendremos un poderoso mtodo de acceso a todas ellas . Quizs este punto es el ms conflictivo del lenguaje , ya que muchos programadores en otros idiomas , y novatos en C , lo ven como un mtodo extrao al menos desacostrumbrado , lo que les produce un cierto rechazo . Sin embargo , y en la medida que uno se va familiarizando con ellos , se convierten en la herramienta ms cmoda y directa para el manejo de variables complejas , argumentos , parmetros , etc , y se empieza a preguntar como es que hizo para programar hasta aqu , sin ellos . Declaracin de un puntero : tipo_de_variable_apuntada Ejemplo: int *pint ; double *pfloat ; char *letra , *codigo , *caracter ; En estas declaraciones slo decimos al compilador que reserve una posicin de memoria para albergar la direccin de una variable , del tipo indicado , la cual ser referenciada con el nombre que hayamos dado al puntero . Cuando declaramos un puntero como: int *a; indicamos que: a representa la direccin de memoria que apunta y *a representa el contenido de la direccin de memoria apuntada Obviamente, un puntero debe ser inicializado antes de usarse, y una de las eventuales formas de hacerlo es la siguiente: int var1 ; int *pint ; /* declaro ( y creo en memoria ) una variable entera ) */ /* un puntero que contendr la direccin de una variable entera */ *nombre_del_puntero ;

pint = &var1 ; /* escribo en la direccin de memoria donde est el puntero la direccin de la variable entera */ Como habamos anticipado en captulos anteriores " &nombre_de_una_variable " implica la direccin de la misma. Esquemticamente, lo que hemos hecho se puede simbolizar de la siguiente forma:

La variable var1 reserva una direccin de memoria de 2 bytes solo para ella, el puntero apunta a esta posicin permitiendo utilizar el contenido de esta. En realidad, como veremos ms adelante, en la declaracin del puntero, est implcita otra informacin: cual es el tamao (en bytes) de la variable apuntada. El smbolo &, direccin, puede aplicarse a variables, funciones, etc, pero n a constantes expresiones, ya que stas no tienen una posicin de memoria asignada. La operacin inversa a la asignacin de un puntero, de referenciacin del mismo, se puede utilizar para hallar el valor contenido por la variable apuntada. As por ejemplo sern expresiones equivalentes: y = var1 ; y = *pint ; printf("%d" , var1 ) ; printf("%d" , *pint) ;

En estos casos, la expresin " *nombre_del_puntero ", implica " contenido de la variable apuntada por el mismo " . Veamos un corto ejemplo de ello:
#include <stdio.h> main() { char var1 ; char *pchar; /*una variable del tipo caracter */ /* un puntero a una variable del tipo caracter */

pc = &var1 ; /*asignamos al puntero la direccion de la variable */ for (var1 = 'a'; var1 <= 'z'; var1++) printf("%c", *pchar) ; /* imprimimos el valor de la variable apuntada */ return 0 ;

} Vemos ac, que en el for se incrementa el valor de la variable , y luego para imprimirla usamos la dereferenciacin de su puntero. El programa imprimir las letras del abecedario de la misma manera que lo habra hecho si la sentencia del printf() huiera sido, printf("%c" , var1 ). Hay un error, que se comete con bastante frecuencia , y es cargar en la direccin apuntada por un puntero a un tipo dado de variable , el contenido de otro tipo de las mismas , por ejemplo : double d = 10.0 ; int i = 7 , *pint ; pint = &i ; *pint = 10 ; *pint = d ; double */ pint = &d ; /* correcto,equivale a asignar a i el valor 10 */ ; /* ERROR se pretende cargar en una variable entera un valor /* INCORRECTO se pretende apuntar a una variable double con un puntero declarado como apuntador a int */

pint = 4358 ; /* ?????? */ El primer error, la asignacin de un double , produce la prdida de informacin dada por la conversin automtica de tipo de variable , ya vista anteriormente , el segundo produce un llamado de atencin rotulado como " asignacin sospechosa de un pointer " . Resumiendo , las variables constantes cargadas por dereferenciacin de un puntero, deben coincidir en tipo con la declaracin de aquel. La asignacin de una constante a un pointer , y no a la variable apuntada por l , es un serio error , ya que debe ser el compilador , el encargado de poner en l el valor de la direccin , aquel as lo declara dando un mensaje de " conversin de puntero no transportable " . Si bien lo compila, ejecutar un programa que ha tenido esta advertencia es similar a jugar a la ruleta rusa , puede "colgarse" la mquina lo que es peor destruirse involuntariamente informacin contenida en un disco , etc. Hay un slo caso en el que esta asignacin de una constante a un puntero es permitida , muchas funciones para indicar que no pueden realizar una accin que se ha producido un error de algun tipo , devuelven un puntero llamado "Null Pointer" , lo que significa que no apunta a ningun lado vlido , dicho puntero ha sido cargado con la direccin NULL ( por lo general en valor 0 ) , as la asignacin : pint = NULL ; es vlida y permite luego operaciones relacionales del tipo if( pint ) ..... if( print != NULL ) para convalidar la validez del resultado devuelto por una funcin . Una advertencia : si bien volveremos ms adelante sobre este tema , debemos desde ahora tener en cuenta que los punteros no son enteros , como parecera a primera vista , ya que el nmero que representa a una posicin de memoria , s lo es . Debido al corto alcance de este tipo de variable, algunos compiladores pueden , para apuntar a una variable muy lejana , usar cualquier otro tipo , con mayor alcance que el antedicho . PUNTEROS Y ARRAYS Hay una relacin muy cercana entre los punteros y los arrays . Y vimos previamente que el designador ( nombre de un array ) era equivalente a la direccin del elemento [0] del mismo . La explicacin de sto es ahora sencilla : el nombre de un array , para el compilador C , es un PUNTERO inicializado con la direccin del primer elemento del array . Sin embargo hay una importante diferencia entre ambos, que haremos notar ms abajo.

Veamos algunas operaciones permitidas entre punteros : ASIGNACION float var1 , conjunto[] = { 9.0 , 8.0 , 7.0 , 6.0 , 5.0 ); float *punt ; punt = conjunto ; var1 = *punt ; *punt = 25.1 ; Es perfectamente vlido asignar a un puntero el valor de otro , el resultado de sta operacin es cargar en el puntero punt la direccin del elemento [0] del array conjunto , y posteriormente en la variable var1 el valor del mismo (9.0) y para luego cambiar el valor de dicho primer elemento a 25.1 Veamos cual es la diferencia entre un puntero y el denominador de un array: el primero es una VARIABLE , es decir que puedo asignarlo , incrementarlo etc , en cambio el segundo es una CONSTANTE , que apunta siempre al primer elemento del array con que fu declarado , por lo que su contenido NO PUEDE SER VARIADO . Si lo piensa un poco, es lgico, ya que "conjunto" implica la direccin del elemento conjunto [0], por lo que, si yo cambiara su valor, apuntara a otro lado dejando de ser , "conjunto" . Desde este punto de vista, el siguiente ejemplo nos muestra un tipo de error bastante frecuente: ASIGNACION ERRONEA int conjunto[5] , lista[] = { 5 , 6 , 7 , 8 , 0 ) ; int *apuntador ; apuntador = lista ; /* correcto */ /* ERROR ( se requiere en Lvalue no constante ) */ /* equivalente a hacer : punt = &conjunto [0] */

conjunto = apuntador; lista = conjunto ;

/* ERROR ( idem ) */

apuntador = &conjunto /* ERROR no se puede aplicar el operador & (direccin) a una constante */

Veamos ahora las distintas modalidades del incremento de un puntero :


INCREMENTO O DECREMENTO DE UN PUNTERO int *pint , arreglo_int[5] ; double *pdou , arreglo_dou[6] ; pint = arreglo_int ; pdou = arreglo_dou ; pint += 1 ; pdou += 1 ; pint++ ; pdou++ ; /* pint apunta a arreglo_int[0] */ /* pdou apunta a arreglo_dou[0] */

/* pint apunta a arreglo_int[1] */ /* pdou apunta a arreglo_dou[1] */ /* pint apunta a arreglo_int[2] */ /* pdou apunta a arreglo_dou[2] */

Hemos declarado y asignado dos punteros , uno a int y otro a double , con las direcciones de dos arrays de esas caracteristicas . Ambos estarn ahora apuntando a los elementos [0] de los arrays . En las dos instrucciones siguientes incrementamos en uno dichos punteros . adonde apuntaran ahora ?. Para el compilador , stas sentencias se leen como : incremente el contenido del puntero ( direccin del primer elemento del array ) en un nmero igual a la cantidad de bytes que tiene la variable con que fu declarado . Es decir que el contenido de pint es incrementado en dos bytes (un int tiene 2 bytes) mientras que pdou es incrementado 8 bytes ( por ser un puntero a double ) , el resultado entonces es el mismo para ambos , ya que luego de la operacin quedan apuntando al elemento SIGUIENTE del array , arreglo_int[1] y arreglo_dou[1] . Vemos que de sta manera ser muy facil "barrer" arrays , independientemente del tamao de variables que lo compongan , permitiendo por otro lado que el programa sea transportable a distintos hardwares sin preocuparnos de la diferente cantidad de bytes que pueden asignar los mismos , a un dado tipo de variable . De manera similar las dos instrucciones siguientes, vuelven a a incrementarse los punteros , apuntando ahora a los elementos siguientes de los arrays. Todo lo dicho es aplicable , en identica manera , al operador de decremento -- .

ARITMETICA DE REFERENCIA
Debido a que los operadores * y ++ -- tienen la misma precedencia y se evaluan de derecha a izquierda , y los parntesis tienen mayor precedencia que ambos , muchas operaciones que los utilizan en conjunto a todos estos operadores , pueden parecer poco claras y dar origen a un sinnmero de errores , (revise un poco la TABLA 13 del captulo 3 ) analicmoslas detalladamente , partiendo de : int *p , a[] = { 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 } ; int var ; p=a; A partir de aqu, el puntero est apuntando a a[0] . Veamos las distintas variantes que puede tener la siguiente instruccin: *p = 27 ; La ms sencilla de las opciones , simplemente asignamos al elemento apuntado por p ( a[0] ) un valor constante . Veamos la inversa de ella: var = *p ; var sera asignada al valor 0 (contenido de a[0]) , y p seguira apuntando al mismo elemento. Que hubiera pasado, si en vez de ello se hubiera escrito: var = *( p + 1 ) ; ac podramos traducir el sentido de la operacin como : cargue var con el contenido del elemento siguiente al apuntado por p ( a[1] ) . Lo interesante de remarcar ac es que p , en s mismo , NO VARIA Y LUEGO DE ESTA SENTENCIA SEGUIRA APUNTANDO A a[0] . De la misma forma : var = *( p + 3 ) asignar 30 a var , sin modificar el contenido de p . En cambio la expresin : var = *( p++ ) ;

podemos leerla como : asigne a var el valor de lo apuntado por p y LUEGO incremente ste para que apunte al prximo elemento . As en var quedara 0 ( valor de a[0] ) y p apuntara finalmente a a[1] . Si en vez de sto hubieramos preincrementado a p tendramos: var = *( ++p ) ; la que puede leerse como : apunte con p al prximo elemento y asigne a var con el valor de ste . En este caso var sera igualada a 10 ( a[1] ) y p quedara apuntando al mismo . En las dos operaciones anteriores los parntesis son superfluos ya que al analizarse los operadores de derecha a izquierda , dara lo mismo escribir : var = *p++ ; var = *++p ; /* sintcticamente igual a var = *(p++) */ /* " " " var = *(++p) */

ARITMETICA DE PUNTEROS La aritmtica ms frecuentemente usada con punteros son las sencillas operaciones de asignacin , incremento decremento y dereferenciacin . Todo otro tipo de aritmtica con ellos est prohibida es de uso peligroso poco transportable . Por ejemplo no est permitido , sumar , restar , dividir , multiplicar , etc , dos apuntadores entre s . Lo cual si lo pensamos un poco es bastante lgico , ya que de nada me servira sumar dos direcciones de memoria , por ejemplo . Otras operaciones estan permitidas , como la comparacin de dos punteros , por ejemplo ( punt1 == punt2 ) ( punt1 < punt2 ) sin embargo este tipo de operaciones son potencialmente peligrosas , ya que con algunos modelos de pointers pueden funcionar correctamente y con otros no . PUNTEROS Y VARIABLES DINAMICAS Recordemos lo expresado en captulo 5 , sobre el mbito existencia de las variables , la menos duradera de ellas era la del tipo local a una funcin , ya que naca y mora con sta . Sin embargo , esto es algo relativo , en cuanto a la funcin main() , ya que sus variables locales ocuparn memoria durante toda la ejecucin del programa. Supongamos un caso tpico , debemos recibir una serie de datos de entrada , digamos del tipo double , y debemos procesar segn un determinado algoritmo a aquellos que aparecen una ms veces con el mismo valor . Si no estamos seguros de cuantos datos van a ingresar a nuestro programa , pondremos alguna limitacin , suficientemente grande a los efectos de la precisin requerida por el problema , digamos 5000 valores como mximo , debemos definir entonces un array de doubles capaz de albergar a cinco mil de ellos , por lo que el mismo ocupar del orden de los 40 k de memoria . Si definimos este array en main() , ese espacio de memoria permanecer ocupado hasta el fn del programa , aunque luego de aplicarle el algoritmo de clculo ya no lo necesitemos ms , comprometiendo seriamente nuestra disponibilidad de memoria para albergar a otras variables . Una solucin posible sera definirlo en una funcin llamada por main() que se ocupara de llenar el array con los datos , procesarlos y finalmente devolviera algn tipo de resultado , borrando con su retorno a la masiva variable de la memoria . Sin embargo en C existe una forma ms racional de utilizar nuestros recursos de memoria de manera conservadora . Los programas ejecutables creados con estos compiladores dividen la memoria disponible en varios segmentos , uno para el cdigo ( en lenguaje mquina ) , otro para albergar las variables globales , otro para el stack ( a travez del cual se pasan argumentos y donde residen las variables locales ) y finalmente un ltimo segmento llamado memoria de apilamiento amontonamiento ( Heap ) .

El Heap es la zona destinada a albergar a las variables dinmicas , es decir aquellas que crecen ( en el sentido de ocupacin de memoria ) y decrecen a lo largo del programa , pudiendose crear y desaparecer (desalojando la memoria que ocupaban) en cualquier momento de la ejecucin . Veamos cual sera la metodologa para crearlas ; supongamos primero que queremos ubicar un nico dato en el Heap , definimos primero un puntero al tipo de la variable deseada : double *p ; notemos que sta declaracin no crea lugar para la variable , sino que asigna un lugar en la memoria para que posteriormente se guarde ah la direccin de aquella Para reservar una cantidad dada de bytes en el Heap , se efectua una llamada a alguna de las funciones de Librera , dedicadas al manejo del mismo . La ms tradicional es malloc() ( su nombre deriva de memory allocation ) , a esta funcin se le d como argumento la cantidad de bytes que se quiere reservar , y nos devuelve un pointer apuntando a la primer posicin de la "pila" reservada . En caso que la funcin falle en su cometido ( el Heap est lleno ) devolvera un puntero inicializado con NULL . p = malloc(8) ; ac hemos pedido 8 bytes ( los necesarios para albergar un double ) y hemos asignado a p el retorno de la funcin , es decir la direccin en el Heap de la memoria reservada. Como es algo engorroso recordar el tamao de cada tipo variable , agravado por el hecho de que , si reservamos memoria de esta forma , el programa no se ejecutar correctamente , si es compilado con otro compilador que asigne una cantidad distinta de bytes a dicha variable , es ms usual utilizar sizeof , para indicar la cantidad de bytes requerida : p = malloc( sizeof(double) ) ; En caso de haber hecho previamente un uso intensivo del Heap, se debera averiguar si la reserva de lugar fu exitosa: if( p == NULL ) rutina_de_error() ; si no lo fu estas sentencias me derivan a la ejecucin de una rutina de error que tomar cuenta de este caso . Por supuesto podra combinar ambas operaciones en una sola, if( ( p = malloc( sizeof(double) ) ) == NULL ) { printf("no hay mas lugar en el Heap ..... Socorro !!" ) ; exit(1) ; } se ha reemplazado aqu la rutina de error , por un mensaje y la terminacin del programa , por medio de exit() retornando un cdigo de error . Si ahora quisiera guardar en el Heap el resultado de alguna operacin , sera tan directo como, *p = a * ( b + 37 ) ; y para recuperarlo , y asignarselo a otra variable bastara con escribir : var = *p ;

PUNTEROS A STRINGS No hay gran diferencia entre el trato de punteros a arrays , y a strings , ya que estos dos

ltimos son entidades de la misma clase . Sin embargo analicemos algunas particularidades . As como inicializamos un string con un grupo de caracteres terminados en '\0' , podemos asignar al mismo un puntero : p = "Esto es un string constante " ; esta operacin no implica haber copiado el texto , sino slo que a p se le ha asignado la direccin de memoria donde reside la "E" del texto . A partir de ello podemos manejar a p como lo hemos hecho hasta ahora . Veamos un ejemplo #include <stdio.h> #define TEXTO1 " Hola , como " #define TEXTO2 "le va a Ud. ? " main() { char palabra[20] , *p ; int i ; p = TEXTO1 ; for( i = 0 ; ( palabra[i] = *p++ ) != '\0' ; i++ ) ; p = TEXTO2 ; printf("%s" , palabra ) ; printf("%s" , p ) ; return 0 ;

Definimos primero dos strings constantes TEXTO1 y TEXTO2 , luego asignamos al puntero p la direccin del primero , y seguidamente en el FOR copiamos el contenido de ste en el array palabra , observe que dicha operacin termina cuando el contenido de lo apuntado por p es el terminador del string , luego asignamos a p la direccin de TEXTO2 y finalmente imprimimos ambos strings , obteniendo una salida del tipo : " Hola , como le va a UD. ? " ( espero que bien ) . Reconozcamos que esto se podra haber escrito ms compacto, si hubieramos recordado que palabra tambien es un puntero y NULL es cero , as podemos poner en vez del FOR while( *palabra++ = *p++ ) ; Vemos que aqu se ha agregado muy poco a lo ya sabido , sin embargo hay un tipo de error muy frecuente , que podemos analizar , fjese en el EJEMPLO siguiente , ve algun problema ?. #include <stdio.h> char *p , palabra[20] ; printf("Escriba su nombre : ") ; scanf("%s" , p ) ;

palabra = " Como le va " ; printf("%s%s" , palabra , p ) ; } Pues hay dos errores , a falta de uno , el primero ya fue analizado antes , la expresin scanf("%s" , p ) es correcta pero , el error implcito es no haber inicializado al puntero p , el cual slo fu definido , pero aun no apunta a ningun lado vlido . El segundo error est dado por la expresin : palabra = " Como le va " ; ( tambin visto anteriormente ) ya que el nombre del array es una constante y no puede ser asignado a otro valor . Como lo escribiramos para que funcione correctamente ? (CORRECTO) #include <stdio.h> #include <stdlib.h> #include <string.h> char *p , palabra[20] ; p = (char *)malloc(sizeof(char)128) ; printf("Escriba su nombre : ") ; scanf("%s" , p ) ; strcpy(palabra , " Como le va " ) ; printf("%s%s" , palabra , p ) ; } Observe que antes de scanf() se ha inicializado a p, mediante el retorno de malloc() y a al array palabra se le copiado el string mediante la funcin vista anteriormente strcpy(). Debemos aclarar tambin que, la secuencia de control %s en el printf() impone enviar a la pantalla un string, estando ste apuntado por el argumento siguiente al control, ste puede ser tanto el nombre de un array, como un puntero, ya que ambos explicitan direcciones. ARRAYS DE PUNTEROS Es una prctica muy habitual , sobre todo cuando se tiene que tratar con strings de distinta longitud , generar array cuyos elementos son punteros , que albergarn las direcciones de dichos strings. Si imaginamos a un puntero como una flecha , un array de ellos equivaldra a un carcaj indio lleno de aquellas . Asi como: char *flecha; defina a un puntero a un caracter , la definicin char *carcaj[5]; implica un array de 5 punteros a caracteres . INICIALIZACION DE ARRAYS DE PUNTEROS Los arrays de punteros pueden ser inicializados de la misma forma que un array comn , es decir dando los valores de sus elementos , durante su definicin , por ejemplo si quisieramos

tener un array donde el subndice de los elementos coincidiera con el nombre de los das de la semana , podramos escribir : char *dias[] = { "nmero de da no vlido" , "lunes" , "martes" , "miercoles" , "jueves" , "viernes" , "sabado" , "por fn es domingo" } Igual que antes, no es necesario en este caso indicar la cantidad de elementos , ya que el compilador los calcula por la cantidad de trminos dados en la inicializacin. Asi el elemento dias[0] ser un puntero con la direccin del primer string, dias[1], la del segundo, etc. PUNTEROS Y FUNCIONES La relacin entre los punteros y las funciones , puede verse en tres casos distintos , podemos pasarle a una funcin un puntero como argumento (por supuesto si su parmetro es un puntero del mismo tipo ) , pueden devolver un puntero de cualquier tipo , como ya hemos visto con malloc() y calloc() , y es posible tambin apuntar a la direccin de la funcin , en otras palabras , al cdigo en vez de a un dato. PUNTEROS COMO PARAMETROS DE FUNCIONES.

Un ejemplo de funciones ya usadas que poseen como parmetros a punteros son: scanf(puntero_a_string_de_control , punteros_a_variables) printf(puntero_a_string_de_control , variables ) En ambas vemos que los strings de control son , como no podra ser de otro modo , punteros , es decir que los podramos definir fuera de la funcin y luego pasarselos a ellas : p_control = "valor : %d " ; printf( p_control , var ) ; PUNTEROS COMO RESULTADO DE UNA FUNCION Las funciones que retornan punteros son por lo general aquellas que modifican un argumento, que les ha sido pasado por direccin ( por medio de un puntero ) , devolviendo un puntero a dicho argumento modificado , las que reservan lugar en el Heap para las variables dinmicas , retornando un puntero a dicho bloque de memoria . As podremos declarar funcines del tipo de: char *funcion1( char * var1 ) ; double *funcion2(int i , double j , char *k ) ; El retorno de las mismas puede inicializar punteros del mismo tipo al devuelto, distinto , por medio del uso del casting . Algunas funciones, tales como malloc() y calloc() definen su retorno como punteros a void :

void *malloc( int tamano ) ; de esta forma al invocarlas , debemos indicar el tipo de puntero de deseamos p = (double *)malloc( 64 ) ;

ESTRUCTURAS, CLASES Y OBJETOS.


El especificador typedef
La palabra reservada typedef representa un especificador de clase de almacenamiento, y se utiliza para definir nuevos especificadores o sinnimos para los tipos existentes. Por ejemplo : typedef int entero ; define a entero como un sinnimo para el tipo int .

Una vez definido un sinnimo, se puede utilizar para definir nuevos especificadores, de manera que un tipo puede tener varios, como se muestra a continuacin. typedef float flotante ; // Define a flotante como sinnimo de // float typedef flotante FLOAT ; // Define a FLOAT como sinnimo de // flotante typedef FLOAT FLOTANTE ; // Define a FLOTANTE como sinnimo de // FLOAT typedef float Float ; // Define a Float como sinnimo de float

En este caso, flotante, FLOAT, FLOTANTE y Float son nuevos identificadores o sinnimos de float . Otro ejemplo es : typedef long double gigante ; // Define a gigante como sinnimo de // long double Cabe hacer notar que typedef no crea nuevos tipos, sino que define nuevos nombres para los tipos existentes.

Estructuras
En ocasiones es necesario contar con tipos de datos que estn compuestos por conjuntos de elementos de diferentes tipos entre s. En C++, esto se logra por medio de estructuras, las cuales son tipos derivados definidos por el usuario que representan colecciones de elementos. Definicion de tipos de estructuras La palabra struct se utiliza para definir un tipo compuesto. Por ejemplo, si se requiere definir un nuevo tipo llamado catalogo cuyos elementos sean: clave nmero entero ,

precio nmero de punto flotante se puede escribir: |------ Etiqueta | struct catalogo { int clave ; float precio ; }; En este caso, catalogo es un nuevo tipo que puede utilizarse para declarar una estructura como: catalogo refac ;

Acceso a los miembros de una estructura


Una vez declarada una estructura, pueden asignrseles valores iniciales a sus miembros, como en: refac.clave = 1001 ; refac.precio = 624.50 ; Obsrvese que el operador punto sirve para representar la dependencia de cada uno de los miembros con su estructura; esto es: refac.clave se refiere al miembro clave de la estructura refac. El operador punto es necesario porque, al declararse varias estructuras del mismo tipo, habr que distinguir los correspondientes miembros de unas y otras. Por ejemplo, si escribimos: catalogo refac, muebles ; En este caso se declaran dos estructuras de tipo catalogo, cuyos identificadores son refac y muebles. Entonces podemos realizar las siguientes asignaciones: refac.clave = 2001 ; refac.precio = 360.45 muebles.clave = 4001 ; muebles.precio = 2500.50 ; En este caso, los miembros clave y precio de las estructuras refac y muebles son inconfundibles. El listado muestra el cdigo fuente para este ejemplo. #include <iostream.h> void main() { struct catalogo { int clave ; float precio ; }; catalogo refac, muebles; refac.clave = 2001 ; refac.precio = 360.45 ; muebles.clave = 4001 ; muebles.precio = 2500.50; cout << refac.clave << " " << refac.precio << "\n"; cout << muebles.clave << " " << muebles.precio; }

En el listado se observa que primero se define el tipo catalogo y despus se utiliza para declarar las estructuras refac y muebles. El tipo catalogo est disponible para declarar otras estructuras, adems de refac y muebles; pero, si existe la seguridad de que eso no ser necesario, se puede cambiar el cdigo fuente a la forma presentada en el listado siguiente. #include <iostream.h> void main() { struct { int clave ; float precio ; } refac, muebles ; refac.clave = 2001 ; refac.precio = 350.45 ; muebles.clave = 4001 ; muebles.precio = 2500.50; cout << refac.clave << " " << refac.precio << "\n"; cout << muebles.clave << " " << muebles.precio; }

Como puede observarse en el listado anterior, puede definirse un tipo struct sin etiqueta, escribiendo la primera lnea as: struct { y la declaracin de las estructuras a utilizar deber escribirse antes del punto y coma de la ltima lnea, como en: } refac, muebles ; Puede decirse que se ha definido un tipo "desechable", ya que slo lo utilizamos para declarar a las estructuras refac y muebles, adems de que no le asignamos una etiqueta.

Arreglos de Estrucuras
Las estructuras, al igual que las dems variables, pueden agruparse en arreglos como se muestra en el listado siguiente: #include <iostream.h> #include <string.h> // DEFINE UN TIPO DE ESTRUCTURA LLAMADO dicc struct dicc { char *ingl ; char *espa ; }; // DECLARA UN ARREGLO DE ESTRUCTURAS LLAMADO tabla Y ASIGNA // VALORES INICIALES A LOS ELEMENTOS DEL ARREGLO. dicc tabla[] = { "absolute","absoluto", "append","anexar",

"begin","iniciar,principiar,comenzar", "close","cerrar", "do","hacer", "file","archivo", "while","mientras" }; // DEFINE A NUMESTR, QUE CONTIENE EL NUMERO DE ESTRUCTURAS // QUE COMPONEN EL ARREGLO tabla #define NUMESTR (sizeof(tabla)/sizeof(dicc)) int busqbin(char* , dicc * , int) ; void main() { char pal[80]; int nret ; do{ cout << "\n\nPara finalizar, escriba FIN en : \n"; cout << "\n Palabra en ingls : "; cin>> pal; nret = busqbin(pal,tabla,NUMESTR); if(nret == -1) { if(strcmp(strupr(pal),"FIN")==0) cout << "\n\n!! HASTA LUEGO !!\n\n"; else cout << "\n\nPALABRA NO REGISTRADA\n\n"; } else cout << "\n" << pal << "=" << tabla[nret].espa ; }while(strcmp(strupr(pal)," FIN")!="0);" } // FUNCION QUE, DADA UNA PALABRA EN INGLES, REALIZA UNA // BUSQUEDA BINARIA EN EL ARREGLO tabla, RETORNANDO EL // NUMERO DE ESTRUCTURA DONDE SE ENCUENTRA LA palabra int busqbin(char *pal, dicc *t, int N) { int bajo=0, alto=N-1, medio, cond; while(bajo <=alto) { medio=(bajo+ alto) / 2 ; if((cond==strcmp(pal, t[medio].ingl)) < 0) alto=medio-1 ; else if(cond> 0) bajo = medio + 1 ; else return(medio) ; } return(-1) ; }

Apuntadores a estructuras
Al declarar una estructura se le asocia un bloque de memoria en la pila en el montculo. La direccin inicial del bloque puede asignarse a un apuntador, por lo que la estructura puede manejarse a travs de ste. Un apuntador a una estructura se declara de la misma forma en que se declara cualquier apuntador.

Por ejemplo, si usamos el tipo dicc definido anteriormente, podemos escribir la lnea: dicc *apd ; que declara a apd como un apuntador a objetos de tipo dicc. En este momento, se puede declarar una variable dinmica utilizando el apuntador apd, como se muestra a continuacin: apd = new dicc; El acceso a los elementos de la estructura apuntada por apd se logra a travs del "operador flecha" -> , que se forma con los smbolos: - ( menos ) y > ( mayor que ) En el listado siguiente se muestra el manejo de una lista simplemente ligada cuyos nodos estn compuestos por estructuras de tipo alumno. #include <iostream.h> #include <conio.h> #include <stdio.h> #include <stlib.h> #include <string.h> #define IZQ 20 #define NOMEM "\nMEMORIA INSUFICIENTE....\n" #define VACIA "\nLISTA VACIA....\n" #define PULSE "\nPULSE CUALQUIER TECLA PARA CONTINUAR....\n" #define ELIM "\nNODO ELIMINADO....\n" #define NOEX "\nNOMBRE INEXISTENTE....\n" void inserta(); void elimina(); void despliega(); // Define a alumno como un tipo de estructura struct alumno { char nombre[31]; int calif; struct alumno *sig; // Liga a cada nodo }; // con el siguiente. alumno *primero, *actual, *previo; // Declara apuntadores // a estructuras de char cad[31]; // tipo alumno. void main() { char opcion; primero = actual = previo = NULL ; // Controla apuntadores for(;;) // Ciclo indefinido { clrscr(); gotoxy(IZQ,1); cout << "LISTA SIMPLEMENTE LIGADA"; gotoxy(IZQ,4); cout << "1.- INSERCION DE UN NODO"; gotoxy(IZQ,6); cout << "2.- ELIMINACION DE UN NODO";

gotoxy(IZQ,8); cout << "3.- DESPLEGADO DE LA LISTA"; gotoxy(IZQ,10); cout << "0.- F I N"; gotoxy(IZQ,14); cout << "Escriba el nmero de su opcin : "; do{ gotoxy(55,14); clreol(); opcion="getche();" } while((opcion < '0') || (opcion> '3')); clrscr(); switch(opcion) { case '1' : inserta(); break; case '2' : elimina(); break; case '3' : despliega(); break; case '0' : exit(0); } } } void inserta() { alumno *nuevo; // Declara apuntador a estructura nuevo = new alumno;// Crea nodo if(!nuevo) // Si nuevo = NULL ( No hay memoria) { cout << NOMEM << PULSE; getch(); return; } gotoxy(IZQ,2); cout << "INSERTANDO....";// Si hay memoria gotoxy(1,4); cout << "NOMBRE : "; gets(nuevo->nombre); // Lee campo nombre para nodo nuevo gotoxy(wherex()+35,wherey()-1); cout << "\nCALIFICACION : "; cin>> nuevo->calif; // Lee campo calif para nodo nuevo if(!primero) // Lista vaca { primero = nuevo; // primero apunta a donde apunta nuev primero->sig = NULL ; // Liga de primero apunta a NULL } else // Lista no vacia { nuevo->sig = primero; // Liga de nuevo apunta a donde // apunta primero primero = nuevo ;//primero apunta a donde apunta nuevo } } void elimina() { if(!primero) // Lista vaca { cout << VACIA << PULSE; getch(); return; } cout << "NOMBRE A ELIMINAR : "; gets(cad); actual="primero;" // actual apunta a donde apunta primero while(actual)//Mientras actual no apunte a NULL(fin lista) { // Si campo nombre de nodo apuntado por actual="cad" if(strcmp(strupr(cad),strupr(actual>nombre))==0) {

if(actual==primero) // Si actual apunta a donde // apunta primero primero = actual->sig; // primero apunta a // donde apuntaliga de actual previo->sig = actual->sig; // Liga de previo apunta // a donde apunta liga // de actual delete actual; // Elimina nodo apuntado por actual cout << ELIM << PULSE; getch(); return; } else // nombre de nodo apuntado por actual !="cad" { previo="actual;" // previo apunta a donde apunta // actual actual="actual-">sig; // actual apunta a donde // apunta liga de actual } } cout << NOEX << PULSE; getch(); } void despliega() { actual="primero;" // actual apunta a donde apunta primero if(!actual) // Si actual apunta a NULL ( lista vaca ) { cout << VACIA << PULSE; getch(); return; } while(actual) // Mientras actual no apunte a NULL { cout << "\n" << actual->nombre;//Despliega nombre y cout << " " << actual->calif;//calif de actual actual = actual->sig; // Actual apunta a donde apunta // liga de actual } cout << "\n\n" << PULSE ; getch(); }

Uniones
Los tipos union comparten muchas de las caractersticas sintcticas y funcionales de los tipos struct, sin embargo existen algunas diferencias; la principal es que la union permite que solamente uno de sus miembros est activo a la vez, mientras que en la estructura todos los miembros estn activos al mismo tiempo. Otra diferencia es que el tamao de la union es el tamao del miembro ms grande, mientras que en la estructura su tamao es la suma de los tamaos de sus miembros. Las uniones son recomendables cuando se van a manejar variables que pueden compartir espacio en la memoria, debido a que sus valores van a requerirse en momentos diferentes. Por ejemplo, sean tres variables : una de tipo int, otra de tipo float, y otra de tipo double. Si no van a manejarse simultneamente, puede definirse un nuevo tipo union que las abarque, as: union nums { int x ; // 2 bytes float y ; // 4 bytes double z ; // 8 bytes }; En este momento se cuenta con un nuevo tipo llamado nums, as que podemos utilizarlo para efectuar la siguiente declaracin: nums varnums ; // Se declara la variable varnums // que ocupa 8 bytes de memoria. Se pueden accesar los miembros de varnums a travs del operador punto, por ejemplo :

varnums.x = 10 ; // Se asigna el valor 10 al miembro // x de la union varnums. En el listado siguiente se muestra un programa completo para el manejo de este ejemplo. #include <iostream.h> void main() { union nums{ int x ; float y ; double z ; }; nums varnums ; varnums.y = 200000.125 ; varnums.z = 3000000.3333 ; varnums.x = 10 ; // Despliega el valor 10 y dos valores inesperados. cout << varnums.x << " " << varnums.y ; cout << " " << varnums.z << "\n" ; varnums.x="10" ; varnums.z="3000000.3333" ; varnums.y="200000.125" ; // Despliega un valor inesperado, el valor 200000.125 y un valor inesperado. cout << varnums.x << " " << varnums.y ; cout << " " << varnums.z << "\n" ; varnums.x="10" ; varnums.y="200000.125" ; varnums.z="3000000.3333" ; // Despliega dos valores inesperados y 3000000.3333 cout << varnums.x << " " << varnums.y ; cout << " " << varnums.z << "\n" ; } Los miembros de una union pueden ser de cualquier tipo, incluyendo estructuras, como se muestra en el listado siguiente: #include <iostream.h> #include <dos.h> // Para int86() void main() { REGS regs; unsigned int tam ; int86(0x12, &regs, &regs) ; tam = regs.x.ax ; cout << "\nEl tamao de la memoria convencional es de " ; cout << tam << " KiloBytes\n" ; } struct WORDREGS

{ unsigned int ax, bx, cx, dx, si, di, flags ; }; struct BYTEREGS { unsigned char al, ah, bl, bh, cl, ch, dl, dh ; }; union REGS { WORDREGS x ; BYTEREGS h ; }; Puede declararse una union sin etiqueta, y en tal caso se le llama union annima. Ejemplo. #include void main() { union { // Ntese la ausencia de etiqueta int a ; char b ; }; // No se ha definido un tipo union, sino una // union annima. a=1 ; // Se accesan los miembros sin utilizar b='X'; // el operador punto, ya que no existe // un identificador para la union. // Despliega un valor inesperado y el valor X cout << a << " " << b << "\n"; b="X" ; a="1" ; // Despliega el valor 1 y un valor inesperado cout << a << " " << b << "\n"; }

Clases y objetos

Definicin de clases
La definicin de la clase punto en C++ puede tomar la siguiente forma : class punto { int x,y ; // Miembros Datos o Atributos void fun() { return y; } // Funciones Miembros o Metodos

}; Aqu no se est haciendo uso del encapsulamiento, puesto que no se ha declarado ninguna funcin miembro dentro de la clase punto.

Funciones miembro
En otros lenguajes orientados a objetos, a las funciones miembro se les conoce como mtodos . Para incluir una funcin miembro en la definicin de una clase, existen dos formas: 1.- Definir la funcin dentro de la definicin de la clase. Por ejemplo : class punto { int x,y ; int dax() { return x ; } // FUNCION EN LINEA }; 2.- Declarar la funcin dentro de la definicin de la clase y escribir la definicin de la funcin fuera de la definicin de la clase. Por ejemplo : class punto { int x,y ; int dax() ; // DECLARACION DE LA FUNCION MIEMBRO }; int punto::dax() // DEFINICION DE LA FUNCION MIEMBRO { return x ; } En la lnea de cabecera de la definicin de la funcin miembro dax() se utiliza el operador de resolucin de ambito ::. Este operador indica que la funcin dax() es una funcin miembro de la clase punto.

Construtores
Los constructores y los destructores son dos tipos especiales de funciones miembro. Un constructor especifica la manera en que ser creado e inicializado un nuevo objeto de cierta clase. Los constructores en C++ pueden ser definidos por el usuario generados por el lenguaje. El compilador de C++ invoca automticamente al constructor apropiado cada vez que se defina un nuevo objeto. Esto puede ocurrir en una declaracin de datos, cuando se copia un objeto o a travs de la asignacin dinmica de memoria a un nuevo objeto por medio del operador new. Para aadir un constructor a la clase punto escribimos:

class punto { int x,y ; int dax() { return x ; } int day() { return y ; } punto(int nx, int ny) ; // DECLARACION DEL CONSTRUCTOR }; punto::punto(int nx, int ny) // DEFINICION DEL CONSTRUCTOR { x = nx ; y = ny ; }

Destructores
Los destructores destruyen a los objetos creados, liberando la memoria asignada. Pueden ser invocados explcitamente por medio del operador delete. Siguiendo con el ejemplo de la clase punto: class punto { int x,y ; int dax() { return x ; } int day() { return y ; } punto(int nx, int nx) ; ~punto() ; // DECLARACION DEL DESTRUCTOR }; punto::punto(int nx, int nx) { x = nx ; y = ny ; } punto::~punto() // DEFINICION DEL DESTRUCTOR { delete x ; delete y ; }

Ambito de clase
El trmino mbito se refiere al rea dentro de un programa donde un identificador dado es accesible. Para controlar la manera en que son accesados los miembros de las clases, C++ ha introducido el concepto de mbito de clase . Todos los miembros de una clase estn en el mbito de esa clase, de manera que cualquier miembro de una clase puede referenciar a cualquier otro miembro de la misma clase. Las funciones miembro de una clase tienen acceso irrestricto a los miembros dato de esa misma clase. El acceso a los datos y funciones miembro de una clase exterior al mbito de la clase es controlado por el programador a travs de los especificadores de acceso.

Especificadores de acceso

Para el control de acceso a los miembros de una clase, se cuenta con tres palabras clave: private: , protected: y public: . La palabra clave apropiada ( incluyendo los dos puntos ) debe colocarse antes de las declaraciones que van a afectarse. private: Los miembros que siguen pueden ser accesados solamente por las funciones miembro declaradas dentro de la misma clase. protected: Los miembros que siguen pueden ser accesados por funciones miembro declaradas dentro de la misma clase, y por funciones miembro de clases derivadas de esta clase. public: Los miembros que siguen pueden ser accesados desde cualquier lugar dentro del mismo mbito de la definicin de la clase. Por predeterminacin, los miembros de las clases de tipo class son private, los de las clases de tipo struct son public. Por ejemplo, para hacer que las funciones miembro de la clase punto puedan ser accesadas desde cualquier lugar, escribimos: class punto { int x, y ; // privadas POR PREDETERMINACION public: // LO QUE SIGUE ES pblico int dax() { return x; } int day() { return y; } punto ( int nx, int ny ) ; ~punto(); } punto::punto(int nx, int ny) { x = nx ; y = ny ; } punto::~punto() { delete x ; delete y ; }

Acceso privilegiado
Una clase puede darle a otra clase o funcin acceso privilegiado a sus reas privadas. Tal acceso debe darse explcitamente, declarando la otra clase o funcin por medio del modificador friend (amigo). Los amigos son tratados como si fueran miembros de la clase y tienen acceso irrestricto a las reas privadas de los objetos. Por ejemplo :

class cosa { private: int dato ; public: friend void carga( cosa t , int x ) ; }; void carga ( cosa t , int x ) { t.dato = x ; } La definicin de la clase cosa contiene el prototipo para la funcin carga y la disea como un amigo. La funcin carga no es un miembro de la clase cosa, sino que es una funcin independiente a la que se le ha dado acceso especial a los miembros privados de los objetos de tipo cosa. Debido a que es un amigo de la clase cosa, carga puede accesar directamente los datos miembro privados de su parmetro t. Una clase completa puede hacerse un amigo de otra, tal como en: class perico ; class paloma { public: friend class perico ; }; class perico { //CUALQUIER COSA }; En el listado se muestra un ejemplo de utilizacin de la clase punto. #include <iostream.h> class punto { int x,y ; public: punto(int nx, int ny) { x = nx ; y = ny ; } int dax() { return x ; } int day() { return y ; } }; void main() { int datox, datoy ; cout << "COORDENADA EN X : " ; cin>> datox ; cout << "COORDENADA EN y : " ; cin>> datoy ; punto objeto(datox , datoy) ;//INVOCA AL CONSTRUCTOR punto

cout << "X=" << objeto.dax() ; cout << '\n' ; cout << " Y=" << objeto.day() ; cout << '\n' ; }

MANEJO DE ARCHIVOS O FICHEROS.


As como hemos revisado la salida y entrada por pantalla y teclado respectivamente, veremos ahora la entrada y/o salida de datos utilizando ficheros, lo cual ser imprescindible para un gran nmero de aplicaciones que deseemos desarrollar.

Ficheros
El estndar de C contiene funciones varias para la edicin de ficheros, estas estn definidas en la cabecera stdio.h y por lo general empiezan con la letra f, haciendo referencia a file. Adicionalmente se agrega un tipo FILE, el cual se usar como apuntador a la informacin del fichero. La secuencia que usaremos para realizar operaciones ser la siguiente: Crear un apuntador del tipo FILE *

Abrir el archivo utilizando la funcin fopen y asignndole el resultado de la llamada a nuestro apuntador. Hacer las diversas operaciones (lectura, escritura, etc). Cerrar el archivo utilizando la funcin fclose.

fopen
sta funcin sirve para abrir y crear ficheros en disco. El prototipo correspondiente de fopen es: FILE * fopen (const char *filename, const char *opentype); Los parmetros de entrada de fopen son: filename: una cadena que contiene un nombre de fichero vlido. opentype: especifica en tipo de fichero que se abrir o se crear.

Una lista de parmetros opentype para la funcin fopen son: "r" : abrir un archivo para lectura, el fichero debe existir. "w" : abrir un archivo para escritura, se crea si no existe o se sobreescribe si existe. "a" : abrir un archivo para escritura al final del contenido, si no existe se crea. "r+" : abrir un archivo para lectura y escritura, el fichero debe existir. "w+" : crear un archivo para lectura y escritura, se crea si no existe o se sobreescribe si existe. "a+" : abrir/crear un archivo para lectura y escritura al final del contenido

Adicionalmente hay tipos utilizando "b" (binary) los cuales no sern mostrados por ahora y que solo se usan en los sistemas operativos que no pertenecen a la familia de unix.

fclose
Esta funcin sirve para poder cerrar un fichero que se ha abierto. El prototipo correspondiente de fclose es: int fclose (FILE *stream); Un valor de retorno cero indica que el fichero ha sido correctamente cerrado, si ha habido algn error, el valor de retorno es la constante EOF. Un ejemplo pequeo para abrir y cerrar el archivo llamado fichero.in en modo lectura: #include <stdio.h> int main(int argc, char** argv) { FILE *fp; fp = fopen ( "fichero.in", "r" ); fclose ( fp ); return 0;

} Como vemos, en el ejemplo se utiliz el opentype "r", que es para la lectura. Otra cosa importante es que el lenguaje C no tiene dentro de si una estructura para el manejo de excepciones o de errores, por eso es necesario comprobar que el archivo fue abierto con xito "if (archivo == NULL)". Si fopen pudo abrir el archivo con xito devuelve la referencia al archivo (FILE *), de lo contrario devuelve NULL y en este caso se debera revisar la direccion del archivo o los permisos del mismo. En estos ejemplos solo vamos a dar una salida con un retorno de 1 que sirve para sealar que el programa termino por un error.

feof
Esta funcin sirve para determinar si el cursor dentro del archivo encontr el final ( end of file). Existe otra forma de verificar el final del archivo que es comparar el caracter que trae fgetc del archivo con el macro EOF declarado dentro de stdio.h, pero este mtodo no ofrece la misma seguridad (en especial al tratar con los archivos "binarios"). La funcin feof siempre devolver cero (Falso) si no es encontrado EOF en el archivo, de lo contrario regresar un valor distinto de cero (Verdadero). El prototipo correspondiente de feof es: int feof(FILE *fichero);

rewind
Literalmente significa "rebobinar", sita el cursor de lectura/escritura al principio del archivo. El prototipo correspondiente de rewind es: void rewind(FILE *fichero);

Lectura
Un archivo generalmente debe verse como un string (una cadena de caracteres) que esta guardado en el disco duro. Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podramos usar para leer un archivo son: int fgetc(FILE *archivo) char *fgets(char *buffer, int tamano, FILE *archivo) size_t fread(void *puntero, size_t tamano, size_t cantidad, FILE *archivo); int fscanf(FILE *fichero, const char *formato, argumento, ...);

Las primeras dos de estas funciones son muy parecidas entre si. Pero la tercera, por el numero y el tipo de parmetros, nos podemos dar cuenta de que es muy diferente, por eso la trataremos aparte junto al fwrite que es su contraparte para escritura.

fgetc
Esta funcin lee un caracter a la vez del archivo que esta siendo sealado con el puntero *archivo. En caso de que la lectura sea exitosa devuelve el caracter ledo y en caso de que no lo sea o de encontrar el final del archivo devuelve EOF.

El prototipo correspondiente de fgetc es: char fgetc(FILE *archivo); Esta funcin se usa generalmente para recorrer archivos de texto. A manera de ejemplo vamos a suponer que tenemos un archivo de texto llamado "prueba.txt" en el mismo directorio en que se encuentra el fuente de nuestro programa. Un pequeo programa que lea ese archivo ser: #include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracter; archivo = fopen("prueba.txt","r"); if (archivo == NULL) exit(1); printf("\nEl contenido del archivo de prueba es \n\n"); while (feof(archivo) == 0) { caracter = fgetc(archivo); printf("%c",caracter); } return 0; }

fgets
Esta funcin est diseada para leer cadenas de caracteres. Leer hasta n-1 caracteres o hasta que lea un retorno de lnea. En este ltimo caso, el carcter de retorno de lnea tambin es ledo. El prototipo correspondiente de fgets es: char *fgets(char *buffer, int tamano, FILE *archivo); El primer parmetro buffer lo hemos llamado as porque es un puntero a un espacio de memoria del tipo char (podramos usar un arreglo de char). El segundo parmetro es tamano que es el limite en cantidad de caracteres a leer para la funcion fgets. Y por ultimo el puntero del archivo por supuesto que es la forma en que fgets sabra a que archivo debe leer. #include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracteres[100]; archivo = fopen("prueba.txt","r");

if (archivo == NULL) exit(1); printf("\nEl contenido del archivo de prueba es \n\n"); while (feof(archivo) == 0) { fgets(caracteres,100,archivo); printf("%s",caracteres); } return 0; } Este es el mismo ejemplo de antes con la diferencia de que este hace uso de fgets en lugar de fgetc. La funcin fgets se comporta de la siguiente manera, leer del archivo apuntado por archivo los caracteres que encuentre y a ponerlos en buffer hasta que lea un caracter menos que la cantidad de caracteres especificada en tamano o hasta que encuentre el final de una linea (\n) o hasta que encuentre el final del archivo ( EOF). En este ejemplo no vamos a profundizar mas que para decir que caracteres es un buffer, los pormenores seran explicados en la seccin de manejo dinmico de memoria. El beneficio de esta funcin es que se puede obtener una linea completa a la vez. Y resulta muy til para algunos fines como la construccin de un parser de algn tipo de archivo de texto.

fread
Para la lectura de ficheros se utilizar la funcin fread, la cual sirve para leer contenidos de un fichero. El prototipo correspondiente de fread es: size_t fread (void *data, size_t size, size_t count, FILE *stream); En estas definiciones se usa el tipo size_t, el cul est definidio en stddef.h y sirve para definir tamaos de objetos. Lo que recibe esta funcin es un puntero donde almacenaremos los datos ledos (comunmente llamado buffer), el tamao de los datos a leer, la cantidad de esos datos a leer y el apuntador al fichero. Aqu hay un ejemplo simple para leer los primeros 100 caracteres de un fichero y almacenarlos en un buffer: #include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buffer[100]; fp = fopen ( "fichero.in", "r+" ); fread ( buffer, sizeof ( char ), 100, fp ); printf("%s", buffer); fclose ( fp ); return 0; }

fscanf
La funcin fscanf funciona igual que scanf en cuanto a parmetros, pero la entrada se toma de un fichero en lugar del teclado. El prototipo correspondiente de fscanf es: int fscanf(FILE *fichero, const char *formato, argumento, ...); Podemos ver un ejemplo de su uso, abrimos el documento "fichero.txt" en modo lectura y leyendo dentro de el. #include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buffer[100]; fp = fopen ( "fichero.txt", "r" ); fscanf(fp, "%s" ,buffer); printf("%s", buffer); fclose ( fp ); return 0; }

Escritura
As como podemos leer datos desde un fichero, tambin se pueden crear y escribir ficheros con la informacin que deseamos almacenar, Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podramos usar para escribir dentro de un archivo son: int fputc(int caracter, FILE *archivo) int fputs(const char *buffer, FILE *archivo) size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo); int fprintf(FILE *archivo, const char *formato, argumento, ...);

fputc
Esta funcin escribe un carcter a la vez del archivo que esta siendo sealado con el puntero *archivo. El valor de retorno es el carcter escrito, si la operacin fue completada con xito, en caso contrario ser EOF. El prototipo correspondiente de fputc es: int fput(int carcter, FILE *archivo); Mostramos un ejemplo del uso de fputc en un "fichero.txt", se escribira dentro del fichero hasta que presionemos la tecla enter.

#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char caracter; fp = fopen ( "fichero.txt", "r+" ); printf("\nIntrouce un texto al fichero: "); while((caracter = getchar()) != '\n') { printf("%c", fputc(caracter, fp)); } fclose ( fp ); return 0; }

fputs
La funcin fputs escribe una cadena en un fichero. No se aade el carcter de retorno de lnea ni el carcter nulo final. El valor de retorno es un nmero no negativo o EOF en caso de error. Los parmetros de entrada son la cadena a escribir y un puntero a la estructura FILE del fichero donde se realizar la escritura. El prototipo correspondiente de fputs es: int fputs(const char *buffer, FILE *archivo) para ver su funcionamiento mostramos el siguiente ejemplo: #include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char cadena[] = "Mostrando el uso de fputs en un fichero.\n"; fp = fopen ( "fichero.txt", "r+" ); fputs( cadena, fp ); fclose ( fp ); return 0; }

fwrite
Esta funcin est pensada para trabajar con registros de longitud constante y forma pareja con fread. Es capaz de escribir hacia un fichero uno o varios registros de la misma longitud almacenados a partir de una direccin de memoria determinada. El valor de retorno es el nmero de registros escritos, no el nmero de bytes. Los parmetros son: un puntero a la zona

de memoria donde se almacenarn los datos ledos, el tamao de cada registro, el nmero de registros a leer y un puntero a la estructura FILE del fichero del que se har la lectura. El prototipo correspondiente de fwrite es: size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo); Un ejemplo concreto del uso de fwrite con su contraparte fread y usando funciones es:

#include <stdio.h> void menu(); void CrearFichero(FILE *Fichero); void InsertarDatos(FILE *Fichero); void VerDatos(FILE *Fichero); struct sRegistro { char Nombre[25]; int Edad; float Sueldo; } registro; int main(int argc, char** argv) { int opcion; int exit = 0; FILE *fichero; while (!exit) { menu(); printf("\nOpcion: "); scanf("%d", &opcion); switch(opcion) { case 1: CrearFichero(fichero); break; case 2: InsertarDatos(fichero); break; case 3: VerDatos(fichero); break; case 4: exit = 1; break; default: printf("\nopcion no valida"); } } return 0; } void menu() { printf("\nMenu:");

printf("\n\t1. Crear fichero"); printf("\n\t2. Insertar datos"); printf("\n\t3. Ver datos"); printf("\n\t4. Salir"); } void CrearFichero(FILE *Fichero) { Fichero = fopen("fichero", "r"); if(!Fichero) { Fichero = fopen("fichero", "w"); printf("\nArchivo creado!"); } else { printf("\nEl fichero ya existe!"); } fclose (Fichero); return; } void InsertarDatos(FILE *Fichero) { Fichero = fopen("fichero", "r+"); if(Fichero == NULL) { printf("\nFichero no existe! \nPor favor creelo"); return; } printf("\nDigita el nombre: "); scanf("%s", &registro.Nombre); printf("\nDigita la edad: "); scanf("%d", &registro.Edad); printf("\nDigita el sueldo: "); scanf("%f", &registro.Sueldo); fwrite(&registro, sizeof(struct sRegistro), 1, Fichero); fclose(Fichero); return; } void VerDatos(FILE *Fichero) { int numero = 1; Fichero = fopen("fichero", "r"); if(Fichero == NULL) { printf("\nFichero no existe! \nPor favor creelo");

return; } fread(&registro, sizeof(struct sRegistro), 1, Fichero); printf("\nNumero \tNombre \tEdad \tSueldo"); while(!feof(Fichero)) { printf("\n%d \t%s \t%d \t%.2f", numero, registro.Nombre, registro.Edad, registro.Sueldo); fread(&registro, sizeof(struct sRegistro), 1, Fichero); numero++; } fclose(Fichero); return; }

fprintf
La funcin fprintf funciona igual que printf en cuanto a parmetros, pero la salida se dirige a un fichero en lugar de a la pantalla. El prototipo correspondiente de fprintf es: int fprintf(FILE *archivo, const char *formato, argumento, ...); Podemos ver un ejemplo de su uso, abrimos el documento "fichero.txt" en modo lectura/escritura y escribimos dentro de el. #include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buffer[100] = "Esto es un texto dentro del fichero."; fp = fopen ( "fichero.txt", "r+" ); fprintf(fp, buffer); fprintf(fp, "%s", "\nEsto es otro texto dentro del fichero."); fclose ( fp ); return 0; }

You might also like