Platzierung des Sternchens in Zeigerdeklarationen

90

Ich habe kürzlich beschlossen, dass ich nur endlich C / C ++ lernen muss, und es gibt eine Sache, die ich über Zeiger oder genauer ihre Definition nicht wirklich verstehe.

Wie wäre es mit diesen Beispielen:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

Nach meinem Verständnis machen die ersten drei Fälle alle dasselbe: Test ist kein int, sondern ein Zeiger auf einen.

Die zweite Reihe von Beispielen ist etwas kniffliger. In Fall 4 sind sowohl test als auch test2 Zeiger auf ein int, während in Fall 5 nur test ein Zeiger ist, während test2 ein "reales" int ist. Was ist mit Fall 6? Gleich wie Fall 5?

Michael Stum
quelle
9
In C / C ++ ändern Leerzeichen ihre Bedeutung nicht.
Sulthan
18
7. int*test;?
Jin Kwon
3
+1, weil ich nur daran gedacht hatte, nach 1 - 3 zu fragen. Das Lesen dieser Frage brachte mir etwas über 4 - 6 bei, an das ich nie gedacht hatte.
Vastlysuperiorman
@ Sulthan Das stimmt 99% der Zeit, aber nicht immer. Auf der Oberseite meines Kopfes befand sich der Typ des Vorlagentyps für den Platzbedarf des Vorlagentyps (vor C ++ 11). In Foo<Bar<char>>der >>musste geschrieben werden > >, um nicht als Rechtsverschiebung behandelt zu werden.
AnorZaken
3
@AnorZaken Du hast recht, das ist ein ziemlich alter Kommentar. Es gibt mehrere Situationen, in denen die Bedeutung eines Leerzeichens geändert wird. Beispielsweise kann der Inkrementierungsoperator ++nicht durch ein Leerzeichen geteilt werden, Bezeichner können nicht durch ein Leerzeichen geteilt werden (und das Ergebnis kann für den Compiler weiterhin zulässig sein, jedoch mit undefiniertem Laufzeitverhalten). Die genauen Situationen sind angesichts des Syntaxproblems in C / C ++ sehr schwer zu definieren.
Sulthan

Antworten:

128

4, 5 und 6 sind dasselbe, nur Test ist ein Zeiger. Wenn Sie zwei Zeiger möchten, sollten Sie Folgendes verwenden:

int *test, *test2;

Oder noch besser (um alles klar zu machen):

int* test;
int* test2;
Milan Babuškov
quelle
3
Fall 4 ist also tatsächlich eine Todesfalle? Gibt es eine Spezifikation oder weitere Lektüre, die erklärt, warum int * test, test2 nur die erste Variable zum Zeiger macht?
Michael Stum
8
@ Michael Stum Es ist C ++. Glaubst du wirklich, dass es eine logische Erklärung gibt?
Joe Phillips
6
Lesen Sie K & R (Die Programmiersprache C). Das erklärt alles sehr deutlich.
Ferruccio
8
Die Fälle 4, 5 und 6 sind "Todesfallen". Dies ist ein Grund, warum viele Gudes im C / C ++ - Stil nur eine Deklaration pro Anweisung vorschlagen.
Michael Burr
14
Whitespace ist für einen C-Compiler unbedeutend (ohne Berücksichtigung des Präprozessors). Egal wie viele Leerzeichen sich zwischen dem Sternchen und seiner Umgebung befinden oder nicht, es hat genau die gleiche Bedeutung.
Ephemient
43

Leerzeichen um Sternchen haben keine Bedeutung. Alle drei bedeuten dasselbe:

int* test;
int *test;
int * test;

Das " int *var1, var2" ist eine böse Syntax, die nur dazu gedacht ist, Menschen zu verwirren und sollte vermieden werden. Es erweitert sich zu:

int *var1;
int var2;
Ates Goral
quelle
14
Vergiss nicht int*test.
Mateen Ulhaq
1
Der Raum vor oder nach dem Sternchen ist nur eine Frage der Ästhetik. Der Google Coding-Standard gilt jedoch für int *test( google-styleguide.googlecode.com/svn/trunk/… ).
@SebastianRaschka Der Google C ++ - Style Guide erlaubt explizit die Platzierung von Sternchen. Vielleicht hat es sich geändert, seit Sie es gelesen haben.
Jared Beck
33

In vielen Codierungsrichtlinien wird empfohlen, nur eine Variable pro Zeile zu deklarieren . Dies vermeidet jegliche Verwirrung, wie Sie sie vor dem Stellen dieser Frage hatten. Die meisten C ++ - Programmierer, mit denen ich gearbeitet habe, scheinen sich daran zu halten.


Ein bisschen beiseite, ich weiß, aber etwas, das ich nützlich fand, ist, Erklärungen rückwärts zu lesen.

int* test;   // test is a pointer to an int

Dies funktioniert sehr gut, insbesondere wenn Sie anfangen, const-Zeiger zu deklarieren, und es wird schwierig zu wissen, ob es der Zeiger ist, der const ist, oder ob es das ist, worauf der Zeiger auf const zeigt.

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const
Scott Langham
quelle
Obwohl die "eine Variable pro Zeile" nützlich erscheint, haben wir die Situation, in der sich das Sternchen mehr links oder mehr rechts befindet, noch nicht vollständig gelöst. Ich bin mir ziemlich sicher, dass im Code out in the wild eine Variante vorherrscht; ein bisschen wie einige Länder auf der rechten Seite fahren, und die anderen fahren in die falsche Richtung, wie Großbritannien. ;-)
Shevy
Leider sehe ich von meinen Abenteuern in der Wildnis viele beider Stile. In meinem Team verwenden wir jetzt das Clang-Format mit einem Stil, auf den wir uns geeinigt haben. Dies bedeutet zumindest, dass der gesamte Code, den unser Team erstellt, denselben Stil für die Leerzeichen hat.
Scott Langham
33

Verwenden Sie die "Spiralregel im Uhrzeigersinn" , um C / C ++ - Deklarationen zu analysieren.

Es gibt drei einfache Schritte:

  1. Beginnen Sie mit dem unbekannten Element und bewegen Sie sich spiralförmig / im Uhrzeigersinn. Wenn Sie auf die folgenden Elemente stoßen, ersetzen Sie diese durch die entsprechenden englischen Aussagen:

    [X]oder []: Array X Größe von ... oder Array undefinierte Größe von ...

    (type1, type2): Funktion übergibt Typ1 und Typ2 und gibt ...

    *: Zeiger auf ...

  2. Machen Sie dies spiralförmig / im Uhrzeigersinn, bis alle Token bedeckt sind.
  3. Lösen Sie immer zuerst alles in Klammern!

Außerdem sollten Erklärungen nach Möglichkeit in separaten Aussagen enthalten sein (was in den allermeisten Fällen der Fall ist).

Michael Burr
quelle
4
Das sieht entmutigend und ziemlich schrecklich aus, tut mir leid.
Joe Phillips
7
es tut es, aber es scheint eine ziemlich gute Erklärung für einige der komplizierteren Konstrukte zu sein
Michael Stum
@ d03boy: Keine Frage - C / C ++ - Deklarationen können ein Albtraum sein.
Michael Burr
2
Die "Spirale" macht keinen Sinn, geschweige denn die "im Uhrzeigersinn". Ich würde es lieber die "Rechts-Links-Regel" nennen, da die Syntax Sie nicht rechts-unten-links-oben, sondern nur rechts-links aussehen lässt.
Ruslan
Ich habe dies als "Rechts-Links-Rechts" -Regel gelernt. C ++ - Leute tun oft so, als wären alle Typinformationen auf der linken Seite, was int* x;eher zum Stil als zum traditionellen int *x;Stil führt. Natürlich spielt der Abstand für den Compiler keine Rolle, aber er wirkt sich auf die Menschen aus. Das Ablehnen der eigentlichen Syntax führt zu Stilregeln, die den Leser stören und verwirren können.
Adrian McCarthy
12

Wie andere erwähnt haben, sind 4, 5 und 6 gleich. Oft verwenden die Leute diese Beispiele, um das Argument zu machen, dass das *zur Variablen anstelle des Typs gehört. Während es sich um eine Frage des Stils handelt, gibt es einige Debatten darüber, ob Sie es so denken und schreiben sollten:

int* x; // "x is a pointer to int"

oder so:

int *x; // "*x is an int"

FWIW Ich bin im ersten Lager, aber der Grund, warum andere das Argument für die zweite Form vorbringen, ist, dass es (meistens) dieses spezielle Problem löst:

int* x,y; // "x is a pointer to int, y is an int"

was möglicherweise irreführend ist; stattdessen würdest du entweder schreiben

int *x,y; // it's a little clearer what is going on here

oder wenn Sie wirklich zwei Zeiger wollen,

int *x, *y; // two pointers

Persönlich sage ich, behalte es bei einer Variablen pro Zeile, dann ist es egal, welchen Stil du bevorzugst.

Huskerchad
quelle
6
Das ist Schwindel, wie nennt man das int *MyFunc(void)? a *MyFuncGibt eine Funktion ein zurück int? Nein. Offensichtlich sollten wir schreiben int* MyFunc(void)und sagen, dass MyFunceine Funktion a zurückgibt int*. Für mich ist klar, dass die Parsing-Regeln für C- und C ++ - Grammatik für die Variablendeklaration einfach falsch sind. Sie sollten die Zeigerqualifizierung als Teil des gemeinsam genutzten Typs für die gesamte Komma-Sequenz enthalten.
v.oddou
1
Ist *MyFunc() aber ein int. Das Problem mit der C-Syntax besteht darin, Präfix- und Postfix- Syntax zu mischen. Wenn nur Postfix verwendet würde, würde es keine Verwirrung geben.
Antti Haapala
1
Das erste Camp kämpft gegen die Syntax der Sprache und führt zu verwirrenden Konstrukten wie int const* x;, die ich ebenso irreführend finde wie a * x+b * y.
Adrian McCarthy
11
#include <type_traits>

std::add_pointer<int>::type test, test2;
Fredoverflow
quelle
#include <windows.h>LPINT test, test2;
Stefan Dragnev
5

In 4, 5 und 6 testist immer ein Zeiger und test2kein Zeiger. Leerzeichen sind in C ++ (fast) nie von Bedeutung.

1800 INFORMATIONEN
quelle
3

Meiner Meinung nach lautet die Antwort BEIDE, je nach Situation. Im Allgemeinen, IMO, ist es besser, das Sternchen neben den Zeigernamen zu setzen, als den Typ. Vergleiche zB:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

Warum ist der zweite Fall inkonsistent? Denn zB int x,y;deklariert zB zwei Variablen des gleichen Typs, aber der Typ wird in der Deklaration nur einmal erwähnt. Dies schafft einen Präzedenzfall und ein erwartetes Verhalten. Und int* pointer1, pointer2;ist damit nicht vereinbar, weil es pointer1als Zeiger deklariert , aber pointer2eine ganzzahlige Variable ist. Offensichtlich fehleranfällig und sollte daher vermieden werden (indem das Sternchen neben den Zeigernamen und nicht neben den Typ gesetzt wird).

Es gibt jedoch einige Ausnahmen, bei denen Sie das Sternchen möglicherweise nicht neben einem Objektnamen platzieren können (und bei denen es darauf ankommt, wo Sie es platzieren), ohne ein unerwünschtes Ergebnis zu erhalten - zum Beispiel:

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

Schließlich wird in einigen Fällen könnte es wohl sein klarer das Sternchen zu setzen neben dem Typ Namen, zB:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight

deLock
quelle
2

Das Grundprinzip in C ist, dass Sie die Variablen so deklarieren, wie Sie sie verwenden. Beispielsweise

char *a[100];

sagt, das *a[42]wird ein char. Und a[42]ein Zeichenzeiger. Und somit aist ein Array von Zeichenzeigern.

Dies liegt daran, dass die ursprünglichen Compiler-Autoren denselben Parser für Ausdrücke und Deklarationen verwenden wollten. (Kein sehr vernünftiger Grund für eine Wahl des Sprachdesigns)

Michel Billaud
quelle
Das Schreiben leitet jedoch char* a[100]; auch ab, dass *a[42];dies ein charund a[42];ein Zeichenzeiger sein wird.
yyny
Nun, wir alle schließen die gleichen Schlussfolgerungen, nur die Reihenfolge variiert.
Michel Billaud
Zitat: "sagt, dass * a [42] ein Zeichen sein wird. Und a [42] ein Zeichenzeiger". Bist du sicher, dass es nicht umgekehrt ist?
DeLock
Wenn Sie den anderen Weg bevorzugen, ist say a[42]ein charZeiger und *a[42]ein Zeichen.
Michel Billaud
2

Ich würde sagen, dass die ursprüngliche Konvention darin bestand, den Stern auf die Zeigernamensseite (rechte Seite der Deklaration) zu setzen

Sie können die gleichen Regeln befolgen, aber es ist keine große Sache, wenn Sie Sterne auf die Typenseite setzen. Denken Sie daran, dass Konsistenz wichtig ist, also immer, aber der Stern auf derselben Seite, unabhängig davon, welche Seite Sie gewählt haben.

TheDrev
quelle
Nun - der Parser scheint jede Variante zuzulassen, aber wenn Dennis und Linus sagen, dass es auf der rechten Seite sein sollte, ist das ziemlich überzeugend. Trotzdem fehlt uns eine Art Begründung und dann auch die Erklärung, warum dies getan wird. Es ist ein bisschen wie eine Tab-gegen-Space-Situation - nur dass man sie gelöst hat, weil Leute, die Leerzeichen anstelle von Tabs verwenden, laut
StackOverflow
1

Der Zeiger ist ein Modifikator für den Typ. Lesen Sie sie am besten von rechts nach links, um besser zu verstehen, wie das Sternchen den Typ ändert. 'int *' kann als "Zeiger auf int 'gelesen werden. In mehreren Deklarationen müssen Sie angeben, dass jede Variable ein Zeiger ist, sonst wird sie als Standardvariable erstellt.

1,2 und 3) Test ist vom Typ (int *). Leerzeichen spielen keine Rolle.

4,5 und 6) Test ist vom Typ (int *). Test2 ist vom Typ int. Auch hier spielt das Leerzeichen keine Rolle.

Ron Warholic
quelle
-2

Als gute Faustregel scheinen viele Leute diese Konzepte zu verstehen, indem sie: In C ++ wird eine Menge semantischer Bedeutung durch die Linksbindung von Schlüsselwörtern oder Bezeichnern abgeleitet.

Nehmen Sie zum Beispiel:

int const bla;

Die Konstante gilt für das Wort "int". Das Gleiche gilt für die Sternchen der Zeiger. Sie gelten für das Schlüsselwort links von ihnen. Und der tatsächliche Variablenname? Ja, das wird durch das erklärt, was davon übrig ist.

mstrobl
quelle
1
Dies beantwortet die Frage nicht. Wenn wir Schlimmer noch, versuchen zu schließen , eine Antwort von ihr, dann ist es die Stern bindet an die Art an seiner linken impliziert, die wie alle anderen auch gesagt hat, ist falsch. Es wird an den Namen der einzelnen Variablen rechts gebunden.
underscore_d