2016-05-09 2 views
3

Les normes C11 parlent du couplage des identificateurs, mais il n'y a pas de discussion évidente sur les règles de liaison des unités de traduction. Ma question est soulevée en compilant deux exemples simples en utilisant clang.Quelles sont les règles pour lier les unités de traduction en C11?

Voici mon premier exemple, qui a deux déclarations de la même fonction, mais avec des types incompatibles:

//testall.c 
extern char myfun(void*); 

int main(){ 
    char a='c'; 
    a=myfun(&a); 
} 

char myfun(char*c){ 
    return *c; 
} 

Puis-je exécuter la commande suivante: $ clang std = c11 testall.c
et les rapports de Clang une erreur:

testall.c:9:10: error: conflicting types for 'myfun'
char myfun(char*c){
^
testall.c:2:17: note: previous declaration is here
extern char myfun(void*);
^
1 error generated.

Je comprends cette erreur, car le pointeur vide et le pointeur sur char sont des types incompatibles.
Ce qui me confond est que lorsque je sépare les deux déclarations en deux unités de traduction différentes, puis de les relier en un seul, clang ne signale aucune erreur:

//test.c 
extern char myfun(void*); 

int main(){ 
    char a='c'; 
    a=myfun(&a); 
} 

// mylib.c 
char myfun(char*c){ 
    return *c; 
} 

J'exécuter cette commande: $ clang std = c11 test.c mylib.c. Clang compile et relie les deux unités de traduction sans signaler d'erreur ou d'avertissement. Je pensais que lier deux unités de traduction suit les règles de la section 6.2.2 Liaisons d'identifiants des normes C11. Mais il semble que ce ne soit pas le cas. Quelqu'un peut-il m'aider à le clarifier?

+0

Ce n'est pas parce que vous ne recevez pas de diagnostic que le code est légal! Traditionnellement, un compilateur ne regarde qu'une seule unité de traduction à la fois (il ne peut donc pas détecter la discordance lors de la compilation) et les informations de type ne sont pas transmises au fichier objet, de sorte que l'éditeur de liens ne peut pas détecter la discordance non plus. –

+0

@NateEldredge Essayez de faire un fichier à la fois au lieu de spécifier les deux sur la ligne de commande. Faire 'gcc a.c b.c' compile et lie en une seule étape. C ne fait pas de nom mangling donc les noms de la fonction ressemblent exactement à l'éditeur de liens. Maintenant, que se passe-t-il si vous avez trois fichiers objets avec 'myfun'? Vous obtiendrez une erreur de redéfinition de la fonction. Donc, clairement, ce que vous avez dit ne peut pas être vrai. –

+0

Bien que 'gcc a.c b.c' exécute à la fois le compilateur et l'éditeur de liens, il s'agit de deux passages distincts qui ne partagent pas d'informations autres que le fichier objet. L'éditeur de liens est capable de détecter plusieurs ** définitions ** du même symbole. Mais dans votre exemple, la fonction 'myfun' est seulement ** définie ** une fois, dans' mylib.c'. Le fichier 'test.c' contient une ** déclaration ** de' myfun' mais pas de ** définition **. Par conséquent, aucune erreur de l'éditeur de liens. –

Répondre

2

Dans votre exemple avec deux fichiers distincts, le comportement n'est pas défini. Voici mon « cas », basé sur la norme C11:

C11 6.2.2 (4):

For an identifier declared with the storage-class specifier extern ... If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

En test.c, myfun a une liaison externe car elle est déclarée avec extern et aucune déclaration préalable est visible.

C11 6.2.2 (5):

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern.

En mylib.c, myfun est déclarée sans spécificateur de classe de stockage, il est donc comme si elle était déclarée avec extern, donc il a une liaison externe dans ce unité de traduction ainsi. .

C11 6.9.1 (7) [Définitions fonction]:

If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit

D'où la définition de myfun dans mylib.c est également une déclaration de myfun (au cas où vous aviez des doutes).

C11 6.2.2 (2):

In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function.

Par conséquent, les deux myfun s désignent la même fonction.

C11 6.2.7 (2):

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

Les deux déclarations de myfun ont des types incompatibles (je pourrais montrer que si vous voulez, mais il est pourquoi Clang plaint dans le seul fichier cas. Par conséquent, le comportement de l'ensemble du programme est indéfini.

Notez que 6.2.7 (2) n'est pas une contrainte donc Clang est pas nécessaire d'émettre un diagnostic lorsqu'il est violé. Cependant, dans le cas d'un seul fichier, il y a une violation effective de contrainte, comme suit se produit sous la rubrique "contraintes":

C11 6.7 (4):

All declarations in the same scope that refer to the same object or function shall specify compatible types.

Alors Clang doit délivrer le diagnostic dans ce cas.

3

Ceci est juste comportement indéfini. C11 ne dit rien sur les liens ou comment ils combinent plusieurs unités de traduction. En pratique, ce n'est pas un problème car il y aurait un fichier d'en-tête avec la déclaration de fonction pour myfun() qui est inclus les deux de ces fichiers C.

+0

Est-ce que votre "comportement non défini" signifie que tout peut arriver comme les normes C11? Ou vous voulez dire que les linkers sont juste hors de la portée des normes C11? –

+0

Je veux dire le premier et le dernier est également vrai. Fondamentalement, les linkers (au moins les linkers Unix et GNU que j'ai utilisés) ne font aucune vérification de type. Donc, un diagnostic n'est pas facile. Mais dans votre exemple particulier, cela fonctionnera aussi comme attendu sur tous les systèmes car 'char *' et 'void *' doivent avoir la même représentation. –