Indexación de matrices 2D: ¿comportamiento indefinido?

5 minutos de lectura

Indexacion de matrices 2D ¿comportamiento indefinido
dragosht

Recientemente me metí en algunas piezas de código haciendo algunas operaciones de indexación de matrices 2D cuestionables. Considerando como ejemplo el siguiente código de ejemplo:

int a[5][5];
a[0][20] = 3;
a[-2][15] = 4;
a[5][-3] = 5;

¿Las operaciones de indexación anteriores están sujetas a un comportamiento indefinido?

  • Hay un buen duplicado de esto, pero no puedo encontrarlo, la función de búsqueda SO es mucho peor que los recuerdos de las personas.

    –MM

    5 de agosto de 2014 a las 13:44

  • Posible duplicado aquí, no estoy seguro si deberíamos cerrar este, sin embargo, como el otro no se pregunta de buena manera, además, la respuesta aceptada aquí es mejor …

    – Aconcagua

    7 febrero 2020 a las 11:50

Indexacion de matrices 2D ¿comportamiento indefinido
dibujó mcgowen

Es un comportamiento indefinido, y he aquí por qué.

El acceso a matrices multidimensionales se puede dividir en una serie de accesos a matrices unidimensionales. En otras palabras, la expresión a[i][j] se puede pensar como (a[i])[j]. Citando C11 §6.5.2.1/2:

La definición del operador subíndice [] es eso E1[E2] es idéntico a (*((E1)+(E2))).

Esto significa que lo anterior es idéntico a *(*(a + i) + j). Siguiendo C11 §6.5.6/8 con respecto a la adición de un número entero y un puntero (énfasis mío):

Si tanto el operando puntero como el resultado apuntan a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz, la evaluación no producirá un desbordamiento; de lo contrario, el comportamiento no está definido.

En otras palabras, si a[i] no es un índice válido, el comportamiento es inmediatamente indefinido, incluso si “intuitivamente” a[i][j] parece dentro de los límites.

Entonces, en el primer caso, a[0] es válido, pero lo siguiente [20] no lo es, porque el tipo de a[0] es int[5]. Por lo tanto, el índice 20 está fuera de los límites.

En el segundo caso, a[-1] ya está fuera de los límites, por lo tanto ya UB.

En el último caso, sin embargo, la expresión a[5] apunta a uno más allá del último elemento de la matriz, que es válido según §6.5.6/8:

… si la expresión P apunta al último elemento de un objeto de matriz, la expresión (P)+1 apunta uno más allá del último elemento del objeto de matriz…

Sin embargo, más adelante en ese mismo párrafo:

Si el resultado apunta a uno más allá del último elemento del objeto de matriz, no se utilizará como operando de un operador * unario que se evalúa.

Entonces, mientras a[5] es un puntero válido, desreferenciarlo causará un comportamiento indefinido, que es causado por el final [-3] indexación (que también está fuera de los límites, por lo tanto, UB).

  • “[…] porque el tipo de a[0] es int[5] […]”—Esa es la parte en la que estoy atascado. a[0] está sujeto a la conversión de lvalue aquí, por lo que decae a int *. No estoy seguro de esto…

    – mafso

    5 de agosto de 2014 a las 13:35

  • A pesar de que se descompone a int *sigue siendo un puntero a una matriz (que me inclino a creer que se considera que tiene solo 5 elementos).

    – Drew McGowen

    5 de agosto de 2014 a las 13:36

  • @mafso a[0] tiene tipo int[5] ; el puntero degradado es un valor r (y apunta a un objeto que es una matriz de 5 enteros)

    –MM

    5 de agosto de 2014 a las 13:42

  • @mafso en realidad, a[5]-3 significa que a + 5 está desreferenciado. Como he mencionado, a[5][-3] es equivalente a *(*(a + 5) - 3); la expresion *(a + 5) es UB.

    – Drew McGowen

    5 de agosto de 2014 a las 13:45

  • tenga en cuenta que un puntero también puede almacenar los límites de lo que está apuntando, por lo que una implementación de verificación de límites es legal. Los límites están determinados por el objeto al que pertenece el objeto al que se apunta.

    –MM

    5 de agosto de 2014 a las 13:50

la indexación de matrices con índices negativos es un comportamiento indefinido. Lo siento a[-3] es lo mismo que *(&a - 3) en la mayoría de las arquitecturas/compiladores, y se acepta sin previo aviso, pero el lenguaje C le permite agregar números enteros negativos a los punteros, pero no usar valores negativos como índices de matriz. Por supuesto, esto ni siquiera se verifica en tiempo de ejecución.

Además, hay algunos problemas que se deben tener en cuenta al definir matrices delante de los punteros. Puede dejar sin especificar solo el primer subíndice, y no más, como en:

int a[][3][2]; /* array of unspecified size, definition is alias of int (*a)[3][2]; */

(de hecho, lo anterior es una definición de puntero, no una matriz, solo imprima sizeof a)

o

en un[4][3][2]; /* array de 24 enteros, el tamaño es 24*sizeof(int) */

cuando hace esto, la forma de evaluar el desplazamiento es diferente para las matrices que para los punteros, así que tenga cuidado. En el caso de matrices, int a[I][J][K];

&a[i][j][k] 

se coloca en

&a + i*(sizeof(int)*J*K) + j*(sizeof(int)*K) + k*(sizeof(int))

pero cuando declaras

int ***a; 

luego a[i][j][k] es lo mismo que:

*(*(*(&a+i)+j)+k)lo que significa que tienes que desreferenciar el puntero aLuego añade (sizeof(int **))*i a su valor, luego elimine la referencia nuevamente, luego agregue (sizeof (int *))*j a ese valor, luego elimine la referencia y agregue (sizeof(int))*k a ese valor para obtener la dirección exacta de los datos.

BR

  • int a[][3][2]; es ilegal. Debe especificar la primera dimensión o proporcionar un inicializador a partir del cual se calcula la primera dimensión. No es un “alias de puntero”. Es posible que se confunda con el significado de un declarador de matriz en una lista de parámetros de función, pero en ese caso int a[4][3][2] es también int (*a)[3][2].

    –MM

    24 de febrero de 2015 a las 20:17

  • en &a + i * (sizeof… te referías (char *)&a ; La aritmética de punteros se realiza en términos del tamaño del objeto al que se apunta.

    –MM

    24 de febrero de 2015 a las 20:18


  • a[i][j][k] es lo mismo que *(*(*(a+i)+j)+k) (nótese la falta de &)

    –MM

    24 de febrero de 2015 a las 20:18

¿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