Antworten:
Die einfachste Antwort ist die Standardnotation, vorausgesetzt, Sie haben nichts gegen die Unklarheiten und Formatunterschiede zwischen verschiedenen Plattformen %p
.
Die C99-Norm (ISO / IEC 9899: 1999) besagt in §7.19.6.1 ¶8:
p
Das Argument soll ein Zeiger auf seinvoid
. Der Wert des Zeigers wird implementierungsdefiniert in eine Folge von Druckzeichen konvertiert.
(In C11 - ISO / IEC 9899: 2011 - sind die Informationen in §7.21.6.1 ¶8 enthalten.)
Auf einigen Plattformen wird dies ein führendes 0x
und auf anderen nicht einschließen , und die Buchstaben können in Klein- oder Großbuchstaben geschrieben sein, und der C-Standard definiert nicht einmal, dass es sich um eine hexadezimale Ausgabe handelt, obwohl ich weiß Keine Implementierung, wo es nicht ist.
Es ist etwas offen zu diskutieren, ob Sie die Zeiger explizit mit einer (void *)
Besetzung konvertieren sollten . Es ist explizit, was normalerweise gut ist (so ist es, was ich tue), und der Standard sagt, dass das Argument ein Zeiger auf sein soll void
. Auf den meisten Maschinen würden Sie eine explizite Besetzung weglassen. Auf einer Maschine, auf der sich die Bitdarstellung einer char *
Adresse für einen bestimmten Speicherort von dem Zeiger " Alles andere" unterscheidet, wäre dies jedoch von Bedeutung Adresse " für denselben Speicherort unterscheidet. Dies wäre eine Maschine mit Wortadresse anstelle einer Maschine mit Byteadressierung. 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 implementierungsdefinierten Verhalten von nicht zufrieden sind %p
, verwenden Sie C99 <inttypes.h>
und uintptr_t
stattdessen:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Auf diese Weise können Sie die Darstellung an Ihre Bedürfnisse anpassen. Ich habe mich dafür entschieden, die hexadezimalen Ziffern in Großbuchstaben zu schreiben, damit die Zahl gleichmäßig gleich hoch ist und die charakteristische Neigung zu Beginn so 0xA1B2CDEF
erscheint, nicht wie 0xa1b2cdef
die, die auch entlang der Zahl auf und ab fällt . Ihre Wahl jedoch in sehr weiten Grenzen. Die (uintptr_t)
Besetzung wird von GCC eindeutig empfohlen, wenn die Formatzeichenfolge zur Kompilierungszeit 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 durchkommen.
Kerrek fragt in den Kommentaren:
Ich bin etwas verwirrt über Standardaktionen und verschiedene Argumente. Werden alle Zeiger standardmäßig auf void * hochgestuft? Andernfalls, wenn beispielsweise
int*
zwei Bytes undvoid*
4 Bytes wären , wäre es eindeutig ein Fehler, vier Bytes aus dem Argument zu lesen, nicht?
Ich war in der Illusion , dass der C - Standard sagt , dass alle Objektzeiger müssen gleich groß sein, so void *
und int *
nicht unterschiedlich groß sein 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 void muss dieselben Darstellungs- und Ausrichtungsanforderungen haben wie ein Zeiger auf einen Zeichentyp. 39) Ebenso müssen Zeiger auf qualifizierte oder nicht qualifizierte Versionen kompatibler Typen dieselben Darstellungs- und Ausrichtungsanforderungen haben. Alle Zeiger auf Strukturtypen müssen dieselben Darstellungs- und Ausrichtungsanforderungen haben. Alle Zeiger auf Vereinigungstypen müssen dieselben Darstellungs- und Ausrichtungsanforderungen haben. Zeiger auf andere Typen müssen nicht dieselben Darstellungs- oder Ausrichtungsanforderungen haben.
39) Dieselben Darstellungs- und Ausrichtungsanforderungen sollen Austauschbarkeit als Argumente für Funktionen, Rückgabewerte von Funktionen und Gewerkschaftsmitglieder implizieren.
(C11 sagt genau dasselbe in Abschnitt §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, unterschiedliche Ausrichtungsanforderungen haben können. Ähnliches gilt für Gewerkschaften. Zeichenzeiger und Leerzeiger müssen die gleichen Anforderungen an Größe und Ausrichtung haben. Zeiger auf Variationen von 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 formal nicht 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 wie Objektzeiger haben. Dies war notwendig, um die verschiedenen Speichermodelle auf DOS-ähnlichen Systemen nicht zu beschädigen. 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 konvertiert werden können und umgekehrt.
Glücklicherweise (für Programmierer, die auf POSIX abzielen) tritt POSIX in die Sicherheitslücke ein und schreibt vor, dass Funktionszeiger und Datenzeiger dieselbe Größe haben:
§2.12.3 Zeigertypen
Alle Funktionszeigertypen müssen dieselbe Darstellung haben wie der Typzeiger für ungültig. Die Konvertierung eines Funktionszeigers in
void *
darf die Darstellung nicht verändern. Einvoid *
Wert, der sich aus einer solchen Konvertierung ergibt, kann ohne Informationsverlust mithilfe einer expliziten Umwandlung wieder in den ursprünglichen Funktionszeigertyp konvertiert werden.Hinweis: Der ISO C-Standard verlangt dies nicht, ist jedoch für die POSIX-Konformität erforderlich.
Es scheint also, dass explizite Casts void *
für eine maximale Zuverlässigkeit des Codes dringend empfohlen werden, wenn ein Zeiger auf eine variable Funktion wie z printf()
. Auf POSIX-Systemen ist es sicher, einen Funktionszeiger zum Drucken in einen ungültigen Zeiger umzuwandeln. Auf anderen Systemen ist dies nicht unbedingt sicher, und es ist auch nicht unbedingt sicher, andere Zeiger als void *
ohne Besetzung zu übergeben.
dlsym()
stattdessen auf die Funktion verschoben wurden . Eines Tages werde ich die Änderung aufschreiben ... aber "eines Tages" ist nicht "heute".
void *
ohne Informationsverlust in ein und zurück konvertiert werden können. Pragmatisch gesehen gibt es nur sehr wenige Maschinen, bei denen die Größe eines Funktionszeigers nicht mit der Größe eines Objektzeigers übereinstimmt. Ich glaube nicht, dass der Standard eine Methode zum Drucken eines Funktionszeigers auf Maschinen bietet, auf denen die Konvertierung problematisch ist.
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 p
Umwandlung ein undefiniertes Verhalten ist und dass das Drucken mit dem Konvertierungsspezifizierer auf implementierungsdefinierte Weise erfolgt.
Verwenden Sie %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 Integralformaten tatsächlich ein undefiniertes Verhalten erhalten. (Erwartet zum Beispiel %u
eine unsigned int
, aber was ist, wenn void*
eine andere Größe oder Ausrichtung erforderlich ist als unsigned int
?)
*) [Siehe Jonathans feine Antwort!] Alternativ %p
, Sie können Zeiger spezifischen Makros verwenden <inttypes.h>
, hinzugefügt in C99.
Alle Objektzeiger sind implizit void*
in C konvertierbar. Um den Zeiger jedoch als variadisches Argument zu übergeben, müssen Sie ihn explizit umwandeln (da beliebige Objektzeiger nur konvertierbar , aber nicht identisch mit ungültigen Zeigern sind):
printf("x lives at %p.\n", (void*)&x);
void *
(obwohl printf()
Sie technisch die explizite Umwandlung benötigen, da es sich um eine variable Funktion handelt). Funktionszeiger sind nicht unbedingt in konvertierbar void *
.
void *
ohne Verlust in Funktionszeiger konvertiert werden können. Glücklicherweise verlangt POSIX dies jedoch ausdrücklich (wobei zu beachten ist, dass es nicht Teil von Standard C ist). In der Praxis können Sie also damit durchkommen (Konvertieren void (*function)(void)
in void *
und zurück in void (*function)(void)
), aber streng genommen ist dies nicht durch den C-Standard vorgeschrieben.
%u
!
%u
und %lu
sind auf allen Maschinen falsch , nicht auf einigen 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), spielt keine Rolle. Es sind die Typen, die übereinstimmen müssen, und sie werden es niemals tun.
Alternativ zu den anderen (sehr guten) Antworten können Sie die entsprechenden Ganzzahlkonvertierungsspezifizierer in uintptr_t
oder intptr_t
(von stdint.h
/ inttypes.h
) umwandeln und verwenden. Dies würde mehr Flexibilität bei der Formatierung des Zeigers ermöglichen, aber genau genommen ist keine Implementierung erforderlich, um diese Typedefs bereitzustellen.
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
Sie, ob es ein undefiniertes Verhalten ist, die Adresse einer Variablen mit dem %u
Formatbezeichner zu drucken . Die Adresse der Variablen ist in den meisten Fällen positiv. Kann ich sie %u
anstelle von verwenden %p
?
%u
ist ein Format für den unsigned int
Typ und kann nicht mit einem Zeigerargument auf verwendet werden printf
.
Sie können %x
oder %X
oder verwenden %p
; Alle von ihnen sind richtig.
%x
, wird die Adresse in Kleinbuchstaben angegeben, zum Beispiel:a3bfbc4
%X
, wird die Adresse in Großbuchstaben angegeben, zum Beispiel:A3BFBC4
Beide sind richtig.
Wenn Sie verwenden %x
oder %X
sechs Positionen für die Adresse in Betracht ziehen, und wenn Sie verwenden %p
, werden acht Positionen für die Adresse berücksichtigt. Beispielsweise:
void*
? Andernfalls, wenn beispielsweiseint*
zwei Bytes undvoid*
4 Bytes wären , wäre es eindeutig ein Fehler, vier Bytes aus dem Argument zu lesen, nicht?