Alternative (K & R) C-Syntax für Funktionsdeklaration im Vergleich zu Prototypen

76

Was ist an dieser CSyntax nützlich - unter Verwendung von Funktionsdeklarationen im K & R-Stil?

int func (p, p2)
    void* p;
    int  p2;
{
    return 0;
}

Ich konnte dies in Visual Studios 2010beta schreiben

// yes, the arguments are flipped
void f()
{
    void* v = 0;
    func(5, v);
}

Ich verstehe nicht. Was ist der Sinn dieser Syntax? Ich kann schreiben:

int func (p, p2)
    int  p2;
{
    return 0;
}
// and write
int func (p, p2)
{
    return 0;
}

Das einzige, was es zu spezifizieren scheint, ist die Anzahl der verwendeten Parameter und der Rückgabetyp. Ich denke, Parameter ohne Typen sind irgendwie cool, aber warum sollte man es zulassen und int paranNamenach dem Funktionsdeklarator? Es ist komisch.

Ist das auch noch Standard C?

Deduplikator
quelle
Es ist ein älterer Stil von Funktionsdeklarationen. Bitte sehen Sie diesen Link. /programming/22500/what-are-the-major-differences-between-ansi-c-and-kr-c Linktext
Leonidas Tsampros

Antworten:

153

Die Frage, die Sie stellen, besteht eigentlich aus zwei Fragen, nicht aus einer. Die meisten Antworten haben bisher versucht, das Ganze mit einer generischen pauschalen Antwort "Dies ist K & R-Stil" abzudecken, während tatsächlich nur ein kleiner Teil davon etwas mit dem sogenannten K & R-Stil zu tun hat (es sei denn, Sie sehen die gesamte C-Sprache als "K & R-Stil" auf die eine oder andere Weise :)

Der erste Teil ist die seltsame Syntax in Funktion Definition

int func(p, p2)
void *p;
int  p2; /* <- optional in C89/90, but not in C99 */
{
  return 0;
}

Dies ist eigentlich eine Funktionsdefinition im K & R-Stil. Andere Antworten haben dies ziemlich gut abgedeckt. Und da ist eigentlich nicht viel dran. Die Syntax ist veraltet, wird aber auch in C99 noch vollständig unterstützt (mit Ausnahme der Regel "no implicit int" in C99, was bedeutet, dass Sie in C99 die Deklaration von nicht auslassen könnenp2 ).

Der zweite Teil hat wenig mit K & R-Stil zu tun. Ich beziehe mich auf die Tatsache, dass die Funktion mit "getauschten" Argumenten aufgerufen werden kann, dh bei einem solchen Aufruf findet keine Parametertypprüfung statt. Dies hat sehr wenig mit der Definition im K & R-Stil an sich zu tun, aber es hat alles damit zu tun, dass Ihre Funktion keinen Prototyp hat. Sie sehen in C, wenn Sie eine Funktion wie diese deklarieren

int foo();

Es deklariert tatsächlich eine Funktion foo, die eine nicht spezifizierte Anzahl von Parametern unbekannten Typs akzeptiert . Sie können es als nennen

foo(2, 3);

und wie

j = foo(p, -3, "hello world");

ans so weiter (Sie bekommen die Idee);

Nur der Aufruf mit den richtigen Argumenten "funktioniert" (was bedeutet, dass die anderen undefiniertes Verhalten erzeugen), aber es liegt ganz bei Ihnen, die Richtigkeit sicherzustellen. Der Compiler muss die falschen nicht diagnostizieren, auch wenn er die richtigen Parametertypen und deren Gesamtzahl auf magische Weise kennt.

Tatsächlich ist dieses Verhalten ein Merkmal der C-Sprache. Eine gefährliche, aber dennoch eine Besonderheit. Es ermöglicht Ihnen, so etwas zu tun

void foo(int i);
void bar(char *a, double b);
void baz(void);

int main()
{
  void (*fn[])() = { foo, bar, baz };
  fn[0](5);
  fn[1]("abc", 1.0);
  fn[2]();
}

dh verschiedene Funktionstypen in einem "polymorphen" Array ohne Typecasts mischen (verschiedene Funktionstypen können hier jedoch nicht verwendet werden). Wiederum sind die inhärenten Gefahren dieser Technik ziemlich offensichtlich (ich erinnere mich nicht, sie jemals benutzt zu haben, aber ich kann mir vorstellen, wo sie nützlich sein kann), aber das ist schließlich C.

Schließlich das Bit, das den zweiten Teil der Antwort mit dem ersten verbindet. Wenn Sie eine Funktionsdefinition im K & R-Stil erstellen, wird kein Prototyp für die Funktion eingeführt. In Bezug auf den Funktionstyp funcdeklariert Ihre Definition funcals

int func();

dh weder die Typen noch die Gesamtzahl der Parameter werden deklariert. In Ihrem ursprünglichen Beitrag sagen Sie "... es scheint anzugeben, wie viele Parameter es verwendet ...". Formal gesehen nicht! Nach Ihrer Zwei-Parameter- funcDefinition im K & R-Stil können Sie immer noch funcals aufrufen

func(1, 2, 3, 4, "Hi!");

und es wird keine Einschränkungsverletzung darin geben. (Normalerweise gibt Ihnen ein Qualitätscompiler eine Warnung).

Eine manchmal übersehene Tatsache ist auch, dass

int f()
{
  return 0;
}

ist auch eine Funktionsdefinition im K & R-Stil, die keinen Prototyp einführt. Um es "modern" zu machen, müssten Sie ein explizites voidin die Parameterliste einfügen

int f(void)
{
  return 0;
}

Entgegen einer weit verbreiteten Meinung werden in C99 sowohl Funktionsdefinitionen im K & R-Stil als auch nicht prototypisierte Funktionsdeklarationen vollständig unterstützt. Ersteres ist seit C89 / 90 veraltet, wenn ich mich richtig erinnere. Für C99 muss die Funktion vor der ersten Verwendung deklariert werden, die Deklaration muss jedoch kein Prototyp sein. Die Verwirrung rührt anscheinend von der populären terminologischen Verwechslung her: Viele Leute nennen jede Funktionsdeklaration "einen Prototyp", während "Funktionsdeklaration" nicht dasselbe ist wie "Prototyp".

Ameise
quelle
1
Aber was ist nun der Unterschied zwischen einer Varargs-Funktion und einer nicht prototypisierten?
Sebastian
1
@ Sebastian Godelet Varargs sind unnötig streng. Das Aufrufen einer vararg-Funktion mit einem int und das Poppen als Byte ist beispielsweise ungültig. Definiert also eine Funktion mit nur vararg-Parametern.
Yyny
@ Sebastian Vararg-Funktionen können tatsächlich mit einer unterschiedlichen Anzahl von Parametern korrekt aufgerufen werden, z printf. Funktionen ohne Prototypen können nur mit einer bestimmten festgelegten Anzahl von Argumenten korrekt aufgerufen werden, aber der Compiler weiß nicht, welches und kann nicht überprüfen (also müssen Sie das tun).
Nicht-Benutzer
Ich denke, es ist Zeit, sich zu ändern supported even in C99. C99 to C11. :-).
sjsam
Ich erinnere mich, als ich mit C auf der Amstrad PCW anfing, war ich verwirrt, weil der Compiler diese alte Funktionsdeklarationssyntax verwendete, aber das Tutorial-Buch, das ich hatte, die neuere Syntax verwendete. Das waren die Tage ... vor 25 Jahren ...!
Andrew Truckle
18

Dies ist eine ziemlich alte K & R C-Syntax (vor ANSI / ISO C). Heutzutage sollten Sie es nicht mehr verwenden (da Sie bereits den großen Nachteil bemerkt haben: Der Compiler überprüft die Argumenttypen nicht für Sie). Der Argumenttyp ist intin Ihrem Beispiel standardmäßig .

Zu der Zeit, als diese Syntax verwendet wurde, fand man manchmal Funktionen wie

foo(p, q) 
{
    return q + p;
}

das war eigentlich eine gültige Definition, wie die Typen für foo, pund qstandardmäßig int.

Dolch
quelle
5

Dies ist einfach eine alte Syntax, die vor der "ANSI C" -Syntax liegt, mit der Sie vielleicht besser vertraut sind. Es heißt normalerweise " K & R C ".

Compiler unterstützen es, vollständig zu sein und natürlich mit alten Codebasen umgehen zu können.

entspannen
quelle
2

Das ist ein Relikt aus der Zeit, als C keine Prototypen für Funktionen hatte. Damals wurde angenommen, dass (ich denke) Funktionen zurückkehren intund alle ihre Argumente angenommen werden int. Die Funktionsparameter wurden nicht überprüft.

Sie sind viel besser dran, Funktionsprototypen in der aktuellen C-Sprache zu verwenden.
Und Sie müssen sie in C99 verwenden (C89 akzeptiert immer noch die alte Syntax).

Für C99 müssen Funktionen deklariert werden (möglicherweise ohne Prototyp). Wenn Sie eine neue Funktion von Grund auf neu schreiben, müssen Sie eine Deklaration bereitstellen. Machen Sie sie auch zu einem Prototyp: Sie verlieren nichts und erhalten zusätzliche Überprüfungen durch den Compiler.

pmg
quelle
4
Falsch. In C99 müssen Funktionen explizit deklariert werden, bevor sie aufgerufen werden. Trotzdem erfordert C99 keine prorotypisierte Erklärung. C99 unterstützt die K & R-Syntax mit einer Änderung vollständig: Die implizite intRegel wurde entfernt.
Am
1

Dies ist die ursprüngliche K & R-Syntax, bevor C 1989 standardisiert wurde. C89 führte Funktionsprototypen ein, die aus C ++ entlehnt wurden, und veraltete die K & R-Syntax. Es gibt keinen Grund, es in neuem Code zu verwenden (und viele Gründe, dies nicht zu tun).

Robert Gamble
quelle