c arrays pointers gcc incomplete-type

Equivalencia de p[0] y*p para tipos de matriz incompletos



arrays pointers (4)

Mi C está un poco oxidada, pero mi lectura es que cuando tienes un int (*p)[] esto:

(*p)[n]

Dice "desreferencia p para obtener una serie de entradas, luego tome la enésima". Lo que, naturalmente, parece estar bien definido. Considerando esto:

p[n][m]

Dice "toma la matriz nth en p, luego toma el mth elemento de esa matriz". Lo que no parece bien definido en absoluto; tienes que saber qué tan grandes son las matrices para encontrar dónde comienza la nth.

Esto podría funcionar para el caso especial específico donde n = 0, porque la 0ª matriz es fácil de encontrar sin importar cuán grandes sean las matrices. Simplemente ha descubierto que GCC no está reconociendo este caso especial. No conozco la especificación del idioma en detalle, así que no sé si eso es un "error" o no, pero mis gustos personales en el diseño del lenguaje son que p[n][m] debería funcionar o no, no que debería funcionar cuando n se sabe estáticamente que es 0 y no de otra manera.

¿Es *p <===> p[0] realmente una regla definitiva de la especificación del lenguaje, o solo una observación? No creo que la desreferenciación y la indexación por cero sean las mismas operaciones cuando estoy programando.

Considere el siguiente código (surgió como resultado de esta discusión ):

#include <stdio.h> void foo(int (*p)[]) { // Argument has incomplete array type printf("%d/n", (*p)[1]); printf("%d/n", p[0][1]); // Line 5 } int main(void) { int a[] = { 5, 6, 7 }; foo(&a); // Line 10 }

GCC 4.3.4 se complains con el mensaje de error:

prog.c: In function ‘foo’: prog.c:5: error: invalid use of array with unspecified bounds

El mismo mensaje de error en GCC 4.1.2, y parece ser invariante de -std=c99 , -Wall , -Wextra .

Así que no está contento con la expresión p[0] , pero está contento con *p , aunque estas (en teoría) deberían ser equivalentes. Si comento en la línea 5, el código compila y hace lo que yo "esperaría" (muestra 6 ).

Presumiblemente uno de los siguientes es cierto:

  1. Mi comprensión de los estándares de C es incorrecta, y estas expresiones no son equivalentes.
  2. GCC tiene un error.

Yo pondría mi dinero en (1).

Pregunta: ¿Alguien puede elaborar sobre este comportamiento?

Aclaración: soy consciente de que esto se puede "resolver" especificando un tamaño de matriz en la definición de la función. Eso no es lo que me interesa.

Para los puntos de "bonificación": ¿Puede alguien confirmar que MSVC 2010 está en error cuando rechaza la línea 10 con el siguiente mensaje?

1><snip>/prog.c(10): warning C4048: different array subscripts : ''int (*)[]'' and ''int (*)[3]''


Para su pregunta de "puntos de bonificación" (probablemente debería haber preguntado esto como una pregunta aparte), MSVC10 está en error. Tenga en cuenta que MSVC solo implementa C89, así que he usado ese estándar.

Para la llamada a la función, C89 §3.3.2.2 nos dice:

Cada argumento debe tener un tipo tal que su valor pueda asignarse a un objeto con la versión no calificada del tipo de su parámetro correspondiente.

Las restricciones para la asignación están en C89 §3.3.16:

Se mantendrá uno de los siguientes: ... ambos operandos son punteros a versiones calificadas o no calificadas de tipos compatibles, y el tipo apuntado por la izquierda tiene todos los calificadores del tipo apuntado por la derecha;

Así que podemos asignar dos punteros (y así llamar a una función con un parámetro de puntero usando un argumento de puntero) si los dos punteros apuntan a tipos compatibles.

La compatibilidad de varios tipos de arreglos se define en C89 §3.5.4.2:

Para que dos tipos de arreglos sean compatibles, ambos deberán tener tipos de elementos compatibles, y si ambos especificadores de tamaño están presentes, tendrán el mismo valor.

Para los dos tipos de matriz int [] e int [3] esta condición se cumple claramente. Por lo tanto la función call es legal.


Sección 6.5.2.1 de n1570, Suscripción de matriz:

Restricciones

Una de las expresiones tendrá el tipo "puntero para completar el tipo de objeto", la otra expresión tendrá un tipo entero y el resultado tendrá el tipo "tipo".

Entonces, el estándar prohíbe la expresión p[0] si p es un puntero a un tipo incompleto. No existe tal restricción para el operador de direccionamiento indirecto * .

Sin embargo, en versiones anteriores / borradores de la norma (n1256 y C99), la palabra "completo" está ausente en ese párrafo. Al no estar involucrado de ninguna manera en el procedimiento estándar, solo puedo adivinar si se trata de un cambio importante o la corrección de una omisión. El comportamiento del compilador sugiere lo último. Esto se ve reforzado por el hecho de que p[i] es por el estándar idéntico a *(p + i) y la última expresión no tiene sentido para un puntero a un tipo incompleto, así que para que p[0] funcione si p es un puntero a un tipo incompleto, se necesitaría un caso especial explícito.


void foo(int (*p)[]) { printf("%d/n", (*p)[1]); printf("%d/n", p[0][1]); // Line 5 }

Aquí, p es un puntero a una matriz de un número no especificado de int s. *p accede a esa matriz, por lo que (*p)[1] es el segundo elemento de la matriz.

p[n] agrega p y n veces el tamaño de la matriz apuntada a, lo cual es desconocido. Incluso antes de considerar el [1] , está roto. Es cierto que cero veces cualquier cosa sigue siendo 0, pero el compilador obviamente comprueba la validez de todos los términos sin cortocircuito tan pronto como vea cero. Asi que...

Así que no está contento con la expresión p [0], pero está contento con * p, aunque estas (en teoría) deberían ser equivalentes.

Como se explicó, claramente no son equivalentes ... piense en p[0] como p + 0 * sizeof *p y es obvio por qué ...

Para los puntos de "bonificación": ¿Puede alguien confirmar que MSVC 2010 está en error cuando rechaza la línea 10 con el siguiente mensaje? 1> / prog.c (10): advertencia C4048: subíndices de matriz diferente: ''int ( ) []'' e ''int ( ) [3]''

Visual C ++ (y otros compiladores) son libres de advertir sobre cosas que ellos piensan que no son una buena práctica, cosas que empíricamente se consideran erróneas, o cosas que los escritores de compiladores simplemente tienen una desconfianza irracional, incluso si son enteramente legal con respecto a la Norma ... Los ejemplos que pueden ser familiares incluyen "comparar firmado y sin firmar" y "asignación dentro de un condicional (se sugiere rodear con paréntesis adicionales)"