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

1356

Ich habe immer vermasseln , wie zu bedienen const int*, const int * constund int const *richtig. Gibt es eine Reihe von Regeln, die definieren, was Sie tun können und was nicht?

Ich möchte alle Do's und Don'ts in Bezug auf Aufgaben, Übergabe an die Funktionen usw. kennen.

MD XF
quelle
175
Sie können die "Cockwise / Spiral Rule" verwenden , um die meisten C- und C ++ - Deklarationen zu entschlüsseln.
James McNellis
52
cdecl.org ist eine großartige Website, die C-Deklarationen automatisch für Sie übersetzt.
Dave Gallagher
6
@Calmarius: Beginnen Sie dort, wo der Typname ist / sein sollte, bewegen Sie sich nach rechts, wenn Sie können, nach links, wenn Sie müssen . int *(*)(char const * const). Beginnen Sie rechts von der Klammer, *dann müssen wir nach links gehen : pointer. Außerhalb der Parens können wir uns nach rechts bewegen : pointer to function of .... Dann müssen wir nach links gehen : pointer to function of ... that returns pointer to int. Wiederholen Sie diesen Vorgang, um den Parameter (the ...) zu erweitern : pointer to function of (constant pointer to constant char) that returns pointer to int. Was wäre die äquivalente einzeilige Deklaration in einer leicht lesbaren Sprache wie Pascal?
Mark K Cowan
1
@ MarkKCowan In Pascal wäre es so etwas wie function(x:^char):^int. Dort implizieren Funktionstypen einen Zeiger auf eine Funktion, sodass sie nicht angegeben werden muss, und Pascal erzwingt keine Konstantenkorrektheit. Es kann von links nach rechts gelesen werden.
Calmarius
5
Das erste, was links von der "const" steht, ist die Konstante. Wenn "const" das am weitesten links stehende ist, dann ist das erste, was rechts davon ist, das, was konstant ist.
Cupcake

Antworten:

2208

Lesen Sie es rückwärts (gemäß Uhrzeigersinn / Spiralregel ):

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

Jetzt kann der erste constauf beiden Seiten des Typs sein, also:

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

Wenn du wirklich verrückt werden willst, kannst du solche Dinge 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 über die Bedeutung von const:

int a = 5, b = 10, c = 15;

const int* foo;     // pointer to constant int.
foo = &a;           // assignment to where foo points to.

/* dummy statement*/
*foo = 6;           // the value of a can´t get changed through the pointer.

foo = &b;           // the pointer foo can be changed.



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

*bar = 16;            // the value of c can be changed through the pointer.    

/* dummy statement*/
bar = &a;             // not possible because bar is a constant pointer.           

fooist ein variabler Zeiger auf eine konstante Ganzzahl. Auf diese Weise können Sie ändern, auf was Sie zeigen, jedoch nicht den Wert, auf den Sie zeigen. Am häufigsten wird dies bei Zeichenfolgen im C-Stil angezeigt, bei denen Sie einen Zeiger auf a haben const char. Sie können ändern, auf welche Zeichenfolge Sie zeigen, aber Sie können den Inhalt dieser Zeichenfolgen nicht ändern. Dies ist wichtig, wenn sich die Zeichenfolge selbst im Datensegment eines Programms befindet und nicht geändert werden sollte.

barist ein konstanter oder fester Zeiger auf einen Wert, der geändert werden kann. Dies ist wie eine Referenz ohne den zusätzlichen syntaktischen Zucker. Aus diesem Grund verwenden Sie normalerweise eine Referenz, bei der Sie einen T* constZeiger verwenden, es sei denn, Sie müssen NULLZeiger zulassen .

Matt Price
quelle
481
Ich möchte eine Faustregel anhängen, die Ihnen dabei helfen kann, herauszufinden, ob 'const' für Zeiger oder für spitze Daten gilt: Teilen Sie die Anweisung am Sternzeichen auf, wenn das Schlüsselwort const im linken Teil erscheint (wie in 'const int * foo') - es gehört zu spitzen Daten, wenn es sich im richtigen Teil befindet ('int * const bar') - es geht um den Zeiger.
Michael
14
@ Michael: Ein großes Lob an Michael für eine so einfache Regel zum Erinnern / Verstehen der Konstantenregel.
Sivabudh
10
@ Jeffrey: Rückwärts lesen funktioniert gut, solange es keine Klammern gibt. Dann, na
ja
12
+1, obwohl eine bessere Zusammenfassung wäre: Zeigerdeklarationen rückwärts lesen , dh nahe an @Michaels Aussage: Stoppen Sie das normale Lesen von links nach rechts beim ersten Sternchen.
Wolf
3
@gedamial es funktioniert, es funktioniert gut, aber Sie müssen es gleichzeitig mit der Deklaration zuweisen (da Sie einen "const-Zeiger" nicht neu zuweisen können). const int x = 0; const int *const px = &x; const int *const *const p = &px;funktioniert gut.
RastaJedi
356

Für diejenigen, die sich mit Clockwise / Spiral Rule nicht auskennen: Beginnen Sie mit dem Namen der Variablen und bewegen Sie sich im Uhrzeigersinn (in diesem Fall rückwärts) zum nächsten Zeiger oder Typ . Wiederholen, bis der Ausdruck endet.

Hier ist eine Demo:

Zeiger auf int

const Zeiger auf int const

Zeiger auf int const

Zeiger auf const int

const Zeiger auf int

Shijing Lv
quelle
8
@ Jan Der Link für das komplexe Beispiel hat keine Berechtigungen. Kannst du es direkt hier posten oder die Anzeigebeschränkungen aufheben?
R71
8
@ Rog es hatte früher alle Open Access-Berechtigungen ... Ich habe den Artikel nicht geschrieben und habe leider selbst keine Zugriffsberechtigungen. Hier ist jedoch eine archivierte Version des Artikels, die noch funktioniert: archive.is/SsfMX
Jan Rüegg
8
Das komplexe Beispiel ist immer noch von rechts nach links, beinhaltet jedoch das Auflösen von Klammern wie gewohnt. Die ganze Spiralsache im Uhrzeigersinn macht das nicht einfacher.
Matthew Read
4
Ultimatives Beispiel: void (*signal(int, void (*fp)(int)))(int);von archive.is/SsfMX
naXa
3
Verlassen Sie sich nicht auf diese Regel. Das ist nicht universell. Es gibt einige Fälle, in denen dies fehlschlägt.
Haccks
150

Ich denke, hier ist bereits alles beantwortet, aber ich möchte nur hinzufügen, dass Sie aufpassen sollten typedef s ! Sie sind NICHT nur Textersetzungen.

Zum Beispiel:

typedef char *ASTRING;
const ASTRING astring;

Die Art von astringist char * constnicht const char *. Dies ist ein Grund, warum ich immer dazu neige, constrechts vom Typ zu stehen, und niemals am Anfang.

Kaz Dragon
quelle
20
Und für mich ist dies der Grund, niemals Zeiger zu tippen. Ich sehe den Nutzen nicht in Dingen wie typedef int* PINT(ich nehme an, es ist etwas, das aus Praktiken in C stammt, und viele Entwickler haben es weiter gemacht). Großartig, ich habe das durch *ein ersetzt P, es beschleunigt nicht das Tippen und führt das von Ihnen erwähnte Problem ein.
Mephane
1
@ Mephane - das kann ich sehen. Für mich scheint es jedoch etwas rückständig zu sein, ein nettes Sprachfeature zu vermeiden, um weiterhin eine außergewöhnliche syntaktische Regel (über die Platzierung von "const") zu verwenden, anstatt die Verwendung der außergewöhnlichen syntaktischen Regel zu vermeiden, damit Sie dieses Sprachfeature sicher verwenden können .
TED
6
@Mephane PINTist in der Tat eine ziemlich dumme Verwendung eines Typedef, vor allem, weil ich denke, dass die Systemspeicher Bier als Speicher verwenden. typedefs sind jedoch sehr nützlich für den Umgang mit Zeigern auf Funktionen.
Annäherung an
5
@ KazDragon DANKE! Ohne sie würde habe ich mit allen , die typedefed vermasselt PVOID, LPTSTRSachen in der Win32 - API!
David Lee
2
@Mephane: Ich musste pSomething ein paar Mal verwenden, wenn ich bestimmte Legacy-Makros verwendete, die geschrieben wurden, um einen Typ zu akzeptieren, aber auseinander brechen würden, wenn der Typ kein einzelner alphanumerischer Bezeichner wäre. :)
Groo
56

Wie so ziemlich jeder darauf hingewiesen hat:

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

Sie müssen Zeigerdeklarationen von rechts nach links lesen.

  • 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 X, das nicht const ist": Sie können den Zeiger p selbst 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 weder den Zeiger p selbst noch das X-Objekt über p ändern.

Luke
quelle
3
Vergiss das nicht const X* p;== X const * p;wie in"p points to an X that is const": the X object can't be changed via p.
Jesse Chisholm
einfache und schöne Erklärung!
Edison Lo
50
  1. Ständige Referenz:

    Ein Verweis auf eine Variable (hier int), die konstant ist. Wir übergeben die Variable hauptsächlich als Referenz, weil Referenzen kleiner als der tatsächliche Wert sind, aber es gibt einen Nebeneffekt, und das liegt daran, dass sie wie ein Alias ​​für die tatsächliche Variable ist. Wir können die Hauptvariable versehentlich durch unseren vollständigen Zugriff auf den Alias ​​ändern, daher machen wir sie konstant, um diese Nebenwirkung zu verhindern.

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

    Sobald ein konstanter Zeiger auf eine Variable zeigt, kann er nicht mehr 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 eine Konstante bezeichnet.

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

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

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

Die allgemeine Regel lautet, dass das constSchlüsselwort für das gilt, was unmittelbar davor steht. Ausnahme, ein Start constgilt für das Folgende.

  • const int*ist dasselbe wie int const*und bedeutet "Zeiger auf Konstante int" .
  • const int* constist dasselbe wie int const* constund bedeutet "konstanter Zeiger auf konstante int" .

Bearbeiten: Wenn diese Antwort für die Dos and Don'ts nicht ausreicht, können Sie dann genauer sagen, was Sie wollen?

Ein Programmierer
quelle
19

Diese Frage zeigt genau, warum ich Dinge gerne so mache, wie ich es in meiner Frage erwähnt habe. Ist const nach Typ ID akzeptabel?

Kurz gesagt, ich finde, der einfachste Weg, sich an die Regel zu erinnern, ist, dass die "const" nach der Sache geht, für die sie gilt. In Ihrer Frage bedeutet "int const *", dass int konstant ist, während "int * const" bedeutet, dass der Zeiger konstant ist.

Wenn jemand beschließt, es ganz vorne zu platzieren (z. B. "const int *"), gilt dies in diesem Fall als besondere Ausnahme für die Sache danach.

Viele Leute nutzen diese spezielle Ausnahme gerne, weil sie denken, dass sie besser aussieht. Ich mag es nicht, weil es eine Ausnahme ist und die Dinge verwirrt.

TED
quelle
2
Ich bin in dieser Angelegenheit hin und her gerissen. Logischerweise macht es Sinn. Die meisten C ++ - Entwickler würden jedoch schreiben const T*und es ist natürlicher geworden. Wie oft Sie überhaupt eine T* constverwenden, reicht normalerweise eine Referenz aus. Ich habe das alles einmal gebissen, als ich ein wollte boost::shared_ptr<const T>und schrieb stattdessen const boost::shared_ptr<T>. Gleiches Thema in einem etwas anderen Kontext.
Matt Price
Eigentlich verwende ich Konstantenzeiger häufiger als Konstanten. Sie müssen auch darüber nachdenken, wie Sie bei Vorhandensein von Zeigern auf Zeiger (usw.) reagieren werden. Zwar sind diese seltener, aber es wäre schön, über Dinge so nachzudenken, wie Sie mit diesen Situationen mit Applomb umgehen können.
TED
1
Der andere nette Vorteil der Platzierung der Konstante rechts vom Typ ist, dass jetzt alles links von jedem constder Typ von dem ist, was Konstante ist, und alles rechts davon ist das, was tatsächlich Konstante ist. Nehmen Sie int const * const * p;als Beispiel. Nein, ich schreibe normalerweise nicht so, das ist nur ein Beispiel. Erstens const: Geben Sie int ein. Und das int, das const ist, ist der Inhalt des const-Zeigers, der der Inhalt von ist p. Zweiter const: Typ ist Zeiger auf constint, const oblect ist der Inhalt vonp
dgnuff
18

Einfache Verwendung von const .

Am einfachsten ist es, eine benannte Konstante zu deklarieren. Dazu deklariert man eine Konstante, als wäre sie eine Variable, fügt constsie jedoch vor. Man muss es sofort im Konstruktor initialisieren, da man den Wert natürlich später nicht setzen kann, da dies ihn ändern würde. Zum Beispiel:

const int Constant1=96; 

erstellt eine ganzzahlige Konstante, die einfallslos aufgerufen wird Constant1 mit dem Wert 96.

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. Dies hat für Programmierer einen Vorteil gegenüber dem C-Präprozessor#define Befehl, da er vom Compiler selbst verstanden und verwendet wird und nicht nur vom Präprozessor vor Erreichen des Haupt-Compilers in den Programmtext eingesetzt wird. Daher sind Fehlermeldungen viel hilfreicher.

Es funktioniert auch mit Zeigern, aber man muss vorsichtig sein, constum festzustellen, ob der Zeiger oder auf was er zeigt, konstant ist oder beides. Zum Beispiel:

const int * Constant2 

deklariert, dass dies Constant2ein variabler Zeiger auf eine konstante Ganzzahl ist und:

int const * Constant2

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

int * const Constant3

deklariert, dass dies Constant3ein konstanter Zeiger auf eine variable Ganzzahl und ist

int const * const Constant4

erklärt das Constant4 ein konstanter Zeiger auf eine konstante Ganzzahl ist. Grundsätzlich gilt 'const' für alles, was sich unmittelbar links davon befindet (außer wenn dort nichts ist. In diesem Fall gilt es für alles, was sich unmittelbar rechts davon befindet).

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

Ufukgun
quelle
9

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

Folgen Sie einfach diesem Rat

  1. Wenn das Wort constlinks vom Sternchen angezeigt wird, ist der Hinweis konstant
  2. Wenn das Wort constrechts vom Sternchen angezeigt wird, ist der Zeiger selbst konstant
  3. Wenn constauf beiden Seiten angezeigt wird, sind beide konstant
rgk
quelle
7

Es ist einfach, aber schwierig. Bitte beachten Sie, dass wir die tauschen können , constQualifier mit einem beliebigen Datentyp ( int, char,float , etc.).

Sehen wir uns die folgenden Beispiele an.


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

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


int *p const==> Falsche Aussage. Der Compiler gibt einen Syntaxfehler aus.

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


const int *p const ==> Falsche Aussage. Der Compiler gibt einen Syntaxfehler aus.

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

const int *const p1 ==> *pund psind schreibgeschützt [ pist ein konstanter Zeiger auf eine konstante Ganzzahl]. Da der Zeiger phier schreibgeschützt ist, sollten sich Deklaration und Definition an derselben Stelle befinden.


int const *p const ==> Falsche Aussage. Der Compiler gibt einen Syntaxfehler aus.

int const int *p ==> Falsche Aussage. Der Compiler gibt einen Syntaxfehler aus.

int const const *p ==> *pist schreibgeschützt und entsprichtint const *p

int const *const p ==> *pund psind schreibgeschützt [ pist ein konstanter Zeiger auf eine konstante Ganzzahl]. Da der Zeiger phier schreibgeschützt ist, sollten sich Deklaration und Definition an derselben Stelle befinden.

Abhijit Sahu
quelle
6

Es gibt viele andere subtile Punkte, die die Korrektheit von Konstanten in C ++ betreffen. Ich nehme an, die Frage hier war einfach über C, aber ich werde einige verwandte Beispiele geben, da das Tag C ++ ist:

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

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

    Ist TYPE & constaber bedeutungslos, weil Referenzen immer const sind.

  • Sie sollten Klassenmethoden, die die Klasse nicht ändern, immer als kennzeichnen const, da Sie die Methode sonst nicht aus einer TYPE const &Referenz aufrufen können . Beispiel:

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

  • Es gibt häufige 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 Nicht-const zurückgeben.

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

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

Afaik, 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, auf den sie sich bezieht, an anderer Stelle ändern.

Jeff Burdges
quelle
6

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

Stattdessen wollen wir nennen den Typ „Zeiger auf Type“; Ich werde es nennen Ptr_:

template< class Type >
using Ptr_ = Type*;

Jetzt Ptr_<char>ist ein Zeiger auf char.

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

Und const Ptr_<const char>ist ein constZeiger auf const char.

Dort.

Geben Sie hier die Bildbeschreibung ein

Prost und hth. - Alf
quelle
3
Hast du ein Zitat für den ersten Satz?
SP2danny
@ sp2danny: Das Googeln von "C-Syntax fehlgeschlagenes Experiment" hustet nur eine Reihe von Interviews mit Bjarne Stroustrup aus, in denen er seine Meinung in diese Richtung äußert , z. B. "Ich betrachte die C-Deklarator-Syntax als ein Experiment, das fehlgeschlagen ist" im Slashdot-Interview. Ich habe also keinen Hinweis auf die Behauptung über die Standpunkte der ursprünglichen Designer von C. Ich denke, sie kann durch einen ausreichend starken Forschungsaufwand gefunden oder vielleicht einfach durch Fragen widerlegt werden, aber ich denke, es ist besser so, wie es jetzt ist. mit diesem Teil der Behauptung, immer noch unentschlossen und wahrscheinlich wahr :)
Prost und hth. - Alf
1
"Die C- und C ++ - Deklarationssyntax wurde von den ursprünglichen Designern wiederholt als fehlgeschlagenes Experiment beschrieben." falsch für C Bitte ändern Sie Ihren Satz über C oder geben Sie einige Zitate an.
Stargateur
3
@ Stargateur: Anscheinend haben Sie die vorhergehenden Kommentare gelesen und etwas gefunden, das Sie für die Pedanterie nutzen können. Viel Glück mit deinem Leben. Wie auch immer, Oldtimer wie ich erinnern sich an eine Menge, die wir nicht beweisen können, ohne sehr zeitaufwändige Nachforschungen anzustellen. Du könntest einfach mein Wort nehmen.
Prost und hth. - Alf
1
@ Stargateur "Sethi (...) stellte fest, dass viele der verschachtelten Deklarationen und Ausdrücke einfacher werden würden, wenn der Indirektionsoperator als Postfix-Operator anstelle des Präfixes verwendet worden wäre, aber bis dahin war es zu spät, um Änderungen vorzunehmen." ist von DMR. Natürlich hat DMR keine konstanten und flüchtigen Schlüsselwörter erfunden, sie stammen aus C ++ / X3J11, wie auf dieser Seite gezeigt.
Antti Haapala
6

Für mich hilft mir die Position, constdh ob es links oder rechts oder sowohl links als auch rechts in Bezug auf das erscheint *, die tatsächliche Bedeutung herauszufinden.

  1. A constlinks von *zeigt an, dass das Objekt, auf das der Zeiger zeigt, ein constObjekt ist.

  2. Ein constrechts von *zeigt an, dass der Zeiger ein constZeiger ist.

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

Geben Sie hier die Bildbeschreibung ein

sri
quelle
3

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

Allgemeine Übung. Versuche alles zu machenconst du kannst. Oder anders ausgedrückt: Machen Sie zunächst alles constund entfernen Sie dann genau die Mindestmenge an consts, die erforderlich ist, damit das Programm funktioniert. Dies ist eine große Hilfe bei der Erreichung der Konstanz und stellt sicher, dass keine subtilen Fehler auftreten, 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. Wenn Sie versuchen, a zu ändernconst Objekt , sollten Sie viel besser herausfinden, wer es constim ersten Tempo deklariert hat , und die Angelegenheit mit ihm besprechen, um einen Konsens darüber zu erzielen, was passieren soll.

Was sehr ordentlich zu Aufgaben führt. Sie können etwas nur zuweisen, wenn es nicht konstant ist. Wenn Sie etwas zuweisen möchten, das const ist, siehe oben. Denken Sie daran, dass in den Erklärungen int const *foo;undint * const bar; verschiedenen Dingen const- andere Antworten hier haben dieses Thema bewundernswert behandelt, so dass ich nicht darauf eingehen werde.

Funktionsparameter:

Wert übergeben: zB ist es void func(int param)Ihnen am anrufenden Standort egal, auf welche Weise oder auf welche Weise. Es kann argumentiert werden, dass es Anwendungsfälle für die Deklaration der Funktion als gibtvoid func(int const param) , die jedoch keine Auswirkungen auf den Aufrufer haben, sondern nur auf die Funktion selbst, da der übergebene Wert von der Funktion während des Aufrufs nicht geändert werden kann.

Als Referenz übergeben: zB void func(int &param)Jetzt macht es einen Unterschied. Wie gerade erklärt, funcdarf sich das ändern param, und jeder anrufende Standort sollte bereit sein, mit den Konsequenzen umzugehen. Wenn Sie die Erklärung void func(int const &param)ändern, um den Vertrag zu ändern, und Garantien, funcdie sich jetzt nicht ändern können param, bedeutet dies, dass das, was übergeben wird, wieder herauskommt. Wie andere angemerkt haben, ist dies sehr nützlich, um ein großes Objekt, das Sie nicht ändern möchten, kostengünstig zu übergeben. Das Übergeben einer Referenz ist viel billiger als das Übergeben eines großen Objekts nach Wert.

Pass by Pointer: zB void func(int *param)und void func(int const *param)Diese beiden sind so ziemlich gleichbedeutend mit ihren Referenzgegenstücken, mit der Einschränkung, dass die aufgerufene Funktion jetzt prüfen muss, es nullptrsei denn, eine andere vertragliche Garantie stellt sicher, funcdass sie niemals ein nullptrIn erhält param.

Meinungsbeitrag zu diesem Thema. In einem solchen Fall die Richtigkeit zu beweisen, ist höllisch schwierig, es ist einfach zu einfach, einen Fehler zu machen. Gehen Sie also kein Risiko ein und überprüfen Sie immer die Zeigerparameter auf nullptr. Sie ersparen sich Schmerzen und Leiden und sind auf lange Sicht schwer zu finden. Und was die Kosten für die Prüfung angeht, ist sie spottbillig, und in Fällen, in denen die im Compiler integrierte statische Analyse sie verwalten kann, wird sie vom Optimierer trotzdem entfernt. Wenn Sie die Link Time Code-Generierung für MSVC oder WOPR (glaube ich) für GCC aktivieren, erhalten Sie sie programmweit, dh auch bei Funktionsaufrufen, die eine Quellcodemodulgrenze überschreiten.

Letztendlich ist all das ein sehr solides Argument, um immer Verweise auf Zeiger zu bevorzugen. Sie sind einfach rundum sicherer.

dgnuff
quelle
3

Die Konstante mit dem int auf beiden Seiten zeigt auf die Konstante int :

const int *ptr=&i;

oder:

int const *ptr=&i;

constafter *macht einen konstanten Zeiger auf int :

int *const ptr=&i;

In diesem Fall sind alle diese Zeiger auf eine konstante Ganzzahl , aber keine davon sind konstante Zeiger:

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

In diesem Fall sind alle Zeiger auf eine konstante Ganzzahl und ptr2 ist ein konstanter Zeiger auf eine konstante Ganzzahl . Aber ptr1 ist kein konstanter Zeiger:

int const *ptr1=&i, *const ptr2=&j;
Jäger
quelle
3
  • Wenn constes links von steht *, bezieht es sich auf den Wert (es spielt keine Rolle, ob es ist const intoder int const)
  • Wenn constes rechts 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 !! . Dies bedeutet, dass Sie es nicht über diesen Zeiger ändern können (dh Sie können $ * p = ... `nicht zuweisen). Der Wert selbst kann auf andere Weise geändert werden. Z.B

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

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

blue_note
quelle
2

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 - das Ding zeigte 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. pp p Daten -int ** const pp;
  3. pp p Daten -int * const *pp;
  4. pp p Daten -int const **pp;
  5. pp p Daten -int * const * const pp;
  6. pp p Daten -int const ** const pp;
  7. pp p Daten -int const * const *pp;
  8. pp 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-Niveaus der Dereferenzierung

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);
Undefiniertes Verhalten
quelle
0
  1. const int*- Zeiger auf konstantes intObjekt.

Sie können den Wert des Zeigers ändern. Sie können den Wert des intObjekts nicht ändern , der Zeiger zeigt auf.


  1. const int * const- konstanter Zeiger auf konstantes intObjekt.

Sie können weder den Wert des Zeigers noch den Wert des intObjekts ändern, auf das der Zeiger zeigt.


  1. int const *- Zeiger auf konstantes intObjekt.

Diese Anweisung entspricht 1. const int*- Sie können den Wert des Zeigers ändern, aber Sie können den Wert des intObjekts nicht ändern , auf den der Zeiger zeigt.


Eigentlich gibt es eine 4. Option:

  1. int * const- konstanter Zeiger auf intObjekt.

Sie können den Wert des Objekts ändern, auf das der Zeiger zeigt, aber Sie können den Wert des Zeigers selbst nicht ändern. Der Zeiger zeigt immer auf dasselbe intObjekt, aber dieser Wert dieses intObjekts kann geändert werden.


Wenn Sie einen bestimmten Typ von C- oder C ++ - Konstrukt bestimmen möchten, können Sie die von David Anderson erstellte Regel im Uhrzeigersinn / Spirale verwenden . aber nicht zu verwechseln mit Andersons Regel von Ross J. Anderson, die etwas ganz Besonderes ist.

RobertS unterstützt Monica Cellio
quelle