Was ist die richtige Verwendung von printf, um mit 0s aufgefüllte Zeiger anzuzeigen

Lesezeit: 8 Minuten

In C möchte ich printf verwenden, um Zeiger anzuzeigen, und damit sie richtig ausgerichtet sind, möchte ich sie mit Nullen auffüllen.

Meine Vermutung war, dass der richtige Weg, dies zu tun, war:

printf("%016p", ptr);

Das funktioniert, aber dieser gcc beschwert sich mit folgender Meldung:

warning: '0' flag used with ‘%p’ gnu_printf format

Ich habe ein bisschen danach gegoogelt, und der folgende Thread behandelt das gleiche Thema, gibt aber keine wirkliche Lösung.

http://gcc.gnu.org/ml/gcc-bugs/2003-05/msg00484.html

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

Benutzeravatar von AProgrammer
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.

    Florian

    10. August 2009 um 15:16 Uhr

  • @Artelius, eines der Wörter, das nur mit Buchstaben beschreibbar ist, die Hexadezimalziffern sind (siehe nedbatchelder.com/text/hexwords.html).

    – Ein Programmierer

    4. September 2009 um 7:46 Uhr

  • Sollte nicht sein DEAD:BEEF?

    – Mike

    18. März 2013 um 18:15 Uhr

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Verwenden:

#include <inttypes.h>

printf("0x%016" PRIXPTR "\n", (uintptr_t) pointer);

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

Benutzeravatar von stefanct
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.

#include <inttypes.h>
#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))

printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer);

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

Benutzeravatar von Keith Thompson
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

Benutzeravatar von groovingandi
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

Benutzeravatar von Rodrigo Queiro
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

1406690cookie-checkWas ist die richtige Verwendung von printf, um mit 0s aufgefüllte Zeiger anzuzeigen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy