Wenn man es liest, scheint der Grund, warum sich gcc beschwert, darin zu liegen, dass die von mir vorgeschlagene Syntax nicht in C99 definiert ist. Aber ich kann anscheinend keinen anderen Weg finden, dasselbe auf eine standardmäßig genehmigte Weise zu tun.
Hier also die Doppelfrage:
Ist mein Verständnis richtig, dass dieses Verhalten nicht durch den C99-Standard definiert ist?
Wenn ja, gibt es eine standardisierte, tragbare Möglichkeit, dies zu tun?
Die schlimmste Warnung aller Zeiten. Sollte IMHO aus gcc entfernt werden.
– mpontillo
11. Dezember 2012 um 2:22 Uhr
Ein Programmierer
#include <inttypes.h>
#include <stdint.h>
printf("%016" PRIxPTR "\n", (uintptr_t)ptr);
aber es wird den Zeiger nicht auf die von der Implementierung definierte Weise drucken (sagt DEAD:BEEF für den segmentierten 8086-Modus).
Interessant. Dies ist das erste Mal, dass ich diese PRIxPTR-Notation sehe. Ist das Standard oder gcc-spezifisch? Wenn Standard, welcher Standard? C89 oder C99?
– Florian
10. August 2009 um 14:50 Uhr
Standard in C99. Das Makro PRIxPTR ist in definiert. Der Typ uintptr_t ist in definiert.
– Ein Programmierer
10. August 2009 um 15:00 Uhr
Lassen Sie mich sicherstellen, dass ich das richtig verstanden habe. Der Grund, warum ich versucht habe, ist nicht portierbar, weil nicht alle Plattformen Zeiger als 0x anzeigen … Aus diesem Grund ist das Auffüllen mit 0s nicht portabel. Auf der anderen Seite wird printf(“%016″PRIxPTR”, (uintptr_t)ptr); auf allen Plattformen meinen Zeiger mit der richtigen Länge drucken, formatiert als eine 0x… Zahl, aufgefüllt mit 0s, ohne unsicheres Casting.
Oder verwenden Sie eine andere Variante der Makros aus diesem Header.
Beachten Sie auch, dass einige Implementierungen von printf() drucke ein ‘0x‘ vor dem Zeiger; andere nicht (und beide sind nach dem C-Standard korrekt).
Beachten Sie, dass Sie formell in uintptr_t umwandeln müssen.
– Ein Programmierer
10. August 2009 um 15:55 Uhr
Oder Sie könnten verwenden "%#016" PRIxPTR stattdessen die # sagt, um die Basis für Oktal und Hexadezimal anzuzeigen.
– Jo D
23. Juli 2010 um 15:57 Uhr
@Joe: Sie haben Recht – Sie könnten das ‘#’ verwenden, aber das gibt 0x01ab oder 0X01AB aus, und ich mag keine dieser Notationen: Ich mag 0x01AB. Was ich geschrieben habe, bringt mir das, was ich mag, und nicht das, was ich nicht mag.
– Jonathan Leffler
23. Juli 2010 um 19:28 Uhr
stefankt
Diese Antwort ähnelt der zuvor in https://stackoverflow.com/a/1255185/1905491 gegebenen, berücksichtigt aber auch die möglichen Breiten (wie von https://stackoverflow.com/a/6904396/1905491 beschrieben, was ich nicht erkannt, bis meine Antwort darunter gerendert wurde ;). Der folgende Ausschnitt druckt Zeiger als 8 0-passed Hex-Zeichen auf vernünftigen * Maschinen, wo sie durch 32 Bit, 16 auf 64b und 4 auf 16b-Systemen dargestellt werden können.
Beachten Sie die Verwendung des Sternchens zum Abrufen der Breite durch das nächste Argument, das sich in C99 befindet (wahrscheinlich früher?), Aber das “in freier Wildbahn” ziemlich selten zu sehen ist. Das ist viel besser als die Verwendung von p Konvertierung, da letztere implementierungsdefiniert ist.
* Die Norm erlaubt uintptr_t größer als das Minimum sein, aber ich gehe davon aus, dass es keine Implementierung gibt, die das Minimum nicht verwendet.
Naive Frage: würde nicht #define PRIxPTR_WIDTH ((int)(sizeof(void*)*2)) funktionieren, auch wenn uintptr_t ist größer als das Minimum?
– Ciro Santilli OurBigBook.com
14. August 2013 um 9:19 Uhr
“Arbeiten” wie beim Kompilieren und Ausführen, ja, aber es würde unnötige führende Nullen drucken.
– stefankt
22. Januar 2018 um 7:40 Uhr
Keith Thompson
Das einzige tragbar Eine Möglichkeit, Zeigerwerte zu drucken, besteht darin, die zu verwenden "%p" Bezeichner, der eine implementierungsdefinierte Ausgabe erzeugt. (Konvertieren in uintptr_t und verwenden PRIxPTR wird wahrscheinlich funktionieren, aber (a) uintptr_t wurde in C99 eingeführt, daher wird es von einigen Compilern möglicherweise nicht unterstützt, und (b) es ist nicht einmal garantiert, dass es in C99 existiert (es gibt möglicherweise keinen Integer-Typ, der groß genug ist, um alle möglichen Zeigerwerte aufzunehmen.
Also verwenden "%p" mit sprintf() (oder snprintf()falls vorhanden), um in eine Zeichenfolge zu drucken, und geben Sie dann die Zeichenfolgenauffüllung mit führenden Leerzeichen aus.
Ein Problem ist, dass es keinen wirklich guten Weg gibt, die maximale Länge der erzeugten Zeichenfolge zu bestimmen "%p".
Dies ist zugegebenermaßen unbequem (obwohl Sie es natürlich in eine Funktion packen können). Wenn Sie keine 100%ige Portabilität benötigen, habe ich noch nie ein System verwendet, auf das der Zeiger gecastet wurde unsigned long und Drucken des Ergebnisses mit "0x%16lx" würde nicht funktionieren. [UPDATE: Yes, I have. 64-bit Windows has 64-bit pointers and 32-bit unsigned long.] (Oder Sie können verwenden "0x%*lx" und die Breite berechnen, wahrscheinlich so etwas wie sizeof (void*) * 2. Wenn Sie wirklich paranoid sind, können Sie Systeme erklären, in denen CHAR_BIT > 8Sie erhalten also mehr als 2 Hexadezimalziffern pro Byte.)
Es ist einfach zu lösen, wenn Sie den Zeiger auf einen langen Typ umwandeln. Das Problem ist, dass dies nicht auf allen Plattformen funktioniert, da davon ausgegangen wird, dass ein Zeiger in einen langen Text passt und dass ein Zeiger in 16 Zeichen (64 Bit) angezeigt werden kann. Dies gilt jedoch für die meisten Plattformen.
so würde es werden:
printf("%016lx", (unsigned long) ptr);
Ich habe nicht die richtige Maschine / das richtige Betriebssystem zur Hand, um dies zu bestätigen, aber ich denke, dass unter 64-Bit-Fenstern Zeiger 64-Bit sind, während lang 32-Bit sind, was Ihre Annahme widerlegt. Wenn ich mich also nicht irre, würde das, was Sie vorschlagen, auf mindestens einer großen Plattform brechen.
– Florian
10. August 2009 um 14:33 Uhr
Wenn die Portabilität auf Windows wichtig ist, ist es wahrscheinlich nicht das Richtige, sich auf C99-Funktionen zu verlassen.
– Ein Programmierer
10. August 2009 um 14:39 Uhr
Auf einer 64-Bit-Plattform sind Ints normalerweise 32 Bits und Longs 64 Bits. Wenn dies ein Problem auf Ihrer Plattform ist, verwenden Sie ein langes langes.
– AnthonyLambert
11. August 2009 um 8:35 Uhr
Eigentlich denke ich, dass Pointer garantiert in einen Long passen. Die einzige Implementierung, die dem nicht entspricht, ist MSVC (64-Bit).
– Jo D
23. Juli 2010 um 15:58 Uhr
Beachten Sie meinen Kommentar zu Windows 64-Bit; das wird dort nicht funktionieren.
– Jonathan Leffler
17. Januar 2018 um 16:21 Uhr
groovingandi
Wie Ihr Link bereits andeutet, ist das Verhalten undefiniert. Ich glaube nicht, dass es dafür eine portable Möglichkeit gibt, da %p selbst von der Plattform abhängt, auf der Sie arbeiten. Sie könnten den Zeiger natürlich auch einfach auf ein int werfen und es in hex anzeigen:
printf("0x%016lx", (unsigned long)ptr);
Ich habe nicht die richtige Maschine / das richtige Betriebssystem zur Hand, um dies zu bestätigen, aber ich denke, dass unter 64-Bit-Fenstern Zeiger 64-Bit sind, während lang 32-Bit sind, was Ihre Annahme widerlegt. Wenn ich mich also nicht irre, würde das, was Sie vorschlagen, auf mindestens einer großen Plattform brechen.
– Florian
10. August 2009 um 14:33 Uhr
Wenn die Portabilität auf Windows wichtig ist, ist es wahrscheinlich nicht das Richtige, sich auf C99-Funktionen zu verlassen.
– Ein Programmierer
10. August 2009 um 14:39 Uhr
Auf einer 64-Bit-Plattform sind Ints normalerweise 32 Bits und Longs 64 Bits. Wenn dies ein Problem auf Ihrer Plattform ist, verwenden Sie ein langes langes.
– AnthonyLambert
11. August 2009 um 8:35 Uhr
Eigentlich denke ich, dass Pointer garantiert in einen Long passen. Die einzige Implementierung, die dem nicht entspricht, ist MSVC (64-Bit).
– Jo D
23. Juli 2010 um 15:58 Uhr
Beachten Sie meinen Kommentar zu Windows 64-Bit; das wird dort nicht funktionieren.
– Jonathan Leffler
17. Januar 2018 um 16:21 Uhr
Rodrigo Queiro
Vielleicht ist das interessant (von einem 32-Bit-Windows-Rechner mit mingw):
rjeq@RJEQXPD /u
$ cat test.c
#include <stdio.h>
int main()
{
char c;
printf("p: %016p\n", &c);
printf("x: %016llx\n", (unsigned long long) (unsigned long) &c);
return 0;
}
rjeq@RJEQXPD /u
$ gcc -Wall -o test test.c
test.c: In function `main':
test.c:7: warning: `0' flag used with `%p' printf format
rjeq@RJEQXPD /u
$ ./test.exe
p: 0022FF77
x: 000000000022ff77
Wie Sie sehen können, füllt die %p-Version mit Nullen bis zur Größe eines Zeigers und dann mit Leerzeichen bis zur angegebenen Größe auf, während die Verwendung von %x und Umwandlungen (die Umwandlungen und Formatbezeichner sind höchst unportabel) nur Nullen verwendet.
Interessant. Es bestätigt, dass %016p” nicht portabel ist, da es auf meinem Computer (64-Bit-Linux) nur mit Nullen und nicht mit Leerzeichen aufgefüllt wird.
– Florian
10. August 2009 um 14:39 Uhr
Das ist kein Fehler, nur ein unerwartetes Feature. 😉 Er wandelt es in einen 64-Bit-Wert auf einer 32-Bit-Architektur um.
– mpontillo
11. Dezember 2012 um 2:26 Uhr
Die Besetzung zu unsigned long vor der Umformung zu unsigned long long bricht wenn sizeof(unsigned long) != sizeof(unsigned long long)wie unter Windows 64-Bit.
– Jonathan Leffler
17. Januar 2018 um 16:24 Uhr
14066900cookie-checkWas ist die richtige Verwendung von printf, um mit 0s aufgefüllte Zeiger anzuzeigenyes
Die schlimmste Warnung aller Zeiten. Sollte IMHO aus gcc entfernt werden.
– mpontillo
11. Dezember 2012 um 2:22 Uhr