Quel est le but des modificateurs h et hh pour printf?
-
14-10-2019 - |
Question
En dehors de %hn
et %hhn
(où les spécifie h
ou hh
la taille du pointe à objet), quel est le point des modificateurs de h
et hh
pour spécificateurs format printf
?
En raison de défaut des promotions qui sont requises par la norme à appliquer pour les fonctions variadique, il est impossible de passer des arguments de type char
ou short
(ou les variantes signé / non signé de celui-ci) à printf
.
Selon 7.19.6.1 (7), le modificateur de h
:
spéci fi es que d suivant, i, o, u, x, ou fi spécifique de conversion X er applique à un short int ou argument int court non signé (l'argument ont été promus en fonction des promotions entières, mais sa valeur doit être converti en short int ou short int unsigned avant impression); ou qu'une conversion n suivant spéci fi er s'applique à un pointeur sur un court l'argument int.
Si l'argument était en fait de type short
ou unsigned short
, puis la promotion à int
suivi d'un retour de conversion short
ou unsigned short
donnera les mêmes valeur promotion de int
sans retour de conversion. Ainsi, pour les arguments de type short
ou unsigned short
, %d
, %u
, etc. devraient donner des résultats identiques à %hd
, %hu
, etc. (et de même pour les types de char
et hh
).
Pour autant que je sache, la seule situation où le modificateur de h
ou hh
pourrait être utile est lorsque l'argument a transmis une int
en dehors de la gamme de short
ou unsigned short
, par exemple.
printf("%hu", 0x10000);
mais je crois comprendre que le passage du mauvais type comme cela se traduit par un comportement non défini de toute façon, de sorte que vous ne pouviez pas attendre d'imprimer 0.
Un cas du monde 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 un type de char
simple qui est signé (dans ce cas, printf("%x", c)
imprimerait fffffff0
ou similaire). Mais cette attente justifiée?
(Note: Qu'est-ce qui se passe est que le type original était char
, qui est promu int
et à l'arrière converti en unsigned char
au lieu de char
, changeant ainsi la valeur qui est imprimé mais ne la norme précise ce comportement, ou est-ce. un détail de mise en œuvre que le logiciel peut être cassé compte sur?)
La solution
L'une des raisons possibles: la symétrie avec l'utilisation de ces modificateurs dans les fonctions d'entrée formatées? Je sais que ce ne serait pas strictement nécessaire, mais peut-être il y avait valeur vu pour cela?
Bien qu'ils ne mentionnent pas l'importance de symétrie pour les modificateurs « h » et « hh » dans
conversion de pointeur d'entrée avec% p a été ajouté à C89, bien qu'il soit évidemment risqué, pour la symétrie avec fprintf. Dans la section sur %% hh et on a ajouté des modificateurs de longueur ll en C99 (voir §7.19.6.2). Je sais qu'il est un fil ténu, mais je spécule de toute façon, donc je pensais que je donnerais tout ce que l'argument qu'il pourrait y avoir. En outre, pour être complet, le « h » modificateur était dans la norme C89 d'origine - il serait sans doute là, même si ce n'était pas strictement nécessaire en raison de l'utilisation actuelle répandue, même s'il n'y aurait pas eu une exigence technique utiliser le modificateur.
fprintf()
, le document de justification C99 ne discuter que « hh » a été ajouté, mais se contente de renvoyer le lecteur à la section fscanf()
:
Autres conseils
En mode %...x
, toutes les valeurs sont interprétées comme non signé. Les nombres négatifs sont donc imprimés que leurs conversions non signées. Dans le calcul du complément à 2, dont la plupart des processeurs utilisent, il n'y a aucune différence dans les motifs de bits entre un nombre négatif signé et son équivalent non signé positif, qui est définie par une arithmétique de module (en ajoutant la valeur maximale pour le domaine de plus un pour le numéro de négatif, selon à la norme C99). Beaucoup de Logiciel- en particulier le code de débogage les plus susceptibles d'utiliser les marques %x
- l'hypothèse silencieuse que la représentation binaire d'une valeur négative signée et son qu'unsigned est le même, ce qui est vrai que sur une machine de complément à 2.
La mécanique de cette fonte sont telles que les représentations hexadécimaux de valeur impliquent toujours, peut-être à tort, qu'un certain nombre a été rendu en complément à 2, tant qu'il n'a pas atteint une condition de bord où les différentes représentations entières ont différentes gammes. Cela est vrai même pour les représentations arithmétiques où la valeur 0 n'est pas représentée avec le motif binaire de tous 0s.
Un short
de négatif affiché comme unsigned long
dans hexidecimal sera donc, sur toute machine, être rembourré avec f
, en raison de l'extension de signe implicite dans la promotion, qui printf
imprimera. valeur est le même, mais il est trompeur vraiment visuellement à la taille du champ, ce qui implique une quantité importante de gamme qui est tout simplement pas présente.
%hx
tronque la représentation affichée pour éviter ce rembourrage, exactement comme vous concluez de votre cas d'utilisation dans le monde réel.
Le comportement de printf
est indéfini lorsqu'il est passé un int
en dehors de la gamme de short
qui doit être imprimé comme short
, mais la mise en œuvre de loin la plus facile simplement les rejets le bit par une brute baissés, et bien que la spécification ne < em> nécessitent tout comportement spécifique, à peu près toute mise en œuvre saine d'esprit va juste effectuer la troncature. Il sont des moyens généralement préférable de le faire, cependant.
Si printf est pas des valeurs de remplissage ou afficher des représentations non signées des valeurs signées, %h
est pas très utile.
L'utilisation que je peux penser est pour le passage d'un unsigned short
ou unsigned char
et en utilisant les indicateurs de conversion de %x
. Vous ne pouvez pas simplement utiliser un %x
nu -. La valeur peut être promu int
plutôt que unsigned int
, et puis vous avez un comportement non défini
Vos alternatives sont soit explicitement jeter l'argument unsigned
; ou à l'utilisation %hx
/ %hhx
avec un argument nu.
Les arguments variadique à printf()
et al sont automatiquement promus à l'aide des conversions par défaut, de sorte que toutes les valeurs de short
ou char
sont promus à int
lorsqu'elle est transmise à la fonction.
En l'absence des modificateurs de h
ou hh
, vous devez masquer les valeurs transmises de manière fiable pour obtenir le comportement correct. Avec les modificateurs, vous n'avez plus à masquer les valeurs; la mise en œuvre de printf()
fait le travail correctement.
Plus précisément, pour le format %hx
, le code à l'intérieur printf()
peut faire quelque chose comme:
va_list args;
va_start(args, format);
...
int i = va_arg(args, int);
unsigned short s = (unsigned short)i;
...print s correctly, as 4 hex digits maximum
...even on a machine with 64-bit `int`!
Je suppose que allègrement short
est une quantité de 16 bits; la norme ne garantit pas en fait que, bien sûr.
Je l'ai trouvé utile pour éviter la coulée lors du formatage à caractères non signés hex:
sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));
Il est une pratique de codage mineur, et a l'air plus propre que plusieurs moulages (OMI).
un autre endroit, il est à portée de main est de vérifier la taille de snprintf. gcc7 ajouté vérification de la taille lors de l'utilisation snprintf donc cela échouera
char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);
il vous oblige à utiliser plus ombles lorsque vous utilisez% d lors du formatage char
ici est un commettras que montre les corrections au lieu d'augmenter la taille du tableau char ils ont changé% d% h. cela donne également une description plus précise
Je suis d'accord avec vous qu'il est pas strictement nécessaire, et donc, par cette raison est seul pas bon dans une fonction de bibliothèque C:)
Il est peut-être « gentil » pour la symétrie des différents drapeaux, mais il est le plus souvent contre-productive, car elle cache la règle « conversion int
».