27 votes

Quelle est la différence entre les variables globales en C et C ++?

J'ai testé le code suivant:

dans le fichier a.c/a.cpp

int a;

dans le fichier b.c/b.cpp

int a;
int main() { return 0; }

Quand je compile les fichiers source avec gcc *.c -o test, il réussit.

Mais quand je compile les fichiers source avec g++ *.c -o test, il échoue:

ccIJdJPe.o:b.cpp:(.bss+0x0): multiple definition of 'a'
ccOSsV4n.o:a.cpp:(.bss+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

Je suis vraiment confus au sujet de cette. Quelle est la différence entre les variables globales en C et C++?

29voto

lpapp Points 22857

Voici les parties pertinentes de la norme. Voir mon explication ci-dessous le texte standard:

§6.9.2/2 Externe de l'objet de définitions

Une déclaration d'un identificateur d'un objet qui a de la portée du fichier sans un initialiseur, et sans stockage de classe rédacteur de devis ou avec la capacité de stockage de classe spécificateur de statique, constitue une tentative de définition. Si une unité de traduction contient un ou plusieurs provisoire définitions pour un identificateur, et l'unité de traduction ne contient pas de définition externe pour l'identifiant, puis le comportement est exactement comme si l'unité de traduction contient un fichier de la portée de la déclaration de l'identificateur, avec le type composite à compter de la fin de l'unité de traduction, avec un initialiseur égal à 0.

La norme ISO C99 §6.9/5 des définitions Externes

Une définition externe est une déclaration externe qui est aussi la définition d'une fonction (autre qu'une ligne de définition) ou un objet. Si un identificateur déclaré avec une liaison externe est utilisé dans une expression (autre que dans le cadre de l'opérande d'un opérateur sizeof dont le résultat est un entier constant), quelque part dans l'ensemble du programme, il doit y avoir exactement une définition externe de l'identificateur; sinon, il n'y aura plus qu'un seul.

Avec la version C, le 'g' variables globales sont "fusionnées" dans un, donc vous aurez seulement à la fin de la journée, qui est déclarée deux fois. C'est OK, en raison du temps lors de la extern n'était pas nécessaire, ou peut-être n'a pas de sorties. Donc, c'est pour l'historique et compatibilité des raisons de construire de l'ancien code. C'est un gcc extension pour cet héritage de la fonctionnalité.

Il rend fondamentalement gcc allouer de la mémoire pour une variable avec le nom "a", donc il peut y avoir plus d'une déclarations, mais une seule définition. C'est pourquoi le code ci-dessous ne fonctionnera pas, même avec gcc.

Il est également appelé tentative de définition. Il n'y a pas une telle chose avec C++, et c'est alors qu'il compile. C++ n'a pas de notion de provisoire de déclaration.

Une tentative de définition des données externes déclaration qui n'a pas de classe de stockage de prescripteur et le pas de l'initialiseur. Une tentative de définition est une définition complète si la fin de l'unité de traduction est atteint et aucune définition n'est apparu avec un initialiseur de l'identificateur. Dans ce cas, le compilateur réserve non initialisée espace pour l'objet défini.

Notez cependant que le code suivant ne compile pas, même avec gcc parce que c'est la tentative de définition de la déclaration de plus avec les valeurs attribuées:

dans le fichier "a.c/a.cpp"

int a = 1;

dans le fichier "b.c/b.cpp"

int a = 2;
int main() { return 0; }

Laissez-nous aller au-delà de cela avec d'autres exemples. Les instructions suivantes montrent normale définitions et de tentative de définitions. Remarque, statique d'en faire un peu la différence puisque c'est à la portée de fichier, et ne serait pas externe plus.

int i1 = 10;         /* definition, external linkage */
static int i2 = 20;  /* definition, internal linkage */
extern int i3 = 30;  /* definition, external linkage */
int i4;              /* tentative definition, external linkage */
static int i5;       /* tentative definition, internal linkage */

int i1;              /* valid tentative definition */
int i2;              /* not legal, linkage disagreement with previous */
int i3;              /* valid tentative definition */
int i4;              /* valid tentative definition */
int i5;              /* not legal, linkage disagreement with previous */

Pour plus de détails sur la page suivante:

http://c0x.coding-guidelines.com/6.9.2.html

Voir aussi ce billet de blog pour plus de détails:

http://ninjalj.blogspot.co.uk/2011/10/tentative-definitions-in-c.html

5voto

Charles Bailey Points 244082

gcc met en œuvre un héritage où non initialisé les variables globales sont placés dans un bloc commun.

Bien que dans chaque unité de traduction, les définitions sont indicatives, en ISO C, à la fin de l'unité de traduction, des tentatives de définitions sont "mis à niveau" pour les définitions complètes si elles n'ont pas déjà été fusionnées en une non-tentative de définition.

En C standard, il est toujours incorrect d'avoir les mêmes variables avec une liaison externe définie plus qu'une unité de traduction, même si ces définitions est venu à partir d'une définition indicative.

Pour obtenir le même comportement que le C++, vous pouvez utiliser l' -fno-common interrupteur avec gcc et le résultat sera le même message d'erreur. (Si vous utilisez l'éditeur de liens GNU et de ne pas utiliser -fno-common vous pourriez aussi envisager d'utiliser l' --warn-common / -Wl,--warn-common option pour sélectionner le temps de lien de comportement sur rencontre de plusieurs courants et non courants des symboles avec le même nom.)

À partir de la page de man de gcc:

-fno-common

Dans le code C, contrôle l'emplacement de non initialisée mondiale les variables. Unix compilateurs C sont traditionnellement autorisés multiples la définition de ces variables dans les différentes unités de compilation par placer les variables dans un bloc commun. C'est le comportement spécifié par -fcommon, et est la valeur par défaut de GCC sur la plupart des les cibles. D'autre part, ce comportement n'est pas requis par la norme ISO C, et sur certaines cibles peuvent porter une vitesse ou la taille du code de pénalité sur les références à des variables. L' -fno-common option spécifie que l' compilateur doit placer non initialisé les variables globales dans les données la section de l'objet du fichier, plutôt que de les générer en tant que commune les blocs. Cela a pour effet que, si la même variable est déclarée (sans extern) dans deux compilations différentes, vous obtiendrez un plusieurs définition d'erreur lorsque vous le lien entre eux. Dans ce cas, vous doit compiler avec -fcommon à la place. Compilation avec -fno-common est utile sur les objectifs pour lesquels elle offre une meilleure de performance, ou si vous souhaitez vérifier que le programme de travail sur d'autres systèmes de toujours traiter variable non initialisée les déclarations de cette façon.

gcc est le comportement est commune, et il est décrit dans l'Annexe J de la norme (qui n'est pas normatif) qui décrit souvent mise en œuvre des extensions à la norme:

J. 5.11 extérieures Multiples définitions

Il peut y avoir plus d'une définition externe de l'identifiant d'un objet, avec ou sans l'utilisation explicite du mot-clé extern; si les définitions sont en désaccord, ou de plus de l'un est initialisé, le comportement est indéfini (6.9.2).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X