Gibt es eine Möglichkeit, einen Zeiger auf eine Funktion in ANSI C zu drucken? Das bedeutet natürlich, dass Sie den Funktionszeiger in einen void-Zeiger umwandeln müssen, aber das scheint nicht möglich zu sein?
#include <stdio.h>
int main() {
int (*funcptr)() = main;
printf("%p\n", (void* )funcptr);
printf("%p\n", (void* )main);
return 0;
}
$ gcc -ansi -pedantic -Wall test.c -o test test.c: In Funktion ‘main’: test.c:6: Warnung: ISO C verbietet die Umwandlung von Funktionszeigern in Objektzeigertypen test.c:7: Warnung: ISO C verbietet die Umwandlung von Funktionszeigern in Objektzeigertypen $ ./test 0x400518
0x400518
Es “funktioniert”, ist aber kein Standard …
Nun, ich wollte gerade eine Antwort akzeptieren, die funktionierte, bis sie gelöscht wurde (obwohl sie keinen Sinn ergab).
– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
30. April 2010 um 0:56 Uhr
Können Sie es in ein int (oder 64-Bit-int auf größeren Systemen) umwandeln und stattdessen drucken?
– Michael Dorgan
30. April 2010 um 0:59 Uhr
@Michael Dorgan: Einen Zeiger auf einen anderen ganzzahligen Typ als umwandeln intptr_t oder uintptr_t ist implementierungsdefiniert, die Implementierung dieser Typen ist jedoch gemäß dem Standard optional.
– Traumlax
30. April 2010 um 1:00 Uhr
@Longpoke: Entschuldigung für das Löschen meiner Antwort, ich war mir nicht sicher, ob es Ihre Compiler-Flags erfüllen würde, also habe ich es vorübergehend entfernt, nur für den Fall, dass dies nicht der Fall wäre.
– Traumlax
30. April 2010 um 1:02 Uhr
Was ich gesucht habe, war ein legaler Weg, und Ihrer scheint legal zu sein, und der Compiler hält auch die Klappe, also danke 🙂
– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
30. April 2010 um 1:08 Uhr
Café
Der einzige legale Weg, dies zu tun, besteht darin, mit einem Zeichentyp auf die Bytes zuzugreifen, aus denen der Zeiger besteht. So was:
#include <stdio.h>
int main() {
int (*funcptr)() = main;
unsigned char *p = (unsigned char *)&funcptr;
size_t i;
for (i = 0; i < sizeof funcptr; i++)
{
printf("%02x ", p[i]);
}
putchar('\n');
return 0;
}
Untersuchen der Bytes des Funktionszeigers mit einem lvalue vom Typ void *oder jeder Nicht-Zeichentyp, ist ein undefiniertes Verhalten.
Was diese Bytes eigentlich den Funktionszeiger bilden bedeuten ist implementierungsabhängig. Sie könnten zum Beispiel nur einen Index in einer Funktionstabelle darstellen; oder es könnten sogar die ersten N Zeichen des Funktionsnamens sein, der in der Symboltabelle nachgeschlagen wird, wenn Sie über den Funktionszeiger aufrufen. Die einzigen Operationen, die für einen Funktionszeiger unterstützt werden müssen, sind das Aufrufen der Funktion durch ihn und der Vergleich mit einem anderen Funktionszeiger oder NULL für strikte Gleichheit/Ungleichheit, sodass es einen sehr großen Spielraum gibt, wie sie implementiert werden.
Wäre es nicht besser zu verwenden uintptr_t Anstatt von unsigned char ? Wie in int (*funcptr)() = main; uintptr_t *p = (uintptr_t *)&funcptr; printf("0x%" PRIxPTR "\n", *p);, Vermeidung einer Schleife? (Angenommen C99 stdint.h und inttypes.h Kopfzeilen werden unterstützt.)
– Chris Lutz
30. April 2010 um 2:14 Uhr
@Chris: uintptr_t hat keine Beziehung zu Funktionszeigern und ist optional. Aus der Perspektive, etwas völlig Tragbares zu schreiben, nein, es ist nicht besser.
– Steve Jessop
30. April 2010 um 2:32 Uhr
Die Antwort von caf ist richtig. Es ist süß zu sehen, dass der undefinierte Code von dreamlax als Antwort akzeptiert wird, während der richtige Code hier schmachtet.
– Windows-Programmierer
30. April 2010 um 4:47 Uhr
Ja, das wird nicht so hübsch sein wie %p 🙁 Ganz zu schweigen von der Endianess und der fehlenden korrekten Darstellung bei segmentierten Modellen.
– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
30. April 2010 um 5:18 Uhr
Leider, bis sie a hinzufügen printf Formatbezeichner zum Drucken von Funktionszeigern ist das Beste, was Sie in strikt konformem C tun können. Endianness ist ein Ablenkungsmanöver – es gibt keine Garantie dafür, dass ein “Funktionszeiger” überhaupt eine Zahl ist (z Zeichen des Funktionsnamens, der beim Aufruf über den Funktionszeiger in der Symboltabelle nachgeschlagen wird). Aus diesem Grund sind Funktionszeiger nicht in und aus anderen Zeigertypen konvertierbar – das liegt daran, dass die Implementierung einen großen Spielraum bei der Implementierung hat.
– Café
30. April 2010 um 7:59 Uhr
Es gibt die Verwendung von Unions, die die Warnung/den Fehler umgehen können, aber das Ergebnis ist immer noch (höchstwahrscheinlich) undefiniertes Verhalten:
#include <stdio.h>
int
main (void)
{
union
{
int (*funcptr) (void);
void *objptr;
} u;
u.funcptr = main;
printf ("%p\n", u.objptr);
return 0;
}
Sie können zwei Funktionszeiger vergleichen (zB printf ("%i\n", (main == funcptr));) mit einer if-Anweisung, um zu testen, ob sie gleich sind oder nicht (ich weiß, dass das den Zweck völlig zunichte macht und sehr wohl irrelevant sein könnte), aber was tatsächlich die Adresse des Funktionszeigers ausgibt, ist Sache des Anbieters der C-Bibliothek Ihrer Zielplattform und Ihres Compilers.
Tippspiel mit Gewerkschaften ist es nicht nicht definiert Verhalten. es ist implementierungsdefiniert Verhalten, und auf den meisten Compilern tut es genau das, was Sie erwarten.
– Rüschenwind
5. Oktober 2016 um 21:30 Uhr
Wandeln Sie den Funktionszeiger in eine ganze Zahl um und dann wieder in einen Zeiger, um “%p” zu verwenden.
#include <stdio.h>
int main() {
int (*funcptr)() = main;
printf("%p\n", (void *)(size_t) funcptr);
printf("%p\n", (void *)(size_t) main);
return 0;
}
Beachten Sie, dass auf einigen Plattformen (z. B. 16-Bit-DOS in den “mittleren” oder “kompakten” Speichermodellen) Zeiger auf Daten und Zeiger auf Funktionen nicht die gleiche Größe haben.
Ja, “16-Bit-DOS” lief auf einer segmentierten Speicherarchitektur. Zum Beispiel hätte der 80286 ein 4-Byte void * und 2 Byte size_t.
– Richter Maygarden
30. April 2010 um 13:59 Uhr
Versuche dies:
#include <stdio.h>
#include <inttypes.h>
int main() {
int (*funcptr)() = main;
unsigned char *p = (unsigned char *)&funcptr;
int i;
/* sample output: 00000000004005e0 */
printf("%016"PRIxPTR"\n", (uintptr_t)main);
/* sample output: 00000000004005e0 */
printf("%016"PRIxPTR"\n", (uintptr_t)funcptr);
/* reflects the fact that this program is running on little-endian machine
sample output: e0 05 40 00 00 00 00 00 */
for (i = 0; i < sizeof funcptr; i++)
{
printf("%02x ", p[i]);
}
putchar('\n');
return 0;
}
Mit diesen Flags werden keine Warnungen ausgegeben
Josef Quinsey
Mit gcc 4.3.4 wird die Antwort von dreamlax mit den Schaltern -O2 -Wstrict-aliasing Folgendes erzeugen:
warning: dereferencing type-punned pointer will break strict-aliasing rules
Hinzugefügt: Ich denke, die Einwände gegen caf’s Antwort über Endianität und Größe sind angemessen, was seine Lösung nicht anspricht (kein Wortspiel beabsichtigt). Dustins Vorschlag, eine Gewerkschaftsbesetzung zu verwenden, ist wahrscheinlich legal (obwohl es nach dem, was ich gelesen habe, einige Debatten zu geben scheint, aber dein Compiler ist wichtiger als das Gesetz). Aber sein Code könnte durch den Einzeiler vereinfacht (oder verschleiert, je nach Geschmack) werden:
weil natürlich &hauptsächlich zerfällt zu hauptsächlich.
Nützliche Informationen, aber das beantwortet seine Frage nicht wirklich, es hätte ein Kommentar zu meiner Antwort sein sollen, im Gegensatz zu einer separaten Antwort insgesamt.
– Traumlax
30. April 2010 um 4:11 Uhr
@dreamlax: Entschuldigung für die separate Antwort, aber ich habe nur 31 Stackoverflow-Punkte, weniger als die 50, die für Kommentare benötigt werden. Aber ich werde diese Antwort so schnell wie möglich in einen Kommentar umwandeln.
– Joseph Quinsey
30. April 2010 um 5:42 Uhr
@café: Entschuldigung. Ich sehe, dass Sie diesen Punkt in Ihrer Antwort bereits auf die Antwort von Dreamlax angesprochen haben.
– Joseph Quinsey
3. Mai 2010 um 16:42 Uhr
Ich bin mir nicht sicher, ob dies als bewährte Methode angesehen wird, aber es erzielt den Effekt ohne eine Schleife, Vereinigungen, Umwandlungen in Nicht-Zeiger-Typen oder zusätzliche Abhängigkeiten string.h
int (*funcptr)() = main;
void* p = NULL;
memcpy(&p, (void**) &funcptr, sizeof(funcptr));
printf("%p", p);
Keine Warnungen mit gcc -ansi -pedantic -Wall -Wstrict-aliasing auf GCC 7.1.1
Nützliche Informationen, aber das beantwortet seine Frage nicht wirklich, es hätte ein Kommentar zu meiner Antwort sein sollen, im Gegensatz zu einer separaten Antwort insgesamt.
– Traumlax
30. April 2010 um 4:11 Uhr
@dreamlax: Entschuldigung für die separate Antwort, aber ich habe nur 31 Stackoverflow-Punkte, weniger als die 50, die für Kommentare benötigt werden. Aber ich werde diese Antwort so schnell wie möglich in einen Kommentar umwandeln.
– Joseph Quinsey
30. April 2010 um 5:42 Uhr
@café: Entschuldigung. Ich sehe, dass Sie diesen Punkt in Ihrer Antwort bereits auf die Antwort von Dreamlax angesprochen haben.
– Joseph Quinsey
3. Mai 2010 um 16:42 Uhr
14107300cookie-checkWie formatiere ich einen Funktionszeiger?yes
Nun, ich wollte gerade eine Antwort akzeptieren, die funktionierte, bis sie gelöscht wurde (obwohl sie keinen Sinn ergab).
– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
30. April 2010 um 0:56 Uhr
Können Sie es in ein int (oder 64-Bit-int auf größeren Systemen) umwandeln und stattdessen drucken?
– Michael Dorgan
30. April 2010 um 0:59 Uhr
@Michael Dorgan: Einen Zeiger auf einen anderen ganzzahligen Typ als umwandeln
intptr_t
oderuintptr_t
ist implementierungsdefiniert, die Implementierung dieser Typen ist jedoch gemäß dem Standard optional.– Traumlax
30. April 2010 um 1:00 Uhr
@Longpoke: Entschuldigung für das Löschen meiner Antwort, ich war mir nicht sicher, ob es Ihre Compiler-Flags erfüllen würde, also habe ich es vorübergehend entfernt, nur für den Fall, dass dies nicht der Fall wäre.
– Traumlax
30. April 2010 um 1:02 Uhr
Was ich gesucht habe, war ein legaler Weg, und Ihrer scheint legal zu sein, und der Compiler hält auch die Klappe, also danke 🙂
– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
30. April 2010 um 1:08 Uhr