¿Cómo funcionan los punteros a punteros en C? (¿y cuándo podrías usarlos?)
¿Cómo funcionan los punteros a punteros en C?
¿Cuándo podrías usarlos?
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 de arriba son las direcciones):
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| | 58 | | | 63 | | 55 | | | h | e | l | l | o | \0 | |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
Lo que puede 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,
const char *c = "hello";
... se define c
como un puntero a la cadena (de 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 indicadores. P.ej:
const char **cp = &c;
Ahora cp
apunta a c
, es decir, contiene la dirección de c
(que es 58). Podemos ir aún más lejos. Considerar:
const char ***cpp = &cp;
Ahora cpp
almacena la dirección de cp
. Entonces tiene el valor 55 (basado en el ejemplo anterior), y lo has adivinado: está almacenado en la dirección 60.
En cuanto a por qué uno usa punteros a punteros:
- El nombre de una matriz normalmente produce la dirección de su primer elemento. Entonces, si la matriz contiene elementos de tipo
t
, una referencia a la matriz tiene tipot *
. Ahora considere una matriz de matrices de tipot
: naturalmente, una referencia a esta matriz 2D tendrá tipo(t *)*
=t **
y, por lo tanto, es un puntero a un puntero. - Aunque un conjunto de cadenas parezca unidimensional, en realidad es bidimensional, ya que las cadenas son conjuntos de caracteres. Por eso:
char **
. - Una función
f
necesitará aceptar un argumento de tipot **
si va a alterar una variable de tipot *
. - Hay muchas otras razones que son demasiado numerosas para enumerarlas aquí.
¿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 sin usar el valor de retorno.
Ejemplo:
int getValueOf5(int *p)
{
*p = 5;
return 1;//success
}
int get1024HeapMemory(int **p)
{
*p = malloc(1024);
if(*p == 0)
return -1;//error
else
return 0;//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 5
int *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 que el argumento main() de cada programa C tiene un puntero a un puntero para argv, donde cada elemento contiene una matriz de caracteres que son las opciones de la línea de comando. Sin embargo, debe tener cuidado cuando use punteros de punteros para apuntar a matrices bidimensionales, es mejor usar un puntero a una matriz bidimensional.
¿Por qué es peligroso?
void test()
{
double **a;
int i1 = sizeof(a[0]);//i1 == 4 == sizeof(double*)
double matrix[ROWS][COLUMNS];
int i2 = sizeof(matrix[0]);//i2 == 240 == COLUMNS * sizeof(double)
}
A continuación se muestra un ejemplo de un puntero a una matriz bidimensional realizado 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 sepa de antemano que usaría una matriz bidimensional.