Apuntadores
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Apuntadores

on

  • 845 views

 

Statistics

Views

Total Views
845
Views on SlideShare
845
Embed Views
0

Actions

Likes
1
Downloads
8
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft Word

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Apuntadores Document Transcript

  • 1. INTRODUCCIÓNLos apuntadores son una parte fundamental de C. Si usted no puede usar los apuntadoresapropiadamente entonces esta perdiendo la potencia y la flexibilidad que C ofrecebásicamente. El secreto para C esta en el uso de apuntadores.C, usa los apuntadores en forma extensiva. ¿Por qué? Es la única forma de expresar algunos cálculos. Se genera código compacto y eficiente. Es una herramienta muy poderosa.C, usa apuntadores explícitamente con: Es la única forma de expresar algunos cálculos. Se genera código compacto y eficiente. Es una herramienta muy poderosa.C, usa apuntadores explícitamente con: Arreglos, Estructuras y FuncionesEl uso de apuntadores en C y C++ es muy importante debido a que permite hacer losprogramas más eficientes y más flexibles. En este artículo se explica de una manerasencilla y breve todo lo referente a la utilización de apuntadores tanto en C como en C++.Todo lo explicado en este artículo aplica tanto para C como para C++, a menos que seespecifique un lenguaje en particular. En algunos ejemplos de código que son aplicables aC aparecen instrucciones de entrada y salida de las librerías estándar de C++.LOS APUNTADORES: Los apuntadores son variables que almacenan direcciones de memoria. En general una variable contiene un valor específico dependiendo de como fue declarada. Un apuntador contiene la dirección de una variable que contiene un valor específico. Una variable se refiere directamente a un valor y un apuntador se refiere indirectamente a un valor. Apuntadores usados en C debido a que a veces son la única manera de expresar un cálculo.
  • 2. Se puede llegar a obtener un código más compacto y eficiente. Cuando se emplean sin cuidado pueden crear programas imposibles de entender. Cuentan con una declaración propia. Los apuntadores disponen de dos operadores: El operador unario o monádico “&” devuelve la dirección de memoria de una variable; El operador de indirección o desreferencia “*” devuelve el ``contenido de un objeto apuntado por un apuntador.Declaración De Apuntadores:Cuando se declara una variable, el compilador reserva un espacio de memoria para ella yasocia el nombre de ésta a la dirección de memoria desde donde comienzan los datos deesa variable. Las direcciones de memoria se suelen describir como números enhexadecimal. Un apuntador es una variable cuyo valor es la dirección de memoria de otravariable. Se dice que un apuntador “apunta” a la variable cuyo valor se almacena a partirde la dirección de memoria que contiene el apuntador. Por ejemplo, si un apuntador palmacena la dirección de una variable x, se dice que “p apunta a x”. Los apuntadores como cualquier otra variable deben de ser declarados antes de que puedan ser utilizados. El tipo de un apuntador lo proporciona implícitamente el tipo de la variable a la que apunta. Los apuntadores pueden ser declarados para apuntar a objetos de cualquier clase. La sintaxis general de declaración es: <tipo> * <variable> Ejemplos de declaraciones: La variable contPtr es del tipo apuntador a entero, (int *) y se lee ``contPtr es un apuntador a int o ``contPtr apunta a una variable entera.NOTA: Un apuntador a cualquier tipo de variables es una dirección en memoria, la cual esuna dirección entera, pero un apuntador NO es un entero.La razón por la cual se asocia un apuntador a un tipo de dato, es por que se debe conoceren cuantos bytes esta guardado el dato. De tal forma, que cuando se incrementa unapuntador, se incrementa el apuntador por un ``bloque de memoria, en donde el bloqueesta en función del tamaño del dato. Por lo tanto para un apuntador a un char, se agregaun byt a la dirección y para un apuntador a entero o a flotante se agregan 4 bytes. De estaforma si a un apuntador a flotante se le suman 2, el apuntador entonces se mueve dosposiciones float que equivalen a 8 bytes.Los Operadores De Los Apuntadores: Un operador de dirección &:Representa la dirección de memoria de la variable que le sigue; Operador unario queregresa la dirección de su operando, ejemplo:
  • 3. main() { int y; int *yPtr; y = 5; yPtr = &y; }2. Un operador de indirección o de desreferencia:*. El operador * aplicado al nombre de un apuntador indica el valor de la variable apuntada;Regresa el valor del objeto hacia el cual su operando apunta, es decir un apuntador,ejemplo: main() { int x,y; int *py; y = 5; *py = y; x = *py + 5; printf(%d %d nn,*py,x); }Veamos con un ejemplo en C la diferencia entre todos estos conceptosEs decir: int x = 25, *pint;pint = &x;La variable pint contiene la dirección de memoria de la variable x. La expresión: *pintrepresenta el valor de la variable (x) apuntada, es decir 25. La variable pint también tienesu propia dirección: &pintInicialización de APUNTADORES:< Almacenamiento > < Tipo > * < Nombre > = < Expresión >Si <Almacenamiento> es extern o static, <Expresión> deberá ser una expresión constantedel tipo <Tipo> expresado.Si <Almacenamiento> es auto, entonces <Expresión> puede ser cualquier expresión del<Tipo> especificado.Ejemplos: La constante entera 0, NULL (cero) proporciona un apuntador nulo a cualquier tipo dedato:int *p;
  • 4. p = NULL; //actualización El nombre de un arreglo de almacenamiento static o extern se transforma según laexpresión:a) float mat[12];float *punt = mat;b) float mat[12];float *punt = &mat[0]; Un “cast” apuntador a apuntador:int *punt = (int *) 123.456;Inicializa el apuntador con el entero. Esto es, en la dirección a la que apunta la variablepunt se almacena el valor 123. Un apuntador a carácter puede inicializarse en la forma:char *cadena = Esto es una cadena”; Se pueden sumar o restar valores enteros a las direcciones de memoria en la forma:(aritmética de APUNTADORES)static int x;int *punt = &x+2, *p = &x-1; Equivalencia: Dos tipos definidos como APUNTADORES a objeto P y apuntador aobjeto son equivalentes sólo si P y Q son del mismo tipo. Aplicado a matrices:nombre_apuntador = nombre_matriz;Apuntadores y Funciones:Cuando C pasa argumentos a funciones, los pasa por valor, es decir, si el parámetro esmodificado dentro de la función, una vez que termina la función el valor pasado de lavariable permanece inalterado.Hay muchos casos que se quiere alterar el argumento pasado a la función y recibir elnuevo valor una vez que la función ha terminado. Para hacer lo anterior se debe usaruna llamada por referencia, en C se puede simular pasando un puntero al argumento. Conesto se provoca que la computadora pase la dirección del argumento a la función.Para entender mejor lo anterior consideremos la función swap() que intercambia el valor dedos argumentos enteros:void swap(int *px, int *py);main()
  • 5. {int x, y;x = 10;y = 20;printf("x=%dty=%dn",x,y);swap(&x, &y);printf("x=%dty=%dn",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 */APUNTADORES Y ARREGLOS: Existe una estrecha relación entre apuntadores y arreglos, tanto que pueden ser usados en forma casi indistinta. En C, un nombre de un arreglo es un índice a la dirección de comienzo del arreglo. En esencia, el nombre de un arreglo es un puntero al arreglo. Una variable de tipo arreglo puede considerarse como un apuntadora tipo del arreglo. Los apuntadores pueden ser utilizados en cualquier operación que involucre subíndices de arreglos.Ejemplo:main(){ int tabla[10],i,x,*pt,*ptr; pt = &tabla[0]; x = *pt; for (i=0; i!10; i++) *(pt+i) = random(); ptr = tabla;
  • 6. for (i=0; i!10; i++) printf(%d nn,*(ptr+i),tabla[i]);} Cuando se suma 1 a un apuntador el incremento se adecua al tamaño en memoria del objeto apuntado. Un apuntador es una variable, por lo que operaciones como pa = a y pa++ son permitidas.Un nombre de un arreglo es una constante, no una variable, de ahí que a = pa o a++ o p =a.Arreglos de apuntadores: Los arreglos pueden contener apuntadores. El uso más común es el de formar arreglos de cadenas de caracteres. Cada entrada en el arreglo es un apuntador al primer carácter de la cadena. Sea la declaración: char * mensaje[4] = {Hola,Adios,Bye,Salut} Cada cadena está almacenada en memoria como una cadena de caracteres terminada en NULL n0. En el arreglo no están colocadas las cadenas, tan solo están almacenados los apuntadores. Aunque el arreglo es de tamaño fijo, permite el acceso a cadenas de caracteres de cualquier longitud.En C se pueden tener arreglos de apuntadores ya que los apuntadores son variables.A continuación se muestra un ejemplo de su uso: ordenar las líneas de un texto dediferente longitud.Los arreglos de apuntadores son una representación de datos que manejan de una formaeficiente y conveniente líneas de texto de longitud variable.¿Cómo se puede hacer lo anterior? Guardar todas las líneas en un arreglo de tipo char grande. Observando que n marca elfin de cada línea. Ver figura 1.1. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primercaracter de cada línea. Comparar dos líneas usando la función de la biblioteca estándar strcmp().
  • 7. Si dos líneas están desacomodadas -- intercambiar (swap) los apuntadores (no eltexto). Figura 1.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 líneas.Apuntadores y arreglos multidimensionales: Puede provocar confusión el uso de arreglos de dos dimensiones y un arreglo de apuntadores. Considerar: int a[10][10]; int *b[10]; El uso de a y b puede ser parecido, desde el momento en que a[5][5] y b[5][5] son referencias validas a un int El arreglo a es un arreglo verdadero, existen 100 celdas de memoria asignadas y se efectúa el cálculo de subíndices rectangulares convencional para localizar un elemento dado Sin embargo, a b la declaración solo le asigna 10 apuntadores, cada uno de los cuales deberá de apuntar a un arreglo de enteros La desventajas de b son: Ocupa más espacio, suponiendo que cada uno apunta a un arreglo de 10 elementos, el tamaño será de 10 apuntadores más 100 elementos. Se debe de crear el espacio de los arreglos antes de asignarlos.
  • 8. o La declaración b tiene dos ventajas:o El acceso se hace más rápido, una inderección es más rapida que el hacer una multiplicación seguida de una suma.o El tamaño de los arreglos apuntados por cada una de las diez localidades pueden ser diferentes. Un arreglo multidimensional puede ser visto en varias formas en C, por ejemplo: Un arreglo de dos dimensiones es un arreglo de una dimensión, donde cada uno de los elementos es en sí mismo un arreglo. Por lo tanto, la notación a[n][m] nos indica que los elementos del arreglo están guardados renglón por renglón. Cuando se pasa una arreglo bidimensional a una función se debe especificar el número de columnas -- el número de renglones es irrelevante. La razón de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para que pueda brincar de renglón en renglón en la memoria. Considerando que una función deba recibir int a[5][35], se puede declarar el argumento de la función como: f( int a[][35] ) { ..... } o aún f( int (*a)[35] ) { ..... } En el último ejemplo se requieren los parénteis (*a) ya que [ ] tiene una precedencia más 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 dirección del primer elemento que se encuentran en el tercer renglón 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 aplicación común de esto.
  • 9. Considera:char *nomb[10];char anomb[10][20];En donde es válido 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 fórmula 20*renglon + columna + dirección_base  En cambio nomb tiene 10 apuntadores a elementos.NOTA: si cada apuntador en nomb indica un arreglo de 20 elementosentonces y solamente entonces 200 chars estarán disponibles (10elementos).Con el primer tipo de declaración se tiene la ventaja de que cada apuntadorpuede apuntar a arreglos de diferente longitud.Considerar:char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... };char anomb[][15] = { "No mes", "Ene", "Feb", "Mar", ... };Lo cual gráficamente se muestra en la figura 1.2. Se puede indicar que sehace un manejo más eficiente del espacio haciendo uso de un arreglo deapuntadores y usando un arreglo bidimensional.
  • 10. Figura 1.2: Arreglo de 2 dimensiones VS. arreglo de apuntadores.Diferentes formas de declarar a[i][j]:Cuando se ve la referencia a un arreglo de dos dimensiones, a[i][j] , no sepuede deducir inmediatamente como fue declarado a :  Como un arreglo de 10 arreglos de tamaño 20 int a[10][20];  Como un arreglo de tamaño 20 de vectores de longitud variable int *a[10];  Como un apuntador de apuntadores a enteros int **a;  Como un apuntador a un arreglo de enteros de tama~no 20 int (* a)[20];  Para poder direccionar un elemento de un apuntador de apuntadores se sigue la regla: tab[i][j] * ( *(tab + i) + j)ARGUMENTOS EN LA LÍNEA DE COMANDOS:
  • 11.  Existe una forma de transmitr al programa los argumentos de la línea de comando, o parámetros, cuando comienza la ejecución Cuando se invoca el main() se le puede pasar dos argumentos, (los nombres son por convención): argc es el número de argumentos en la línea de comandos. argv[] es un apuntador a un arreglo de cadena de caracteres que contienen los argumentos, uno por cadena Ejemplo de uso: main( int argc, char *argv[]) { int i; for (i=0; i!argc; i++) printf(Argumento %d : %s,); }  Ya que el primer elemento del arreglo, ( *argv[] ) apunta a la cadena que contiene el nombre del comando, argc es al menos igual a 1.  Esta estructura de datos es creada por el sistema operativo, (Unix u otro), por lo que la única preocupación del programador es usarla, no generarla. Fallas comunes con apuntadores: A continuación se muestran dos errores comunes que se hacen con los apuntadores:  No asignar un apuntador a una dirección de memoria antes de usarlo: int *x *x = 100; lo adecuado será, tener primeramente una localidad física de memoria, digamos int y; int *x, y; x = &y; *x = 100;  Indirección no válida: Supongamos que se tiene una función llamada malloc() la cual trata de asignar memoria dinámicamente (en tiempo de ejecución), la
  • 12. cual regresa un apuntador al bloque de memoria requerida si sepudo o un apuntador a nulo en otro caso.char *malloc() -- una función de la biblioteca estándar que se verámás adelante.Supongamos que se tiene un apuntador char *pConsiderar:*p = (char *) malloc(100): /* pide 100 bytes de la memoria */*p = y;Existe un error en el código anterior. ¿Cuál es?El * en la primera línea ya que malloc regresa un apuntador y *p noapunta a ninguna dirección.El código correcto deberá ser:p = (char *) malloc(100);Ahora si malloc no puede regresar un bloque de memoria, entoncesp es nulo, y por lo tanto no se podrá hacer:*p = y;Un buen programa en C debe revisar lo anterior, por lo que elcódigo anterior puede ser reescrito como:p = (char *) malloc(100): /* pide 100 bytes de la memoria */if ( p == NULL ){printf("Error: fuera de memorian");exit(1);}*p = y; Apuntadores de mayor complejidad:int *p; p es un apuntador a un enteroint *p[10]; p es un arreglo de 10 apuntadores a enterosint (*p)[10]; p es un apuntador a un arreglo de 10 enterosint *p(void); p es una función que devuelve un apuntador a entero
  • 13. p es una función que acepta un argumento que es un apuntador a int p(char *a); carácter, devuelve un entero p es una función que acepta un argumento que es un apuntador a int *p(char *a); carácter, devuelve un apuntador a entero p es un apuntador a función que acepta un argumento que es un int (*p)(char *a); apuntador a carácter, devuelve un apuntador a entero int (*p(char p es una función que acepta un argumento que es un apuntador a *a))[10]; carácter, devuelve un apuntador a un arreglo de 10 enteros p es un apuntador a función que acepta un argumento que es un int p(char (*a)[]); apuntador a un arreglo de caracteres, devuelve un apuntador a entero p es un apuntador a función que acepta un argumento que es un int p(char *a[]); arreglo de apuntadores a caracteres, devuelve un apuntador a en p es una función que acepta un argumento que es un arreglo de int *p(char a[]); caracteres, devuelve un apuntador a entero p es una función que acepta un argumento que es un apuntador a int *p(char (*a)[]); arreglo de caracteres, devuelve un apuntador a entero p es una función que acepta un argumento que es un apuntador a int *p(char *a[]); arreglo de apuntadores a caracteres, devuelve un apuntador a en int (*p)(char p es una función que acepta un argumento que es un apuntador a (*a)[]); arreglo de caracteres, devuelve un apuntador a entero p es un apuntador a una función que acepta un argumento que es int *(*p)(char apuntador a un arreglo de apuntadores a caracteres, devuelve un (*a)[]); apuntador a entero int *(*p)(char p es un apuntador a una función que acepta un argumento que es *a[]); arreglo de apuntadores a caracteres, devuelve un apuntador a en p es una arreglo de 10 apuntadores a función, cada función devu int(*p[10])(void); un entero int (*p[10])(char * p es un arreglo de 10 apuntadores a función; cada función acepta a); argumento que es un apuntador a carácter y devuelve un entero int *(*p[10])(char p es un arreglo de 10 apuntadores a función; cada función acepta a); argumento que es un carácter, y devuelve un apuntador a entero char p es un arreglo de 10 apuntadores a función; cada función acepta *(*p[10])(char * argumentoque es un carácter, y devuelve un apuntador a carácte a); BIBLIOGRAFÍA8. Apuntadores
  • 14. Los apuntadores son una parte fundamental de C. Si usted nopuede usar los apuntadores apropiadamente entonces estaperdiendo la potencia y la flexibilidad que C ofrece básicamente.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 cálculos.Se genera código compacto y eficiente.Es una herramienta muy poderosa.C usa apuntadores explícitamente con:Es la única forma de expresar algunos cálculos.Se genera código compacto y eficiente.Es una herramienta muy poderosa.C usa apuntadores explícitamente con:Arreglos,Estructuras yFunciones8.1 Definición de un apuntadorUn apuntador es una variable que contiene la dirección enmemoria de otra variable. Se pueden tener apuntadores acualquier tipo de variable.El operador unario o monádico & devuelve la dirección dememoria de una variable.El operador de indirección 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. Porejemplo, no se puede asignar la dirección de un short int aun long int.Para tener una mejor idea, considerar el siguiente código:
  • 15. main() { int x = 1, y = 2; int *ap; ap = &x; y = *ap; x = ap; *ap = 3; }Cuando se compile el código se mostrará el siguiente mensaje:warning: assignment makes integer from pointerwithout a cast.Con el objetivo de entender el comportamiento del códigosupongamos que la variable x esta en la localidad de lamemoria 100, y en 200 y ap en 1000. Nota: un apuntador esuna variable, por lo tanto, sus valores necesitan ser guardadosen algún lado. int x = 1, y = 2; int *ap; ap = &x; 100 200 1000 x 1 y 2 ap 100Las variables x e y son declaradas e inicializadascon 1 y 2 respectivamente, ap es declarado como un apuntadora entero y se le asigna la dirección de x (&x). Por lo que ap secarga con el valor 100. y = *ap; 100 200 1000 x 1 y 1 ap 100Después y obtiene el contenido de ap. En el ejemplo ap apuntaa la localidad de memoria 100 -- la localidad de x. Por lotanto, y obtiene el valor de x -- el cual es 1. x = ap;
  • 16. 100 200 1000 x 100 y 1 ap 100Como se ha visto C no es muy estricto en la asignación devalores de diferente tipo (apuntador a entero). Así que esperfectamente legal (aunque el compilador genera un aviso decuidado) asigna el valor actual de ap a la variable x. El valorde ap en ese momento es 100. *ap = 3; 100 200 1000 x 3 y 1 ap 100Finalmente se asigna un valor al contenido de un apuntador(*ap).Importante: Cuando un apuntador es declarado apunta a algúnlado. Se debe inicializar el apuntador antes de usarlo. Por loque: main() { int *ap; *ap = 100; }puede generar un error en tiempo de ejecución o presentar uncomportamiento errático.El uso correcto será: main() { int *ap; int x; ap = &x; *ap = 100; }Con los apuntadores se puede realizar también aritméticaentera, por ejemplo: main() {
  • 17. float *flp, *flq; *flp = *flp + 10; ++*flp; (*flp)++; flq = flp; }NOTA: Un apuntador a cualquier tipo de variables es unadirección en memoria -- la cual es una dirección entera, pero unapuntador NO es un entero.La razón por la cual se asocia un apuntador a un tipo de dato, espor que se debe conocer en cuantos bytes esta guardado eldato. De tal forma, que cuando se incrementa un apuntador, seincrementa el apuntador por un ``bloque de memoria, en dondeel bloque esta en función del tamaño del dato.Por lo tanto para un apuntador a un char, se agrega un byte a ladirección y para un apuntador a entero o a flotante se agregan 4bytes. De esta forma si a un apuntador a flotante se le suman 2,el apuntador entonces se mueve dos posiciones float queequivalen a 8 bytes.8.2 Apuntadores y FuncionesCuando C pasa argumentos a funciones, los pasa por valor, esdecir, si el parámetro es modificado dentro de la función, unavez que termina la función el valor pasado de la variablepermanece inalterado.Hay muchos casos que se quiere alterar el argumento pasado ala función y recibir el nuevo valor una vez que la función haterminado. Para hacer lo anterior se debe usar una llamada porreferencia, en C se puede simular pasando un puntero alargumento. Con esto se provoca que la computadora pase ladirección del argumento a la función.Para entender mejor lo anterior consideremos lafunción swap() que intercambia el valor de dos argumentosenteros:
  • 18. void swap(int *px, int *py); main() { int x, y; x = 10; y = 20; printf("x=%dty=%dn",x,y); swap(&x, &y); printf("x=%dty=%dn",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 arreglosExiste una relación estrecha entre los punteros y los arreglos.En C, un nombre de un arreglo es un índice a la dirección decomienzo del arreglo. En esencia, el nombre de un arreglo es unpuntero 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] esidéntica a ap+t. Se debe tener cuidado ya que C no hace unarevisión de los límites del arreglo, por lo que se puede irfácilmente más alla del arreglo en memoria y sobreescribir otrascosas.
  • 19. C sin embargo es mucho más sútil en su relación entre arreglos y apuntadores. Por ejemplo se puede teclear solamente:ap = a; en vez de ap = &a[0]; y también *(a + i) en vezde 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, asegúrese haberla entendido. Con lo comentado se puede entender como los arreglos son pasados a las funciones. Cuando un arreglo es pasado a una función 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 razón por la cual se declara la función como:int strlen(char s[]); y una declaración equivalente es intstrlen(char *s); ya que char s[] es igual que char *s. La función strlen() es una función de la biblioteca estándar que regresa la longitud de una cadena. Se muestra enseguida la versión de esta función que podría escribirse: int strlen(char *s) { char *p = s; while ( *p != 0 ) p++; return p - s;
  • 20. } Se muestra enseguida una función para copiar una cadena en otra. Al igual que en el ejercicio anterior existe en la biblioteca estándar una función que hace lo mismo. void strcpy(char *s, char *t) { while ( (*s++ = *t++) != 0 ); } En los dos últimos ejemplos se emplean apuntadores y asignación 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 continuación se muestra un ejemplo de su uso: ordenar las líneas de un texto de diferente longitud. Los arreglos de apuntadores son una representación de datos que manejan de una forma eficiente y conveniente líneas de texto de longitud variable. ¿Cómo se puede hacer lo anterior?1. Guardar todas las líneas en un arreglo de tipo char grande. Observando que n marca el fin de cada línea. Ver figura 8.1.2. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primer caracter de cada línea.3. Comparar dos líneas usando la función de la biblioteca estándar strcmp().4. Si dos líneas están desacomodadas -- intercambiar (swap) los apuntadores (no el texto).
  • 21. 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 líneas. 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 dimensión, donde cada uno de los elementos es en sí mismo un arreglo. Por lo tanto, la notación a[n][m] nos indica que los elementos del arreglo están guardados renglón por renglón. Cuando se pasa una arreglo bidimensional a una función se debe especificar el número de columnas -- el número de renglones es irrelevante. La razón de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para que pueda brincar de renglón en renglón en la memoria.
  • 22. Considerando que una función deba recibir int a[5][35], se puede declarar el argumento de la función como: f( int a[][35] ) { ..... } o aún f( int (*a)[35] ) { ..... } En el último ejemplo se requieren los parénteis (*a) ya que [ ] tiene una precedencia más alta que *. Por lo tanto:int (*a)[35]; declara un apuntador a un arreglo de 35 enteros, ypor ejemplo si hacemos la siguiente referencia a+2, nos estaremosrefiriendo a la dirección del primer elemento que se encuentran en eltercer renglón de la matriz supuesta, mientras queint *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 aplicación común de esto. Considera: char *nomb[10]; char anomb[10][20]; En donde es válido 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 fórmula20*renglon + columna + dirección_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 estarán disponibles (10 elementos).
  • 23. Con el primer tipo de declaración se tiene la ventaja de que cadaapuntador 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 gráficamente se muestra en la figura 8.2. Se puedeindicar que se hace un manejo más eficiente del espaciohaciendo uso de un arreglo de apuntadores y usando un arreglobidimensional. Figura 8.2: Arreglo de 2 dimensiones VS. arreglo de apuntadores.8.6 Inicialización estática dearreglos de apuntadoresLa inicialización de arreglos de apuntadores es una aplicaciónideal para un arreglo estático interno, por ejemplo: func_cualquiera()
  • 24. { static char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... }; }Recordando que con el especificador de almacenamiento declase static se reserva en forma permanente memoria el arreglo,mientras el código se esta ejecutando.8.7 Apuntadores y estructurasLos apuntadores a estructuras se definen fácilmente y en unaforma 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 asignación que se hace corresponde a la figura 8.3
  • 25. Figura 8.3: Esquema de una lista ligada con 2 elementos.Nota: Solamente se puede declarar sig como un apuntadortipo ELEMENTO. No se puede tener un elemento del tipo variableya que esto generaría una definición recursiva la cual no estapermitida. Se permite poner una referencia a un apuntador yaque los los bytes se dejan de lado para cualquier apuntador.8.8 Fallas comunes conapuntadoresA continuación se muestran dos errores comunes que se hacencon los apuntadores.No asignar un apuntador a una dirección de memoria antesde usarlo int *x *x = 100; lo adecuado será, tener primeramente una localidad física de memoria, digamos int y; int *x, y; x = &y; *x = 100;Indirección no válida Supongamos que se tiene una función llamada malloc() la cual trata de asignar memoria dinámicamente (en tiempo de ejecución), la cual regresa un apuntador al bloque de memoria requerida si se pudo o un apuntador a nulo en otro caso. char *malloc() -- una función de la biblioteca estándar que se verá más adelante. Supongamos que se tiene un apuntador char *p
  • 26. Considerar: *p = (char *) malloc(100): /* pide 100 bytes de la memoria */ *p = y; Existe un error en el código anterior. ¿Cuál es? El * en la primera línea ya que malloc regresa un apuntador y *p no apunta a ninguna dirección. El código 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 código anterior puede ser reescrito como: p = (char *) malloc(100): /* pide 100 bytes de la memoria */ if ( p == NULL ) { printf("Error: fuera de memorian"); exit(1); } *p = y;8.2 Apuntadores y FuncionesCuando C pasa argumentos a funciones, los pasa por valor, esdecir, si el parámetro es modificado dentro de la función, unavez que termina la función el valor pasado de la variablepermanece inalterado.
  • 27. Hay muchos casos que se quiere alterar el argumento pasado ala función y recibir el nuevo valor una vez que la función haterminado. Para hacer lo anterior se debe usar una llamada porreferencia, en C se puede simular pasando un puntero alargumento. Con esto se provoca que la computadora pase ladirección del argumento a la función.Para entender mejor lo anterior consideremos lafunción swap() que intercambia el valor de dos argumentosenteros: void swap(int *px, int *py); main() { int x, y; x = 10; y = 20; printf("x=%dty=%dn",x,y); swap(&x, &y); printf("x=%dty=%dn",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 arreglosExiste una relación estrecha entre los punteros y los arreglos.En C, un nombre de un arreglo es un índice a la dirección decomienzo del arreglo. En esencia, el nombre de un arreglo es unpuntero al arreglo. Considerar lo siguiente: int a[10], x; int *ap; ap = &a[0]; /* ap apunta a la direccion de a[0] */
  • 28. 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 idéntica a ap+t. Se debe tener cuidado ya que C no hace una revisión de los límites del arreglo, por lo que se puede ir fácilmente más alla del arreglo en memoria y sobreescribir otras cosas. C sin embargo es mucho más sútil en su relación entre arreglos y apuntadores. Por ejemplo se puede teclear solamente:ap = a; en vez de ap = &a[0]; y también *(a + i) en vezde 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, asegúrese haberla entendido. Con lo comentado se puede entender como los arreglos son pasados a las funciones. Cuando un arreglo es pasado a una función 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 razón por la cual se declara la función como:int strlen(char s[]); y una declaración equivalente es intstrlen(char *s); ya que char s[] es igual que char *s.
  • 29. La función strlen() es una función de la biblioteca estándar que regresa la longitud de una cadena. Se muestra enseguida la versión de esta función que podría escribirse: int strlen(char *s) { char *p = s; while ( *p != 0 ) p++; return p - s; } Se muestra enseguida una función para copiar una cadena en otra. Al igual que en el ejercicio anterior existe en la biblioteca estándar una función que hace lo mismo. void strcpy(char *s, char *t) { while ( (*s++ = *t++) != 0 ); } En los dos últimos ejemplos se emplean apuntadores y asignación 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 continuación se muestra un ejemplo de su uso: ordenar las líneas de un texto de diferente longitud. Los arreglos de apuntadores son una representación de datos que manejan de una forma eficiente y conveniente líneas de texto de longitud variable. ¿Cómo se puede hacer lo anterior?1. Guardar todas las líneas en un arreglo de tipo char grande. Observando que n marca el fin de cada línea. Ver figura 8.1.2. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primer caracter de cada línea.
  • 30. 3. Comparar dos líneas usando la función de la biblioteca estándar strcmp().4. Si dos líneas están 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 líneas. 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 dimensión, donde cada uno de los elementos es en sí mismo un arreglo. Por lo tanto, la notación a[n][m] nos indica que los elementos del arreglo están guardados renglón por renglón.
  • 31. Cuando se pasa una arreglo bidimensional a una función se debe especificar el número de columnas -- el número de renglones es irrelevante. La razón de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para que pueda brincar de renglón en renglón en la memoria. Considerando que una función deba recibir int a[5][35], se puede declarar el argumento de la función como: f( int a[][35] ) { ..... } o aún f( int (*a)[35] ) { ..... } En el último ejemplo se requieren los parénteis (*a) ya que [ ] tiene una precedencia más alta que *. Por lo tanto:int (*a)[35]; declara un apuntador a un arreglo de 35 enteros, ypor ejemplo si hacemos la siguiente referencia a+2, nos estaremosrefiriendo a la dirección del primer elemento que se encuentran en eltercer renglón de la matriz supuesta, mientras queint *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 aplicación común de esto. Considera: char *nomb[10]; char anomb[10][20]; En donde es válido 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. -
  • 32. El acceso de los elementos anomb en memoria se hace bajo lasiguiente fórmula20*renglon + columna + dirección_base-En cambio nomb tiene 10 apuntadores a elementos.NOTA: si cada apuntador en nomb indica un arreglo de 20elementos entonces y solamente entonces 200 chars estarándisponibles (10 elementos).Con el primer tipo de declaración se tiene la ventaja de que cadaapuntador 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 gráficamente se muestra en la figura 8.2. Se puedeindicar que se hace un manejo más eficiente del espaciohaciendo uso de un arreglo de apuntadores y usando un arreglobidimensional. Figura 8.2: Arreglo de 2 dimensiones VS. arreglo de apuntadores.
  • 33. 8.6 Inicialización estática dearreglos de apuntadoresLa inicialización de arreglos de apuntadores es una aplicaciónideal para un arreglo estático interno, por ejemplo: func_cualquiera() { static char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... }; }Recordando que con el especificador de almacenamiento declase static se reserva en forma permanente memoria el arreglo,mientras el código se esta ejecutando.8.7 Apuntadores y estructurasLos apuntadores a estructuras se definen fácilmente y en unaforma 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;
  • 34. ELEMENTO n1, n2; n1.sig = &n2;La asignación 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 apuntadortipo ELEMENTO. No se puede tener un elemento del tipo variableya que esto generaría una definición recursiva la cual no estapermitida. Se permite poner una referencia a un apuntador yaque los los bytes se dejan de lado para cualquier apuntador.8.8 Fallas comunes conapuntadoresA continuación se muestran dos errores comunes que se hacencon los apuntadores.No asignar un apuntador a una dirección de memoria antesde usarlo int *x *x = 100; lo adecuado será, tener primeramente una localidad física de memoria, digamos int y; int *x, y; x = &y; *x = 100;Indirección no válida Supongamos que se tiene una función llamada malloc() la cual trata de asignar memoria dinámicamente (en tiempo de ejecución), la cual regresa
  • 35. un apuntador al bloque de memoria requerida si se pudo o un apuntador a nulo en otro caso. char *malloc() -- una función de la biblioteca estándar que se verá más 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 código anterior. ¿Cuál es? El * en la primera línea ya que malloc regresa un apuntador y *p no apunta a ninguna dirección. El código 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 código anterior puede ser reescrito como: p = (char *) malloc(100): /* pide 100 bytes de la memoria */ if ( p == NULL ) { printf("Error: fuera de memorian"); exit(1); } *p = y;8.9 Ejercicios
  • 36. 1. Escribir el programa que ordena las líneas de un texto leído desde la entrada estándar, donde cada línea tiene diferente longitud, según lo descrito en la sección de arreglo de apuntadores. 2. Escribir una función que convierta una cadena s a un número de punto flotante usando apuntadores. Considerar que el número tiene el siguiente formato 99999999.999999, es decir, no se dará en notación científica. La función deberá suministrársele una cadena y deberá devolver un número. 3. Escribir un programa que encuentre el número de veces que una palabra dada (esto es, una cadena corta) ocurre en una sentencia (una cadena larga). Leer los datos de la entrada estándar. La primera línea es una sola palabra, en la segunda línea se tiene un texto general. Leer ambas hasta encontrar un caracter de nueva línea. Recordar que se debe insertar un caracter nulo antes de procesar. La salida típica podría ser: La palabra es "el" La sentencia es "el perro, el gato y el canario" La palabra ocurrio 3 veces.Los apuntadores son un tipo de dato, que almacena informacion en la memoria dela direccion de otras variables.Es decir si tienes este algoritmo:::#include<iostream>using namespace std;int main (){char a;a = s;char * ap;ap = &a;return 0;
  • 37. }tienes una variable a que se guarda en una direccion x de la memoria RAM, lavariable *ap; guarda esa direccion x. y eso es un apuntador.Clase 2: ApuntadoresEsquema de Un ComputadorLa Memoria del Computador Almacena datos e instrucciones de programa. Puede considerarse a la memoria como una lista o arregl o de casillas identificadas o enumeradas En las direcciones de memoria se almacenan datos en la forma de palabras Las palabras est�n constituidas por uno o mas Bytes, dependiendo de la arquitectura de la maquina
  • 38. Los Bytes, est�n compuestos por 8 bits Los bits, o d�gitos binarios, son la unidad m�nima de almacenamiento, almacenan los valores 1 = encendido, y 0 = apagadoCaracter�sticas de los Apuntadores Son variables que mantienen direcciones de memoria Poderosos para manejar datos, de manera no posible en otros lenguajes Permiten el pasaje de par�metros por referencia. Cuando se utilizan de manera incorrecta, son una fuente de bugs en los programas y frustraci�n en el programadorIntroducci�nAl ejecutarse un programa, las variables se almacenan en memoria,cada una en su propia y �nica direcci�n o localidad. Las variables engeneral contienen un valor odato. Por ejemplo cuando se declara:int count = 5;El valor "5" es almacenado en memoria y puede ser accedido usando la variable�count�.�Un apuntador es un tipo especial de variable que en lugar de contener un valor odato, contiene una direcci�n de memoria. As� como los datos pueden modificarsecuando se trabaja con una variable normal, el valor de la direcci�n almacenada enun apuntador tambi�n puede modificarse. Usualmente, la direcci�n almacenadaen el apuntador es la direcci�n correspondiente a alguna otra variable delprograma.int *ptr;ptr = &count /* Guarda direcci�n de count en ptr */ /* el operador unario & retorna la direcci�n de la �variable */Para tomar el valor o contenido que es almacenado en la localidad de memoriamenelapuntador, es necesario �de referenciar� al apundator. Esto se hace usando eloperador unario �*�.int total;
  • 39. total = *ptr; /* The value in the address stored in ptr is assigned to total */La mejor manera de aprender apuntadores es con la pr�ctica y los ejemplos. Losapuntadores son un t�pico dif�cil. No se preocupen si el panorama no estacompletamente claro todav�a.Ejemplo de Declaraci�n e Inicializaci�nint main(){ int j; int k; int l; int *pt1; /* Declares an integer pointer */ int *pt2; /* Declares an integer pointer */ float values[100]; float results[100]; float *pt3; /* Declares a float pointer */ float *pt4; /* Declares a float pointer */ j = 1; k = 2; pt1 = &j; /* pt1 contains the address of the variable j */ pt2 = &k; /* pt2 contains the address of variable k */ pt3 = values; /* pt3 contains the address of the first element of values */ pt3 = &values[0]; /* This is the equivalent of the above statement */ return 0;}Dereferenciacion de Apuntadores y Asignaci�n de valoresLa dereferenciacion permite la manipulaci�n de los datos contenidos en ladirecci�n de memoria guardada en el apuntador. El apuntador guarda unadirecci�n de memoria. Al de referenciar un apuntador, podemos modificar los datoso valores contenidos en esa direcci�n de memoria. El operadorunario �*�, esutilizado para de referenciar a los apuntadores.*pt1 =*pt1 + 2;
  • 40. Esta instrucci�n suma dos al valor apuntado por pt1. Es decir, la instrucci�n sumados a; contenido de la direcci�n de memoria almacenada por pt1. Entonces, apartir del programa principal, pt1 contiene �la direcci�n de j. La variable "j" fueinicializada con 1. El efecto de la instrucci�n mostrada arriba es sumar 2 a j.El contenido de la direcci�n almacenada en un apuntador, puede ser asignado aotro apuntador o a otra variable*pt2 = *pt1; /* assigns the contents of the memory pointed to by pt1 */ /* to the contents of the memory pointer to by pt2; */k = *pt2; /* assigns the contents of the address pointer to by pt2 to k. */Aritm�tica de Apuntadores Parte del poder de los apuntadores proviene de la habilidad de hacer aritm�tica enlos apuntadores mismos. Los apuntadores pueden ser incrementados,decrementados y manipulados utilizando expresiones aritm�ticas. Consideremos elapuntador "pt3" y el arreglo de float "values" declarados en el programa principalmostrado anteriormente:pt3 = &values[0]; /* The address of the first element of "values" is stored in pt3*/pt3++; /* pt3 now contains the address of the second element of values */*pt3 = 3.1415927; /* The second element of values now has pie (actually pi)*/pt3 += 25; /* pt3 now points to the 27th element of values */*pt3 = 2.22222; / The 27th element of values is now 2.22222 */pt3 = values; /*pt3 points to the start of values, now */for (ii = 0; ii < 100; ii++){ *pt3++ = 37.0; /* This sets the entire array to 37.0 */}pt3 = &values[0]; /* pt3 contains the address of the first element of values */pt4 = &results[0];
  • 41. /* pt4 contains the address of the first element of results */for (ii=0; ii < 100; ii++){ *pt4 = *pt3; /* The contents of the address contained in pt3 are assigned to the contents of the address contained in pt4 */ pt4++; pt3++;}Pasaje de ParámetrosPor valor: Los valores que se pasan se copian a los parámetros de la función, si elvalor de un parámetro se modifica dentro de la función, no se altera su valor en elprograma que lo llama.Por Referencia: Permiten modificar dentro de la función el valor actual de lavariable que fue pasada como parámetro. Es decir, el valor de la variable si sealtera en el programa que llama.Podemos distinguir entre parámetros formales y parámetros realesParámetros Formales: Aquellos que van en la definición de la función o unprototipo. Son como variables locales a la función que sirven de comunicación conel exterior.Parámetros Reales: Se colocan en la invocación de la función, pueden serexpresiones o variables del mismo tipo del parámetro formal correspondiente.Deben colocarse en la misma posición del parámetro formal correspondiente,respetando el número tipo y posición de los parámetros formales.Ejemplo función Swapvoid Swap(int x, int y) {int temp ;temp = x;x = y;y = temp;}Con la invocacion, Swap(a,b) ? no tendria ningun efecto, pues los parametros sehan pasado por valor. Es decir, no se afectan los valores de a y b.Debe hacerse pasando los parametros por referenciavoid Swap(int *apx, int *apy){
  • 42. int temp ;temp = *apx;*apx = *apy;*apyy = temp ;}main(){int a = 20, b = 100;Swap(&a, &b);// Los valores de a y b se intercambian, a = 100, b= 20}ApuntadoresDefinición 2 Un apuntador es una variable que contiene una dirección dememoria.Supongamos una variable de tipo entero que se llama contenidoRAM y otravariable que se llama direccionRAM que puede contener una variable de tipoentero. En C/C++ una variable precedida del operador & devuelve la direcciónde la variable en lugar de su contenido. Así que para asignar la dirección deuna variable a otra variable del tipo que contiene direcciones se usansentencias como esta:direccionRam = &contenidoRAM
  • 43. Figura 2: contenidoRAM se asigna a la localidad de memoria con dirección 7751En la figura 2 se ilustra el nombre de la variable contenidoRAM y se observaque se encuentra en la dirección 7751 de la memoria. El contenido de estalocalidad no se muestra. Una variable que contiene una dirección, talcomo direccionRAM, se llama variable apuntador o simplemente apuntador.Despues que la sentencia anterior se ejecuta, la dirección de contenidoRAM seráasignada a la variable apuntador direccionRAM. La relación se expresadiciendo que direccionRAM apunta acontenidoRAM. La figura 3 ilustra estarelación. Figura 3: Notación de flecha para los apuntadoresEl accceso al contenido de una celda cuya dirección está almacenada en lavariable direccionRAM es tan sencillo como poner al inicio de la variableapuntador un asterisco: *direccionRAM. Lo que se ha hecho es eliminar lareferencia directa. Por ejemplo, si se ejecutan las siguientes dos sentencias, elvalor de la celda llamada contenidoRAM será de 20 (véase la figura 4).direccionRAM = &contenidoRAM;*direccionRAM = 20;
  • 44. Figura 4: A contenidoRAM se le asigna el valor entero 20Introducción a los apuntadoresLos apuntadores son variables que guardan direcciones en C y C++. Proporcionanmucha utilidad al programador para accesar y manipular datos de maneras que no esposible en otros lenguajes. Tambien son utiles para pasarle parametros a lasfunciones de tal modo que les permiten modificar y regresar valores a la rutina que lasllama. Cuando se utilizan incorrectamente, son también fuente tanto de fallas en elprograma como de frustración para el programador :P.IntroducciónMientras un programa esta corriendo todas las variables se guardan en memoria, cadauna tiene su dirección o localidad única. Generalmente, una variable y su localidadasociada contienen valores. Por ejemplo, cuando declaras: int count = 5;El valor "5" se guarda en memoria y puede ser accesado usando la variable "count".Un apuntador es un tipo especial de variable que contiene una dirección de memoriaen lugar del valor de un dato. Tal como un dato es modificado cuando una variablenormal es usada, el valor de la dirección guardado en el apuntador cambia cuandoeste es manipulado.Generalmente, la dirección guardada en el apuntador es la dirección de alguna otravariable. int *ptr; ptr = &count /* Guarda la dirección de count en ptr */
  • 45. /* El operador unario & regresa la dirección de una variable */Para obtener el valor que esta guardado en la localidad de memoria del apuntador esnecesario referenciar el apuntador. La referencia se hace con el operador unario "*". int total; total = *ptr; /* El valor de la direccipon guardada en ptr es asignada a total */La mejor manera de aprender a usar apuntadores es con ejemplos. Hay ejemplos delos tipos de operaciones ya discutidas abajo. Los apuntadores son un tema dificil. Note preocupes si todavía no queda todo claro.Declaración e inicializaciónDeclarar e inicializar apuntadores es bastante fácil. int main() { int j; int k; int l; int *pt1; /* Declara un apuntador entero */ int *pt2; /* Declara un apuntador entero */ float values[100]; float results[100]; float *pt3; /* Declara un apuntador flotante */ float *pt4; /* Declara un apuntador flotante */ j = 1; k = 2; pt1 = &j; /* pt1 contiene la dirección de la variable j */ pt2 = &k; /* pt2 contiene la dirección de la variable k */ pt3 = values; /* pt3 contiene la dirección del primer elemento de values */ pt3 = &values[0]; /* Esto es equivalente a la afirmación de arriba */ return 0; }Referencia de apuntadores/ Asignación de valoresLa referencia permite manipular los datos contenidos en la dirección de memoriaguardada en el apuntador. El apuntador guarda una dirección de memoria. Lareferencia permite que los datos en esa dirección de memoria sean modificados. Eloperador unario "*" se usa para la referencia. Por ejemplo: *pt1 =*pt1 + 2;Esto le añade dos al valor "apuntado por" pt1. Esto quiere decir que esta instrucción lesuma 2 al contenido de la dirección de memoria guardada en el apuntador pt1. Así, enel programa main, pt1 contiene la dirección de j. La variable "j" fue inicializada en 1. Elefecto del código de arriba es sumar 2 a j.El contenido de las direcciones guardadas en un apuntador pueden ser asignadas aotro apuntador o variable.
  • 46. *pt2 = *pt1; /* asigna el contenido de la memoria apuntada por pt1 */ /* al contenido de la memoria apuntada por pt2 */ k = *pt2; /* asigna el contenido del apuntador pt2 por dos a k */Aritmética de apuntadoresParte del poder de los apuntadores viene de la habilidad de realizar operacionesmatemáticas sobre los mismos apuntadores. Los apuntadores pueden serincrementados, decrementados y manipulados usando expresiones matemáticas.Recordando el apuntador flotante "pt3" y el arreglo flotante "values" declarados en elprograma main de arriba. pt3 = &values[0]; /* La dirección del primer elemento de "values" se guarda en pt3*/ pt3++; /* pt3 ahora contiene la dirección del segundo elemento de values*/ *pt3 = 3.1415927; /* El segundo elemento de values tiene pay (de hecho pi)*/ pt3 += 25; /* pt3 ahora apunta al elemento 27 de values */ *pt3 = 2.22222; /* el elemento 27 de values ahora es 2.22222 */ pt3 = values; /*pt3 apunta al primer elemento de values, ahora */ for (ii = 0; ii < 100; ii++) { *pt3++ = 37.0; /* esto pone todo el arreglo en 37.0 */ } pt3 = &values[0]; /* pt3 contiene la dirección del primer elemento de values */ pt4 = &results[0]; /* pt4 contiene la dirección del primer elemento de results */ for (ii=0; ii < 100; ii++) { *pt4 = *pt3; /* Los contenidos de las direcciones de pt3 se le asignan a los contenidos de las direcciones de pt4 */ pt4++; pt3++; } 7.1 ApuntadoresEn una computadora cada posición de memoria tiene una dirección y unvalor específico almacenado en esa posición. Se han utilizado nombres devariables en lugar de direcciones porque los nombres son más fáciles de
  • 47. recordar. Para almacenar un nuevo valor en memoria se asigna a unavariable, y la computadora envía una dirección a la memoria seguida porel valor a almacenar en esa posición.Pascal proporciona un tipo especial de variable denominado apuntador,que es una variable cuyo valor es una dirección de una posición dememoria.Al definir un tipo apuntador se debe indicar el tipo de valores que sealmacenarán en las posiciones designadas por los apuntadores. La razónes que los diferentes tipos de datos requieren diferentes cantidades dememoria para almacenar sus constantes, una variable apuntador puedecontener una dirección de una posición de memoria adecuada sólo paraun tipo dado. Por esta razón se dice que un apuntador apunta a unavariable particular.Una variable tipo apuntador contiene la dirección de la posición de otravariablePara declarar una variable de tipo apuntador en Pascal se debeespecificar el nombre de la variable y el tipo del valor que se almacenaráen la posición de memoria a la que el apuntador se refiere.Formato: Type tipo_apuntador = ^tipo datoUna variable de tipo tipo_apuntador es un apuntador hacia un dato detipo nombre del tipo. El signo ^ se lee "apunta hacia".Ejemplos: 1. Type 2. Apuntador = ^real; 3. Var 4. P : Apuntador; 5. {define el tipo,cuyos valores apuntan 6. a posiciones que contienen números reales 7. P apunta a posiciones que 8. contienen números reales} 9. Var
  • 48. 10. P : ^real; 11. 12. Type 13. registro = record 14. nombre:string[10]; 15. edad :integer; 16. sexo :char 17. end; 18. Var 19. P :integer; 20. Q :^registro;P sólo puede contener direcciones de posiciones de memoria adecuadapara almacenar enteros. Q se limita a datos de tipo registro.Capítulo 29:PunterosEnlaces patrocinadosRepetro - DatabrasRegime Especial Aduaneiro Repetro Databraswww.repetro.com Introducción Como vimos anteriormente en la lección número 5, lasestructuras de datos dinámicasson aquellas cuya ocupación dememoria puede aumentar o disminuir durante el tiempo deejecución. Mediante los punteros, tema que estudiaremos acontinuación, podemos crear estructuras de datos dinámicas quetienen capacidad de variar en tamaño y ocupar tanta memoria comorealmente requieran. Estas estructuras son llamadas punteros.7.1 Concepto
  • 49. Los punteros son también llamados apuntadores. Son tiposde datos que permiten crear estructuras de datos dinámicas, lascuales pueden variar en tamaño y memoria requerida. Las variablesque se crean y se destruyen durante la ejecución sellaman variables dinámicas o anónimas. Así, durante la ejecución deun programa, puede haber una posición de memoria específicaasociada con una variable dinámica y posteriormente puede noexistir ninguna posición de memoria asociada con ella. Una estructura de datos dinámica es una colección deelementos llamados nodos de la estructura - normalmente tiporegistro- que se enlazan o encadenan juntos. Este enlace seestablece asociando con cada nodo un puntero que apunta al nodosiguiente de la estructura. Las estructuras de datos dinámicas son útiles paraalmacenar y procesar conjuntos de datos cuyos tamaños cambiandurante la ejecución del programa, por ejemplo, el conjunto detrabajos que se han introducido en una computadora y estánesperando su ejecución o el conjunto de nombres de pasajeros yasignación o el conjunto de nombres de pasajeros y asignaciónrespectiva de asientos de un vuelo de avión determinado. Pascal proporciona los métodos para asignar y liberarespacio de memoria utilizando punteros y losprocedimientos new y dispose. El tipo de datos puntero es de tipo simple pues no se puederomper en otros componentes más pequeños, como sí sucede conel array o el registro. Los punteros son variables que se utilizan paraalmacenar la dirección de memoria de otra variable. Las variablesque se utilizan para almacenar direcciones son llamadas variablespuntero o simplemente puntero. Gráficamente se representa así
  • 50. Al definir un puntero se debe indicar el tipo de valores que sealmacenarán en las posiciones designadas por los punteros. Estose debe a que los diferentes tipos de datos requieren distintascantidades de memoria para almacenar sus constantes, unavariable puntero puede contener una dirección de una posición dememoria adecuada sólo para un tipo dado. Por esta razón se dice que un puntero apunta a una variableparticular, es decir, a otra posición de memoria. Una variable tipo puntero contiene la dirección de la posiciónde otra variable.