Estoy estudiando un poco de C ++ y estoy luchando con punteros. Entiendo que puedo tener 3 niveles de punteros declarando:
int *(*x)[5];
por lo que *x
es un puntero a una matriz de 5 elementos que son punteros a int
. También lo sé 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]
?
x[0]
,x[0][0]
yx[0][0][0]
tienen diferentes tipos. No se pueden comparar. ¿Qué quieres decir con!=
?int **x[5]
es una matriz de 5 elementos. Un elemento es un puntero a puntero a int`int** x[5]
sería una matriz de cinco punteros que apuntan a punteros que apuntan a int.int *(*x)[5]
es un puntero a una matriz de cinco punteros que apuntan a int.x[0] != x[0][0] != x[0][0][0]
significa? Esta no es una comparación válida en C ++. Incluso si lo divide enx[0] != x[0][0]
yx[0][0] != x[0][0][0]
todavía no es válido. Entonces, ¿qué significa tu pregunta?Respuestas:
x
es un puntero a una matriz de 5 punteros aint
.x[0]
es una matriz de 5 punteros aint
.x[0][0]
es un puntero a unint
.x[0][0][0]
es unint
.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 tantox[0]
lo 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 unint
valor 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]
.fuente
0x100
debería aparecer inmediatamente a la izquierda del cuadro que contiene10
, de la misma manera que0x500
aparece a la izquierda de su cuadro. En lugar de estar muy a la izquierda y abajo.es, según tu propia publicación,
que se simplifica
¿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 tiene algún
int
valor.fuente
x[0][0]
es(x[0])[0]
, es decir*((*(x+0))+0)
, no*(x+0+0)
. La desreferencia ocurre antes del segundo[0]
.x[0][0] != *(x+0+0)
al igual quex[2][3] != x[3][2]
.Aquí está el diseño de memoria de su puntero:
x[0]
produce "dirección de matriz",x[0][0]
produce "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 los hacks señalan correctamente, 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:Puedes pasar
x[0]
alsizeof
operador. 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 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 lanzadox
un puntero de tipoint**
.Debido a la caída 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 llamadaprintf("%p", x[0])
imprimirá el contenido de las celdas de memoria etiquetadas como "dirección de la matriz".fuente
x[0]
No es la dirección de la matriz.x[0]
no es la dirección de la matriz, es la matriz misma. He agregado una explicación en profundidad de esto y de por qué escribí quex[0]
es la "dirección de la matriz". Espero que te guste.printf("%zu\n", sizeof x[0]);
informa el tamaño de la matriz, no el tamaño de un puntero.sizeof x[0]
...x[0]
elimina la referencia del puntero más externo ( puntero a la matriz de tamaño 5 del puntero a int) y da como resultado una matriz de tamaño 5 del 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 .
fuente
Vamos a considerar paso a paso expresiones
x[0]
,x[0][0]
yx[0][0][0]
.Como
x
se define de la siguiente maneraentonces expresión
x[0]
es una matriz de tipoint *[5]
. Tenga en cuenta que expresiónx[0]
es equivalente a 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ónLa expresión
x[0][0]
es equivalentey[0]
y tiene tipoint *
. Vamos a denotarlo como z, es decir, tenemos una declaraciónexpresión
x[0][0][0]
es equivalente a expresióny[0][0]
que a su vez es equivalente a expresiónz[0]
y tiene tipoint
.Entonces tenemos
x[0]
tiene tipoint *[5]
x[0][0]
tiene tipoint *
x[0][0][0]
tiene tipoint
Por lo tanto, son objetos de diferentes tipos y, por cierto, de diferentes tamaños.
Ejecutar por ejemplo
fuente
Lo primero que tengo que decir es que
De la siguiente imagen todas las cosas están claras.
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:
Salida
Programa 2:
Salida
fuente
x[0]
No contiene la dirección de la hormiga. Es una gran variedad. Se descompondrá al puntero a su primer elemento.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
fuente
x[0][0]
sería una sola caja llena de pedazos de papel que tienen las ubicaciones de las cajas de zapatos escritas en ellas?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:
que puede reescribirse como (para más claro):
Debido al principio, tenemos:
Por lo tanto:
Para que puedas descubrir la diferencia.
fuente
x[0]
es una matriz de 5 pulgadas, no un puntero. (puede decaer a un puntero en la mayoría de los contextos, pero la distinción es importante aquí).*(*x)[5]
es unint
, así(*x)[5]
es unint *
, entonces*x
is an(int *)[5]
, sox
is an*((int *)[5])
. Es decir,x
es un puntero a un conjunto de 5 punteros aint
.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
permitiría a las comparaciones que desea, ya que
y
,y[0]
,y[0][0]
,y[0][0][0]
tendría diferentes valores y tipos, pero la misma direcciónNo ocupa espacio contiguo.
x
yx [0]
tener la misma dirección, perox[0][0]
yx[0][0][0]
son cada uno en diferentes direccionesfuente
int *(*x)[5]
es diferente aint **x[5]
Ser
p
un puntero: está apilando las desreferencias conp[0][0]
, lo que es equivalente a*((*(p+0))+0)
.En la referencia C (&) y la notación de desreferencia (*):
Es equivalente a:
Mira eso, el & * puede ser refactorizado, simplemente eliminándolo:
fuente
p == p
.&(&p[0])[0]
es diferente ap[0][0]
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 se
x
señala a sí mismo.Esto es bastante fácil de construir, pero claramente un poco más difícil de entender. En el siguiente programa, 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 .
Esto se compila sin advertencias tanto en C89 como en C99, y el resultado es el siguiente:
Curiosamente, los tres valores son idénticos. ¡Pero esto no debería ser una sorpresa! Primero, analicemos el programa.
Declaramos
x
como un 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 quex
se refiere a una ubicación de memoria real. En la familia de lenguajes C, los contenidosx
son solo basura, algo que queda del uso anterior de la ubicación, porx
lo que 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 dex
, necesitamos hacer un reparto para que no recibamos advertencias.El modelo de memoria se vería así:
Por lo tanto, el bloque de memoria de 4 bytes en la dirección
0xbfd9198c
contiene el patrón de bits correspondiente al valor hexadecimal0xbfd9198c
. 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 el 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, aunque 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
x[0]
,x[0][0]
yx[0][0][0]
ser igual, pero podrían serlo si se lo obliga. Si algo te pasó por la cabeza, ¡avísame para que te lo aclare!fuente
x[0]
en realidad no representa un objeto válido del tipo correctox
es un puntero a una matriz, por lo que podemos usar el[]
operador para especificar un desplazamiento desde ese puntero y desreferenciarlo. ¿Qué hay de extraño allí? El resultado dex[0]
es una matriz, y C no se queja si imprime eso usando,%p
ya que así es como se implementa debajo de todos modos.-pedantic
bandera no produce advertencias, por lo que C está bien con los tipos ...El tipo de
int *(*x)[5]
es, esint* (*)[5]
decir, un puntero a una matriz de 5 punteros a ints.x
es la dirección de la primera matriz de 5 punteros a ints (una dirección con tipoint* (*)[5]
)x[0]
la dirección de la primera matriz de 5 punteros a ints (misma dirección con tipoint* [5]
) (dirección de desplazamiento x por0*sizeof(int* [5])
ej. índice * tamaño-de-tipo-señalado-y desreferenciación)x[0][0]
es el primer puntero a un int en la matriz (la misma dirección con tipoint*
) (dirección de desplazamiento x por0*sizeof(int* [5])
y desreferencia y luego por0*sizeof(int*)
y desreferencia)x[0][0][0]
es el primer int señalado por el puntero a un int (desplazamiento de la dirección x por0*sizeof(int* [5])
y desreferencia y desplazamiento de esa dirección por0*sizeof(int*)
y desreferenciación y desplazamiento de esa dirección por0*sizeof(int)
y desreferenciación)El tipo de
int *(*y)[5][5][5]
es, esint* (*)[5][5][5]
decir, un puntero a una matriz 3D de punteros de 5x5x5 a intsx
es la dirección de la primera matriz 3D de punteros de 5x5x5 a entradas con tipoint*(*)[5][5][5]
x[0]
es la dirección de la primera matriz 3D de punteros de 5x5x5 a ints (dirección de desplazamiento x por0*sizeof(int* [5][5][5])
y desreferencia)x[0][0]
es la dirección de la primera matriz 2d de punteros 5x5 a ints (compensa la dirección x por0*sizeof(int* [5][5][5])
y desreferencia luego compensa esa dirección por0*sizeof(int* [5][5])
)x[0][0][0]
es la dirección de la primera matriz de 5 punteros a ints (compensación de dirección x por0*sizeof(int* [5][5][5])
y desreferenciación y compensación de esa dirección por0*sizeof(int* [5][5])
y compensación de esa dirección por0*sizeof(int* [5])
)x[0][0][0][0]
es el primer puntero a un int en la matriz (desplazar la dirección x por0*sizeof(int* [5][5][5])
y desreferenciar y compensar esa dirección por0*sizeof(int* [5][5])
y compensar esa dirección por0*sizeof(int* [5])
y compensar esa dirección por0*sizeof(int*)
y desreferenciar)x[0][0][0][0][0]
es el primer int señalado por el puntero a un int (desplazar dirección x por0*sizeof(int* [5][5][5])
y desreferenciar y compensar esa dirección por0*sizeof(int* [5][5])
y compensar esa dirección por0*sizeof(int* [5])
y compensar esa dirección por0*sizeof(int*)
y desreferenciar y compensar esa dirección por0*sizeof(int)
y desreferenciar)En cuanto a la descomposición de la matriz:
Esto es equivalente a pasar
int* x[][5][5]
o,int* (*x)[5][5]
es decir, todos decaen a este último. Es por eso que no recibirá una advertencia del compilador para usarx[6][0][0]
en la función, pero lo haráx[0][6][0]
porque esa información de tamaño se conservax[0]
es la dirección de la primera matriz 3D de punteros de 5x5x5 a intsx[0][0]
es la dirección de la primera matriz 2d de punteros 5x5 a intsx[0][0][0]
es la dirección de la primera matriz de 5 punteros a intsx[0][0][0][0]
es el primer puntero a un int en la matrizx[0][0][0][0][0]
es el primer int señalado por el puntero a un intEn el último ejemplo, es semánticamente mucho más claro de usar en
*(*x)[0][0][0]
lugar dex[0][0][0][0][0]
, esto se debe a que el primero y el último[0]
aquí se interpretan como una desreferencia de puntero en lugar de un índice en una matriz multidimensional, debido al tipo. Sin embargo, son idénticos porque(*x) == x[0]
independientemente de la semántica. También podría usar*****x
, lo que parecería que está desreferenciando el puntero 5 veces, pero en realidad se interpreta exactamente igual: un desplazamiento, una desreferencia, una desreferencia, 2 desplazamientos en una matriz y una desreferencia, simplemente por el tipo está aplicando la operación a.Esencialmente cuando usted
[0]
o*
un*
tipo de matriz no, es un desplazamiento y una desreferencia debido al orden de precedencia de*(a + 0)
.Cuando
[0]
o*
una*
a un tipo de matriz, entonces es un desplazamiento a continuación, eliminar la referencia de un idempotente (al eliminar la referencia se resuelve por el compilador para producir la misma dirección - es una operación idempotente).Cuando usted
[0]
o*
un tipo con un tipo de matriz 1d es un desplazamiento y luego una desreferenciaSi usted
[0]
o**
un tipo de matriz 2D es solo un desplazamiento, es decir, un desplazamiento y luego una desreferencia idempotente.Si usted
[0][0][0]
o***
un tipo de matriz 3D es una desviación de compensación + idempotente, luego una desviación de compensación + idempotente, luego una desviación de compensación + idempotente y luego una desreferencia. La verdadera desreferencia solo ocurre cuando el tipo de matriz está completamente despojado.Para el ejemplo del
int* (*x)[1][2][3]
tipo se desenvuelve en orden.x
tiene un tipoint* (*)[1][2][3]
*x
tiene un tipoint* [1][2][3]
(offset 0 + desreferencia idempotente)**x
tiene un tipoint* [2][3]
(offset 0 + desreferencia idempotente)***x
tiene un tipoint* [3]
(offset 0 + desreferencia idempotente)****x
tiene un tipoint*
(desplazamiento 0 + desreferencia)*****x
tiene tipoint
(desplazamiento 0 + desreferencia)fuente