Discussion:
Punteros a punteros
(demasiado antiguo para responder)
Shiva
2003-09-14 16:29:36 UTC
Permalink
Hola,

¿¿Alguien tiene algún tutorial en el que vengan bien explicados los
punteros a punteros??


Gracias.
Fernando Arbeiza
2003-09-14 16:46:47 UTC
Permalink
Post by Shiva
¿¿Alguien tiene algún tutorial en el que vengan bien explicados los
punteros a punteros??
Si te manejas en inglés, tienes un tutorial majo sobre punteros en:

<URL: http://pw1.netcom.com/~tjensen/ptr/pointers.htm>

Una vez que entiendes bien los punteros, un puntero a puntero sólo es
introducir un nivel más de indirección. Es decir, manejar el valor al
que referencia un puntero, como otro puntero.

Un saludo.
--
Fernando Arbeiza <URL: mailto:***@ono.com>
Crea tu propio Linux: <URL: http://www.escomposlinux.org/lfs-es>
Shiva
2003-09-14 17:11:26 UTC
Permalink
Pero lo que no entiendo es para qué sirve un puntero a puntero.
Post by Fernando Arbeiza
Post by Shiva
¿¿Alguien tiene algún tutorial en el que vengan bien explicados los
punteros a punteros??
<URL: http://pw1.netcom.com/~tjensen/ptr/pointers.htm>
Una vez que entiendes bien los punteros, un puntero a puntero sólo es
introducir un nivel más de indirección. Es decir, manejar el valor al
que referencia un puntero, como otro puntero.
Un saludo.
--
Crea tu propio Linux: <URL: http://www.escomposlinux.org/lfs-es>
nako
2003-09-14 18:18:54 UTC
Permalink
Post by Shiva
Pero lo que no entiendo es para qué sirve un puntero a puntero.
Como sabrás los nombres de vectores son punteros.

así un puntero a punteros, puede referenciar perfectamente a un vector de
punteros.

Si estos punteros son por ejemplo a enteros, podemos tener una matriz de
enteros. Esta matriz se podría reservar de forma dinamica(con lo que
conlleva, agrandar en tiempo de ejecución, liberar memoria, etc), además
es de dos dimensiones, pero se le pueden sumar (puntero a punteros a
punteros a enteros 3D).

No se si me he explicado bien, lo has entendido???

Saludos




--------------------------------------------------

Mensaje enviado desde http://grupos.buscadoc.org

--------------------------------------------------
Martin J. Sanchez
2003-09-15 09:58:02 UTC
Permalink
Post by Shiva
Pero lo que no entiendo es para qué sirve un puntero a puntero.
Lo siguiente esta extraido de algunas de mis respuetas anteriores a
esta pregunta (para mas informacion busca en groups.google.com por
"punteros" o "variable puntero".


Declaraciones y definiciones
============================
Cada declaracion/definicion de una variable se compone de una lista de
atributos parciales y un tipo final.
Para saber lo que representa un identificador, y por tanto que es
aplicable a el, debes descomponer su declaracion en la lista de
atributos parciales aplicando la precedencia de operadores (tal y como
hace el compilador), hasta llegar al tipo final. Por ejemplo, en:
char *p;
se toma el identificador 'p', el operador aplicado a p de mayor
precedencia es *, este operador se traduce a 'el valor de lo apuntado
por'. Finalmente encontramos char que es el tipo final de la expresion
'*p'. Lista de declaracion:
p ----- idetificador
*p ----- el valor de lo apuntado por p
char *p --- el valor de lo apuntado por p es char

Punteros y variables punteros
=============================
Un puntero no es mas que la localizacion, o direccion, donde se
almacena un dato.
Se dice que un puntero, apunta al lugar donde se encuentra la
informacion que se desea.

Por ejemplo (simplificando), si en la posicion 100 de la memoria hay
un char, el puntero a ese dato es el 100 (su direccion).

En C, podemos declarar una variable en la que meteremos un puntero.
Ej:
char *puntero1;
esto define 'puntero1' como una variable donde pondremos un puntero.
(a 'puntero1' se le llama 'variable puntero').

Ahora metemos en puntero1 la direccion del dato:
puntero1=100;
Ahora se dice que puntero1 apunta al char que esta en la pos. 100.
Nota: Ya que el compilador intenta prevenir de errores, se exige que
el valor de la izda de una expresion sea del mismo tipo (o
convertible) al de la izquierda. Por ello para hacer lo anterior
deberiamos haber dicho que 100 es una direccion a char:
puntero1=(char*)100;
aunque normalmente tendremos:
char var='a';
char *puntero1= &var; /* se coge la direccion de var y se
mete en puntero1 */

Para poder cambiar el dato al que apunta puntero1 se utiliza:
*puntero1='A';
Ahora el valor del dato de la posicion 100 es 'A'

Para recuperar el dato de la posicion apuntada por el puntero, se
utiliza:
char c;
c = *puntero1;
Ahora c (una variable) contiene 'A'

Un array no es una variable puntero
===================================
Por ejemplo en:
1) char *p= "hola";
o
2)
char array[]="hola";
char *p=array;

en memoria tendras:
900 (direccion donde esta la variable p)
--------
| 1000 |
--------

1000 (es la direccion inicial donde se ha puesto "hola")
-------
| 'h' | -------
| 'o' | -------
| 'l' | -------
| 'a' | -------
| 0 |
-------

En el ejemplo 1: "hola" se evalua a la direccion de comienzo (1000)
de la string.
Igualmente en el ejemplo 2, 'array' es una constante indicando la
direccion de comienzo del array (tambien 1000).

En ambos ejemplos 'p' es una variable puntero. Como es una variable se
puede modificar para apuntar a otro sitio:
p= "que tal?";

Sin embargo en el ejemplo 2 'array' es una constante (la direccion
donde esta el array) y no se puede modificar para apuntar a otro
sitio. Por ejemplo:
array = "que tal?"; /* error array no es un lvalue */
"No es un lvalue" significa que 'array' no puede aparecer en la
izquierda de una expresion de asignacion.

Punteros a punteros
===================
En el siguiente ejemplo:
char *p1 = "hola";
char *p2= "que tal?";
char **p;

p1 y p2 son variables punteros que apuntan a sus respectivas cadenas.
Mientras que p es una variable puntero a puntero.
Estudiemos la declaracion de p:
p -- identificador
*p -- el contenido apuntado por p
** -- el contenido apuntado por lo apuntado por p
char -- el contenido apuntado por lo apuntado por p es char

Si ahora ponemos:
p= &p1;
En memoria tendriamos algo como:

800 (direccion donde esta la variable p)
--------
| 900 |
--------

900 (direccion donde esta la variable p1)
--------
| 1000 |
--------

1000 (es la direccion inicial donde se ha puesto "hola")
-------
| 'h' | -------
| 'o' | -------
| 'l' | -------
| 'a' | -------
| 0 |
-------

Ahora podremos hacer que p cambie para apuntar a p2:
p= &p2;

Como p contiene la direccion de otra variable, podemos cambiar el
valor de la otra variable para que apunte a otro sitio:
p= &p2;
*p = p1;
ahora p2 y p1 apuntan a lo mismo. Como ves aqui se han hecho dos
cosas:
- inicializar p para que contenga la direccion de p2.
- A traves de p se accede al valor de p2 y se cambia para apuntar a
p1.

Tambien podriamos coger el elemento x de de lo apuntado por p1:
p= &p1;
char c;
c= *(*p+x);
La ultima expresion es equivalente a:
c= (*p)[x];

Todo esto no parece servir de mucho. De hecho podriamos haber hecho
todo esto usando p1 o p2 directamente. Pero veamos donde es util.

Supon que deseas una funcion que devuelve un numero de error (si lo
hay) y ademas quieres que ponga un puntero para apuntar al mensaje de
error correspondiente. La quieres usas asi:
int main()
{
char *msgError="";
....
if( getMsgError( msgError ) ) {
printf( "%s", msgError );
abort();
}
}

Aqui hay un error de base. Al llamar a la funcion:
getMsgError( msgError )
lo que esta haciendo el compilador es coger el valor contenido en
msgError (la direccion de una cadena nula) y la mete en la pila,
despues hace la llamada a getMsgError.
Esto provoca que la funcion getMsgError reciba la direccion de la
cadena (NO de la variable msgError), por lo que no puede cambiar
msgError para que apunte a la cadena adecuada.

Es evidente por tanto que hay que pasar la direccion de la variable
puntero para que la funcion pueda cambiar su contenido:
if( getMsgError( &msgError ) ) {

Ahora estas pasando un puntero a la variable y por tanto la funcion
podra cambiar la variable.

La funcion getMsgError podria estar definida asi:

int getMsgError( char** msgError)
{
const char *msgError[] = {
"No hay error",
"Error 1",
"Error 2",
....
};
int error= getCodeError();
return msgError[numerror];
}



Espero que esto haya quedado claro (me he hartado de hacer copy&paste)
y que te aclare algo el tema.

Un saludo,
Martin.
Martin J. Sanchez
2003-09-15 10:03:59 UTC
Permalink
Post by Shiva
Pero lo que no entiendo es para qué sirve un puntero a puntero.
Lo siguiente esta extraido de algunas de mis respuetas anteriores a
esta pregunta (para mas informacion busca en groups.google.com por
"punteros" o "variable puntero".


Declaraciones y definiciones
============================
Cada declaracion/definicion de una variable se compone de una lista de
atributos parciales y un tipo final.
Para saber lo que representa un identificador, y por tanto que es
aplicable a el, debes descomponer su declaracion en la lista de
atributos parciales aplicando la precedencia de operadores (tal y como
hace el compilador), hasta llegar al tipo final. Por ejemplo, en:
char *p;
se toma el identificador 'p', el operador aplicado a p de mayor
precedencia es *, este operador se traduce a 'el valor de lo apuntado
por'. Finalmente encontramos char que es el tipo final de la expresion
'*p'. Lista de declaracion:
p ----- idetificador
*p ----- el valor de lo apuntado por p
char *p --- el valor de lo apuntado por p es char

Punteros y variables punteros
=============================
Un puntero no es mas que la localizacion, o direccion, donde se
almacena un dato.
Se dice que un puntero, apunta al lugar donde se encuentra la
informacion que se desea.

Por ejemplo (simplificando), si en la posicion 100 de la memoria hay
un char, el puntero a ese dato es el 100 (su direccion).

En C, podemos declarar una variable en la que meteremos un puntero.
Ej:
char *puntero1;
esto define 'puntero1' como una variable donde pondremos un puntero.
(a 'puntero1' se le llama 'variable puntero').

Ahora metemos en puntero1 la direccion del dato:
puntero1=100;
Ahora se dice que puntero1 apunta al char que esta en la pos. 100.
Nota: Ya que el compilador intenta prevenir de errores, se exige que
el valor de la izda de una expresion sea del mismo tipo (o
convertible) al de la izquierda. Por ello para hacer lo anterior
deberiamos haber dicho que 100 es una direccion a char:
puntero1=(char*)100;
aunque normalmente tendremos:
char var='a';
char *puntero1= &var; /* se coge la direccion de var y se
mete en puntero1 */

Para poder cambiar el dato al que apunta puntero1 se utiliza:
*puntero1='A';
Ahora el valor del dato de la posicion 100 es 'A'

Para recuperar el dato de la posicion apuntada por el puntero, se
utiliza:
char c;
c = *puntero1;
Ahora c (una variable) contiene 'A'

Un array no es una variable puntero
===================================
Por ejemplo en:
1) char *p= "hola";
o
2)
char array[]="hola";
char *p=array;

en memoria tendras:
900 (direccion donde esta la variable p)
--------
| 1000 |
--------

1000 (es la direccion inicial donde se ha puesto "hola")
-------
| 'h' | -------
| 'o' | -------
| 'l' | -------
| 'a' | -------
| 0 |
-------

En el ejemplo 1: "hola" se evalua a la direccion de comienzo (1000)
de la string.
Igualmente en el ejemplo 2, 'array' es una constante indicando la
direccion de comienzo del array (tambien 1000).

En ambos ejemplos 'p' es una variable puntero. Como es una variable se
puede modificar para apuntar a otro sitio:
p= "que tal?";

Sin embargo en el ejemplo 2 'array' es una constante (la direccion
donde esta el array) y no se puede modificar para apuntar a otro
sitio. Por ejemplo:
array = "que tal?"; /* error array no es un lvalue */
"No es un lvalue" significa que 'array' no puede aparecer en la
izquierda de una expresion de asignacion.

Punteros a punteros
===================
En el siguiente ejemplo:
char *p1 = "hola";
char *p2= "que tal?";
char **p;

p1 y p2 son variables punteros que apuntan a sus respectivas cadenas.
Mientras que p es una variable puntero a puntero.
Estudiemos la declaracion de p:
p -- identificador
*p -- el contenido apuntado por p
** -- el contenido apuntado por lo apuntado por p
char -- el contenido apuntado por lo apuntado por p es char

Si ahora ponemos:
p= &p1;
En memoria tendriamos algo como:

800 (direccion donde esta la variable p)
--------
| 900 |
--------

900 (direccion donde esta la variable p1)
--------
| 1000 |
--------

1000 (es la direccion inicial donde se ha puesto "hola")
-------
| 'h' | -------
| 'o' | -------
| 'l' | -------
| 'a' | -------
| 0 |
-------

Ahora podremos hacer que p cambie para apuntar a p2:
p= &p2;

Como p contiene la direccion de otra variable, podemos cambiar el
valor de la otra variable para que apunte a otro sitio:
p= &p2;
*p = p1;
ahora p2 y p1 apuntan a lo mismo. Como ves aqui se han hecho dos
cosas:
- inicializar p para que contenga la direccion de p2.
- A traves de p se accede al valor de p2 y se cambia para apuntar a
p1.

Tambien podriamos coger el elemento x de de lo apuntado por p1:
p= &p1;
char c;
c= *(*p+x);
La ultima expresion es equivalente a:
c= (*p)[x];

Todo esto no parece servir de mucho. De hecho podriamos haber hecho
todo esto usando p1 o p2 directamente. Pero veamos donde es util.

Supon que deseas una funcion que devuelve un numero de error (si lo
hay) y ademas quieres que ponga un puntero para apuntar al mensaje de
error correspondiente. La quieres usas asi:
int main()
{
char *msgError="";
....
if( getMsgError( msgError ) ) {
printf( "%s", msgError );
abort();
}
}

Aqui hay un error de base. Al llamar a la funcion:
getMsgError( msgError )
lo que esta haciendo el compilador es coger el valor contenido en
msgError (la direccion de una cadena nula) y la mete en la pila,
despues hace la llamada a getMsgError.
Esto provoca que la funcion getMsgError reciba la direccion de la
cadena (NO de la variable msgError), por lo que no puede cambiar
msgError para que apunte a la cadena adecuada.

Es evidente por tanto que hay que pasar la direccion de la variable
puntero para que la funcion pueda cambiar su contenido:
if( getMsgError( &msgError ) ) {

Ahora estas pasando un puntero a la variable y por tanto la funcion
podra cambiar la variable.

La funcion getMsgError podria estar definida asi:

int getMsgError( char** pmsgError)
{
const char *msgError[] = {
"No hay error",
"Error 1",
"Error 2",
....
};
int error= getCodeError();
if( pmsgError ) // si se ha pasado un puntero valido
*pmsgError= msgError[error];
return error;
}



Espero que esto haya quedado claro (me he hartado de hacer copy&paste)
y que te aclare algo el tema.

Un saludo,
Martin.
Martin J. Sanchez
2003-09-15 18:58:53 UTC
Permalink
On Mon, 15 Sep 2003 12:03:59 +0200, Martin J. Sanchez
Post by Martin J. Sanchez
...
- A traves de p se accede al valor de p2 y se cambia para apuntar a
p1.
...
El ultimo comentario es erroneo. Deberia decir:
- A traves de p se accede al valor de p2 y se cambia para apuntar a
lo mismo que apunta p1.

Un saludo,
Martin.

Carlos P.C.
2003-09-14 16:56:51 UTC
Permalink
Hola,
Alguien tiene algn tutorial en el que vengan bien explicados los
punteros a punteros??
Gracias.
Echale un vistazo a la siguiente página, es un curso enfocado a punteros :-)

http://www.elrincondelc.com/cursos/punteros.php

Suerte :D
Loading...