Richtiger Formatbezeichner zum Drucken von Zeiger oder Adresse?

Lesezeit: 10 Minuten

Benutzeravatar von San
San

Welchen Formatbezeichner sollte ich verwenden, um die Adresse einer Variablen zu drucken? Ich bin zwischen den folgenden Losen verwirrt.

%u – Ganzzahl ohne Vorzeichen

%x – Hexadezimalwert

%p – leerer Zeiger

Welches ist das optimale Format, um eine Adresse zu drucken?

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Die einfachste Antwort, vorausgesetzt, Sie stören sich nicht an den Launen und Variationen im Format zwischen verschiedenen Plattformen, ist der Standard %p Notation.

Der C99-Standard (ISO/IEC 9899:1999) sagt in §7.19.6.1 ¶8:

p Das Argument soll ein Zeiger auf sein void. Der Wert des Zeigers wird in einer implementierungsdefinierten Weise in eine Folge von Druckzeichen umgewandelt.

(In C11 – ISO/IEC 9899:2011 – befinden sich die Informationen in §7.21.6.1 ¶8.)

Auf einigen Plattformen enthält dies einen führenden 0x und auf anderen wird es nicht, und die Buchstaben könnten in Klein- oder Großbuchstaben sein, und der C-Standard definiert nicht einmal, dass es sich um eine hexadezimale Ausgabe handeln soll, obwohl ich keine Implementierung kenne, wo dies nicht der Fall ist.

Es ist etwas offen zu diskutieren, ob Sie die Zeiger explizit mit a konvertieren sollten (void *) gießen. Es ist explizit, was normalerweise gut ist (also tue ich es), und der Standard sagt: „Das Argument soll ein Hinweis darauf sein void‘. Auf den meisten Computern würden Sie davonkommen, wenn Sie eine explizite Umwandlung weglassen würden. Es wäre jedoch wichtig auf einer Maschine, wo die Bit-Darstellung von a char * Adresse für einen bestimmten Speicherplatz unterscheidet sich von der ‘alles andere Zeiger‘ Adresse für denselben Speicherplatz. Dies wäre eine wortadressierte statt byteadressierte Maschine. Solche Maschinen sind heutzutage nicht üblich (wahrscheinlich nicht verfügbar), aber die erste Maschine, an der ich nach der Universität gearbeitet habe, war eine solche (ICL Perq).

Wenn Sie mit dem implementierungsdefinierten Verhalten von nicht zufrieden sind %pdann C99 verwenden <inttypes.h> und uintptr_t stattdessen:

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

Dadurch können Sie die Darstellung individuell anpassen. Die Hexadezimalziffern habe ich in Großbuchstaben gewählt, damit die Zahl einheitlich die gleiche Höhe und die charakteristische Senke am Anfang hat 0xA1B2CDEF erscheint also nicht wie 0xa1b2cdef die auch entlang der Nummer auf und ab geht. Ihre Wahl jedoch innerhalb sehr weiter Grenzen. Das (uintptr_t) cast wird von GCC eindeutig empfohlen, wenn es den Formatstring zur Kompilierzeit lesen kann. Ich denke, es ist richtig, die Besetzung anzufordern, obwohl ich sicher bin, dass es einige gibt, die die Warnung ignorieren und die meiste Zeit damit davonkommen würden.


Kerrek fragt in den Kommentaren:

Ich bin ein bisschen verwirrt über Standard-Promotions und variadische Argumente. Werden alle Zeiger standardmäßig auf void* hochgestuft? Ansonsten, wenn int* waren, sagen wir, zwei Bytes, und void* wären 4 Bytes, dann wäre es eindeutig ein Fehler, vier Bytes aus dem Argument zu lesen, nicht?

Ich hatte die Illusion, dass der C-Standard besagt, dass alle Objektzeiger die gleiche Größe haben müssen, also void * und int * können nicht unterschiedlich groß sein. Was ich jedoch denke, ist der relevante Abschnitt des C99-Standards, der nicht so nachdrücklich ist (obwohl ich keine Implementierung kenne, bei der das, was ich vorgeschlagen habe, tatsächlich falsch ist):

§6.2.5 Typen

§26 Ein Zeiger auf void muss die gleichen Darstellungs- und Ausrichtungsanforderungen haben wie ein Zeiger auf einen Zeichentyp.39) In ähnlicher Weise müssen Zeiger auf qualifizierte oder nicht qualifizierte Versionen kompatibler Typen die gleichen Darstellungs- und Ausrichtungsanforderungen haben. Alle Zeiger auf Strukturtypen müssen die gleichen Darstellungs- und Ausrichtungsanforderungen haben. Alle Zeiger auf Union-Typen müssen untereinander die gleichen Darstellungs- und Ausrichtungsanforderungen haben. Zeiger auf andere Typen müssen nicht die gleichen Darstellungs- oder Ausrichtungsanforderungen haben.

39) Die gleichen Darstellungs- und Ausrichtungsanforderungen sollen die Austauschbarkeit als Argumente für Funktionen, Rückgabewerte von Funktionen und Mitglieder von Vereinigungen implizieren.

(C11 sagt genau dasselbe in Abschnitt §6.2.5, ¶28 und Fußnote 48.)

Daher müssen alle Zeiger auf Strukturen die gleiche Größe haben und die gleichen Ausrichtungsanforderungen haben, obwohl die Strukturen, auf die die Zeiger zeigen, möglicherweise unterschiedliche Ausrichtungsanforderungen haben. Ebenso für Gewerkschaften. Zeichenzeiger und Leerzeiger müssen dieselben Größen- und Ausrichtungsanforderungen haben. Hinweise auf Variationen auf int (Bedeutung unsigned int und signed int) müssen die gleichen Größen- und Ausrichtungsanforderungen haben; ähnlich für andere Typen. Aber der C-Standard sagt das nicht formell sizeof(int *) == sizeof(void *). Na ja, SO ist gut, um Sie dazu zu bringen, Ihre Annahmen zu überprüfen.

Der C-Standard verlangt definitiv nicht, dass Funktionszeiger die gleiche Größe haben wie Objektzeiger. Das war notwendig, um die unterschiedlichen Speichermodelle auf DOS-ähnlichen Systemen nicht zu zerstören. Dort könnten Sie 16-Bit-Datenzeiger, aber 32-Bit-Funktionszeiger haben oder umgekehrt. Aus diesem Grund schreibt der C-Standard nicht vor, dass Funktionszeiger in Objektzeiger umgewandelt werden können und umgekehrt.

Glücklicherweise (für Programmierer, die auf POSIX abzielen) tritt POSIX in die Bresche und schreibt vor, dass Funktionszeiger und Datenzeiger die gleiche Größe haben:

§2.12.3 Zeigertypen

Alle Funktionszeigertypen sollen die gleiche Darstellung wie der Typzeiger auf void haben. Umwandlung eines Funktionszeigers in void * verändert die Darstellung nicht. EIN void * Der aus einer solchen Konvertierung resultierende Wert kann mithilfe einer expliziten Umwandlung ohne Informationsverlust in den ursprünglichen Funktionszeigertyp zurückkonvertiert werden.

Hinweis: Der ISO C-Standard erfordert dies nicht, aber es ist für die POSIX-Konformität erforderlich.

Es scheint also, dass explizite Casts dazu führen void * sind für maximale Zuverlässigkeit im Code dringend zu empfehlen, wenn ein Zeiger auf eine variadische Funktion wie z printf(). Auf POSIX-Systemen ist es sicher, einen Funktionszeiger zum Drucken in einen void-Zeiger umzuwandeln. Auf anderen Systemen ist dies nicht unbedingt sicher, und es ist auch nicht unbedingt sicher, andere Zeiger als zu übergeben void * ohne Besetzung.

  • Ich bin ein bisschen verwirrt über Standard-Promotions und variadische Argumente. Werden alle Zeiger zum Standard hochgestuft void*? Ansonsten, wenn int* waren, sagen wir, zwei Bytes, und void* wären 4 Bytes, dann wäre es eindeutig ein Fehler, vier Bytes aus dem Argument zu lesen, nicht?

    – Kerrek SB

    29. Januar 2012 um 17:30 Uhr

  • Beachten Sie, dass bei einer Aktualisierung von POSIX (POSIX 2013) Abschnitt 2.12.3 entfernt wurde und die meisten Anforderungen nach verschoben wurden dlsym() funktionieren stattdessen. Eines Tages werde ich die Änderung aufschreiben … aber „eines Tages“ ist nicht „heute“.

    – Jonathan Leffler

    4. Juni 2014 um 18:18 Uhr

  • Gilt diese Antwort auch für Zeiger auf Funktionen? Können sie umgewandelt werden void *? Hmm, ich sehe deinen Kommentar hier. Da nur eine Ein-Wat-Konvertierung erforderlich ist (Funktionszeiger auf void *), es funktioniert dann?

    – chux – Wiedereinsetzung von Monica

    3. Oktober 2015 um 13:59 Uhr


  • @chux: Streng genommen lautet die Antwort “Nein”, aber in der Praxis lautet die Antwort “Ja”. Der C-Standard garantiert nicht, dass Funktionszeiger in a konvertiert werden können void * und zurück ohne Informationsverlust. Pragmatisch gesehen gibt es sehr wenige Maschinen, bei denen die Größe eines Funktionszeigers nicht der Größe eines Objektzeigers entspricht. Ich glaube nicht, dass der Standard eine Methode zum Drucken eines Funktionszeigers auf Maschinen bietet, auf denen die Konvertierung problematisch ist.

    – Jonathan Leffler

    3. Oktober 2015 um 14:07 Uhr

  • “und zurück ohne Informationsverlust” ist drucktechnisch nicht relevant. Hilft das?

    – chux – Wiedereinsetzung von Monica

    3. Oktober 2015 um 14:19 Uhr

p ist der Konvertierungsbezeichner zum Drucken von Zeigern. Benutze das.

int a = 42;

printf("%p\n", (void *) &a);

Denken Sie daran, dass das Weglassen des Casts ein undefiniertes Verhalten ist und dass das Drucken mit p Der Konvertierungsspezifizierer erfolgt in einer implementierungsdefinierten Weise.

  • Verzeihung, warum ist das Weglassen der Besetzung “undefiniertes Verhalten”? Spielt es eine Rolle, um welche Variable es sich handelt, wenn Sie nur die Adresse und nicht den Wert benötigen?

    – Valdo

    29. Januar 2012 um 13:54 Uhr

  • @valdo weil C es sagt (C99, 7.19.6.1p8) “p Das Argument soll ein Zeiger auf void sein.”

    – au

    29. Januar 2012 um 13:55 Uhr

  • @valdo: Es ist nicht unbedingt so, dass alle Zeiger die gleiche Größe / Darstellung haben.

    – Café

    29. Januar 2012 um 14:06 Uhr

Benutzeravatar von Kerrek SB
Kerrek SB

Verwenden %p, für “Zeiger”, und verwenden Sie nichts anderes *. Der Standard garantiert nicht, dass Sie einen Zeiger wie eine bestimmte Art von Ganzzahl behandeln dürfen, sodass Sie mit den ganzzahligen Formaten tatsächlich ein undefiniertes Verhalten erhalten würden. (Zum Beispiel, %u erwartet ein unsigned intAber was wenn void* hat eine andere Größe oder Ausrichtungsanforderung als unsigned int?)

*) [See Jonathan’s fine answer!] Alternativ zu %pSie kann Verwenden Sie zeigerspezifische Makros von <inttypes.h>hinzugefügt in C99.

Alle Objektzeiger sind implizit konvertierbar in void* in C, aber um den Zeiger als variadisches Argument zu übergeben, müssen Sie ihn explizit umwandeln (da beliebige Objektzeiger nur Cabrioaber nicht identisch um Zeiger zu entwerten):

printf("x lives at %p.\n", (void*)&x);

  • Alle Objekt Zeiger sind konvertierbar in void * (obwohl für printf() benötigen Sie technisch gesehen die explizite Umwandlung, da es sich um eine variadische Funktion handelt). Funktionszeiger sind nicht unbedingt konvertierbar void *.

    – Café

    29. Januar 2012 um 13:54 Uhr

  • @caf: Oh, ich wusste nichts von den variadischen Argumenten – behoben! Vielen Dank!

    – Kerrek SB

    29. Januar 2012 um 13:58 Uhr

  • Standard-C erfordert nicht, dass Funktionszeiger in konvertierbar sind void * und zurück zum Funktionszeiger ohne Verlust; Glücklicherweise erfordert POSIX dies jedoch ausdrücklich (wobei darauf hingewiesen wird, dass es nicht Teil von Standard C ist). In der Praxis können Sie also damit durchkommen (Konvertieren void (*function)(void) zu void * und zurück zu void (*function)(void)), aber streng genommen ist es nicht von der C-Norm vorgeschrieben.

    – Jonathan Leffler

    29. Januar 2012 um 14:18 Uhr

  • Jonathan und R.: Das ist alles sehr interessant, aber ich bin mir ziemlich sicher, dass wir hier nicht versuchen, Funktionszeiger zu drucken, also ist das vielleicht nicht ganz der richtige Ort, um darüber zu diskutieren. Ich würde hier viel lieber etwas Unterstützung für mein Beharren sehen, es nicht zu verwenden %u!

    – Kerrek SB

    29. Januar 2012 um 16:08 Uhr


  • %u und %lu sind falsch dran alle Maschinen, nicht einige Maschinen. Die Angabe von printf ist sehr klar, dass das Verhalten undefiniert ist, wenn der übergebene Typ nicht mit dem vom Formatbezeichner geforderten Typ übereinstimmt. Ob die Größe der Typen übereinstimmt (was je nach Maschine wahr oder falsch sein kann) ist irrelevant; Es sind die Typen, die übereinstimmen müssen, und das werden sie nie.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    29. Januar 2012 um 18:09 Uhr

Als Alternative zu den anderen (sehr guten) Antworten könntest du zu casten uintptr_t oder intptr_t (aus stdint.h/inttypes.h) und verwenden Sie die entsprechenden ganzzahligen Konvertierungsbezeichner. Dies würde mehr Flexibilität bei der Formatierung des Zeigers ermöglichen, aber streng genommen ist keine Implementierung erforderlich, um diese Typedefs bereitzustellen.

Benutzeravatar von Pasha
Pascha

Sie können verwenden %x oder %X oder %p; alle sind richtig.

  • Wenn du benutzt %xwird die Adresse in Kleinbuchstaben angegeben, zum Beispiel: a3bfbc4
  • Wenn du benutzt %Xwird die Adresse in Großbuchstaben angegeben, zum Beispiel: A3BFBC4

Beides ist richtig.

Wenn du benutzt %x oder %X Es werden sechs Positionen für die Adresse berücksichtigt, und wenn Sie verwenden %p es erwägt acht Positionen für die Adresse. Zum Beispiel:

uOZTC

  • Willkommen bei S.O. Bitte nehmen Sie sich etwas Zeit, um die anderen Antworten zu lesen, sie erklären deutlich eine Reihe von Details, die Sie übersehen.

    – Antoine L

    18. Februar 2020 um 19:35 Uhr

  • Im Speziellen %X ist schlichtweg falsch. Es erwartet unsigned int und es könnte “funktionieren”, wenn die Zeigergröße des Systems gleich der Ganzzahlgröße ist, aber auf den heutigen Desktop-Systemen ist dies nicht der Fall. Der richtige Weg ist die Verwendung des Formats %p und werfen Sie den Zeiger auf (void*)also ist keine Ihrer Lösungen richtig.

    – Wetterfahne

    9. Mai um 17:43 Uhr


  • Willkommen bei S.O. Bitte nehmen Sie sich etwas Zeit, um die anderen Antworten zu lesen, sie erklären deutlich eine Reihe von Details, die Sie übersehen.

    – Antoine L

    18. Februar 2020 um 19:35 Uhr

  • Im Speziellen %X ist schlichtweg falsch. Es erwartet unsigned int und es könnte “funktionieren”, wenn die Zeigergröße des Systems gleich der Ganzzahlgröße ist, aber auf den heutigen Desktop-Systemen ist dies nicht der Fall. Der richtige Weg ist die Verwendung des Formats %p und werfen Sie den Zeiger auf (void*)also ist keine Ihrer Lösungen richtig.

    – Wetterfahne

    9. Mai um 17:43 Uhr


1426250cookie-checkRichtiger Formatbezeichner zum Drucken von Zeiger oder Adresse?

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

Privacy policy