una - tipos de matrices




Con matrices, ¿por qué es un caso que un[5]== 5[a]? (12)

Acabo de descubrir que esta fea sintaxis podría ser "útil", o al menos muy divertida para jugar cuando se quiere tratar 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 código real, pero de todos modos me pareció interesante :)

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

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


Buena pregunta / respuestas.

Solo quiero señalar que los punteros 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 a está en una dirección que es el comienzo de la matriz, y el símbolo p está 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.


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 es una "variable"

nombre de la matriz es un "mnemotécnico" 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 de ambos es

"Aritmética de punteros" calculada internamente como

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


Creo que las otras respuestas están perdiendo algo.

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

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

Pero la conmutación de la adición 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 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 de la norma C del operador + ( 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 tan fácilmente haber dicho:

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, tanto i + p como 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 las cuales solo la primera es realmente necesaria.

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 de 1967), que bien puede haberla obtenido incluso de Lenguas anteriores (CPL? Algol?).

Así que la idea de que la indexación de matrices se define en términos de adición, y esa adición, incluso de un puntero y un entero, es conmutativa, se remonta muchas décadas, a las lenguas ancestrales de C.

Esos lenguajes estaban mucho menos escritos que el C moderno. En particular, la distinción entre punteros y enteros a menudo se ignoraba. (Los programadores tempranos de C a veces usaban punteros como enteros sin signo, antes de que la palabra clave unsigned se agregara al lenguaje). Por lo tanto, la idea de hacer una adición no 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 quería agregar dos "cosas", ya sea que esas "cosas" sean enteros, punteros o alguna otra cosa, no era el idioma adecuado para 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 que requiera poner el puntero a la izquierda y el entero a la derecha puede 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] significan exactamente lo mismo, aunque la última forma nunca debe aparecer fuera del IOCCC .


En matrices C , arr[3] y 3[arr] son iguales, y sus notaciones de puntero equivalentes son *(arr + 3) a *(3 + arr) . Pero, por el contrario, [arr]3 o [3]arr no es correcto y generará un error de sintaxis, ya que (arr + 3)* y (3 + arr)* no son expresiones válidas. El motivo es que el operador debe colocarse antes de la dirección proporcionada por la expresión, no después de la dirección.


No es una respuesta, sino sólo 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&);
};

Para responder a la pregunta literalmente. No siempre es cierto que x == 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

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


Tiene una muy buena explicación en UN TUTORIAL SOBRE PUNTOS Y ARRAYS EN C por Ted Jensen.

Ted Jensen lo explicó como:

De hecho, esto es cierto, es decir, donde se escribe a[i] se puede reemplazar con *(a + i) sin ningún problema. De hecho, el compilador creará el mismo código en cualquier caso. Así vemos que la aritmética de punteros es lo mismo que la indexación de matriz. Cualquiera de las dos sintaxis produce el mismo resultado.

Esto NO significa que los punteros y las matrices sean la misma cosa, 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 la indexación de matrices y la otra que usa aritmética de punteros, que producen resultados idénticos.

Ahora, mirando esta última expresión, parte de ella ... (a + i) , es una adición simple que usa 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 de 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';

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.


en compilador de c

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

¡Hay diferentes maneras de referirse a un elemento en una matriz! (No en todo lo que sea)


tipos de puntero

1) puntero a los datos

int *ptr;

2) puntero const a los datos

int const *ptr;

3) const puntero a const data

int const *const ptr;

y los arrays son tipo de (2) de nuestra lista
Cuando define una matriz a la vez , se inicializa una dirección en ese puntero
Como sabemos, no podemos cambiar ni modificar el valor de const en nuestro programa porque genera un ERROR en el momento de la compilación

La principal diferencia que encontré es ...

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

======
y de vuelta a tu pregunta ...
a [5] no es más que * (a + 5)
puedes entender facilmente por
a - dirección que contiene (la gente la llama como dirección base) como un (2) tipo de puntero en nuestra lista
[] - ese operador puede ser reemplazable con el puntero *.

así que finalmente...

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




pointer-arithmetic