1845 Stimmen

Was ist der Unterschied zwischen const int*, const int * const, und int const *?

Ich bringe immer durcheinander, wie man const int* , const int * const y int const * richtig. Gibt es eine Reihe von Regeln, die festlegen, was Sie tun dürfen und was nicht?

Ich möchte wissen, was ich tun und was ich lassen soll, wenn es um Zuweisungen, die Übergabe an Funktionen usw. geht.

256 Stimmen

Sie können die "Clockwise/Spiral Rule" um die meisten C- und C++-Deklarationen zu entschlüsseln.

76 Stimmen

cdecl.org ist eine großartige Website, die automatisch C-Deklarationen für Sie übersetzt.

11 Stimmen

@Calmarius: Beginnen Sie an der Stelle, an der sich der Typenname befindet bzw. befinden sollte, gehen Sie nach rechts, wenn Sie können, und nach links, wenn Sie müssen. . int *(*)(char const * const) . Beginnen Sie rechts von der eingeklammerten * dann müssen wir nach links gehen: pointer . Außerhalb der Klammern können wir nach rechts gehen: pointer to function of ... . Dann müssen wir nach links gehen: pointer to function of ... that returns pointer to int . Wiederholen Sie den Vorgang, um den Parameter zu erweitern (die ... ) : pointer to function of (constant pointer to constant char) that returns pointer to int . Wie würde die entsprechende einzeilige Erklärung in einer leicht lesbaren Sprache wie Pascal aussehen?

4voto

blue_note Punkte 25040
  • wenn const es nach links von * bezieht sich auf den Wert (es spielt keine Rolle, ob es sich um const int o int const )
  • wenn const es auf der rechten Seite von * verweist er auf den Zeiger selbst
  • es kann beides gleichzeitig sein

Ein wichtiger Punkt: const int *p bedeutet nicht, dass der Wert, auf den Sie sich beziehen, konstant ist!! . Es bedeutet, dass Sie es nicht ändern können. durch diesen Zeiger (das heißt, Sie können nicht $*p = ...` zuweisen). Der Wert selbst kann auf andere Weise geändert werden. Zum Beispiel

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

Dies soll vor allem in Funktionssignaturen verwendet werden, um sicherzustellen, dass die Funktion die übergebenen Argumente nicht versehentlich ändern kann.

4voto

slh Punkte 26

Ich habe ein Bild gezeichnet, um dies zu erklären, vielleicht hilfreich.

int const v y const int v sind identisch.

enter image description here

2voto

dgnuff Punkte 2771

Dabei geht es hauptsächlich um die zweite Zeile: bewährte Verfahren, Zuweisungen, Funktionsparameter usw.

Allgemeine Praxis. Versuchen Sie, alles const die Sie können. Oder anders ausgedrückt: Machen Sie alles const zu beginnen, und dann genau die Mindestmenge von const die notwendig sind, damit das Programm funktioniert. Dies wird eine große Hilfe bei der Erreichung von const-Korrektheit sein und wird dazu beitragen, dass subtile Fehler nicht eingeführt werden, wenn Leute versuchen, in Dinge zuzuweisen, die sie nicht ändern sollen.

Vermeiden Sie const_cast<> wie die Pest. Es gibt ein oder zwei legitime Anwendungsfälle dafür, aber die sind sehr selten und weit entfernt. Wenn Sie versuchen, eine const Objekt zu finden, ist es viel besser, denjenigen zu finden, der es angemeldet hat const und besprechen Sie die Angelegenheit mit ihnen, um einen Konsens darüber zu erzielen, was geschehen soll.

Das führt sehr gut zu den Aufträgen. Sie können etwas nur zuweisen, wenn es nicht konstant ist. Wenn Sie in etwas zuweisen wollen, das const ist, siehe oben. Denken Sie daran, dass in den Deklarationen int const *foo; y int * const bar; verschiedene Dinge sind const - Die anderen Antworten hier haben dieses Thema bereits ausführlich behandelt, so dass ich nicht näher darauf eingehen werde.

Funktionsparameter:

Übergabe nach Wert: z.B. void func(int param) Es ist Ihnen am Anruferstandort egal, was Sie tun. Es kann argumentiert werden, dass es Anwendungsfälle gibt, in denen die Funktion als void func(int const param) aber das hat keine Auswirkungen auf den Aufrufer, sondern nur auf die Funktion selbst, da der übergebene Wert von der Funktion während des Aufrufs nicht geändert werden kann.

Übergabe durch Referenz: z.B. void func(int &param) Jetzt macht es einen Unterschied. Wie gerade erklärt func darf sich ändern param und jede aufrufende Stelle sollte bereit sein, mit den Folgen umzugehen. Die Änderung der Deklaration in void func(int const &param) den Vertrag ändert, und garantiert, dass func kann nun nicht mehr ändern param Das bedeutet, dass das, was hineingelangt, auch wieder herauskommt. Wie andere bereits festgestellt haben, ist dies sehr nützlich, um ein großes Objekt, das man nicht ändern möchte, kostengünstig zu übergeben. Die Übergabe einer Referenz ist viel billiger als die Übergabe eines großen Objekts als Wert.

Übergabe durch Zeiger: z.B. void func(int *param) y void func(int const *param) Diese beiden sind so gut wie gleichbedeutend mit ihren Referenz-Gegenstücken, mit dem Vorbehalt, dass die aufgerufene Funktion nun prüfen muss, ob nullptr es sei denn, eine andere vertragliche Garantie sichert zu func dass sie niemals eine nullptr en param .

Meinungsbeitrag zu diesem Thema. Der Nachweis der Korrektheit in einem solchen Fall ist höllisch schwierig, es ist einfach zu leicht, einen Fehler zu machen. Gehen Sie also kein Risiko ein, und überprüfen Sie Zeigerparameter immer auf nullptr . Damit ersparen Sie sich auf lange Sicht Schmerzen und Leiden und schwer zu findende Bugs. Und was die Kosten für die Prüfung angeht, so ist sie spottbillig, und in Fällen, in denen die in den Compiler eingebaute statische Analyse sie bewältigen kann, wird der Optimierer sie ohnehin auslassen. Schalten Sie die Link Time Code Generation für MSVC oder WOPR (glaube ich) für GCC ein, und Sie erhalten sie programmweit, d.h. auch in Funktionsaufrufen, die eine Quellcode-Modulgrenze überschreiten.

Am Ende des Tages alle der oben genannten macht ein sehr solides Argument zu immer lieber Referenzen zu Zeigern. Sie sind einfach rundum sicherer.

2voto

Undefined Behavior Punkte 1930

Nur der Vollständigkeit halber für C nach den anderen Erklärungen, nicht sicher für C++.

  • pp - Zeiger auf Zeiger
  • p - Zeiger
  • Daten - die Sache zeigt, in Beispielen x
  • fett - schreibgeschützte Variable

Zeiger

  • p Daten - int *p;
  • p Daten - int const *p;
  • p Daten - int * const p;
  • p Daten - int const * const p;

Zeiger auf Zeiger

  1. pp p Daten - int **pp;
  2. Seiten p Daten - int ** const pp;
  3. Seiten p Daten - int * const *pp;
  4. pp p Daten - int const **pp;
  5. Seiten p Daten - int * const * const pp;
  6. Seiten p Daten - int const ** const pp;
  7. Seiten p Daten - int const * const *pp;
  8. Seiten p Daten - int const * const * const pp;

    // Example 1 int x; x = 10; int *p = NULL; p = &x; int pp = NULL; pp = &p; printf("%d\n", pp);

    // Example 2 int x; x = 10; int *p = NULL; p = &x; int const pp = &p; // Definition must happen during declaration printf("%d\n", pp);

    // Example 3 int x; x = 10; int const p = &x; // Definition must happen during declaration int const *pp = NULL; pp = &p; printf("%d\n", **pp);

    // Example 4 int const x = 10; // Definition must happen during declaration int const * p = NULL; p = &x; int const pp = NULL; pp = &p; printf("%d\n", pp);

    // Example 5 int x; x = 10; int const p = &x; // Definition must happen during declaration int const * const pp = &p; // Definition must happen during declaration printf("%d\n", **pp);

    // Example 6 int const x = 10; // Definition must happen during declaration int const *p = NULL; p = &x; int const const pp = &p; // Definition must happen during declaration printf("%d\n", pp);

    // Example 7 int const x = 10; // Definition must happen during declaration int const const p = &x; // Definition must happen during declaration int const const *pp = NULL; pp = &p; printf("%d\n", **pp);

    // Example 8 int const x = 10; // Definition must happen during declaration int const const p = &x; // Definition must happen during declaration int const const * const pp = &p; // Definition must happen during declaration printf("%d\n", **pp);

N-Ebenen der Dereferenz

Machen Sie einfach weiter, aber möge die Menschheit Sie exkommunizieren.

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);

2voto

  1. const int* - Zeiger auf Konstante int Objekt.

Sie können den Wert des Zeigers ändern, nicht aber den Wert des int Objekt, auf das der Zeiger zeigt.


  1. const int * const - Konstante Zeiger auf Konstante int Objekt.

Sie können weder den Wert des Zeigers noch den Wert des int Objekt, auf das der Zeiger zeigt.


  1. int const * - Zeiger auf Konstante int Objekt.

Diese Aussage ist gleichbedeutend mit 1. const int* - Sie können den Wert des Zeigers ändern, aber nicht den Wert des int Objekt, auf das der Zeiger zeigt.


Es gibt noch eine 4. Möglichkeit:

  1. int * const - Konstanter Zeiger auf int Objekt.

Sie können den Wert des Objekts, auf das der Zeiger zeigt, ändern, aber nicht den Wert des Zeigers selbst. Der Zeiger wird immer auf denselben int Objekts, sondern dieser Wert des int Objekt geändert werden kann.


Wenn Sie einen bestimmten Typ eines C- oder C++-Konstrukts bestimmen wollen, können Sie die Uhrzeiger/Spiralregel von David Anderson; aber nicht zu verwechseln mit Anderson-Regel von Ross J. Anderson, was etwas ganz Besonderes ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X