Pointeur de déréférencement sur le type incomplet

J’ai vu beaucoup de questions à ce sujet mais je vais poser la question différemment sans code spécifique. Existe-t-il un moyen de déterminer facilement ce qui rend le type incomplet? Dans mon cas, j’utilise le code de quelqu’un d’autre et je suis tout à fait sûr de ne pas avoir les en-têtes corrects, mais (puisque les ordinateurs le font beaucoup plus rapidement et mieux que les globes oculaires humains) “Hé, tu penses que tu as le type X à la ligne 34 mais ça manque en fait.” L’erreur elle-même n’apparaît que lorsque vous atsortingbuez, ce qui n’est pas très utile.

J’ai vu une question l’autre jour où quelqu’un a utilisé par inadvertance un type incomplet en spécifiant quelque chose comme struct a { int q; }; struct A *x; x->q = 3; struct a { int q; }; struct A *x; x->q = 3; . Le compilateur savait que struct A était une structure, bien que A soit totalement indéfini, grâce au mot struct clé struct .

C’était en C ++, où un tel usage de struct est atypique (et, en fin de compte, peut conduire à une prise de vue). En C si vous le faites

 typedef struct a { ... } a; 

alors vous pouvez utiliser a comme nom de fichier et omettre la struct plus tard. Cela conduira le compilateur à vous donner une erreur d’identifiant non définie plus tard, plutôt qu’un type incomplet, si vous avez mal saisi le nom ou oublié un en-tête.

Que voulez-vous dire, l’erreur ne s’affiche que lorsque vous atsortingbuez? Par exemple sur GCC, sans cession en vue:

 int main() { struct blah *b = 0; *b; // this is line 6 } 

incompletetype.c:6: error: dereferencing pointer to incomplete type .

L’erreur est à la ligne 6, c’est là que j’ai utilisé un type incomplet comme s’il s’agissait d’un type complet. J’étais bien jusque-là.

L’erreur est que vous devriez avoir inclus n’importe quel en-tête qui définit le type. Mais le compilateur ne peut probablement pas deviner quelle ligne aurait dû être incluse: n’importe quelle ligne en dehors d’une fonction serait correcte, à peu près. Il ne va pas non plus parcourir tous les fichiers texte de votre système en recherchant un en-tête qui le définisse et vous suggérer de l’inclure.

Alternativement (bon point, potatoswatter), l’erreur est à la ligne où b été défini, quand vous vouliez spécifier un type qui existe réellement, mais qui spécifiait réellement blah . Trouver la définition de la variable b ne devrait pas être trop difficile dans la plupart des cas. Les IDE peuvent généralement le faire pour vous, les avertissements du compilateur ne peuvent peut-être pas être dérangés. C’est un code assez odieux, cependant, si vous ne trouvez pas les définitions des choses que vous utilisez.

Je ne comprends pas exactement quel est le problème. Le type incomplet n’est pas le type “manquant”. Le type d’incompétence est un type déclaré mais non défini (en cas de type de structure). Il est facile de trouver la déclaration sans définition. En ce qui concerne la recherche de la définition manquante … le compilateur ne vous aidera pas ici, car c’est ce qui a causé l’erreur en premier lieu.

Une des raisons principales des erreurs de type incomplètes dans C sont les fautes de frappe dans les noms de type, qui empêchent le compilateur de faire correspondre un nom à un autre (comme pour faire correspondre la déclaration à la définition). Mais encore une fois, le compilateur ne peut pas vous aider ici. Le compilateur ne fait pas de suppositions sur les fautes de frappe.

Une autre raison possible est la référence indirecte. Si un code fait référence à une structure qui ne figure pas dans le fichier c actuel, le compilateur se plaindra.

a-> b-> c // erreur si b non inclus dans le fichier c actuel

cette erreur montre généralement si le nom de votre structure est différent de l’initialisation de votre structure dans le code, donc normalement, c trouvera le nom de la structure que vous avez mise et si la structure d’origine n’est pas trouvée, cela apparaîtrait généralement, ou Si vous pointez un pointeur sur ce pointeur, l’erreur apparaîtra.

Une solution

Parlant pour le langage C, je viens de trouver de manière empirique que le code de déclaration suivant sera la solution;

 typedef struct ListNode { int data; ListNode * prev; ListNode * next; } ListNode; 

Donc, en règle générale, je donne le même nom à la fois pour la définition du type et pour le nom de la structure;

 typedef struct X { // code for additional types here X* prev; // reference to pointer X* next; // reference to pointer } X; 

B – Échantillons problématiques

Lorsque les déclarations suivantes sont considérées comme incomplètes par le compilateur gcc lors de l’exécution de l’instruction suivante. ;

 removed->next->prev = removed->prev; 

Et j’obtiens la même erreur pour le code de déréférencement signalé dans la sortie d’erreur;

 >gcc Main.c LinkedList.c -o Main.exe -w LinkedList.c: In function 'removeFromList': LinkedList.c:166:18: error: dereferencing pointer to incomplete type 'struct ListNode' removed->next->prev = removed->prev; 

Pour les deux déclarations de fichier d’en-tête répertoriées ci-dessous;

 typedef struct { int data; ListNode * prev; ListNode * next; } ListNode; 

Plus celui-ci

 typedef struct ListNodeType { int data; ListNode * prev; ListNode * next; } ListNode; 

En dehors des scénarios possibles impliquant une optimisation complète du programme, le code de code généré pour quelque chose comme:

 struct foo *bar; struct foo *test(struct foo *whatever, int blah) { return blah ? whatever: bar; } 

sera totalement insensible à ce que les membres de struct foo pourraient contenir. Étant donné que les utilitaires make recomstacknt généralement toute unité de compilation dans laquelle la définition complète d’une structure apparaît, même lorsque ces modifications ne peuvent pas réellement affecter le code généré, il est fréquent d’omettre les définitions de structure complètes des unités de compilation qui n’ont pas besoin eux, et une telle omission n’est généralement pas digne d’un avertissement.

Un compilateur doit avoir une structure complète ou une définition d’union pour savoir comment gérer les objects de déclaration du type à durée automatique ou statique, les déclarations d’agrégats contenant des membres du type ou le code qui accède aux membres de la structure ou de l’union. Si le compilateur ne dispose pas des informations nécessaires pour effectuer l’une des opérations ci-dessus, il n’aura pas d’autre choix que d’en parler.

Incidemment, il y a une autre situation où le standard permettrait à un compilateur d’exiger une définition d’union complète pour être visible, mais ne nécessiterait pas de diagnostic: si deux structures commencent par une séquence initiale commune et qu’un type d’union contenant les deux est visible est le code de traitement qui utilise un pointeur de l’un des types de structure pour inspecter un membre de cette séquence initiale commune, le compilateur doit reconnaître que ce code peut accéder au membre correspondant d’une structure de l’autre type. Je ne sais pas quels compilateurs, le cas échéant, sont conformes à la norme lorsque le type d’union complet est visible, mais pas quand ce n’est pas le cas [gcc est enclin à générer du code non conforme à moins que l’indicateur -fno-ssortingct-aliasing used, auquel cas il génèrera du code conforme dans les deux cas] mais si l’on veut écrire du code qui utilise la règle CIS de manière à garantir un comportement correct sur des compilateurs conformes, il faut s’assurer que la définition complète du type d’union est visible; Si vous ne le faites pas, le compilateur risque de générer un code factice en mode silencieux.