Déclaration ou définition en C

Wiki des variables externes :

Si ni le mot-clé extern ni une valeur d’initialisation ne sont présents, l’instruction peut être une déclaration ou une définition. Il appartient au compilateur d’parsingr les modules du programme et de décider.

Je n’ai pas été en mesure de bien comprendre le sens de cette affirmation en ce qui concerne C.

int i; 

n’est pas nécessairement une déclaration (comme je le supposais jusqu’à présent), mais pourrait aussi être une définition (par définition de Définition & Déclaration sur la même page Web, aucun jeu de mots n’est prévu)?

En un mot, voici la déclaration ci-dessus: a. juste une déclaration, ou b. déclaration + définition?

Référence: déclaration et définition de variable

Résumé des réponses reçues:

  Declaration Definition Tentative Definition Initialized int i; (inside a block) Yes Yes No No int i=5; (inside a block) Yes Yes No Yes(to 5) int i; (otherwise) Yes No Yes Yes(to 0) extern int i; Yes No No No All definitions are declarations but not vice-versa. 

En supposant que c’est à la scope du fichier, c’est une “définition provisoire”. À partir de 6.9.2 / 2 “Définitions d’objects externes”:

La déclaration d’un identifiant pour un object ayant une scope de fichier sans initialiseur, et sans spécificateur de classe de stockage ou avec le spécificateur de classe de stockage static, constitue une définition provisoire. Si une unité de traduction contient une ou plusieurs définitions provisoires pour un identificateur et que l’unité de traduction ne contient aucune définition externe pour cet identificateur, le comportement est exactement comme si l’unité de traduction contenait une déclaration de la scope du fichier de cet identificateur, avec le type composite as de la fin de l’unité de traduction, avec un initialiseur égal à 0.

Cela signifie qu’il serait également possible d’avoir les éléments suivants dans l’unité de traduction:

 int i = 42; 

puisque cette déclaration a un initialiseur explicite, c’est la définition de la variable i .

Dans la mesure où la déclaration se trouve dans une scope de bloc, la norme indique ce qui suit (6.2.2 / 2 “Liens d’identificateurs”):

Chaque déclaration d’un identifiant sans lien dénote une entité unique.

(paragraphe 6) Les identificateurs suivants n’ont pas de lien: … un identificateur d’étendue de bloc pour un object déclaré sans le spécificateur de classe de stockage extern.

Donc, dans la scope du bloc, la déclaration serait également une définition.

La norme C dit que

Une définition d’un identifiant est une déclaration pour cet identifiant qui: pour un object, entraîne la réservation du stockage pour cet object (…)

Les définitions englobent les déclarations, c’est-à-dire que chaque définition est nécessairement une déclaration, il n’a donc aucun sens de dire que

 int i; 

n’est pas une déclaration. C’est une déclaration qui se trouve être aussi une définition. Ou, c’est une définition, donc une déclaration.

Dans le contexte des variables:

  • Une déclaration de variable est une instruction décrivant l’apparence de cette variable. Alors:

     extern int x; 

    dans la scope globale se traduit par: “quelque part dans le code, il existe une variable appelée x qui a le type int et le lien externe. Une déclaration est nécessaire avant de pouvoir jamais faire référence à x .

  • Une définition est une instruction qui crée une instance de cette variable. Alors:

     int x; 

    dans la scope globale crée une seule variable de type int avec une liaison externe. Donc, si vous mettez cette ligne dans un en-tête, chaque unité de traduction incluant cet en-tête essaiera de créer sa propre copie de x , ce qui n’est pas souhaitable – c’est pourquoi nous avons uniquement des déclarations dans les fichiers d’en-tête. Il en va de même pour les fonctions: si vous fournissez le corps de la fonction, c’est une définition.

De plus, formellement, chaque définition est une sorte de déclaration, car elle doit également spécifier l’apparence de cette variable / fonction. Ainsi, si une définition existe déjà dans une scope donnée, vous n’avez pas besoin de déclarations supplémentaires pour l’utiliser.

De la spécification C99:

La déclaration d’un identifiant pour un object ayant une scope de fichier sans initialiseur, et sans spécificateur de classe de stockage ou avec le spécificateur de classe de stockage static, constitue une définition provisoire. Si une unité de traduction contient une ou plusieurs définitions provisoires pour un identificateur et que l’unité de traduction ne contient aucune définition externe pour cet identificateur, le comportement est exactement comme si l’unité de traduction contenait une déclaration de la scope du fichier de cet identificateur, avec le type composite as de la fin de l’unité de traduction, avec un initialiseur égal à 0.

Il s’agit donc d’un cas dans lequel une simple déclaration sans initialiseur peut être une déclaration.

Comme C utilise les termes:

Une “définition” crée quelque chose (qui occupe une sorte de mémoire). Cela décrit aussi quelque chose. Cela signifie qu’une “définition” est aussi une “déclaration”.

Une “déclaration” décrit simplement quelque chose. L’idée est que le compilateur doit savoir comment construire le code qui utilise la chose définie ailleurs. Plus tard, l’éditeur de liens lie ensuite l’utilisation à quelque chose.

Les déclarations vous permettent de comstackr le code et de le lier (ultérieurement) séparément.