C función const multidimensional-array argumento extraña advertencia
multidimensional array c++ (6)
Ehllo,
Recibo una advertencia extraña sobre este código:
typedef double mat4[4][4];
void mprod4(mat4 r, const mat4 a, const mat4 b)
{
/* yes, function is empty */
}
int main()
{
mat4 mr, ma, mb;
mprod4(mr, ma, mb);
}
salida de gcc de la siguiente manera:
$ gcc -o test test.c
test.c: In function ''main'':
test.c:13: warning: passing argument 2 of ''mprod4'' from incompatible pointer
type
test.c:4: note: expected ''const double (*)[4]'' but argument is of type ''double
(*)[4]''
test.c:13: warning: passing argument 3 of ''mprod4'' from incompatible pointer
type
test.c:4:
note: expected ''const double (*)[4]'' but argument is of type ''double
(*)[4]''
definiendo la función como:
void mprod4(mat4 r, mat4 a, mat4 b)
{
}
O definiendo matrices en main como:
mat4 mr;
const mat4 ma;
const mat4 mb;
O llamando a la función en main como:
mprod4(mr, (const double(*)[4])ma, (const double(*)[4])mb);
O incluso definiendo mat4 como:
typedef double mat4[16];
hacer que la advertencia desaparezca. Wat está pasando aquí? ¿Estoy haciendo algo inválido?
La versión de gcc es 4.4.3 si es relevante.
También publiqué en gcc bugzilla: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47143
Mi solución actual consiste en crear macros feos que arrojan cosas para mí:
#ifndef _NO_UGLY_MATRIX_MACROS
#define mprod4(r, a, b) mprod4(r, (const double(*)[4])a, (const double(*)[4])b)
#endif
Gracias por tu atención.
Respuesta de Joseph S. Myers en gcc bugzilla:
No es un error. Los parámetros de función son del tipo "puntero a array [4] de const double" porque const en un tipo de matriz se aplica al tipo de elemento, recursivamente, y luego el tipo de matriz más externa, solo, de un parámetro del tipo de matriz se descompone en un puntero , y los argumentos pasados son de tipo "puntero a matriz [4] de doble" después del decaimiento de matriz a puntero, y el único caso en el que se permite que los calificadores se agreguen en la asignación, pase de argumento, etc. califica en el puntero inmediato objetivo, no aquellos anidados más profundamente.
Suena bastante confuso para mí, la función de sonido de Liek espera:
pointer to array[4] of const doubles
y estamos pasando
pointer to const array[4] of doubles
intead
¿O sería el inverso? Las advertencias sugieren que la función espera una:
const double (*)[4]
que me parece más parecido a un
pointer to const array[4] of doubles
.
Estoy realmente confundido con esta respuesta, ¿alguien que pueda entender lo que dijo podría aclarar y ejemplificar?
Gracias de nuevo por su atención
Para resolverlo de forma práctica, se podría usar una estructura, y se evita el cambio de double[4][4]
en el double (*)[4]
incómodo a-bit double (*)[4]
, y la constness también funciona intuitivamente, mientras que la misma cantidad de la memoria se usa:
struct mat4 { double m[4][4]; }; void myfunc(struct mat4 *r, const struct mat4 *a, const struct mat4 *b) { } int main(void) { struct mat4 mr, ma, mb; myfunc(&mr, &ma, &mb); }
Aquí hay un problema (en mi humilde opinión): double[4][4]
en una firma de función.
Sabes que es un double[4][4]
, pero el compilador ve el double(*)[4]
en la lista de parámetros de funciones, que notablemente no tiene restricción de tamaño de matriz. Convierte tu matriz 2D de 4 por 4 objetos en un puntero a una matriz 1D de 4 objetos, y el puntero puede indexarse de manera válida como si fuera una matriz de 4 objetos.
Pasaría todos los objetos mat4
por el puntero:
void mprod4(mat4 *r, const mat4 *a, const mat4 *b);
// and, if you don''t want to hairy your syntax
#define mprod4(r, a, b) (mprod4)(&r, (const mat4 *)&a, (const mat4 *)&b)
Esto (creo) asegurará la exactitud de la const y la corrección del tamaño de la matriz. Puede hacer que mprod4
un poco más difícil de escribir, y todavía implica algunos lanzamientos peludos, pero valdrá la pena (en mi humilde opinión) (especialmente después de la macro anterior):
void mprod4(mat4 *r, const mat4 *a, const mat4 *b)
{
// all indexing of the matricies must be done after dereference
for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++)
{
(*r)[i][j] = (*a)[i][j] * (*b)[i][j];
// you could make it easier on yourself if you like:
#define idx(a, i, j) ((*a)[i][j])
idx(r, i, j) = idx(a, i, j) * idx(b, i, j)
}
}
Puede que se vea un poco mal cuando lo escribas, pero creo que será más limpio en cuanto a los tipos de letra. (Tal vez he estado pensando demasiado en C ++ ...)
Creo que el problema son las restricciones especificadas en C99 6.5.16.1 (1) , que parecen prohibir la mezcla de calificaciones en las asignaciones, excepto los punteros para los que se define una excepción de calificador inclusivo. El problema es que con punteros indirectos, termina pasando un puntero a una cosa a un puntero a otro. La asignación no es válida porque, si lo fuera, podría engañarlo para que modifique un objeto const calificado con el siguiente código:
const char **cpp;
char *p;
const char c = ''A'';
cpp = &p; // constraint violation
*cpp = &c; // valid
*p = 0; // valid by itself, but would clobber c
Puede parecer razonable que a cpp
, que promete no modificar ningún char
, se le pueda asignar un puntero a un objeto que apunta a caracteres no calificados. Después de todo, eso está permitido para punteros simples indirectos, por lo que, por ejemplo, puede pasar un objeto mutable al segundo parámetro de strcpy(3)
, el primer parámetro a strchr(3)
y muchos otros parámetros que se declaran con const
.
Pero con el puntero indirecto, en el siguiente nivel, se permite la asignación desde un puntero calificado, y ahora una asignación de puntero perfectamente descalificada derrotará a un objeto calificado.
No veo de inmediato cómo una matriz 2-D podría llevar a esta situación, pero en cualquier caso golpea la misma restricción en el estándar.
Como en su caso, en realidad no está engañando para destruir un conflicto, lo correcto para su código parece ser insertar el elenco.
Actualización: OK chicos, sucede que este problema está en C faq , y toda esta discusión también ha tenido lugar varias veces en la lista de errores gcc y en la lista de correo gcc.
- Lista de errores de Gcc: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=20230 .
- Preguntas más frecuentes de C: es la pregunta 11.10: http://c-faq.com/ansi/constmismatch.html
La lección: puede pasar un T *x
cuando se espera const T *x
, por excepción explícita, pero T *x
y const T *x
todavía son tipos distintos, por lo que no puede pasar un puntero a ninguno a un puntero al otro.
Creo que en C99, puedes hacer esto, pero no estoy seguro de que sea útil:
void mprod4(double mr[4][4], double ma[const 4][const 4], double mb[const 4][const 4])
{
}
No tengo un compilador C99 a mano, pero recuerdo haber leído algo en la especificación C99 con respecto a los calificadores dentro del []
para matrices como argumentos. También puede poner static
allí (por ejemplo, ma[static 4]
) pero, por supuesto, eso significa algo más.
Editar
Aquí está, sección 6.7.3.5 párrafo 7.
Una declaración de un parámetro como "matriz de tipo" se ajustará a "indicador calificado para escribir", donde los calificadores de tipo (si los hay) son los especificados dentro de
[
y]
de la derivación de tipo de matriz. Si la palabra clavestatic
también aparece dentro de[
y]
de la derivación del tipo de matriz, entonces para cada llamada a la función, el valor del argumento real correspondiente proporcionará acceso al primer elemento de una matriz con al menos tantos elementos como se especifique por la expresión de tamaño.
El compilador solo está siendo anal.
Está pasando un argumento que es esencialmente un puntero no const, y la función se declara para aceptar un puntero const como argumento. Estos dos son, de hecho, incompatibles. No es un problema real porque se supone que el compilador debe funcionar siempre que pueda asignar el valor del primer tipo a la variable del segundo tipo. De ahí una advertencia pero no un error.
EDITAR: parece que gcc no se queja de otras conconst para generar conversiones, por ejemplo, pasando char * donde se espera un const char *. En este caso, me inclino a aceptar que Joseph Myers de Bugzilla es correcto.
Para explicar lo que dijo Joseph: la función espera que pase un pointer to array[4] of const double
, pero está pasando un pointer to array[4] of double
. Estos tipos no son compatibles, por lo que se obtiene un error. Parece que deberían ser compatibles, pero no lo son.
A los efectos de pasar parámetros a funciones (o asignaciones de variables), siempre puede convertir una X
en una const X
, o un pointer to X
en un pointer to const X
para cualquier tipo X
Por ejemplo:
int x1 = 0;
const int x2 = x1; // ok
int *x3 = &x1;
const int *x4 = x3; // ok: convert "pointer to int" to "pointer to const int"
int **x5 = &x3;
const int **x6 = x5; // ERROR: see DigitalRoss''s answer
int *const *x7 = x5; // ok: convert "pointer to (pointer to int)" to
// "pointer to const (pointer to int)"
Solo se le permite agregar calificadores (es decir, const
, volatile
y restrict
calificadores) al primer nivel de los punteros. No puede agregarlos a niveles más altos de punteros porque, como mencionó DigitalRoss , hacerlo le permitiría violar accidentalmente la corrección de const. Esto es lo que José quiere decir con "el único caso en el que se permite que los calificadores se agreguen en la asignación, el pase de los argumentos, etc. son calificadores en el objetivo inmediato del apuntador, no aquellos anidados más profundamente".
Entonces, volviendo a la respuesta de Joseph, no puede convertir un pointer to array[4] of double
a pointer to array[4] of const double
porque no hay tipo X
tal que está convirtiendo de pointer to X
para pointer to const X
Si intentas usar array[4] of double
para X
, verás que puedes convertir a pointer to const array[4] of double
, que es un tipo diferente. Sin embargo, no existe tal tipo en C: puede tener una matriz de tipo const
, pero no existe una const array
.
Por lo tanto, no hay forma de resolver perfectamente tu problema. Tendrá que agregar moldes a todas sus llamadas a funciones (ya sea manualmente o mediante una función de macro o de ayuda), reescribir sus funciones para no tomar los parámetros de const
(malo ya que no le permite pasar en matrices de const
), o cambie el tipo de mat4
para que sea una matriz de 1 dimensión o una estructura, como se sugirió user502515 .