Elenco T[][] a T*

6 minutos de lectura

Elenco T a T
Ajay Brahmakshatriya

¿Es seguro lanzar una matriz 2D de tipo T para T* y desreferenciar los elementos?

Dado que el diseño de la memoria de la matriz 2D es lineal, el puntero base debe ser igual al puntero del primer elemento. Dado que el tipo final al que apuntan también es el mismo, no debería haber ningún problema de diferencia de alineación.

¿O hay algún aspecto que puede causar un comportamiento indefinido?

Para que quede claro, me refiero a algo como esto:

int arr[10][10];
int p = *((int*) arr);

Además, la misma pregunta si accedo a elementos más allá de la primera matriz, es decir (int*) arr + 13. ¿Estaría incluido en la cláusula de acceso fuera de los límites? Como estoy accediendo fuera de los límites de la primera matriz.

  • @dasblinkenlight Sí, también estoy eliminando referencias en la misma declaración para obtener int. Funciona, solo tengo curiosidad si esto invoca algún tipo de UB.

    –Ajay Brahmakshatriya

    8 mayo 2017 a las 15:27

  • No tengo claro por qué lo harías querer para. int *p = *arr; es válido, y por más indirección, int val = **arr; es también ¿Por qué enturbiar algo de eso con yesos? Eres tú De Verdad preguntando si la dirección del primer elemento de una matriz de matrices es equivalente a la dirección del primer elemento de la primera matriz en una matriz de matrices, y si el estándar garantiza eso?

    – WhozCraig

    8 de mayo de 2017 a las 15:29


  • No dudaría en usar dicho código en mi código privado y productivo. pero No me atrevería a preguntar esto aquí. Espero una buena respuesta que cite los respectivos párrafos estándar …

    – El gato de Scheff

    8 de mayo de 2017 a las 15:29

  • Ha habido algunos argumentos aquí sobre si se puede calcular (int*) arr + 13 o si se vuelve indefinido una vez que pasa el final de la “primera” matriz (aunque las que he visto han sido para C++, no para C, y las partes relevantes del estándar pueden ser diferentes). Sin embargo, no creo que haya ninguna razón para pensar que obtienes UB si permaneces dentro de la primera matriz.

    – Daniel H.

    8 mayo 2017 a las 15:31

  • El consenso que he visto es que probablemente no esté permitido en C++, pero funcionará en prácticamente cualquier sistema en la práctica. No me sorprendería si nos perdimos algo y en realidad está permitido. También estoy seguro de que C es más permisivo en algunos aspectos, por lo que incluso si es UB en C++, hay muchas posibilidades de que esté permitido en C. No puedo esperar a ver cuál es la respuesta aquí.

    – Daniel H.

    8 mayo 2017 a las 15:42


Elenco T a T
Cristóbal

El elenco en sí está bien. Lo que de hecho podría ser cuestionable sería usar un puntero a un elemento dentro de un subarreglo para acceder a elementos en otro diferente: mientras que la operación está claramente bien definida en un nivel inferior (todo está alineado correctamente, sin relleno, los tipos coinciden, .. .), mi impresión del estándar C ha sido que está redactado de una manera que permite que las implementaciones de verificación de límites cumplan con los estándares.

Tenga en cuenta, sin embargo, que atravesar linealmente una matriz multidimensional aún podría estar permitido, ya que un puntero que apunta más allá de una subarreferencia (que normalmente no se debe desreferenciar) también pasa a ser un puntero al primer elemento de la siguiente subarrelación. A lo que conduce este pensamiento es a que la aritmética de punteros no es asociativa:

Tengo entendido que una expresión como (int *)arr + 13 implica un comportamiento indefinido1pero podría quedar bien definido si lo divide en dos pasos ((int *)arr + 10) + 3.

Si desea hacerlo en un solo paso, por supuesto, también existe la opción de bajar al nivel de byte, es decir (int *)((char *)arr + 13 * sizeof (int))que no debería ser problemático ya que, a diferencia de otros tipos de punteros, los punteros de caracteres están delimitados por el objeto envolvente más externo.

He tenido discusiones sobre esto antes, pero no recuerdo si alguna vez hubo una conclusión definitiva que resolviera esta ambigüedad en particular.


1 C11, sección 6.5.6 §8

[…] 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. […]

  • Agregué el elenco ya que quería el primer elemento después de una sola desreferencia. *arr devolvería la dirección del primer elemento que sería el mismo que arr.

    –Ajay Brahmakshatriya

    8 mayo 2017 a las 15:51

  • Sí, siempre tenemos sizeof (int [10][10]) == 100 * sizeof (int). No creo que eso se indique explícitamente en ninguna parte, pero, por ejemplo, el ejemplo 2 de 6.5.3.4 es el modismo conocido sizeof array / sizeof array[0]que solo funciona si las matrices no pueden agregar relleno.

    – Cristóbal

    8 mayo 2017 a las 16:42

  • @AjayBrahmakshatriya: El Estándar requiere expresamente que los compiladores traten la zancada de una matriz como igual al tamaño del elemento, y el tamaño de una matriz como igual al tamaño del elemento multiplicado por el número declarado de elementos. Así, dado int foo[4][5]la Norma garantiza que foo[0][4] y foo[0][1] se almacenarán consecutivamente. Estos requisitos serían bastante tontos e inútiles si no hubiera una manera de obtener un puntero que pudiera usarse para acceder a todos los elementos de una matriz secuencialmente. Desafortunadamente, el Estándar no es exactamente claro sobre lo que se requiere para hacer eso.

    – Super gato

    8 mayo 2017 a las 18:49

  • @supercat He visto eso dicho antes, pero en realidad nunca vi dónde dice el estándar que las matrices no pueden tener relleno al final. es al menos un de facto estándar porque ningún compilador quiere romper sizeof(arr) / sizeof(arr[0])pero no estoy seguro de dónde se indica realmente.

    – Daniel H.

    9 mayo 2017 a las 13:40

  • @DanielH: Al buscarlo, la única mención que veo es una declaración de que sizeof expresión informará el tamaño de una matriz. Tal vez vi la garantía más explícita en K&R2 que no es el estándar, pero el estándar no establece ninguna disposición que permita una int foo[7][3] declaración para crear una matriz donde sizeof foo[0] != 3* sizeof foo[0][0].

    – Super gato

    9 mayo 2017 a las 15:57

¿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