Crear un puntero a una matriz bidimensional

Resuelto Dill asked hace 15 años • 10 respuestas

Necesito un puntero a una matriz bidimensional estática. ¿Cómo se hace esto?

static uint8_t l_matrix[10][20];

void test(){
   uint8_t **matrix_ptr = l_matrix; //wrong idea 
}

Recibo todo tipo de errores como:

  • Advertencia: asignación de un tipo de puntero incompatible
  • El valor con subíndice no es ni una matriz ni un puntero.
  • error: uso no válido del miembro de matriz flexible
Dill avatar Jun 27 '09 20:06 Dill
Aceptado

Aquí quieres hacer un puntero al primer elemento de la matriz.

uint8_t (*matrix_ptr)[20] = l_matrix;

Con typedef, esto se ve más limpio

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

Entonces podrás volver a disfrutar de la vida :)

matrix_ptr[0][1] = ...;

Tenga cuidado con el mundo puntero/matriz en C, hay mucha confusión en torno a esto.


Editar

Revisando algunas de las otras respuestas aquí, porque los campos de comentarios son demasiado cortos para hacerlo allí. Se propusieron múltiples alternativas, pero no se mostró cómo se comportan. Así es como lo hacen

uint8_t (*matrix_ptr)[][20] = l_matrix;

Si corrige el error y agrega la dirección del operador &como en el siguiente fragmento

uint8_t (*matrix_ptr)[][20] = &l_matrix;

Luego, ese crea un puntero a un tipo de matriz incompleta de elementos de tipo matriz de 20 uint8_t. Debido a que el puntero apunta a una matriz de matrices, debe acceder a él con

(*matrix_ptr)[0][1] = ...;

Y como es un puntero a una matriz incompleta, no puedes utilizarlo como atajo.

matrix_ptr[0][0][1] = ...;

Porque la indexación requiere que se conozca el tamaño del tipo de elemento (la indexación implica la adición de un número entero al puntero, por lo que no funcionará con tipos incompletos). Tenga en cuenta que esto solo funciona en C, porque T[]y T[N]son tipos compatibles. C++ no tiene un concepto de tipos compatibles , por lo que rechazará ese código, porque T[]y T[10]son tipos diferentes.


La siguiente alternativa no funciona en absoluto, porque el tipo de elemento de la matriz, cuando la ves como una matriz unidimensional, no uint8_t es , perouint8_t[20]

uint8_t *matrix_ptr = l_matrix; // fail

La siguiente es una buena alternativa.

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

Accedes a él con

(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now

Tiene la ventaja de que conserva el tamaño de la dimensión exterior. Entonces puedes aplicar sizeof en él.

sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20

Hay otra respuesta que aprovecha el hecho de que los elementos de una matriz se almacenan de forma contigua

uint8_t *matrix_ptr = l_matrix[0];

Ahora, eso formalmente solo le permite acceder a los elementos del primer elemento de la matriz bidimensional. Es decir, se cumple la siguiente condición

matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid

matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior

Notarás que probablemente funcione hasta 10*20-1, pero si aplicas un análisis de alias y otras optimizaciones agresivas, algún compilador podría hacer una suposición que puede romper ese código. Dicho esto, nunca me he encontrado con un compilador que falle (pero, de nuevo, no he usado esa técnica en código real), e incluso las preguntas frecuentes de C tienen esa técnica contenida (con una advertencia sobre su UB). ), y si no puedes cambiar el tipo de matriz, esta es una última opción para salvarte :)

Johannes Schaub - litb avatar Jun 27 '2009 13:06 Johannes Schaub - litb

Para comprender esto completamente , debe comprender los siguientes conceptos:

¡Las matrices no son punteros!

En primer lugar (y se ha predicado bastante), las matrices no son punteros . En cambio, en la mayoría de los usos, "decaen" a la dirección de su primer elemento, que se puede asignar a un puntero:

int a[] = {1, 2, 3};

int *p = a; // p now points to a[0]

Supongo que funciona de esta manera para que se pueda acceder al contenido de la matriz sin copiarlos todos. Eso es solo un comportamiento de los tipos de matrices y no implica que sean lo mismo.



matrices multidimensionales

Las matrices multidimensionales son solo una forma de "particionar" la memoria de una manera que el compilador/máquina pueda entender y operar.

Por ejemplo, int a[4][3][5]= una matriz que contiene 4*3*5 (60) 'fragmentos' de memoria de tamaño entero.

La ventaja sobre usar int a[4][3][5]vs simple int b[60]es que ahora están 'particionados' (es más fácil trabajar con sus 'fragmentos', si es necesario), y el programa ahora puede realizar comprobaciones enlazadas.

De hecho, int a[4][3][5]se almacena exactamente como int b[60]en la memoria. La única diferencia es que el programa ahora lo administra como si fueran entidades separadas de ciertos tamaños (específicamente, cuatro grupos de tres grupos de cinco).

Tenga en cuenta: Ambos int a[4][3][5]y int b[60]son iguales en la memoria, y la única diferencia es cómo los maneja la aplicación/compilador.

{
  {1, 2, 3, 4, 5}
  {6, 7, 8, 9, 10}
  {11, 12, 13, 14, 15}
}
{
  {16, 17, 18, 19, 20}
  {21, 22, 23, 24, 25}
  {26, 27, 28, 29, 30}
}
{
  {31, 32, 33, 34, 35}
  {36, 37, 38, 39, 40}
  {41, 42, 43, 44, 45}
}
{
  {46, 47, 48, 49, 50}
  {51, 52, 53, 54, 55}
  {56, 57, 58, 59, 60}
}

A partir de esto, se puede ver claramente que cada "partición" es sólo una matriz de la que el programa realiza un seguimiento.



Sintaxis

Ahora bien, las matrices son sintácticamente diferentes de los punteros . Específicamente, esto significa que el compilador/máquina los tratará de manera diferente. Esto puede parecer una obviedad, pero eche un vistazo a esto:

int a[3][3];

printf("%p %p", a, a[0]);

El ejemplo anterior imprime la misma dirección de memoria dos veces, así:

0x7eb5a3b4 0x7eb5a3b4

Sin embargo, sólo se puede asignar uno a un puntero de forma tan directa :

int *p1 = a[0]; // RIGHT !

int *p2 = a; // WRONG !

¿Por qué no se puede a asignar a un puntero pero a[0] sí se puede?

Esto, simplemente, es una consecuencia de los arreglos multidimensionales, y explicaré por qué:

En el nivel de ' a', todavía vemos que tenemos otra 'dimensión' que esperar. Sin embargo, en el nivel de ' a[0]', ya estamos en la dimensión superior, por lo que en lo que respecta al programa, solo estamos viendo una matriz normal.

Quizás te estés preguntando:

¿Por qué importa si la matriz es multidimensional con respecto a crear un puntero para ella?

Lo mejor es pensar de esta manera:

Una 'decadencia' de una matriz multidimensional no es solo una dirección, sino una dirección con datos de partición (también conocido como que sus datos subyacentes están hechos de otras matrices), que consiste en límites establecidos por la matriz más allá de la primera dimensión.

Esta lógica de 'partición' no puede existir dentro de un puntero a menos que la especifiquemos:

int a[4][5][95][8];

int (*p)[5][95][8];

p = a; // p = *a[0] // p = a+0

De lo contrario, se pierde el significado de las propiedades de clasificación de la matriz.

También tenga en cuenta el uso de paréntesis alrededor de *p: int (*p)[5][95][8]- Esto es para especificar que estamos creando un puntero con estos límites, no una matriz de punteros con estos límites:int *p[5][95][8]



Conclusión

Revisemos:

  • Las matrices se descomponen en direcciones si no tienen otro propósito en el contexto utilizado.
  • Las matrices multidimensionales son solo matrices de matrices; por lo tanto, la dirección 'degradada' llevará la carga de "Tengo subdimensiones"
  • Los datos de dimensión no pueden existir en un puntero a menos que se los proporcione .

En resumen: las matrices multidimensionales se descomponen en direcciones que tienen la capacidad de comprender su contenido.

Super Cat avatar Jan 10 '2016 06:01 Super Cat

En

int *ptr= l_matrix[0];

puedes acceder como

*p
*(p+1)
*(p+2)

después de todo, las matrices bidimensionales también se almacenan como 1-d.

Sagar avatar Feb 18 '2012 00:02 Sagar

Buen día,

La declaracion

static uint8_t l_matrix[10][20];

ha reservado almacenamiento para 10 filas de 20 ubicaciones unit8_t, es decir, 200 ubicaciones de tamaño uint8_t, y cada elemento se encuentra calculando 20 x fila + columna.

Así que no

uint8_t (*matrix_ptr)[20] = l_matrix;

¿le da lo que necesita y apunta al elemento de la columna cero de la primera fila de la matriz?

Editar: Pensando un poco más en esto, ¿no es un nombre de matriz, por definición, un puntero? Es decir, el nombre de una matriz es sinónimo de la ubicación del primer elemento, es decir, l_matrix[0][0]?

Edit2: Como mencionaron otros, el espacio para comentarios es demasiado pequeño para seguir discutiendo. De todos modos:

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

no proporciona ninguna asignación de almacenamiento para la matriz en cuestión.

Como se mencionó anteriormente, y tal como lo define la norma, la declaración:

static uint8_t l_matrix[10][20];

ha reservado 200 ubicaciones secuenciales de tipo uint8_t.

Refiriéndose a l_matrix usando declaraciones de la forma:

(*l_matrix + (20 * rowno) + colno)

will give you the contents of the colno'th element found in row rowno.

All pointer manipulations automatically take into account the size of the object pointed to. - K&R Section 5.4, p.103

This is also the case if any padding or byte alignment shifting is involved in the storage of the object at hand. The compiler will automatically adjust for these. By definition of the C ANSI standard.

HTH

cheers,

Rob Wells avatar Jun 27 '2009 13:06 Rob Wells