array - pointers c++
¿Por qué es x[0]!=X[0][0]!=X[0][0][0]? (11)
Aquí está el diseño de memoria de su puntero:
+------------------+
x: | address of array |
+------------------+
|
V
+-----------+-----------+-----------+-----------+-----------+
| pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
+-----------+-----------+-----------+-----------+-----------+
|
V
+--------------+
| some integer |
+--------------+
x[0]
produce "dirección de la matriz",
x[0][0]
produce el "puntero 0",
x[0][0][0]
produce "algún número entero".
Creo que debería ser obvio ahora, por qué todos son diferentes.
Lo anterior es lo suficientemente cercano para la comprensión básica, por eso lo escribí de la manera en que lo escribí. Sin embargo, como señala acertadamente los hacks, la primera línea no es 100% precisa. Así que aquí vienen todos los detalles finos:
A partir de la definición del lenguaje C, el valor de
x[0]
es la matriz completa de punteros enteros.
Sin embargo, las matrices son algo con lo que realmente no puedes hacer nada en C. Siempre manipulas su dirección o sus elementos, nunca la matriz completa en su conjunto:
-
Puede pasar
x[0]
al operadorsizeof
. Pero eso no es realmente un uso del valor, su resultado depende solo del tipo. -
Puede tomar su dirección que produce el valor de
x
, es decir, "dirección de la matriz" con el tipoint*(*)[5]
. En otras palabras:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
-
En todos los demás contextos , el valor de
x[0]
decaerá en un puntero al primer elemento de la matriz. Es decir, un puntero con el valor "dirección de matriz" y el tipoint**
. El efecto es el mismo que si hubiera convertidox
en un puntero de tipoint**
.
Debido a la disminución de la matriz de punteros en el caso 3., todos los usos de
x[0]
finalmente resultan en un puntero que señala el comienzo de la matriz de punteros;
la llamada
printf("%p", x[0])
imprimirá el contenido de las celdas de memoria etiquetadas como "dirección de la matriz".
Estoy estudiando un poco de C ++ y estoy luchando con punteros. Entiendo que puedo tener 3 niveles de punteros declarando:
int *(*x)[5];
de modo que
*x
es un puntero a una matriz de 5 elementos que son punteros a
int
.
También sé que
x[0] = *(x+0);
,
x[1] = *(x+1)
y así sucesivamente ...
Entonces, dada la declaración anterior, ¿por qué
x[0] != x[0][0] != x[0][0][0]
?
Consideremos las expresiones paso a paso
x[0]
,
x[0][0]
x[0][0][0]
.
Como
x
se define de la siguiente manera
int *(*x)[5];
entonces la expresión
x[0]
es una matriz de tipo
int *[5]
.
Tenga en cuenta que la expresión
x[0]
es equivalente a la expresión
*x
.
Eso es desreferenciar un puntero a una matriz, obtenemos la matriz en sí.
Vamos a denotarlo como si es que tenemos una declaración
int * y[5];
La expresión
x[0][0]
es equivalente a
y[0]
y tiene el tipo
int *
.
Vamos a denotarlo como z, es decir, tenemos una declaración
int *z;
la expresión
x[0][0][0]
es equivalente a la expresión
y[0][0]
que a su vez es equivalente a la expresión
z[0]
y tiene el tipo
int
.
Entonces tenemos
x[0]
tiene el tipo
int *[5]
x[0][0]
tiene el tipo
int *
x[0][0][0]
tiene el tipo
int
Por lo tanto, son objetos de diferentes tipos y, por cierto, de diferentes tamaños.
Ejecutar por ejemplo
std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;
Estás intentando comparar diferentes tipos por valor
Si toma las direcciones, puede obtener más de lo que espera
Tenga en cuenta que su declaración hace la diferencia
int y [5][5][5];
permitiría las comparaciones que desee, ya que
y
,
y[0]
,
y[0][0]
,
y[0][0][0]
tendrían diferentes valores y tipos pero la misma dirección
int **x[5];
No ocupa espacio contiguo.
x
x [0]
tienen la misma dirección, pero
x[0][0]
x[0][0][0]
están en direcciones diferentes
Hay un principio en C ++ para que: una declaración de una variable indique exactamente la forma de usar la variable. Considera tu declaración:
int *(*x)[5];
que puede reescribirse como (para más claro):
int *((*x)[5]);
Debido al principio, tenemos:
*((*x)[i]) is treated as an int value (i = 0..4)
→ (*x)[i] is treated as an int* pointer (i = 0..4)
→ *x is treated as an int** pointer
→ x is treated as an int*** pointer
Por lo tanto:
x[0] is an int** pointer
→ x[0][0] = (x[0]) [0] is an int* pointer
→ x[0][0][0] = (x[0][0]) [0] is an int value
Para que puedas descubrir la diferencia.
Las otras respuestas son correctas, pero ninguna de ellas enfatiza la idea de que es posible que las tres contengan el mismo valor , por lo que están de alguna manera incompletas.
La razón por la que esto no se puede entender a partir de las otras respuestas es que todas las ilustraciones, si bien son útiles y definitivamente razonables en la mayoría de las circunstancias, no cubren la situación en la que el puntero
x
apunta a sí mismo.
Esto es bastante fácil de construir, pero claramente un poco más difícil de entender. En el programa a continuación, veremos cómo podemos forzar que los tres valores sean idénticos.
NOTA: El comportamiento en este programa no está definido, pero lo estoy publicando aquí simplemente como una demostración interesante de algo que los punteros pueden hacer, pero no deberían .
#include <stdio.h>
int main () {
int *(*x)[5];
x = (int *(*)[5]) &x;
printf("%p/n", x[0]);
printf("%p/n", x[0][0]);
printf("%p/n", x[0][0][0]);
}
Esto se compila sin advertencias tanto en C89 como en C99, y el resultado es el siguiente:
$ ./ptrs
0xbfd9198c
0xbfd9198c
0xbfd9198c
Curiosamente, los tres valores son idénticos. ¡Pero esto no debería ser una sorpresa! Primero, analicemos el programa.
Declaramos
x
como puntero a una matriz de 5 elementos donde cada elemento es de tipo puntero a int.
Esta declaración asigna 4 bytes en la pila de tiempo de ejecución (o más dependiendo de su implementación; en mi máquina, los punteros son 4 bytes), por lo que
x
se refiere a una ubicación de memoria real.
En la familia de lenguajes C, el contenido de
x
es simplemente basura, algo que queda del uso anterior de la ubicación, por lo que
x
no apunta a ninguna parte, ciertamente no al espacio asignado.
Entonces, naturalmente, podemos tomar la dirección de la variable
x
y ponerla en algún lugar, así que eso es exactamente lo que hacemos.
Pero seguiremos adelante y lo pondremos en x.
Dado que
&x
tiene un tipo diferente que
x
, necesitamos hacer un lanzamiento para que no recibamos advertencias.
El modelo de memoria se vería así:
0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+
Por lo tanto, el bloque de memoria de 4 bytes en la dirección
0xbfd9198c
contiene el patrón de bits correspondiente al valor hexadecimal
0xbfd9198c
.
Suficientemente simple.
A continuación, imprimimos los tres valores. Las otras respuestas explican a qué se refiere cada expresión, por lo que la relación debería ser clara ahora.
Podemos ver que los valores son los mismos, pero solo en un sentido de nivel muy bajo ... sus patrones de bits son idénticos, pero los datos de tipo asociados con cada expresión significan que sus valores interpretados son diferentes.
Por ejemplo, si imprimimos
x[0][0][0]
usando la cadena de formato
%d
, obtendríamos un número negativo enorme, por lo que los "valores" son, en la práctica, diferentes, pero el patrón de bits es lo mismo.
Esto es realmente simple ... en los diagramas, las flechas solo apuntan a la misma dirección de memoria en lugar de a diferentes direcciones. Sin embargo, si bien pudimos forzar un resultado esperado de un comportamiento indefinido, es solo eso: indefinido. Este no es un código de producción, sino simplemente una demostración en aras de la integridad.
En una situación razonable, usará
malloc
para crear la matriz de 5 punteros int y nuevamente para crear las entradas que se apuntan en esa matriz.
malloc
siempre devuelve una dirección única (a menos que no tenga memoria, en cuyo caso devuelve NULL o 0), por lo que nunca tendrá que preocuparse por punteros autorreferenciales como este.
Esperemos que esa sea la respuesta completa que estás buscando.
No debe esperar que
x[0]
,
x[0][0]
x[0][0][0]
sean iguales, pero podrían serlo si se lo obliga.
Si algo te pasó por la cabeza, ¡házmelo saber para que pueda aclarar!
Lo primero que tengo que decir es que
x [0] = * (x + 0) = * x;
x [0] [0] = * (* (x + 0) + 0) = * * x;
x [0] [0] [0] = * (* (* (x + 0) + 0)) = * * * x;
Entonces * x ≠ * * x ≠ * * * x
De la siguiente imagen, todo está claro.
x[0][0][0]= 2000
x[0][0] = 1001
x[0] = 10
Es solo un ejemplo, donde el valor de x [0] [0] [0] = 10
y la dirección de x [0] [0] [0] es 1001
esa dirección se almacena en x [0] [0] = 1001
y la dirección de x [0] [0] es 2000
y esa dirección se almacena en x [0] = 2000
Entonces x [0] [0] [0] ≠ x [0] [0] ≠ x [0]
.
EDICIONES
Programa 1:
{
int ***x;
x=(int***)malloc(sizeof(int***));
*x=(int**)malloc(sizeof(int**));
**x=(int*)malloc(sizeof(int*));
***x=10;
printf("%d %d %d %d/n",x,*x,**x,***x);
printf("%d %d %d %d %d",x[0][0][0],x[0][0],x[0],x,&x);
}
Salida
142041096 142041112 142041128 10
10 142041128 142041112 142041096 -1076392836
Programa 2:
{
int x[1][1][1]={10};
printf("%d %d %d %d /n ",x[0][0][0],x[0][0],x[0],&x);
}
Salida
10 -1074058436 -1074058436 -1074058436
Si tuviera que ver las matrices desde una perspectiva del mundo real, aparecería así:
x[0]
es un contenedor de carga lleno de cajas.
x[0][0]
es una caja individual, llena de cajas de zapatos, dentro del contenedor de carga.
x[0][0][0]
es una única caja de zapatos dentro de la caja, dentro del contenedor de carga.
Incluso si fuera la única caja de zapatos en la única caja en el contenedor de carga, sigue siendo una caja de zapatos y no un contenedor de carga
Siendo
p
un puntero: está apilando desreferencias con
p[0][0]
, que es equivalente a
*((*(p+0))+0)
.
En la referencia C (&) y la notación de desreferencia (*):
p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])
Es equivalente a:
p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0
Mire eso, el & * se puede refactorizar, simplemente eliminándolo:
p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)
x
es un puntero a una matriz de 5 punteros a
int
.
x[0]
es una matriz
de 5 punteros a
int
.
x[0][0]
es un puntero a un
int
.
x[0][0][0]
es un
int
.
x[0]
Pointer to array +------+ x[0][0][0]
x -----------------> | | Pointer to int +-------+
0x500 | 0x100| x[0][0]----------------> 0x100 | 10 |
x is a pointer to | | +-------+
an array of 5 +------+
pointers to int | | Pointer to int
0x504 | 0x222| x[0][1]----------------> 0x222
| |
+------+
| | Pointer to int
0x508 | 0x001| x[0][2]----------------> 0x001
| |
+------+
| | Pointer to int
0x50C | 0x123| x[0][3]----------------> 0x123
| |
+------+
| | Pointer to int
0x510 | 0x000| x[0][4]----------------> 0x000
| |
+------+
Puedes ver eso
-
x[0]
es una matriz y se convertirá en puntero a su primer elemento cuando se use en una expresión (con algunas excepciones). Por lo tanto,x[0]
dará la dirección de su primer elementox[0][0]
que es0x500
. -
x[0][0]
contiene la dirección de unint
que es0x100
. -
x[0][0][0]
contiene un valorint
de10
.
Entonces,
x[0]
es igual a
&x[0][0]
y, por lo tanto,
&x[0][0] != x[0][0]
.
Por lo tanto,
x[0] != x[0][0] != x[0][0][0]
.
x[0] != x[0][0] != x[0][0][0]
es, según tu propia publicación,
*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`
que se simplifica
*x != **x != ***x
¿Por qué debería ser igual?
El primero es la dirección de algún puntero.
El segundo es la dirección de otro puntero.
Y el tercero es un valor
int
.
-
x[0]
desreferencia el puntero más externo ( puntero a matriz de tamaño 5 de puntero a int) y da como resultado una matriz de tamaño 5 de puntero aint
; -
x[0][0]
desreferencia el puntero más externo e indexa la matriz, lo que resulta en un puntero aint
; -
x[0][0][0]
desreferencia todo, lo que resulta en un valor concreto.
Por cierto, si alguna vez te sientes confundido por el significado de este tipo de declaraciones, usa cdecl .