Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Effet et usage d'extern ?

30 réponses
Avatar
mpg
Bonjour,

Je suis en train de lire le bouquin de Braquelaire pour essayer
d'acquérir quelques bases consistantes en C (j'ai déjà bidouillé un peu
de C en me basant essentiellement ce que j'avais autrefois appris en
Pascal et sur une lecture très partielle et rapide du K&R).

Je viens de finir le chapitre deux (déclarations) et n'arrive toujours
pas à comprendre ce que fait et à quoi sert la spécification de
rangement 'extern', alors que je devrais l'avoir compris à ce stade de
ma lecture, si j'ai bien saisi l'organisation du bouquin (à moins que le
secret ne réside dans le chapitre sur la modularisation). Je viens donc
demander votre aide.

Les exemples présentés sur l'usage d'extern font en général intervenir
deux fichiers, mettons a.c et b.c, qui utilisent une même variable
globale var. Ils évoquent le fait que si on n'y prend pas garde, on peut
par exemple déclarer var comme int dans a.c, comme float dans b.c, et
que les deux désigneront quand même le même objet. Je comprends très
bien pourquoi un tel état de fait est à éviter à tout prix (je ne
comprends même pas que le compilateur (ou l'éditeur de liens, enfin
quelqu'un) ne m'envoie pas sur les roses si j'essaie de faire ça, en
fait).

Ensuite on voit comment l'inclusion dans a.c et b.c d'un même fichier
d'en-tête déclarant var évite de problème (cette fois, le compilateur
geule si on fait n'importe quoi). Bien. Je n'ai pas saisi le rôle
qu'extern était censé jouer là-dedans.

J'ai cherché dans la FAQ du groupe, la question 5.3 indique aussi
conseille aussi d'utiliser extern pour ses déclarations de variables
globales (quand on tient à en utiliser), et précise : « Ceci évitera des
redéfinitions de la variable lors des inclusions d'en-têtes. » Je
persiste à avoir l'impression que ce qui évite la re-définition de la
variable, c'est l'inclusion du fichier d'en-tête, pas la déclaraiont
'extern'.

Merci d'avance pour tout éclaircissement.

PS : je ne sais pas si la question est reliée, mais pour une variable, y
a-t-il une différence entre déclaration et définition ?

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/

10 réponses

1 2 3
Avatar
Eric Levenez
Le 23/04/09 23:14, dans ,
« mpg » <mpg+ a écrit :

Je viens de finir le chapitre deux (déclarations) et n'arrive toujours
pas à comprendre ce que fait et à quoi sert la spécification de
rangement 'extern',



Dans a.c :

int var;
// ...
var = 24;

Dans b.c :

extern int var;
// ...
var = 42;

Les deux sources (linkés ensemble) travaillent sur la même variable. Le
extern permet au compilateur de savoir cela. Sans lui chaque source, étant
compilé séparément, aurait sa propre instance de la variable var, sans
rapport l'une avec l'autre.

--
Éric Lévénez
FAQ de fclc : <http://www.levenez.com/lang/c/faq/&gt;
Avatar
espie
In article ,
mpg <mpg+ wrote:
globale var. Ils évoquent le fait que si on n'y prend pas garde, on peut
par exemple déclarer var comme int dans a.c, comme float dans b.c, et
que les deux désigneront quand même le même objet. Je comprends très
bien pourquoi un tel état de fait est à éviter à tout prix (je ne
comprends même pas que le compilateur (ou l'éditeur de liens, enfin
quelqu'un) ne m'envoie pas sur les roses si j'essaie de faire ça, en
fait).



C est historiquement lie a Unix, et les editeurs de liens "historiques" etaient
extremement primitifs: aucune notion de typage, juste des noms de symboles.
Donc globalement, tant que l'objet a le meme nom, c'est tout bon, un linker
traditionnel est content.

C'est pas si bete que ca comme facon de faire: pour avoir "mieux", il faut
que ton editeur de liens connaisse le typage de ton langage, et tu le
rends plus ou moins dependant du langage en question...
Avatar
mpg
Eric Levenez scripsit:

Les deux sources (linkés ensemble) travaillent sur la même variable. Le
extern permet au compilateur de savoir cela. Sans lui chaque source, étant
compilé séparément, aurait sa propre instance de la variable var, sans
rapport l'une avec l'autre.



Euh, alors je vais peut-être dire une énormité, mais je croyais que les
deux travaillaient de toutes façons sur la même instance de var à partir
du moment om aucun des deux n'utilisaient ne déclarait var comme
static ?

Bon, concrètement, sur un exemple complet :

--- BEGIN a.h ---
void inc_var(void);
--- END a.h ---

--- BEGIN a.c ---
#include "a.h"
float var;

void inc_var(void)
{
var += 1.0;
}
--- END a.c ---

--- BEGIN b.c ---
#include <stdio.h>
#include "a.h"

int var = 1;

int main(void)
{
printf("var=%dn", var);
inc_var();
printf("var=%dn", var);
return 0;
}
--- END b.c ---

Je compile tout ça, j'exécute : j'ai bien le résultat aberrant attendu,
qui montre que les deux fichiers utilisent la même instance de var, sans
la traiter avec le même type.

Je change la deuxième ligne de b.c en 'extern float var;', le
comportement à l'exécution ne change pas, et à la compilation je ne me
prends toujours pas d'avertissement (compilé avec gcc -Wall -pedantic).

Du coup je ne vois pas d'influence de 'extern' ni sur le comportement à
l'exécution, ni sur la capacité du compilateur à se rendre compte que je
suis en train de faire n'importe quoi. Ça m'embête, parce que si extern et
qu'on recommande de l'utiliser, ça doit bien être pour quelque chose,
j'aimerais comprendre pour quoi...

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
mpg
Marc Espie scripsit:

C est historiquement lie a Unix, et les editeurs de liens
"historiques" etaient extremement primitifs: aucune notion de typage,
juste des noms de symboles. Donc globalement, tant que l'objet a le
meme nom, c'est tout bon, un linker traditionnel est content.

C'est pas si bete que ca comme facon de faire: pour avoir "mieux", il
faut que ton editeur de liens connaisse le typage de ton langage, et
tu le rends plus ou moins dependant du langage en question...



D'accord, je vois. Le problème est en quelque sorte que les rôles sont
trop bien séparés : seul le compilateur connaît les types, mais seul le
linker « voit » l'ensemble des fichiers. C'est raisonnable.

Par contre ça m'éclaire toujours pas sur le rapport entre extern et la
choucroute :-)

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
j4e8a16n
gcc 3.4.5

----------------------------------------- sans extern
var=1
var65353216

------------------------------------------avec extern

3.4.5>"C:MinGWbingcc.exe" -Wall -pedantic a.c b.c
b.c: In function `main':
b.c:18: warning: int format, double arg (arg 2)
b.c:20: warning: int format, double arg (arg 2)
Avatar
-ed-
On 23 avr, 23:14, mpg <mpg+ wrote:
<...>

Ensuite on voit comment l'inclusion dans a.c et b.c d'un même fichier
d'en-tête déclarant var évite de problème (cette fois, le compila teur
geule si on fait n'importe quoi). Bien. Je n'ai pas saisi le rôle
qu'extern était censé jouer là-dedans.



<...>
PS : je ne sais pas si la question est reliée, mais pour une variable, y
a-t-il une différence entre déclaration et définition ?



http://mapage.noos.fr/emdel/notes.htm#globales
Avatar
mpg
j4e8a16n scripsit:

------------------------------------------avec extern

3.4.5>"C:MinGWbingcc.exe" -Wall -pedantic a.c b.c
b.c: In function `main':
b.c:18: warning: int format, double arg (arg 2)
b.c:20: warning: int format, double arg (arg 2)



Bon, chez moi, GCC 4.3.3, même avec -Wall et -pedantic, ne voit pas le
problème. Ceci dit, si extern peut aider certaines versions de certains
compilateurs à émettre des avertissements appropriés, même si ça ne
marche pas à tous les coups, c'est déjà ça.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
mpg
-ed- scripsit:

PS : je ne sais pas si la question est reliée, mais pour une variable, y
a-t-il une différence entre déclaration et définition ?



http://mapage.noos.fr/emdel/notes.htm#globales



Merci. J'en a profité pour aller lire aussi #definitions. Du coup je
crois avoir compris qu'en fait 'int x;' est une définition de x qui vaut
aussi déclaration, alors que 'extern int x;' est une déclaration mais
pas une définition, c'est-à-dire que le compilateur ne va pas réserver
d'espace mémoire pour x, comptant trouver une définition de x plus tard
ailleurs.

D'ailleurs, si j'essaie de compiler un programme avec deux fichiers et
que je mets 'extern int var;' dans les deux sans qu'aucun ne contienne
de définition de var, alors effectivement ça foire en me disant que
"undefined reference to `var'" à l'édition de liens.

Je crois que je commence à y voir un peu plus clair, merci à vous tous.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Antoine Leca
Le 23/04/2009 22:29, mpg écrivit :
--- BEGIN a.c ---
float var;

--- BEGIN b.c ---
int var = 1;




Je compile tout ça,



On s'arrêtera là si tu veux bien.
Certains compilateurs (c'est la tradition Unix) acceptent cela. Mais pas
tous. D'autres vont gueuler, en particulier certains vont te dire que
«var» est _défini_ à deux endroits distincts, et c'est un endroit de trop.


Je change la deuxième ligne de b.c en 'extern float var;', le
comportement à l'exécution ne change pas, et à la compilation je ne me
prends toujours pas d'avertissement (compilé avec gcc -Wall -pedantic).



Utilise gcc -W -Wall : -pedantic ne sert en pratique à rien, et
certaines versions de GCC ont des options dans -W qui ne sont pas dans
-Wall (et de mémoire, cela a une influence sur l'édition des liens).

De fait, extern (devant la déclaration de float var, par exemple) sert à
dire au compilateur « ceci n'est *pas* une définition de var, seulement
une déclaration » (c'est-à-dire la velléité de pouvoir utiliser var un
peu plus loin dans ce source). Le résultat sera donc accepté par les
compilateurs C.


Comme tu le remarques, cela ne résout pas le problème de la disjonction
des types (et les options de GCC ne vont pas aider: l'information du
type de la variable a été perdue lors de la phase compilation, et à la
phase édition-des-liens qui suit, seul moment où on met en relation a et
b, GCC n'a plus la possibilité de s'apercevoir du problème).
Pour résoudre cela, il faut ajouter dans "ab.h"
extern int var;
Et à partir de là, il n'est plus possible de laisser passer l'erreur à
la compilation de b.c.
Déclarer les variables globales dans des fichiers d'en-tête devrait être
obligatoire, en tous cas c'est une bonne habitude de documentation à
prendre (indépendamment du sujet extern).



Antoine
Avatar
Mickaël Wolff
mpg wrote:

Bon, concrètement, sur un exemple complet :

--- BEGIN a.h ---


extern float var ;
void inc_var(void);
--- END a.h ---



ceci va passer :
--- BEGIN a.c ---
#include "a.h"
float var;

void inc_var(void)
{
var += 1.0;
}
--- END a.c ---



Ceci va casser :

--- BEGIN b.c ---



Je compile tout ça, j'exécute : j'ai bien le résultat aberrant attendu,
qui montre que les deux fichiers utilisent la même instance de var, sans
la traiter avec le même type.



C'est pour qu'il faut que tu déclares extern ta variable globale dans
l'en-tete. Lorsqu'un module compile, il ne sait rien du voisin. D'ou
le fait que les modules compilent.
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
1 2 3