Unterschied zwischen char * und const char *?


176

Was ist der Unterschied zwischen

char* name

was auf ein konstantes String-Literal zeigt, und

const char* name

Was meinst du mit " konstantes String-Literal" in C (nicht C ++)
gbulmer

1
... char * Name kann gemacht werden, um auf ein konstantes String-Literal zu verweisen
Iceman

Die Konstante im "Konstanten-String-Literal" ist redundant, da alle String-Literale theoretisch konstante Entitäten sind. Es ist der Inhalt der Variablen, der entweder konstant oder veränderbar gemacht werden kann. Die "const" -Deklaration löst einfach einen Fehler bei der Kompilierung aus, wenn Sie versuchen, den Inhalt des Zeichens zu ändern, auf das "name"
Cupcake

Einfach: Der Name "char * name" ist ein Zeiger auf char, dh beide können hier geändert werden. Der Name "const char * name" ist ein Zeiger auf const char, dh der Zeiger kann sich ändern, aber nicht char.
akD

Lesen Sie diese Dinge von rechts nach links.
Jiapeng Zhang

Antworten:


405

char*ist ein veränderbarer Zeiger auf ein veränderliches Zeichen / eine veränderbare Zeichenfolge.

const char*ist ein veränderbarer Zeiger auf ein unveränderliches Zeichen / eine unveränderliche Zeichenfolge. Sie können den Inhalt der Position (en), auf die dieser Zeiger zeigt, nicht ändern. Außerdem müssen Compiler Fehlermeldungen ausgeben, wenn Sie dies versuchen. Aus dem gleichen Grund ist die Umstellung von const char *auf char*veraltet.

char* constist ein unveränderlicher Zeiger (er kann nicht auf einen anderen Ort zeigen), aber der Inhalt des Ortes, auf den er zeigt, ist veränderlich .

const char* constist ein unveränderlicher Zeiger auf ein unveränderliches Zeichen / eine unveränderliche Zeichenfolge.


4
Verwirrung kann durch die Verwendung einer Variablen nach den oben genannten Anweisungen und durch Bezugnahme auf diese Variable behoben werden.
ankit.karwasra

3
@ ankit.karwasra, Sie haben noch eines verpasst:char const *
Pacerier

Ich nehme an, zwei Optionen mit veränderlichen Zeichen / Zeichenfolgen sind sehr gefährlich, da Sie einen Segmetationsfehlerspeicher erstellen könnten und wenn Sie wirklich schlau sind, könnten Sie den Computer hacken. Aus diesem Grund haben Compiler in diesen Implementierungen, glaube ich, immer Warnungen angezeigt
Daniel N.

1
Wird die Mutation char *beim Ausführen keinen Segmentierungsfehler verursachen?
Divyanshu Maithani

1
Also benutze ich, constwenn ich möchte, dass der Compiler einen Fehler macht, wenn ich die Daten versehentlich vergessen und geändert habe, oder?
Buchhalter م

43
char *name

Sie können das Zeichen ändern, auf das gezeigt wird name, und auch das Zeichen, auf das es zeigt.

const char* name

Sie können das Zeichen auf welche namePunkte ändern, aber Sie können das Zeichen, auf das es zeigt, nicht ändern.
Korrektur: Sie können den Zeiger ändern, aber nicht das Zeichen, auf das nameverweist ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , siehe "Beispiele"). ). In diesem Fall gilt der constBezeichner fürchar und nicht das Sternchen.

Gemäß der MSDN - Seite und http://en.cppreference.com/w/cpp/language/declarations , die constvor dem *Teil der Decl-Spezifikationsfolge, während der constnach *Teil des declarator ist.
Auf eine Deklarationsspezifiziererfolge können mehrere Deklaratoren folgen, weshalb as und as const char * c1, c2deklariert werdenc1const char *c2const char .

BEARBEITEN:

Aus den Kommentaren geht hervor, dass Ihre Frage nach dem Unterschied zwischen den beiden Deklarationen fragt, wenn der Zeiger auf ein Zeichenfolgenliteral zeigt.

In diesem Fall Sie sollten nicht das Zeichen , auf die ändern namePunkte, wie es in Folge konnte nicht definiertes Verhalten . String-Literale können in schreibgeschützten Speicherbereichen zugewiesen werden (Implementierung definiert), und ein Benutzerprogramm sollte sie ohnehin nicht ändern. Jeder Versuch, dies zu tun, führt zu undefiniertem Verhalten.

Der einzige Unterschied in diesem Fall (bei Verwendung mit String-Literalen) besteht darin, dass die zweite Deklaration Ihnen einen leichten Vorteil verschafft. Compiler geben normalerweise eine Warnung aus, wenn Sie im zweiten Fall versuchen, das Zeichenfolgenliteral zu ändern.

Online-Beispielbeispiel:

#include <string.h>
int main()
{
    char *str1 = "string Literal";
    const char *str2 = "string Literal";
    char source[] = "Sample string";

    strcpy(str1,source);    //No warning or error, just Undefined Behavior
    strcpy(str2,source);    //Compiler issues a warning

    return 0;
}

Ausgabe:

cc1: Warnungen werden als Fehler behandelt
prog.c: In Funktion 'main':
prog.c: 9: Fehler: Übergabe von Argument 1 von 'strcpy' verwirft Qualifizierer vom Zeigerzieltyp

Beachten Sie, dass der Compiler für den zweiten Fall warnt, nicht jedoch für den ersten.


Danke. Ich habe mit dem konstanten String-Literal gemischt, das wie folgt definiert ist: char * name = "String-Literal"; Das Ändern von "String Literal" ist undefiniert.
Iceman

@ user1279782: Err, warte! Sprechen Sie hier über Punkte, die auf String-Literale verweisen? In diesem Fall sollten Sie dasname Zeichen, auf das die Punkte zeigen, in beiden Fällen nicht ändern. Dies könnte zu UB führen.
Alok Save

Ja, das war der Punkt. In diesem Fall verhalten sich char * name und const char * name also ähnlich, oder?
Iceman

4
Diese Antwort ist entweder äußerst zweideutig oder einfach falsch. Ich würde interpretieren "Sie können das Zeichen nicht ändern, auf das der Name zeigt, aber Sie können das Zeichen ändern, auf das es zeigt." Da nicht in der Lage ist , den Zeiger selbst, zu modifizieren , aber die Speicherstelle zu ändern , in der Lage , dass es auf, die nicht korrekt ist: ideone.com/6lUY9s alternativ für reines C: ideone.com/x3PcTP
shroudednight

1
@shroudednight: Sie müssen etwas mehr über undefinierte Verhaltensweisen lernen und unterscheiden zwischen: erlaubt und sollte nicht durchgeführt werden. :)
Alok Save

16
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)

constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error

constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok

// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";

lcharp[0] = 'X';      // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error

// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X';          // compile error
((char*)astr)[0] = 'X'; // ok

1
Keiner Ihrer Zeiger verweist gemäß der Frage auf "konstante Zeichenfolgenliterale".
Café

Es ist erwähnenswert, dass das Ändern des char *Werts einen Segmentierungsfehler verursacht, da wir versuchen, ein Zeichenfolgenliteral zu ändern (das im Nur-Lese-Speicher vorhanden ist)
Divyanshu Maithani

10

In keinem Fall können Sie ein Zeichenfolgenliteral ändern, unabhängig davon, ob der Zeiger auf dieses Zeichenfolgenliteral als char *oder deklariert ist const char *.

Der Unterschied besteht jedoch darin, dass const char *der Compiler eine Diagnose geben muss, wenn Sie versuchen, den Zeigerwert zu ändern. Wenn der Zeiger dies ist, char *ist dies jedoch nicht der Fall.


1
"In keinem Fall können Sie ein Zeichenfolgenliteral ändern, unabhängig davon, ob ... [es] als char * oder const char * deklariert ist." Ich stimme zu, dass der Programmierer es nicht versuchen sollte, aber sagen Sie, dass jeder C-Compiler auf jedem Plattform wird den Code ablehnen, dafür sorgen, dass der Code zur Laufzeit fehlschlägt, oder etwas anderes? Ich glaube, eine Datei könnte die Definition und Initialisierung haben, und eine andere Datei könnte enthalten extern ... nameund haben *name = 'X';. Auf einem 'richtigen Betriebssystem' könnte dies fehlschlagen, aber auf eingebetteten Systemen würde ich erwarten, dass es etwas Plattform- / Compilerspezifisches tut.
Gbulmer

@gbulmer: Sie können ein Zeichenfolgenliteral in einem korrekten C-Programm nicht ändern. Was ein falsches C-Programm, das versucht, dazu führen kann, ist weder hier noch da.
Café

@gbulmer: Eine nützliche Definition ist ein Programm, das keine im C-Sprachstandard festgelegten Einschränkungen verletzt. Mit anderen Worten, ein Programm, das ein Zeichenfolgenliteral ändert, ist genauso falsch wie eines, das einen Nullzeiger dereferenziert oder eine Division durch 0 durchführt, ist falsch.
Café

caf - Ich dachte, das könnten Sie meinen. Dann scheint "In keinem Fall können Sie ein String-Literal ändern" zu viel zu sagen. Es wäre richtig zu sagen: "In beiden Fällen wurden die vom C-Sprachstandard festgelegten Einschränkungen verletzt, unabhängig davon ... Es ist dem Compiler oder dem Laufzeitsystem nicht in allen Fällen möglich, Verstöße gegen den Standard zu identifizieren." Ich gehe davon aus, dass der Standard die Position einnimmt, dass der Effekt undefiniert ist.
Gbulmer

1
Wenn ein Standard so oder so nichts behaupten kann, scheint es genau die richtige Grenze und hilfreich zu sein, Verhalten als "undefiniert" zu definieren. Um die Beziehung zu behaupten, dass ein 'korrektes C-Programm' einen Nullzeiger nicht dereferenzieren kann , klingt dies gleichbedeutend mit dem Beweis des Halteproblems. Aber es macht mir nichts aus. Ich würde es nicht tun und erwarten, damit "
schottfrei

4

FALL 1:

char *str = "Hello";
str[0] = 'M'  //Warning may be issued by compiler, and will cause segmentation fault upon running the programme

Die obigen Sätze str zeigen auf den Literalwert "Hello", der im Binärbild des Programms fest codiert ist und im Speicher als schreibgeschützt gekennzeichnet ist. Dies bedeutet, dass jede Änderung in diesem String-Literal unzulässig ist und Segmentierungsfehler verursachen würde.

FALL 2:

const char *str = "Hello";
str[0] = 'M'  //Compile time error

Fall 3:

char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".

2

Das erste können Sie tatsächlich ändern, wenn Sie möchten, das zweite können Sie nicht. Informieren Sie sich über die constRichtigkeit (es gibt einige nette Anleitungen zum Unterschied). Es gibt auch char const * nameStellen, an denen Sie es nicht neu festlegen können.


Was genau kann sich ändern?
Antti Haapala

2

Die Frage ist, was der Unterschied zwischen ist

char *name

was auf ein konstantes String-Literal zeigt, und

const char *cname

Dh gegeben

char *name = "foo";

und

const char *cname = "foo";

Es gibt keinen großen Unterschied zwischen der 2 und beide können als richtig angesehen werden. Durch die lange Tradition von C - Code haben die Stringliterale eine Art hatten char[], nicht const char[], und es gibt viele älteren Code, der ebenfalls akzeptieren char *statt const char *, auch wenn sie die Argumente nicht ändern.

Der Hauptunterschied der 2 im Allgemeinen besteht darin, dass *cnameoder cname[n]wird zu l-Werten des Typs ausgewertet const char, wohingegen *nameoder name[n]zu l-Werten des Typs ausgewertet wird char, die modifizierbare l-Werte sind . Ein konformer Compiler muss eine Diagnosemeldung erstellen, wenn das Ziel der Zuweisung kein veränderbarer Wert ist . Bei der Zuordnung zu lWerten des Typs muss keine Warnung ausgegeben werden char:

name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostics message

Der Compiler ist nicht erforderlich , die Zusammenstellung in jedem Fall zu stoppen; es reicht aus, dass es eine Warnung für die Zuordnung zu erzeugt cname[0]. Das resultierende Programm ist kein korrektes Programm. Das Verhalten des Konstrukts ist undefiniert . Es kann abstürzen oder noch schlimmer, es kann nicht abstürzen und das String-Literal im Speicher ändern.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.