Ich habe eine Variable vom Typ size_t
und möchte sie mit drucken printf()
. Welchen Formatbezeichner verwende ich, um es portabel zu drucken?
In 32-Bit-Maschine %u
scheint richtig. Ich habe mit kompiliert g++ -g -W -Wall -Werror -ansi -pedantic
und es gab keine Warnung. Wenn ich diesen Code jedoch auf einem 64-Bit-Computer kompiliere, wird eine Warnung ausgegeben.
size_t x = <something>;
printf("size = %u\n", x);
warning: format '%u' expects type 'unsigned int',
but argument 2 has type 'long unsigned int'
Die Warnung verschwindet wie erwartet, wenn ich das in ändere %lu
.
Die Frage ist, wie kann ich den Code schreiben, damit er auf 32- und 64-Bit-Computern warnungsfrei kompiliert wird?
Bearbeiten: Als Problemumgehung könnte eine Antwort darin bestehen, die Variable in eine Ganzzahl umzuwandeln, die beispielsweise groß genug ist unsigned long
, und mit zu drucken %lu
. Das würde in beiden Fällen funktionieren. Ich suche, ob es eine andere Idee gibt.
unsigned long
ist die beste Option, wenn Ihre libc-Implementierung denz
Modifikator nicht unterstützt . Der C99-Standard empfiehltsize_t
, keinen ganzzahligen Konvertierungsrang größer als zu habenlong
, damit Sie einigermaßen sicher sindAntworten:
Verwenden Sie den
z
Modifikator:quelle
printf()
Längenmodifikatoren des C ++ 0x-Entwurfs vom 09.11.2009 (Tabelle 84 auf Seite 672) enthalten-pedantic
, benötigen Sie entweder einen Compiler, der den C ++ 1x-Entwurf unterstützt (höchst unwahrscheinlich), oder Sie müssen Ihren Code in eine Datei verschieben, die als C99 kompiliert wurde. Andernfalls ist Ihre einzige Option Ihre Variablen zu werfenunsigned long long
und verwenden%llu
maximal tragbar zu sein.Sieht so aus, als ob es je nach verwendetem Compiler unterschiedlich ist (blech):
%zu
(oder%zx
, oder%zd
aber das zeigt es an, als ob es signiert wäre, etc.)%Iu
(oder%Ix
, oder%Id
aber das ist wieder signiert usw.) - aber ab cl v19 (in Visual Studio 2015) unterstützt Microsoft%zu
(siehe diese Antwort auf diesen Kommentar )... und natürlich, wenn Sie C ++ verwenden, können Sie
cout
stattdessen verwenden, wie von AraK vorgeschlagen .quelle
z
wird auch von newlib (dh cygwin) unterstützt%zd
ist falsch fürsize_t
; Es ist korrekt für den entsprechenden signierten Typsize_t
, abersize_t
selbst ist ein nicht signierter Typ.%zu
(und%zx
falls sie Hex wollen). Richtig, das%zu
hätte wahrscheinlich an erster Stelle in der Liste stehen sollen. Fest.%zd
es überhaupt in der Liste sein sollte. Ich kann mir keinen Grund vorstellen, einen Wert zu verwenden,%zd
anstatt ihn%zu
zu druckensize_t
. Es ist nicht einmal gültig (hat undefiniertes Verhalten), wenn der Wert überschreitetSIZE_MAX / 2
. (Der Vollständigkeit%zo
ssize_t
der signierte Typ dem entsprichtsize_t
, daher kann keine Übereinstimmung garantiert werden"%zd"
. (Es ist wahrscheinlich auf den meisten Implementierungen.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…Verwenden Sie für C89
%lu
den Wert und wandeln Sie ihn in Folgendes umunsigned long
:Verwenden Sie für C99 und höher
%zu
:quelle
unsigned long long
?uint64_t
und dann dasPRIu64
Makro aus inttypes.h verwenden, das den Formatbezeichner enthält.Erweiterung der Antwort von Adam Rosenfield für Windows.
Ich habe diesen Code sowohl in VS2013 Update 4 als auch in VS2015 getestet:
VS2015 generierte Binärausgänge:
während der von VS2013 erzeugte sagt:
Hinweis:
ssize_t
ist eine POSIX-Erweiterung undSSIZE_T
ähnelt den Windows-Datentypen . Daher habe ich hinzugefügt<BaseTsd.h>
Referenz .Mit Ausnahme der folgenden C99 / C11-Header sind alle C99-Header in der VS2015-Vorschau verfügbar:
Auch C11
<uchar.h>
jetzt in der neuesten Vorschau enthalten.Weitere Informationen zur Standardkonformität finden Sie in dieser alten und der neuen Liste.
quelle
Für diejenigen, die darüber sprechen, dies in C ++ zu tun, das die C99-Erweiterungen nicht unbedingt unterstützt, empfehle ich das Boost :: -Format von Herzen. Dies macht die Frage size_t type size strittig:
Da Sie keine Größenangaben im Boost :: -Format benötigen, können Sie sich nur Gedanken darüber machen, wie Sie den Wert anzeigen möchten.
quelle
%u
dann.quelle
printf
Spezifizierer. Ich würde vermuten, dass sie einige andere nicht angegebene Einschränkungen haben, die die Verwendungstd::cout
eines Problems erschweren .printf
Spezifizierer fragen ".quelle
Wie AraK sagte, funktioniert die C ++ - Streams-Schnittstelle immer portabel.
Wenn Sie C stdio möchten, gibt es für bestimmte Fälle von "portabel" keine tragbare Antwort darauf. Und es wird hässlich, denn wie Sie gesehen haben, kann die Auswahl der falschen Formatflags eine Compilerwarnung oder eine falsche Ausgabe ergeben.
C99 hat versucht, dieses Problem mit inttypes.h-Formaten wie "%" PRIdMAX "\ n" zu lösen. Aber genau wie bei "% zu" unterstützt nicht jeder c99 (wie MSVS vor 2013). Es gibt "msinttypes.h" -Dateien, die herumschwirren, um damit umzugehen.
Wenn Sie in einen anderen Typ umwandeln, erhalten Sie abhängig von den Flags möglicherweise eine Compiler-Warnung zum Abschneiden oder Ändern des Vorzeichens. Wenn Sie diese Route wählen, wählen Sie einen größeren relevanten Typ mit fester Größe. Eines von unsigned long long und "% llu" oder unsigned long "% lu" sollte funktionieren, aber llu kann in einer 32-Bit-Welt, die übermäßig groß ist, auch die Dinge verlangsamen. (Bearbeiten - Mein Mac gibt eine 64-Bit-Warnung für% llu aus, die nicht mit size_t übereinstimmt, obwohl% lu,% llu und size_t alle dieselbe Größe haben. Und% lu und% llu haben auf meinem MSVS2012 nicht dieselbe Größe Möglicherweise müssen Sie ein passendes Format verwenden.)
In diesem Fall können Sie Typen mit fester Größe verwenden, z. B. int64_t. Aber warte! Jetzt sind wir wieder bei c99 / c ++ 11 und älteres MSVS schlägt erneut fehl. Außerdem hast du auch Casts (zB map.size () ist kein Typ mit fester Größe)!
Sie können einen Header oder eine Bibliothek eines Drittanbieters verwenden, z. B. Boost. Wenn Sie noch keinen verwenden, möchten Sie Ihr Projekt möglicherweise nicht auf diese Weise aufblasen. Wenn Sie nur für dieses Problem einen hinzufügen möchten, verwenden Sie C ++ - Streams oder die bedingte Kompilierung.
Sie sind also auf C ++ - Streams, bedingte Kompilierung, Frameworks von Drittanbietern oder etwas Portables angewiesen, das zufällig für Sie funktioniert.
quelle
Warnt es Sie, wenn Sie eine 32-Bit-Ganzzahl ohne Vorzeichen an ein% lu-Format übergeben? Es sollte in Ordnung sein, da die Konvertierung genau definiert ist und keine Informationen verliert.
Ich habe gehört, dass einige Plattformen Makros definieren
<inttypes.h>
, die Sie in das Formatzeichenfolgenliteral einfügen können, aber ich sehe diesen Header in meinem Windows C ++ - Compiler nicht, was bedeutet, dass er möglicherweise nicht plattformübergreifend ist.quelle
%lu
, sollten Sie densize_t
Wert in umwandelnunsigned long
. Es gibt keine implizite Konvertierung (außer Werbeaktionen) für Argumente zuprintf
.C99 definiert dafür "% zd" usw. (Dank an die Kommentatoren) In C ++ gibt es dafür keinen tragbaren Formatbezeichner - Sie könnten
%p
dieses Wort verwenden , das in diesen beiden Szenarien nicht funktioniert, aber auch keine tragbare Wahl ist und den Wert in hexadezimaler Form angibt .Alternativ können Sie Streaming (z. B. Stringstream) oder einen sicheren Printf-Ersatz wie das Boost-Format verwenden . Ich verstehe, dass dieser Rat nur von begrenztem Nutzen ist (und C ++ erfordert). (Bei der Implementierung der Unicode-Unterstützung haben wir einen ähnlichen Ansatz verwendet, der unseren Anforderungen entspricht.)
Das grundlegende Problem für C besteht darin, dass printf, das eine Ellipse verwendet, von Natur aus unsicher ist. Es muss die Größe des zusätzlichen Arguments aus den bekannten Argumenten bestimmen, sodass es nicht behoben werden kann, um "was auch immer Sie haben" zu unterstützen. Wenn Ihr Compiler also keine proprietären Erweiterungen implementiert, haben Sie kein Glück.
quelle
z
Größe modidfier ist Standard C, aber einige libc-Implementierungen stecken 1990 aus verschiedenen Gründen fest (z. B. Microsoft hat C grundsätzlich zugunsten von C ++ und - in jüngerer Zeit - C # aufgegeben)%zd
ist falsch, es ist nicht signiert, so sollte es sein%zu
.Auf einigen Plattformen und für einige Typen stehen bestimmte Druckkonvertierungsspezifizierer zur Verfügung, aber manchmal muss auf das Casting auf größere Typen zurückgegriffen werden.
Ich habe dieses knifflige Problem hier mit Beispielcode dokumentiert: http://www.pixelbeat.org/programming/gcc/int_types/ und es regelmäßig mit Informationen zu neuen Plattformen und Typen aktualisiert.
quelle
Wenn Sie den Wert von size_t als Zeichenfolge drucken möchten, gehen Sie folgendermaßen vor:
Ergebnis ist:
Nummer: 2337200120702199116
Text: Lass uns fischen gehen anstatt auf unserem aber zu sitzen !!
Bearbeiten: Erneutes Lesen der Frage aufgrund der Abstimmungen Ich habe festgestellt, dass sein Problem nicht% llu oder% I64d ist, sondern der Typ size_t auf verschiedenen Computern. Siehe diese Frage https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/
size_t ist auf einem 32-Bit-Computer ein Int ohne Vorzeichen und auf einem 64-Bit-Computer ein Long Long Int ohne Vorzeichen
,% ll erwartet jedoch immer ein Long Long Int ohne Vorzeichen.
size_t variiert in der Länge unter verschiedenen Betriebssystemen, während% llu gleich ist
quelle