it-swarm.com.de

Warum steht der Stern vor dem Variablennamen und nicht nach dem Typ?

Warum benennen die meisten C-Programmierer Variablen so:

int *myVariable;

eher als wie folgt:

int* myVariable;

Beides ist gültig. Es scheint mir, dass der Stern ein Teil des Typs ist, nicht ein Teil des Variablennamens. Kann jemand diese Logik erklären?

170
WBlasko

Sie sind genau gleichwertig. In

int *myVariable, myVariable2;

Es scheint offensichtlich, dass myVariable den Typ int * hat, während myVariable2 den Typ int hat. Im

int* myVariable, myVariable2;

es mag offensichtlich erscheinen, dass beide vom Typ int * sind, aber das ist nicht korrekt als myVariable2 hat den Typ int.

Daher ist der erste Programmierstil intuitiver.

227
luiscubal

Wenn Sie es anders sehen, *myVariable ist vom Typ int, was einen Sinn ergibt.

111
biozinc

Weil das * in dieser Zeile enger an die Variable als an den Typ gebunden ist:

int* varA, varB; // This is misleading

Wie @Lundin weiter unten ausführt, fügt const noch mehr Feinheiten hinzu, über die man nachdenken muss. Sie können dies vollständig umgehen, indem Sie eine Variable pro Zeile deklarieren, was niemals mehrdeutig ist:

int* varA;
int varB;

Die Balance zwischen klarem Code und prägnantem Code ist schwer zu finden - ein Dutzend redundanter Zeilen von int a; ist auch nicht gut. Trotzdem verwende ich standardmäßig eine Deklaration pro Zeile und mache mir später Gedanken über das Kombinieren von Code.

39
ojrac

Bisher hat hier noch niemand erwähnt, dass dieser Stern tatsächlich der " Dereferenzierungsoperator " in C ist.

*a = 10;

Die obige Zeile bedeutet nicht, dass ich a10 Zuweisen möchte, sondern dass ich 10 Jedem Speicherort zuweisen möchte, auf den a zeigt. Und ich habe noch nie jemanden schreiben gesehen

* a = 10;

hast du? Der Dereferenzierungsoperator wird also so gut wie immer ohne Leerzeichen geschrieben. Dies ist wahrscheinlich, um es von einer Multiplikation zu unterscheiden, die über mehrere Zeilen verteilt ist:

x = a * b * c * d
  * e * f * g;

Hier wäre *e Irreführend, nicht wahr?

Okay, was bedeutet nun eigentlich die folgende Zeile:

int *a;

Die meisten Leute würden sagen:

Dies bedeutet, dass a ein Zeiger auf einen int -Wert ist.

Dies ist technisch korrekt, die meisten Leute sehen/lesen es gerne so und das ist die Art und Weise, wie moderne C-Standards es definieren würden (beachten Sie, dass Sprache C selbst vor allen ANSI- und ISO-Standards liegt). Aber es ist nicht die einzige Möglichkeit, es zu betrachten. Sie können diese Zeile auch folgendermaßen lesen:

Der dereferenzierte Wert von a ist vom Typ int.

Tatsächlich kann der Stern in dieser Deklaration auch als Dereferenzierungsoperator angesehen werden, was auch seine Platzierung erklärt. Und dass a ein Zeiger ist, ist überhaupt nicht wirklich deklariert. Dies impliziert, dass das einzige, was Sie tatsächlich dereferenzieren können, ein Zeiger ist.

Der C-Standard definiert nur zwei Bedeutungen für den Operator *:

  • indirektionsoperator
  • multiplikationsoperator

Und Indirektion ist nur eine einzige Bedeutung, es gibt keine zusätzliche Bedeutung für die Deklaration eines Zeigers, es gibt nur Indirektion, was die Dereferenzierungsoperation bewirkt, sie führt einen indirekten Zugriff aus, also auch innerhalb einer Anweisung wie int *a; ist ein indirektes Zugriff (* bedeutet indirekter Zugriff) und daher ist die zweite obige Anweisung dem Standard viel näher als die erste.

30
Mecki

Ich gehe hier auf einen Ast und sage, dass es eine klare Antwort auf diese Frage gibt , sowohl für Variablendeklarationen als auch für Parameter und Return types, dh das Sternchen sollte neben dem Namen stehen: int *myVariable;. Sehen Sie sich an, wie Sie andere Symboltypen in C deklarieren, um zu verstehen, warum:

int my_function(int arg); für eine Funktion;

float my_array[3] Für ein Array.

Das allgemeine Muster, das als Deklaration folgt auf Verwendung bezeichnet wird, besteht darin, dass der Typ eines Symbols in das Teil vor dem Namen und die Teile um der Name und diese Teile um den Namen imitieren die Syntax, die Sie verwenden würden, um einen Wert des Typs auf der linken Seite zu erhalten:

int a_return_value = my_function(729);

float an_element = my_array[2];

und: int copy_of_value = *myVariable;.

C++ wirft einen Schraubenschlüssel in die Arbeit mit Referenzen, da die Syntax an der Stelle, an der Sie Referenzen verwenden, mit der von Werttypen identisch ist. Sie könnten also argumentieren, dass C++ einen anderen Ansatz als C verfolgt. Andererseits behält C++ dieselbe Syntax bei Verhalten von C im Fall von Zeigern, so dass Referenzen in dieser Hinsicht wirklich als ungerade gelten.

17
Injektilo

Das ist nur eine Frage der Präferenz.

Wenn Sie den Code lesen, ist die Unterscheidung zwischen Variablen und Zeigern im zweiten Fall einfacher, aber es kann zu Verwirrung führen, wenn Sie sowohl Variablen als auch Zeiger eines gemeinsamen Typs in eine einzige Zeile setzen (was häufig durch Projektrichtlinien verhindert wird, weil die Lesbarkeit abnimmt).

Ich bevorzuge es, Zeiger mit dem entsprechenden Zeichen neben dem Typnamen zu deklarieren, z.

int* pMyPointer;
11
macbirdie

Ein großer Guru sagte einmal: "Lies es nach der Art des Compilers, das musst du."

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

Zugegeben, dies war das Thema der Const-Platzierung, aber hier gilt die gleiche Regel.

Der Compiler liest es wie folgt:

int (*a);

nicht so wie:

(int*) a;

Wenn Sie es sich zur Gewohnheit machen, den Stern neben die Variable zu setzen, werden Ihre Deklarationen leichter lesbar. Es vermeidet auch Schandflecken wie:

int* a[10];

- Bearbeiten -

Um genau zu erklären, was ich meine, wenn ich sage, dass es als int (*a) analysiert wird, bedeutet dies, dass * Enger an a gebunden ist als an int. in der Art und Weise, wie der Ausdruck 4 + 3 * 73 aufgrund der höheren Priorität von 7 enger an 4 bindet als an *.

Mit Entschuldigungen für die ASCII-Kunst, eine Zusammenfassung der A.S.T. Das Parsen von int *a sieht ungefähr so ​​aus:

      Declaration
      /         \
     /           \
Declaration-      Init-
Secifiers       Declarator-
    |             List
    |               |
    |              ...
  "int"             |
                Declarator
                /       \
               /        ...
           Pointer        \
              |        Identifier
              |            |
             "*"           |
                          "a"

Wie deutlich zu sehen ist, bindet * Enger an a, da ihr gemeinsamer Vorfahr Declarator ist, während Sie den gesamten Baum hinauf zu Declaration gehen müssen. ], um einen gemeinsamen Vorfahren zu finden, der die int enthält.

7
dgnuff

In K & R platzieren sie den Zeigeroperator neben dem Variablennamen und nicht neben dem Typ. Persönlich halte ich dieses Buch für die ultimative C-Autorität/Bibel. Ich komme ebenfalls aus Objective-C und folge der Namenskonvention *.

6
The_Androctonus

Weil es sinnvoller ist, wenn Sie Erklärungen haben wie:

int *a, *b;
3
Greg Rogers

Um mehrere Zeiger in einer Zeile zu deklarieren, bevorzuge ich int* a, * b;, das intuitiver "a" als Zeiger auf eine Ganzzahl deklariert und Stile nicht mischt, wenn es ebenfalls "b" deklariert. Wie jemand sagte, würde ich sowieso nicht zwei verschiedene Typen in derselben Anweisung deklarieren.

2
heyitsluke

Ich habe bereits eine ähnliche Frage in CP beantwortet, und da dies von niemandem erwähnt wurde, muss ich auch hier darauf hinweisen, dass C eine Sprache mit freiem Format ist , Welchen Stil Sie auch wählen, es ist in Ordnung, während der Parser jeden Token unterscheiden kann. Diese Besonderheit von C führte zu einer ganz besonderen Art von Wettbewerb namens C-Verschleierungswettbewerb .

1
Frankie_C

Leute, die int* x; Bevorzugen, versuchen, ihren Code in eine fiktive Welt zu zwingen, in der sich der Typ links und der Bezeichner (Name) rechts befindet.

Ich sage "fiktiv", weil:

In C und C++ wird der deklarierte Bezeichner im Allgemeinen von den Typinformationen umgeben.

Das mag verrückt klingen, aber Sie wissen, dass es wahr ist. Hier sind einige Beispiele:

  • int main(int argc, char *argv[]) bedeutet "main ist eine Funktion, die ein int und ein Array von Zeigern auf char nimmt und ein int zurückgibt." Mit anderen Worten, die meisten der Typinformationen befinden sich rechts. Einige Leute denken, dass Funktionsdeklarationen nicht zählen, weil sie irgendwie "speziell" sind. OK, lass uns eine Variable ausprobieren.

  • void (*fn)(int) bedeutet, dass fn ein Zeiger auf eine Funktion ist, die ein int annimmt und nichts zurückgibt.

  • int a[10] Deklariert 'a' als Array von 10 ints.

  • pixel bitmap[height][width].

  • Klar, ich habe Beispiele ausgewählt, die eine Menge Typinformationen auf der rechten Seite haben, um meinen Standpunkt zu verdeutlichen. Es gibt viele Deklarationen, in denen die meisten - wenn nicht alle - des Typs links stehen, wie zum Beispiel struct { int x; int y; } center.

Diese Deklarationssyntax entstand aus dem Wunsch von K & R, dass Deklarationen die Verwendung widerspiegeln. Das Lesen einfacher Deklarationen ist intuitiv und das Lesen komplexerer Deklarationen kann durch Erlernen der Rechts-Links-Rechts-Regel (manchmal auch als Spiralregel oder nur als Rechts-Links-Regel bezeichnet) beherrscht werden.

C ist so einfach, dass viele C-Programmierer diesen Stil annehmen und einfache Deklarationen als int *p Schreiben.

In C++ wurde die Syntax etwas komplexer (mit Klassen, Verweisen, Vorlagen, Aufzählungsklassen), und als Reaktion auf diese Komplexität werden Sie mehr Mühe haben, den Typ in vielen Deklarationen vom Bezeichner zu trennen. Mit anderen Worten, Sie sehen möglicherweise mehr Deklarationen im int* p - Stil, wenn Sie eine große Menge C++ - Code auschecken.

In beiden Sprachen steht can immer der Typ auf der linken Seite von variable Deklarationen, indem (1) niemals mehrere Variablen in derselben Anweisung deklariert und (2) verwendet werden von typedefs (oder Alias-Deklarationen, die ironischerweise die Alias-Bezeichner links von Typen setzen). Beispielsweise:

typedef int array_of_10_ints[10];
array_of_10_ints a;
1
Adrian McCarthy