Con las matrices, ¿por qué ocurre que a [5] == 5 [a]?


1622

Como señala Joel en el podcast Stack Overflow # 34 , en C Programming Language (también conocido como: K & R), se menciona esta propiedad de los arrays en C:a[5] == 5[a]

Joel dice que se debe a la aritmética del puntero, pero aún no lo entiendo. ¿Por quéa[5] == 5[a] ?


48
¿Algo como un [+] también funcionaría como * (a ++) O * (++ a)?
Egon

45
@Egon: Eso es muy creativo, pero desafortunadamente no es así como funcionan los compiladores. El compilador interpreta a[1]como una serie de tokens, no cadenas: * ({ubicación entera de} a {operador} + {entero} 1) es lo mismo que * ({entero} 1 {operador} + {ubicación entera de} a) pero no es lo mismo que * ({ubicación entera de} un {operador} + {operador} +)
Dinah

11
Una variación compuesta interesante de esto se ilustra en el acceso a la matriz Illogical , donde tiene char bar[]; int foo[];y foo[i][bar]se usa como una expresión.
Jonathan Leffler

55
@EldritchConundrum, ¿por qué crees que 'el compilador no puede verificar que la parte izquierda sea un puntero'? Sí puede. Es cierto que a[b]= *(a + b)para cualquier ay b, pero fue la libre elección de los diseñadores de lenguaje para +ser definidos conmutativos para todos los tipos. Nada podría impedirles prohibir i + pmientras lo permiten p + i.
ach

13
@Andrey One generalmente espera +ser conmutativo, por lo que tal vez el verdadero problema sea elegir hacer que las operaciones de puntero se parezcan a la aritmética, en lugar de diseñar un operador de desplazamiento separado.
Eldritch Conundrum

Respuestas:


1926

El estándar C define al []operador de la siguiente manera:

a[b] == *(a + b)

Por a[5]lo tanto evaluará a:

*(a + 5)

y 5[a]evaluará a:

*(5 + a)

aes un puntero al primer elemento de la matriz. a[5]es el valor que está 5 elementos más lejos a, que es el mismo *(a + 5), y de las matemáticas de la escuela primaria sabemos que son iguales (la suma es conmutativa ).


325
Me pregunto si no es más como * ((5 * sizeof (a)) + a). Gran explicación sin embargo.
John MacIntyre

92
@Dinah: Desde la perspectiva del compilador C, tienes razón. No se necesita sizeof y esas expresiones que mencioné son LO MISMO. Sin embargo, el compilador tendrá en cuenta sizeof cuando produzca código de máquina. Si a es una matriz int, a[5]se compilará a algo así en mov eax, [ebx+20]lugar de[ebx+5]
Mehrdad Afshari

12
@Dinah: A es una dirección, digamos 0x1230. Si a estaba en una matriz int de 32 bits, entonces un [0] está en 0x1230, un [1] está en 0x1234, un [2] en 0x1238 ... a [5] en x1244, etc. Si solo sumamos 5 a 0x1230, obtenemos 0x1235, que está mal.
James Curran

36
@ sr105: Ese es un caso especial para el operador +, donde uno de los operandos es un puntero y el otro un entero. El estándar dice que el resultado será del tipo del puntero. El compilador / tiene que ser / lo suficientemente inteligente.
aib

48
"de las matemáticas de la escuela primaria sabemos que son iguales". Entiendo que estás simplificando, pero estoy con aquellos que sienten que esto se ha simplificado demasiado . No es elemental eso *(10 + (int *)13) != *((int *)10 + 13). En otras palabras, hay más cosas aquí que la aritmética de la escuela primaria. La conmutatividad se basa críticamente en el compilador que reconoce qué operando es un puntero (y a qué tamaño de objeto). Para decirlo de otra manera,(1 apple + 2 oranges) = (2 oranges + 1 apple) , pero (1 apple + 2 oranges) != (1 orange + 2 apples).
LarsH

288

Porque el acceso a la matriz se define en términos de punteros. a[i]se define como media *(a + i), que es conmutativa.


42
Las matrices no están definidas en términos de punteros, pero el acceso a ellas sí.
Carreras de ligereza en órbita

55
Yo agregaría "entonces es igual a *(i + a), que se puede escribir como i[a]".
Jim Balter

44
Le sugiero que incluya la cita del estándar, que es la siguiente: 6.5.2.1: 2 Una expresión postfix seguida de una expresión entre corchetes [] es una designación subíndice de un elemento de un objeto de matriz. La definición del operador de subíndice [] es que E1 [E2] es idéntico a (* ((E1) + (E2))). Debido a las reglas de conversión que se aplican al operador binario +, si E1 es un objeto de matriz (equivalente, un puntero al elemento inicial de un objeto de matriz) y E2 es un número entero, E1 [E2] designa el elemento E2-ésimo de E1 (contando desde cero).
Vality

Para ser más correctos: las matrices decaen en punteros cuando accedes a ellas.
12431234123412341234123

Nitpick: No tiene sentido decir que " *(a + i)es conmutativo". Sin embargo, *(a + i) = *(i + a) = i[a]porque la suma es conmutativa.
Andreas Rejbrand

231

Creo que las otras respuestas se están perdiendo algo.

Sí, p[i]es por definición equivalente a *(p+i), que (porque la suma es conmutativa) es equivalente a *(i+p), que (de nuevo, por definición del []operador) es equivalente a i[p].

(Y en array[i], el nombre de la matriz se convierte implícitamente en un puntero al primer elemento de la matriz).

Pero la conmutatividad de la suma no es tan obvia en este caso.

Cuando ambos operandos son del mismo tipo, o incluso de diferentes tipos numéricos que se promueven a un tipo común, la conmutatividad tiene mucho sentido: x + y == y + x .

Pero en este caso estamos hablando específicamente de la aritmética de punteros, donde un operando es un puntero y el otro es un número entero. (Entero + entero es una operación diferente, y puntero + puntero no tiene sentido).

La descripción del +operador del estándar C ( N1570 6.5.6) dice:

Además, ambos operandos tendrán un tipo aritmético o un operando será un puntero a un tipo de objeto completo y el otro tendrá un tipo entero.

Podría haber dicho con la misma facilidad:

Además, ambos operandos tendrán un tipo aritmético o el operando izquierdo será un puntero a un tipo de objeto completo y el operando derecho tendrá un tipo entero.

en cuyo caso ambos i + pyi[p] sería ilegal.

En términos de C ++, realmente tenemos dos conjuntos de +operadores sobrecargados , que pueden describirse libremente como:

pointer operator+(pointer p, integer i);

y

pointer operator+(integer i, pointer p);

de los cuales solo el primero es realmente necesario.

Entonces, ¿por qué es así?

C ++ heredó esta definición de C, que la obtuvo de B (la conmutatividad de la indexación de matrices se menciona explícitamente en la Referencia de usuarios de 1972 a B ), que la obtuvo de BCPL (manual fechado en 1967), que bien podría haberla obtenido incluso idiomas anteriores (CPL? Algol?).

Entonces, la idea de que la indexación de matrices se define en términos de suma, y ​​esa suma, incluso de un puntero y un entero, es conmutativa, se remonta muchas décadas atrás, a los lenguajes ancestrales de C.

Esos lenguajes fueron mucho menos fuertemente tipados que el C moderno. En particular, a menudo se ignoraba la distinción entre punteros y enteros. (Los primeros programadores de C a veces usaban punteros como enteros sin signo, antes deunsigned palabra clave se agregara al lenguaje). Por lo tanto, la idea de hacer que la suma no sea conmutativa porque los operandos son de diferentes tipos probablemente no se les habría ocurrido a los diseñadores de esos idiomas. Si un usuario quería agregar dos "cosas", ya sea que esas "cosas" sean números enteros, punteros u otra cosa, no dependía del idioma evitarlo.

Y a lo largo de los años, cualquier cambio en esa regla habría roto el código existente (aunque el estándar ANSI C de 1989 podría haber sido una buena oportunidad).

Cambiar C y / o C ++ para requerir colocar el puntero a la izquierda y el entero a la derecha podría romper algún código existente, pero no habría pérdida de poder expresivo real.

Así que ahora tenemos arr[3]y 3[arr]significamos exactamente lo mismo, aunque la última forma nunca debería aparecer fuera del IOCCC .


12
Fantástica descripción de esta propiedad. Desde una vista de alto nivel, creo que 3[arr]es un artefacto interesante, pero rara vez debería usarse. La respuesta aceptada a esta pregunta (< stackoverflow.com/q/1390365/356> ) que pregunté hace un tiempo ha cambiado la forma en que pensaba acerca de la sintaxis. Aunque a menudo técnicamente no hay una forma correcta o incorrecta de hacer estas cosas, este tipo de características lo hacen pensar de manera separada de los detalles de implementación. Hay un beneficio en esta forma diferente de pensar que se pierde en parte cuando te fijas en los detalles de implementación.
Dinah

3
La suma es conmutativa. Para el estándar C definirlo de otra manera sería extraño. Es por eso que no podría decirse con la misma facilidad: "Además, ambos operandos tendrán un tipo aritmético o el operando izquierdo será un puntero a un tipo de objeto completo y el operando derecho tendrá un tipo entero". - Eso no tendría sentido para la mayoría de las personas que agregan cosas.
iheanyi

99
@iheanyi: la suma suele ser conmutativa, y generalmente requiere dos operandos del mismo tipo. La adición de puntero le permite agregar un puntero y un número entero, pero no dos punteros. En mi humilde opinión, ya es un caso especial suficientemente extraño que requerir que el puntero sea el operando izquierdo no sería una carga significativa. (Algunos idiomas usan "+" para la concatenación de cadenas; eso ciertamente no es conmutativo.)
Keith Thompson

3
@ Supercat, eso es aún peor. Eso significaría que a veces x + 1! = 1 + x. Eso violaría por completo la propiedad asociativa de la suma.
iheanyi

3
@iheanyi: Creo que querías decir propiedad conmutativa; la adición ya no es asociativa, ya que en la mayoría de las implementaciones (1LL + 1U) -2! = 1LL + (1U-2). De hecho, el cambio haría que algunas situaciones sean asociativas que actualmente no lo son, por ejemplo, 3U + (UINT_MAX-2L) sería igual a (3U + UINT_MAX) -2. Sin embargo, lo mejor sería que el lenguaje agregue nuevos tipos distintos para enteros promocionables y anillos algebraicos "envolventes", de modo que agregar 2 a un valor ring16_tque contenga 65535 produciría un ring16_tvalor 1, independiente del tamaño deint .
supercat

196

Y por supuesto

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

La razón principal de esto fue que en los años 70, cuando se diseñó C, las computadoras no tenían mucha memoria (64KB era mucho), por lo que el compilador de C no hizo mucha verificación de sintaxis. Por lo tanto, " X[Y]" se tradujo ciegamente en " *(X+Y)"

Esto también explica las sintaxis " +=" y " ++". Todo en la forma " A = B + C" tenía la misma forma compilada. Pero, si B era el mismo objeto que A, entonces estaba disponible una optimización de nivel de ensamblaje. Pero el compilador no era lo suficientemente brillante como para reconocerlo, por lo que el desarrollador tuvo que ( A += C). De manera similar, si Cfuera así 1, estaba disponible una optimización de nivel de ensamblaje diferente, y nuevamente el desarrollador tuvo que hacerlo explícito, porque el compilador no lo reconoció. (Los compiladores más recientes lo hacen, por lo que esas sintaxis son en gran medida innecesarias en estos días)


127
En realidad, eso se evalúa como falso; el primer término "ABCD" [2] == 2 ["ABCD"] se evalúa como verdadero, o 1, y 1! = 'C': D
Jonathan Leffler

8
@ Jonathan: la misma ambigüedad condujo a la edición del título original de esta publicación. ¿Somos las marcas iguales de equivalencia matemática, sintaxis de código o pseudocódigo? Argumento equivalencia matemática, pero como estamos hablando de código, no podemos escapar de que estamos viendo todo en términos de sintaxis de código.
Dinah

19
¿No es esto un mito? ¿Quiero decir que los operadores + = y ++ fueron creados para simplificar para el compilador? Algunos códigos se aclaran con ellos, y es útil tener una sintaxis, sin importar lo que haga el compilador.
Thomas Padron-McCarthy

66
+ = y ++ tiene otro beneficio significativo. Si el lado izquierdo cambia alguna variable durante la evaluación, el cambio solo se realizará una vez. a = a + ...; Lo haré dos veces.
Johannes Schaub - litb

8
No - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Dereferencing una cadena le da un char, no una subcadena
MSalters

55

Una cosa que nadie parece haber mencionado sobre el problema de Dinah con sizeof :

Solo puede agregar un entero a un puntero, no puede agregar dos punteros juntos. De esa manera, al agregar un puntero a un entero, o un entero a un puntero, el compilador siempre sabe qué bit tiene un tamaño que debe tenerse en cuenta.


1
Hay una conversación bastante exhaustiva sobre esto en los comentarios de la respuesta aceptada. Hice referencia a dicha conversación en la edición a la pregunta original, pero no abordé directamente su preocupación muy válida de sizeof. No estoy seguro de cómo hacer esto mejor en SO. ¿Debo hacer otra edición al origen? ¿pregunta?
Dinah

49

Para responder la pregunta literalmente. No siempre es cierto quex == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

huellas dactilares

false

27
En realidad, un "nan" no es igual a sí mismo: cout << (a[5] == a[5] ? "true" : "false") << endl;es false.
Verdaderamente el

99
@TrueY: Dijo eso específicamente para el caso NaN (y específicamente eso x == xno siempre es cierto). Creo que esa era su intención. Entonces él es técnicamente correcto (y posiblemente, como dicen, ¡el mejor tipo de correcto!).
Tim Čas

44
La pregunta es sobre C, su código no es código C. También hay una NANen la <math.h>que es mejor que 0.0/0.0, debido a 0.0/0.0que es UB cuando __STDC_IEC_559__no está definido (La mayoría de las implementaciones no definen __STDC_IEC_559__, pero en la mayoría de las implementaciones 0.0/0.0seguirá funcionando)
12431234123412341234123

26

Me acabo de enterar de que esta sintaxis fea podría ser "útil", o al menos muy divertida de jugar cuando se quiere lidiar con una matriz de índices que se refieren a posiciones en la misma matriz. ¡Puede reemplazar los corchetes anidados y hacer que el código sea más legible!

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Por supuesto, estoy bastante seguro de que no hay un caso de uso para eso en el código real, pero lo encontré interesante de todos modos :)


Cuando vea i[a][a][a]que piensa que soy un puntero a una matriz o una matriz de un puntero a una matriz o una matriz ... y aes un índice. Cuando vea a[a[a[i]]], cree que a es un puntero a una matriz o una matriz y ies un índice.
12431234123412341234123

1
¡Guauu! Es muy bueno el uso de esta función "estúpida". Podría ser útil en el concurso algorítmico en algunos problemas))
Serge Breusov

26

Buena pregunta / respuestas.

Solo quiero señalar que los punteros y las matrices C no son lo mismo , aunque en este caso la diferencia no es esencial.

Considere las siguientes declaraciones:

int a[10];
int* p = a;

En a.out, el símbolo aestá en una dirección que es el comienzo de la matriz, y el símbolo pestá en una dirección donde se almacena un puntero, y el valor del puntero en esa ubicación de memoria es el comienzo de la matriz.


2
No, técnicamente no son lo mismo. Si define alguna b como int * const y hace que apunte a una matriz, sigue siendo un puntero, lo que significa que en la tabla de símbolos, b se refiere a una ubicación de memoria que almacena una dirección, que a su vez señala dónde está la matriz .
PolyThinker

44
Muy buen punto. Recuerdo que tuve un error muy desagradable cuando definí un símbolo global como char s [100] en un módulo, declararlo como extern char * s; En otro módulo. Después de vincularlo todo, el programa se comportó de manera muy extraña. Porque el módulo que usaba la declaración externa estaba usando los bytes iniciales de la matriz como puntero a char.
Giorgio

1
Originalmente, en el abuelo BCPL de C, una matriz era un puntero. Es decir, lo que obtuviste cuando escribiste (he transcrito a C) int a[10]fue un puntero llamado 'a', que apuntaba a suficiente almacenamiento para 10 enteros, en otro lugar. Por lo tanto, a + i y j + i tenían la misma forma: agregar el contenido de un par de ubicaciones de memoria. De hecho, creo que BCPL no tenía tipo, por lo que eran idénticos. Y la escala de tamaño de tipo no se aplicaba, ya que BCPL estaba puramente orientado a palabras (en máquinas con direcciones de palabras también).
Dave

Creo que la mejor manera de entender la diferencia es comparar int*p = a;a int b = 5; En este último, "b" y "5" son ambos enteros, pero "B" es una variable, mientras que "5" es un valor fijo. Del mismo modo, "p" y "a" son direcciones de un carácter, pero "a" es un valor fijo.
James Curran

20

Para punteros en C, tenemos

a[5] == *(a + 5)

y también

5[a] == *(5 + a)

Por eso es cierto que a[5] == 5[a].


15

No es una respuesta, sino solo algo de reflexión. Si la clase está sobrecargando el operador de índice / subíndice, la expresión 0[x]no funcionará:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

Como no tenemos acceso a la clase int , esto no se puede hacer:

class int
{
   int operator[](const Sub&);
};

2
class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } };
Ben Voigt

1
¿Realmente has intentado compilarlo? ¡Hay un conjunto de operadores que no se pueden implementar fuera de clase (es decir, como funciones no estáticas)!
Ajay

3
Uy, tienes razón. " operator[]será una función miembro no estática con exactamente un parámetro". Estaba familiarizado con esa restricción operator=, no pensé que se aplicara [].
Ben Voigt

1
Por supuesto, si cambia la definición de []operador, nunca volvería a ser equivalente ... si a[b]es igual *(a + b)y cambia esto, también tendrá que sobrecargar int::operator[](const Sub&);y intno es una clase ...
Luis Colorado

77
Esto ... no es ... C.
MD XF

11

Tiene muy buena explicación en UN TUTORIAL SOBRE PUNTEROS Y ARREGLOS EN C por Ted Jensen.

Ted Jensen lo explicó como:

De hecho, esto es cierto, es decir, donde sea que uno escriba a[i]puede ser reemplazado por*(a + i) sin ningún problema. De hecho, el compilador creará el mismo código en cualquier caso. Por lo tanto, vemos que la aritmética del puntero es lo mismo que la indexación de matrices. Cualquiera de las sintaxis produce el mismo resultado.

Esto NO significa que los punteros y las matrices son lo mismo, no lo son. Solo estamos diciendo que para identificar un elemento dado de una matriz tenemos la opción de dos sintaxis, una que usa indexación de matriz y la otra que usa aritmética de puntero, que producen resultados idénticos.

Ahora, mirando esta última expresión, parte de ella ... (a + i), es una simple adición usando el operador + y las reglas de C indican que dicha expresión es conmutativa. Es decir (a + i) es idéntico a (i + a). Así podríamos escribir *(i + a)tan fácilmente como *(a + i). ¡Pero *(i + a)podría haber venido i[a]! De todo esto viene la curiosa verdad de que si:

char a[20];

escritura

a[3] = 'x';

es lo mismo que escribir

3[a] = 'x';

44
a + i NO es una simple suma, porque es una aritmética de puntero. si el tamaño del elemento de a es 1 (char), entonces sí, es como entero +. Pero si es (por ejemplo) un número entero, entonces podría ser equivalente a a + 4 * i.
Alex Brown

@AlexBrown Sí, es una aritmética de puntero, que es exactamente la razón por la cual su última oración está equivocada, a menos que primero eche 'a' para ser un (char *) (suponiendo que un int es 4 caracteres). Realmente no entiendo por qué tanta gente está obsesionada con el resultado del valor real de la aritmética de puntero. El propósito completo de la aritmética del puntero es abstraer los valores de puntero subyacentes y dejar que el programador piense en los objetos que se están manipulando en lugar de en los valores de dirección.
jschultz410

8

Sé que la pregunta está respondida, pero no pude resistirme a compartir esta explicación.

Recuerdo los principios del diseño del compilador, supongamos que aes una intmatriz y un tamaño de int2 bytes, y la dirección base para a1000.

Cómo a[5]funcionará ->

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Entonces,

Del mismo modo, cuando el código c se divide en código de 3 direcciones, 5[a]se convertirá en ->

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

Así que, básicamente, tanto las declaraciones apuntan a la misma ubicación en la memoria y, por tanto, a[5] = 5[a].

Esta explicación también es la razón por la cual los índices negativos en las matrices funcionan en C.

es decir, si a[-5]accedo me dará

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

Me devolverá el objeto en la ubicación 990.


6

En C arrays , arr[3]y 3[arr]son los mismos, y sus notaciones puntero equivalentes son *(arr + 3)a *(3 + arr). Pero por el contrario [arr]3, o [3]arrno es correcto y resultará en el error de sintaxis, como (arr + 3)*y (3 + arr)*no son expresiones válidas. El motivo es que el operador de desreferencia debe colocarse antes de la dirección producida por la expresión, no después de la dirección.


6

en el compilador de c

a[i]
i[a]
*(a+i)

Hay diferentes maneras de referirse a un elemento en una matriz. (NO ES LO EXTRAÑO)


5

Un poco de historia ahora. Entre otros lenguajes, BCPL tuvo una influencia bastante importante en el desarrollo temprano de C. Si declaró una matriz en BCPL con algo como:

let V = vec 10

que en realidad asignó 11 palabras de memoria, no 10. Por lo general, V fue la primera y contenía la dirección de la siguiente palabra. Entonces, a diferencia de C, nombrar V fue a esa ubicación y recogió la dirección del elemento cero de la matriz. Por lo tanto, la indirección de matriz en BCPL, expresada como

let J = V!5

realmente tenía que hacerlo J = !(V + 5)(usando la sintaxis BCPL) ya que era necesario buscar V para obtener la dirección base de la matriz. Así V!5y5!V fueron sinónimos. Como observación anecdótica, WAFL (Warwick Functional Language) fue escrito en BCPL, y lo mejor de mi memoria solía usar la última sintaxis en lugar de la primera para acceder a los nodos utilizados como almacenamiento de datos. De acuerdo, esto es de hace 35 a 40 años, así que mi memoria está un poco oxidada. :)

La innovación de prescindir de la palabra adicional de almacenamiento y hacer que el compilador inserte la dirección base de la matriz cuando se nombró vino más tarde. Según el documento de historia de C, esto sucedió aproximadamente cuando las estructuras se agregaron a C.

Tenga !en cuenta que en BCPL era un operador de prefijo unario y un operador de infijo binario, en ambos casos haciendo indirección. solo que la forma binaria incluía una adición de los dos operandos antes de hacer la indirección. Dada la naturaleza orientada a las palabras de BCPL (y B), esto realmente tenía mucho sentido. La restricción de "puntero e entero" se hizo necesaria en C cuando ganó tipos de datos, y se sizeofconvirtió en una cosa.


1

Bueno, esta es una característica que solo es posible debido al soporte de idiomas.

El compilador interpreta a[i]como *(a+i)y la expresión se 5[a]evalúa como *(5+a). Como la suma es conmutativa, resulta que ambos son iguales. Por lo tanto, la expresión se evalúa como true.


Aunque redundante, esto es claro, conciso y breve.
Bill K

0

C ª

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

El puntero es una "variable"

El nombre de la matriz es un "mnemónico" o "sinónimo"

p++;es válido pero a++no es válido

a[2] es igual a 2 [a] porque la operación interna en ambos es

"Aritmética de puntero" calculada internamente como

*(a+3) es igual *(3+a)


-4

tipos de puntero

1) puntero a datos

int *ptr;

2) puntero constante a los datos

int const *ptr;

3) puntero constante a datos constantes

int const *const ptr;

y las matrices son del tipo (2) de nuestra lista
Cuando define una matriz a la vez , se inicializa una dirección en ese puntero
Como sabemos que no podemos cambiar o modificar el valor constante en nuestro programa porque genera un ERROR en la compilación hora

La principal diferencia que encontré es ...

Podemos reinicializar el puntero por una dirección, pero no el mismo caso con una matriz.

======
y volviendo a su pregunta ...
a[5]no es más *(a + 5)
que puede entender fácilmente al
a contener la dirección (la gente la llama como dirección base) como un (2) tipo de puntero en nuestra lista
[]: ese operador puede ser reemplazable con puntero *.

así que finalmente...

a[5] == *(a +5) == *(5 + a) == 5[a] 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.