Nachdem ich so viele ähnliche Fragen gesehen (und gestellt!) Habe
Was
int (*f)(int (*a)[5])
bedeutet in C?
und selbst wenn ich sehe , dass sie ein Programm erstellt haben , das den Leuten hilft, die C-Syntax zu verstehen, frage ich mich:
Warum wurde die Syntax von C so entworfen?
Wenn ich beispielsweise Zeiger entwerfe, würde ich "einen Zeiger auf ein 10-Elemente-Array von Zeigern" in übersetzen
int*[10]* p;
und nicht
int* (*p)[10];
was meiner Meinung nach die meisten Leute zustimmen würden, ist viel weniger einfach.
Also frage ich mich, warum die, äh, nicht intuitive Syntax? Gab es ein bestimmtes Problem, das die Syntax löst (vielleicht eine Mehrdeutigkeit?), Von dem ich nichts weiß?
cdecl
Befehl ist sehr praktisch, um komplexe C-Deklarationen zu dekodieren. Es gibt auch ein Webinterface bei cdecl.org .Antworten:
Mein Verständnis der Geschichte davon ist, dass es auf zwei Hauptpunkten basiert ...
Erstens zogen es die Sprachautoren vor, die Syntax variablenzentriert anstatt typzentriert zu gestalten. Das heißt, sie wollten, dass ein Programmierer sich die Deklaration ansieht und denkt: "Wenn ich den Ausdruck schreibe
*func(arg)
, führt das zu einemint
; wenn ich schreibe,*arg[N]
habe ich einen Gleitkomma" anstatt "func
muss ein Zeiger auf eine Funktion sein, die dies ausführt und die Rückkehr , dass “.Der C-Eintrag auf Wikipedia behauptet, dass:
... unter Berufung auf Seite 122 von K & R2, die ich leider nicht vorlegen muss, um das erweiterte Angebot für Sie zu finden.
Zweitens ist es wirklich sehr, sehr schwierig, eine Syntax für die Deklaration zu finden, die konsistent ist, wenn Sie mit beliebigen Indirektionsebenen arbeiten. Ihr Beispiel eignet sich möglicherweise gut, um den Typ auszudrücken, den Sie sich dort spontan ausgedacht haben. Skaliert es sich jedoch auf eine Funktion, die einen Zeiger auf ein Array dieser Typen nimmt und ein anderes abscheuliches Durcheinander zurückgibt? (Vielleicht schon, aber hast du nachgesehen? Kannst du es beweisen? ).
Denken Sie daran, dass ein Teil des Erfolgs von C auf der Tatsache beruht, dass Compiler für viele verschiedene Plattformen geschrieben wurden. Es wäre daher möglicherweise besser gewesen, ein gewisses Maß an Lesbarkeit zu ignorieren, um das Schreiben von Compilern zu vereinfachen.
Trotzdem bin ich kein Experte für Sprachgrammatik oder Compilerschreiben. Aber ich weiß genug, um zu wissen, dass es viel zu wissen gibt;)
quelle
Viele der Merkwürdigkeiten der C-Sprache lassen sich durch die Funktionsweise der Computer bei ihrer Entwicklung erklären. Da der Speicherplatz sehr begrenzt war, war es sehr wichtig, die Größe der Quellcodedateien selbst zu minimieren . Die Programmierpraxis in den 70er und 80er Jahren bestand darin, sicherzustellen, dass der Quellcode so wenig Zeichen wie möglich enthielt und möglichst keine übermäßigen Quellcode-Kommentare.
Das ist heute natürlich lächerlich, da der Speicherplatz auf den Festplatten so gut wie unbegrenzt ist. Aber es ist ein Teil des Grundes, warum C im Allgemeinen eine so seltsame Syntax hat.
In Bezug auf Array-Zeiger sollte Ihr zweites Beispiel lauten
int (*p)[10];
(ja, die Syntax ist sehr verwirrend). Ich würde das vielleicht als "int pointer to array of ten" lesen ... das macht etwas Sinn. Ohne die Klammer würde der Compiler es stattdessen als ein Array von zehn Zeigern interpretieren, was der Deklaration eine ganz andere Bedeutung geben würde.Da sowohl Array-Zeiger als auch Funktionszeiger in C eine ziemlich undurchsichtige Syntax haben, ist es sinnvoll, die Unheimlichkeit wegzuschreiben. Vielleicht so:
Dunkles Beispiel:
Nicht obskures, gleichwertiges Beispiel:
Wenn Sie sich mit Arrays von Funktionszeigern beschäftigen, können die Dinge noch dunkler werden. Oder die dunkelste von allen: Funktionen, die Funktionszeiger zurückgeben (mild nützlich). Wenn Sie für solche Dinge keine Typedefs verwenden, werden Sie schnell verrückt.
quelle
Es ist ziemlich einfach:
int *p
bedeutet, dass*p
es sich um ein int handelt;int a[5]
bedeutet dasa[i]
ist ein int.Bedeutet, dass
*f
es sich bei einer Funktion*a
um ein Array mit fünf Ganzzahlen handelt. Diesf
gilt auch für eine Funktion, die einen Zeiger auf ein Array mit fünf Ganzzahlen nimmt und int zurückgibt. In C ist es jedoch nicht sinnvoll, einen Zeiger auf ein Array zu übergeben.C-Deklarationen werden sehr selten so kompliziert.
Außerdem können Sie mit typedefs Folgendes klären:
quelle
typedef
alsconst
,volatile
und die Fähigkeit, Dinge innerhalb von Deklarationen zu initialisieren. Viele der lästigen Zweideutigkeiten der Deklarationssyntax (zB obint const *p, *q;
binden sollteconst
auf die Art oder die declarand) konnten nicht in der Sprache auftreten , wie ursprünglich vorgesehen. Ich wünschte, die Sprache hätte einen Doppelpunkt zwischen dem Typ und dem Deklarationszeichen eingefügt, erlaubte aber das Weglassen, wenn eingebaute "Reserviert-Wort" -Typen ohne Qualifizierer verwendet wurden. Die Bedeutung vonint: const *p,*q;
undint const *: p,*q;
wäre klar gewesen.Ich denke, Sie müssen * [] als Operatoren betrachten, die an eine Variable angehängt sind. * wird vor eine Variable geschrieben, [] danach.
Lesen wir den Typ-Ausdruck
Das innerste Element ist daher p, eine Variable
bedeutet: p ist eine Variable.
Bevor die Variable ein * enthält, wird der Operator * immer vor den Ausdruck gesetzt, auf den er sich bezieht.
bedeutet: Variable p ist ein Zeiger. Ohne das () hätte der Operator [] auf der rechten Seite eine höhere Priorität, d. H
würde analysiert werden als
Der nächste Schritt ist []: Da es kein weiteres () gibt, hat [] daher eine höhere Priorität als das äußere *
bedeutet: (Variable p ist ein Zeiger) auf ein Array. Dann haben wir den zweiten *:
bedeutet: ((Variable p ist ein Zeiger) auf ein Array) von Zeigern
Schließlich haben Sie den Operator int (einen Typnamen), der die niedrigste Priorität hat:
bedeutet: (((Variable p ist ein Zeiger) auf ein Array) von Zeigern) auf eine ganze Zahl.
Das gesamte System basiert also auf Typausdrücken mit Operatoren, und jeder Operator hat seine eigenen Vorrangregeln. Dadurch können sehr komplexe Typen definiert werden.
quelle
Es ist nicht so schwer, wenn man anfängt zu denken, und C war nie eine sehr einfache Sprache. Und ist
int*[10]* p
wirklich nicht einfacher alsint* (*p)[10]
Und welche Art von k wäre inint*[10]* p, k;
quelle
pointer to int
undint
sind nicht einmal der gleiche Typ, so sollten sie separat deklariert werden. Zeitraum. Hören Sie auf den Mann. Er hat 18k Vertreter aus einem Grund.