Effets du mot clé extern sur les fonctions C

En C, je n’ai remarqué aucun effet du mot-clé extern utilisé avant la déclaration de fonction. Au début, je pensais que lors de la définition de extern int f(); dans un seul fichier vous oblige à l’implémenter en dehors de la scope du fichier. Cependant, j’ai découvert que les deux:

 extern int f(); int f() {return 0;} 

et

 extern int f() {return 0;} 

comstackr très bien, sans avertissements de gcc. J’ai utilisé gcc -Wall -ansi ; il n’accepterait même pas // commentaires.

Existe-t-il des effets pour l’utilisation des définitions de fonctions extern avant ? Ou est-ce juste un mot-clé facultatif sans effets secondaires pour les fonctions.

Dans ce dernier cas, je ne comprends pas pourquoi les concepteurs standard ont choisi de traiter la grammaire avec des mots-clés superflus.

EDIT: Pour clarifier, je sais qu’il y a un usage pour extern dans les variables, mais je ne parle que des fonctions extern dans.

Nous avons deux fichiers, foo.c et bar.c.

Voici foo.c

 #include  volatile unsigned int stop_now = 0; extern void bar_function(void); int main(void) { while (1) { bar_function(); stop_now = 1; } return 0; } 

Maintenant, voici bar.c

 #include  extern volatile unsigned int stop_now; void bar_function(void) { while (! stop_now) { printf("Hello, world!\n"); sleep(30); } } 

Comme vous pouvez le voir, nous n’avons pas d’en-tête partagé entre foo.c et bar.c, mais bar.c a besoin de quelque chose déclaré dans foo.c lorsqu’il est lié et foo.c a besoin d’une fonction de bar.c lorsqu’il est lié.

En utilisant «extern», vous dites au compilateur que tout ce qui suit sera trouvé (non statique) au moment du lien; ne réservez rien pour cela dans la passe en cours car il sera rencontré plus tard. Les fonctions et les variables sont traitées également à cet égard.

C’est très utile si vous avez besoin de partager des modules entre des modules et que vous ne voulez pas l’initialiser dans un en-tête.

Techniquement, chaque fonction dans un en-tête public de bibliothèque est «externe», mais leur étiquetage en tant que tel n’a que très peu, voire aucun avantage, selon le compilateur. La plupart des compilateurs peuvent comprendre cela par eux-mêmes. Comme vous le voyez, ces fonctions sont définies ailleurs.

Dans l’exemple ci-dessus, main () n’imprimerait hello world qu’une seule fois, mais continuerait à entrer bar_function (). Notez également que bar_function () ne va pas retourner dans cet exemple (puisque ce n’est qu’un exemple simple). Imaginez simplement que stop_now soit modifié lorsqu’un signal est traité (donc volatil) si cela ne semble pas assez pratique.

Les externs sont très utiles pour des choses comme les gestionnaires de signaux, un mutex que vous ne voulez pas mettre dans un en-tête ou une structure, etc. La plupart des compilateurs vont optimiser pour ne pas réserver de mémoire pour les objects externes. Nous allons le réserver dans le module où l’object est défini. Cependant, encore une fois, il est inutile de le spécifier avec des compilateurs modernes lors du prototypage de fonctions publiques.

J’espère que cela pourra aider 🙂

Autant que je me souvienne de la norme, toutes les déclarations de fonctions sont considérées comme “externes” par défaut, il n’est donc pas nécessaire de le spécifier explicitement.

Cela ne rend pas ce mot clé inutile puisqu’il peut également être utilisé avec des variables (et dans ce cas, c’est la seule solution pour résoudre les problèmes de liaison). Mais avec les fonctions – oui, c’est facultatif.

Vous devez distinguer deux concepts distincts: la définition de fonction et la déclaration de symbole. “extern” est un modificateur de liaison, une indication pour le compilateur à propos de la définition du symbole mentionné ci-après (l’indicateur est “not here”).

Si j’écris

 extern int i; 

dans la scope du fichier (en dehors d’un bloc fonction) dans un fichier C, vous dites alors “la variable peut être définie ailleurs”.

 extern int f() {return 0;} 

est à la fois une déclaration de la fonction f et une définition de la fonction f. La définition dans ce cas remplace l’externe.

 extern int f(); int f() {return 0;} 

est d’abord une déclaration, suivie de la définition.

L’utilisation de extern est incorrecte si vous souhaitez déclarer et définir simultanément une variable de scope de fichier. Par exemple,

 extern int i = 4; 

donnera une erreur ou un avertissement, en fonction du compilateur.

L’utilisation de extern est utile si vous voulez explicitement éviter la définition d’une variable.

Laisse-moi expliquer:

Disons que le fichier ac contient:

 #include "ah" int i = 2; int f() { i++; return i;} 

Le fichier ah comprend:

 extern int i; int f(void); 

et le fichier bc contient:

 #include  #include "ah" int main(void){ printf("%d\n", f()); return 0; } 

L’extérieur de l’en-tête est utile, car il indique au compilateur pendant la phase de liaison, “ceci est une déclaration et non une définition”. Si je supprime la ligne dans ac qui définit i, lui alloue de l’espace et lui atsortingbue une valeur, le programme ne devrait pas comstackr avec une référence non définie. Cela indique au développeur qu’il a fait référence à une variable, mais ne l’a pas encore définie. Si, par contre, j’omets le mot-clé “extern” et que int i = 2 supprime la ligne int i = 2 , le programme comstack toujours – je serai défini avec une valeur par défaut de 0.

Les variables de scope de fichier sont implicitement définies avec une valeur par défaut de 0 ou NULL si vous ne leur affectez pas explicitement une valeur, contrairement aux variables de scope de bloc que vous déclarez en haut d’une fonction. Le mot-clé extern évite cette définition implicite et évite ainsi les erreurs.

Pour les fonctions, dans les déclarations de fonction, le mot clé est en effet redondant. Les déclarations de fonction n’ont pas de définition implicite.

Le mot-clé extern prend différentes formes en fonction de l’environnement. Si une déclaration est disponible, le mot clé extern prend le lien comme indiqué précédemment dans l’unité de traduction. En l’absence d’une telle déclaration, extern spécifie un lien externe.

 static int g(); extern int g(); /* g has internal linkage */ extern int j(); /* j has tentative external linkage */ extern int h(); static int h(); /* error */ 

Voici les paragraphes pertinents du projet C99 (n1256):

6.2.2 Liaisons d’identificateurs

[…]

4 Pour un identifiant déclaré avec le spécificateur de classe de stockage extern dans une scope dans laquelle une déclaration préalable de cet identifiant est visible, 23) si la déclaration préalable spécifie une liaison interne ou externe, la liaison de l’identifiant à la déclaration ultérieure est la même comme le lien spécifié à la déclaration préalable. Si aucune déclaration préalable n’est visible ou si la déclaration préalable ne spécifie aucun lien, l’identificateur a un lien externe.

5 Si la déclaration d’un identificateur pour une fonction n’a pas de spécificateur de classe de stockage, sa liaison est déterminée exactement comme si elle avait été déclarée avec le spécificateur de classe de stockage extern. Si la déclaration d’un identificateur pour un object a une scope de fichier et aucun spécificateur de classe de stockage, sa liaison est externe.

Les fonctions en ligne ont des règles spéciales sur ce que signifie extern . (Notez que les fonctions en ligne sont une extension C99 ou GNU; elles n’étaient pas dans l’original C.

Pour les fonctions non intégrées, extern n’est pas nécessaire car il est activé par défaut.

Notez que les règles pour C ++ sont différentes. Par exemple, extern "C" est nécessaire sur la déclaration C ++ des fonctions C que vous allez appeler depuis C ++, et il existe différentes règles concernant l’ inline .

Le mot clé extern informe le compilateur que la fonction ou la variable a un lien externe – en d’autres termes, qu’il est visible à partir de fichiers autres que celui dans lequel il est défini. En ce sens, il a le sens inverse du mot-clé static . C’est un peu bizarre de mettre extern au moment de la définition, car aucun autre fichier n’aurait de visibilité sur la définition (ou cela aboutirait à des définitions multiples). Normalement, vous placez extern dans une déclaration à un moment donné avec une visibilité externe (comme un fichier d’en-tête) et placez la définition ailleurs.

déclarer une fonction extern signifie que sa définition sera résolue au moment de la liaison, pas pendant la compilation.

Contrairement aux fonctions normales, qui ne sont pas déclarées extern, elles peuvent être définies dans n’importe quel fichier source (mais pas dans plusieurs fichiers sources, sinon vous aurez une erreur de l’éditeur de liens). dont il est déclaré extern.So, dans le cas où le lieur résout la définition de la fonction dans le même fichier.

Je ne pense pas que cela serait très utile, mais faire ce genre d’expériences permet de mieux comprendre le fonctionnement du compilateur et de l’éditeur de liens du langage.

La raison pour laquelle il n’a aucun effet est que, au moment de la liaison, l’éditeur de liens tente de résoudre la définition externe (dans votre cas, extern int f() ). Peu importe qu’il le trouve dans le même fichier ou dans un fichier différent, tant qu’il est trouvé.

J’espère que ça répond à ta question.