it-swarm.com.de

Richtiger Formatbezeichner zum Drucken des Zeigers oder der Adresse?

Welchen Formatbezeichner soll ich verwenden, um die Adresse einer Variablen auszudrucken? Ich bin verwirrt zwischen dem unten stehenden Los.

% u - Ganzzahl ohne Vorzeichen

% x - Hexadezimalwert

% p - ungültiger Zeiger

Welches wäre das optimale Format, um eine Adresse auszudrucken?

161
San

Die einfachste Antwort ist die Standardnotation %p, Vorausgesetzt, Sie stören sich nicht an den Abweichungen und Formatunterschieden zwischen den verschiedenen Plattformen.

In der C99-Norm (ISO/IEC 9899: 1999) heißt es in §7.19.6.1 ¶8:

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

(In C11 - ISO/IEC 9899: 2011 - finden Sie die Informationen in §7.21.6.1 ¶8.)

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

Es ist etwas offen zu diskutieren, ob Sie die Zeiger explizit mit einem (void *) - Cast konvertieren sollten. Es ist explizit, was normalerweise gut ist (so ist es, was ich tue), und der Standard sagt 'das Argument soll ein Zeiger auf void sein'. Auf den meisten Maschinen würden Sie mit dem Weglassen einer expliziten Besetzung davonkommen. Auf einem Computer, auf dem sich die Bitdarstellung einer char * - Adresse für einen bestimmten Speicherort von dem "" - Zeiger unterscheidet, ist dies jedoch von Bedeutung. -) 'Adresse für den gleichen Speicherort. Dies wäre eine Word-adressierte Maschine anstelle einer byte-adressierten Maschine. Solche Maschinen sind heutzutage nicht üblich (wahrscheinlich nicht verfügbar), aber die erste Maschine, an der ich nach dem Studium gearbeitet habe, war eine solche (ICL Perq).

Wenn Sie mit dem implementierungsspezifischen Verhalten von %p Nicht zufrieden sind, verwenden Sie stattdessen C99 <inttypes.h> Und uintptr_t:

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

Auf diese Weise können Sie die Darstellung genau auf Ihre Bedürfnisse abstimmen. Ich habe die hexadezimalen Ziffern in Großbuchstaben angegeben, damit die Zahl einheitlich die gleiche Höhe hat und die charakteristische Neigung am Anfang von 0xA1B2CDEF Nicht wie bei 0xa1b2cdef Erscheint, die nach oben und unten abfällt auch entlang der Nummer. Ihre Wahl jedoch in sehr weiten Grenzen. Die Umwandlung von (uintptr_t) Wird von GCC eindeutig empfohlen, wenn die Formatzeichenfolge beim Kompilieren gelesen werden 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 Standardaktionen und unterschiedliche Argumente. Werden alle Zeiger standardmäßig für ungültig erklärt *? Wenn andernfalls int* Beispielsweise zwei Bytes und void* 4 Bytes wären, wäre es eindeutig ein Fehler, vier Bytes aus dem Argument non? Zu lesen.

Ich hatte die Illusion, dass der C-Standard besagt, dass alle Objektzeiger dieselbe Größe haben müssen, sodass void * Und int * Keine unterschiedlichen Größen haben können. Was ich jedoch für den relevanten Abschnitt des C99-Standards halte, ist nicht so nachdrücklich (obwohl ich keine Implementierung kenne, bei der das, was ich vorgeschlagen habe, wahr ist, tatsächlich falsch ist):

§6.2.5 Typen

¶26 Ein Zeiger auf ungültig muss die gleichen Darstellungs- und Ausrichtungsanforderungen erfüllen wie ein Zeiger auf einen Zeichentyp.39) Ebenso 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 Verbindungstypen müssen die gleichen Darstellungs- und Ausrichtungsanforderungen haben. Zeiger auf andere Typen müssen nicht die gleichen Darstellungs- oder Ausrichtungsanforderungen haben.

39) Dieselben Darstellungs- und Ausrichtungsanforderungen implizieren die Austauschbarkeit von Argumenten für Funktionen, Rückgabewerte von Funktionen und Gewerkschaftsmitgliedern.

(C11 sagt genau das Gleiche in den Abschnitten §6.2.5, ¶28 und Fußnote 48.)

Daher müssen alle Zeiger auf Strukturen dieselbe Größe haben und dieselben Ausrichtungsanforderungen haben, auch wenn die Strukturen, auf die die Zeiger zeigen, möglicherweise unterschiedliche Ausrichtungsanforderungen haben. Ähnliches gilt für Gewerkschaften. Zeichenzeiger und Leerzeiger müssen die gleichen Größen- und Ausrichtungsanforderungen haben. Zeiger auf Variationen von int (dh unsigned int Und signed int) Müssen die gleichen Größen- und Ausrichtungsanforderungen haben. Ähnliches gilt für andere Typen. Der C-Standard sagt jedoch formal nicht, dass sizeof(int *) == sizeof(void *). Na ja, SO ist gut dafür, dass Sie Ihre Annahmen überprüfen.

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

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

§2.12.3 Zeigertypen

Alle Funktionszeigertypen müssen dieselbe Darstellung haben wie der zu stornierende Typzeiger. Die Konvertierung eines Funktionszeigers nach void * Soll die Darstellung nicht verändern. Ein void * - Wert, der aus einer solchen Konvertierung resultiert, kann unter Verwendung einer expliziten Umwandlung ohne Informationsverlust in den ursprünglichen Funktionszeigertyp zurückkonvertiert werden.

Hinweis: Der ISO C-Standard schreibt dies nicht vor, ist jedoch für die POSIX-Konformität erforderlich.

Es scheint also, dass explizite Umwandlungen in void * Dringend empfohlen werden, um maximale Zuverlässigkeit im Code zu erzielen, wenn ein Zeiger auf eine variable Funktion wie printf() übergeben wird. Auf POSIX-Systemen ist es sicher, einen Funktionszeiger zum Drucken auf einen ungültigen Zeiger zu setzen. Auf anderen Systemen ist es nicht unbedingt sicher, dies zu tun, und es ist auch nicht unbedingt sicher, andere Zeiger als void * Ohne Umwandlung zu übergeben.

201

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

int a = 42;

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

Denken Sie daran, dass das Weglassen der Umwandlung undefiniertes Verhalten ist und dass das Drucken mit dem Konvertierungsspezifizierer p auf implementierungsdefinierte Weise erfolgt.

40
ouah

Verwenden Sie %p Als "Zeiger" und nichts anderes *. Der Standard garantiert nicht, dass Sie einen Zeiger wie eine bestimmte Ganzzahl behandeln dürfen, sodass Sie bei den integrierten Formaten ein undefiniertes Verhalten erhalten. (Zum Beispiel erwartet %u Einen unsigned int, Aber was ist, wenn void* Eine andere Größe oder Ausrichtung als unsigned int Alternativ zu %p Können Sie können zeigerspezifische Makros aus <inttypes.h> Verwenden, die in C99 hinzugefügt wurden.

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

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

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

8
R..