Ich habe immer vermasseln , wie zu bedienen const int*
, const int * const
und 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.
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?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.Antworten:
Lesen Sie es rückwärts (gemäß Uhrzeigersinn / Spiralregel ):
int*
- Zeiger auf intint const *
- Zeiger auf const intint * const
- const Zeiger auf intint const * const
- const Zeiger auf const intJetzt kann der erste
const
auf 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 intint ** const
- ein const Zeiger auf einen Zeiger auf ein intint * const *
- ein Zeiger auf einen const-Zeiger auf ein intint const **
- ein Zeiger auf einen Zeiger auf eine const intint * const * const
- ein const-Zeiger auf einen const-Zeiger auf ein intUnd um sicherzustellen, dass wir uns über die Bedeutung von
const
:foo
ist 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 habenconst 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.bar
ist 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 einenT* const
Zeiger verwenden, es sei denn, Sie müssenNULL
Zeiger zulassen .quelle
const int x = 0; const int *const px = &x; const int *const *const p = &px;
funktioniert gut.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:
quelle
void (*signal(int, void (*fp)(int)))(int);
von archive.is/SsfMXIch 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:
Die Art von
astring
istchar * const
nichtconst char *
. Dies ist ein Grund, warum ich immer dazu neige,const
rechts vom Typ zu stehen, und niemals am Anfang.quelle
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 ersetztP
, es beschleunigt nicht das Tippen und führt das von Ihnen erwähnte Problem ein.PINT
ist 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.PVOID
,LPTSTR
Sachen in der Win32 - API!Wie so ziemlich jeder darauf hingewiesen hat:
Was ist der Unterschied zwischen
const X* p
,X* const p
undconst X* const p
?quelle
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.
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.
Konstante Zeiger
Sobald ein konstanter Zeiger auf eine Variable zeigt, kann er nicht mehr auf eine andere Variable zeigen.
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.
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.
quelle
Die allgemeine Regel lautet, dass das
const
Schlüsselwort für das gilt, was unmittelbar davor steht. Ausnahme, ein Startconst
gilt für das Folgende.const int*
ist dasselbe wieint const*
und bedeutet "Zeiger auf Konstante int" .const int* const
ist dasselbe wieint const* const
und 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?
quelle
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.
quelle
const T*
und es ist natürlicher geworden. Wie oft Sie überhaupt eineT* const
verwenden, reicht normalerweise eine Referenz aus. Ich habe das alles einmal gebissen, als ich ein wollteboost::shared_ptr<const T>
und schrieb stattdessenconst boost::shared_ptr<T>
. Gleiches Thema in einem etwas anderen Kontext.const
der Typ von dem ist, was Konstante ist, und alles rechts davon ist das, was tatsächlich Konstante ist. Nehmen Sieint const * const * p;
als Beispiel. Nein, ich schreibe normalerweise nicht so, das ist nur ein Beispiel. Erstensconst
: Geben Sie int ein. Und das int, das const ist, ist der Inhalt des const-Zeigers, der der Inhalt von istp
. Zweiter const: Typ ist Zeiger aufconst
int, const oblect ist der Inhalt vonp
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
const
sie 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: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,
const
um festzustellen, ob der Zeiger oder auf was er zeigt, konstant ist oder beides. Zum Beispiel:deklariert, dass dies
Constant2
ein variabler Zeiger auf eine konstante Ganzzahl ist und:ist eine alternative Syntax, die dasselbe tut, während
deklariert, dass dies
Constant3
ein konstanter Zeiger auf eine variable Ganzzahl und isterklä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
quelle
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
const
links vom Sternchen angezeigt wird, ist der Hinweis konstantconst
rechts vom Sternchen angezeigt wird, ist der Zeiger selbst konstantconst
auf beiden Seiten angezeigt wird, sind beide konstantquelle
Es ist einfach, aber schwierig. Bitte beachten Sie, dass wir die tauschen können ,
const
Qualifier mit einem beliebigen Datentyp (int
,char
,float
, etc.).Sehen wir uns die folgenden Beispiele 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
==> Falsche Aussage. Der Compiler gibt einen Syntaxfehler aus.int *const p
==>p
ist schreibgeschützt [p
ist ein konstanter Zeiger auf eine Ganzzahl]. Da der Zeigerp
hier 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
==>*p
ist schreibgeschütztconst int *const p1
==>*p
undp
sind schreibgeschützt [p
ist ein konstanter Zeiger auf eine konstante Ganzzahl]. Da der Zeigerp
hier 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
==>*p
ist schreibgeschützt und entsprichtint const *p
int const *const p
==>*p
undp
sind schreibgeschützt [p
ist ein konstanter Zeiger auf eine konstante Ganzzahl]. Da der Zeigerp
hier schreibgeschützt ist, sollten sich Deklaration und Definition an derselben Stelle befinden.quelle
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 & const
aber 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 einerTYPE 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.quelle
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 nennenPtr_
:Jetzt
Ptr_<char>
ist ein Zeiger aufchar
.Ptr_<const char>
ist ein Zeiger aufconst char
.Und
const Ptr_<const char>
ist einconst
Zeiger aufconst char
.Dort.
quelle
Für mich hilft mir die Position,
const
dh ob es links oder rechts oder sowohl links als auch rechts in Bezug auf das erscheint*
, die tatsächliche Bedeutung herauszufinden.A
const
links von*
zeigt an, dass das Objekt, auf das der Zeiger zeigt, einconst
Objekt ist.Ein
const
rechts von*
zeigt an, dass der Zeiger einconst
Zeiger ist.Die folgende Tabelle stammt aus dem Stanford CS106L Standard C ++ - Programmierlabor.
quelle
Dies betrifft hauptsächlich die zweite Zeile: Best Practices, Zuweisungen, Funktionsparameter usw.
Allgemeine Übung. Versuche alles zu machen
const
du kannst. Oder anders ausgedrückt: Machen Sie zunächst allesconst
und entfernen Sie dann genau die Mindestmenge anconst
s, 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 ändern
const
Objekt , sollten Sie viel besser herausfinden, wer esconst
im 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 Dingenconst
- 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 ¶m)
Jetzt macht es einen Unterschied. Wie gerade erklärt,func
darf sich das ändernparam
, und jeder anrufende Standort sollte bereit sein, mit den Konsequenzen umzugehen. Wenn Sie die Erklärungvoid func(int const ¶m)
ändern, um den Vertrag zu ändern, und Garantien,func
die sich jetzt nicht ändern könnenparam
, 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)
undvoid 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, esnullptr
sei denn, eine andere vertragliche Garantie stellt sicher,func
dass sie niemals einnullptr
In erhältparam
.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.
quelle
Die Konstante mit dem int auf beiden Seiten zeigt auf die Konstante int :
oder:
const
after*
macht einen konstanten Zeiger auf int :In diesem Fall sind alle diese Zeiger auf eine konstante Ganzzahl , aber keine davon sind konstante Zeiger:
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:
quelle
const
es links von steht*
, bezieht es sich auf den Wert (es spielt keine Rolle, ob es istconst int
oderint const
)const
es rechts von ist*
, bezieht es sich auf den Zeiger selbstEin 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.BDies soll hauptsächlich in Funktionssignaturen verwendet werden, um sicherzustellen, dass die Funktion die übergebenen Argumente nicht versehentlich ändern kann.
quelle
Nur der Vollständigkeit halber für C nach den anderen Erklärungen, nicht sicher für C ++.
x
Zeiger
int *p;
int const *p;
int * const p;
int const * const p;
Zeiger auf Zeiger
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const *pp;
int const * const * const pp;
N-Niveaus der Dereferenzierung
Mach einfach weiter, aber möge die Menschheit dich exkommunizieren.
quelle
const int*
- Zeiger auf konstantesint
Objekt.Sie können den Wert des Zeigers ändern. Sie können den Wert des
int
Objekts nicht ändern , der Zeiger zeigt auf.const int * const
- konstanter Zeiger auf konstantesint
Objekt.Sie können weder den Wert des Zeigers noch den Wert des
int
Objekts ändern, auf das der Zeiger zeigt.int const *
- Zeiger auf konstantesint
Objekt.Diese Anweisung entspricht 1.
const int*
- Sie können den Wert des Zeigers ändern, aber Sie können den Wert desint
Objekts nicht ändern , auf den der Zeiger zeigt.Eigentlich gibt es eine 4. Option:
int * const
- konstanter Zeiger aufint
Objekt.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
int
Objekt, aber dieser Wert diesesint
Objekts 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.
quelle