Funktionszeiger in C – Art und Verwendung

Lesezeit: 9 Minuten

Benutzer-Avatar
Benjamin Barrois

Ich habe hier gerade eine interessante Frage gelesen, die mich über zwei weitere Dinge wundern lässt:

  1. Warum sollte irgendjemand Funktionszeiger vergleichen, wenn man bedenkt, dass die Eindeutigkeit von Funktionen durch ihre unterschiedlichen Namen gewährleistet ist?
  2. Sieht der Compiler Funktionszeiger als spezielle Zeiger? Ich meine, sieht es sie wie, sagen wir, Hinweise auf void * oder enthält es umfangreichere Informationen (wie Rückgabetyp, Anzahl der Argumente und Argumenttypen?)

  • Die “reichere Information” wird im Zeiger gespeichert Typz.B, int (*) (int). Der Typ speichert, wie die Funktion aufgerufen werden soll.

    – Benutzer202729

    29. Januar 2018 um 6:40 Uhr

  • Und… using ptr = int (*) (int); ptr a = f; ptr b = f; if (a == b) { /* a is equal to b */ }

    – Benutzer202729

    29. Januar 2018 um 6:41 Uhr

  • @ user202729: Sie versuchen also zu sagen, ohne es zu sagen: “Es kann nützlich sein, wenn Sie Funktionen mit mehreren Zeigern durchsuchen”, ist das richtig?

    – Benjamin Barrois

    29. Januar 2018 um 6:44 Uhr

  • Der Code, der den Vergleich durchführen muss, weiß nicht unbedingt, woher der Zeiger stammt und wie er in der Quelle heißt.

    – Thorbjørn Ravn Andersen

    29. Januar 2018 um 14:09 Uhr

Benutzer-Avatar
StoryTeller – Unslander Monica

Warum sollte man Funktionszeiger vergleichen? Hier ist ein Beispiel:

#include <stdbool.h>

/*
 * Register a function to be executed on event. A function may only be registered once.
 * Input:
 *   arg - function pointer
 * Returns:
 *   true on successful registration, false if the function is already registered.
 */
bool register_function_for_event(void (*arg)(void));

/*
 * Un-register a function previously registered for execution on event.
 * Input:
 *   arg - function pointer
 * Returns:
 *   true on successful un-registration, false if the function was not registered.
 */
bool unregister_function_for_event(void (*arg)(void));

Der Körper von register_function_for_event sieht nur arg. Es wird kein Funktionsname angezeigt. Es muss Funktionszeiger vergleichen, um zu melden, dass jemand dieselbe Funktion zweimal registriert.

Und wenn Sie so etwas unterstützen möchten unregister_function_for_event Zur Ergänzung des oben Gesagten ist die einzige Information, die Sie haben, die Funktionsadresse. Sie müssten es also erneut übergeben und damit vergleichen, um das Entfernen zu ermöglichen.

Was die reichhaltigeren Informationen betrifft, ja. Wenn der Funktionstyp einen Prototyp enthält, ist er Teil der statischen Typinformationen. Beachten Sie, dass in C ein Funktionszeiger ohne Prototyp deklariert werden kann, aber das ist ein veraltetes Feature.

Benutzer-Avatar
Ajay Brahmakshatriya

  1. Warum sollte jemand Pointer vergleichen? Betrachten Sie das folgende Szenario –

    Sie haben ein Array von Funktionszeigern, sagen wir, es ist eine Rückrufkette, und Sie müssen jeden von ihnen aufrufen. Die Liste wird mit a abgeschlossen NULL (oder Wächter) Funktionszeiger. Sie müssen vergleichen, ob Sie das Ende der Liste erreicht haben, indem Sie mit diesem Sentinel-Zeiger vergleichen. Dieser Fall rechtfertigt auch die Bedenken früherer OPs, dass verschiedene Funktionen unterschiedliche Zeiger haben sollten, selbst wenn sie ähnlich sind.

  2. Sieht der Compiler sie anders? Ja. Die Typinformationen beinhalten alle Informationen über die Argumente und den Rückgabetyp.

    Beispielsweise wird/sollte der folgende Code vom Compiler abgelehnt werden –

    void foo(int a);
    void (*bar)(long) = foo; // Without an explicit cast
    

  • Schön, “Rückrufketten” ist wohl die kürzest mögliche Antwort. +1

    – Benutzer541686

    29. Januar 2018 um 7:23 Uhr

  • Was meinst du damit, du kannst keinen Nullfunktionszeiger haben? Sie können einen Null-Funktionszeiger haben. Funktionszeiger mit statischer Speicherdauer und ohne expliziten Initialisierer werden standardmäßig sogar auf Nullzeiger initialisiert.

    – Benutzer2357112

    29. Januar 2018 um 20:21 Uhr

  • @ user2357112 Ich stelle mir vor, dass dies bedeutet, dass ein Funktionszeiger, der durch Verweisen auf eine tatsächliche Funktion erhalten wird, niemals null sein kann – wenn Sie also eine haben void foo() {}, foo != NULL.

    – Nik

    29. Januar 2018 um 20:54 Uhr

  • @NicHartley: Die Antwort erhebt jedoch den Anspruch im Zusammenhang mit der Beendigung eines Arrays von Funktionszeigern. In diesem Zusammenhang wäre ein Null-Funktionszeiger die natürliche Wahl (und dies würde natürlich ausgedrückt werden als 0 oder NULL anstatt zu versuchen, die Adresse einer tatsächlichen Funktion zu nehmen).

    – Benutzer2357112

    29. Januar 2018 um 21:33 Uhr

  • Ich finde die Erklärung hier ziemlich schwach, obwohl das hauptsächlich die Fragen sind, die nicht mehr Kontext enthalten. Die referenzierte Frage befasst sich mit der nicht standardmäßigen COMDAT-Faltung, die dies tun würde nicht ein Problem sein, wenn man mit dem vergleicht nullptr. Das Problem tritt nur auf, wenn wir zwei tatsächliche Funktionen miteinander vergleichen.

    – Voo

    29. Januar 2018 um 22:07 Uhr

Benutzer-Avatar
R Sahu

  1. Warum sollte irgendjemand Funktionszeiger vergleichen, wenn man bedenkt, dass die Eindeutigkeit von Funktionen durch ihre unterschiedlichen Namen gewährleistet ist?

Ein Funktionszeiger kann zu verschiedenen Zeiten in einem Programm auf verschiedene Funktionen zeigen.

Wenn Sie eine Variable wie z

void (*fptr)(int);

es kann auf jede Funktion zeigen, die ein akzeptiert int als Input und Returns void.

Nehmen wir an, Sie haben:

void function1(int)
{
}

void function2(int)
{
}

Sie können Folgendes verwenden:

fptr = function1;
foo(fptr);

oder:

fptr = function2;
foo(fptr);

Vielleicht möchten Sie verschiedene Dinge in tun foo je nachdem ob fptr weist auf die eine oder andere Funktion hin. Daher die Notwendigkeit für:

if ( fptr == function1 )
{
    // Do stuff 1
}
else
{
    // Do stuff 2
}
  1. Sieht der Compiler Funktionszeiger als spezielle Zeiger? Ich meine, sieht es sie wie, sagen wir, Zeiger auf void * oder enthält es umfangreichere Informationen (wie Rückgabetyp, Anzahl der Argumente und Argumenttypen?)

Ja, Funktionszeiger sind spezielle Zeiger, anders als Zeiger, die auf Objekte zeigen.

Der Typ eines Funktionszeigers enthält all diese Informationen zur Kompilierzeit. Geben Sie also einen Funktionszeiger an, der Compiler hat alle diese Informationen – den Rückgabetyp, die Anzahl der Argumente und ihre Typen.

  • Insbesondere die Größe eines Funktionszeigers kann unterschiedlich sein, höchstwahrscheinlich auf Harvard-Architekturen. Der Funktionszeiger (Wert) selbst trägt die genannten Informationen nicht, sondern der Typ.

    – PlasmaHH

    29. Januar 2018 um 11:11 Uhr

  • Ich unterstütze das, was @PlasmaHH gesagt hat, es gibt CPU-Architekturen, bei denen Funktionszeiger größer als a sind void*: Genauer gesagt sind Funktionszeiger auf dem PPC tatsächlich Zeigerpaare, einer zeigt auf den Code und der andere auf eine globale Referenztabelle, die von der ausführbaren/gemeinsam genutzten Bibliothek abhängt, aus der die Funktion geladen wurde. Daher ist es nicht möglich, einen Funktionszeiger darauf zu werfen void* und zurück auf PPC.

    – Cmaster – Wiedereinsetzung von Monica

    29. Januar 2018 um 12:52 Uhr

Benutzer-Avatar
Serge Ballesta

Der klassische Teil über Funktionszeiger wird bereits in der Antwort anderer besprochen:

  • Wie andere Zeiger können Zeiger auf Funktionen zu unterschiedlichen Zeiten auf verschiedene Objekte zeigen, sodass ein Vergleich sinnvoll sein kann.
  • Zeiger auf Funktionen sind etwas Besonderes und sollten nicht in anderen Zeigertypen gespeichert werden (nicht even void * und sogar in C-Sprache).
  • Der reichhaltige Teil (Funktionssignatur) wird im Funktionstyp gespeichert – der Grund für den obigen Satz.

Aber C hat einen (alten) Funktionsdeklarationsmodus. Zusätzlich zum vollständigen Prototypmodus, der den Rückgabetyp und den Typ für alle Parameter deklariert, kann C den sogenannten Parameterliste Modus, der der alte K&R-Modus ist. In diesem Modus deklariert die Deklaration nur den Rückgabetyp:

int (*fptr)();

In C deklariert es einen Zeiger auf eine Funktion, die ein zurückgibt int und willkürliche Parameter akzeptieren. Es ist einfach undefiniertes Verhalten (UB), es mit einer falschen Parameterliste zu verwenden.

Das ist also legaler C-Code:

#include <stdio.h>
#include <string.h>

int add2(int a, int b) {
    return a + b;
}
int add3(int a, int b, int c) {
    return a + b + c;
}

int(*fptr)();
int main() {
    fptr = add2;
    printf("%d\n", fptr(1, 2));
    fptr = add3;
    printf("%d\n", fptr(1, 2, 3));
    /* fprintf("%d\n", fptr(1, 2)); Would be UB */
    return 0;
}

Tun Sie nicht so, als hätte ich Ihnen dazu geraten! Es wird jetzt als veraltetes Feature angesehen und sollte vermieden werden. Ich warne nur davor. IMHO könnte es nur einige akzeptable Ausnahmefälle geben.

Stellen Sie sich vor, wie Sie eine ähnliche Funktionalität wie in implementieren würden WNDCLASS?

Es hat ein lpszClassName um Fensterklassen voneinander zu unterscheiden, aber nehmen wir an, Sie brauchten (oder hatten) keinen String zur Verfügung, um verschiedene Klassen voneinander zu unterscheiden.

Was du tun have ist die Fensterklassenprozedur lpfnWndProc (vom Typ WindowProc).

Was würdest du jetzt tun, wenn jemand anruft? RegisterClass zweimal mit dem gleichen lpfnWndProc?
Du musst erkennen Neuregistrierungen derselben Klasse irgendwie und geben einen Fehler zurück.

Das ist ein Fall, in dem es logisch ist, die Callback-Funktionen zu vergleichen.

  • Weißt du, das wäre ein gewesen Großartig Beispiel vor 15 Jahren. Aber ich glaube nicht, dass viele neue Programmierer heutzutage viel mit der Win32-API zu tun haben … alles ist darauf ausgerichtet viel Dinge auf höherem Niveau, heutzutage.

    – Jules

    29. Januar 2018 um 21:46 Uhr

  • @Jules: Ich denke, deshalb habe ich gesagt “Funktionalität ähnlich der von WNDCLASS” statt “WNDCLASS“. Um ehrlich zu sein, bin ich mir nicht sicher, ob C selbst ist in erster Linie der Inbegriff der modernen Abstraktion…

    – Benutzer541686

    29. Januar 2018 um 21:55 Uhr


Benutzer-Avatar
Lundin

1) Es gibt viele Situationen. Nehmen Sie zum Beispiel die typische Implementierung einer endlichen Zustandsmaschine:

typedef void state_func_t (void);

const state_func_t* STATE_MACHINE[] =
{
  state_init,
  state_something,
  state_something_else
};

...

for(;;)
{
  STATE_MACHINE[state]();
}

Möglicherweise müssen Sie für eine bestimmte Situation zusätzlichen Code in den Aufrufer einfügen:

if(STATE_MACHINE[state] == state_something)
{
  print_debug_stuff();
}

2) Ja, der C-Compiler sieht sie als unterschiedliche Typen. Tatsächlich haben Funktionszeiger eine strengere Typsicherheit als andere C-Typen, da sie nicht implizit in/aus konvertiert werden können void*, wie es Zeiger auf Objekttypen können. (C11 6.3.2.3/1). Sie können auch nicht explizit in/aus umgewandelt werden void* – Dies würde nicht standardmäßige Erweiterungen aufrufen.

Alles im Funktionszeiger ist wichtig, um seinen Typ zu bestimmen: der Typ des Rückgabewerts, der Typ der Parameter und die Anzahl der Parameter. Diese müssen alle übereinstimmen, oder zwei Funktionszeiger sind nicht kompatibel.

  • Weißt du, das wäre ein gewesen Großartig Beispiel vor 15 Jahren. Aber ich glaube nicht, dass viele neue Programmierer heutzutage viel mit der Win32-API zu tun haben … alles ist darauf ausgerichtet viel Dinge auf höherem Niveau, heutzutage.

    – Jules

    29. Januar 2018 um 21:46 Uhr

  • @Jules: Ich denke, deshalb habe ich gesagt “Funktionalität ähnlich der von WNDCLASS” statt “WNDCLASS“. Um ehrlich zu sein, bin ich mir nicht sicher, ob C selbst ist in erster Linie der Inbegriff der modernen Abstraktion…

    – Benutzer541686

    29. Januar 2018 um 21:55 Uhr


Funktionszeiger sind Variablen. Warum sollte irgendjemand Variablen vergleichen, wenn man bedenkt, dass die Eindeutigkeit von Variablen konzeptionell durch ihre unterschiedlichen Namen gewährleistet ist? Nun, manchmal können zwei Variablen denselben Wert haben und Sie möchten herausfinden, ob dies der Fall ist.

C betrachtet Zeiger auf Funktionen mit der gleichen Argumentliste und dem gleichen Rückgabewert als vom gleichen Typ.

1381490cookie-checkFunktionszeiger in C – Art und Verwendung

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

Privacy policy