Quel est le but de l'h et hh modificateurs pour printf?
Côté de %hn
et %hhn
(où le h
ou hh
spécifie la taille de la -pointu à objet), ce qui est le point de la h
et hh
des modificateurs pour printf
les spécificateurs de format?
Pour défaut des promotions qui sont requis par la norme à appliquer pour les variadic fonctions, il est impossible de passer des arguments de type char
ou short
(ou tout signé/non signé variantes de celle-ci) à printf
.
Selon 7.19.6.1(7), le h
modificateur:
Spécifie qu'une suite de d, i, o, u, x ou x indicateur de conversion s'applique à un
short int ou unsigned short int argument (l'argument ne
ont été promus en fonction de l'entier des promotions, mais sa valeur est
être converti short int ou unsigned short int avant d'imprimer);
ou qu'une suite de n indicateur de conversion s'applique à un pointeur sur un court
argument int.
Si l'argument est en fait de type short
ou unsigned short
, puis la promotion de int
suivie d'une conversion en arrière pour short
ou unsigned short
donnera le même valeur que la promotion de int
sans aucune conversion. Ainsi, pour les arguments de type short
ou unsigned short
, %d
, %u
, etc. devrait donner des résultats identiques à %hd
, %hu
, etc. (et de même pour la char
types et hh
).
Aussi loin que je peux dire, la seule situation où le h
ou hh
modificateur pourrait éventuellement être utile lorsque l'argument passé un int
en dehors de la plage de short
ou unsigned short
, par exemple
printf("%hu", 0x10000);
mais ma compréhension est que passer du mauvais type, comme il en résulte un comportement indéfini de toute façon, de sorte que vous ne pouvait pas s'attendre à imprimer 0.
Un cas réel que j'ai vu est un code comme ceci:
char c = 0xf0;
printf("%hhx", c);
où l'auteur s'attend à imprimer f0
en dépit de la mise en œuvre ayant une plaine char
type signé (dans ce cas, printf("%x", c)
serait d'imprimer fffffff0
ou similaire). Mais est-ce une attente justifiée?
(Note: Ce qui se passe est que le type d'origine a été char
, qui sera promu à int
et converti en unsigned char
au lieu de char
, et donc de changer la valeur qui sera imprimé. Mais la norme spécifier ce problème, ou est-ce un détail d'implémentation qui cassé logiciel peut être en s'appuyant sur?)
- La grande question, et encore plus de vous voir être impitoyablement pointilleux avec la réponse.
- Très belle question. Je suis en train de travailler sur la forme d'un i/o de la mise en œuvre dès maintenant et tout en essayant de suivre la norme, je suis tombé sur la même question. Merci pour l'affichage
Vous devez vous connecter pour publier un commentaire.
Une raison possible: par symétrie avec l'utilisation de ces modificateurs dans la mise en forme des fonctions d'entrée? Je sais qu'il ne serait pas strictement nécessaire, mais peut-être qu'il a valeur perçue pour qui?
Bien qu'ils ne parlent pas de l'importance de la symétrie pour le "h" et "hh" modificateurs dans le C99 Justification document, le comité ne le mentionne comme une contrepartie pour laquelle le "%p" spécificateur de conversion est pris en charge pour
fscanf()
(même si ce n'était pas la C99 - "%p" le support est en C90):Dans la section sur les
fprintf()
, le C99 justification document ne discuter que "hh" a été ajouté, mais simplement renvoie le lecteur à l'fscanf()
section:Je sais que c'est un fil ténu, mais je suis de spéculer, de toute façon, alors j'ai pensé que je donnerais quel que soit l'argument, il pourrait être.
Également, pour être complet, le "h" modificateur était dans l'original norme C89 - il sera sans doute là, même si elle n'était pas strictement nécessaire en raison d'une généralisation de l'utilisation existante, même si il pourrait ne pas avoir été une exigence technique à utiliser le modificateur.
h
ethh
modificateurs?printf("%hu", (unsigned int) 0x10000);
. Je peux imaginer les arguments dans les deux sens - je préfère qu'il a été bien défini, mais pourrait voir que le libellé "Spécifie qu'une suite de d, i, o, u, x ou x indicateur de conversion s'applique à un short int ou unsigned short int argument" jette ce pas défini dans le territoire, même si la suit immédiatement "(l'argument ont été promus en fonction de l'entier des promotions, mais sa valeur doit être convertie en short int ou unsigned short int avant l'impression)" qu'il jette en arrière.printf("%hx",1u);
aurait défini comportement; en revanche, en l'absence de texte précisant que "h" a été un juridique modificateur d'un tel programme serait UB, ne serait-il pas?Dans
%...x
mode, toutes les valeurs sont interprétées comme non signés. Les nombres négatifs sont donc imprimés comme leur unsigned conversions. En complément de 2 arithmétique, dont la plupart des processeurs, il n'y a pas de différence dans les modèles de bits signé entre un nombre négatif et positif, non signé équivalent, qui est définie par le module arithmétique (l'ajout de la valeur maximale du champ, plus un pour le nombre négatif, selon le standard C99). Beaucoup de logiciels - en particulier le débogage de code les plus susceptibles d'utiliser%x
- rend le silencieux hypothèse que la représentation binaire signée d'un négatif de la valeur et de ses unsigned casting est le même, ce qui n'est vrai que dans un 2 en complément de la machine.La mécanique de cette distribution sont telles que hexadécimal de la représentation de la valeur impliquent toujours, probablement à tort, qu'un certain nombre a été rendu en complément de 2, tant qu'il n'a pas touché un bout de où les différents entier représentations ont différentes gammes. Cette même est vrai pour l'arithmétique des représentations où la valeur 0 n'est pas représentée avec le modèle binaire de tous les 0.
Négatif
short
affichée comme uneunsigned long
en hexadécimal sera donc, sur n'importe quelle machine, être rembourré avecf
, en raison implicite de l'extension du signe dans la promotion, quiprintf
à l'impression. Le valeur est le même, mais c'est vraiment visuellement trompeuse quant à la taille du champ, ce qui implique une quantité importante de gamme qui n'est tout simplement pas présent.%hx
tronque l'affichage de la représentation pour éviter ce rembourrage, exactement comme vous l'avez conclu, à partir de votre monde réel de cas d'utilisation.Le comportement de
printf
est pas défini lorsqu'il est passé d'unint
en dehors de la plage deshort
qui doit être imprimé comme unshort
, mais la méthode la plus simple de mise en œuvre, de loin, tout simplement ignore le bit élevé par un brut abattu, alors que les spec n'est pas besoin tout comportement spécifique, à peu près tout sane mise en œuvre va, il suffit de faire la troncature. Il y a généralement de meilleures façons de le faire.Si printf n'est pas les valeurs de remplissage ou de l'affichage unsigned représentations de valeurs signées,
%h
n'est pas très utile.%x
,%u
, ou%o
) entraîne un comportement indéfini. Aussi, autant que je puisse dire, une conforme à la mise en œuvre peut simplement ignorer de la présence deh
ouhh
modificateur sauf avec%n
.(unsigned)
et(signed)
quoi que ce soit, au sein de la même largeur sont garantis de ne pas faire de réelles modifications de la séquence de bits de données, il suffit de l'interprétation de cette séquence de bits. (Jette que de modifier la largeur sont à zéro, étendue d'inscription ou prolongée, le cas échéant.)%x
est défini à travailler sur des valeurs non signées, de sorte qu'ils sont d'abord exprimés, à partir d'signé non signé, ce qui change pas de données, mais ne change pas l'interprétation - en effet, à l'aide de%x
avec un nombre négatif vous présente son modèle de bits. Et%x
est un type entier, et leh
modificateur de travaux sur les types integer, donc je pense que c'est pris en charge.h
sur%x
, citant linux.die.net/man/3/printf , en référence à des modificateurs de longueur: "Ici," conversion d'entier correspond à d, i, o, u, x ou x de conversion." Donc%x
et%X
est, au moins sous Linux, officiellement inclus dans le champ d'application de ce que l'h
modificateur peut être formellement attaché.%hx
est valide. C'est spécifiée par la norme. Mais%hx
implique ununsigned short
argument, qui est promu à un positifint
, qui (par les exigences de la norme) a la même représentation que leunsigned int
valeur. Donc, autant que je peux dire,%x
devrait fonctionner tout aussi bien.UINT_MAX
. Vous avez tout à fait raison que cela arrive juste à faire absolument rien pour le nombre de bits de motif dans un complément de 2 ordinateur. (Jette à un plus petit type non signé de mise en œuvre spécifique, mais pas à la même taille ou plus grand type.) Donc, mon conseil se dresse sur, et uniquement sur des machines pour une utilisation 2 en complément de leur arithmétique entière. Ajustez votre code si vous ciblez un qui ne l'est pas.UINT_MAX
" est erroné. Vous avez oublié le +1, entre autres détails. Une fois que vous le fixer, il devient l'équivalent de l'arithmétique modulaire.%hx
n'a pas de sens pour le compilateur tous le compilateur se soucie seulement de la conversion ascendante deshort
àsigned int
dans les variadic paramètre. Il peut donc faire un signe-les étendre que vous ne souhaitez pas. Bien sûr, cela s'applique uniquement si vous avez passé un signé à court et essayé de l'utiliser comme si il ont été signés. Compte tenu de la façon dont beaucoup d'abusprintf
a été mis à travers les années, ce n'est pas une invraisemblable affaire.%hx
ne devriez pas faire quoi que ce soit quand utilisé strictement légalement, mais il semble sûr de dire que la stricte légalité est peu probable.%hx
est limitée à quandprintf
est utilisé illégalement (pour représenter une signé argument non signés, qui est généralement sans danger, mais n'est sûr que sur un 2 en complément de la machine; le résultat est tout à fait beaucoup de code cassé en commun des bibliothèques sur la non-2'c machines), ce qui en fait intrinsèquement de mise en œuvre spécifiques. Rationnellement, il n'y a aucune utilisation particulière lors de la conversion déjà arrivé dans une très étroite de portée juridique.La seule utilisation que je peux penser est la pour passer un
unsigned short
ouunsigned char
et à l'aide de la%x
indicateur de conversion. Vous ne pouvez pas simplement utiliser un nu -%x
- la valeur peut être promu àint
plutôt queunsigned int
, et puis vous avez un comportement indéterminé.Vos solutions de rechange sont soit de convertir explicitement l'argument de
unsigned
; ou à l'utilisation%hx
/%hhx
avec un nu-argument.unsigned short
ouunsigned char
sera promu àint
, c'est toujours positive, donc C nécessite la représentation pour correspondre à la représentation deunsigned
. Autant que je sache, ce paramètre décalage est valable dans variadic les arguments de la fonction et les arguments de fonctions sans prototypes tant que la valeur est positive, comme une valeur signée. Certainement%x
est prévu pour fonctionner avecint
arguments tant qu'ils sont positifs...printf
de la famille, la norme donne desunsigned int
que le type de l'argument de%x
, et plus tard dit "Si aucun argument n'est pas le type correct pour la spécification de conversion, le comportement est indéfini." - je ne crois pas que vous permet de passer unint
.printf("%x", 1);
(qui aurait besoin d'être1U
au lieu de1
par votre raisonnement).Les variadic arguments pour
printf()
et al sont automatiquement promus en utilisant les conversions par défaut, de sorte que touteshort
ouchar
valeurs promues àint
lorsqu'il est passé à la fonction.En l'absence de la
h
ouhh
modificateurs, vous auriez à masquer les valeurs passées à obtenir le bon comportement de manière fiable. Avec les modificateurs, vous n'avez plus à masquer les valeurs; laprintf()
mise en œuvre fait le travail correctement.Spécifiquement, pour le format
%hx
, le code à l'intérieurprintf()
peut faire quelque chose comme:Je suis allègrement en supposant que
short
est un 16 bits, la norme ne garantit pas que, bien sûr.J'ai trouvé utile pour éviter de jeter lors du formatage de caractères non signés à l'hex:
C'est un mineur de codage de commodité, et l'air plus propre que plusieurs distributions (OMI).
un autre endroit c'est pratique, c'est snprintf vérification de la taille.
gcc7 ajouté à la taille de vérifier lors de l'utilisation de snprintf
si cela ne fonctionne pas
de sorte qu'il vous oblige à utiliser de plus gros char lors de l'utilisation de %d lors de la mise en forme d'un char
ici est un commit qui montre ces correctifs au lieu d'augmenter le char sur la taille du tableau, ils ont changé %d à %h. ce aussi donner plus d'exactitude de la description
https://github.com/Mellanox/libvma/commit/b5cb1e34a04b40427d195b14763e462a0a705d23#diff-6258d0a11a435aa372068037fe161d24
-Wformat-overflow
avertissement, gcc documents qu'il estime "arguments Numériques qui sont connus pour être limité à un sous-groupe de type" qui est toujours le cas pour les promus de la station. Mais le niveau 2 n'a pas de décrire ce comportement...? gcc.gnu.org/onlinedocs/gcc/Warning-Options.htmlJe suis d'accord avec vous qu'il n'est pas strictement nécessaire, et donc, par cette seule raison n'est pas bon dans une fonction de la bibliothèque C 🙂
Il pourrait être "gentil" pour la symétrie des indicateurs différents, mais c'est surtout contre-productif car il cache la "conversion à
int
" la règle.