1
votes

Pourquoi dois-je déclarer un global comme `extern` si je veux l'utiliser?

Je comprends que les variables de portée fichier en C sont par défaut extern - même si cela n'est pas spécifié explicitement (comme expliqué dans cette réponse à Les variables globales en C sont statiques ou non? ).

Cependant, je sais aussi que si ah a déclaré extern int x = 10; , et bc veut accéder à x < / code>, il doit le déclarer comme extern .

Pourquoi en est-il ainsi? Pourquoi ne pas simplement accéder à x sans aucun élément supplémentaire extern ? Quelle est l'explication technique de ce mécanisme?


3 commentaires

Je comprends que les variables de portée de fichier en C sont par défaut extern - non, elles ne le sont pas. Ils peuvent être externes édités dans d'autres unités de traduction à moins qu'ils ne soient statiques . Les fichiers h ont un peu à voir avec cela car ils sont simplement collés textuellement dans les sources. Si vous avez extern int x dedans, c'est le même que vous le mettriez dans le fichier c inclus.


Voir aussi Comment utiliser extern pour partager des variables entre des fichiers sources?


Notez que lorsqu'il est réglé sur difficile (avec -Werror ), GCC (9.1.0) dit: ext59.c: 1: 12: error: 'x' initialisé et déclaré 'extern' [ -Werror] lorsqu'on vous demande de compiler extern int x = 10; - vous ne devez pas combiner extern avec un initialiseur. (Sans -Werror , c'est juste un avertissement qui apparaît même sans demande d'avertissement.)


3 Réponses :


1
votes

Parce que la variable est externe au fichier; il est défini dans un autre fichier, et vous dites simplement à ce fichier qu'il existe, mais il ne le trouvera pas. Ensuite, c'est le travail de l'éditeur de liens de résoudre cela.

Exemple:

// b.c
#include "a.h"

ac a défini la variable

// a.h
extern int x;

ah sait que la variable existe, mais il ne sait pas où. Tout fichier qui inclut ah gagnera cette connaissance

// a.c
int x = 7;

bc, car il a inclus ah, sait maintenant que la variable x existe, mais ne sait pas où. p>

L'éditeur de liens résoudra ces différentes utilisations de la même variable x. Ils feraient mieux d'être les mêmes, sinon il y aura des problèmes.

Vous pourriez mentir à ah, et écrire un float au lieu de int , et seulement le L'éditeur de liens peut remarquer que, parce que le compilateur n'a littéralement aucune connaissance sur ac (enfin, ac devrait inclure ah, donc il le remarquera, mais vous pourriez lui mentir si vous ne l'incluez pas)

Dans le l'ensemble du projet, il doit y avoir une et une seule définition non externe de chaque variable. 0 ou 2 ou plus, et l'éditeur de liens signalera une erreur.


0 commentaires

1
votes

Le stockage d'une variable globale n'est alloué qu'une seule fois. Par exemple, dans main.c:

extern int gMyCounter;

Dans tout autre module (fichier C) où vous voulez utiliser ou accéder à cette variable, vous devez dire au compilateur sur son existence et son type. Vous faites cela avec le mot-clé extern , par exemple dans mymodule.c:

int gMyCounter;

Pendant le temps de liaison, l'éditeur de liens voit que mymodule.o a besoin de la variable gMyCounter et la recherche dans les autres fichiers et bibliothèques .o. Il le trouve dans main.o.


7 commentaires

Donc, si je comprends bien - déclarer une variable avec extern signifie "cette variable est définie ailleurs". Et l'endroit où il est défini n'a pas besoin de dire extern ?


@AvivCohn Pour gMyCounter déclaré à la portée du fichier avec lien externe: (1) extern int gMyCounter; (pas d'initialiseur) signifie qu'il est défini ailleurs; (2) int gMyCounter; (pas d'initialiseur) signifie qu'il est provisoirement défini ici, mais pourrait être entièrement défini ailleurs; (3) extern int gMyCounter = 0; et int gMyCounter = 0; (avec initialiseur) signifient tous deux qu'il est entièrement défini ici. Le mot clé extern est redondant s'il existe un initialiseur.


@IanAbbott, donc extern int gMyCounter = 1; dans mymodule.c lui allouerait de nouveau du stockage ? (Comme extern int gMyCounter; dans main.c allouerait également du stockage?). Cela conduira-t-il à une plainte de l'éditeur de liens concernant un symbole à définition multiple?


@PaulOgilvie extern int gMyCounter; n'alloue pas de stockage, mais extern int gMyCounter = 1; le fait. Si une variable est définie dans plus d'une unité de traduction, l'éditeur de liens se plaindra sauf sur les systèmes qui placent les variables non résolues et provisoirement définies d'une unité de traduction (qui seront automatiquement initialisées à 0) dans une section BSS (stockage partagé en bloc) et réduisent plusieurs définitions de la même variable non résolue, définie provisoirement à partir de différentes unités de traduction en une seule variable. Cela sort du cadre du standard C, mais c'est assez courant, hérité de Fortran.


@IanAbbott: En général, plusieurs définitions provisoires et une ou zéro définition normale seront résolues, mais plusieurs définitions normales entraîneront une erreur.


@EricPostpischil Oui, c'est exact. Merci de clarifier. Mon excuse est que j'ai manqué d'espace dans le commentaire. Pour être clair, l'éditeur de liens peut réduire toutes les variables automatiquement initialisées avec un lien externe avec le même nom provenant de différentes unités de traduction en une seule variable par nom.


@EricPostpischil Il semble que vous ayez raison de combiner les définitions provisoires et normales de la même variable dans différentes unités de traduction également. Merci encore pour la clarification. (Maintenant, je dois comprendre comment cela fonctionne dans les coulisses.)



0
votes

Le fichier d'en-tête ah ne doit pas contenir

#include "a.h"

mais juste

int x = 10;

Ensuite le fichier ac devrait définir

extern int x;

et quand le fichier bc veut utiliser x il devrait simplement p>

extern int x = 10;

et le compilateur, lors de la compilation de bc sauront que x est défini ailleurs, et l'éditeur de liens trouvera la référence et le réparer.


À partir de l'édition du lien ajouté: si la définition est dans une unité de compilation qui n'est pas vue par le compilateur alors la variable ne devient pas disponible comme par magie: vous devez la déclarer comme extern là où le compilateur peut voir il sait donc qu'il sera disponible. Il sert un peu le même objectif qu'un prototype de fonction: c'est une suppression, pas une définition.


0 commentaires