536 Stimmen

Wie kann man eine size_t-Variable portabel mit der printf-Familie drucken?

Ich habe eine Variable vom Typ size_t und ich möchte es drucken mit printf() . Welchen Formatspezifikator muss ich verwenden, um es portabel zu drucken?

Auf einem 32-Bit-Rechner, %u scheint richtig zu sein. Ich habe kompiliert mit g++ -g -W -Wall -Werror -ansi -pedantic und es gab keine Warnung. Aber wenn ich den Code auf einem 64-Bit-Rechner 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 %lu .

Die Frage ist, wie kann ich den Code so schreiben, dass er sowohl auf 32- als auch auf 64-Bit-Maschinen warnfrei kompiliert werden kann?

Edit: Als Workaround könnte eine Lösung darin bestehen, die Variable in eine Ganzzahl zu "casten", die groß genug ist, sagen wir unsigned long und drucken mit %lu . Das würde in beiden Fällen funktionieren. Ich bin auf der Suche, ob es eine andere Idee gibt.

9voto

vulcan raven Punkte 31125

Eine Erweiterung der Antwort von Adam Rosenfield für Windows.

Ich habe diesen Code sowohl mit VS2013 Update 4 als auch mit VS2015 Preview getestet:

// 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 erzeugt binäre Ausgaben:

1
1
2

während die von VS2013 generierte Version sagt:

zu
zx
zd

Nota: ssize_t ist eine POSIX-Erweiterung und SSIZE_T ist eine ähnliche Sache in Windows-Datentypen daher habe ich hinzugefügt <BaseTsd.h> Hinweis.

Außerdem sind mit Ausnahme der folgenden C99/C11-Header alle C99-Header in der VS2015-Vorschau verfügbar:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

Außerdem ist C11s <uchar.h> ist jetzt in der neuesten Vorschau enthalten.

Weitere Einzelheiten finden Sie hier alt とのことです。 neu Liste für Standardkonformität.

6voto

swestrup Punkte 4061

Für diejenigen, die dies in C++ tun wollen, das nicht unbedingt die C99-Erweiterungen unterstützt, empfehle ich von Herzen boost::format. Dies macht die Frage nach der Größe des Typs size_t überflüssig:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

Da Sie in boost::format keine Größenangaben benötigen, können Sie sich einfach darum kümmern, wie Sie den Wert anzeigen wollen.

5voto

Khaled Alshaya Punkte 90854
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!

3voto

supercat Punkte 72939

In den meisten Kontexten, in denen ein Programmierer die Ausgabe einer size_t würde der Programmierer eine vernünftige Obergrenze für den auszugebenden Zahlenwert haben. Wenn ein Programmierer z. B. eine Meldung ausgibt, wie groß ein int ist, zu verwenden:

printf("int is %u bytes", (unsigned)sizeof (int) );

wäre in der Praxis genauso tragbar wie, aber möglicherweise schneller und kleiner als:

printf("int is %zu bytes", sizeof (int) );

Die einzige Situation, in der ein solches Konstrukt scheitern könnte, wäre auf einer Plattform, auf der die Anzahl der Bytes, die ein Padding auf einer int ist absurd groß im Verhältnis zur Größe des größten Wertes an unsigned int darstellen kann (es ist etwas unplausibel, dass sizeof (int) größer als 65535 sein könnte, aber noch unwahrscheinlicher ist, dass sie so groß sein könnte, ohne unsigned genügend Wertbits haben, um eine Zahl zu repräsentieren, die größer ist als sizeof (int) .

2voto

Rick Berge Punkte 464

Wie AraK sagte, wird die C++-Streams-Schnittstelle immer portabel funktionieren.

std::size_t s = 1024; std::cout << s; // oder jede andere Art von Stream wie stringstream!

Wenn Sie C stdio wollen, gibt es keine portable Antwort darauf für bestimmte Fälle von "portable". Und es wird hässlich, denn wie Sie gesehen haben, kann die Wahl der falschen Formatflags eine Compilerwarnung oder eine falsche Ausgabe zur Folge haben.

C99 versuchte, dieses Problem mit inttypes.h-Formaten wie "%"PRIdMAX" zu lösen. \n ". Aber genau wie bei "%zu" unterstützt nicht jeder c99 (wie MSVS vor 2013). Es gibt "msinttypes.h"-Dateien, die sich mit diesem Problem befassen.

Wenn Sie in einen anderen Typ casten, erhalten Sie je nach Flags möglicherweise eine Compiler-Warnung wegen Trunkierung oder Vorzeichenwechsel. Wenn Sie diesen Weg gehen, wählen Sie einen größeren relevanten Typ mit fester Größe. Einer von unsigned long long und "%llu" oder unsigned long "%lu" sollte funktionieren, aber llu kann die Dinge in einer 32bit-Welt auch als zu groß verlangsamen. (Bearbeiten - mein Mac gibt in 64 Bit eine Warnung aus, weil %llu nicht mit size_t übereinstimmt, obwohl %lu, %llu und size_t alle die gleiche Größe haben. Und %lu und %llu sind nicht die gleiche Größe auf meinem MSVS2012. Sie müssen also möglicherweise ein passendes Format verwenden).

Sie können auch Typen mit fester Größe verwenden, z. B. int64_t. Aber halt! Jetzt sind wir wieder bei c99/c++11, und ältere MSVS versagt wieder. Außerdem gibt es auch Casts (z.B. map.size() ist kein Typ mit fester Größe)!

Sie können einen Drittanbieter-Header oder eine Bibliothek wie boost verwenden. Wenn Sie nicht bereits eine verwenden, möchten Sie vielleicht nicht Ihr Projekt auf diese Weise aufblasen. Wenn Sie bereit sind, eine nur für dieses Problem hinzuzufügen, warum nicht C++-Streams oder bedingte Kompilierung verwenden?

Sie sind also auf C + +-Streams, bedingte Kompilierung, 3rd-Party-Frameworks, oder etwas Art von portablen, die zufällig für Sie zu arbeiten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X