Confirmation différence déclaration/définition de variable

Savoir si je ne fais pas fausse route

Le problème exposé dans ce sujet a été résolu.

Bonjour tout le monde ! Avant de me faire tirer dessus je tiens à préciser que j’ai fait plusieurs recherches et que ma question n’est pas de savoir la différence entre définition et déclaration de variable, je pense que j’ai compris le truc mais si jamais vous sentez que non dites le moi ^^

Mes recherches :

Bon mon soucis c’est lorsque je "déclare" une variable dans une fonction. Ici je ne parle pas d’une variable externe car là le truc est clair : je la définie hors de toutes fonctions et je la déclare (si nécessaire avec le mot clef extern si la définition se trouve dans un autre fichier que la déclaration et que j’ai pas inclus de fichier header, blablabla) dans la fonction qui veut l’utiliser.

Donc en gros dans les tout premiers programmes qu’on fait quand on débute. On nous dit qu’il faut déclarer nos variables avant de pouvoir les utiliser mais, si j’ai bien compris, la ligne :

1
int nombre;

définie et déclare notre variable, non ? Pourquoi ne dit-on pas qu’on définie et déclare une variable ? Je suis d’accord pour dire que si on déclare c’est que forcément c’est défini avant mais ça me parait pas explicite… Du coup ma question est : Est-ce que je mélange tout ou pas… Ça m’a l’air important et je ne voudrai pas continuer avec des bases erronées !

Je vous remercie de votre temps et en attendant une réponse éclairée si possible éclairante, je vous souhaite une bonne journée/soirée/nuit !

+0 -0

Salut,

Pourquoi ne dit-on pas qu’on définit et déclare une variable ? Je suis d’accord pour dire que si on déclare c’est que forcément c’est défini avant mais ça me parait pas explicite…

petit_pingouin

Il est parfaitement possible de déclarer une variable avant de la définir. C’est d’ailleurs le même principe que pour les fonctions, elles sont très souvent déclarées à l’aide d’un prototype avant d’être définies. Le code suivant est donc parfaitement correct.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
extern int status;


int
main(void)
{
        return status;
}


int status = 20;

Par ailleurs, il ne faut pas perdre de vue qu’une définition est une déclaration, c’est juste qu’elle réserve en plus l’espace mémoire nécessaire. Donc dire qu’une variable est définie implique qu’elle est également déclarée.

+1 -0

Merci pour ta réponse !

En effet j’ai bien mélangé les pinceaux là…

Autant pour les fonctions je comprenais bien le fait de déclarer le prototype et après définir le corps de la fonction mais là :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int status; // J'ai enlevé le extern pour voir 

int
main(void)
{
    printf("%d\n", status);
    return status;
}


int status = 20;

J’ai compilé ce machin et ça semble renvoyer la bonne valeur, comment diable a t-il su que la première ligne était une déclaration et pas une définition :o

M’enfin je m’écarte de ma question et ce n’est plus adapté au sujet, je vais sagement finir mon bouquin, le relire pour éviter de dire de grosses bêtises, et je reviendrais vous embêter si jamais des questions subsistent ^^

Quoi qu’il en soit, encore merci !

J’ai compilé ce machin et ça semble renvoyer la bonne valeur, comment diable a t-il su que la première ligne était une déclaration et pas une définition :o

petit_pingouin

Mmm… Voilà qui me fait réaliser que mon ancien tutoriel sur les identificateurs n’est peut-être pas si inutile que cela. Il va falloir que je songe à le republier. En attendant, voici un de ses passages qui devrait t’éclairer sur le sujet. ;)

Les identificateurs d’objet

Une définition d’un identificateur d’objet est une déclaration qui alloue l’objet qu’il référence . Vous voilà bien peu avancé… Heureusement, il y a une règle simple et absolue pour différencier une déclaration et une définition d’un identificateur d’objet : une déclaration d’un identificateur d’objet, en dehors de tout bloc, comportant une initialisation est une définition. Dans tous les autres cas, il s’agit d’une déclaration.

1
2
3
4
int a;          /* Déclaration */
static int b;   /* Déclaration */
extern int c;   /* Déclaration */
int d = 10;     /* Définition */

Autrement dit, le compilateur déduit que la seconde déclaration est une définition du fait qu’elle comporte une initialisation. ;)

M’enfin je m’écarte de ma question et ce n’est plus adapté au sujet, je vais sagement finir mon bouquin, le relire pour éviter de dire de grosses bêtises, et je reviendrais vous embêter si jamais des questions subsistent ^^

petit_pingouin

De mémoire, j’ai également trouvé le K&R un peu confus sur le sujet lors de sa lecture. N’hésite donc pas si tu as d’autres questions.

+1 -0

Re ! Du coup je suis parti fouiller un peu partout et j’ai compris quelques trucs en plus :

  • Bon le extern a l’air plus important que n’avait l’air de le dire le K&R la première fois qu’il aborde le sujet, personnellement j’ai pas eu l’impression lors de la première lecture que ça permettait de faire la différence entre déclaration et définition. M’enfin quand on cherche on finit par s’en apercevoir.

  • Ah me voilà bien embêté … Tu me dis que

1
int a; /* Déclaration */ 

mais pourtant j’ai lu et j’ai testé :

1
2
3
4
5
6
7
8
int status;

int
main(void)
{
    printf("%d\n", status);
    return status;
}

Ce bout de code fonctionne et status est d’office initialisé à 0 (donc d’abord défini, non ?). Donc j’en étais venu à penser que sans extern impossible de faire une déclaration de variable globale correctement parce pour moi le fait que le compilateur déduise que la première définition est en faite une simple déclaration parce que la dernière ligne est une définition je trouve pas ça propre/joli ^^. (je fais référence au code de mon message précédent)

Bon plus je cherche et plus je me perds, j’y arriverai pas décidément :lol:

+0 -0
  • Ah me voilà bien embêté … Tu me dis que
1
int a; /* Déclaration */ 

Stop ! C’est de ma faute, je pensais que le passage cité était suffisant pour l’explication, mais visiblement il ne l’est pas. Voici donc le passage complet.

Les identificateurs d’objet

Une définition d’un identificateur d’objet est une déclaration qui alloue l’objet qu’il référence [17]. Vous voilà bien peu avancé… Heureusement, il y a une règle simple et absolue pour différencier une déclaration et une définition d’un identificateur d’objet : une déclaration d’un identificateur d’objet, en dehors de tout bloc, comportant une initialisation est une définition [18]. Dans tous les autres cas, il s’agit d’une déclaration.

1
2
3
4
int a;          /* Déclaration */
static int b;   /* Déclaration */
extern int c;   /* Déclaration */
int d = 10;     /* Définition */

Cependant, il y a une (petite) subtilité : les déclarations d’identificateurs d’objet, en dehors de tout bloc, à l’exception de celles précédées du mot‐clé extern, sont appelées des définitions potentielles. Et, dans le cas où un fichier comprend une ou plusieurs définitions potentielles d’un identificateur d’objet mais aucune définition de cet identificateur, une définition est implicitement incluse au début du fichier avec un initialiseur valant zéro [19].

Rassurez-vous, nous allons revoir cela en douceur. Avant toute chose, il est nécessaire de bien différencier une déclaration, une définition potentielle et une définition d’un identificateur d’objet. Pour ce faire, voici un exemple simple.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 * Cette déclaration ne comporte pas d'initialisation.
 * Elle n'est pas précédée du mot‐clé « extern ».
 * Il s'agit donc d'une définition potentielle.
 */
int n;

/*
 * Cette déclaration comporte une initialisation.
 * Il s'agit donc d'une définition.
 */
extern int n = 10;

/*
 * Cette déclaration ne comporte pas d'initialisation.
 * Elle n'est pas précédée du mot‐clé « extern ».
 * Il s'agit donc d'une définition potentielle.
 */
static int n;

/*
 * Cette déclaration ne comporte pas d'initialisation.
 * Elle est précédée du mot‐clé « extern ».
 * Il s'agit donc d'une déclaration.
 */
extern int n;

Ensuite, reprenons cette règle pas à pas à l’aide du code ci‐dessous.

1
2
3
4
5
6
7
int n;

int
main(void)
{
        return n;
}

Comme vous le voyez, nous avons un fichier comprenant une définition potentielle de l’identificateur d’objet n, mais aucune définition de cet identificateur. Ce que dit l’obscure règle que je vous ai présentée auparavant, c’est que dans le cas où un fichier comprend une ou plusieurs définitions potentielles d’un identificateur mais aucune définition de cet identificateur (ce qui est le cas de notre fichier), une définition est implicitement inclue au début de ce fichier avec un initialiseur valant zéro. Autrement dit, appliquée à notre exemple, cela donne ceci :

1
2
3
4
5
6
7
8
9
/* Définition implicite */
int n = 0;
int n;

int
main(void)
{
        return n;
}
+1 -0

Hey fallait tout dire d’un coup ! :lol:

Désolé ça va faire plusieurs heures que je cherche des informations sur ce truc, je peux plus comprendre des trucs implicites ^^ En plus tu dis : Cependant, il y a une (petite) subtilité … Ah ! El famoso détail qui tue :D

En tout cas un énorme Merci à toi ! Maintenant tout est bien clair, du moins assez pour aborder la suite un peu sereinement, de nouveaux problèmes vont sûrement se pointer (Héhé pas mal non ? :lol:) mais c’est le jeu. Franchement je pense que ce cours est loin d’être inutile mais après je comprends qu’il doit y avoir du travail pour le rééditer :)

Je te souhaite une bonne soirée, je pense que je peux clore le sujet !

En tout cas un énorme Merci à toi !

petit_pingouin

Avec plaisir. ;)

Franchement je pense que ce cours est loin d’être inutile mais après je comprends qu’il doit y avoir du travail pour le rééditer :)

petit_pingouin

À vrai dire, il est déjà en markdown, donc cela ne me prendra pas longtemps pour le proposer à la validation, il faut juste que je retouche une ou deux choses. Quoi qu’il en soit, je suis heureux de voir qu’il semble tout de même utile. ^^

+0 -0

Enfin la problématique de la distinction entre déclaration (il existe une variable appelée truc) et définition (voilà la variable truc) n’est vraiment utile que pour des variables globales partagées entre plusieurs unités de compilation (rt c’est pas en général le code qu’on veut écrire). Car pour les variables locales (idéalement 100% des variables qu’on manipule), la distinction n’existe pas.

+2 -0

Enfin la problématique de la distinction entre déclaration (il existe une variable appelée truc) et définition (voilà la variable truc) n’est vraiment utile que pour des variables globales partagées entre plusieurs unités de compilation (rt c’est pas en général le code qu’on veut écrire). Car pour les variables locales (idéalement 100% des variables qu’on manipule), la distinction n’existe pas.

Davidbrcz

Je ne partage pas ton point de vue quant au fait que les variables globales (comprendre : définies en dehors de tout bloc et partagées ou non entre plusieurs unités de compilation) devraient être proscrites, mais sinon, oui, cette distinction est avant tout intéressante pour les variables et/ou fonctions partagées par plusieurs unités de compilation. Bien que, foncièrement, c’est aussi intéressant à savoir pour les variables et/ou fonction propre à une unité de compilation.

Il me semble qu’on en avait déjà parlé, mais je pense aussi que ça serait cool que tu le republies quelque part. C’est un point franchement pas clair dans la plupart des cours de C. ;)

Lucas-84

Puisque c’est demander si gentiment, c’est fait, je viens de le soumettre à la validation. ^^

+1 -0

Je partage ici l’avis de David ici. Il n’y a pas vraiment de distinction qui compte ici hormis le cas des variables globales où l’on a besoin de dire: "il existe une variable de ce type et accessible de partout, mais elle sera définie dans une autre unité de traduction".

Le simple fait que l’on ne puisse pas écrire

1
2
int i;
int i;

est une preuve suffisante pour dire qu’il s’agit bien de définitions. Elle déclarent aussi, mais ce sont en plus des définitions. J’aime bien cette Q/A: https://stackoverflow.com/a/1410632/15934 Bref, ce sont bel et bien des définitions, le potentiel n’a aucun sens.

La norme C (dans le draft n1570 que j’ai) emploie "declaration" partout, mais c’est le §6.7/5 qui est intéressant

Semantics

5 A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:

  • for an object, causes storage to be reserved for that object;
  • for a function, includes the function body;119)
  • for an enumeration constant, is the (only) declaration of the identifier;
  • for a typedef name, is the first (or only) declaration of the identifier.
Norme C §6.7/5

On est bien dans le premier cas.


Je vais réagir à un autre point:

Donc en gros dans les tout premiers programmes qu’on fait quand on débute. On nous dit qu’il faut déclarer nos variables avant de pouvoir les utiliser

petit_pingouin

Effectivement, dans ces langages, il n’est pas possible d’utiliser un élément sans qu’il ne soit connu. Il convient de les déclarer. Dans le cas des variables locales, toute déclaration sera une définition : l’espace adéquat dans la pile est réservé: la variable est réservée et prête à être utilisée.

Maintenant, le truc est qu’il est important d’initialiser la variable pour pouvoir s’en servir. Je pense qu’une des confusions classiques vient de là. Sans parler que certaines règles qualité demandent de tout déclarer en début de programme car le C89 ne permet pas autre chose – ce qui n’est pas ce qu’il y a de mieux à faire quand on a le choix.

Le simple fait que l’on ne puisse pas écrire

1
2
int i;
int i;

est une preuve suffisante pour dire qu’il s’agit bien de définitions. Elle déclarent aussi, mais ce sont en plus des définitions. J’aime bien cette Q/A: https://stackoverflow.com/a/1410632/15934 Bref, ce sont bel et bien des définitions, le potentiel n’a aucun sens.

lmghs

Pourtant, le code que tu décris peut bien être compilé et est parfaitement légal depuis le C89.

1
2
3
4
5
6
7
8
9
int i;
int i;


int
main(void)
{
        return i;
}

Il y a deux définitions potentielles qui sont précédées d’une « vraie » définition implicite initialisant i à zéro.

Maintenant, le truc est qu’il est important d’initialiser la variable pour pouvoir s’en servir. Je pense qu’une des confusions classiques vient de là. Sans parler que certaines règles qualité demandent de tout déclarer en début de programme car le C89 ne permet pas autre chose – ce qui n’est pas ce qu’il y a de mieux à faire quand on a le choix.

lmghs

Les définitions d’objet de classe de stockage automatique se font en début de bloc, pas en début de programme. Je sais que tu le sais (hu hu), mais les mots sont importants ici.

+0 -0

Pourtant, le code que tu décris peut bien être compilé et est parfaitement légal depuis le C89.

Au temps. Je ne savais pas que le C était différent du C++ ici – car ce code n’est pas légal en C++. (Ceci dit, nous sommes encore dans un cas de variable globale… ^^’ /me qui essaie de se rattraper aux branches)

Les définitions d’objet de classe de stockage automatique se font en début de bloc, pas en début de programme. Je sais que tu le sais (hu hu), mais les mots sont importants ici.

Taurre

Tout à fait. Tu as raison de me reprendre. En écrivant "programme" je pensais en fait à sous-programme, et donc oui, à "début de bloc".

Puisque c’est demander si gentiment, c’est fait, je viens de le soumettre à la validation. ^^

Taurre

Publication expresse ! 😍

🍪 :)

🍙 ありがと🍘

+0 -0

En C++, cela produit un "error: redefinition of …". Et a ma connaissance, il n’y a pas de notion de "definition potentielle". (Vous confirmez, les autres devs C++ ?)

gbdivers

Dans la mesure où le potentiel est contextuel et lié à l’endroit où la déclaration est réalisée comme dans l’exemple de @Taurre, i.e. hors fonction. Je dirais que oui, en C++.

Sauf qu’à bien chercher, il y le cas des variables membres, ou autres attributs de structures. J’ai bien envie de dire qu’il ne s’agit que d’une déclaration: C’est que que dit d’ailleurs g++. gcc parle lui de "duplicate member".

Dans tous les cas, je n’aime pas dire potentiel sans expliciter les contextes exacts où il s’agit d’une déclaration et ceux où il s’agit d’une définition.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte