J'ai une variable de type size_t
et je souhaite l'imprimer avec printf()
. Quel spécificateur de format dois-je utiliser pour l’imprimer de manière portable?
Sur une machine 32 bits, %u
semble correct. J'ai compilé avec g++ -g -W -Wall -Werror -ansi -pedantic
et il n'y a pas eu d'avertissement. Mais lorsque je compile ce code sur une machine 64 bits, il génère 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 qui se pose est la suivante: comment puis-je écrire le code afin qu'il compile sans avertissement sur les machines 32 et 64 bits?
Edit: En guise de solution de contournement, une des solutions pourrait être de "convertir" la variable en un entier suffisamment grand, par exemple, unsigned long
, et d’imprimer avec %lu
. Cela fonctionnerait dans les deux cas. Je cherche s'il y a une autre idée.
Utilisez le modificateur z
:
size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x); // prints as unsigned decimal
printf("%zx\n", x); // prints as hex
printf("%zd\n", y); // prints as signed decimal
Cela varie selon le compilateur que vous utilisez (blech):
%zu
(ou %zx
ou %zd
mais qui l'affiche comme s'il était signé, etc.)%Iu
(ou %Ix
, ou %Id
mais là encore c'est signé, etc.) - mais à partir de la version 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 cout
à la place de suggéré par AraK .
Pour C89, utilisez %lu
et convertissez la valeur en unsigned long
:
size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);
Pour C99 et les versions ultérieures, utilisez %zu
:
size_t foo;
...
printf("foo = %zu\n", foo);
Étendre la réponse d'Adam Rosenfield pour Windows.
J'ai testé ce code avec les aperçus VS2013 Update 4 et VS2015:
// test.c
#include <stdio.h>
#include <BaseTsd.h> // see the note below
int main()
{
size_t x = 1;
SSIZE_T y = 2;
printf("%zu\n", x); // prints as unsigned decimal
printf("%zx\n", x); // prints as hex
printf("%zd\n", y); // prints as signed decimal
return 0;
}
VS2015 a généré des sorties binaires:
1
1
2
tandis que celui généré par VS2013 dit:
zu
zx
zd
Remarque: ssize_t
est une extension POSIX et SSIZE_T
est similaire à Types de données Windows , d'où j'ai ajouté la référence <BaseTsd.h>
.
De plus, à l'exception des en-têtes C99/C11 suivants, tous les en-têtes C99 sont disponibles dans l'aperçu VS2015:
C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>
De plus, le <uchar.h>
de C11 est maintenant inclus dans le dernier aperçu.
Pour plus de détails, voir cette old et la new liste de conformité standard.
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
Pour ceux qui parlent de faire cela en C++ qui ne supporte pas nécessairement les extensions C99, alors je recommande vivement boost :: format. Cela rend la question de taille de type size_t inutile:
std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);
Etant donné que vous n'avez pas besoin d'indicateurs de taille dans boost :: format, vous pouvez simplement vous inquiéter de la façon dont vous voulez afficher la valeur.
printf("size = %zu\n", sizeof(thing) );
Comme AraK l'a dit, l'interface de flux c ++ fonctionnera toujours de manière portable.
std :: size_t s = 1024; std :: cout << s; // ou tout autre type de flux comme stringstream!
Si vous voulez C stdio, il n'y a pas de réponse portable à cette question dans certains cas de "portable". Et cela devient moche puisque, comme vous l'avez vu, choisir les indicateurs de format incorrects peut générer un avertissement pour le compilateur ou produire une sortie incorrecte.
C99 a essayé de résoudre ce problème avec les formats inttypes.h tels que "%" PRIdMAX "\ n". Mais comme avec "% zu", tout le monde ne supporte pas c99 (comme MSVS avant 2013). Il existe des fichiers "msinttypes.h" qui gèrent ce problème.
Si vous transtypez dans un type différent, vous pouvez recevoir un avertissement du compilateur en cas de troncature ou de changement de signe, en fonction des indicateurs. Si vous choisissez cette route, choisissez un type de taille fixe plus important. Un groupe long non signé long et "% llu" ou long signé "% lu" devrait fonctionner, mais llu peut également ralentir les choses dans un monde 32 bits aussi excessivement grand. (Éditer - mon mac émet un avertissement en 64 bits pour% llu ne correspondant pas à size_t, même si% lu,% llu et size_t ont tous la même taille. Et% lu et% llu ne sont pas de la même taille sur mon MSVS2012. vous devrez peut-être utiliser + un format qui correspond.)
D'ailleurs, vous pouvez choisir des types de taille fixe, tels que int64_t. Mais attendez! Nous sommes maintenant de retour à c99/c ++ 11 et l'ancienne version de MSVS échoue à nouveau. De plus, vous avez également des conversions (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 souhaitez en ajouter un juste pour ce problème, pourquoi ne pas utiliser les flux c ++ ou la compilation conditionnelle?
Il ne vous reste donc que des flux c ++, une compilation conditionnelle, des frameworks tiers, ou quelque chose de portable qui fonctionne pour vous.
Vous avertira-t-il si vous passez un entier non signé 32 bits au format% lu? Cela devrait aller puisque la conversion est bien définie et ne perd aucune information.
J'ai entendu dire que certaines plates-formes définissent des macros dans <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 n'est peut-être pas multiplateforme.