he declarado un struct
y trato de pasar una matriz de esas estructuras (así como una double
matriz de dobles y un entero) en una función. obtengo un “el tipo de matriz tiene un tipo de elemento incompleto” mensaje de gcc cuando lo compilo. ¿En qué me equivoqué al pasar el struct
a la función?
typedef struct graph_node {
int X;
int Y;
int active;
} g_node;
void print_graph(g_node graph_node[], double weight[][], int nodes);
yo tambien he probado struct g_node graph_node[]
pero me sale lo mismo.
Es la matriz la que está causando problemas en:
void print_graph(g_node graph_node[], double weight[][], int nodes);
La segunda y siguientes dimensiones deben darse:
void print_graph(g_node graph_node[], double weight[][32], int nodes);
O simplemente puede dar un puntero a puntero:
void print_graph(g_node graph_node[], double **weight, int nodes);
Sin embargo, aunque se ven similares, son muy diferentes internamente.
Si usa C99, puede usar arreglos calificados de forma variable. Citando un ejemplo del estándar C99 (sección §6.7.5.2 Array Declarators):
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
typedef int VLA[m][m]; // valid: block scope typedef VLA
struct tag {
int (*y)[n]; // invalid: y not ordinary identifier
int z[n]; // invalid: z not ordinary identifier
};
int D[m]; // valid: auto VLA
static int E[m]; // invalid: static block scope VLA
extern int F[m]; // invalid: F has linkage and is VLA
int (*s)[m]; // valid: auto pointer to VLA
extern int (*r)[m]; // invalid: r has linkage and points to VLA
static int (*q)[m] = &B; // valid: q is a static block pointer to VLA
}
pregunta en comentarios
[…] En mi main(), la variable que intento pasar a la función es una double array[][]
, entonces, ¿cómo pasaría eso a la función? Paso array[0][0]
en él me da un tipo de argumento incompatible, al igual que &array
y &array[0][0]
.
En tus main()
la variable debe ser:
double array[10][20];
o algo levemente similar; quizás
double array[][20] = { { 1.0, 0.0, ... }, ... };
Debería poder pasar eso con un código como este:
typedef struct graph_node
{
int X;
int Y;
int active;
} g_node;
void print_graph(g_node graph_node[], double weight[][20], int nodes);
int main(void)
{
g_node g[10];
double array[10][20];
int n = 10;
print_graph(g, array, n);
return 0;
}
Eso compila (al código objeto) limpiamente con GCC 4.2 (i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Basado en Apple Inc. compilación 5658) (LLVM compilación 2336.9.00)) y también con GCC 4.7.0 en Mac OS X 10.7.3 usando la línea de comando:
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c zzz.c
El compilador necesita saber el tamaño de la segunda dimensión en su matriz bidimensional. Por ejemplo:
void print_graph(g_node graph_node[], double weight[][5], int nodes);
Publicar esto en caso de que alguien se encuentre con esta pregunta y se pregunte sobre las razones formales por las que []
obras y [][]
no lo hace, en general. Hay varias reglas en juego: las reglas de lo que hace que una declaración de matriz sea válida y las reglas de cómo las matrices pasan como parámetros a las funciones “decaen” en un puntero al primer elemento.
C17 6.7.6.2/1 Declaradores de matrices:
El tipo de elemento no debe ser un tipo incompleto o de función.
En caso de double weight[][]
el tipo de elemento es double[]
, un tipo incompleto (matriz), que no se puede declarar en ninguna parte, parámetro o no. Debido a que esta regla de declaración de matrices se aplica antes que la regla de “desintegración de matrices” de parámetros de funciones, que se encuentra en C17 6.7.6.3/7 Declaradores de funciones:
Una declaración de un parámetro como ”matriz de tipo” se ajustará a ”puntero calificado a tipo”
Esas reglas asumen que ya tenemos una declaración de la matriz, lo que debería hacerse de acuerdo con la regla 6.7.6.2 citada anteriormente.
En el caso de una matriz de una sola dimensión double[]
entonces este es un tipo de matriz incompleta pero el tipo de elemento es double
, que es un tipo completo. Tal declaración de matriz está permitida de acuerdo con C17 6.7.6.2/4:
Si el tamaño no está presente, el tipo de matriz es un tipo incompleto.
Siempre que se utilice una matriz de este tipo con una lista de inicializadores, double foo[] = { 1.0f };
luego C17 6.7.9/22 establece que se le da un tamaño dependiendo de los inicializadores y se convierte en un tipo completo al final de la declaración:
Si se inicializa una matriz de tamaño desconocido, su tamaño está determinado por el elemento indexado más grande con un inicializador explícito. El tipo de matriz se completa al final de su lista de inicializadores.
Si no se inicializa, sino que simplemente forma parte de una lista de parámetros de función, entonces se aplica la regla mencionada anteriormente de “desintegración de matriz” y double[]
se reemplaza con double*
.
Ahora, en caso de que tengamos un parámetro de matriz como double [][3]
entonces es un tipo de matriz incompleta pero el tipo de elemento double [3]
es un tipo de matriz completa, por lo que es una declaración válida. En ese caso, el parámetro se ajusta a un puntero a dicho tipo de elemento, double (*)[3]
. Y esa es la razón por la cual la dimensión de matriz más a la izquierda en una declaración de parámetro de matriz multidimensional puede omitirse; en realidad, no importa qué tamaño escribamos allí.