J'ai ce programme simple:
#include <stdio.h>
struct S
{
int i;
};
void swap(struct S *a, struct S *b)
{
struct S temp;
temp = *a /* Oops, missing a semicolon here... */
*a = *b;
*b = temp;
}
int main(void)
{
struct S a = { 1 };
struct S b = { 2 };
swap(&a, &b);
}
Comme vu par exemple sur ideone.com, cela donne une erreur:
prog.c: In function 'swap': prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *') *a = *b; ^
Pourquoi le compilateur ne détecte-t-il pas le point-virgule manquant?
Remarque: Cette question et sa réponse sont motivées par cette question . Bien qu'il y ait d' autres questions similaires à celle-ci, je n'ai rien trouvé mentionnant la capacité de forme libre du langage C qui est à l'origine de cela et des erreurs associées.
Réponses:
C est un langage de forme libre . Cela signifie que vous pouvez le formater de plusieurs façons et que ce sera toujours un programme légal.
Par exemple, une déclaration comme
pourrait être écrit comme
ou comme
Donc, quand le compilateur voit les lignes
ça pense que ça veut dire
Ce n'est bien sûr pas une expression valide et le compilateur s'en plaindra au lieu du point-virgule manquant. La raison pour laquelle ce n'est pas valide est parce que
a
c'est un pointeur vers une structure, donc*a * a
essaie de multiplier une instance de structure (*a
) avec un pointeur vers une structure (a
).Bien que le compilateur ne puisse pas détecter le point-virgule manquant, il signale également l'erreur totalement indépendante sur la mauvaise ligne. Ceci est important à noter car peu importe combien vous regardez la ligne où l'erreur est signalée, il n'y a pas d'erreur. Parfois, des problèmes comme celui-ci vous obligeront à regarder les lignes précédentes pour voir si elles sont correctes et sans erreurs.
Parfois, vous devez même chercher dans un autre fichier pour trouver l'erreur. Par exemple, si un fichier d'en-tête définit une structure la dernière fois qu'il le fait dans le fichier d'en-tête et que le point-virgule terminant la structure est manquant, l'erreur ne sera pas dans le fichier d'en-tête mais dans le fichier qui comprend le fichier d'en-tête.
Et parfois, cela devient encore pire: si vous incluez deux (ou plus) fichiers d'en-tête et que le premier contient une déclaration incomplète, l'erreur de syntaxe sera très probablement indiquée dans le deuxième fichier d'en-tête.
Le concept des erreurs de suivi est lié à cela . Certaines erreurs, généralement dues à des points-virgules manquants, sont signalées comme des erreurs multiples . C'est pourquoi il est important de commencer par le haut lors de la correction des erreurs, car la correction de la première erreur peut faire disparaître plusieurs erreurs.
Cela peut bien sûr conduire à corriger une erreur à la fois et à des recompilations fréquentes, ce qui peut être fastidieux avec de grands projets. Cependant, reconnaître de telles erreurs de suivi est quelque chose qui vient avec l'expérience, et après les avoir vues plusieurs fois, il est plus facile de déterrer les vraies erreurs et de corriger plus d'une erreur par recompilation.
la source
temp = *a * a = *b
pourrait être une expression valide si ellesoperator*
étaient surchargées. (La question est étiquetée «C», cependant.)Il y a trois choses à retenir.
*
en C peut être à la fois un opérateur unaire et un opérateur binaire. En tant qu'opérateur unaire, cela signifie «déréférencer», en tant qu'opérateur binaire, cela signifie «multiplier».Le résultat de ces deux faits est lorsque nous analysons.
Le premier et le dernier
*
sont interprétés comme unaires mais le second*
est interprété comme binaire. Du point de vue de la syntaxe, cela semble correct.Ce n'est qu'après l'analyse que le compilateur essaie d'interpréter les opérateurs dans le contexte de leurs types d'opérandes qu'une erreur se produit.
la source
Quelques bonnes réponses ci-dessus, mais je vais élaborer.
C'est en fait un cas
x = y = z;
où les deuxx
et sey
voient attribuer la valeur dez
.Ce que vous dites est
the contents of address (a times a) become equal to the contents of b, as does temp
.En bref,
*a *a = <any integer value>
est une déclaration valide. Comme indiqué précédemment, le premier*
déréférence un pointeur, tandis que le second multiplie deux valeurs.la source
y
n'est même pas une variable, c'est l'expression*a *a
, et vous ne pouvez pas attribuer le résultat d'une multiplication.La plupart des compilateurs analysent les fichiers sources dans l'ordre et signalent la ligne où ils découvrent que quelque chose n'allait pas. Les 12 premières lignes de votre programme C pourraient être le début d'un programme C valide (sans erreur). Les 13 premières lignes de votre programme ne le peuvent pas. Certains compilateurs noteront l'emplacement des choses qu'ils rencontrent qui ne sont pas des erreurs en eux-mêmes et, dans la plupart des cas, ne déclencheront pas d'erreurs plus tard dans le code, mais pourraient ne pas être valides en combinaison avec autre chose. Par exemple:
La déclaration
int foo;
en elle-même serait parfaitement bien. De même la déclarationfloat foo;
. Certains compilateurs peuvent enregistrer le numéro de ligne où la première déclaration est apparue, et associer un message d'information à cette ligne, pour aider le programmeur à identifier les cas où la définition précédente est en fait la définition erronée. Les compilateurs peuvent également conserver les numéros de ligne associés à quelque chose comme ado
, qui peut être signalé si le associéwhile
n'apparaît pas au bon endroit. Cependant, pour les cas où l'emplacement probable du problème précède immédiatement la ligne où l'erreur est découverte, les compilateurs ne prennent généralement pas la peine d'ajouter un rapport supplémentaire pour la position.la source