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

1651
Dinah 2008-12-20 07:01.

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

Joel dice que se debe a la aritmética de punteros, pero todavía no lo entiendo. ¿Por qué lo hacea[5] == 5[a] ?

17 answers

1952
Mehrdad Afshari 2008-12-20 07:04.

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

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

Por a[5]lo tanto evaluará:

*(a + 5)

y 5[a]evaluará para:

*(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 que *(a + 5), y de matemáticas de la escuela primaria sabemos que son iguales (la suma es conmutativa ).

289
David Thornley 2008-12-20 07:05.

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

237
Keith Thompson 2013-08-23 15:37.

Creo que las otras respuestas están pasando por alto algo.

Sí, p[i]es por definición equivalente a *(p+i), que (porque la adición es conmutativa) es equivalente a *(i+p), que (de nuevo, según la 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, conmutatividad tiene mucho sentido: x + y == y + x.

Pero en este caso estamos hablando específicamente de 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 deben tener un tipo aritmético, o un operando debe ser un puntero a un tipo de objeto completo y el otro debe tener un tipo entero.

Con la misma facilidad podría haber dicho:

Para agregar, ambos operandos deben tener un tipo aritmético, o el operando izquierdo debe ser un puntero a un tipo de objeto completo y el operando derecho debe tener un tipo entero.

en cuyo caso ambos i + py i[p]serían ilegales.

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 sólo 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 con fecha de 1967), que bien puede 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 a muchas décadas atrás, a los lenguajes ancestros de C.

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

Y a lo largo de los años, cualquier cambio a 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 que requiera 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 .

199
James Curran 2008-12-20 07:07.

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 mucha), por lo que el compilador de C no revisó mucho la sintaxis. Por lo tanto, " X[Y]" se tradujo más bien ciegamente a " *(X+Y)"

Esto también explica las sintaxis " +=" y " ++". Todo en el formulario " A = B + C" tenía el mismo formulario compilado. 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 hacerlo ( A += C). De manera similar, si lo Cestaba 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)

56
user30364 2009-02-12 05:56.

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

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

50
Peter Lawrey 2011-08-12 03:50.

Para responder a 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
29
Frédéric Terrazzoni 2012-06-11 09:50.

Acabo de descubrir que esta fea sintaxis podría ser "útil", o al menos muy divertida de jugar cuando quieres 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 :)

26
PolyThinker 2008-12-20 22:16.

Buenas preguntas / respuestas.

Solo quiero señalar que los punteros de C y las matrices 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.

20
Noname 2012-03-23 21:05.

Para punteros en C, tenemos

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

y también

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

De ahí que sea cierto que a[5] == 5[a].

16
Ajay 2011-06-19 22:37.

No es una respuesta, sino algo para pensar. Si la clase tiene un operador de índice / subíndice sobrecargado, 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&);
};
12
A.s. Bhullar 2013-09-27 20:46.

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

Ted Jensen lo explicó como:

De hecho, esto es cierto, es decir, dondequiera que se escriba, a[i]se puede reemplazar *(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 de punteros es lo mismo que la indexación de matrices. Cualquiera de las dos sintaxis produce el mismo resultado.

Esto NO significa que los punteros y las matrices sean 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 usando indexación de matriz y la otra usando aritmética de puntero, que arrojan 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 establecen que dicha expresión es conmutativa. Es decir (a + i) es idéntico a (i + a). Por lo tanto, podríamos escribir con la *(i + a)misma facilidad que *(a + i). ¡Pero *(i + a)podría haber venido de i[a]! De todo esto surge la curiosa verdad de que si:

char a[20];

escribiendo

a[3] = 'x';

es lo mismo que escribir

3[a] = 'x';
8
Ajinkya Patil 2016-05-04 22:24.

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 el tamaño intes de 2 bytes, y la dirección base para aes 1000.

¿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

Asi que,

De manera similar, cuando el código c se divide en un 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 es también la razón por la que los índices negativos en 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.

7
Krishan 2013-12-18 01:22.

En C matrices , arr[3]y 3[arr]son lo mismo, y sus notaciones de puntero equivalentes son *(arr + 3)a *(3 + arr). Pero por el contrario [arr]3o [3]arrno es correcto y dará como resultado un error de sintaxis, ya que (arr + 3)*y (3 + arr)*no son expresiones válidas. La razón es que el operador de desreferencia debe colocarse antes de la dirección que proporciona la expresión, no después de la dirección.

6
AVIK DUTTA 2014-10-29 23:14.

en el compilador c

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

¡Hay diferentes formas de referirse a un elemento en una matriz! (NO ES EXTRAÑO)

5
dgnuff 2019-04-09 08:45.

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. Normalmente, V era la primera y contenía la dirección de la palabra inmediatamente siguiente. Entonces, a diferencia de C, nombrar V fue a esa ubicación y tomó la dirección del elemento cero de la matriz. Por lo tanto, la indirección de arreglos 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!5y 5!Vfueron sinónimos. Como observación anecdótica, WAFL (Warwick Functional Language) fue escrito en BCPL, y en lo mejor de mi memoria tendía 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 entre 35 y 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 insertara la dirección base de la matriz cuando se nombró llegó más tarde. Según el artículo de historia de C, esto sucedió aproximadamente en el momento en que se agregaron las estructuras 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 suma de los dos operandos antes de realizar la indirección. Dada la naturaleza orientada a palabras de BCPL (y B), esto en realidad tenía mucho sentido. La restricción de "puntero y entero" se hizo necesaria en C cuando ganó tipos de datos y se sizeofconvirtió en una cosa.

1
Harsha J K 2018-04-03 08:42.

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). Dado que la suma es conmutativa, resulta que ambos son iguales. Por lo tanto, la expresión se evalúa como true.

0
Jayghosh Wankar 2017-02-13 03:54.

Cía

 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 pes una "variable", el nombre de la matriz aes un "mnemónico" o "sinónimo", por lo que 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+2)igual*(2+a)

MORE COOL STUFF

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

¿Caduca el repelente de insectos?

¿Caduca el repelente de insectos?

¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?

Actualice a un Sonicare por tan solo $ 30

Actualice a un Sonicare por tan solo $ 30

¿Quiere probar un cepillo de dientes Sonicare sin gastar mucho dinero en uno de sus modelos favoritos de gama alta? Puede comprar un kit de la Serie 2 o Serie 3 por tan solo $ 30 hoy en Amazon. Haga clic aquí para ver la lista completa de modelos elegibles y tenga en cuenta que se descontarán $ 10 adicionales en su carrito.

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Tapas elásticas de silicona de Tomorrow's Kitchen, paquete de 12 | $14 | Amazonas | Código promocional 20OFFKINJALids son básicamente los calcetines de la cocina; siempre perdiéndose, dejando contenedores huérfanos que nunca podrán volver a cerrarse. Pero, ¿y si sus tapas pudieran estirarse y adaptarse a todos los recipientes, ollas, sartenes e incluso frutas en rodajas grandes que sobran? Nunca más tendrás que preocuparte por perder esa tapa tan específica.

Cuéntanos tus mejores trucos de Washington, DC

Cuéntanos tus mejores trucos de Washington, DC

Hemos pirateado algunas ciudades industriales en esta columna, como Los Ángeles y Las Vegas. Ahora es el momento de una ciudad militar-industrial-compleja.

Un minorista está eliminando su sección de tallas grandes y mezclando tallas más grandes con todo lo demás

Un minorista está eliminando su sección de tallas grandes y mezclando tallas más grandes con todo lo demás

Un minorista está enlatando su sección de tallas grandes. Pero no están tomando la categoría solo en línea o descontinuándola por completo.

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.

India me está pateando el culo

India me está pateando el culo

Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.

ℝ

“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.

¿Merrick Garland le ha fallado a Estados Unidos?

Es más de la mitad de la presidencia de Biden. ¿Qué está esperando Merrick Garland?

¿Merrick Garland le ha fallado a Estados Unidos?

Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.

Language