Confusion entre déclaration et définition d’une variable en C

Je suis nouveau en C et je ressens une certaine confusion entre la déclaration et la définition d’une variable. Une autre chose que j’aimerais savoir, c’est si ce qui suit est vrai:

“La déclaration apparaît plusieurs fois et la définition vient une fois.”

Également:

int x; 

Est-ce une déclaration seulement? Puisque la mémoire est allouée pour x alors pourquoi n’est-ce pas une définition au lieu d’une déclaration?

Ce n’est pas quelque chose que vous voyez trop en C, mais cela fonctionne comme ceci:

Dans un fichier d’en-tête, vous pouvez avoir une ligne comme celle-ci:

 extern int x; //declaration 

En raison du modificateur extern , cela indique au compilateur qu’il existe un int nommé x quelque part . Le compilateur ne lui alloue pas d’espace – il ajoute simplement int x à la liste des variables que vous pouvez utiliser. Cela n’allouera de l’espace pour x lorsqu’il verra une ligne comme celle-ci:

 int x; //definition 

Vous pouvez voir cela car seul l’ int x; Si la ligne change votre exécutable, vous pouvez avoir autant d’ extern int x; lignes comme vous vous sentez. Tant qu’il n’y a que int x; tout fonctionnera comme vous le souhaitez – avoir plusieurs déclarations ne change rien.

Un meilleur exemple vient de C ++ (désolé, s’il s’agit d’une question en C uniquement – cela s’applique également aux struct , mais je ne connais pas la syntaxe par cœur):

 class Pineapple; //declaration Pineapple* ptr; //this works Pineapple pine; //this DOES NOT work 

Cette déclaration indique au compilateur qu’il existe une classe appelée “Ananas”. Cela ne nous dit rien sur la classe (quelle est sa taille, quels sont ses membres). Nous pouvons utiliser des pointeurs sur Ananas maintenant, mais nous ne pouvons pas encore avoir d’instances – nous ne soaps pas ce qui constitue un ananas, nous ne soaps donc pas combien d’espace une instance prend.

 class Pineapple { public: int ounces; char* name; }; //definition Pineapple* ptr; //still works Pineapple pine; //this works now too! //we can even get at member variables, 'cause we know what they are now: pine.ounces = 17; 

Après une définition , nous soaps tout sur la classe, nous pouvons donc avoir des instances également. Et comme dans l’exemple C, vous pouvez avoir plusieurs déclarations, mais une seule définition.

J’espère que cela t’aides!

Écrivant simplement int x; au niveau global ou local, est à la fois une déclaration et une définition. En règle générale, la déclaration indique au compilateur “Cette variable existera à un moment donné sous ce nom afin que vous puissiez l’utiliser.” La définition demande au compilateur de s’organiser pour que la variable soit créée – cela ne peut évidemment se produire qu’une fois.

En général, vous utiliserez ceci dans un fichier d’en-tête:

 // Foo.h #ifndef FOO_H #define FOO_H // make sure structs aren't redefined extern int bar; // Declare a variable bar #endif 

Et dans un seul fichier source

 #include "foo.h" int bar; // Define bar 

Si vous définissez la barre dans plusieurs fichiers, vous obtiendrez une erreur. vous ne pouvez pas créer la variable deux fois. Mais vous devez en informer le compilateur dans chaque fichier source dans lequel vous utilisez bar . D’où la déclaration extern .

La sémantique précise est définie au §6.9.2 de la norme C et peut être résumée comme suit:

  • Lorsqu’une variable est déclarée à la scope du fichier avec un initialiseur, il s’agit d’une définition externe . (§6.9.2 / 1)
  • Lorsqu’une variable est déclarée à la scope du fichier sans initialiseur, sans spécificateur de classe de stockage ou avec le spécificateur de classe de stockage static , il s’agit d’une définition provisoire . Si l’unité de traduction (fichier) a une ou plusieurs définitions provisoires et aucune définition externe , le compilateur ajoute automatiquement une déclaration de scope de fichier vrai à la fin de l’unité de traduction, avec un initialiseur zéro. (§6.9.2 / 2)

Cela signifie que, à proprement parler, int x; n’est pas une définition; mais il crée automatiquement une définition si et seulement s’il n’y a pas d’autre définition avec un initialiseur, et pas de définition static (ce troisième cas est un comportement indéfini en raison d’un désaccord de liaison conformément au § 6.2.2 / 7).

Notez que extern int x; n’est pas une définition externe . C’est une déclaration avec un spécificateur de classe de stockage extern . En tant que tel, extern int x; seul ne crée pas de définition, mais si vous avez les deux:

 extern int x; int x; 

Ensuite, vous finirez par avoir une définition créée à un moment donné du fichier.

Techniquement parlant, il est également légal de le faire:

 extern int x; int x; int x = 42; 

Dans ce cas, l’ int x; au milieu est superflu et n’a aucun effet. Cela dit, cette forme est médiocre, car cela prête à confusion dans le cas présent où la définition est.