Wie formatiere ich einen Funktionszeiger?

Lesezeit: 7 Minuten

Benutzer-Avatar von L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

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

Benutzeravatar von caf
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;
}

Benutzte diese Flags:

gcc -ansi -pedantic -Wall -O2 -Wstrict-aliasing c.c

Mit diesen Flags werden keine Warnungen ausgegeben

Benutzeravatar von Joseph Quinsey
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:

printf("%p\n", ((union {int (*from)(void); void *to;})funcptr).to);

Dadurch wird die strikte gcc-Aliasing-Warnung entfernt (aber ist sie „richtig“?).

Aggregierte Casts funktionieren nicht, wenn Sie den Schalter -pedantic verwenden oder z. B. SGI IRIX verwenden, daher müssen Sie Folgendes verwenden:

printf("%p\n", ((union {int (*from)(void); void *to;} *)&funcptr)->to);

Aber in Bezug auf die ursprüngliche Frage: Ihr Ursprung liegt in der Verwendung von -pedantic, was ich für etwas pedantisch halte :).

Weiter bearbeiten: Beachten Sie, dass Sie nicht verwenden können hauptsächlich im letzten Beispiel, wie in:

printf("%p\n", ((union {int (*from)(void); void *to;})   main).to);  // ok
printf("%p\n", ((union {int (*from)(void); void *to;} *)&main)->to); // wrong!

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

1410730cookie-checkWie formatiere ich einen Funktionszeiger?

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

Privacy policy