Considérons le programme C suivant :
int f() { return 9; }
int main() {
int (*h1)(int);
h1 = f; // why is this allowed?
return h1(7);
}
Selon la norme C11, Sec. 6.5.16.1, dans une affectation simple, "l'un des éléments suivants doit tenir", et le seul élément pertinent de la liste est le suivant :
l'opérande gauche a un type de pointeur atomique, qualifié ou non qualifié, et (en considérant le type que l'opérande gauche aurait après la conversion en lvalue) les deux opérandes sont des pointeurs vers des versions qualifiées ou non qualifiées de types compatibles, et le type pointé par le gauche a tous les qualificatifs du type pointé par le droit ;
De plus, il s'agit d'une "contrainte", ce qui signifie qu'une mise en œuvre conforme doit signaler un message de diagnostic si elle est violée.
Il me semble que cette contrainte est violée dans l'affectation du programme ci-dessus. Les deux côtés de l'affectation sont des pointeurs de fonction. La question est donc de savoir si les deux types de fonctions sont compatibles. La réponse à cette question se trouve à la Sec. 6.7.6.3 :
Pour que deux types de fonction soient compatibles, les deux doivent spécifier des types de retour compatibles.146) En outre, les listes de types de paramètres, si elles sont toutes deux présentes, doivent concorder quant au nombre de paramètres et à l'utilisation du terminateur d'ellipse ; les paramètres correspondants doivent avoir des types compatibles. Si un type possède une liste de types de paramètres et que l'autre type est spécifié par un déclarateur de fonction qui ne fait pas partie d'une définition de fonction et qui contient une liste d'identifiants vide, la liste de paramètres ne doit pas comporter de terminaison en ellipse et le type de chaque paramètre doit être compatible avec le type qui résulte de l'application des promotions d'arguments par défaut. Si un type possède une liste de paramètres et que l'autre type est spécifié par une définition de fonction qui contient une liste d'identificateurs (éventuellement vide), les deux types doivent s'accorder sur le nombre de paramètres, et le type de chaque prototype de paramètre doit être compatible avec le type qui résulte de l'application des promotions d'arguments par défaut au type de l'identificateur correspondant.
Dans ce cas, l'un des types, celui de h1, a une liste de types de paramètres ; l'autre, f, n'en a pas. Par conséquent, la dernière phrase de la citation ci-dessus s'applique : en particulier, "les deux doivent s'accorder sur le nombre de paramètres". Il est clair que h1 prend un paramètre. Qu'en est-il de f ? Le point suivant intervient juste avant la citation ci-dessus :
Une liste vide dans un déclarateur de fonction qui fait partie d'une définition de cette fonction spécifie que la fonction n'a pas de paramètres.
Donc clairement f prend 0 paramètres. Donc les deux types ne s'accordent pas sur le nombre de paramètres, les deux types de fonction sont incompatibles, et l'affectation viole une contrainte, et un diagnostic doit être émis.
Cependant, gcc 4.8 et Clang n'émettent aucun avertissement lors de la compilation du programme :
tmp$ gcc-mp-4.8 -std=c11 -Wall tmp4.c
tmp$ cc -std=c11 -Wall tmp4.c
tmp$
À propos, les deux compilateurs émettent des avertissements si f est déclaré "int f(void) ...", mais cela ne devrait pas être nécessaire d'après ma lecture de la norme ci-dessus.
Les questions :
Q1 : L'affectation "h1=f ;" dans le programme ci-dessus viole-t-elle la contrainte "les deux opérandes sont des pointeurs vers des versions qualifiées ou non qualifiées de types compatibles" ? Plus précisément :
Q2 : Le type de h1 dans l'expression "h1=f" est pointeur-à-T1 pour un certain type de fonction T1. Quel est exactement le type T1 ?
Q3 : Le type de f dans l'expression "h1=f" est pointeur-à-T2 pour un certain type de fonction T2. Quel est exactement le type T2 ?
Q4 : Les types T1 et T2 sont-ils compatibles ? (Veuillez citer les sections appropriées de la norme ou d'autres documents pour étayer la réponse).
Q1', Q2', Q3', Q4' : Supposons maintenant que la déclaration de f soit modifiée en "int f(void) { return 9 ; }". Répondez à nouveau aux questions 1 à 4 pour ce programme.
1 votes
Si je mets cela dans clang j'obtiens : functCheck.cxx:4:6 : error : assigning to 'int (*)(int)' from incompatible type 'int ()' : different number of parameters (1 vs 0) h1 = f ; // why is this allowed... ^ ~ 1 erreur générée.
6 votes
@user2950041 : C'est une question sur le C. Le C a des notions de déclaration, de prototype et de définition différentes de celles du C++.
0 votes
@KerrekSB Bien sûr. C'est pourquoi j'ai utilisé un compilateur C, j'ai juste la mauvaise habitude de terminer les fichiers par .cxx. Le compilateur s'en moque
7 votes
@user2950041 : En fait, le compilateur s'en soucie. Clang déduit le langage à partir de l'extension du fichier. (Par exemple, vous pouvez mettre des modèles dans votre fichier
cxx
et le compiler sans problème aveccc
; si vous nommez le fichier commefoo.xyz
il ne compilera pas).8 votes
Non pas que je trouve quoi que ce soit qui soutienne cela dans vos citations ci-dessus, mais dans simple vieux c une liste de paramètres vide signifiait que vous pouviez fournir un nombre quelconque d'arguments. Par exemple, void f() != void f(void). Je ne suis pas assez à l'aise avec les donjons profonds du C pour savoir si f() == f(...), mais j'en doute car les varargues ont besoin au moins du premier argument pour s'accrocher.
1 votes
@user2950041 Je pense finalement qu'il s'agit d'un bug, que diriez-vous de déposer un bug dans gcc Bugzilla ?
1 votes
Pour autant que je sache, cela devrait être signalé mais la ligne
h1 = (int (*)())f;
(ou un ensemble équivalent de déclarations qui attribuentf
à une variable de typeint (*)()
) n'a pas besoin d'être signalé puisque le type de l'expression à droite de l'affectation provient d'un déclarateur et non d'une définition. Cela semble être un comportement vraiment bizarre, et je ne suis pas surpris que gcc se trompe.0 votes
@Stian : iirc the
f()
représente un ensemble arbitraire de paramètres dans les déclarations de fonctions, mais je suis loin d'être sûr que cela soit également valable au moment de la définition de la fonction.0 votes
Il est probable que personne n'ait remarqué cette formulation jusqu'à présent, j'aurais simplement supposé qu'il s'agissait d'une formulation équivalente pour les pointeurs de fonctions vers des fonctions réelles
0 votes
@MvG Il est également valable au moment de la définition, il existe un autre texte standard qui couvre tout cela en détail.
1 votes
Aucune des 4 réponses ci-dessous ne répond à ma question, alors laissez-moi la clarifier. Lequel des cas suivants est le cas : (a) il s'agit d'un bogue dans Clang et gcc, ou (b) je lis mal la norme. Si (b), comment exactement ? Je suppose qu'une troisième possibilité est (c) ni Clang ni gcc ne prétendent être des implémentations conformes, et ceci est juste un exemple de la façon dont ils ne le sont pas.
1 votes
La documentation GCC indiquent que c'est une chose qu'ils ne veulent pas changer. Leur raisonnement, cependant, semble indiquer qu'ils ne savent pas que C99 et C11 exigent un diagnostic, ou que nous interprétons mal la norme et que Gcc est conforme à cet égard. Ad (c) : Ils réclamation pour être conforme aux normes (avec les bonnes options).
1 votes
Dépose un rapport de bogue avec LLVM : llvm.org/bugs/show_bug.cgi?id=20313
0 votes
C11 6.5.1(2) : "Un identifiant est une expression primaire, à condition qu'il ait été déclaré comme désignant un objet (auquel cas il s'agit d'une lvalue) ou une fonction (auquel cas il s'agit d'un désignateur de fonction)". 6.3.2.1(4) : "Un désignateur de fonction est une expression qui a un type de fonction. Sauf lorsqu'il s'agit de l'opérande de l'opérateur sizeof, de l'opérateur _Alignof65) ou de l'opérateur unaire &, un désignateur de fonction de type ''type de retour de fonction'' est converti en une expression qui a le type ''pointeur vers le type de retour de fonction''".
0 votes
@SteveSiegel à la lumière de la réponse de Shafik qui vient d'être postée, peut-être que le rapport de bug devrait être rétracté. ( DR316 Q2 est cette question, et la résolution du Comité est que le code devrait être accepté)
0 votes
J'ai clarifié la question exacte en ajoutant 8 sous-questions très spécifiques à la fin. J'espère que cela encouragera les gens à clarifier la ou les réponses.