30 votes

Définitions de structures et de fonctions dans la portée

Donc, autant que je sais, c'est légal dans C:

foo.c

struct foo {
   int a;
};

bar.c

struct foo {
    char a;
};

Mais la même chose avec des fonctions est illégal:

foo.c

int foo() {
    return 1;
}

bar.c

int foo() {
    return 0;
}

et entraînera une erreur de couplage (plusieurs définition de la fonction foo).

Pourquoi est-ce? Quelle est la différence entre struct noms et les noms de fonction qui fait C incapables de gérer une mais pas l'autre? Aussi ce comportement ne s'étendre à C++?

25voto

R Sahu Points 24027

Pourquoi est-ce?

struct foo {
   int a;
};

définit un modèle pour créer des objets. Il ne crée pas des objets ou des fonctions. À moins d' struct foo est utilisé quelque part dans votre code, autant que le compilateur/linker est concerné, ces lignes de code peut aussi bien ne pas exister.

Veuillez noter qu'il existe une différence dans la façon dont le C et le C++ traiter incompatible struct définitions.

Les différentes définitions de l' struct foo dans votre posté code est ok dans un programme C, tant que vous ne mélangez pas leur utilisation.

Cependant, il n'est pas légal en C++. En C++, ils ont une liaison externe et doit être définie de manière identique. Voir 3.2 définition de la règle/5 pour plus de détails.

19voto

AndreyT Points 139512

La distinction d'un concept dans ce cas est appelé couplage.

En C struct, union ou enum balises ont pas de lien. Ils sont effectivement locales à leur portée.

6.2.2 les identificateurs
6 Les identifiants suivants ont pas de lien: un identificateur déclaré être rien d'autre que un objet ou une fonction; un identificateur déclaré être un paramètre de la fonction; un bloc de portée l'identificateur d'un objet déclaré sans stockage de classe spécificateur extern.

Ils ne peuvent pas être de nouveau déclarée dans le même champ d'application (sauf pour soi-disant sa déclaration). Mais ils peuvent être librement re-déclaré dans des champs d'application différents, y compris les différentes unités de traduction. Dans des champs d'application différents, ils pourront déclarer complètement indépendant types. C'est ce que vous avez dans votre exemple: dans deux différentes unités de traduction (c'est à dire dans deux fichiers différents scopes) que vous avez déclarée à deux distincts et non reliés struct foo types. C'est parfaitement légal.

Pendant ce temps, les fonctions ont des liens en C. Dans votre exemple de ces deux définitions de définir la même fonction foo avec externe de liaison. Et vous n'êtes pas autorisé à fournir plus d'une définition de la liaison externe de la fonction dans l'ensemble de votre programme

6.9 des définitions Externes
5 [...] Si un identificateur déclaré externes le couplage est utilisé dans une expression (autre que dans le cadre de l'opérande d'un sizeof ou _Alignof de l'opérateur dont le résultat est un entier constant), quelque part dans l'ensemble de la programme il doit y avoir exactement une définition externe de l'identifiant; sinon, il n'y ne doivent pas être à plus d'un.


En C++, la notion de lien est étendu: il attribue des lien à un éventail beaucoup plus large d'entités, y compris les types. En C++ de la classe de types de liaison. Les Classes déclarées dans l'espace de noms de la portée ont une liaison externe. Et Une Définition de la Règle de C++ stipule explicitement que, si une classe avec une liaison externe a plusieurs définitions (à travers les différentes unités de traduction), il doit être défini de manière équivalente dans l'ensemble de ces unités de traduction (http://eel.is/c++le projet de base.def.rll n ° 12). Donc, en C++, votre struct définitions serait illégal.

Vos définitions de fonctions demeurent illégales en C++ à cause de C++ ODR règle (mais essentiellement pour les mêmes raisons que dans C).

13voto

Jonathan Wakely Points 45593

Vos définitions de fonction à la fois de déclarer une entité appelée foo avec une liaison externe, et la norme dit qu'il ne doit pas être plus d'une définition d'une entité avec une liaison externe. La structure des types définis ne sont pas des entités avec une liaison externe, donc vous pouvez avoir plus d'une définition de l' struct foo.

Si vous avez déclaré des objets avec une liaison externe en utilisant le même nom, puis que serait une erreur:

foo.c

struct foo {
   int a;
};
struct foo obj;

bar.c

struct foo {
    char a;
};
struct foo obj;

Maintenant, vous avez deux objets appelés obj que les deux ont une liaison externe, qui n'est pas autorisé.

Il serait toujours mal, même si l'un des objets est déclaré seulement, ne sont pas définis:

foo.c

struct foo {
   int a;
};
struct foo obj;

bar.c

struct foo {
    char a;
};
extern struct foo obj;

Il n'est pas défini, parce que les deux déclarations d' obj font référence au même objet, mais ils n'ont pas de types compatibles (parce qu' struct foo est défini différemment dans chaque fichier).

C++ est similaire, mais des règles plus complexes, afin de tenir compte inline fonctions et inline variables, les modèles, et d'autres fonctionnalités C++. En C++, les exigences pertinentes sont connus sous le nom d'Une Définition de la Règle (ou ODR). Une différence notable est que le C++ n'autorise même pas les deux struct définitions, même si elles ne sont jamais utilisées pour déclarer des objets avec une liaison externe ou autrement "partagé" entre les unités de traduction.

4voto

dbush Points 8590

Les deux déclarations pour struct foo sont incompatibles les uns avec les autres, étant donné que les membres ne sont pas les mêmes. En les utilisant à la fois au sein de chaque unité de traduction est très bien tant que vous ne faites rien de confondre les deux.

Si par exemple, vous avez fait ceci:

foo.c:

struct foo {
   char a;
};

void bar_func(struct foo *f);

void foo_func()
{
    struct foo f;
    bar_func(&f);
}

bar.c:

struct foo {
   int a;
};

void bar_func(struct foo *f)
{
    f.a = 1000;
}

Vous serait en invoquant un comportement indéfini parce que l' struct foo que bar_func attend n'est pas compatible avec l' struct foo que foo_func fournit.

La compatibilité des structures est détaillée dans la section 6.2.7 du C standard:

1 Deux types sont compatibles type si leurs types sont les mêmes. Des règles supplémentaires pour déterminer si les deux types sont compatibles sont décrit dans 6.7.2 pour les spécificateurs de type, dans 6.7.3 pour le type qualificatifs, et dans 6.7.6 pour declarators. En outre, deux de la structure, de l'union, ou les types énumérés déclaré à séparer les unités de traduction sont compatibles si leurs tags et les membres satisfont aux exigences suivantes: Si l'on est déclarée avec un tag, l'autre doit être déclarée avec le même tag. Si les deux sont remplis de n'importe où au sein de leur traduction les unités, puis les conditions supplémentaires suivantes s'appliquent: il est être un one-to-one correspondance entre leurs membres, de sorte que chaque paire de membres correspondants sont déclarées avec des types compatibles; si un membre de la paire est déclarée avec un alignement prescripteur, l' autres est déclarée avec un équivalent d'alignement spécificateur; et si l'on membre de la paire est déclarée avec un nom, l'autre est déclarée avec le même nom. Pour les deux structures, les membres correspondants sont déclaré dans le même ordre. Pour les deux structures ou des syndicats, bit correspondant-champs ont les mêmes largeurs. Pour les deux les énumérations, les membres associés ont les mêmes valeurs.

2 Toutes les déclarations qui font référence au même objet ou la fonction est compatible type; sinon, le comportement est indéfini.

Pour résumer, les deux instances de l' struct foo doit avoir des membres avec le même nom et de même type et dans le même ordre pour être compatible.

De telles règles sont nécessaires afin qu'un struct peuvent être définis une fois pour toutes dans un fichier d'en-tête et en-tête est inclus dans plusieurs fichiers source. Il en résulte dans l' struct étant définie dans plusieurs fichiers sources, mais avec chaque instance étant compatible.

3voto

molbdnilo Points 9289

La différence n'est pas tellement dans les noms que dans l'existence; une structure de définition n'est pas stocké n'importe où et son nom n'existe que lors de la compilation.
(C'est le programmeur est responsable de s'assurer qu'il n'y a pas de conflit dans l'utilisation de l'portant le même nom à des structures. Sinon, notre cher vieil ami Comportement Indéfini appelle à l'aide.)

D'autre part, une fonction doit être stocké quelque part, et si elle a une liaison externe, l'éditeur de liens doit son nom.

Si vous faites vos fonctions static, de sorte qu'ils sont "invisibles" à l'extérieur de leurs respectifs unité de compilation, le lien d'erreur disparaît.

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