Você está na página 1de 22

Apuntadores en C o C++ -Ejemplo>> DOMINGO, ENERO 25, 2009

Debido a una peticion de poner un ejemplo acerca de el uso de apuntadores ver postApuntadores en C o C++ -Conceptos-, expondr un ejemplo prctico del uso de apuntadores. Este ejemplo tratar de que sea sencillo, simplemente es una lista donde se almacenan nmeros, se dan de alta y se pueden dar de baja y se pueden listar. Eso es todo lo que hara el programita. Primero mostrar el ejemplo ejecutndose, y al finalizar mostrar el cdigo fuente. Como es por demas evidente, la importancia de esto no es el "qu", sino el "cmo", en otras palabras, no se usar ningun programa visual, ni UI ni mucho menos.

Este seria la pantalla principal

Al seleccionar la opcion 1, podemos insertar el numero, digamos el 100 El programa lo inserta en la lista, y si hay ms de uno lo insertar al final. Insertemos algunos numeros...

Los listamos

Digamos que ahora queremos borrar el dato "102" que ocupa la posicion 3 de la lista. (no me salgas que es la posicion 2 OK?)

Los volvemos a listar:

Ahora el codigo fuente: void Menu(); void Insertar(); void Eliminar(); int MostrarDatos(); void Salir(); typedef struct Lista { int dato; struct Lista *siguiente; } tipoNodo;

Lista *Inicio; //Apuntador al inicio de la lista tipoNodo *pNodo; typedef tipoNodo *List; int main(int argc, char* argv[]) { Menu(); getch(); return 0; } //--- Funciones -------------void Menu(){ int opc; clrscr(); cout<<"1.- Insertar elemento en la lista"<<endl; cout<<"2.- Eliminar Elemento de la lista"<<endl; cout<<"3.- Mostrar datos de la lista"<<endl;

cout<<"4.- Salir"<<endl; cin>>opc; switch(opc){ case 1: Insertar(); getch(); Menu(); break; case 2: Eliminar(); MostrarDatos(); getch(); Menu(); break; case 3: MostrarDatos(); getch(); Menu(); break; case 4: Salir(); break; default: cout<<"Esa opcion no existe, elige una de las disponibles"<<endl; getch(); Menu(); break; } } void Insertar(){ tipoNodo *ante, *ptr; /// ante apuntador que apunta al registro anterior // ptr apuntador que se va a ir moviendo.... pNodo siempre apunta al principio Lista *tmpNodo= new(tipoNodo); // tmpNodo apuntador temporal, es el nuevo registro que se va a insertar. if(!tmpNodo) //verifica si pudo obtener memoria { cout<<"Memoria Agotada"; return; } cout<<"Ingresa el numero"<<endl; cin>>tmpNodo->dato; cout<< "Dato Ingresado: "; cout<< tmpNodo->dato; if (!pNodo) { /*si esta vacia la lista, lo pone al inicio*/ tmpNodo->siguiente=NULL; pNodo=tmpNodo; return; } ante=NULL; //apuntador que apunta al registro anterior ptr=pNodo; //apuntador que se va a ir moviendo.... pNodo siempre apunta al principio while (ptr) { ante=ptr; ptr=ptr->siguiente; } /* inserto al final */ ante->siguiente=tmpNodo; tmpNodo->siguiente=NULL; return; } void Eliminar(){ int DatoAEliminar, contador, numelementos; tipoNodo *ante, *ptr; contador=0;

ante=NULL; //apuntador que apunta al registro anterior //apuntador que se va a ir moviendo.... pNodo siempre apunta al principio ptr=pNodo; numelementos=MostrarDatos(); //Muestra los datos como referencia al usuario. cout<< "\n\r Dame el # del dato a eliminar: "; cin>> DatoAEliminar; if (DatoAEliminar>numelementos){ cout<< "Elemento no existe, seleccione uno de la lista"; return; } while (ptr) { ++contador; if (contador == DatoAEliminar){ // lo encontro, entonces lo borra if (pNodo->siguiente == NULL) { //Es el primer elemento y el unico pNodo=NULL; return; } if (pNodo->siguiente == ptr->siguiente){ //es el primero, pero hay otros enlazados pNodo=ptr->siguiente; return; } if (ptr->siguiente != NULL){ // es alguien de enmedio a borrar ante->siguiente=ptr->siguiente; ptr=NULL; return; } else { //es el ultimo ante->siguiente=NULL; ptr=NULL; return; } } //se va al siguiente ante=ptr; ptr=ptr->siguiente; } return; } int MostrarDatos(){ tipoNodo *ptr, *ante; int contador; contador=0; ptr=pNodo; clrscr(); cout<< "- Listado - \n\r"; cout<< "\n\r"; cout<< " # : Dato \n\r"; cout<< "--- ----- \n\r"; while(ptr) { ante=ptr; cout<< ++contador; cout<< " : "; cout<<ante->dato; cout <<"\n\r"; ptr=ptr->siguiente; } return contador; } void Salir(){ cout<<"Pulsa una tecla para salir"<<endl; }

// EJ09_02.CPP // Invierte una cadena #include <iostream.h> void Invertir(char *s) { char *t= s; while (*t) t++; // Pongo t al final de la cadena. En el caracter '\0' t--; // Me pongo en el anterior

while(s < t) { char Temp= *s; *s++= *t; *t--= Temp; } }

// Hasta que lleguemos al medio

// Intercambiar

void main() { char *s= "SOLOS NABOS ES AVE Y NADA"; Invertir(s); cout << s; }

Uso de los operadores & y *


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> int main() { int a; entero */

/* a es un entero */

int *ptrA;

/* ptrA es un apuntador a un

a = 7; ptrA = &a; /* ptrA toma la direcci n de a */ printf( "La direccion de a es %p" "nEl valor de ptrA es %p", &a, ptrA ); printf( "nnEl valor de a es %d" "nEl valor de *ptrA es %d", a, *ptrA ); printf( "nnMuestra de que * y & son complementos " "uno del otron&*ptrA = %p" "n*&ptrA = %pn", &*ptrA, *&ptrA ); return 0; /* indica terminaci n exitosa */ } /* fin de main */

Eleva al cubo una variable mediante una llamada por valor


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> int cuboPorValor( int n ); /* prototipo */ int main(){ int numero = 5; /* inicializa numero */ printf( "El valor original de numero es %d", numero ); /* pasa numero por valor a cuboPorValor */ numero = cuboPorValor( numero ); printf( "nEl nuevo valor de numero es %dn", numero ); return 0; /* indica terminaci n exitosa */ } /* fin de main */ /* calcula y devuelve el cubo de un argumento entero */int cuboPorValor( int n )

18 { 19 return n * n * n; /* eleva al cubo la variable local n y devuelve el resultado */ 20 21 } /* fin de la funci n cuboPorValor */ 22 23 24 25

Este programa coloca valores dentro de un arreglo, ordena los valores en orden ascendente, e imprime los resultados del arreglo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <stdio.h> #define TAMANIO 10 void ordenamBurbuja( int * const arreglo, const int tamanio ); /* prototipo */ int main() { /* inicializa el arreglo a */ int a[ TAMANIO ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; int i; /* contador */ printf( "Elementos de datos en el orden originaln" ); /* ciclo a trav s del arreglo a */ printf( "%4d", a[ i ] ); } /* fin de for */ for ( i = 0; i < TAMANIO; i++ ) {

ordenamBurbuja( a, TAMANIO ); /* ordena el arreglo */ printf( "nElementos de datos en orden ascendenten" ); /* ciclo a trav s del arreglo a */ for ( i = 0; i < TAMANIO; i++ ) { } /* fin de for */ printf( "%4d", a[ i ] );

printf( "n" ); return 0; /* indica terminaci n exitosa */ } /* fin de main */ /* ordena un arreglo de enteros mediante el uso del algoritmo de la burbuja */void ordenamBurbuja( int * const arreglo, const int tamanio ) { void intercambia( int *ptrElemento1, int *ptrElemento2 ); /* prototipo */ int pasada; /* contador de pasadas */ int j; /* contador de comparaciones */ /* ciclo para controlar las pasadas */ for ( pasada = 0; pasada < tamanio - 1; pasada++ ) { /* ciclo para controla las comparaciones durante cada pasada */ j < tamanio - 1; j++ ) { for ( j = 0;

/* intercambia los elementos adyacentes si no est n en orden */ if ( arreglo[ j ] > arreglo[ j + 1 ] ) { intercambia( &arreglo[ j ], &arreglo[ j + 1 ] ); } /* fin de if */ } /* fin del for interno */ } /* fin del for externo */ } /* fin de la funci n ordenamBurbuja */

49 50 /* intercambia los valores en las ubicaciones de memoria a los cuales apunta 51 ptrElemento1 y ptrElemento2 */void intercambia( int *ptrElemento1, int *ptrElemento2 ) 52 53 { int almacena = *ptrElemento1; 54 *ptrElemento1 = *ptrElemento2; 55 *ptrElemento2 = almacena;} /* fin de la funci n intercambia */ 56 57 58 59 60 61 62 63 64 65 66

1 comentarios en "Este programa coloca valores dentro de un arreglo, ordena los valores en orden ascendente, e imprime los resultados del arreglo."

Programa para barajar y repartir cartas


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <stdio.h> #include <stdlib.h> #include <time.h> /* prototipos */void baraja( int wMazo[][ 13 ] ); void reparte( const int wMazo[][ 13 ], const char *wCara[], const char *wPalo[] ); int main(){ /* inicializa el arreglo palo */ const char *palo[ 4 ] = { "Corazones", "Diamantes", "Treboles", "Espadas" }; /* inicializa el arreglo cara */ const char *cara[ 13 ] = { "As", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Ocho", "Nueve", "Diez", "Sota", "Reyna", "Rey" }; /* inicializa el arreglo mazo */ int mazo[ 4 ][ 13 ] = { 0 }; srand( time( 0 ) ); /* semilla del generador de n meros aleatorios */ baraja( mazo ); reparte( mazo, cara, palo ); return 0; /* indica terminaci n exitosa */ } /* fin de main */ /* baraja las cartas del mazo */ void baraja( int wMazo[][ 13 ] ) { int fila; /* n mero de fila */ int columna; /* n mero de columna */ int carta; /* contador */ /* elige aleatoriamente un espacio para cada una de las 52 cartas */ 1; carta <= 52; carta++ ) { */ do { fila = rand() % 4; columna = rand() % 13; for ( carta =

/* elije una nueva ubicaci n al azar hasta que encuentra un espacio vac o

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

} while( wMazo[ fila ][ columna ] != 0 ); /* fin de do...while */ /* coloca el n mero de carta en el espacio vac o del mazo */ } /* fin de for */ wMazo[ fila ][ columna ] = carta; } /* fin de la funci n baraja */ /* reparte las cartas del mazo *wCara[], const char *wPalo[] { int carta; /* contador de int fila; /* contador de */void reparte( const int wMazo[][ 13 ], const char ) cartas */ filas */ int columna; /* contador de columnas */

/* reparte cada una de las 52 cartas */ for ( carta = 1; carta <= 52; carta++ ) { /* realiza el ciclo a trav s de las filas de wMazo */ for ( fila = 0; fila <= 3; fila++ ) { /* realiza el ciclo a trav s de las columnas de wMazo en la fila actual */ for ( columna = 0; columna <= 12; columna++ ) { /* si el espacio contiene la carta actual, despliega la carta */ if ( wMazo[ fila ][ columna ] == carta ) { printf( "%6s de %-9s%c", wCara[ columna ], wPalo[ fila ], } /* fin de if */ carta % 2 == 0 ? 'n' : 't' ); } /* fin de for */ } /* fin de for */ } /* fin de for */ } /* fin de la funci n reparte */

Demostracion de un arreglo de apuntadores a funciones #include <stdio.h>


/* prototipos */ void funcion1( int a ); void funcion2( int b ); void funcion3( int c ); int main() { /* inicializa el arreglo de 3 apuntadores a funciones que toman cada una un argumento entero y devuelven void */ void (*f[ 3 ])( int ) = { funcion1, funcion2, funcion3 }; int eleccion; /* variable para almacenar la elecci n del usuario */ printf( "Introduzca un numero entre 0 y 2, 3 para terminar: " ); scanf( "%d", &eleccion ); /* procesa la elecci n del usuario */ while ( eleccion >= 0 && eleccion < 3 ) { /* invoca la funci n en la ubicaci n de la eleccion en el arreglo f y pasa la elecci n como argumento */ (*f[ eleccion ])( eleccion ); printf( "Introduzca un numero entre 0 y 2, 3 para terminar: "); scanf( "%d", &eleccion ); } /* fin de while */

printf( "Termina le ejecucion del programa.n" ); return 0; /* indica terminaci n exitosa */ } /* fin de main */ void funcion1( int a ) { printf( "Usted introdujo %d de manera que invoco a la funcion1nn", a ); } /* fin de la funcion1 */ void funcion2( int b ) { printf( "Usted introdujo %d de manera que invoco a la funcion2nn", b ); } /* fin de la funcion2 */ void funcion3( int c ) { printf( "Usted introdujo %d de manera que invoco a la funcion2nn", c ); } /* fin de la funcion3 */ 8. Apuntadores Los apuntadores son una parte fundamental de C. Si usted no puede usar los apuntadores apropiadamente entonces esta perdiendo la potencia y la flexibilidad que C ofrece bsicamente. El secreto para C esta en el uso de apuntadores. C usa los apuntadores en forma extensiva. Porqu? Es la nica forma de expresar algunos clculos. Se genera cdigo compacto y eficiente. Es una herramienta muy poderosa. C usa apuntadores explcitamente con: Es la nica forma de expresar algunos clculos. Se genera cdigo compacto y eficiente. Es una herramienta muy poderosa. C usa apuntadores explcitamente con: Arreglos, Estructuras y Funciones 8.1 Definicin de un apuntador Un apuntador es una variable que contiene la direccin en memoria de otra variable. Se pueden tener apuntadores a cualquier tipo de variable. El operador unario o mondico & devuelve la direccin de memoria de una variable. El operador de indireccin o dereferencia * devuelve el ``contenido de un objeto apuntado por un apuntador''. Para declarar un apuntador para una variable entera hacer: int *apuntador; Se debe asociar a cada apuntador un tipo particular. Por ejemplo, no se puede asignar la direccin de un short int a un long int. Para tener una mejor idea, considerar el siguiente cdigo: main() { int x = 1, y = 2; int *ap; ap = &x; y = *ap;

x = ap; *ap = 3; } Cuando se compile el cdigo se mostrar el siguiente mensaje: warning: assignment makes integer from pointer without a cast. Con el objetivo de entender el comportamiento del cdigo supongamos que la variable x esta en la localidad de la memoria 100, y en 200 y ap en 1000. Nota: un apuntador es una variable, por lo tanto, sus valores necesitan ser guardados en algn lado. int x = 1, y = 2; int *ap; ap = &x; 100 200 1000 x 1 y 2 ap 100 Las variables x e y son declaradas e inicializadas con 1 y 2 respectivamente, ap es declarado como un apuntador a entero y se le asigna la direccin de x (&x). Por lo que ap se carga con el valor 100. y = *ap; 100 200 1000 x 1 y 1 ap 100 Despus y obtiene el contenido de ap. En el ejemplo ap apunta a la localidad de memoria 100 -- la localidad de x. Por lo tanto, y obtiene el valor de x -- el cual es 1. x = ap; 100 200 1000 x 100 y 1 ap 100 Como se ha visto C no es muy estricto en la asignacin de valores de diferente tipo (apuntador a entero). As que es perfectamente legal (aunque el compilador genera un aviso de cuidado) asigna el valor actual de ap a la variable x. El valor de ap en ese momento es 100. *ap = 3; 100 200 1000 x 3 y 1 ap 100 Finalmente se asigna un valor al contenido de un apuntador (*ap). Importante: Cuando un apuntador es declarado apunta a algn lado. Se debe inicializar el apuntador antes de usarlo. Por lo que: main() { int *ap; *ap = 100; } puede generar un error en tiempo de ejecucin o presentar un comportamiento errtico. El uso correcto ser: main() { int *ap; int x; ap = &x; *ap = 100; } Con los apuntadores se puede realizar tambin aritmtica entera, por ejemplo: main() { float *flp, *flq; *flp = *flp + 10; ++*flp;

(*flp)++; flq = flp; } NOTA: Un apuntador a cualquier tipo de variables es una direccin en memoria -- la cual es una direccin entera, pero un apuntador NO es un entero. La razn por la cual se asocia un apuntador a un tipo de dato, es por que se debe conocer en cuantos bytes esta guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador por un ``bloque'' de memoria, en donde el bloque esta en funcin del tamao del dato. Por lo tanto para un apuntador a un char, se agrega un byte a la direccin y para un apuntador a entero o a flotante se agregan 4 bytes. De esta forma si a un apuntador a flotante se le suman 2, el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes. 8.2 Apuntadores y Funciones Cuando C pasa argumentos a funciones, los pasa por valor, es decir, si el parmetro es modificado dentro de la funcin, una vez que termina la funcin el valor pasado de la variable permanece inalterado. Hay muchos casos que se quiere alterar el argumento pasado a la funcin y recibir el nuevo valor una vez que la funcin ha terminado. Para hacer lo anterior se debe usar una llamada por referencia, en C se puede simular pasando un puntero al argumento. Con esto se provoca que la computadora pase la direccin del argumento a la funcin. Para entender mejor lo anterior consideremos la funcin swap() que intercambia el valor de dos argumentos enteros: void swap(int *px, int *py); main() { int x, y; x = 10; y = 20; printf("x=%d\ty=%d\n",x,y); swap(&x, &y); printf("x=%d\ty=%d\n",x,y); } void swap(int *px, int *py) { int temp; temp = *px; /* guarda el valor de la direccion x */ *px = *py; /* pone y en x */ *py = temp; /* pone x en y */ } 8.3 Apuntadores y arreglos Existe una relacin estrecha entre los punteros y los arreglos. En C, un nombre de un arreglo es un ndice a la direccin de comienzo del arreglo. En esencia, el nombre de un arreglo es un puntero al arreglo. Considerar lo siguiente: int a[10], x; int *ap; ap = &a[0]; /* ap apunta a la direccion de a[0] */ x = *ap; /* A x se le asigna el contenido de ap (a[0] en este caso) */

*(ap + 1) = 100; /* Se asigna al segundo elemento de 'a' el valor 100 usando ap*/ Como se puede observar en el ejemplo la sentencia a[t] es idntica a ap+t. Se debe tener cuidado ya que C no hace una revisin de los lmites del arreglo, por lo que se puede ir fcilmente ms alla del arreglo en memoria y sobreescribir otras cosas. C sin embargo es mucho ms stil en su relacin entre arreglos y apuntadores. Por ejemplo se puede teclear solamente:

ap = a; en vez de ap = &a[0]; y tambin *(a + i) en vez de a[i], esto es, &a[i] es equivalente con a+i. Y como se ve en el ejemplo, el direccionamiento de apuntadores se puede expresar como: a[i] que es equivalente a *(ap + i) Sin embargo los apuntadores y los arreglos son diferentes: Un apuntador es una variable. Se puede hacer ap = a y ap++. Un arreglo NO ES una variable. Hacer a = ap y a++ ES ILEGAL. Este parte es muy importante, asegrese haberla entendido. Con lo comentado se puede entender como los arreglos son pasados a las funciones. Cuando un arreglo es pasado a una funcin lo que en realidad se le esta pasando es la localidad de su elemento inicial en memoria. Por lo tanto: strlen(s) es equivalente a strlen(&s[0]) Esta es la razn por la cual se declara la funcin como: int strlen(char s[]); y una declaracin equivalente es int strlen(char *s); ya que char s[] es igual que char *s. La funcin strlen() es una funcin de la biblioteca estndar que regresa la longitud de una cadena. Se muestra enseguida la versin de esta funcin que podra escribirse: int strlen(char *s) { char *p = s; while ( *p != '\0' ) p++; return p - s; } Se muestra enseguida una funcin para copiar una cadena en otra. Al igual que en el ejercicio anterior existe en la biblioteca estndar una funcin que hace lo mismo. void strcpy(char *s, char *t) { while ( (*s++ = *t++) != '\0' ); } En los dos ltimos ejemplos se emplean apuntadores y asignacin por valor. Nota: Se emplea el uso del caracter nulo con la sentencia while para encontrar el fin de la cadena.

8.4 Arreglos de apuntadores En C se pueden tener arreglos de apuntadores ya que los apuntadores son variables. A continuacin se muestra un ejemplo de su uso: ordenar las lneas de un texto de diferente longitud. Los arreglos de apuntadores son una representacin de datos que manejan de una forma eficiente y conveniente lneas de texto de longitud variable. Cmo se puede hacer lo anterior? Guardar todas las lneas en un arreglo de tipo char grande. Observando que \n marca el fin de cada lnea. Ver figura 8.1. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primer caracter de cada lnea. Comparar dos lneas usando la funcin de la biblioteca estndar strcmp(). Si dos lneas estn desacomodadas -- intercambiar (swap) los apuntadores (no el texto). Figura 8.1: Arreglos de apuntadores (Ejemplo de ordenamiento de cadenas). Con lo anterior se elimina: el manejo complicado del almacenamiento. alta sobrecarga por el movimiento de lneas. 8.5 Arreglos multidimensionales y apuntadores

Un arreglo multidimensional puede ser visto en varias formas en C, por ejemplo: Un arreglo de dos dimensiones es un arreglo de una dimensin, donde cada uno de los elementos es en s mismo un arreglo. Por lo tanto, la notacin a[n][m] nos indica que los elementos del arreglo estn guardados rengln por rengln. Cuando se pasa una arreglo bidimensional a una funcin se debe especificar el nmero de columnas -- el nmero de renglones es irrelevante. La razn de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para que pueda brincar de rengln en rengln en la memoria. Considerando que una funcin deba recibir int a[5][35], se puede declarar el argumento de la funcin como: f( int a[][35] ) { ..... } o an f( int (*a)[35] ) { ..... } En el ltimo ejemplo se requieren los parnteis (*a) ya que [ ] tiene una precedencia ms alta que *. Por lo tanto: int (*a)[35]; declara un apuntador a un arreglo de 35 enteros, y por ejemplo si hacemos la siguiente referencia a+2, nos estaremos refiriendo a la direccin del primer elemento que se encuentran en el tercer rengln de la matriz supuesta, mientras que int *a[35]; declara un arreglo de 35 apuntadores a enteros. Ahora veamos la diferencia (sutil) entre apuntadores y arreglos. El manejo de cadenas es una aplicacin comn de esto. Considera: char *nomb[10]; char anomb[10][20]; En donde es vlido hacer nomb[3][4] y anomb[3][4] en C. Sin embargo: anomb es un arreglo verdadero de 200 elementos de dos dimensiones tipo char. El acceso de los elementos anomb en memoria se hace bajo la siguiente frmula 20*renglon + columna + direccin_base En cambio nomb tiene 10 apuntadores a elementos. NOTA: si cada apuntador en nomb indica un arreglo de 20 elementos entonces y solamente entonces 200 chars estarn disponibles (10 elementos). Con el primer tipo de declaracin se tiene la ventaja de que cada apuntador puede apuntar a arreglos de diferente longitud. Considerar: char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... }; char anomb[][15] = { "No mes", "Ene", "Feb", "Mar", ... }; Lo cual grficamente se muestra en la figura 8.2. Se puede indicar que se hace un manejo ms eficiente del espacio haciendo uso de un arreglo de apuntadores y usando un arreglo bidimensional. Figura 8.2: Arreglo de 2 dimensiones VS. arreglo de apuntadores. 8.6 Inicializacin esttica de arreglos de apuntadores La inicializacin de arreglos de apuntadores es una aplicacin ideal para un arreglo esttico interno, por ejemplo: func_cualquiera() {

static char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... }; } Recordando que con el especificador de almacenamiento de clase static se reserva en forma permanente memoria el arreglo, mientras el cdigo se esta ejecutando. 8.7 Apuntadores y estructuras Los apuntadores a estructuras se definen fcilmente y en una forma directa. Considerar lo siguiente: main() { struct COORD { float x,y,z; } punto; struct COORD *ap_punto; punto.x = punto.y = punto.z = 1; ap_punto = &punto; /* Se asigna punto al apuntador */ ap_punto->x++; /* Con el operador -> se accesan los miembros */ ap_punto->y+=2; /* de la estructura apuntados por ap_punto */ ap_punto->z=3; } Otro ejemplo son las listas ligadas: typedef struct { int valor; struct ELEMENTO *sig; } ELEMENTO; ELEMENTO n1, n2; n1.sig = &n2; La asignacin que se hace corresponde a la figura 8.3 Figura 8.3: Esquema de una lista ligada con 2 elementos. Nota: Solamente se puede declarar sig como un apuntador tipo ELEMENTO. No se puede tener un elemento del tipo variable ya que esto generara una definicin recursiva la cual no esta permitida. Se permite poner una referencia a un apuntador ya que los los bytes se dejan de lado para cualquier apuntador. 8.8 Fallas comunes con apuntadores A continuacin se muestran dos errores comunes que se hacen con los apuntadores. No asignar un apuntador a una direccin de memoria antes de usarlo int *x *x = 100; lo adecuado ser, tener primeramente una localidad fsica de memoria, digamos int y; int *x, y; x = &y; *x = 100; Indireccin no vlida Supongamos que se tiene una funcin llamada malloc() la cual trata de asignar memoria dinmicamente (en tiempo de ejecucin), la cual regresa un apuntador al bloque de memoria requerida si se pudo o un apuntador a nulo en otro caso. char *malloc() -- una funcin de la biblioteca estndar que se ver ms adelante. Supongamos que se tiene un apuntador char *p Considerar: *p = (char *) malloc(100): /* pide 100 bytes de la memoria */

*p = 'y'; Existe un error en el cdigo anterior. Cul es? El * en la primera lnea ya que malloc regresa un apuntador y *p no apunta a ninguna direccin. El cdigo correcto deber ser: p = (char *) malloc(100); Ahora si malloc no puede regresar un bloque de memoria, entonces p es nulo, y por lo tanto no se podr hacer: *p = 'y'; Un buen programa en C debe revisar lo anterior, por lo que el cdigo anterior puede ser reescrito como: p = (char *) malloc(100): /* pide 100 bytes de la memoria */ if ( p == NULL ) { printf("Error: fuera de memoria\n"); exit(1); } *p = 'y'; 8.9 Ejercicios Escribir el programa que ordena las lneas de un texto ledo desde la entrada estndar, donde cada lnea tiene diferente longitud, segn lo descrito en la seccin de arreglo de apuntadores. Escribir una funcin que convierta una cadena s a un nmero de punto flotante usando apuntadores. Considerar que el nmero tiene el siguiente formato 99999999.999999, es decir, no se dar en notacin cientfica. La funcin deber suministrrsele una cadena y deber devolver un nmero. Escribir un programa que encuentre el nmero de veces que una palabra dada (esto es, una cadena corta) ocurre en una sentencia (una cadena larga). Leer los datos de la entrada estndar. La primera lnea es una sola palabra, en la segunda lnea se tiene un texto general. Leer ambas hasta encontrar un caracter de nueva lnea. Recordar que se debe insertar un caracter nulo antes de procesar. La salida tpica podra ser: La palabra es "el" La sentencia es "el perro, el gato y el canario" La palabra ocurrio 3 veces. VARIABLES Y PUNTEROS Monografa creado por unixOZ . Extraido de: http://tux.cl/articulos.php?id=32 04 Enero 2005 < anterior | 1 2 3 4 5 | siguiente > Una variable es un contenedor que almacena un dato de cierto tipo (los cuales son expuestos a repetivos cambios durante un programa). Al declarar una variable, el compilador se preocupa de reservar una cierta cantidad de memoria para trabajar este. En maquinas de 32 bits el valor de un entero es de 4 bytes, mientras que en una de 16 bits el entero vale 2 bytes. (Ojo, que la mayoria de los tarros que tengas a tu dispocicion van a ser 32 bits). Para ver estos valores, se puede hacer un pequeo programa: ## ##

#include int main(void) { printf("tamano de un int: %d ", sizeof(int)); /* se puede hacer con cualquier tipo de variables */ return 0; }##

## Declaramos variable asi: ##

## ##

int i; /* declaracion */

i = 2; /* le damos un valor a la variable */

/* ojo: 2 = i; es ilegal en C */

##

Como se explica en el libro "The C Programming Language" de Kernighan y Ritchie, hay dos valores para i. El primero es el valor del entero guardado (osea 2), mientras que el segundo es la direccin de memoria. Tambin se le puede llamar rvalue y lvalue a estos valores respectivamente. Ahora, para declarar una variable que guarde la direccin de memoria de otra (del mismo tipo) usamos los punteros: ## ## ## El int indica que la direccin de memoria que se va a almacenar es una variable de tipo entero. Ahora, para almacenar la direccin de memoria se usa el '&': ## ## ## Se puede ver claramente en el siguiente ejemplo: ## ###include int i, j; int *puntero; int main(void) { i = 5; j = 10; puntero = &j; printf("i tiene el valor %d y esta guardado en %p ", esta guardado en %p ", j, (void *)&j); printf("puntero tiene el valor %p y esta guardado return 0;}## ## El '(void *)' es un casting (convertir de un tipo de variable a otro) para usar el '%p'. Cuando un puntero ya apunta a algo, se puede comenzar a trabajar con ellos: ## ## ## Hay una relacin muy cercana entre punteros y arreglos (muchos autores plantean que debe discutirse simultneamente). Supongamos el siguiente ejemplo: i, (void *)&i); printf("j tiene el valor %d y en %p ", puntero, (void *)&puntero);

int *puntero; /* el '*' le indica al compilador que es una

variable puntero */ ##

puntero = &i;##

*puntero = 6; /* el compilador sabe cuantos bytes copiar a

esa direccin de memoria */##

## ## int arreglo[] = {1, 3, 56, -9, 6, 7}; elemento de arreglo */## ## Ahora podemos trabajar esta declaracin: ## ###include int arreglo[] = {1, 3, 56, -9, 6, 7}; int *m; int main(void){ int i; m = &arreglo[0]; for (i = 0; i < 6; i++) printf("arreglo[%d] = %d",i, *m++); return 0;}## ## Como se puede ver, se imprimiran todos los elementos del arreglo.Al igual como escribimos m = &arreglo[0]; podemos escribir m = arreglo; pues cumplen la misma fucin. Osea que el nombre del arreglo es la direccin de memoria del primer elemento de ese arreglo. Es importante tambin mencionar la igualdad (se puede apreciar en el siguiente ejemplo): ##arreglo[indice]; = *(arreglo + indice) = *(indice + arreglo);## ## ###include int main(void) { static int lista[] = {1, 5, 85, 55}; for (i = 0; i < 4; i++) printf("%d ", *(lista+i)); }## ## PUNTEROS Y CADENAS DE CARACTERES Monografa creado por unixOZ . Extraido de: http://tux.cl/articulos.php?id=32 04 Enero 2005 < anterior | 1 2 3 4 5 | siguiente > int i;

int *m;

m = &arreglo[0]; /* m tiene la direccin de memoria del

primer

Como en C una cadena de caracteres es un arreglo, podemos aprovechar de seguir viendo la relacin entre punteros y arreglos. Para declarar un arreglo se usa la siguiente sintxis: ##char cadena[100] = "Hola soy una cadena";## Para entender mas claramente como trabajar cadenas con punteros, se usa: ## ###include <stdio.h> char string1[100] = "hola"; char string2[100]; char *copiar(char *destino, char *origen) { while (*origen != '') *destino++ = *origen++; *destino = ''; return destino; } int main(void) { copiar(string2, string1); puts(string2); /* se imprime hola en pantalla */ }## ## En este ejemplo se ilustran varios aspectos importantes.copiar() copia el contenido de origen a destino. Como en C una cadena termina con un , se ve claramente que la sentencia while(*origen != '') *destino++ = *origen++; hace que se copie el contenido de origen a destino, hasta que origen termine (ojo, que el while se pudo haber escrito (while *origen), pues el valor del parntesis se convertir en 0 o FALSO al mismo tiempo. Este ultimo hace que entremos en un area llamdo aritmetica de punteros: ## ## (*p)++; /* incrementa en 1 el valor al que apunta */ valor */## ## PUNTEROS Y ESTRUCTURAS Monografa creado por unixOZ . Extraido de: http://tux.cl/articulos.php?id=32 04 Enero 2005 < anterior | 1 2 3 4 5 | siguiente > Sabemos que podemos definir un nuevo tipo de datos: ## ##

*p++; /* retorna el valor apuntado por puntero

y luego incrementa el

typedef struct { int h;

char *i; } NuevoTipoDato;## ## Supongamos que queremos escribir una funcin que acepte como parmetros un puntero a una estructura, y dentro de la funcin queremos usar un miembro de la estructura: ## ## ## ##strpt->h## tambin puede escribirse como ##(*strpt).h##. #include <stdio.h> int x,y; int *apuntador;

NuevoTipoDato *strpt;

strpt->i = "hola lector";

strpt->h = 10; /* accedemos a los miembros de

la estructura */##

int main() { printf("Programa para ilustrar como se pasa un valor por medio de apuntadores\n\n"); printf("Le asignamos a X el valor de 10\n"); printf("Y se lo pasaremos a Y por medio de un apuntador\n\n"); x=10; //Le asignamos un valor a la variable X. apuntador=&x; //Le asignamos la direccion de memoria donde esta el valor de X. y=*apuntador; //Le damos a Y el valor de la direccion donde apunta el puntero. printf("X=%d Y=%d\n\n",x,y); //Imprimimos los dos valores de X y de Y.

return 0; } #include<stdio.h> #include<iostream.h> void cuboporreferencia(int *ptrn); int main(){ int numero =5; cout<<"el valor original de numero es"<<numero<<endl; cuboporreferencia(&numero); cout<<"el numero valor de numero es"<<numero<<endl; return 0; } void cuboporreferencia(int *ptrn){ *ptrn = *ptrn * *ptrn * *ptrn;

} #include<iostream.h> int cuboporvalor(int n); int main(){ int numero =5; cout<<"el valor original de numero es"<<numero<<endl; numero=cuboporvalor(numero); cout<<"el numero valor de numero es"<<numero<<endl; return 0; } int cuboporvalor(int n){ return n*n*n; } CALCULAR EL AREA DE UN TRIANGULO
#include "stdafx.h" #include "stdio.h" int main(int argc, char* argv[]) { float area,base,altura; printf("Dame el valor de la base:"); scanf("%f",&base); printf("Dame el valor de la altura:"); scanf("%f",&altura); area=(base*altura)/2; printf("El area es:%f\n",area); return 0; }

.-----------

Solucin del Ejercicio 1 - Suma de dos nmeros (Alternativa doble - Lenguaje C)


#include <stdio.h> int main() { int a, b, suma; printf( "Introduzca primer numero (entero): " ); scanf( "%d", &a ); printf( "Introduzca segundo numero (entero): " ); scanf( "%d", &b ); suma = a + b; if ( suma > 0 ) printf( "LA SUMA SI ES MAYOR QUE CERO." ); else printf( "LA SUMA NO ES MAYOR QUE CERO." ); return 0; } Programa (.c): Suma de dos nmeros (Solucin 1) Una segunda solucin es: #include <stdio.h> int main()

{ int a, b; printf( "Introduzca primer numero (entero): " ); scanf( "%d", &a ); printf( "Introduzca segundo numero (entero): " ); scanf( "%d", &b ); if ( a + b > 0 ) printf( "LA SUMA SI ES MAYOR QUE CERO." ); else printf( "LA SUMA NO ES MAYOR QUE CERO." ); return 0;

#include <iostream.h> #include <conio.h> void suma(float,float); void resta(float,float); void multiplicacion(float,float); void division(float,float); main() { float n1,n2; cout<<\n -Operaciones aritmeticas basicas- \n <<\n Ingrese cantidad 1:; cin>>n1; cout<<\n Ingrese cantidad 2:; cin>>n2; suma(n1,n2); resta(n1,n2); multiplicacion(n1,n2); division(n1,n2); cout<<\n\n Los numeros ingresados fueron: <<n1<< y <<n2; getch(); } void suma(float x,float y) { cout<<\n La suma = <<(x+y); } void resta(float x,float y) { cout<<\n La resta = <<(x-y); } void multiplicacion(float x,float y) { cout<<\n La multiplicacion = <<(x*y); } void division(float x,float y) { cout<<\n La division = <<(x/y); }

Ejercicios con punteros


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include "conio.h" #include "stdio.h" #include "ctype.h" void main(){ clrscr(); char cad1[100], *ptr_cad1; char cad2[100], *ptr_cad2; printf("Dame la cadena: ");gets(cad1); ptr_cad1=cad1; ptr_cad2=cad2; do { *ptr_cad2=*ptr_cad1; *ptr_cad2++; } while(*ptr_cad1++); ptr_cad2=cad2; do { printf("%c",*ptr_cad2); }while(*ptr_cad2++); getch(); }

Você também pode gostar