No, no la tarea ... solo quería saber ... porque lo veo mucho cuando leo el código C.
1
Un puntero a puntero no es un caso especial de algo, por lo que no entiendo lo que no entiendes sobre vacío **.
akappa
para matrices 2D, el mejor ejemplo es la línea de comando args "prog arg1 arg2" se almacena char ** argv. Y si la persona que llama no quiere asignar la memoria (la función llamada asignará la memoria)
Supongamos una computadora de 8 bits con direcciones de 8 bits (y, por lo tanto, solo 256 bytes de memoria). Esto es parte de esa memoria (los números en la parte superior son las direcciones):
54555657585960616263646566676869+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+||58|||63||55||| h | e | l | l | o | \0 ||+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
Lo que puedes ver aquí es que en la dirección 63 comienza la cadena "hola". Entonces, en este caso, si esta es la única aparición de "hola" en la memoria, entonces,
constchar*c ="hello";
... define ccomo un puntero a la cadena (solo lectura) "hola", y por lo tanto contiene el valor 63. cdebe almacenarse en algún lugar: en el ejemplo anterior en la ubicación 58. Por supuesto, no solo podemos señalar caracteres , pero también a otros punteros. P.ej:
constchar**cp =&c;
Ahora cpapunta a c, es decir, contiene la dirección de c(que es 58). Podemos ir aún más lejos. Considerar:
constchar***cpp =&cp;
Ahora cppalmacena la dirección de cp. Por lo tanto, tiene el valor 55 (basado en el ejemplo anterior), y lo adivinó: está almacenado en la dirección 60.
En cuanto a por qué uno usa punteros a punteros:
El nombre de una matriz generalmente produce la dirección de su primer elemento. Entonces, si la matriz contiene elementos de tipo t, una referencia a la matriz tiene tipo t *. Ahora considere una matriz de matrices de tipo t: naturalmente, una referencia a esta matriz 2D tendrá type (t *)*= t **, y por lo tanto es un puntero a un puntero.
Aunque una serie de cadenas suena unidimensional, de hecho es bidimensional, ya que las cadenas son matrices de caracteres. Por lo tanto: char **.
Una función fnecesitará aceptar un argumento de tipo t **para alterar una variable de tipo t *.
Muchas otras razones que son demasiado numerosas para enumerarlas aquí.
sí buen ejemplo ... entiendo lo que son ... pero cómo y cuándo usarlos es más importante ... ahora
2
Stephan hizo un buen trabajo reproduciendo, básicamente, el diagrama del lenguaje de programación The C de Kernighan & Richie. Si está programando C, y no tiene este libro y le gusta la documentación en papel, le recomiendo que lo obtenga, el gasto (bastante) modesto se amortizará muy rápidamente en productividad. Tiende a ser muy claro en sus ejemplos.
J. Polfer
44
char * c = "hola" debe ser constante char * c = "hola". Además, es muy engañoso decir que "una matriz se almacena como la dirección del primer elemento". Una matriz se almacena como ... una matriz. A menudo, su nombre arroja un puntero a su primer elemento, pero no siempre. Acerca de los punteros a punteros, simplemente diría que son útiles cuando una función tiene que modificar un puntero pasado como parámetro (en su lugar, pasa un puntero al puntero).
Bastien Léonard
44
A menos que esté malinterpretando esta respuesta, se ve mal. c se almacena en 58 y apunta a 63, cp se almacena en 55 y apunta a 58, y cpp no se representa en el diagrama.
Thanatos
1
Se ve bien. Que un problema menor fue todo lo que me impedía decir: Gran publicación. La explicación en sí fue excelente. Cambio a un voto positivo. (¿Quizás stackoverflow necesita revisar los punteros?)
Thanatos
46
¿Cómo funcionan los punteros a punteros en C?
Primero, un puntero es una variable, como cualquier otra variable, pero que contiene la dirección de una variable.
Un puntero a un puntero es una variable, como cualquier otra variable, pero que contiene la dirección de una variable. Esa variable resulta ser un puntero.
¿Cuándo los usarías?
Puede usarlos cuando necesite devolver un puntero a alguna memoria en el montón, pero no usar el valor de retorno.
Ejemplo:
int getValueOf5(int*p){*p =5;return1;//success}int get1024HeapMemory(int**p){*p = malloc(1024);if(*p ==0)return-1;//errorelsereturn0;//success}
Y lo llamas así:
int x;
getValueOf5(&x);//I want to fill the int varaible, so I pass it's address in//At this point x holds 5int*p;
get1024HeapMemory(&p);//I want to fill the int* variable, so I pass it's address in//At this point p holds a memory address where 1024 bytes of memory is allocated on the heap
También hay otros usos, como el argumento main () de cada programa en C tiene un puntero a un puntero para argv, donde cada elemento contiene una matriz de caracteres que son las opciones de línea de comando. Sin embargo, debe tener cuidado cuando utilice punteros de punteros para apuntar a matrices bidimensionales, es mejor utilizar un puntero a una matriz bidimensional.
Aquí hay un ejemplo de un puntero a una matriz bidimensional hecho correctamente:
int(*myPointerTo2DimArray)[ROWS][COLUMNS]
Sin embargo, no puede usar un puntero a una matriz bidimensional si desea admitir un número variable de elementos para las FILAS y COLUMNAS. Pero cuando sabes de antemano, usarías una matriz bidimensional.
Me gusta este ejemplo de código de "mundo real" de uso de puntero a puntero, en Git 2.0, confirme 7b1004b :
Linus dijo una vez:
De hecho, desearía que más personas entendieran el tipo de codificación realmente básico de bajo nivel. No son cosas grandes y complejas, como la búsqueda de nombres sin cerradura, sino simplemente un buen uso de punteros a punteros, etc.
Por ejemplo, he visto a demasiadas personas que eliminan una entrada de lista enlazada individualmente haciendo un seguimiento de la entrada "anterior" , y luego para eliminar la entrada, haciendo algo como
y cada vez que veo un código así, simplemente digo "Esta persona no entiende los punteros". Y lamentablemente es bastante común.
Las personas que entienden los punteros simplemente usan un " puntero al puntero de entrada " e inicializan eso con la dirección de list_head. Y luego, a medida que atraviesan la lista, pueden eliminar la entrada sin usar ningún condicionante, simplemente haciendo un
*pp = entry->next
La aplicación de esa simplificación nos permite perder 7 líneas de esta función, incluso al agregar 2 líneas de comentarios.
Debe iterar sobre él desde el principio hasta el final y eliminar un elemento específico cuyo valor sea igual al valor de to_remove.
La forma más obvia de hacer esto sería:
list_entry *entry = head;/* assuming head exists and is the first entry of the list */
list_entry *prev = NULL;while(entry){/* line 4 */if(entry->val == to_remove)/* this is the one to remove ; line 5 */if(prev)
prev->next = entry->next;/* remove the entry ; line 7 */else
head = entry->next;/* special case - first entry ; line 9 *//* move on to the next entry */
prev = entry;
entry = entry->next;}
Lo que estamos haciendo arriba es:
iterar sobre la lista hasta que aparezca la entrada NULL, lo que significa que hemos llegado al final de la lista (línea 4).
Cuando nos encontramos con una entrada que queremos eliminar (línea 5),
asignamos el valor del siguiente puntero actual al anterior,
eliminando así el elemento actual (línea 7).
Hay un caso especial arriba: al comienzo de la iteración no hay una entrada anterior ( preves NULL), por lo que para eliminar la primera entrada de la lista debe modificar el encabezado (línea 9).
Lo que Linus decía es que el código anterior podría simplificarse haciendo que el elemento anterior sea un puntero a un puntero en lugar de solo un puntero .
El código entonces se ve así:
El código anterior es muy similar a la variante anterior, pero observe cómo ya no necesitamos estar atentos al caso especial del primer elemento de la lista, ya ppque no está NULLal principio. Simple e inteligente.
Además, alguien en ese hilo comentó que la razón por la que esto es mejor es porque *pp = entry->nextes atómica. Ciertamente NO es atómico .
La expresión anterior contiene dos operadores de desreferencia ( *y ->) y una asignación, y ninguna de esas tres cosas es atómica. Este es un error común, ¡pero por desgracia casi nada en C debería suponerse que es atómico (incluidos los operadores ++y --)!
@kumar buena referencia. Lo he incluido en la respuesta para mayor visibilidad.
VonC
Este video fue esencial para mí para entender tu ejemplo. En particular, me sentía confundido (y beligerante) hasta que dibujé un diagrama de memoria y seguí el progreso del programa. Dicho esto, todavía me parece algo misterioso.
Chris
@Chris Gran video, ¡gracias por mencionarlo! He incluido tu comentario en la respuesta para mayor visibilidad.
VonC
14
Cuando cubrimos consejos en un curso de programación en la universidad, nos dieron dos pistas sobre cómo comenzar a aprender sobre ellos. El primero fue ver Pointer Fun With Binky . El segundo era pensar en los ojos de los eglefinos de A través del espejo de Lewis Carroll
"Estás triste", dijo el Caballero en un tono ansioso: "Déjame cantarte una canción para consolarte".
"¿Es muy largo?" Preguntó Alice, porque había escuchado mucha poesía ese día.
“Es largo”, dijo el Caballero, “pero es muy, muy hermoso. Todos los que me oyen cantarlo ... o les trae lágrimas a los ojos, o bien ...
"¿O si no qué?" dijo Alice, porque el Caballero había hecho una pausa repentina.
“O de lo contrario no, ya sabes. El nombre de la canción se llama 'Haddocks' Eyes '".
"Oh, ese es el nombre de la canción, ¿verdad?", Dijo Alice, tratando de sentirse interesada.
"No, no entiendes", dijo el Caballero, un poco molesto. “Así se llama el nombre. El nombre realmente es 'The Aged Aged Man' ".
"¿Entonces debería haber dicho 'Así se llama la canción'?" Alice se corrigió a sí misma.
“No, no deberías: ¡eso es otra cosa! La canción se llama 'Ways And Means': ¡pero así es como se llama!
"Bueno, ¿cuál es la canción, entonces?" dijo Alice, que para entonces estaba completamente desconcertada.
"Estaba llegando a eso", dijo el Caballero. "La canción realmente es 'A-sitting On A Gate': y la canción es mi propio invento".
Cuando se requiere una referencia a un puntero. Por ejemplo, cuando desea modificar el valor (dirección apuntada) de una variable de puntero declarada en el alcance de una función de llamada dentro de una función llamada.
Si pasa un solo puntero como argumento, modificará copias locales del puntero, no el puntero original en el alcance de la llamada. Con un puntero a un puntero, modifica el último.
Un puntero a un puntero también se llama un identificador . Un uso para esto es a menudo cuando un objeto se puede mover en la memoria o eliminar. A menudo, uno es responsable de bloquear y desbloquear el uso del objeto para que no se mueva al acceder.
A menudo se usa en entornos con memoria restringida, es decir, Palm OS.
Considere la figura y el programa a continuación para comprender mejor este concepto .
Según la figura, ptr1 es un puntero único que tiene una dirección de variable num .
ptr1 =#
Del mismo modo, ptr2 es un puntero a puntero (puntero doble) que tiene la dirección del puntero ptr1 .
ptr2 =&ptr1;
Un puntero que apunta a otro puntero se conoce como doble puntero. En este ejemplo ptr2 es un puntero doble.
Valores del diagrama anterior:
Address of variable num has :1000Address of Pointer ptr1 is:2000Address of Pointer ptr2 is:3000
Ejemplo:
#include<stdio.h>int main (){int num =10;int*ptr1;int**ptr2;// Take the address of var
ptr1 =#// Take the address of ptr1 using address of operator &
ptr2 =&ptr1;// Print the value
printf("Value of num = %d\n", num );
printf("Value available at *ptr1 = %d\n",*ptr1 );
printf("Value available at **ptr2 = %d\n",**ptr2);}
Salida:
Value of num =10Value available at *ptr1 =10Value available at **ptr2 =10
es un puntero al valor de la dirección del puntero. (eso es terrible, lo sé)
básicamente, le permite pasar un puntero al valor de la dirección de otro puntero, por lo que puede modificar dónde apunta otro puntero desde una subfunción, como:
Un puntero a puntero es, bueno, un puntero a puntero.
Un ejemplo significativo de someType ** es una matriz bidimensional: tiene una matriz, llena de punteros a otras matrices, así que cuando escribe
dpointer [5] [6]
accede a la matriz que contiene punteros a otras matrices en su quinta posición, obtiene el puntero (deje su nombre en fpointer) y luego acceda al sexto elemento de la matriz referenciado a esa matriz (fpointer [6]).
los punteros a punteros no deben confundirse con las matrices de rango 2, por ejemplo, int x [10] [10] donde escribe x [5] [6] accede al valor en la matriz.
Pete Kirkham
Este es solo un ejemplo donde un vacío ** es apropiado. Un puntero a puntero es solo un puntero que apunta, bueno, a un puntero.
akappa
1
Cómo funciona: es una variable que puede almacenar otro puntero.
¿Cuándo los usaría? Muchos usos uno de ellos es si su función quiere construir una matriz y devolverla a la persona que llama.
//returns the array of roll nos {11, 12} through paramater// return value is total number of studentsint fun(int**i ){int*j;*i =(int*)malloc (2*sizeof(int));**i =11;// e.g., newly allocated memory 0x2000 store 11
j =*i;
j++;*j =12;;// e.g., newly allocated memory 0x2004 store 12return2;}int main(){int*i;int n = fun(&i );// hey I don't know how many students are in your class please send all of their roll numbers.for(int j=0; j<n; j++)
printf("roll no = %d \n", i[j]);return0;}
Hay muchas explicaciones útiles, pero no encontré solo una breve descripción, así que ...
Básicamente, el puntero es la dirección de la variable. Código de resumen corto:
int a,*p_a;//declaration of normal variable and int pointer variable
a =56;//simply assign value
p_a =&a;//save address of "a" to pointer variable*p_a =15;//override the value of the variable//print 0xfoo and 15 //- first is address, 2nd is value stored at this address (that is called dereference)
printf("pointer p_a is having value %d and targeting at variable value %d", p_a,*p_a);
Respuestas:
Supongamos una computadora de 8 bits con direcciones de 8 bits (y, por lo tanto, solo 256 bytes de memoria). Esto es parte de esa memoria (los números en la parte superior son las direcciones):
Lo que puedes ver aquí es que en la dirección 63 comienza la cadena "hola". Entonces, en este caso, si esta es la única aparición de "hola" en la memoria, entonces,
... define
c
como un puntero a la cadena (solo lectura) "hola", y por lo tanto contiene el valor 63.c
debe almacenarse en algún lugar: en el ejemplo anterior en la ubicación 58. Por supuesto, no solo podemos señalar caracteres , pero también a otros punteros. P.ej:Ahora
cp
apunta ac
, es decir, contiene la dirección dec
(que es 58). Podemos ir aún más lejos. Considerar:Ahora
cpp
almacena la dirección decp
. Por lo tanto, tiene el valor 55 (basado en el ejemplo anterior), y lo adivinó: está almacenado en la dirección 60.En cuanto a por qué uno usa punteros a punteros:
t
, una referencia a la matriz tiene tipot *
. Ahora considere una matriz de matrices de tipot
: naturalmente, una referencia a esta matriz 2D tendrá type(t *)*
=t **
, y por lo tanto es un puntero a un puntero.char **
.f
necesitará aceptar un argumento de tipot **
para alterar una variable de tipot *
.fuente
¿Cómo funcionan los punteros a punteros en C?
Primero, un puntero es una variable, como cualquier otra variable, pero que contiene la dirección de una variable.
Un puntero a un puntero es una variable, como cualquier otra variable, pero que contiene la dirección de una variable. Esa variable resulta ser un puntero.
¿Cuándo los usarías?
Puede usarlos cuando necesite devolver un puntero a alguna memoria en el montón, pero no usar el valor de retorno.
Ejemplo:
Y lo llamas así:
También hay otros usos, como el argumento main () de cada programa en C tiene un puntero a un puntero para argv, donde cada elemento contiene una matriz de caracteres que son las opciones de línea de comando. Sin embargo, debe tener cuidado cuando utilice punteros de punteros para apuntar a matrices bidimensionales, es mejor utilizar un puntero a una matriz bidimensional.
¿Por qué es peligroso?
Aquí hay un ejemplo de un puntero a una matriz bidimensional hecho correctamente:
Sin embargo, no puede usar un puntero a una matriz bidimensional si desea admitir un número variable de elementos para las FILAS y COLUMNAS. Pero cuando sabes de antemano, usarías una matriz bidimensional.
fuente
Me gusta este ejemplo de código de "mundo real" de uso de puntero a puntero, en Git 2.0, confirme 7b1004b :
Chris señala en los comentarios al video de 2016 " El problema del doble puntero de Linus Torvalds " de Philip Buuck .
Kumar señala en los comentarios la publicación del blog " Linus sobre la comprensión de los punteros ", donde Grisha Trubetskoy explica:
fuente
Cuando cubrimos consejos en un curso de programación en la universidad, nos dieron dos pistas sobre cómo comenzar a aprender sobre ellos. El primero fue ver Pointer Fun With Binky . El segundo era pensar en los ojos de los eglefinos de A través del espejo de Lewis Carroll
fuente
Es posible que desee leer esto: Punteros a punteros
Espero que esto ayude a aclarar algunas dudas básicas.
fuente
Cuando se requiere una referencia a un puntero. Por ejemplo, cuando desea modificar el valor (dirección apuntada) de una variable de puntero declarada en el alcance de una función de llamada dentro de una función llamada.
Si pasa un solo puntero como argumento, modificará copias locales del puntero, no el puntero original en el alcance de la llamada. Con un puntero a un puntero, modifica el último.
fuente
Un puntero a un puntero también se llama un identificador . Un uso para esto es a menudo cuando un objeto se puede mover en la memoria o eliminar. A menudo, uno es responsable de bloquear y desbloquear el uso del objeto para que no se mueva al acceder.
A menudo se usa en entornos con memoria restringida, es decir, Palm OS.
fuente
Considere la figura y el programa a continuación para comprender mejor este concepto .
Según la figura, ptr1 es un puntero único que tiene una dirección de variable num .
Del mismo modo, ptr2 es un puntero a puntero (puntero doble) que tiene la dirección del puntero ptr1 .
Un puntero que apunta a otro puntero se conoce como doble puntero. En este ejemplo ptr2 es un puntero doble.
Valores del diagrama anterior:
Ejemplo:
Salida:
fuente
es un puntero al valor de la dirección del puntero. (eso es terrible, lo sé)
básicamente, le permite pasar un puntero al valor de la dirección de otro puntero, por lo que puede modificar dónde apunta otro puntero desde una subfunción, como:
fuente
Tienes una variable que contiene una dirección de algo. Eso es un puntero.
Luego tiene otra variable que contiene la dirección de la primera variable. Eso es un puntero a puntero.
fuente
Un puntero a puntero es, bueno, un puntero a puntero.
Un ejemplo significativo de someType ** es una matriz bidimensional: tiene una matriz, llena de punteros a otras matrices, así que cuando escribe
dpointer [5] [6]
accede a la matriz que contiene punteros a otras matrices en su quinta posición, obtiene el puntero (deje su nombre en fpointer) y luego acceda al sexto elemento de la matriz referenciado a esa matriz (fpointer [6]).
fuente
Cómo funciona: es una variable que puede almacenar otro puntero.
¿Cuándo los usaría? Muchos usos uno de ellos es si su función quiere construir una matriz y devolverla a la persona que llama.
fuente
Creé un video de 5 minutos que explica cómo funcionan los punteros:
https://www.youtube.com/watch?v=3X-ray3tDjQ
fuente
Hay muchas explicaciones útiles, pero no encontré solo una breve descripción, así que ...
Básicamente, el puntero es la dirección de la variable. Código de resumen corto:
También se puede encontrar información útil en el tema Qué significa referencia y desreferencia
Y no estoy tan seguro de cuándo pueden ser útiles los punteros, pero en común es necesario usarlos cuando se realiza una asignación de memoria manual / dinámica: malloc, calloc, etc.
Así que espero que también ayude a aclarar la problemática :)
fuente