extern

extern - C++ - Programmation

Marsh Posté le 06-08-2002 à 22:52:35    

Catégorie
 
Spécificateurs de classe de stockage
 
Syntaxe
 
extern <définition de données > ;
 
[extern] <prototype de fonction> ;
 
Description
 
Le modificateur extern permet d'indiquer que la valeur initiale et de stockage réel d'une variable, ou le corps d'une fonction, est défini dans un module de code source séparé. Les fonctions déclarées avec externe sont visibles dans tous les fichiers source d'un programme, à moins que vous ne redéfinissiez la fonction en statique.
 
Le mot clé extern est facultatif pour un prototype de fonction.
 
Utilisez extern "c" pour empêcher que les noms de fonction soient substantypés dans des programmes C++.
 
je capte a quoi ca seet, masi je comprend pas ce que veux dire substantypés  

Reply

Marsh Posté le 06-08-2002 à 22:52:35   

Reply

Marsh Posté le 06-08-2002 à 22:57:30    

Comme le C++ (contrairement au C) accepte d'avoir pluseurs fonctions avec le même nom différencié seulement par le type de paramètre, le nom de la fonction n'est pas suffisant pour identifier de manière unique la fonction dans un fichier objet, donc après le nom, le compilateur rajoute des caractères dépendants du type des paramètres.  
 
Le fait de déclarer une fonction extern "C" va donc éviter ces ajouts (substantypage), afin de pouvoir appeler la fonction depuis un programme C
 
par exemple (bidon, mais c'est l'idée) une fonction  
extern "C" void toto (int) va donner dans le fichier objet toto
 
alors que  
void toto (int) va donner dans le fichier objet toto_aDaa


---------------
brisez les rêves des gens, il en restera toujours quelque chose...  -- laissez moi troller sur discu !
Reply

Marsh Posté le 06-08-2002 à 23:01:32    

si j'ai bien compris on peut pas surcharger un fonction extern "C", et le fait de pas substantyper permet d'appeler depusi d'autres langages gérant pas le substantypage

Reply

Marsh Posté le 06-08-2002 à 23:09:57    

Toutafé.
 

Code :
  1. void toto (int a){}
  2.   void toto (int a, int b) {}


 
compile, tandis que  
 

Code :
  1. extern "C" {
  2.   void toto (int a){}
  3.   void toto (int a, int b) {}
  4. }


 
ne compile pas :  
 
 


[kadreg@luggage]/home/kadreg$ g++ -c extern.cpp
extern.cpp: In function `void toto (int, int)':
extern.cpp:4: declaration of C function `void toto (int, int)'  
conflicts with
extern.cpp:2: previous declaration `void toto (int)' here
[kadreg@luggage]/home/kadreg$  


---------------
brisez les rêves des gens, il en restera toujours quelque chose...  -- laissez moi troller sur discu !
Reply

Marsh Posté le 06-08-2002 à 23:11:40    

sinon en "C" tout court, le mot clef extern devant un proto de fonction dans un fichier xxx.c sert juste à préciser que le corp de la fonction n'est pas précisé dans xxx.c(ni dans ses inclusions). Il faut juste s'assurer qu'un objet correspondant au prototype de la fonction extern soit trouvé au moment de l'édition de liens.
 
 
en fait, <<extern en C>> et <<extern "C" en C++>>, ca ne fait pas la même chose  [:snake12]


Message édité par schnapsmann le 06-08-2002 à 23:13:21

---------------
From now on, you will speak only when spoken to, and the first and last words out of your filthy sewers will be "Sir!"
Reply

Marsh Posté le 06-08-2002 à 23:25:55    

extern tout seul devant le prototype d'une fonction
ne sert a rien (sauf a etre plus explicit que ne le demande
le standard C).
Elle est incluse dans la table des liens par défaut
pour eviter cela, on utilise static.
une fonction sans son corps n'est pas instanciée alors
qu'une variable est instanciée par défaut sauf si on précise extern devant sa déclaration, par contre si on lui assigne une valeur en meme temps qu'on la declare alors elle est instanciée même avec le extern.
C'est un peu compliqué mais c'est la faute au C qui est concu un peu bizarrement; il y a des comportements par défaut ce qui fait que selon qu'on precise ou pas tel modificateur, ca va influer sur un autre comportement (portée du nom, mode d'instanciation).
 
extern "C", c'est pour éviter le "name mangling".
 
j'avais fait un petit article a l'époque sur le fonctionnement (basique) d'un compilateur/editeur de lien:
voici le texte.
 
Etapes d'une compilation d'un projet C++
La compilation d'un programme, lib ou autre en C++ comporte trois etapes. Le preprocessing ou precompilation. La compilation. L'edition des liens. (avec etapes intermediaires suivant le compilateur).  
 
Le preprocessing
La particularité de cette etape est de ne pas generer de binaire mais de simplement generer du source a partir de directives de compilation trouvées dans les fichiers. Cela inclut les macros, les #include et l'instanciation des classes et fonctions templates. (les templates doivent etre traites un peu a part parce que cela peut etre vu comme un vrai metalangage de programmation qui sert a generer du C++).  
C'est pour ca qu'on dit que le compilateur n'agit pas sur un fichier mais sur une "translation unit", une unité de traduction qui est le fichier source original ou toutes les macros ont été deployées et tous les fichiers include ont ete inclus. C'est important de comprendre cette notion de "translation unit" pour travailler sur des projets C++ avec plusieurs fichiers.  
 
La compilation
Le compilateur agit sur une "translation unit" qui est un fichier .cpp avec toutes les includes et toutes les macros deployées afin de generer un fichier objet (c'est a dire un fichier binaire mais avec des appels symboliques entre les unités de traduction).  
Le compilateur est borgne et ne voit que ce qui concerne sa translation unit. C'est pour ca qu'on doit lui fournir un certain nombre d'indication sur ce qui se trouve dans les autres unités avant que l'edition des liens ne raccroche tous les bouts ensembles.  
Ces indications sont des declarations de fonctions sous forme de prototypes ou des declarations de variables "extern".  
 
Qu'est-ce qu'un nom en C++?
un nom est un mot réservé par le programmeur par une declaration. La declaration a toujours lieu en amont de toute reference a ce nom, ainsi le compilateur lorsqu'il tombe sur la reference en question sait a quoi cela se rapporte: variable, fonction ou type (classe, enum etc..).  
On a quatre portées principalement pour les noms; bloc, fonction, globale ou prototype.  
La plus large c'est la portée globale ou "file scope". Le "file scope" qui devrait etre plus precisemment appelée "translation unit scope" parce qu'elle commence a la premiere declaration du nom et se termine a la fin de l'unité de traduction. C'est le cas pour une variable, fonction ou type declaré en dehors de tout bloc {} ou en dehors d'un declaration de fonction.  
Ce qui precede est valable en C.  
 
exemple: dans  
void affiche(char * _str);  
on a deux declarations de nom. la premiere c'est "affiche" qui est une fonction de portée globale. La seconde c'est "_str" qui est une variable dont la portée est restreinte au prototype (on peut meme s'abstenir de la nommer en vrai).  
 
 
La resolution des noms lors du linkage(où l'on reparle de l'extern "C" )  
Une fois chaque unité compilée, il est nécessaire de recoller les morceaux c'est à dire de remplacer les liaisons symboliques par de vraies liaisons. C'est la que la classe d'enregistrement (storage class) et la defiguration des noms (name mangling ou "substantypage" ) interviennent.  
 
Tout nom de portée global est accessible aux autres unités par défaut ("storage class extern" ). C'est a dire qu'il suffit d'inclure un prototype equivalent pour pouvoir faire appel à cette fonction depuis une autre unité de traduction. Le prototype est une definition de fonction sans corps (sans son code). Pour limiter la portée d'un nom a l'unité courante, il faut utiliser le mot clé "static", ou alors utiliser un espace de nom anonyme ("nameless namespace" ). Les methodes d'une classe, meme static, sont "extern" par defaut (confusant n'est-ce pas ;) ?). Une classe et ses methodes ne peut donc etre dissimulée aux autres unités qu'en la declarant dans un espace de nom anonyme.  
 
Cependant le C++ introduit des nouveautés qui imposent au compilateur d'adjoindre quelques infos supplementaires au nom reel de l'objet avant d'appeler l'editeur de lien.  
En C++, il est possible de surcharger une fonction par le nombre et le type des arguments. Pour pouvoir discerner deux fonctions C++ avec le meme nom, il faut donc ajouter la signature de la fonction au nom d'origine. De meme une fonction peut-etre declaré dans un namespace ou etre la methode d'une classe avec des conventions d'appels differentes (this passé implicitement pour une fonction non static).  
La consequence de cela c'est que le format des fichiers objet C et des fichiers objet C++ ne sont pas compatibles, un nom defiguré en C++, ne peut pas etre rattaché a une fonction C qui est inscrit tel quel dans le fichier objet; la fonction affiche sera designée "_affiche" dans le fichier objet C, mais "?affiche@@YAXXZ" dans le cas du fichier objet C++.  
C'est pour ca aussi qu'il est important que le prototype de la fonction soit exact sur le nombre et le type des arguments passés en C++ (en C c'est moins grave a la compilation mais ca peut planter a l'execution).  
 
Lorsque tu utilises un fichier objet C depuis un programme C++, il faut donc preceder la declaration du nom d'un extern "C" qui indique qu'on revient aux conventions du C et donc qu'on laisse tomber la defiguration. Cela est necessaire aussi pour d'autres langages que le C++ qui ne savent travailler que sur les fichiers objets du C sans defiguration.  
 
Pourquoi il est inutile et dangereux d'utiliser extern "C" dans d'autres cas
Un compilateur sait par avance comment il va defigurer les noms de fonction et de variables et donc saura s'y retrouver dans les objets globaux "extern". Il est donc inutile de faire appel aux conventions "C" dans un meme projet hors librairies exterieures (lib standard ou autres).  
La defiguration est necesaire parce qu'elle permet de s'y retrouver dans les fonctions surchargées, les namespaces et autres nouveautés du C++, en utilisant extern "C" tu reviens au plus grand denominateur commun entre le C et le C++.  
 

Reply

Marsh Posté le 06-08-2002 à 23:39:24    

[OT]
"substantypés", ce n'est pas un mot que j'ai vu employé
souvent, contrairement a "mangled name"
mais il est vrai qu'il est plus facile de trouver des ressources C++ en anglais sur le web :)
 
LeGreg

Reply

Marsh Posté le 07-08-2002 à 03:29:55    

substantypage... J'ai appris "décoration de noms".
 
On peut surcharger une fonction extern "C", comme ceci:

Code :
  1. extern "C" void toto(int  a){} ;
  2.            void toto(char a){} ;
  3.            void toto(long a){} ;


 
extern et static spécifient si une variable/fonction est accessible/crée dans un autre source.
Ceci vaut au niveau global (extern y est implicite):

Code :
  1. //extern sur une déclaration: est crée ailleurs dans un source quelconque (peut être le même).
  2. //extern sur une définition : est accessible depuis un source quelconque.
  3. //static sur une déclaration: qualifies la future définition.
  4. //static sur une définition : est inaccessible depuis un autre source.
  5. //static dans une fonction ou une classe à un sens différent.
  6. //variables (par défaut, une déclaration est une création).
  7.        int a  ;//crée ici, implicitement   accessible depuis un autre source. =0 implicite.
  8. extern int b=0;//crée ici, explicitement   accessible depuis un autre source.
  9. static int d  ;//crée ici, explicitement inaccessible depuis un autre source. =0 implicite.
  10. extern int c  ;//existe dans un quelconque source (peut-être le même).
  11. //fonctions
  12.        void f() ;//existe dans un quelconque source (peut-être le même).
  13. extern void g() ;//idem (extern était implicite).
  14.        void h(){} ;//crée ici, implicitement   accessible depuis un autre source..
  15. extern void i(){} ;//crée ici, explicitement   accessible depuis un autre source.
  16. static void j(){} ;//crée ici, explicitement inaccessible depuis un autre source.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 07-08-2002 à 11:17:34    

Il y a une certaine confusion concernant le sens de static
liée à son utilisation. J'avoue que les concepteurs du C n'ont pas été tres fins sur ce coup-la.
 
En fait, il y a des storage class (classe d'instanciation) qui sont principalement auto, static, dynamic (je passe sur les register et autres) en langage C.
 
le mot clé static devant une declaration de variable et ceci quelle que soit sa situation (bloc, fichier, classe, etc..), signifie que la variable est instanciée en mode statique, c'est a dire qu'elle existe pendant toute la durée de vie du programme.
 
Ceci dit, lorsqu'une variable est défini hors du scope de toute fonction/classe, elle est *automatiquement* statique. Donc les concepteurs du C ont du se dire: "le mot clé static devant une variable du scope global ne rajoute pas de sens donc on va lui en donner un pour s'epargner un keyword". Et donc, plutot que de définir un keyword "intern" pour les variables a portée interne à l'unité de traduction, ils ont défini que static aurait ce sens.
 
A noter qu'en C++, static au sens de "intern" n'a plus cours officiellement, la norme préconise d'utiliser les espaces de noms anonymes (anonymous namespace), meme si ca marche toujours tres bien sur tous les compilateurs.
 
A+
LeGreg

Reply

Marsh Posté le 08-08-2002 à 04:19:26    

legreg a bien parlé.
 
Ce sens multiple de static m'avait tellement perturbé que j'avais récréer les mots-clés "manquants" dans mon en-tête utilitaire:

Code :
  1. #define intern static //pour les variables/fonctions globales
  2. #define unique static //pour les variables locales/membres de classe


 

  • substantypage
  • décoration de nom
  • défiguration de nom
  • mangled name ->nom déformé (ou mutilé/massacré/estropié/dénaturé)

Je crois qu'on a fait le tour de la question, là ?
Ou alors il y en a d'autres ?


Message édité par Musaran le 08-08-2002 à 04:19:58

---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed