Ist es besser, C-void-Argumente “void foo(void)” oder nicht “void foo()” zu verwenden? [duplicate]

Lesezeit: 10 Minuten

Benutzeravatar von Zifre
Zifre

Was ist besser: void foo() oder void foo(void)? Mit Void sieht es hässlich und widersprüchlich aus, aber mir wurde gesagt, dass es gut ist. Ist das wahr?

Bearbeiten: Ich weiß, dass einige alte Compiler seltsame Dinge tun, aber wenn ich nur GCC verwende, ist es void foo() OK? Werden foo(bar); dann akzeptiert werden?

void foo(void);

Das ist die richtige Art, in C “keine Parameter” zu sagen, und es funktioniert auch in C++.

Aber:

void foo();

Bedeutet in C und C++ unterschiedliche Dinge! In C bedeutet es “könnte eine beliebige Anzahl von Parametern unbekannten Typs annehmen” und in C++ bedeutet es dasselbe wie foo(void).

Listenfunktionen mit variablen Argumenten sind von Natur aus nicht typsicher und sollten nach Möglichkeit vermieden werden.

  • Beachten Sie, dass void foo(); bedeutet keine varargs-Funktion; es bedeutet nur, dass dem Compiler nicht mitgeteilt wurde, was seine Argumente sind. Eine varargs-Funktion muss (gemäß Standard) mit den Auslassungspunkten deklariert werden. Auch void foo() { … } – Funktionsdefinition statt Deklaration – ist in Ordnung (obwohl die Konsistenz nahe legt, auch hier void foo(void) { … } zu verwenden).

    – Jonathan Leffler

    20. Juni 2009 um 14:05 Uhr

  • Sie sagen “dem Compiler wurde nicht gesagt, was seine Argumente sind”, ich sage “er könnte eine beliebige Anzahl von Parametern unbekannter Typen annehmen”. Was ist der Unterschied?

    – Daniel Earwicker

    20. Juni 2009 um 15:41 Uhr

  • Er sagt, dass dem Compiler die Anzahl / Art der Argumente nicht mitgeteilt wurde. Aber Sie sagen, die Funktion könnte eine beliebige Anzahl von Parametern unbekannter Typen annehmen. Aber das ist was ... ist für (varargs-Funktion). Das klingt ein bisschen verwirrend, aber ich denke, Sie wollten sagen: “Nimm eine bestimmte Anzahl von Argumenten kalt, aber diese Anzahl ist nicht bekannt”. Es ist so: Du hast einen Topf und weißt nicht, wie viele Liter er fassen kann. Das heißt aber nicht, dass der Topf beliebig viele Liter fassen kann. Ab einer bestimmten Menge läuft es einfach über 🙂 Also z void foo(): Bei einigen Beträgen/Typen verursacht es nur ein undefiniertes Verhalten

    – Johannes Schaub – litb

    22. Juli 2009 um 10:35 Uhr

  • () wird in C99 als veraltetes Feature deklariert. Ich denke, die einzige Verwirrung hier ist entstanden, weil ich erwähnt habe, dass Vararg-Listen unmittelbar nach der Diskussion leerer Parameterlisten unsicher sind. Das lag einfach daran, dass ich mir vorstelle, dass jemand die Erklärung liest und dann denkt: “Oh, ich frage mich, was für erstaunliche Dinge ich mit dieser Funktion erreichen kann?” Dann ist das nächste, was sie herausfinden werden, die Verwendung ... Funktionen wie schreiben printf, und davon wollte ich gleich abraten. Weit davon entfernt, das zu suggerieren () ist der Weg, Varargs zu machen, ich sage, dass es am besten ist, Varargs ganz zu vermeiden.

    – Daniel Earwicker

    22. Juli 2009 um 11:52 Uhr

  • Es scheint mir, dass void foo(void); könnte leicht mit void foo(void*) verwechselt werden; , während foo() wahrscheinlich nicht mit foo(void*) verwechselt werden würde;

    – ArtOfWarfare

    8. Oktober 2012 um 20:16 Uhr

Johannes Schaub - Benutzerbild der litb
Johannes Schaub – litb

Es gibt zwei Möglichkeiten, Parameter in C anzugeben. Die eine verwendet eine Bezeichnerliste und die andere eine Parametertypliste. Die Bezeichnerliste kann weggelassen werden, die Typliste jedoch nicht. Um also zu sagen, dass eine Funktion keine Argumente in einer Funktionsdefinition akzeptiert, tun Sie dies mit einer (weggelassenen) Bezeichnerliste

void f() {
    /* do something ... */
}

Und das mit einer Parametertypliste:

void f(void) {
    /* do something ... */
}

Wenn in einer Parametertypliste der einzige Parametertyp void ist (er darf dann keinen Namen haben), bedeutet dies, dass die Funktion keine Argumente akzeptiert. Aber diese beiden Möglichkeiten, eine Funktion zu definieren, haben einen Unterschied in Bezug auf das, was sie deklarieren.

Identifikatorlisten

Die erste definiert, dass die Funktion eine bestimmte Anzahl von Argumenten akzeptiert, aber weder die Anzahl noch die benötigten Typen mitgeteilt werden – wie bei allen Funktionsdeklarationen, die Bezeichnerlisten verwenden. Der Anrufer muss also die Typen und die Anzahl vorher genau kennen. Wenn also der Aufrufer die Funktion aufruft und ihr ein Argument gibt, ist das Verhalten undefiniert. Der Stapel könnte beispielsweise beschädigt werden, weil die aufgerufene Funktion ein anderes Layout erwartet, wenn sie die Kontrolle übernimmt.

Die Verwendung von Bezeichnerlisten in Funktionsparametern ist veraltet. Es wurde in alten Zeiten verwendet und ist immer noch in vielen Produktionscodes vorhanden. Sie können aufgrund dieser Argument-Promotions ernsthafte Gefahren verursachen (wenn der beförderte Argumenttyp nicht mit dem Parametertyp der Funktionsdefinition übereinstimmt, ist das Verhalten auch undefiniert!) und sind natürlich viel weniger sicher. Verwenden Sie also immer die void Dinge für Funktionen ohne Parameter, sowohl in Nur-Deklarationen als auch in Definitionen von Funktionen.

Liste der Parametertypen

Die zweite definiert, dass die Funktion null Argumente akzeptiert und teilt dies auch mit – wie in allen Fällen, in denen die Funktion mit einer Parametertypliste deklariert wird, die als a bezeichnet wird prototype. Wenn der Aufrufer die Funktion aufruft und ihr ein Argument gibt, ist das ein Fehler und der Compiler gibt einen entsprechenden Fehler aus.

Die zweite Art, eine Funktion zu deklarieren, hat viele Vorteile. Eine davon ist natürlich, dass Menge und Art der Parameter überprüft werden. Ein weiterer Unterschied besteht darin, dass der Compiler, da er die Parametertypen kennt, implizite Konvertierungen der Argumente in den Typ der Parameter anwenden kann. Wenn keine Parametertypliste vorhanden ist, ist dies nicht möglich, und Argumente werden in heraufgestufte Typen konvertiert (das wird als Standardargumenthochstufung bezeichnet). char wird werden intzum Beispiel während float wird werden double.

Zusammengesetzter Typ für Funktionen

Übrigens, wenn eine Datei sowohl eine ausgelassene Bezeichnerliste als auch eine Parametertypliste enthält, “gewinnt” die Parametertypliste. Der Typ der Funktion am Ende enthält einen Prototyp:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

Denn beide Erklärungen sagen nichts Widersprüchliches aus. Der Zweite hatte aber noch etwas zu sagen. Das heißt, dass ein Argument akzeptiert wird. Dasselbe kann umgekehrt erfolgen

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

Die erste definiert eine Funktion unter Verwendung einer Bezeichnerliste, während die zweite dann einen Prototyp dafür bereitstellt, indem sie eine Deklaration verwendet, die eine Parametertypliste enthält.

  • Wie kann ich alle meine Funktionen im Programm ändern, die keine Accept-Parameter verwenden, um myfunction(void) mit Code-Formatierungstools wie Astyle zu leeren? Gibt es eine Option, die ich mit astyle angeben kann, um es für alle Funktionen zu ändern?

    – Benutzer7375520

    4. Februar 2017 um 19:41 Uhr

  • @user7375520 verwende gcc mit -Wstrict-prototypes

    – ljrk

    3. November 2017 um 20:52 Uhr

Benutzeravatar von Uri
Uri

void foo(void) ist besser, weil es ausdrücklich sagt: keine Parameter erlaubt.

void foo() bedeutet, dass Sie (unter einigen Compilern) Parameter senden könnten, zumindest wenn dies die Deklaration Ihrer Funktion und nicht ihre Definition ist.

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

C99-Zitate

Diese Antwort zielt darauf ab, die relevanten Teile der zu zitieren und zu erläutern C99 N1256 Standardentwurf.

Definition von declarator

Der Begriff Deklarator wird viel auftauchen, also lasst es uns verstehen.

Aus der Sprachgrammatik entnehmen wir, dass die folgenden Unterstriche Deklaratoren sind:

int f(int x, int y);
    ^^^^^^^^^^^^^^^

int f(int x, int y) { return x + y; }
    ^^^^^^^^^^^^^^^

int f();
    ^^^

int f(x, y) int x; int y; { return x + y; }
    ^^^^^^^

Deklaratoren sind sowohl Teil von Funktionsdeklarationen als auch von Definitionen.

Es gibt 2 Arten von Deklaratoren:

  • Liste der Parametertypen
  • Kennungsliste

Liste der Parametertypen

Deklarationen sehen so aus:

int f(int x, int y);

Definitionen sehen so aus:

int f(int x, int y) { return x + y; }

Sie wird Parametertypliste genannt, weil wir den Typ jedes Parameters angeben müssen.

Identifikatorliste

Definitionen sehen so aus:

int f(x, y)
    int x;
    int y;
{ return x + y; }

Deklarationen sehen so aus:

int g();

Wir können keine Funktion mit einer nicht leeren Bezeichnerliste deklarieren:

int g(x, y);

Weil 6.7.5.3 “Funktionsdeklaratoren (einschließlich Prototypen)” sagt:

3 Eine Bezeichnerliste in einem Funktionsdeklarator, die nicht Teil einer Definition dieser Funktion ist, muss leer sein.

Sie wird Identifikatorliste genannt, weil wir nur die Identifikatoren angeben x und y an f(x, y)Typen kommen danach.

Dies ist eine ältere Methode und sollte nicht mehr verwendet werden. 6.11.6 Funktionsdeklaratoren sagt:

1 Die Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Parametertyp-Deklaratoren im Prototypformat) ist ein veraltetes Feature.

und die Einführung erklärt, was ein ist veraltetes Merkmal:

Bestimmte Merkmale sind veraltet, was bedeutet, dass sie in zukünftigen Überarbeitungen dieser Internationalen Norm zurückgezogen werden können. Sie werden wegen ihrer weiten Verbreitung beibehalten, aber ihre Verwendung in neuen Implementierungen (für Implementierungsfeatures) oder neuen Programmen (für language [6.11] oder Bibliotheksfunktionen [7.26]) wird davon abgeraten

f() vs f(void) für Deklarationen

Wenn du nur schreibst:

void f();

es ist notwendigerweise eine Bezeichnerlistendeklaration, weil 6.7.5 „Erklärer“ sagt, definiert die Grammatik als:

direct-declarator:
    [...]
    direct-declarator ( parameter-type-list )
    direct-declarator ( identifier-list_opt )

daher kann nur die Version der Kennungsliste leer sein, da sie optional ist (_opt).

direct-declarator ist der einzige Grammatikknoten, der die Klammer definiert (...) Teil des Deklarators.

Wie disambiguieren und verwenden wir also die bessere Parametertypliste ohne Parameter? 6.7.5.3 Funktionsdeklaratoren (einschließlich Prototypen) sagt:

10 Der Sonderfall eines unbenannten Parameters vom Typ void als einziges Element in der Liste gibt an, dass die Funktion keine Parameter hat.

So:

void f(void);

ist die Art.

Dies ist eine explizit erlaubte magische Syntax, da wir a nicht verwenden können void Geben Sie Argument auf andere Weise ein:

void f(void v);
void f(int i, void);
void f(void, int);

Was kann passieren, wenn ich eine f()-Deklaration verwende?

Vielleicht lässt sich der Code gut kompilieren: 6.7.5.3 Funktionsdeklaratoren (einschließlich Prototypen):

14 Die leere Liste in einem Funktionsdeklarator, der nicht Teil einer Definition dieser Funktion ist, gibt an, dass keine Informationen über die Anzahl oder Typen der Parameter bereitgestellt werden.

Sie können also davonkommen:

void f();
void f(int x) {}

In anderen Fällen kann sich UB einschleichen (und wenn Sie Glück haben, sagt Ihnen der Compiler), und Sie werden es schwer haben, herauszufinden, warum:

void f();
void f(float x) {}

Siehe: Warum funktioniert eine leere Deklaration für Definitionen mit int-Argumenten, aber nicht für Float-Argumente?

f() und f(void) für Definitionen

f() {}

vs

f(void) {}

sind ähnlich, aber nicht identisch.

6.7.5.3 Funktionsdeklaratoren (einschließlich Prototypen) sagt:

14 Eine leere Liste in einem Funktionsdeklarator, der Teil einer Definition dieser Funktion ist, gibt an, dass die Funktion keine Parameter hat.

das sieht ähnlich aus wie die Beschreibung von f(void).

Aber still… Es scheint, dass:

int f() { return 0; }
int main(void) { f(1); }

entspricht undefiniertem Verhalten, während:

int f(void) { return 0; }
int main(void) { f(1); }

ist nicht konform, wie besprochen unter: Warum erlaubt gcc, dass Argumente an eine Funktion übergeben werden, die so definiert ist, dass sie keine Argumente hat?

TODO versteht genau warum. Hat damit zu tun, ein Prototyp zu sein oder nicht. Prototyp definieren.

Benutzeravatar von Maja
Maja

Neben syntaktischen Unterschieden bevorzugen viele Menschen auch die Verwendung void function(void) aus praktischen Gründen:

Wenn Sie die Suchfunktion verwenden und die Implementierung der Funktion finden möchten, können Sie nach suchen function(void)und es wird sowohl den Prototyp als auch die Implementierung zurückgeben.

Wenn Sie die weglassen voidmusst du suchen function() und findet daher auch alle Funktionsaufrufe, was das Auffinden der eigentlichen Implementierung erschwert.

Benutzeravatar von Peter Mortensen
Peter Mortensen

In C++ gibt es nein Unterschied in main() und main(void).

Aber in C, main() wird mit angerufen irgendein Anzahl Parameter.

Beispiel:

main (){
    main(10, "abc", 12.28);
    // Works fine!
    // It won't give the error. The code will compile successfully.
    // (May cause a segmentation fault when run)
}

main(void) wird angerufen werden ohne beliebige Parameter. Wenn wir versuchen, es zu übergeben, führt dies zu einem Compiler-Fehler.

Beispiel:

main (void) {
     main(10, "abc", 12.13);
     // This throws "error: too many arguments to function ‘main’ "
}

1426300cookie-checkIst es besser, C-void-Argumente “void foo(void)” oder nicht “void foo()” zu verwenden? [duplicate]

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

Privacy policy