funciones - punteros en c
Crear un puntero a matriz bidimensional (11)
Necesito un puntero a una matriz estática de 2 dimensiones. ¿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 del tipo de puntero incompatible
- el valor subscrito no es matriz ni puntero
- error: uso no válido del miembro de matriz flexible
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 puedes disfrutar de la vida otra vez :)
matrix_ptr[0][1] = ...;
Tenga cuidado con el mundo puntero / matriz en C, mucha confusión es alrededor de 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 el operador de dirección de correo electrónico &
gusta en el siguiente fragmento
uint8_t (*matrix_ptr)[][20] = &l_matrix;
Luego, ese crea un puntero a un tipo de matriz incompleta de elementos del tipo matriz de 20 uint8_t. Como el puntero es para una matriz de matrices, debe acceder a ella con
(*matrix_ptr)[0][1] = ...;
Y como es un puntero a una matriz incompleta, no se puede hacer como un acceso directo
matrix_ptr[0][0][1] = ...;
Debido a que la indexación requiere que se conozca el tamaño del tipo de elemento (la indexación implica una adición de un 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 lo ve como una matriz unidimensional, no es uint8_t
, pero uint8_t[20]
uint8_t *matrix_ptr = l_matrix; // fail
La siguiente es una buena alternativa
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
Usted accede a ella 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 hace uso del hecho de que los elementos en una matriz se almacenan contiguamente
uint8_t *matrix_ptr = l_matrix[0];
Ahora, eso formalmente solo le permite acceder a los elementos del primer elemento de la matriz de dos dimensiones. 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 lanzas un análisis de alias y otras optimizaciones agresivas, algún compilador podría suponer que podría romper ese código. Habiendo dicho eso, nunca me he encontrado con un compilador que falla en él (pero, de nuevo, no he usado esa técnica en código real), e incluso la C FAQ contiene esa técnica (con una advertencia sobre su UB''ness) ), y si no puede cambiar el tipo de matriz, esta es la última opción para guardarlo :)
Desea un puntero al primer elemento, entonces;
static uint8_t l_matrix[10][20];
void test(){
uint8_t *matrix_ptr = l_matrix[0]; //wrong idea
}
En
int *ptr= l_matrix[0];
puedes acceder como
*p
*(p+1)
*(p+2)
después de todas las 2 matrices dimensionales también se almacenan como 1-d.
En C99 (compatible con clang y gcc) existe una sintaxis oscura para pasar matrices multidimensionales a funciones por referencia:
int l_matrix[10][20];
void test(int matrix_ptr[static 10][20]) {
}
int main(void) {
test(l_matrix);
}
A diferencia de un puntero simple, esto hace alusión al tamaño de la matriz, lo que teóricamente permite al compilador advertir acerca de pasar una matriz demasiado pequeña y detectar el acceso fuera de límites obvio.
Lamentablemente, no repara sizeof()
y parece que los compiladores todavía no usan esa información, por lo que sigue siendo una curiosidad.
En el caso general, un puntero a una matriz 2-D se declara así:
int ***matrix_ptr = &l_matrix;
Las definiciones del puntero se resuelven desde el "adentro hacia afuera", o la notación más cercana al nombre de la variable, y cada vez más lejos con cada paso. De izquierda a derecha:
El tercero " " es el "puntero". El segundo " " es para la primera dimensión de la matriz. El primero " " es para la segunda dimensión de la matriz.
Puede continuar agregando " " para cualquier dimensión adicional que desee agregar a la matriz.
No es necesario conocer las dimensiones de la matriz para declarar el puntero a esa matriz.
La notación [] es azúcar sintáctica para el cálculo de las compensaciones de matriz, pero es un azúcar agradable:
matrix_ptr[0][1] = ...;
No es necesario que la segunda dimensión de la matriz 2-D tenga el mismo número de elementos en cada posición.
Gday,
La declaracion
static uint8_t l_matrix[10][20];
ha reservado almacenamiento para 10 filas de 20 ubicaciones unit8_t, es decir, 200 ubicaciones uint8_t de tamaño, encontrándose cada elemento calculando 20 x fila + columna.
Entonces no
uint8_t (*matrix_ptr)[20] = l_matrix;
darle lo que necesita y señalar el elemento columna cero de la primera fila de la matriz?
Editar: ¿ Pensando en esto un poco más, no es un nombre de matriz, por definición, un puntero? Es decir, el nombre de una matriz es un sinónimo de la ubicación del primer elemento, es decir l_matrix [0] [0]?
Edit2: como mencionaron otros, el espacio de comentarios es demasiado pequeño para una discusión posterior. De todas formas:
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 según lo define el estándar, la declaración:
static uint8_t l_matrix[10][20];
ha reservado 200 ubicaciones secuenciales del tipo uint8_t.
Refiriéndose a l_matrix usando declaraciones del formulario:
(*l_matrix + (20 * rowno) + colno)
le dará el contenido del elemento colno''th encontrado en la fila rowno.
Todas las manipulaciones del puntero tienen en cuenta automáticamente el tamaño del objeto apuntado. - K & R Sección 5.4, p.103
Este es también el caso si cualquier cambio de alineación de relleno o byte está involucrado en el almacenamiento del objeto en cuestión. El compilador se ajustará automáticamente para esto. Por definición del estándar C ANSI.
HTH
aclamaciones,
La sintaxis básica del puntero de inicialización que apunta a la matriz multidimensional es
type (*pointer)[ 1st dimension size ][2nd dimension size ][..]=&array_name
La sintaxis básica para llamar es
(*pointer_name)[ 1st index][2nd index][...]
Aquí hay un ejemplo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char balance[5][100] = { {"Subham"},{"Messi"} };//The multidimentional array...
char (*p)[5][100]=&balance;//Pointer initialization...
printf("%s/n",(*p)[0]);//Calling...
printf("%s/n",(*p)[1]);//Calling...
return 0;
}
La salida es:
Subham
Messi
Eso lo hizo ...
Para comprender completamente esto, debe comprender los siguientes conceptos:
¡Las matrices no son punteros!
En primer lugar (y se ha predicado lo suficiente), las matrices no son punteros . En cambio, en la mayoría de los usos, se "descomponen" en 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 matriz y no implica que sean lo mismo.
Arrays multidimensionales
Las matrices multidimensionales son solo una manera de ''particionar'' la memoria de una manera que el compilador / máquina puede entender y operar.
Por ejemplo, int a[4][3][5]
= una matriz que contiene 4 * 3 * 5 (60) ''trozos'' de memoria de tamaño entero.
La ventaja sobre el uso de int a[4][3][5]
vs plain int b[60]
es que ahora están ''particionados'' (es más fácil trabajar con sus ''fragmentos'', si es necesario), y el programa puede ahora realizar comprobaciones encuadernadas
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]
e int b[60]
son los mismos 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, puede ver claramente que cada "partición" es solo una matriz de la que el programa realiza un seguimiento.
Sintaxis
Ahora, 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 obvio, 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, como esta:
0x7eb5a3b4 0x7eb5a3b4
Sin embargo, solo uno puede asignarse a un puntero tan directamente :
int *p1 = a[0]; // RIGHT !
int *p2 = a; // WRONG !
¿Por qué no se puede asignar un puntero a un puntero sino a a[0]
?
Esto, simplemente, es una consecuencia de matrices multidimensionales, y explicaré por qué:
En el nivel de '' a
'', todavía vemos que tenemos otra ''dimensión'' que esperar. En el nivel de '' a[0]
'', sin embargo, ya estamos en la dimensión superior, por lo que respecta al programa, solo estamos viendo una matriz normal.
Usted puede estar preguntando:
¿Por qué importa si la matriz es multidimensional en lo que respecta a hacer un puntero para ello?
Lo mejor es pensar de esta manera:
Un ''decaimiento'' de una matriz multidimensional no es solo una dirección, sino una dirección con datos de partición (si bien todavía comprende 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 lo especifiquemos:
int a[4][5][95][8];
int (*p)[5][95][8];
p = a; // p = *a[0] // p = a+0
De lo contrario, el significado de las propiedades de clasificación de la matriz se pierde.
También tenga en cuenta el uso de paréntesis alrededor de *p
: int (*p)[5][95][8]
- Eso es para especificar que estamos haciendo 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 "cargada" tendrá la carga de "Tengo subdimensiones".
- Los datos de dimensión no pueden existir en un puntero a menos que se lo proporcione .
En resumen: las matrices multidimensionales se degradan en direcciones que tienen la capacidad de comprender sus contenidos.
Puedes hacerlo así:
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
Siempre puede evitar juguetear con el compilador declarando la matriz como lineal y haciendo el cálculo (de fila, col) a índice de matriz usted mismo.
static uint8_t l_matrix[200];
void test(int row, int col, uint8_t val)
{
uint8_t* matrix_ptr = l_matrix;
matrix_ptr [col+y*row] = val; // to assign a value
}
esto es lo que el compilador habría hecho de todos modos.
También puede agregar un desplazamiento si desea usar índices negativos:
uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
l_matrix[-4][1]=7;
Si su compilador da un error o advertencia, puede usar:
uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;