Ich spreche nicht von Zeigern auf const-Werte, sondern von const-Zeigern selbst.
Ich lerne C und C ++ über das Grundlegende hinaus und habe bis heute festgestellt, dass Zeiger wertmäßig an Funktionen übergeben werden, was Sinn macht. Dies bedeutet, dass ich innerhalb einer Funktion den kopierten Zeiger auf einen anderen Wert zeigen lassen kann, ohne den ursprünglichen Zeiger des Aufrufers zu beeinflussen.
Was bringt es also, einen Funktionsheader zu haben, der sagt:
void foo(int* const ptr);
In einer solchen Funktion können Sie ptr nicht auf etwas anderes verweisen lassen, da es const ist und Sie nicht möchten, dass es geändert wird, sondern eine Funktion wie diese:
void foo(int* ptr);
Funktioniert das genauso gut! da der Zeiger sowieso kopiert wird und der Zeiger im Aufrufer nicht betroffen ist, selbst wenn Sie die Kopie ändern. Was ist der Vorteil von const?
const
Parameter.const
-korrektheitsgarantien. Wir fühlen uns nur sicherer, dass unser Code zweifellos korrekt ist.Antworten:
const
ist ein Tool, das Sie zur Verfolgung eines sehr wichtigen C ++ - Konzepts verwenden sollten:Auch wenn die Funktionalität dadurch nicht geändert wird, führt das Hinzufügen zu
const
einem Compilerfehler, wenn Sie Dinge tun, die Sie nicht tun wollten. Stellen Sie sich folgenden Tippfehler vor:Wenn Sie verwenden
int* const
, würde dies einen Compilerfehler erzeugen, da Sie den Wert in ändernptr
. Das Hinzufügen von Einschränkungen über die Syntax ist im Allgemeinen eine gute Sache. Gehen Sie einfach nicht zu weit - das Beispiel, das Sie gegeben haben, ist ein Fall, in dem sich die meisten Menschen nicht darum kümmernconst
.quelle
Ich lege Wert darauf, nur
const
Argumente zu verwenden, da dies mehr Compilerprüfungen ermöglicht: Wenn ich versehentlich einen Argumentwert innerhalb der Funktion neu zuweise, beißt mich der Compiler.Ich verwende Variablen selten wieder. Es ist sauberer, neue Variablen zu erstellen, um neue Werte aufzunehmen. Daher sind im Wesentlichen alle meine Variablendeklarationen
const
(mit Ausnahme einiger Fälle wie Schleifenvariablen, in denenconst
der Code nicht funktioniert).Beachten Sie, dass dies nur bei der Definition einer Funktion sinnvoll ist . Es gehört nicht in die Deklaration , was der Benutzer sieht. Und dem Benutzer ist es egal, ob ich
const
für Parameter innerhalb der Funktion verwende.Beispiel:
Beachten Sie, wie sowohl das Argument als auch die lokale Variable sind
const
. Weder ist notwendig, aber mit Funktionen, die noch etwas größer sind, hat mich dies wiederholt vor Fehlern bewahrt.quelle
+1
von einem anderenconst
besessenen Fanatiker. Ich ziehe es jedoch vor, dass meine Compiler mich ankläffen. Ich mache zu viele Fehler und würde zu sehr leiden, wenn sie beißen würden.const
Strategie "es sei denn, es gibt einen guten Grund". Es gibt jedoch einige gute Ausnahmen, z. B. Kopieren und Tauschenconst
das Argument und kommentieren Sie vorzugsweise, warum. Diese winzige zusätzliche Arbeit rechtfertigt nicht, alle Argumente als nichtconst
standardmäßig zu markieren und sich allen potenziellen Fehlern zu öffnen, die dadurch entstehen.Ihre Frage berührt etwas Allgemeineres: Sollten Funktionsargumente const sein?
Die Konstanz von Wertargumenten (wie Ihr Zeiger) ist ein Implementierungsdetail und nicht Teil der Funktionsdeklaration. Dies bedeutet, dass Ihre Funktion immer folgende ist:
Es liegt ganz bei der Implementiererin der Funktion, ob sie die Argumentvariable functions-scope auf veränderliche oder konstante Weise verwenden möchte:
Befolgen Sie also die einfache Regel,
const
die Deklaration (Header) niemals einzufügen, und fügen Sie sie in die Definition (Implementierung) ein, wenn Sie die Variable nicht ändern möchten oder müssen.quelle
const
die Deklaration und den (Prototyp-Teil von) der Definition werden sie im Allgemeinen identisch sein.const
die Erklärung einfach nicht ein . Es liegt ganz bei Ihnen, ob Sie das Qualifikationsmerkmal zur Implementierung hinzufügen möchten.const
Sinn macht , die Erklärung, aber nicht die Definition abzugeben. Es fühlt sich einfach wie ein Fehler in der Sprache an, dass dies der einzige Fall ist, in dem es Sinn macht, die Deklaration und Definition nicht identisch zu machen. (Es ist natürlich kaum Cs einzige Panne.)Das const-Qualifikationsmerkmal der obersten Ebene wird in Deklarationen verworfen, sodass die Deklarationen in der Frage genau dieselbe Funktion deklarieren. Andererseits überprüft der Compiler in der Definition (Implementierung), ob der Zeiger, wenn Sie ihn als const markieren, nicht im Hauptteil der Funktion geändert wird.
quelle
int* t ptr
ist ein Syntaxfehler. Ohne das sind die beiden für Überlastungszwecke identisch.Sie haben Recht, für den Anrufer macht es absolut keinen Unterschied. Aber für den Verfasser der Funktion kann es ein Sicherheitsnetz sein "okay, ich muss sicherstellen, dass ich diesen Punkt nicht auf das Falsche hinweise". Nicht sehr nützlich, aber auch nicht nutzlos.
Es ist im Grunde das gleiche wie
int const the_answer = 42
in Ihrem Programm.quelle
const int
undint const
sind gleichwertig, währendconst int*
undint* const
haben zwei verschiedene Bedeutungen!int const
Teil; Ich habe den Typ seit einiger Zeit vor const gestellt (es klingt unnatürlich) und bin mir der Unterschiede bewusst. Dieser Artikel könnte sich als nützlich erweisen. Ich selbst hatte jedoch etwas andere Gründe, zu diesem Stil zu wechseln.Es gibt viel zu
const
Schlüsselwort hat bieten, es ist ziemlich komplex. Im Allgemeinen wird das Hinzufügen von viel const zu Ihrem Programm als gute Programmierpraxis angesehen. Durchsuchen Sie das Web nach "const-Korrektheit" und Sie werden viele Informationen dazu finden.Das Schlüsselwort const ist ein sogenanntes "Typqualifikationsmerkmal", andere sind
volatile
undrestrict
. Zumindest flüchtig folgt den gleichen (verwirrenden) Regeln wie const.Erstens dient das Schlüsselwort const zwei Zwecken. Am naheliegendsten ist es, Daten (und Zeiger) vor absichtlichem oder versehentlichem Missbrauch zu schützen, indem sie schreibgeschützt werden. Jeder Versuch, eine const-Variable zu ändern, wird vom Compiler zur Kompilierungszeit erkannt.
Es gibt aber auch einen anderen Zweck in jedem System mit Nur-Lese-Speicher, nämlich sicherzustellen, dass eine bestimmte Variable in einem solchen Speicher zugeordnet ist - beispielsweise EEPROM oder Flash. Diese werden als nichtflüchtige Speicher (NVM) bezeichnet. Eine in NVM zugewiesene Variable folgt natürlich weiterhin allen Regeln einer const-Variablen.
Es gibt verschiedene Möglichkeiten, das
const
Schlüsselwort zu verwenden:Deklarieren Sie eine konstante Variable.
Dies kann entweder als erfolgen
Diese beiden Formen sind völlig gleichwertig . Der letztere Stil wird als schlechter Stil angesehen und sollte nicht verwendet werden.
Der Grund, warum die zweite Zeile als schlechter Stil angesehen wird, liegt wahrscheinlich darin, dass "Speicherklassenspezifizierer" wie statisch und extern auch nach dem tatsächlichen Typ deklariert werden können.
int static
usw. . Dies wird jedoch für Speicherklassenspezifizierer als veraltete Funktion gekennzeichnet vom C-Ausschuss (Entwurf ISO 9899 N1539, 6.11.5). Aus Gründen der Konsistenz sollte man daher auch keine Typqualifizierer auf diese Weise schreiben. Es dient keinem anderen Zweck, als den Leser irgendwie zu verwirren.Deklarieren Sie einen Zeiger auf eine konstante Variable.
Dies bedeutet, dass der Inhalt von 'X' nicht geändert werden kann. Dies ist die normale Art und Weise, wie Sie solche Zeiger deklarieren, hauptsächlich als Teil von Funktionsparametern für "const-Korrektheit". Da 'X' nicht unbedingt als const deklariert werden muss, kann es sich um eine beliebige Variable handeln. Mit anderen Worten, Sie können eine Variable jederzeit auf const "upgraden". Technisch gesehen erlaubt C auch das Herabstufen von const auf eine einfache Variable durch explizite Typumwandlungen, dies wird jedoch als schlechte Programmierung angesehen, und Compiler geben normalerweise Warnungen davor aus.
Deklarieren Sie einen konstanten Zeiger
Dies bedeutet, dass der Zeiger selbst konstant ist. Sie können ändern, auf was es zeigt, aber Sie können den Zeiger selbst nicht ändern. Dies hat nicht viele Verwendungszwecke, es gibt einige, wie zum Beispiel sicherzustellen, dass die Adresse eines Zeigers (Zeiger auf Zeiger) nicht geändert wird, während er als Parameter an eine Funktion übergeben wird. Sie müssen etwas schreiben, das nicht zu lesbar ist:
Ich bezweifle, dass viele C-Programmierer die const und * genau dort rein bekommen können. Ich weiß, ich kann nicht - ich musste mich bei GCC erkundigen. Ich denke, deshalb sieht man diese Syntax für Zeiger zu Zeiger selten, obwohl sie als gute Programmierpraxis angesehen wird.
Konstante Zeiger können auch verwendet werden, um sicherzustellen, dass die Zeigervariable selbst im schreibgeschützten Speicher deklariert ist. Sie können beispielsweise eine zeigerbasierte Nachschlagetabelle deklarieren und in NVM zuweisen.
Und natürlich können, wie durch andere Antworten angezeigt, konstante Zeiger auch verwendet werden, um "konstante Korrektheit" zu erzwingen.
Deklarieren Sie einen konstanten Zeiger auf konstante Daten
Dies sind die beiden oben beschriebenen Zeigertypen, kombiniert mit allen Attributen von beiden.
Deklarieren Sie eine schreibgeschützte Elementfunktion (C ++).
Da dies mit C ++ gekennzeichnet ist, sollte ich auch erwähnen, dass Sie Elementfunktionen einer Klasse als const deklarieren können. Dies bedeutet, dass die Funktion beim Aufrufen kein anderes Mitglied der Klasse ändern darf, was sowohl den Programmierer der Klasse vor versehentlichen Fehlern schützt als auch den Aufrufer der Mitgliedsfunktion darüber informiert, dass sie nichts durcheinander bringen bis es anruft. Die Syntax lautet:
quelle
(imo) es macht wirklich keinen Sinn als Standard. Die sinnvollere Standardeinstellung ist die Übergabe als nicht neu zuweisbarer Zeiger (
int* const arg
). Das heißt, ich hätte es vorgezogen, wenn Zeiger, die als Argumente übergeben wurden, implizit als const deklariert wurden.Der Vorteil ist, dass es einfach und manchmal unklar ist, wenn Sie die Adresse ändern, auf die das Argument verweist, sodass Sie einen Fehler einführen können, wenn es nicht einfach ist. Das Ändern der Adresse ist untypisch. Es ist klarer, eine lokale Variable zu erstellen, wenn Sie die Adresse ändern möchten. Außerdem ist die Manipulation von Rohzeigern eine einfache Möglichkeit, Fehler einzuführen.
Daher ist es klarer, eine unveränderliche Adresse zu übergeben und eine Kopie (in diesen atypischen Fällen) zu erstellen, wenn Sie die Adresse ändern möchten, auf die das Argument verweist:
Das Hinzufügen dieses lokalen Elements ist praktisch kostenlos und verringert die Wahrscheinlichkeit von Fehlern, während die Lesbarkeit verbessert wird.
Auf einer höheren Ebene: Wenn Sie das Argument als Array bearbeiten, ist es für den Client in der Regel klarer und weniger fehleranfällig, das Argument als Container / Sammlung zu deklarieren.
Im Allgemeinen ist das Hinzufügen von const zu Werten, Argumenten und Adressen eine gute Idee, da Sie die Nebenwirkungen, die der Compiler gerne erzwingt, nicht immer erkennen. Daher ist es genauso nützlich wie const wie in anderen Fällen (z. B. ähnelt die Frage 'Warum sollte ich Werte const deklarieren?'). Zum Glück haben wir auch Referenzen, die nicht neu zugeordnet werden können.
quelle
const
Schlüsselwort zu haben, sollte es einmutable
Schlüsselwort haben (nun ja, aber mit der falschen Semantik).Wenn Sie eingebettete Systeme oder Gerätetreiber programmieren, bei denen Geräte mit Speicherzuordnung vorhanden sind, werden häufig beide Formen von 'const' verwendet, um zu verhindern, dass der Zeiger neu zugewiesen wird (da er auf eine feste Hardwareadresse verweist), und wenn das Peripheriegerät Das Register, auf das es zeigt, ist ein schreibgeschütztes Hardwareregister. Dann erkennt eine andere Konstante viele Fehler zur Kompilierungszeit und nicht zur Laufzeit.
Ein schreibgeschütztes 16-Bit-Peripherie-Chipregister könnte ungefähr so aussehen:
static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;
Dann können Sie das Hardwareregister einfach lesen, ohne auf die Assemblersprache zurückgreifen zu müssen:
input_word = *peripheral;
quelle
int iVal = 10; int * const ipPtr = & iVal;
Genau wie bei einer normalen const-Variablen muss ein const-Zeiger bei der Deklaration auf einen Wert initialisiert werden, und sein Wert kann nicht geändert werden.
Dies bedeutet, dass ein const-Zeiger immer auf denselben Wert zeigt. Im obigen Fall zeigt ipPtr immer auf die Adresse von iVal. Da der Wert, auf den gezeigt wird, immer noch nicht konstant ist, ist es möglich, den Wert, auf den gezeigt wird, durch Dereferenzieren des Zeigers zu ändern:
* ipPtr = 6; // erlaubt, da pnPtr auf eine nicht konstante int zeigt
quelle
Dieselbe Frage kann für jeden anderen Typ gestellt werden (nicht nur für Zeiger):
quelle
Ihre Frage ist wirklich mehr darüber, warum Sie eine Variable als const definieren und nicht nur als const-Zeigerparameter für eine Funktion. Hier gelten die gleichen Regeln wie beim Definieren einer Variablen als Konstante, wenn es sich um einen Funktionsparameter, eine Mitgliedsvariable oder eine lokale Variable handelt.
In Ihrem speziellen Fall macht es funktional keinen Unterschied wie in vielen anderen Fällen, wenn Sie eine lokale Variable als const deklarieren, aber es gibt eine Einschränkung, dass Sie diese Variable nicht ändern können.
quelle
Das Übergeben eines const-Zeigers an eine Funktion ist wenig sinnvoll, da er sowieso als Wert übergeben wird. Dies ist nur eines der Dinge, die das allgemeine Sprachdesign zulässt. Wenn Sie es nur verbieten, weil es keinen Sinn ergibt, wird nur die Sprachspezifikation festgelegt. größer.
Wenn Sie sich in einer Funktion befinden, ist dies natürlich ein anderer Fall. Ein Zeiger, der nicht ändern kann, worauf er zeigt, ist eine Behauptung, die den Code klarer macht.
quelle
Ich denke, ein Vorteil wäre, dass der Compiler aggressivere Optimierungen innerhalb der Funktion durchführen kann, da er weiß, dass sich dieser Zeiger nicht ändern kann.
Es vermeidet auch zB. Übergeben dieses Zeigers an eine Unterfunktion, die eine nicht konstante Zeigerreferenz akzeptiert (und daher den Zeiger wie ändern könnte
void f(int *&p)
), aber ich stimme zu, dass die Nützlichkeit in diesem Fall etwas eingeschränkt ist.quelle
Ein Beispiel dafür, wo ein const-Zeiger hoch anwendbar ist, kann auf diese Weise demonstriert werden. Angenommen, Sie haben eine Klasse mit einem dynamischen Array darin und möchten dem Benutzer den Zugriff auf das Array übergeben, ohne ihm jedoch die Rechte zum Ändern des Zeigers zu erteilen. Erwägen:
Welches produziert:
Aber wenn wir das versuchen:
Wir bekommen:
So klar können wir den Inhalt des Arrays ändern, aber nicht den Zeiger des Arrays. Gut, wenn Sie sicherstellen möchten, dass der Zeiger einen konsistenten Status hat, wenn Sie ihn an den Benutzer zurückgeben. Es gibt jedoch einen Haken:
Wir können die Speicherreferenz des Zeigers weiterhin löschen, auch wenn wir den Zeiger selbst nicht ändern können.
Wenn Sie also möchten, dass die Speicherreferenz immer auf etwas verweist (IE wird niemals geändert, ähnlich wie eine Referenz derzeit funktioniert), ist sie in hohem Maße anwendbar. Wenn Sie möchten, dass der Benutzer vollen Zugriff hat und ihn ändert, ist non-const genau das Richtige für Sie.
Bearbeiten:
Nachdem er den Kommentar okorz001 bemerkt hat, dass er nicht zugewiesen werden kann, weil GetArray () ein Operand mit dem richtigen Wert ist, ist sein Kommentar völlig korrekt, aber das oben Gesagte gilt immer noch, wenn Sie einen Verweis auf den Zeiger zurückgeben (ich nehme an, ich habe GetArray angenommen) unter Bezugnahme auf eine Referenz), zum Beispiel:
Wird im ersten zurückkehren, was zu einem Fehler führt:
Aber die zweite wird trotz möglicher Konsequenzen für die Unterseite fröhlich auftreten.
Offensichtlich wird die Frage aufgeworfen, warum Sie einen Verweis auf einen Zeiger zurückgeben möchten. Es gibt seltene Fälle, in denen Sie dem betreffenden Originalzeiger Speicher (oder Daten) direkt zuweisen müssen (z. B. Erstellen eines eigenen malloc / free- oder new / free-Frontends). In diesen Fällen handelt es sich jedoch um eine nicht konstante Referenz . Ein Verweis auf einen const-Zeiger Ich bin nicht auf eine Situation gestoßen, die dies rechtfertigen würde (es sei denn, es handelt sich möglicherweise um deklarierte const-Referenzvariablen anstatt um Rückgabetypen?).
Überlegen Sie, ob wir eine Funktion haben, die einen konstanten Zeiger verwendet (im Gegensatz zu einer, die dies nicht tut):
Der Fehler in der const erzeugt also folgende Meldung:
Was gut ist, da wir das wahrscheinlich nicht wollen, es sei denn, wir wollen die in den Kommentaren angegebenen Probleme verursachen. Wenn wir das Dekrement in der const-Funktion herausarbeiten, geschieht Folgendes:
Obwohl A 'Daten [1]' ist, wird es eindeutig als 'Daten [0]' behandelt, da der NonConst-Zeiger die Dekrementierungsoperation zuließ. Wenn die const implementiert ist, wie eine andere Person schreibt, erkennen wir den potenziellen Fehler, bevor er auftritt.
Eine andere wichtige Überlegung ist, dass ein const-Zeiger als Pseudoreferenz verwendet werden kann, indem das Objekt, auf das die Referenz zeigt, nicht geändert werden kann (man fragt sich, ob es vielleicht so implementiert wurde). Erwägen:
Beim Kompilieren wird der folgende Fehler ausgegeben:
Was wahrscheinlich eine schlechte Sache ist, wenn ein ständiger Verweis auf A gewünscht wurde. Wenn dies auskommentiert
B = NULL
ist, lässt der Compiler uns gerne ändern*B
und daher A. Dies scheint bei Ints möglicherweise nicht nützlich zu sein. Überlegen Sie jedoch, ob Sie eine einzelne Haltung einer grafischen Anwendung hatten, in der Sie einen nicht modifizierbaren Zeiger wollten, der darauf verweist und den Sie übergeben könnten um.Die Verwendung ist variabel (entschuldigen Sie das unbeabsichtigte Wortspiel), aber bei richtiger Verwendung ist es ein weiteres Werkzeug in der Box, das Sie bei der Programmierung unterstützt.
quelle
Temp.GetArray() = NULL
schlägt fehl, weilTemp.GetArray()
es sich um einen r-Wert handelt, nicht weil er const-qualifiziert ist. Außerdem glaube ich, dass das const-Qualifikationsmerkmal von allen Rückgabetypen entfernt ist.Es gibt nichts Besonderes an Zeigern, bei denen man niemals möchte, dass sie konstant sind. So wie Sie konstante
int
Werte für Klassenmitglieder haben können, können Sie aus ähnlichen Gründen auch konstante Zeiger haben: Sie möchten sicherstellen, dass niemand jemals ändert, worauf verwiesen wird. C ++ - Referenzen adressieren dies etwas, aber das Zeigerverhalten wird von C geerbt.quelle
Ich glaube, dies würde verhindern, dass Code den Zeiger innerhalb des Funktionskörpers inkrementiert oder dekrementiert.
quelle
Arten der Deklaration von Variablen wie:
(1) Deklarieren einer konstanten Variablen.
DataType const varibleName;
const dataType* PointerVaribleName=&X;
dataType* const PointerVaribleName=&X;
quelle