La matriz asignada estática normal tiene este aspecto y se puede acceder a ella mediante las siguientes fórmulas:
const int N = 3; const int M = 3; int a1[N][M] = { {0,1,2}, {3,4,5}, {6,7,8} }; int x = a1[1][2]; // x = 5 int y = *(a1+2+N*1); // y = 5, this is what [] operator is doing in the backgroundArray es una región continua de memoria. Se ve diferente en el caso de la asignación dinámica de matrices, en su lugar hay una matriz de punteros a matrices:
int** a2 = new int*[N]; for (int i = 0; i < N; i++) a2[i] = new int[M]; //Assignment of values as in previous example int x = a2[1][2]; int y = *(*(a2+1))+2); // This is what [] operator is doing in the background, it needs to dereference pointers twiceComo podemos ver, las operaciones realizadas por el operador [] son completamente diferentes en el caso de una matriz continua típica y una matriz asignada dinámicamente. Mis preguntas ahora son las siguientes:
Para esta declaración de una matriz
int a1[N][M] = { {0,1,2}, {3,4,5}, {6,7,8} };estos registros
int x = a1[1][2]; int y = *(a1+2+N*1);no son equivalentes.
La segunda es incorrecta. La expresión *(a1+2+N*1) tiene el tipo int[3] que se convierte implícitamente en un objeto del tipo int * utilizado como inicializador. Entonces, la variable entera y se inicializa con un puntero.
El operador a1[1] se evalúa como *( a1 + 1 ) . El resultado es una matriz unidimensional del tipo int[3] .
Entonces aplicando el segundo operador de subíndice obtendrás *( *( a1 + 1 ) + 2 ) .
La diferencia entre las expresiones cuando se usa el arreglo bidimensional y el arreglo asignado dinámicamente es que el designador del arreglo bidimensional en esta expresión (a1 + 1) se convierte implícitamente en un puntero a su primer elemento del tipo int ( * )[3] mientras que el puntero a la matriz de punteros asignada dinámicamente todavía tiene el mismo tipo int ** .
En el primer caso, al desreferenciar la expresión *(a1 + 1 ) obtendrá lvalue del tipo int[3] que, a su vez, se usa en la expresión *( a1 + 1) + 2 se vuelve a convertir implícitamente en un puntero del tipo int * .
En el segundo caso, la expresión *(a1 + 1) produce un objeto del tipo int * .
En los dos casos se utiliza la aritmética de punteros. La diferencia es que cuando usa matrices en el operador de subíndice, se convierten implícitamente en punteros a sus primeros elementos.
Cuando está asignando matrices dinámicamente cuando ya está tratando con punteros a sus primeros elementos.
Por ejemplo, en lugar de estas asignaciones
int** a2 = new int*[N]; for (int i = 0; i < N; i++) a2[i] = new int[M];podrías simplemente escribir
int ( *a2 )[M] = new int[N][M];¿Es correcta mi comprensión de las operaciones []?
int y = *(a1+2+N*1); // y = 5, this is what [] operator is doing in the background
Por definición, la forma de traducir los operadores de subíndices a la correspondiente aritmética de punteros e indirección es:
int y = *(*(a1+1)+2) Que es exactamente lo mismo que en el caso de int** .
Cómo el compilador C/C++ puede distinguir qué [] operación debe realizar
El compilador usa el sistema de tipos. Conoce los tipos de las expresiones y sabe qué significa operación de subíndice para cada tipo.
¿Funcionará correctamente en lenguaje C usando malloc en lugar de new? No veo ninguna razón por la que no en realidad.
No importa cómo se crea una matriz. El operador de subíndice funciona de la misma manera con todos los punteros.
a1 y a2 son tipos diferentes y, como tales, el comportamiento del operator [] dependerá de cómo ese tipo defina al operador. En este caso, se trata de comportamientos intrínsecos del compilador que se ajustan a la especificación de C++, pero también podría ser un std::unique_ptr<> o MyClass con el operator[]