Con arreglos, ¿por qué sucede que un[5] == 5[a]?

9 minutos de lectura

avatar de usuario
Dina

Como señala Joel en Podcast de desbordamiento de pila n.º 34en el lenguaje de programación C (también conocido como: K & R), se menciona esta propiedad de las matrices en C: a[5] == 5[a]

Joel dice que es por la aritmética de punteros pero sigo sin entender. Por que a[5] == 5[a]?

  • sería algo como un[+] también funcionan como *( a++) O *(++a) ?

    – Egon

    13 mayo 2010 a las 16:14

  • @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}un {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}+)

    – Dina

    13 mayo 2010 a las 17:24

  • Una variación compuesta interesante de esto se ilustra en el acceso a arreglos ilógicos, donde tiene char bar[]; int foo[]; y foo[i][bar] se usa como expresión.

    –Jonathan Leffler

    17 de octubre de 2012 a las 6:38

  • @EldritchConundrum, ¿por qué crees que ‘el compilador no puede comprobar que la parte izquierda es un puntero’? Sí puede. Eso es verdad a[b] = *(a + b) para cualquier dado a y bpero fue la elección libre de los diseñadores del lenguaje para + ser definido conmutativo para todos los tipos. Nada podía impedirles prohibir i + p mientras permite p + i.

    – ach

    14/03/2014 a las 19:46


  • @Andrey Uno suele esperar + ser conmutativo, por lo que tal vez el verdadero problema es elegir hacer que las operaciones con punteros se parezcan a la aritmética, en lugar de diseñar un operador de compensación separado.

    – Enigma sobrenatural

    18 de marzo de 2014 a las 10:36

avatar de usuario
keith thompson

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 la definición de la [] 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 promocionan 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 estándar C de la + operador (N1570 6.5.6) dice:

Para la suma, 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:

Para la suma, ambos operandos deben tener tipo aritmético, o la izquierda
operando será un puntero a un tipo de objeto completo y el operando derecho
tendrá tipo entero.

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

En términos de C++, realmente tenemos dos conjuntos de sobrecargados + operadores, 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 de esta manera?

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 el 1972 Referencia de los usuarios a B), que lo obtuvo de BCPL (manual fechado en 1967), que bien puede haberlo obtenido incluso de idiomas anteriores (¿CPL? ¿Algol?).

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

Esos lenguajes estaban mucho menos tipificados 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 que unsigned se agregó una palabra clave 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 lenguajes. Si un usuario quisiera agregar dos “cosas”, ya sea que esas “cosas” sean números enteros, punteros u otra cosa, no dependía del idioma para evitarlo.

Y a lo largo de los años, cualquier cambio en esa regla habría violado 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 número 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] significando exactamente lo mismo, aunque la última forma nunca debe aparecer fuera de la COICC.

  • Fantástica descripción de esta propiedad. Desde un punto de vista de alto nivel, creo 3[arr] es un artefacto interesante, pero rara vez se debe usar. La respuesta aceptada a esta pregunta () que hice hace un tiempo ha cambiado mi forma de pensar sobre la sintaxis. Aunque a menudo técnicamente no hay una forma correcta o incorrecta de hacer estas cosas, este tipo de funciones lo hacen pensar de una manera que es independiente 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.

    – Dina

    24 de agosto de 2013 a las 1:01

  • La suma es conmutativa. Que el estándar C lo defina de otra manera sería extraño. Es por eso que no podría decir tan fácilmente “Para la suma, ambos operandos tendrán un tipo aritmético, o el operando de la izquierda será un puntero a un tipo de objeto completo y el operando de la derecha tendrá un tipo entero”. – Eso no tendría sentido para la mayoría de las personas que agregan cosas.

    – iheanyi

    21 de abril de 2014 a las 17:54

  • @iheanyi: la suma suele ser conmutativa, y generalmente toma dos operandos del mismo tipo. La suma de punteros le permite agregar un puntero y un número entero, pero no dos punteros. En mi humilde opinión, ese ya es un caso especial suficientemente extraño en el 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

    21 de abril de 2014 a las 18:13

  • @supercat, eso es aún peor. Eso significaría que a veces x + 1 != 1 + x. Eso violaría completamente la propiedad asociativa de la suma.

    – iheanyi

    21/10/2014 a las 16:34

  • @iheanyi: creo que te referías a la propiedad conmutativa; la suma 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 asociativas algunas situaciones que actualmente no lo son, por ejemplo, 3U+(UINT_MAX-2L) sería igual a (3U+UINT_MAX)-2. Lo que sería mejor, sin embargo, es que el lenguaje agregue nuevos tipos distintos para enteros promocionables y anillos algebraicos de “envoltura”, de modo que sumar 2 a un ring16_t que contiene 65535 produciría un ring16_t con valor 1, independiente del tamaño de int.

    – Super gato

    21/10/2014 a las 16:46

  • Los arreglos no se definen en términos de punteros, pero acceso para ellos es.

    – Carreras de ligereza en órbita

    12 mayo 2011 a las 23:20

  • Yo agregaría “así que es igual a *(i + a)que se puede escribir como i[a]“.

    –Jim Balter

    5 de abril de 2013 a las 22:11

  • Le sugiero que incluya la cita del estándar, que es la siguiente: 6.5.2.1: 2 Una expresión posterior seguida de una expresión entre corchetes [] es una designación con subíndice de un elemento de un objeto de matriz. La definición del operador subíndice [] es ese 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 (equivalentemente, un puntero al elemento inicial de un objeto de matriz) y E2 es un número entero, E1[E2] designa el elemento E2-th de E1 (contando desde cero).

    – Valor

    17 de febrero de 2015 a las 21:41

  • Nitpick: No tiene sentido decir eso “*(a + i) es conmutativa”. Sin embargo, *(a + i) = *(i + a) = i[a] porque suma es conmutativo.

    – Andreas Rejbrand

    13 oct 2019 a las 22:18


  • @AndreasRejbrand OTOH + es el único operador binario en la expresión, por lo que está bastante claro qué puede ser conmutativo.

    – U. Windl

    4 de noviembre de 2020 a las 13:03

avatar de usuario
samuel danielson

Porque es útil para evitar anidamientos confusos.

Prefieres leer esto:

array[array[head].next].prev

o esto:

head[array].next[array].prev

Por cierto, C++ tiene una propiedad conmutativa similar para las llamadas a funciones. en lugar de escribir g(f(x)) como debe hacer en C, puede usar funciones miembro para escribir x.f().g(). Reemplace f y g con tablas de búsqueda y puede escribir g[f[x]] (estilo funcional) o (x[f])[g] (estilo oop). Este último se vuelve realmente agradable con estructuras que contienen índices: x[xs].y[ys].z[zs]. Usando la notación más común que es zs[ys[xs[x].y].z].

avatar de usuario
franji1

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 (64 KB era mucha), por lo que el compilador de C no verificaba mucho la sintaxis. Por lo tanto “X[Y]“fue traducido ciegamente a”*(X+Y)

Esto también explica el “+=” y “++” sintaxis. 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). Del mismo modo, si C era 1, estaba disponible una optimización de nivel de ensamblado 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)

avatar de usuario
error_29

Porque el compilador C siempre convierte la notación de matriz en notación de puntero.
a[5] = *(a + 5) además 5[a] = *(5 + a) = *(a + 5)
Entonces, ambos son iguales.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad