¿Es & * p válido C, dado que p es un puntero a un tipo incompleto?


¿Es el siguiente ejemplo una unidad de traducción completa válida en C?

struct foo;

struct foo *bar(struct foo *j)
{
    return &*j;
}

struct foo es un tipo incompleto,pero no puedo encontrar una prohibición explícita de desreferenciar un tipo incompleto en el estándar C. En particular, §6.5.3.2 dice:

El operador unario & produce la dirección de su operando. Si el el operando tiene el tipo "type", el resultado tiene el tipo"pointer to type". Si el operando es el resultado de un operador unario *, ni que operador nor el operador & se evalúa y el resultado es como si ambos fueron omitidos, excepto que las restricciones a los operadores todavía aplicar y el resultado no es un lvalue.

El hecho de que el resultado no sea un lvalue no es pertinente - no es necesario que los valores de retorno lo sean. Las restricciones en el operador * son simplemente:

El operando del operador unario * tendrá el tipo puntero.

Y en el operador & están:

El operando de el operador unario & será una función designator, el resultado de un operador [] o unario *, o un lvalue que designa un objeto que no es un campo de bits y no está declarado con el especificador de clase de almacenamiento register.

Ambos están trivialmente satisfechos aquí, por lo que el resultado debería ser equivalente a solo return j;.

Sin embargo, gcc 4.4.5 no compila este código. En su lugar da el siguiente error:

y.c:5: error: dereferencing pointer to incomplete type

¿Es esto un defecto en gcc?

Author: caf, 2011-08-04

3 answers

Sí, creo que es un error. Incluso lvalues de tipos incompletos, por lo que *j, parecen estar permitidos dependiendo del contexto:

6.3.2.1 ... Un lvalue es una expresión con un tipo de objeto tipo incompleto distinto de void

Básicamente esto debería funcionar siempre y cuando no hagas nada con un lvalue tal que necesite saber acerca de la estructura del struct. Así que si no accede al objeto o pregunta sobre su tamaño, esto es legal.

 6
Author: Jens Gustedt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-04 20:59:15

Sí. Los punteros en C suelen tener el mismo tamaño (en algunos sistemas embebidos, pueden ser diferentes). Esto significa que el compilador puede generar el código de ensamblador correcto para esto aunque el tipo sea "desconocido".

Puede usar este enfoque para ocultar completamente la estructura de datos interna hacia el exterior. Use un typedef para declarar un puntero a una estructura y solo declare la estructura en sus archivos de encabezado internos (es decir, los archivos que no forman parte de su API pública).

La razón por qué se queja gcc 4.4.5 es solo eso: Si usa punteros al tipo incompleto fuera de la implementación, debería funcionar. Pero el código es parte de la implementación y aquí, probablemente quieras tener el tipo completo.

 2
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-04 08:59:56

El estándar C99 (ISO/IEC 9899:1999) describe el comportamiento:

§6.5.3.2 Operadores de direcciones e indirectas

El & operador unario devuelve la dirección de su operando. Si el operando tiene tipo"type", el resultado tiene el tipo "puntero a tipo". Si el operando es el resultado de un operador unario * , ni ese operador ni el operador & se evalúan y el resultado es como si ambos fueran omitido, excepto que las restricciones a los operadores siguen siendo aplicables y el el resultado no es un lvalue.

Esto significa que &*j es equivalente a j.

Sin embargo, j se supone que es un puntero a un objeto, y es solo un puntero a un tipo incompleto, como dice GCC 4.4.5.

§6.3.2.3 Punteros

Un puntero a void se puede convertir a o desde un puntero a cualquier objeto incompleto tipo. Un puntero a cualquier tipo incompleto o de objeto se puede convertir en un puntero a void y otra vez; el resultado comparará igual al puntero original.

Tenga en cuenta que distingue entre un tipo de objeto y un tipo incompleto; esto ocurre con frecuencia en el estándar.

Entonces, esta observación en la pregunta es incorrecta:

Ambos están trivialmente satisfechos aquí,

La variable j no es un puntero a un objeto; es un puntero a un tipo incompleta, que no es un objeto.


§6.2.5 Tipos

[...] Tipo ser particionado en tipos de objeto (tipos que describen completamente los objetos), tipos de función (tipos que describen funciones), y tipos incompletos (tipos que describen objetos pero carecen información necesaria para determinar sus tamaños).

 2
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-04 19:42:48