Gibt es einen praktischen Nutzen für eine Funktion, die nichts tut?

Lesezeit: 10 Minuten

Benutzeravatar von Epidem7c
Epidem7c

Würde es eine Verwendung für eine Funktion geben, die beim Ausführen nichts tut, dh:

void Nothing() {}

Beachten Sie, dass ich nicht über eine Funktion spreche, die eine bestimmte Zeit wartet, wie z sleep()nur etwas, das so viel Zeit in Anspruch nimmt, wie der Compiler / Interpreter ihm gibt.

  • Etwas verwandt mit dem, warum wir erfunden haben 0 zur Ergänzung u 1 zur Multiplikation. Das nichts tun Die Operation (für irgendetwas) scheint für jeden einzelnen praktischen Anwendungsfall (niedrige Abstraktionsebene) nutzlos zu sein, wird jedoch für einige Generics unerlässlich (z 1 müssten wir viele Fälle in unseren Berechnungen berücksichtigen – exponentiell mehr Fälle, wenn die Anzahl der Parameter zunimmt).

    – Elliot

    23. Mai um 14:25 Uhr


  • Es ist so nützlich wie int identity(int x) { return x; }. Manchmal benötigen Sie es nur als Standardparameter, mit dem Benutzer ihre eigene Funktion zum Anpassen eines Algorithmus bereitstellen können.

    – Goswin von Brederlow

    23. Mai um 16:14 Uhr

  • Unbedingt. Dies ist eine bequeme Möglichkeit, ein Feature zu deaktivieren, das als Zeiger auf eine Funktion implementiert ist. Wenn aktiviert, zeigt der Zeiger auf die Implementierung. Wenn deaktiviert, zeigt der Zeiger auf Nothing.

    – Jeff Holt

    24. Mai um 2:00 Uhr

  • In C++ das Überschreiben einer Basisklassenfunktion, die etwas tut, oder gleichermaßen in der Basisklasse, die einige untergeordnete Klassen möglicherweise überschreiben müssen.

    – mgh42

    24. Mai um 5:40 Uhr


  • Das erinnert mich auch an die Fragen, warum Computer eine NO-OP-Anweisung haben.

    – Barmar

    25. Mai um 14:36 ​​Uhr

Eine solche Funktion könnte als Callback-Funktion notwendig sein.

Angenommen, Sie hätten eine Funktion, die so aussieht:

void do_something(int param1, char *param2, void (*callback)(void))
{
    // do something with param1 and param2
    callback();
}

Diese Funktion erhält einen Zeiger auf eine Funktion, die sie anschließend aufruft. Wenn Sie diesen Rückruf nicht unbedingt für irgendetwas verwenden müssen, übergeben Sie eine Funktion, die nichts tut:

do_something(3, "test", Nothing);

  • Nur um diese wunderbar prägnante Antwort zu erweitern: Wir könnte bieten die gleiche Funktionalität durch Hinzufügen eines booleschen Parameters; etwas wie requires_callbackund verwenden Sie eine if-Anweisung in der Funktion … aber es wäre langsamer und einfach mühsamer!

    – Elliot

    23. Mai um 14:50 Uhr

  • @Elliott Oder noch besser, die Funktion könnte a akzeptieren NULL Zeiger für den Rückruf und überprüfen Sie diesen Wert, bevor Sie ihn aufrufen.

    – dbusch

    23. Mai um 14:51 Uhr

  • Jede Option, bei der Sie eine Art Indikator übergeben, um den Rückruf nicht zu verwenden, erfordert, dass die Person, die die Funktion geschrieben hat, die den Rückruf verwendet, an die Möglichkeit gedacht hat, dass der Benutzer manchmal nichts tun möchte, und dann speziell Code geschrieben hat, um diese Möglichkeit zu unterstützen . Das Übergeben einer Do-Nothing-Funktion funktioniert auch dann, wenn der API-Implementierer diese Verwendung nicht vorhergesehen hat (oder wenn Sie sich einfach nicht sicher sind, ob dies der Fall ist oder nicht, was die große Schwäche der Nullzeiger-Idee ist; die API-Signatur wird nicht kommunizieren ob null richtig gehandhabt wird). Es ist also etwas weniger gekoppelt.

    – Ben

    24. Mai um 3:21 Uhr

  • Das erinnert mich ein bisschen an den Text „Diese Seite wurde absichtlich leer gelassen“, den Sie manchmal auf Prüfungsunterlagen sehen, um deutlich zu machen, dass der Student nicht versehentlich ein leeres Blatt Papier bekommen hat

    – Eberesche

    24. Mai um 7:03 Uhr

  • Darf ich einen weiteren gültigen Anwendungsfall hinzufügen? Die Software meines Unternehmens verwendet stark Plugins. Jedes Plugin hat eine API. Nehmen wir an, Sie könnten zwischen dem Plugin foo/bar und foo/baz wählen. Die Plugin-Leiste kann etwas mit einer bestimmten Funktion tun, während das Plugin-Baz möglicherweise nichts mit dieser Funktion tut, aber beide Plugins müssen sie als Teil der Plugin-API implementieren. Intern verwendet es Callback-Funktionen.

    – mgey

    24. Mai um 16:08 Uhr


Wenn ich Tabellen erstellt habe, die Funktionszeiger enthalten, verwende ich leere Funktionen.

Zum Beispiel:

typedef int(*EventHandler_Proc_t)(int a, int b); // A function-pointer to be called to handle an event
struct 
{
   Event_t             event_id;
   EventHandler_Proc_t proc;
}  EventTable[] = {    // An array of Events, and Functions to be called when the event occurs
    {  EventInitialize, InitializeFunction },
    {  EventIncrement,  IncrementFunction  },
    {  EventNOP,        NothingFunction    },  // Empty function is used here.
};

In dieser Beispieltabelle I könnte stellen NULL anstelle der NothingFunctionund überprüfen Sie, ob die .proc ist NULL bevor Sie es anrufen. Aber ich denke, es macht den Code einfacher, eine Do-Nothing-Funktion in die Tabelle aufzunehmen.

  • Der Code ist nicht nur sauberer, wenn es keine NULL-Prüfungen gibt, es gibt auch einen kleinen Leistungsvorteil. Für die überwiegende Mehrheit der Fälle ist dies unbedeutend, aber sagen Sie, wenn Sie eine Interrupt-Handler-Zustandsmaschine schreiben, könnte die Strafe des if-Tests erheblich sein. Und es gibt deterministisches und einheitliches Ausführungsverhalten, das im Allgemeinen gute Eigenschaften sind und für einen gut funktionierenden Interrupt-Handler von entscheidender Bedeutung sein können. Wenn Sie also A oder B wählen können, wenn es im allgemeinen Fall einen unbedeutenden Unterschied gibt, aber in einigen (seltenen) Fällen B viel besser ist als A, dann sollte B Ihr Goto-Ansatz sein.

    – hlovdal

    25. Mai um 16:50 Uhr

Joshuas Benutzeravatar
Josua

Ja. Viele Dinge möchten eine Funktion erhalten, um über bestimmte Ereignisse zu informieren (Rückrufe). Eine Funktion, die nichts tut, ist ein guter Weg, um zu sagen: “Das ist mir egal.”

Mir sind keine Beispiele in der Standardbibliothek bekannt, aber viele Bibliotheken, die darauf aufbauen, haben Funktionszeiger für Ereignisse.

Beispielsweise definiert glib einen Callback „GLib.LogFunc(log_domain, log_level, message, *user_data)“ für die Bereitstellung des Loggers. Eine leere Funktion wäre der Rückruf, den Sie bereitstellen, wenn die Protokollierung deaktiviert ist.

  • Ein Beispiel aus der Standardbibliothek wäre a signal Handler.

    – Jakow Galka

    23. Mai um 14:11 Uhr

  • @YakovGalka: SIG_IGN bitte, und das ist die POSIX-Bibliothek, nicht die Standardbibliothek.

    – Josua

    23. Mai um 14:12 Uhr


  • @Joshua: Die Standardbibliothek hat signal(). Ebenfalls, Constraint-Handler wenn Sie Anhang K verwenden.

    – DevSolar

    23. Mai um 14:16 Uhr


  • @DevSolar: Das stellt sich als eine der Stellen heraus, an denen es heißt, dass es etwas hat und wirklich nicht. Haben Sie jemals versucht, es unter DOS zu verwenden? Es funktioniert nicht. Das Nehmen einer Fließkommaausnahme löst SIGFPE nicht aus. Ich versuchte es. Da ist ein SIGTERM. Es ist sowohl unter DOS als auch unter Win32 völlig nutzlos.

    – Josua

    23. Mai um 14:20 Uhr

  • @Joshua: Es ist eine Frage der Implementierung. Du kann damit es funktioniert (als Bibliotheks-Implementierer), aber so wie es in der Signalverarbeitungsabteilung ist, stört es die meisten (einschließlich mir) nicht wirklich, da es von vornherein sehr wenig Gewinn bringt. — Wie dem auch sei, was ich hier hervorheben wollte, ist, dass Yakovs Kommentar nicht falsch war.

    – DevSolar

    23. Mai um 14:35 Uhr

Benutzeravatar von Daniel R. Collins
Daniel R. Collins

Ein Anwendungsfall wäre ein möglicherweise temporäres Stummel mitten in der Entwicklung eines Programms funktionieren.

Wenn ich etwas von oben nach unten entwickle, ist es üblich, dass ich einige Funktionsprototypen entwerfe, die Hauptfunktion schreibe und an diesem Punkt den Compiler ausführen möchte, um zu sehen, ob ich bisher Syntaxfehler habe. Um diese Kompilierung zu ermöglichen, muss ich die fraglichen Funktionen implementieren, was ich tun werde, indem ich zunächst nur leere “Stubs” erstelle, die nichts tun. Sobald ich diesen Kompilierungstest bestanden habe, kann ich fortfahren und die Funktionen einzeln ausarbeiten.

Das Gaddis-Lehrbuch Beginnend mit C++: Von Kontrollstrukturen bis hin zu Objektenaus der ich lehre, beschreibt sie so (Abschn. 6.16):

Ein Stub ist eine Dummy-Funktion, die anstelle der eigentlichen Funktion, die sie darstellt, aufgerufen wird. Es zeigt normalerweise eine Testnachricht an, die bestätigt, dass es aufgerufen wurde, und nicht mehr.

Eine Funktion, die Argumente nimmt und nichts mit ihnen macht, kann als Paar mit einer Funktion verwendet werden, die etwas Nützliches tut, so dass die Argumente auch dann noch ausgewertet werden, wenn die No-Op-Funktion verwendet wird. Dies kann in Protokollierungsszenarien nützlich sein, in denen die Argumente dennoch ausgewertet werden müssen, um zu überprüfen, ob die Ausdrücke zulässig sind und um sicherzustellen, dass wichtige Nebeneffekte auftreten, aber die Protokollierung selbst nicht erforderlich ist. Die no-op-Funktion kann vom Präprozessor ausgewählt werden, wenn die Protokollierungsebene zur Kompilierzeit auf eine Ebene eingestellt wurde, die keine Ausgabe für diese bestimmte Protokollanweisung wünscht.

Soweit ich mich erinnere, gab es zwei leere Funktionen Lions-Kommentar zu UNIX, 6. Ausgabe, mit Quellcodeund die Einführung in die Neuauflage Anfang dieses Jahrhunderts namens Ritchie, Kernighan und Thompson darauf.

Die Funktion, die ihr Argument verschlingt und nichts zurückgibt, ist eigentlich allgegenwärtig in C, aber nicht explizit ausgeschrieben, weil sie implizit in fast jeder Zeile aufgerufen wird. Die häufigste Verwendung dieser leeren Funktion im traditionellen C war das unsichtbare Verwerfen des Werts einer Anweisung. Aber seit C89 kann dies explizit als geschrieben werden (void). Das lint Werkzeug, das verwendet wurde, um sich zu beschweren, wenn ein Funktionsrückgabewert ignoriert wurde, ohne ihn explizit an diese eingebaute Funktion zu übergeben, die nichts zurückgibt. Die Motivation dahinter war zu versuchen, Programmierer daran zu hindern, Fehlerbedingungen stillschweigend zu ignorieren, und Sie werden immer noch auf einige alte Programme stoßen, die den Codierungsstil verwenden, (void)printf("hello, world!\n");.

Eine solche Funktion könnte verwendet werden für:

  • Rückrufe (die die anderen Antworten erwähnt haben)
  • Ein Argument für Funktionen höherer Ordnung
  • Benchmarking eines Frameworks ohne Overhead für die Durchführung von No-Op
  • Einen eindeutigen Wert des richtigen Typs haben, mit dem andere Funktionszeiger verglichen werden können. (Insbesondere in einer Sprache wie C, wo alle Funktionszeiger konvertierbar und miteinander vergleichbar sind, aber die Konvertierung zwischen Funktionszeigern und anderen Arten von Zeigern nicht portierbar ist.)
  • Das einzige Element eines Singleton-Werttyps in einer funktionalen Sprache
  • Wenn ein Argument übergeben wird, das streng ausgewertet wird, könnte dies eine Möglichkeit sein, einen Rückgabewert zu verwerfen, aber Nebeneffekte auszuführen und auf Ausnahmen zu testen
  • Ein Dummy-Platzhalter
  • Beweis bestimmter Theoreme im typisierten Lambda-Kalkül

Benutzeravatar von Trevortni
Trevortni

Eine andere vorübergehende Verwendung für eine Do-Nothing-Funktion könnte darin bestehen, dass eine Zeile vorhanden ist, um einen Haltepunkt zu setzen, beispielsweise wenn Sie die Laufzeitwerte überprüfen müssen, die an eine neu erstellte Funktion übergeben werden, damit Sie bessere Entscheidungen darüber treffen können, was Der Code, den Sie dort eingeben, muss zugreifen. Ich persönlich verwende gerne Selbstzuweisungen, dh i = i wenn ich diese Art von Haltepunkt brauche, aber eine No-Op-Funktion würde vermutlich genauso gut funktionieren.

void MyBrandNewSpiffyFunction(TypeImNotFamiliarWith whoKnowsWhatThisVariableHas)
{
  DoNothing(); // Yay! Now I can put in a breakpoint so I can see what data I'm receiving!
  int i = 0;
  i = i;    // Another way to do nothing so I can set a breakpoint
}

  • Ihr Debugger sollte es Ihnen ermöglichen, an beliebigen Stellen einen Haltepunkt zu setzen. Unter der Motorhaube würde es so etwas wie ein emittieren int 3 Anweisung (in der x86-Welt). Dies ist ein Workaround für ein unzureichendes Tool, kein Grund für ein erstklassiges Sprachfeature. Und selbst wenn Sie ein unangemessenes Werkzeug hätten, gibt es praktisch unbegrenzte No-Ops, die Sie einfügen könnten, die gültiges C wären, selbst wenn C keine leeren Funktionen zulässt.

    – Cody Grey

    24. Mai um 23:27 Uhr


  • Ich stimme zu, dass der Debugger Ihnen erlauben sollte, einen Haltepunkt an beliebigen Stellen zu setzen. Und doch sind wir hier. Wie auch immer, wer hat was von erstklassigen Sprachfeatures gesagt? Das OP fragte nach Gründen, warum No-Op-Funktionen nützlich sein könnten, und dies trifft sicherlich zu.

    – Trevortni

    24. Mai um 23:41 Uhr

  • Sehr schön, ich habe das eigentlich nur benutzt, um etwas zu debuggen

    – unendlichnull

    25. Mai um 12:57 Uhr

  • Dies ist besonders nützlich in Umgebungen, die nur wenige Hardware-Haltepunkte unterstützen und Sie den Leistungsaufwand von Software-Haltepunkten nicht möchten. Ein Haltepunkt innerhalb der Funktion wird unabhängig davon ausgelöst, von wo aus die Funktion aufgerufen wird. Sehr praktisch, wenn Software-Haltepunkte das Timing so stark verändern, dass sich Fehler nicht reproduzieren.

    – bta

    25. Mai um 19:01 Uhr

  • @CodyGray Ich frage mich, ob diese “beliebigen Orte” verschachtelte Funktionsaufrufe wie die enthalten daz() anrufen foo(bar(), baz(daz())); (aber nicht an daz Funktion, die von anderen Stellen im Programm aufgerufen werden kann). Natürlich könnte man zur Disassemblierung wechseln, um dort einen Haltepunkt zu setzen, aber gibt es C-Level-Debugger, die dazu in der Lage sind?

    – Ruslan

    25. Mai um 19:12 Uhr

1418170cookie-checkGibt es einen praktischen Nutzen für eine Funktion, die nichts tut?

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

Privacy policy