it-swarm.com.de

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

Ich versuche immer, const int*, const int * const und int const * richtig zu verwenden. Gibt es eine Reihe von Regeln, die definieren, was Sie tun können und was nicht?

Ich möchte alle Aufgaben und Gebote in Bezug auf Aufgaben, Weitergabe an die Funktionen usw. kennen.

1126
ultraman

Lesen Sie es rückwärts (wie von im Uhrzeigersinn/Spiralregel getrieben):

  • int* - Zeiger auf int
  • int const * - Zeiger auf const int
  • int * const - const Zeiger auf int
  • int const * const - const Zeiger auf const int

Nun kann die erste const auf beiden Seiten des Typs stehen, also:

  • const int * == int const *
  • const int * const == int const * const

Wenn Sie wirklich verrückt werden möchten, können Sie Folgendes tun:

  • int ** - Zeiger auf Zeiger auf int
  • int ** const - ein const-Zeiger auf einen Zeiger auf ein int
  • int * const * - ein Zeiger auf einen const-Zeiger auf ein int
  • int const ** - ein Zeiger auf einen Zeiger auf eine const int
  • int * const * const - ein const-Zeiger auf einen const-Zeiger auf ein int
  • ...

Und um sicherzustellen, dass wir uns mit der Bedeutung von const klar sind

const int* foo;
int *const bar; //note, you actually need to set the pointer 
                //here because you can't change it later ;)

foo ist ein variabler Zeiger auf eine konstante Ganzzahl. Auf diese Weise können Sie den Punkt ändern, auf den Sie zeigen, nicht jedoch den Wert, auf den Sie zeigen. Meistens wird dies bei Strings im C-Stil gesehen, bei denen Sie einen Zeiger auf einen const char haben. Sie können die Zeichenfolge ändern, auf die Sie zeigen, aber Sie können den Inhalt dieser Zeichenfolgen nicht ändern. Dies ist wichtig, wenn sich der String selbst im Datensegment eines Programms befindet und nicht geändert werden sollte.

bar ist ein konstanter oder fester Zeiger auf einen Wert, der geändert werden kann. Dies ist wie eine Referenz ohne den extra syntaktischen Zucker. Aus diesem Grund würden Sie normalerweise einen Verweis verwenden, an dem Sie einen T* const-Zeiger verwenden, sofern Sie nicht NULL-Zeiger zulassen müssen.

1920
Matt Price

Für diejenigen, die die Uhrzeigersinn/Spiralregel nicht kennen: Beginnen Sie mit dem Namen der Variablen und bewegen Sie sie im Uhrzeigersinn (in diesem Fall rückwärts) zum nächsten Zeiger oder Typ . Wiederholen Sie diesen Vorgang, bis der Ausdruck endet.

hier ist eine Demo:

pointer to int

const pointer to int const

pointer to int const

pointer to const int

const pointer to int

274
Shijing Lv

Ich denke, hier ist schon alles beantwortet, aber ich möchte nur hinzufügen, dass Sie sich typedefs hüten sollten! Sie sind NICHT nur Textersatz.

Zum Beispiel:

typedef char *ASTRING;
const ASTRING astring;

Der Typ von astring ist char * const, nicht const char *. Dies ist ein Grund, warum ich immer const rechts vom Typ setze und nie am Anfang.

130
Kaz Dragon

Wie ziemlich alle darauf hingewiesen haben:

Was ist der Unterschied zwischen const X* p, X* const p und const X* const p?

Sie müssen Zeigerdeklarationen lesen rechts nach links.

  • const X* p bedeutet "p zeigt auf ein X, das const ist": Das X-Objekt kann nicht über p geändert werden.

  • X* const p bedeutet "p ist ein const-Zeiger auf ein nicht-const-X": Sie können den Zeiger p nicht ändern, aber Sie können das X-Objekt über p ändern.

  • const X* const p bedeutet "p ist ein const-Zeiger auf ein X, das const ist": Sie können den Zeiger p nicht ändern, noch können Sie das X-Objekt über p ändern.

47
luke
  1. Konstante Referenz:

    Ein Verweis auf eine Variable (hier int), die konstant ist. Wir übergeben die Variable hauptsächlich als Referenz, da die Referenzen kleiner sind als der tatsächliche Wert. Es gibt jedoch einen Nebeneffekt, der auf die tatsächliche Variable zurückzuführen ist. Wir können die Hauptvariable versehentlich durch unseren vollständigen Zugriff auf den Alias ​​ändern, so dass wir ihn konstant machen, um diesen Nebeneffekt zu verhindern.

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    
  2. Konstante Zeiger

    Wenn ein konstanter Zeiger auf eine Variable zeigt, kann er nicht auf eine andere Variable zeigen. 

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    
  3. Zeiger auf Konstante

    Ein Zeiger, durch den der Wert einer Variablen, auf die er zeigt, nicht geändert werden kann, wird als Zeiger auf Konstante bezeichnet.

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  4. Konstante Zeiger auf eine Konstante

    Ein konstanter Zeiger auf eine Konstante ist ein Zeiger, der weder die Adresse, auf die er zeigt, noch den an dieser Adresse gespeicherten Wert ändern kann.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    
41
Behrooz Tabesh

Diese Frage zeigt genau, warum ich Dinge so mache, wie ich sie in meiner Frage erwähnte Ist const nach Typ-ID akzeptabel?

Kurz gesagt, ich finde, der einfachste Weg, mich an die Regel zu erinnern, besteht darin, dass das "const" after die Sache ist, für die es gilt. In Ihrer Frage bedeutet "int const *", dass das int konstant ist, während "int * const" bedeuten würde, dass der Zeiger konstant ist.

Wenn jemand beschließt, es ganz nach vorne zu stellen (zB: "const int *"), gilt dies als besondere Ausnahme für die Sache danach.

Viele Leute verwenden diese besondere Ausnahme gerne, weil sie der Meinung sind, dass sie schöner aussieht. Ich mag es nicht, weil es eine Ausnahme ist und daher Dinge durcheinander bringt.

16
T.E.D.

Die allgemeine Regel lautet, dass das const-Schlüsselwort für das gilt, was sofort vorangeht. Ausnahme, ein Start von const gilt für das Folgende.

  • const int* ist das gleiche wie int const* und bedeutet "Zeiger auf Konstante int".
  • const int* const ist das gleiche wie int const* const und bedeutet "Konstante Zeiger auf Konstante int".

Edit: Wenn diese Antwort nicht genug ist, könnten Sie sich bei den Geboten und Verboten genauer äußern, was Sie wollen?

15
AProgrammer

Einfache Verwendung von "const"

Die einfachste Verwendung besteht darin, eine benannte Konstante zu deklarieren. Dazu deklariert man eine Konstante als wäre sie eine Variable, fügt jedoch „const“ hinzu. Man muss ihn sofort im Konstruktor initialisieren, da man den Wert natürlich nicht später einstellen kann, da sich dies ändern würde. Zum Beispiel,

const int Constant1=96; 

erstellt eine ganzzahlige Konstante mit dem Wert 96, die als "Constant1" bezeichnet wird.

Solche Konstanten sind nützlich für Parameter, die im Programm verwendet werden, aber nach dem Kompilieren des Programms nicht geändert werden müssen. Es hat einen Vorteil für Programmierer gegenüber dem # #define-Befehl des C-Präprozessors, da es vom Compiler selbst verstanden und verwendet wird und nicht nur vom Präprozessor vor dem Erreichen des Hauptcompilers in den Programmtext eingefügt wird .

Es funktioniert auch mit Zeigern, aber man muss darauf achten, wo "const" ist, um zu bestimmen, ob der Zeiger oder was er zeigt, konstant ist oder beides. Zum Beispiel,

const int * Constant2 

deklariert, dass Constant2 ein variabler Zeiger auf eine konstante Ganzzahl und ist

int const * Constant2

ist eine alternative Syntax, die dasselbe tut, während

int * const Constant3

deklariert, dass Constant3 ein konstanter Zeiger auf eine variable Ganzzahl und ist

int const * const Constant4

deklariert, dass Constant4 ein konstanter Zeiger auf eine konstante Ganzzahl ist. Grundsätzlich gilt „const“ für das, was sich unmittelbar links befindet (es sei denn, es ist nichts vorhanden. In diesem Fall gilt es für das, was unmittelbar ist).

ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

14
ufukgun

Ich hatte den gleichen Zweifel wie Sie, bis ich auf dieses Buch des C++ - Guru Scott Meyers gestoßen bin. Siehe den dritten Punkt in diesem Buch, in dem er ausführlich über die Verwendung von const spricht.

Folgen Sie einfach diesem Rat

  1. Wenn das Wort const links vom Sternchen erscheint, ist das, worauf gezeigt wird, konstant
  2. Wenn das Wort const rechts neben dem Sternchen erscheint, ist der Zeiger selbst konstant
  3. Wenn const auf beiden Seiten erscheint, sind beide konstant
7
rgk

Die C- und C++ - Deklarationssyntax wurde von den ursprünglichen Designern wiederholt als fehlgeschlagenes Experiment beschrieben.

Lassen Sie uns stattdessen name vom Typ "Zeiger auf Type"; Ich nenne es Ptr_:

template< class Type >
using Ptr_ = Type*;

Nun ist Ptr_<char> ein Zeiger auf char.

Ptr_<const char> ist ein Zeiger auf const char.

Und const Ptr_<const char> ist ein const-Zeiger auf const char.

Dort.

 enter image description here

Es ist einfach aber knifflig. Bitte beachten Sie, dass wir das const-Qualifikationsmerkmal mit einem beliebigen Datentyp (int, char, float usw.) austauschen können.

Sehen wir uns die Beispiele unten an.


const int *p ==> *p ist schreibgeschützt [p ist ein Zeiger auf eine konstante Ganzzahl]

int const *p ==> *p ist schreibgeschützt [p ist ein Zeiger auf eine konstante Ganzzahl]


int *p const ==> Falsch Anweisung. Compiler gibt einen Syntaxfehler aus.

int *const p ==> p ist schreibgeschützt [p ist ein konstanter Zeiger auf eine Ganzzahl] . Da der Zeiger p hier schreibgeschützt ist, sollten Deklaration und Definition an derselben Stelle stehen.


const int *p const ==> Falsch Anweisung. Compiler gibt einen Syntaxfehler aus.

const int const *p ==> *p ist schreibgeschützt

const int *const p1 ==> *p und p sind schreibgeschützt [p ist ein konstanter Zeiger auf eine konstante Ganzzahl]. Da der Zeiger p hier schreibgeschützt ist, sollten Deklaration und Definition an derselben Stelle sein.


int const *p const ==> Falsch Anweisung. Compiler gibt einen Syntaxfehler aus.

int const int *p ==> Falsch Anweisung. Compiler gibt einen Syntaxfehler aus.

int const const *p ==> *p ist schreibgeschützt und entspricht int const *p

int const *const p ==> *p und p sind schreibgeschützt [p ist ein konstanter Zeiger auf eine konstante Ganzzahl]. Da der Zeiger p hier schreibgeschützt ist, sollten Deklaration und Definition an derselben Stelle sein.

5
Abhijit Sahu

In C++ gibt es viele andere subtile Punkte, die sich mit der Konstanten-Korrektheit befassen. Ich nehme an, die Frage hier betraf einfach C, aber ich werde einige verwandte Beispiele nennen, da das Tag C++ ist:

  • Sie übergeben häufig große Argumente wie Zeichenfolgen als TYPE const &, wodurch verhindert wird, dass das Objekt geändert oder kopiert wird. Beispiel:

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    Aber TYPE & const ist bedeutungslos, weil Referenzen immer const sind.

  • Sie sollten Klassenmethoden, die die Klasse nicht ändern, immer als const kennzeichnen. Andernfalls können Sie die Methode nicht über einen TYPE const &-Verweis aufrufen. Beispiel:

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • Es gibt Situationen, in denen sowohl der Rückgabewert als auch die Methode const sein sollten. Beispiel:

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    Tatsächlich dürfen const-Methoden keine internen Klassendaten als Referenz auf non-const zurückgeben.

  • Infolgedessen muss häufig sowohl eine const-Methode als auch eine Nicht-const-Methode mit const-Überladung erstellt werden. Wenn Sie beispielsweise T const& operator[] (unsigned i) const; definieren, möchten Sie wahrscheinlich auch die Nicht-const-Version von:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Es gibt keine const-Funktionen in C, Nicht-Member-Funktionen können selbst keine const in C++ sein, const-Methoden können Nebenwirkungen haben und der Compiler kann keine const-Funktionen verwenden, um doppelte Funktionsaufrufe zu vermeiden. Tatsächlich kann sogar eine einfache int const & -Referenz den Wert anzeigen, auf den sie sich bezieht, und an anderer Stelle geändert werden.

5
Jeff Burdges

Das const mit dem int auf beiden Seiten macht Zeiger auf konstantes int .

const int *ptr=&i;

oder 

int const *ptr=&i;

const nach '*' macht konstanten Zeiger auf int .

int *const ptr=&i;

In diesem Fall sind dies alle Zeiger auf Konstante Integer , aber keiner davon ist konstanter Zeiger. 

 const int *ptr1=&i, *ptr2=&j;

In diesem Fall sind alle Zeiger auf Konstante Integer und ptr2 ist Konstante Zeiger auf Konstante Integer . Ptr1 ist jedoch kein konstanter Zeiger.

int const *ptr1=&i, *const ptr2=&j;
2
Hunter

Dies betrifft hauptsächlich die zweite Zeile: Best Practices, Zuweisungen, Funktionsparameter usw.

Allgemeine Übung. Versuchen Sie, alles const zu machen, das Sie können. Oder anders ausgedrückt: Machen Sie zunächst alles const und entfernen Sie dann genau die minimale Menge von consts, die für das Funktionieren des Programms erforderlich ist. Dies ist eine große Hilfe bei der Erreichung von Konst-Korrektheit und wird dazu beitragen, dass subtile Fehler nicht eingeführt werden, wenn Leute versuchen, 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 es gibt nur sehr wenige Fälle. Wenn Sie versuchen, ein const-Objekt zu ändern, sollten Sie denjenigen, der es als const deklariert hat, im ersten Schritt besser finden und die Angelegenheit mit ihm besprechen, um einen Konsens darüber zu erzielen, was geschehen soll.

Was sehr ordentlich in Aufgaben führt. Sie können nur etwas zuweisen, wenn es nicht konstant ist. Wenn Sie etwas zuweisen möchten, das const ist, lesen Sie oben. Denken Sie daran, dass in den Deklarationen int const *foo; und int * const bar; verschiedene Dinge const sind - andere Antworten hier haben dieses Thema bewundernswert abgedeckt, daher werde ich nicht darauf eingehen.

Funktionsparameter:

Wert übergeben: z. void func(int param) Sie interessieren sich nicht für die eine oder andere Weise auf der aufrufenden Site. Es kann das Argument gemacht werden, dass es Anwendungsfälle gibt, um die Funktion als void func(int const param) zu deklarieren, dies hat jedoch keine Auswirkungen auf den Aufrufer, sondern nur auf die Funktion selbst, da der übergebene Wert nicht von der Funktion während des Aufrufs geändert werden kann.

Referenz übergeben: z. void func(int &param) Jetzt macht es einen Unterschied. Wie soeben erklärt, darf funcparam ändern, und jede aufrufende Site sollte bereit sein, mit den Folgen fertig zu werden. Das Ändern der Deklaration in void func(int const &param) ändert den Vertrag und garantiert, dass func jetzt param nicht geändert werden kann. Das heißt, was übergeben wird, ist das, was zurückkommt. Wie bereits erwähnt, ist dies sehr nützlich, wenn Sie ein großes Objekt kostengünstig übergeben möchten, das Sie nicht ändern möchten. Das Übergeben einer Referenz ist viel billiger als das Übergeben eines großen Objekts nach Wert.

Zeiger übergeben: z. void func(int *param) und void func(int const *param) Diese beiden sind ziemlich gleichbedeutend mit ihren Referenzgegenstücken, mit der Einschränkung, dass die aufgerufene Funktion nun nach nullptr suchen muss, es sei denn, eine andere vertragliche Garantie gewährleistet, dass func niemals eine nullptr in param erhält.

Gutachten zu diesem Thema. In einem solchen Fall die Richtigkeit zu beweisen, ist höllisch schwierig, es ist einfach zu verdammt einfach, einen Fehler zu machen. Gehen Sie also kein Risiko ein und prüfen Sie immer die Zeigerparameter für nullptr. Sie ersparen sich Schmerzen und Leiden und werden langfristig schwer zu finden sein. Und was die Kosten für den Check angeht, ist er extrem billig. In Fällen, in denen die statische Analyse, die in den Compiler integriert ist, dies erledigen kann, wird das Optimierungsprogramm dies trotzdem tun. Aktivieren Sie die Link-Timecode-Generierung für MSVC oder WOPR (glaube ich) für GCC, und Sie werden programmweit verfügbar sein, d. H. Sogar bei Funktionsaufrufen, die eine Quellcode-Modulgrenze überschreiten.

Am Ende des Tages sind alle oben genannten Punkte ein sehr solider Fall, um immer Verweise auf Zeiger zu bevorzugen. Sie sind einfach rundum sicherer.

1
dgnuff

Für mich hilft die Position von const, d. H. Ob sie nach LINKS oder RECHTS oder sowohl LINKS als auch RECHTS relativ zum * erscheint, die tatsächliche Bedeutung herauszufinden.

  1. Eine const zur LEFT von * zeigt an, dass das Objekt, auf das der Zeiger zeigt, ein const-Objekt ist.

  2. Eine const zum RECHTS von * zeigt an, dass der Zeiger ein const-Zeiger ist.

Die folgende Tabelle stammt aus dem Stanford CS106L Standard-C++ - Programmierlabor.

 enter image description here

1
srivatsahc
  • wenn constlinks von _*_ ist, bezieht es sich auf den Wert (es ist egal, ob es _const int_ oder _int const_ ist)
  • wenn constrechts von _*_ ist, bezieht es sich 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. Der Wert selbst kann geändert werden. Z.B

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

Dies ist hauptsächlich für Funktionssignaturen gedacht, um sicherzustellen, dass die Funktion die übergebenen Argumente nicht ändern kann.

0
blue_note

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

  • pp - Zeiger auf Zeiger
  • p - Zeiger
  • daten - das Ding zeigte in Beispielen x
  • fett - Nur lesbare Variable

Zeiger

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

Zeiger auf Zeiger

  1. pp p data - int **pp;
  2. pp p Daten - int ** const pp;
  3. pp p data - int * const *pp;
  4. pp p data - int const **pp;
  5. pp p data - int * const * const pp;
  6. pp p data - int const ** const pp;
  7. pp p data - int const * const *pp;
  8. pp p data - 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

Mach einfach weiter, aber möge die Menschheit dich exkommunizieren.

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

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