const char * const versus const char *?

109

Ich führe einige Beispielprogramme durch, um mich mit C ++ vertraut zu machen, und bin auf die folgende Frage gestoßen. Hier ist zunächst der Beispielcode:

void print_string(const char * the_string)
{
    cout << the_string << endl;
}

int main () {
    print_string("What's up?");
}

Im obigen Code könnte stattdessen der Parameter für print_string gewesen sein const char * const the_string . Welches wäre dafür richtiger?

Ich verstehe, dass der Unterschied darin besteht, dass einer ein Zeiger auf ein konstantes Zeichen ist, während der andere ein konstanter Zeiger auf ein konstantes Zeichen ist. Aber warum funktionieren beide? Wann wäre es relevant?

Bild
quelle

Antworten:

243

Letzteres verhindert, dass Sie the_stringinnen Änderungen vornehmen print_string. Hier wäre es eigentlich angebracht, aber vielleicht hat die Ausführlichkeit den Entwickler abgeschreckt.

char* the_string: Ich kann chardie the_stringPunkte ändern, charauf die sie zeigen , und ich kann die Punkte ändern, auf die sie zeigen.

const char* the_string: Ich kann chardie the_stringPunkte ändern , aber ich kann die Punkte, charauf die sie zeigen, nicht ändern .

char* const the_string: Ich kann chardie the_stringPunkte nicht ändern , aber ich kann die Punkte ändern, charauf die sie zeigen.

const char* const the_string: Ich kann weder chardie the_stringPunkte ändern, noch die Punkte, charauf die sie zeigen.

Kent Boogaart
quelle
11
+1 für den letzten Satz. Konst-Korrektheit ist ausführlich, aber es lohnt sich.
Mskfisher
6
@Xeo: Ihre Form ist noch verwirrender, weil sie nur eine Transposition entfernt ist, um ihre Bedeutung vollständig zu ändern. const char *ist viel besser, weil das constauf der gegenüberliegenden Seite ist.
R .. GitHub STOP HELPING ICE
7
@R ..: Nun, zumindest für mich ist es nicht. Wenn ich von rechts nach links lese, erhalte ich "Zeiger auf const char". Für mich fühlt es sich einfach besser an.
Xeo
6
Nun, Sie täuschen sich selbst, weil C-Typen von innen nach außen gelesen werden, nicht von links nach rechts. :-)
R .. GitHub STOP HELPING ICE
11
Es ist mir ein wenig peinlich, dass ich anscheinend der einzige bin, der das nicht versteht ... aber was ist der Unterschied zwischen "dem Zeichen, auf das es zeigt" und "dem Zeichen, auf das es zeigt"?
Mangel
138
  1. Veränderbarer Zeiger auf ein veränderliches Zeichen

    char *p;
  2. Veränderbarer Zeiger auf ein konstantes Zeichen

    const char *p;
  3. Konstanter Zeiger auf ein veränderliches Zeichen

    char * const p; 
  4. Konstanter Zeiger auf ein konstantes Zeichen

    const char * const p;
James Michael Hare
quelle
Sollte das nicht sein: const char* p; --> constant pointer to mutable characterund char *const p; --> mutable pointer to constant character
PnotNP
2
@NulledPointer Nr. C ++ - Deklarationen werden von rechts nach links gebildet. Sie können also lesen const char * pals: "p ist ein Zeiger auf eine Zeichenkonstante" oder ein veränderlicher Zeiger auf ein konstantes Zeichen, wie James korrekt angibt. das gleiche mit dem zweiten :).
Samidamaru
28

const char * constMittel Zeiger sowie die Daten der Zeiger zeigt auf, sind beide konst!

const char *bedeutet, dass nur die Daten, auf die der Zeiger zeigt, const sind. Zeiger selbst ist jedoch nicht const.

Beispiel.

const char *p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //okay, changing the non-const pointer. 

const char * const p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //error, changing the const pointer. 
Nawaz
quelle
19

(Ich weiß, dass dies alt ist, aber ich wollte es trotzdem teilen.)

Ich wollte nur auf Thomas Matthews Antwort eingehen. Die Rechts-Links-Regel für C-Typ-Deklarationen besagt ziemlich genau: Wenn Sie eine C-Typ-Deklaration lesen, beginnen Sie am Bezeichner und gehen Sie nach rechts, wenn Sie können, und nach links, wenn Sie nicht können.

Dies lässt sich am besten anhand einiger Beispiele erklären:

Beispiel 1

  • Beginnen Sie an der Kennung, wir können nicht nach rechts gehen, also gehen wir nach links

    const char* const foo
                ^^^^^

    foo ist eine Konstante ...

  • Weiter links

    const char* const foo
              ^

    foo ist ein ständiger Zeiger auf ...

  • Weiter links

    const char* const foo
          ^^^^

    foo ist ein konstanter Zeiger auf char ...

  • Weiter links

    const char* const foo
    ^^^^^

    foo ist ein konstanter Zeiger auf die char- Konstante (Complete!)

Beispiel 2

  • Beginnen Sie an der Kennung, wir können nicht nach rechts gehen, also gehen wir nach links

    char* const foo
          ^^^^^

    foo ist eine Konstante ...

  • Weiter links

    char* const foo
        ^

    foo ist ein ständiger Zeiger auf ...

  • Weiter links

    char* const foo
    ^^^^

    foo ist ein konstanter Zeiger auf char (Complete!)

Beispiel 1337

  • Beginnen Sie an der Kennung, aber jetzt können wir richtig gehen!

    const char* const* (*foo[8])()
                            ^^^

    foo ist ein Array von 8 ...

  • Drücken Sie die Klammer, damit Sie nicht mehr nach rechts und nach links gehen können

    const char* const* (*foo[8])()
                        ^

    foo ist ein Array von 8 Zeigern auf ...

  • Fertig in Klammern, kann jetzt nach rechts gehen

    const char* const* (*foo[8])()
                                ^^

    foo ist ein Array von 8 Zeigern auf eine Funktion, die ...

  • Nichts mehr rechts, geh nach links

    const char* const* (*foo[8])()
                     ^

    foo ist ein Array von 8 Zeigern auf eine Funktion, die einen Zeiger auf eine ...

  • Weiter links

    const char* const* (*foo[8])()
                ^^^^^

    foo ist ein Array von 8 Zeigern auf Funktionen, die einen Zeiger auf eine Konstante zurückgeben ...

  • Weiter links

    const char* const* (*foo[8])()
              ^

    foo ist ein Array von 8 Zeigern auf Funktionen, die einen Zeiger auf einen konstanten Zeiger auf ...

  • Weiter links

    const char* const* (*foo[8])()
          ^^^^

    foo ist ein Array von 8 Zeigern auf Funktionen, die einen Zeiger auf einen konstanten Zeiger auf ein Zeichen zurückgeben ...

  • Weiter links

    const char* const* (*foo[8])()
    ^^^^^

    foo ist ein Array von 8 Zeigern auf Funktionen, die einen Zeiger auf einen konstanten Zeiger auf eine char- Konstante zurückgeben (Complete!)

Weitere Erklärung: http://www.unixwiz.net/techtips/reading-cdecl.html

Garrett
quelle
CMIIW, const char * const foo sollte äquivalent zu char const * const foo sein?
Luochenhuan
@luochenhuan Ja, in der Tat sind sie.
Garrett
12

Viele Leute schlagen vor, den Typspezifizierer von rechts nach links zu lesen.

const char * // Pointer to a `char` that is constant, it can't be changed.
const char * const // A const pointer to const data.

In beiden Formen zeigt der Zeiger auf konstante oder schreibgeschützte Daten.

In der zweiten Form kann der Zeiger nicht geändert werden. Der Zeiger zeigt immer auf dieselbe Stelle.

Thomas Matthews
quelle
3

Der Unterschied besteht darin, dass constder Programmierer ohne das Extra innerhalb der Methode ändern kann, auf die der Zeiger zeigt. beispielsweise:

 void print_string(const char * the_string)
 {
    cout << the_string << endl;
    //....
    the_string = another_string();
    //....

 }

Das wäre stattdessen illegal, wenn die Unterschrift wäre void print_string(const char * const the_string)

Viele Programmierer empfinden das zusätzliche constSchlüsselwort (in den meisten Szenarien) als zu ausführlich und lassen es weg, obwohl es semantisch korrekt wäre.

leonbloy
quelle
2

In letzterem Fall garantieren Sie, dass sowohl der Zeiger als auch das Zeichen nicht geändert werden. Im ersten Fall garantieren Sie nur, dass sich der Inhalt nicht ändert, aber Sie können den Zeiger bewegen

Jesus Ramos
quelle
Ahh, ohne die letzte Konstante könnte ich den Zeiger tatsächlich so einstellen, dass er auf eine ganz andere Zeichenfolge zeigt?
Bild
Ja, ohne diese letzte Konstante können Sie den Parameterzeiger verwenden, um eine Iteration durch Zeigerarithmetik durchzuführen. Wenn diese Konstante vorhanden wäre, müssten Sie einen eigenen Zeiger erstellen, der eine Kopie dieses Parameters ist.
Jesus Ramos
2

Es gibt keinen Grund, warum einer von beiden nicht funktionieren würde. Allesprint_string() tun müssen, ist den Wert auszudrucken. Es wird nicht versucht, es zu ändern.

Es ist eine gute Idee, eine Funktion zu erstellen, die Markierungsargumente nicht als const ändert. Der Vorteil ist, dass Variablen, die sich nicht ändern können (oder die Sie nicht ändern möchten), fehlerfrei an diese Funktionen übergeben werden können.

In Bezug auf die genaue Syntax möchten Sie angeben, welche Art von Argumenten "sicher" sind, um an die Funktion übergeben zu werden.

Jonathan Wood
quelle
2

Ich denke, es ist selten relevant relevant, weil Ihre Funktion nicht mit Argumenten wie & * the_string oder ** the_string aufgerufen wird. Der Zeiger selbst ist ein Argument vom Typ Wert. Selbst wenn Sie ihn ändern, werden Sie die Kopie, die zum Aufrufen Ihrer Funktion verwendet wurde, nicht ändern. Die Version, die Sie anzeigen, stellt sicher, dass sich die Zeichenfolge nicht ändert, und ich denke, das ist in diesem Fall ausreichend.

Eglin
quelle
2

const char *bedeutet, dass Sie den Zeiger nicht verwenden können, um zu ändern, auf was gezeigt wird. Sie können den Zeiger jedoch so ändern, dass er auf etwas anderes zeigt.

Erwägen:

const char * promptTextWithDefault(const char * text)
{
    if ((text == NULL) || (*text == '\0'))
        text = "C>";
    return text;
}

Der Parameter ist ein nicht const char *konstanter Zeiger auf const char, sodass er in einen anderen Wert geändert werden kann (wie eine konstante Zeichenfolge). Wenn wir jedoch fälschlicherweise geschrieben haben*text = '\0' haben, wird ein Kompilierungsfehler angezeigt.

Wenn Sie nicht beabsichtigen, zu ändern, auf was der Parameter zeigt, können Sie den Parameter möglicherweise festlegen, dies ist const char * const textjedoch nicht üblich. Normalerweise erlauben wir Funktionen, die an Parameter übergebenen Werte zu ändern (da wir Parameter nach Wert übergeben, wirkt sich jede Änderung nicht auf den Aufrufer aus).

Übrigens: Es ist eine gute Praxis, dies zu vermeiden, char const *da es oft falsch verstanden wird - es bedeutet dasselbe wie const char *, aber zu viele Leute lesen es als Bedeutung char * const.

TonyR
quelle
Beeindruckend! Ich habe versucht, den Unterschied zwischen meiner const char *und der Unterschrift herauszufinden char const *- die Art und Weise, wie Sie Ihre BTW formuliert haben, hat wirklich geholfen!
Salbei
1

Fast alle anderen Antworten sind korrekt, aber sie übersehen einen Aspekt davon: Wenn Sie das Extra constfür einen Parameter in einer Funktionsdeklaration verwenden, ignoriert der Compiler es im Wesentlichen. Lassen Sie uns für einen Moment die Komplexität Ihres Beispiels als Zeiger ignorieren und einfach einen verwenden int.

void foo(const int x);

deklariert die gleiche Funktion wie

void foo(int x);

Nur in der Definition der Funktion ist das besonders constaussagekräftig:

void foo(const int x) {
    // do something with x here, but you cannot change it
}

Diese Definition ist mit einer der obigen Erklärungen vereinbar. Der Anrufer ist es egal, dass xistconst ein Implementierungsdetail, das am Anrufort nicht relevant ist.

Wenn Sie einen constZeiger auf constDaten haben, gelten dieselben Regeln:

// these declarations are equivalent
void print_string(const char * const the_string);
void print_string(const char * the_string);

// In this definition, you cannot change the value of the pointer within the
// body of the function.  It's essentially a const local variable.
void print_string(const char * const the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // COMPILER ERROR HERE
}

// In this definition, you can change the value of the pointer (but you 
// still can't change the data it's pointed to).  And even if you change
// the_string, that has no effect outside this function.
void print_string(const char * the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // OK, but not observable outside this func
}

Nur wenige C ++ - Programmierer machen sich die Mühe, Parameter zu consterstellen, selbst wenn dies möglich ist, unabhängig davon, ob diese Parameter Zeiger sind.

Adrian McCarthy
quelle
"Der Compiler wird es im Wesentlichen ignorieren" ist nicht immer wahr. Visual C ++ 2015 würde eine Warnung ausgeben, wenn Sie das Extra constzu Ihrem Funktionsparameter in der Definition hinzufügen, aber keine Deklaration.
Raymai97
@ raymai97: Ich denke, das ist ein Fehler im 2015er Compiler, aber ich habe 2015 nicht zum Testen zur Hand. Ich arbeite wie im Jahr 2017 beschrieben, was nach meinen Gesprächen mit einigen Standardexperten das erwartete Verhalten ist.
Adrian McCarthy
-1

Der Unterschied zwischen den beiden besteht darin, dass char * auf einen beliebigen Zeiger zeigen kann. Const char * verweist dagegen auf Konstanten, die im Abschnitt DATA der ausführbaren Datei definiert sind. Daher können Sie die Zeichenwerte einer const char * -String nicht ändern.

Maz
quelle
Ich frage nicht nach dem Unterschied zwischen char * und const char *. Ich
frage
Diese Antwort ist falsch. A const char*kann überall hin zeigen, wo es gefällt.
Cubic