Pourquoi la taille d'une fonction en C est-elle toujours de 1 octet?

87

Lorsque nous vérifions la taille d'une fonction en utilisant sizeof(), nous obtenons toujours 1 octet . Que signifie ce 1 octet?

user1645461
la source

Réponses:

80

C'est une violation de contrainte, et votre compilateur devrait le diagnostiquer. S'il le compile malgré cela, votre programme a un comportement indéfini [merci à @Steve Jessop pour la clarification du mode d'échec, et voir la réponse de @Michael Burr pour savoir pourquoi certains compilateurs permettent cela]: De C11, 6.5.3.4./ 1:

L' sizeofopérateur ne doit pas être appliqué à une expression qui a le type de fonction

Kerrek SB
la source
11
C'est une contrainte, ce qui signifie que dans un compilateur conforme, il est diagnostiqué. Si le compilateur le compile quand même (après l'avoir diagnostiqué), alors le comportement n'est pas défini. Si le compilateur ne le diagnostique pas (ce que par exemple gcc ne manque pas -pedantic), vous avez un compilateur non conforme et chaque programme a un comportement non défini.
Steve Jessop
1
Le comportement le place dans la même catégorie qu'une extension GNU C, mais je n'ai aucune idée de pourquoi quelqu'un veut ce comportement, donc je ne sais pas pourquoi les auteurs de GNU ont fait tout leur possible pour l'ajouter.
Steve Jessop
1
@SteveJessop: Notez que c'est égal avec -std=c11, pas gnu11 . C'est une extension de compilateur vraiment bizarre.
Kerrek SB le
6
Ooh! Je parie que c'est pour activer l'arithmétique sur les pointeurs de fonction, de la même manière que sizeof(void)1 dans GNU C.
Steve Jessop
3
Concernant -std=c11: quelqu'un devrait renvoyer les -std=c*options aux normes de publicité. Ils n'activent pas le mode de conformité, ils désactivent simplement les extensions qui empêcheraient la compilation d'un programme bien formé (comme typeofêtre un mot-clé, car un programme C bien formé peut l'utiliser comme nom de variable, mais gccpar défaut, rejetterait cela ). Pour désactiver en outre les extensions qui permettent aux programmes mal formés de passer sans diagnostic, vous avez besoin de -pedanticou -pedantic-errors.
Steve Jessop le
56

Ce n'est pas un comportement indéfini - la norme du langage C nécessite un diagnostic lors de l'utilisation de l' sizeofopérateur avec un désignateur de fonction (un nom de fonction) car il s'agit d'une violation de contrainte pour l' sizeofopérateur.

Cependant, en tant qu'extension du langage C, GCC autorise l'arithmétique sur les voidpointeurs et les pointeurs de fonction, ce qui se fait en traitant la taille d'une voidou d'une fonction comme 1. En conséquence, l' sizeofopérateur évaluera 1pour voidou une fonction avec GCC. Voir http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html#Pointer-Arith

Vous pouvez demander à GCC d'émettre un avertissement lors de l'utilisation sizeofavec ces opérandes en utilisant les options -pedanticou -Wpointer-arithde GCC. Ou faites-en une erreur avec -Werror=pointer-arith.

Michael Burr
la source
Votre logique est imparfaite. C nécessite des messages de diagnostic pour certains UB, mais pas tous. Vous ne pouvez pas déclarer que quelque chose a défini un comportement simplement parce qu'il y a un diagnostic.
MSalters le
4
C nécessite un diagnostic pour toutes les violations de contrainte (5.1.1.3 Diagnostics en C99 ou C11). Une contrainte (3.8 en C99 / C11) est une "restriction, soit syntaxique, soit sémantique, par laquelle l'exposition des éléments du langage doit être interprétée", qui semble dire que quelque chose qui ne suit pas les contraintes ne peut pas être interprété .
Michael Burr
3
Et pour être clair, une violation de contrainte n'entraîne pas un comportement indéfini. C'est une erreur, comme une erreur de syntaxe. Par exemple, la norme dit, "si une exigence" doit "ou" ne doit pas "qui apparaît en dehors d'une contrainte est violée, le comportement n'est pas défini". Si une violation de contrainte aboutissait à UB, pourquoi la norme ne parlerait-elle que des «doivent» et «ne doivent pas» qui ne sont pas ici des contraintes?
Michael Burr
3
Je n'ai vraiment rien dit sur UB, sauf qu'une sizeoffonction n'est pas UB (ce que j'ai mentionné à peu près uniquement parce que d'autres réponses ont déclaré que c'était UB). Mais j'ai peut-être confondu cela à cause de la façon dont j'ai structuré la phrase. Pour être plus clair. sizeofune fonction n'est pas UB (comme plusieurs réponses l'ont affirmé). C'est une violation de contrainte. En tant que tel, il nécessite un diagnostic. GCC l'autorise en tant qu'extension.
Michael Burr
2
@KerrekSB: l'OP n'obtient pas de diagnostic car il utilise probablement GCC, ce qui permet cette utilisation comme extension de langage C.
Michael Burr
13

Cela signifie que le rédacteur du compilateur a décidé d'une valeur de 1 plutôt que de faire voler les démons de votre nez (en fait, c'était une autre utilisation non définie de sizeofqui nous a donné cette expression: "le compilateur C lui-même DOIT émettre un diagnostic SI c'est le premier requis diagnostic résultant de votre programme, puis PEUT lui-même faire voler des démons de votre nez (ce qui, au fait, pourrait bien ÊTRE le message de diagnostic documenté) tout comme il PEUT émettre des diagnostics supplémentaires pour d'autres violations des règles de syntaxe ou des contraintes (ou, d'ailleurs, quelle que soit la raison qu'il choisit). " https://groups.google.com/forum/?fromgroups=#!msg/comp.std.c/ycpVKxTZkgw/S2hHdTbv4d8J

De là, il y a un terme d'argot "démons nasaux" pour tout ce qu'un compilateur décide de faire en réponse à une construction non définie. 1est le démon nasal de ce compilateur pour ce cas.

Jon Hanna
la source
@IlmariKaronen Je dois admettre cependant qu'il y a une raison pour laquelle la plupart de mes réponses sur C (et C ++) sont soit sur des principes généraux indépendants du langage, soit sur des pépites historiques comme ça. Mon expérience C est à la limite de l'historique en soi :)
Jon Hanna
7

Comme d'autres l'ont souligné, sizeof () peut prendre n'importe quel identifiant valide, mais il ne retournera pas de résultat valide (un résultat honnêtement vrai et valide) pour les noms de fonction. De plus, cela peut certainement, ou non, entraîner le syndrome des «démons sans nez».

Si vous souhaitez profiler la taille de la fonction de votre programme, vérifiez la carte de l'éditeur de liens, qui se trouve dans le répertoire des résultats intermédiaires (celui où les éléments sont compilés dans .obj / .o ou où se trouve l'image / l'exécutable résultant). Parfois, il existe une option pour générer ou non ce fichier de carte ... il dépend du compilateur / éditeur de liens.

Si vous vouliez la taille d'un pointeur vers une fonction, ils ont tous la même taille, la taille d'un mot d'adressage sur votre cpu.

jpinto3912
la source
1
Quelle sorte de déclaration est "il peut certainement ou non" ??? N'est-ce pas vrai pour tout?
Kerrek SB le
@KerrekSB oui, mais ici, il peut ou non faire quoi que ce soit, et toujours être dans les règles. Il est vrai qu'un compilateur peut ou non refuser de compiler int x = 1;mais un seul d'entre eux est autorisé pour un compilateur conforme aux normes. Lorsqu'elle sizeof()est appliquée à une fonction, elle peut ou non retourner une valeur définie, ou refuser de compiler, ou renvoyer une valeur aléatoire basée sur ce qui se trouve dans un registre particulier à ce moment-là. Les démons nasaux littéraux sont peu probables, mais dans la lettre de la norme.
Jon Hanna
@kerrek Cela peut être vrai pour rien ou ne pas être faux pour quoi que ce soit ... considérez cela comme étant illogique et flou.
jpinto3912
1
Si vous souhaitez connaître la taille d'un pointeur sur une fonction, appliquez-le sizeofà un pointeur sur une fonction.
alexis le