J'ai une variable de type size_t
et je veux l'imprimer en utilisant printf()
. Quel spécificateur de format dois-je utiliser pour l'imprimer de manière portable?
Dans une machine 32 bits, %u
semble juste. J'ai compilé avec g++ -g -W -Wall -Werror -ansi -pedantic
, et il n'y a eu aucun avertissement. Mais quand je compile ce code sur une machine 64 bits, il produit un avertissement.
size_t x = <something>;
printf("size = %u\n", x);
warning: format '%u' expects type 'unsigned int',
but argument 2 has type 'long unsigned int'
L'avertissement disparaît, comme prévu, si je change cela en %lu
.
La question est, comment puis-je écrire le code, afin qu'il compile sans avertissement sur les machines 32 et 64 bits?
Edit: Comme solution de contournement, je suppose qu'une réponse pourrait être de "convertir" la variable en un entier suffisamment grand, par exemple unsigned long
, et d'imprimer à l'aide %lu
. Cela fonctionnerait dans les deux cas. Je cherche s'il y a une autre idée.
unsigned long
est la meilleure option si votre implémentation libc ne prend pas en charge lez
modificateur; la norme C99 recommande desize_t
ne pas avoir un rang de conversion entier supérieur àlong
, donc vous êtes raisonnablement en sécuritéRéponses:
Utilisez le
z
modificateur:la source
printf()
modificateurs de longueur du brouillon C ++ 0x du 2009-11-09 (tableau 84 à la page 672)-pedantic
, vous devrez soit obtenir un compilateur prenant en charge le brouillon C ++ 1x (très peu probable), soit vous devrez déplacer votre code dans un fichier compilé en C99. Sinon, votre seule option est de convertir vos variablesunsigned long long
et de les utiliser%llu
pour être portable au maximum.Il semble que cela varie en fonction du compilateur que vous utilisez (blech):
%zu
(ou%zx
, ou%zd
mais cela l'affiche comme s'il était signé, etc.)%Iu
(ou%Ix
, ou%Id
mais c'est signé, etc.) - mais à partir de cl v19 (dans Visual Studio 2015), Microsoft prend en charge%zu
(voir cette réponse à ce commentaire )... et bien sûr, si vous utilisez C ++, vous pouvez utiliser à la
cout
place comme suggéré par AraK .la source
z
est également supporté par newlib (ie cygwin)%zd
est incorrect poursize_t
; il est correct pour le type signé correspondant àsize_t
, maissize_t
lui-même est un type non signé.%zu
(et%zx
au cas où ils voudraient de l'hexagone). C'est vrai, cela%zu
aurait probablement dû être le premier dans la liste. Fixé.%zd
devrait être du tout dans la liste. Je ne vois aucune raison d'utiliser%zd
plutôt que%zu
d'imprimer unesize_t
valeur. Ce n'est même pas valide (a un comportement indéfini) si la valeur dépasseSIZE_MAX / 2
. (Pour être complet, vous pourriez mentionner%zo
pour octal.)ssize_t
le type signé corresponde àsize_t
, il n'est donc pas garanti de correspondre"%zd"
. (Il se trouve probablement sur la plupart des implémentations.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…Pour C89, utilisez
%lu
et convertissez la valeur enunsigned long
:Pour C99 et versions ultérieures, utilisez
%zu
:la source
unsigned long long
- être ?uint64_t
puis utiliser laPRIu64
macro de inttypes.h, qui contient le spécificateur de format.Prolongeant la réponse d'Adam Rosenfield pour Windows.
J'ai testé ce code avec à la fois sur VS2013 Update 4 et VS2015 preview:
Sorties binaires générées par VS2015:
tandis que celui généré par VS2013 dit:
Remarque:
ssize_t
est une extension POSIX etSSIZE_T
est une chose similaire dans les types de données Windows , d'où j'ai ajouté une<BaseTsd.h>
référence.De plus, à l'exception des en-têtes C99 / C11 suivants, tous les en-têtes C99 sont disponibles dans l'aperçu VS2015:
De plus, les C11
<uchar.h>
sont désormais inclus dans le dernier aperçu.Pour plus de détails, consultez cette ancienne et la nouvelle liste pour la conformité standard.
la source
Pour ceux qui parlent de faire cela en C ++ qui ne prend pas nécessairement en charge les extensions C99, je recommande vivement boost :: format. Cela rend la question de taille de type size_t théorique:
Comme vous n'avez pas besoin de spécificateurs de taille au format boost ::, vous pouvez simplement vous soucier de la façon dont vous souhaitez afficher la valeur.
la source
%u
alors.la source
printf
spécificateur. Je suppose qu'ils ont d'autres contraintes non déclarées qui rendent l'utilisation d'std::cout
un problème.printf
prescripteur. »la source
Comme l'a dit AraK, l'interface des flux c ++ fonctionnera toujours de manière portable.
Si vous voulez C stdio, il n'y a pas de réponse portable à cela pour certains cas de "portable". Et cela devient moche car, comme vous l'avez vu, choisir les mauvais indicateurs de format peut générer un avertissement du compilateur ou donner une sortie incorrecte.
C99 a essayé de résoudre ce problème avec des formats inttypes.h comme "%" PRIdMAX "\ n". Mais tout comme avec "% zu", tout le monde ne prend pas en charge c99 (comme MSVS avant 2013). Il y a des fichiers "msinttypes.h" qui flottent pour gérer cela.
Si vous effectuez un cast vers un type différent, selon les indicateurs, vous pouvez obtenir un avertissement du compilateur pour la troncature ou un changement de signe. Si vous suivez cette route, choisissez un type de taille fixe pertinent plus grand. L'un des longs non signés et "% llu" ou des longs "% lu" non signés devrait fonctionner, mais llu peut également ralentir les choses dans un monde 32 bits comme excessivement grand. (Modifier - mon mac émet un avertissement en 64 bits pour% llu ne correspondant pas à size_t, même si% lu,% llu et size_t sont tous de la même taille. Et% lu et% llu ne sont pas de la même taille sur mon MSVS2012. Donc vous devrez peut-être caster + utiliser un format qui correspond.)
Pour cette question, vous pouvez aller avec des types de taille fixe, tels que int64_t. Mais attendez! Maintenant, nous sommes de retour à c99 / c ++ 11, et les anciens MSVS échouent à nouveau. De plus, vous avez également des moulages (par exemple map.size () n'est pas un type de taille fixe)!
Vous pouvez utiliser un en-tête ou une bibliothèque tiers tel que boost. Si vous n'en utilisez pas déjà un, vous ne voudrez peut-être pas gonfler votre projet de cette façon. Si vous êtes prêt à en ajouter un juste pour ce problème, pourquoi ne pas utiliser des flux c ++ ou une compilation conditionnelle?
Donc, vous en êtes aux flux c ++, à la compilation conditionnelle, aux frameworks tiers ou à quelque chose de portable qui vous convient.
la source
Vous avertira-t-il si vous passez un entier non signé 32 bits au format% lu? Cela devrait être bien car la conversion est bien définie et ne perd aucune information.
J'ai entendu dire que certaines plates-formes définissent des macros en ce
<inttypes.h>
que vous pouvez insérer dans le littéral de chaîne de format, mais je ne vois pas cet en-tête sur mon compilateur Windows C ++, ce qui implique qu'il ne peut pas être multiplateforme.la source
%lu
, vous devez convertir lasize_t
valeur enunsigned long
. Il n'y a pas de conversion implicite (autre que les promotions) pour les arguments versprintf
.C99 définit "% zd" etc. pour cela. (Merci aux commentateurs) Il n'y a pas de spécificateur de format portable pour cela en C ++ - vous pouvez utiliser
%p
ce mot qui dans les deux scénarios, mais ce n'est pas un choix portable non plus, et donne la valeur en hexadécimal.Alternativement, utilisez un streaming (par exemple stringstream) ou un remplacement de printf sûr tel que Boost Format . Je comprends que ce conseil n'est que d'une utilité limitée (et nécessite C ++). (Nous avons utilisé une approche similaire adaptée à nos besoins lors de l'implémentation de la prise en charge unicode.)
Le problème fondamental pour C est que printf utilisant des points de suspension est dangereux par conception - il doit déterminer la taille de l'argument supplémentaire à partir des arguments connus, il ne peut donc pas être corrigé pour prendre en charge "tout ce que vous avez". Donc, à moins que votre compilateur implémente des extensions propriétaires, vous n'avez pas de chance.
la source
z
modidfier de taille est C standard, mais certaines implémentations de libc sont bloquées en 1990 pour diverses raisons (par exemple, Microsoft a essentiellement abandonné C en faveur de C ++ et - plus récemment - C #)%zd
est faux, il n'est pas signé donc il devrait l'être%zu
.Sur certaines plates-formes et pour certains types, des spécificateurs de conversion printf spécifiques sont disponibles, mais il faut parfois recourir à la conversion vers des types plus grands.
J'ai documenté ce problème délicat ici, avec un exemple de code: http://www.pixelbeat.org/programming/gcc/int_types/ et le mettre à jour périodiquement avec des informations sur de nouvelles plates-formes et types.
la source
si vous souhaitez imprimer la valeur d'un size_t sous forme de chaîne, vous pouvez le faire:
le résultat est:
numéro: 2337200120702199116
texte: Allons pêcher au lieu de s'asseoir sur notre mais !!
Edit: relisant la question en raison des votes négatifs, j'ai remarqué que son problème n'est pas% llu ou% I64d mais le type size_t sur différentes machines voir cette question https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/
size_t est entier non signé sur une machine 32 bits et entier long long non signé sur 64 bits
mais% ll attend toujours un entier long long non signé.
size_t varie en longueur sur différents systèmes d'exploitation tandis que% llu est le même
la source