Je sais que les variables globales ne doivent pas être définies dans un en-tête et que nous devrions plutôt utiliser Mais j'ai néanmoins essayé de définir une variable globale dans l'en-tête suivant J'ai des résultats intéressants lorsque vous essayez d'utiliser cet en-tête en C et en C ++ p> in c, j'ai inclus l'en-tête dans mais quand je l'exécute en C ++ avec un code similaire ( extern code> pour ne les déclarer que dans l'en-tête.
lib.h code> strong>: p>
main.c code> et dans
lib.c code>, et il compile et fonctionne simplement bien: p>
lib.h code> et
lib.cpp code> est identique que ci-dessus), il donne un message d'erreur sur la variable
i code> ayant plusieurs définitions: p>
4 Réponses :
Je ne sais pas pourquoi cela fonctionne dans C mais c'est faux en C et C ++. Essayez: // lib.c or lib.cpp
#include "lib.h"
int i = 0;
void add()
{
++i;
}
Si le compilateur est GCC, voir GCC.GNU .org / onlinedocs / GCC / ... . GCC obéit la standard C ici lors de l'utilisation -fno-courant code>, mais sa valeur par défaut est généralement
-fcommon code>.
@Schepler Il n'y a rien à obéir, car la norme C dit simplement que "le comportement est indéfini b>"
Je pense que toute la question concerne la raison pour laquelle il compile en C mais pas en C ++ - pas comment le rendre compilé en C ++.
@Anttihaapala droite, bon point.
Alors, voici la chose délicate à propos du préprocesseur: il copie et pâtes lorsque vous utilisez un #define. Ce qui signifie que le int i; code> que main.cpp voit n'est pas le même
int i; code> que lib.cpp voit. P>
Lorsque je l'exécute en C ++ avec un code analogue, il donne un message d'erreur sur la variable I ayant plusieurs définitions. Pourquoi est-ce? P>
La norme C ++ dit: P>
[basic.def.odr] Chaque programme doit contenir
exactement une définition forte> de toutes les fonctions non lignes ou variable forte> odr-utilisée dans ce programme en dehors d'un déclaration abandonnée; Aucun diagnostic requis. P> blockQuote> lib.cpp (je suppose que votre fichier source "analogue" en C ++) et MAIN.CPP Définissez la variable globale
int i code>. En tant que tel, le programme est mal formé. P>
solution: Ne déclarez que la variable dans l'en-tête. Définissez dans exactement une unité de traduction: p>
xxx pré> blockQquote>
OP demande pourquoi il compile dans C.
@LightnessRacesinorbit N'hésitez pas à répondre à cela. Ils ont également demandé pourquoi il ne compile pas en C ++.
Cette différence de comportement n'est pas une coïncidence, ni un bug dans les compilateurs. C'est l'application stricte des normes C et C ++, qui divergent sur la signification d'avoir plusieurs in c ++, Ceci est défini dans la norme C ++, dans la section [basic.def.odr] em> p>
in c, Ceci est défini dans la norme C11, section 6.9.2 Définitions d'objet externe em>: p>
/ 2: strong> une déclaration d'un identifiant pour un objet qui a une portée de fichier
sans initialisateur, et sans spécificateur de classe de stockage ni avec
Le spécificateur de classe de stockage statique constitue une provisoire
définition. Si une unité de traduction contient un ou plusieurs provisoires
Les définitions d'un identifiant et de l'unité de traduction ne contiennent pas de
Définition externe pour cet identifiant, alors le comportement est exactement
comme si l'unité de traduction contient une déclaration de portée de la portée de cette
Identifiant, avec le type composite à partir de la fin de la traduction
unité, avec une initialisée égale à 0. p>
blockQuote>
Notez que cette clause est libellée d'une manière qui ne dit rien sur le cas où la même variable est définie dans plusieurs unités de traduction. La dernière phrase de la citation standard ci-dessus ne signifie pas que c'est une variable différente dans chaque fichier (pour cela, vous auriez besoin de liaison interne, avec Cette neutralité a une raison: p>
La norme identifie le cas comme comportement endédifié: p>
Mais la norme identifie également le cas avec plusieurs définitions comme une extension commune largement prise en charge, tant que ces définitions ne se contredisent pas les unes des autres: P>
conseil important strong> p>
Pour cette raison, si vous souhaitez écrire un code portable, je recommande fortement d'utiliser int i; code> dans la portée globale. p>
en C ++, il est invalide h2>
int i; code> est une définition d'un objet (non initialisé). Une règle de définition (ODR) forte> ne vous permet pas de définir Plusieurs fois, la même variable globale. p>
en C Il est valide h2>
int i; code> est une définition provisoire
statique code>). Il dit simplement que le comportement est comme si la valeur initiale de la variable serait de 0. p>
extern code>; Si les définitions sont en désaccord, ou plus d'un est
initialisé, le comportement est indéfini p>
blockQuote> li>
ul>
extern code> dans l'en-tête et de définir la valeur dans une seule et unique de l'unité de compilation. Ceci est sûr, clair, sans ambiguïté et fonctionne en C ainsi que C ++. p>
Cela dispose de 2 définitions de 2 fichiers différents qui ont un comportement non défini en C mais fonctionne comme une extension commune. Non plus int i; code> ont une liaison interne.
En effet, ce n'est pas un lien interne, ma faute. J'ai modifié et ajouté la citation qui montre qu'il est parfaitement valide pour avoir plusieurs définitions provisoires dans plusieurs unités de traduction.
@LighessRacsinorbit C'est la seule réponse qui parle de C, mais elle est incorrecte dans cela. Voir C11 Annexe J.2. quelle liste: "Un identifiant avec une liaison externe est utilisé, mais dans le programme, il n'existe pas exactement une définition externe B> pour l'identifiant, [...] (6.9)." je>
@Christophe que le libellé standard parle de multiples déclarations provisoires au sein du même TU, non de TUS.
@LighessRacsinorbit En effet, la façon dont il est libellé ne dit rien sur plusieurs fichiers. Et Antidi a raison à propos de J.2. Mais J.5.11 précise que c'est une extension commune tant que les différentes définitions ne se contredisent pas (qui était ma compréhension et aussi mon expérience). Je modifierai pour terminer ces différents aspects.
@Anttihaapala OK! Vous avez raison sur J.2! Mais il y a aussi J5.11 qui reconnaît qu'il s'agit d'une extension largement prise en charge (bien que non portable). Donc j'ai édité pour éviter de fausses impressions ;-)
Le fait est que lorsque vous regardez dans C, le comportement de cela est indéfini. Il n'est en aucun cas distinct de dire le pointeur NULL DÉRÉFERENDING ou divisant par zéro, ou Écrire aux littéraux strings . Vous discutez que cela va bien, juste parce qu'il est répertorié dans les extensions communes.
"Notez que cette clause est libellée d'une manière qui ne dit rien sur le cas où la même variable est définie dans plusieurs unités de traduction" i> droite, car il ne s'agit pas de cette affaire, et donc non plus t lié à l'affaire dans la question.
Commencez par éliminer
en utilisant NAMESPACE STD; code>, c'est exactement le genre de chose qu'il peut causer.
@Tzaulmen j'ai essayé et ça ne marche pas.
Hmm, laissez-moi courir à travers mon IDE
Comment compilez-vous les deux programmes? Afficher la ligne de commande de compilation. En outre, la variable
i code> est ininitialisée.
Ceci n'est pas valide dans les deux langues, mais au moins GCC (en mode C) le permet par défaut - Quel compilateur utilisez-vous?
@aschepler invalide? Pourquoi? C'est une mauvaise idée, mais valable ... Avez-vous voulu utiliser une variable non initialisée?
@ Eugenesh.Chis jette une erreur de compilateur dans MSVC14, comme pour OP.
@Eugenesh. Je ne sais pas sur C, mais en C ++, il viole la règle d'une définition.
@Eugenesh. Violation de l'ORD. En C, c'est une définition provisoire, mais a toujours un lien externe, et chaque TU agira comme s'il y a une définition avec l'initialisateur zéro. En C ++, il ne s'agit que de deux définitions, période.
Le code est un peu malade de mauvaises pratiques, je crois que je connais la réponse, cependant.
@schepler ahh. Je n'ai pas remarqué que nous avons deux i> TUS, y compris l'en-tête ...
L'en-tête est copié dans le fichier du CPP (c'est ce que #include). Donc, si vous l'incluez dans deux fichiers CPP, vous venez de définir deux globaux différents avec le même nom. C'est littéralement le message d'erreur que vous avez posté, alors ... c'est un bon message d'erreur, et pas du tout surprenant.
C ++ et C ont à la fois une règle d'une définition. La différence est que c dit que si un comportement violé n'est pas défini. Certains lieurs b> C permettent de relier des variables ininitialisées avec le même nom, voir Extensions communes C11 J.5, J.5.11 Plusieurs définitions externes . Cela ne fonctionne pas par exemple dans MSVC.
Strictement, il compile à la fois C et C ++ - l'erreur est une erreur de liaison plutôt qu'une erreur de compilateur.
@Tzalumen Cela n'a rien à voir avec
à l'aide de NAMESPACE STD; code>.
@Clifford strictement la norme C n'utilise pas le verbe compile i> du tout, mais traduisez i>; Le mot se compilet elle-même est sémantiquement ambigu; Il est généralement communiqué d'être un synonyme de traduire i> dans les environnements communs C. C ne nécessite même pas de lieur, on vient de dire que c'est une possibilité.
@Anttihaapala: D'accord, mais je parle de la mise en œuvre des termes et de la chaîne d'outils utilisés par l'OP dans cette question i> question, pas la norme C. La distinction a pratique pratique i> plutôt que académique i> utilitaire lors de la correction des problèmes ou de la compréhension des diagnostics.