web-dev-qa-db-fra.com

Pourquoi C autorise plusieurs déclarations globales de la même variable mais PAS plusieurs déclarations locales?

J'ai remarqué que si je déclare plusieurs fois une variable globale, le compilateur ne produit même pas d'avertissement.

Cependant, si je déclare plusieurs fois une variable locale dans une fonction, par exemple, le compilateur gcc génère une erreur et ne compile pas le fichier. (Je demande en termes de gcc, mais c'est plus une question de conception de langage générale, pas une question sur gcc, car je pense qu'il est probable que d'autres compilateurs se comportent de la même manière).

Quelle est l'explication de ce comportement?

8
yoyo_fun

Selon codage-directives :

Dans l'ensemble d'unités de traduction et de bibliothèques qui constitue un programme entier, chaque déclaration d'un identifiant particulier avec liaison externe dénote le même objet ou la même fonction. Au sein d'une unité de traduction, chaque déclaration d'un identifiant avec liaison interne dénote le même objet ou la même fonction. Chaque déclaration d'un identifiant sans lien indique une entité unique.

La variable locale n'a pas de lien. il y a donc un nom Collision se produit. Ainsi, la déclaration multiple de variable locale n'est pas possible.

La variable globale a un lien externe. Ainsi, plusieurs déclarations de variables globales sont possibles.

11
msc

@msc donne une bonne introduction aux règles derrière ce comportement.

J'ai remarqué que si je déclare plusieurs fois une variable globale, le compilateur ne produit même pas d'avertissement.

C a trois types de déclarations globales pour les objets, à savoir ceux qui le sont (et je passe par-dessus static ici):

  1. déclarations qui ne sont pas des définitions - extern int a;
  2. déclarations qui sont aussi des définitions - int a = 3; ou extern int a = 3;
  3. définitions provisoires - int a;

Plusieurs déclarations de type 1 et 3 sont autorisées, tandis qu'au plus une définition (type 2) est autorisée.


Quelle est l'explication de ce comportement?

Si vous posez également des questions sur la motivation de ces règles, c'est le support de compilation séparée. (Voir nité de traduction ).

Afin de diviser un programme en plusieurs fichiers compilés séparément, nous avons besoin de quelques fonctionnalités, à savoir (a) être capable de déclarer sans nécessairement définir , et, (b) déclaration avant.

Au sein d'une unité de traduction, nous devons pouvoir faire référence aux fonctions et données globales dans une autre unité de traduction. Et nous aimerions également une vérification des erreurs, ici, pour découvrir les définitions manquantes et les définitions en double erronées.

Parfois, dans la même unité de traduction, nous déclarons un global, puis le définissons plus tard. Cela peut se produire si nous avons besoin d'une déclaration directe pour une raison quelconque, ou si nous utilisons un fichier d'en-tête commun (qui fournit des déclarations) au sein de l'unité de traduction qui propose également des définitions explicites.

Étant donné que la compilation séparée en C s'applique en reliant les fonctions globales et les données ensemble, ces fonctionnalités sont requises au niveau global mais pas au niveau local.

Comme le souligne @msc, rien de tout cela n'est nécessaire pour les variables locales car elles n'ont pas de lien.

C (comme beaucoup d'autres langues) ne fournit pas de lien pour les variables locales car la langue n'essaye pas de prendre en charge une seule fonction couvrant plusieurs unités de traduction distinctes.

(Bien sûr, vous pouvez avoir une fonction couvrant plusieurs fichiers source, mais pas plusieurs unités de traduction.)

Une définition provisoire fonctionne comme une déclaration en ce sens qu'elle est autorisée dans plusieurs unités de traduction (et se combine également très bien avec d'autres déclarations). Cependant, s'il n'y a pas de définition (non provisoire) pour l'identifiant dans l'ensemble du programme, l'ensemble (une ou plusieurs) définitions provisoires sur plusieurs unités de traduction (pour un identifiant) est considéré comme une définition pour l'objet dont l'initialiseur est zéro.

Ceci peut être implémenté en les mettant dans la . Section BSS avec la taille et l'alignement appropriés; l'éditeur de liens les fera correspondre à la vraie définition s'il est trouvé, ou bien les fera correspondre les uns aux autres, en leur donnant un espace mis à zéro dans BSS.


La notion de compilation séparée peut être entièrement prise en charge sans la fonction de définitions provisoires - je pense que les définitions provisoires sont là principalement pour des raisons historiques. (Je ne dis pas qu'ils ne sont pas utiles, juste si le langage a été créé aujourd'hui, cela pourrait être considéré comme inutile et donc ne pas être proposé.)

9
Erik Eidt