Si bien la advertencia de respuesta: la asignación del tipo de puntero incompatible para la matriz de lista de enlaces , noté que cualquier identificador no declarado precedido con la palabra clave struct
se considera como identificadores declarados hacia adelante.
Por ejemplo, el siguiente programa compila bien:
/* Compile with "gcc -std=c99 -W -Wall -O2 -pedantic %" */ #include <stdio.h> struct foo { struct bar *next; /* Linked list */ }; int main(void) { struct bar *a = 0; struct baz *b = 0; struct foo c = {0}; printf("bar -> %p\n", (void *)a); printf("baz -> %p\n", (void *)b); printf("foo -> %p, %zu\n", (void *)&c, sizeof c); /* Remove %zu if compiling with -ansi flag */ return 0; }
Mi pregunta: ¿Qué regla guía a un compilador de C
para tratar los struct identifier
no declarados como tipos de struct
incompletos declarados hacia adelante?
La Norma dice ( 6.2.5.28 )
Todos los punteros a tipos de estructuras tendrán los mismos requisitos de representación y alineación que los demás.
Esto significa que el compilador sabe cómo representar los punteros a cualquier estructura, incluso aquellas que (todavía) no están definidas.
Su programa trata solo con punteros a tales estructuras , por lo que está bien.
Se describe en 6.2.5 Tipos y 6.7.2.3 Etiquetas.
struct identifier
es un tipo de objeto.
6.2.5 Tipos
- El significado de un valor almacenado en un objeto o devuelto por una función está determinado por el tipo de expresión utilizada para acceder a él. (Un identificador declarado como objeto es la expresión más simple; el tipo se especifica en la declaración del identificador). Los tipos se dividen en tipos de objetos (tipos que describen objetos) y tipos de funciones (tipos que describen funciones). En varios puntos dentro de una unidad de traducción, un tipo de objeto puede estar incompleto (sin suficiente información para determinar el tamaño de los objetos de ese tipo) o completo (con suficiente información). 37)
37) Un tipo puede estar incompleto o completo a lo largo de toda una unidad de traducción, o puede cambiar de estado en diferentes puntos dentro de una unidad de traducción.
- Un tipo de matriz de tamaño desconocido es un tipo incompleto. Se completa, para un identificador de ese tipo, especificando el tamaño en una declaración posterior (con vinculación interna o externa). Un tipo de estructura o unión de contenido desconocido (como se describe en 6.7.2.3) es un tipo incompleto. Se completa, para todas las declaraciones de ese tipo, declarando la misma estructura o etiqueta de unión con su contenido definitorio más adelante en el mismo ámbito.
6.7.2.3 Etiquetas
- Todas las declaraciones de estructura, unión o tipos enumerados que tienen el mismo alcance y usan la misma etiqueta declaran el mismo tipo. Independientemente de si hay una etiqueta o qué otras declaraciones del tipo están en la misma unidad de traducción, el tipo es incompleto 129) hasta inmediatamente después de la llave de cierre de la lista que define el contenido, y completo a partir de entonces.
129) Sólo se puede utilizar un tipo incompleto cuando no se necesita el tamaño de un objeto de ese tipo. No es necesario, por ejemplo, cuando se declara un nombre typedef para ser un especificador de una estructura o unión, o cuando se declara un puntero o una función que devuelve una estructura o unión. (Véanse los tipos incompletos en 6.2.5.) La especificación tiene que estar completa antes de llamar o definir dicha función.
Además de la respuesta proporcionada por 2501, y su comentario de que " En mi caso, ni siquiera existe la declaración directa ", lo siguiente.
Cualquier uso de una struct tag
cuenta como una declaración (hacia adelante) del tipo de estructura, si no se había declarado antes. Aunque una forma más formal sería decir que esto simplemente cuenta como un tipo, ya que el estándar C no menciona "declaraciones hacia adelante de tipos de estructura", solo tipos de estructura completos e incompletos (6.2.5p22) .
6.7.2 Especificadores de tipo nos dice que un especificador de estructura o unión es un especificador de tipo , y 6.7.2.1 Especificadores de estructura y unión, el párrafo 1 nos dice que a su vez identificador de estructura es un especificador de estructura o unión .
Supongamos que tiene una declaración de lista enlazada, algo así como
struct node { struct node *next; int element; };
entonces la "declaración directa implícita" de este tipo incompleto es esencial para que esta estructura funcione. Después de todo, el struct node
de tipo solo está completo en el punto y coma final. Pero debe consultarlo para declarar el next
puntero.
Además, una declaración de struct node
(de tipo incompleto) puede quedar fuera del alcance, como cualquier otra declaración. Esto sucede, por ejemplo, si tiene algún prototipo
int function(struct unknown *parameter);
donde la struct unknown
queda fuera del alcance inmediatamente al final de la declaración. Cualquier otro struct unknown
s declarado no es el mismo que este. Eso está implícito en el texto de 6.2.5p22 :
Un tipo de estructura o unión de contenido desconocido (como se describe en 6.7.2.3) es un tipo incompleto. Se completa, para todas las declaraciones de ese tipo, declarando la misma estructura o etiqueta de unión con su contenido definitorio más adelante en el mismo ámbito .
Es por eso que gcc advierte sobre esto:
foo.c:1:21: warning: 'struct unknown' declared inside parameter list foo.c:1:21: warning: its scope is only this definition or declaration, which is probably not what you want
Puede solucionar esto colocando una declaración de avance adicional antes, lo que hace que el alcance comience antes (y, por lo tanto, finalice más tarde):
struct unknown; int function(struct unknown *parameter);