Różnica między char * i const char *?


177

Jaka jest różnica pomiędzy

char* name

co wskazuje na stały literał ciągu i

const char* name

co masz na myśli przez „ stały ciąg literału” w C (nie C ++)
gbulmer

1
... char * name może wskazywać na stały literał ciągu
Iceman

stała w „stałym literale łańcuchowym” jest zbędna, ponieważ wszystkie literały łańcuchowe są teoretycznie stałymi jednostkami. To zawartość zmiennej może być stała lub zmienna. Deklaracja „const” po prostu zwróci błąd czasu kompilacji, jeśli spróbujesz zmienić zawartość znaku wskazywanego przez „name”
Cupcake.

Proste: nazwa "char * name" jest wskaźnikiem do znaku, tzn. Obie można tutaj zmienić. "const char * name" nazwa jest wskaźnikiem do znaku stałego, tj. wskaźnik może się zmieniać, ale nie znak.
akD

Przeczytaj te rzeczy od prawej do lewej.
Jiapeng Zhang

Odpowiedzi:


406

char*jest zmiennym wskaźnikiem do zmiennego znaku / łańcucha.

const char*jest zmiennym wskaźnikiem do niezmiennego znaku / ciągu. Nie można zmienić zawartości lokalizacji (miejsc), na które wskazuje ten wskaźnik. Ponadto kompilatory są zobowiązane do przekazywania komunikatów o błędach, gdy próbujesz to zrobić. Z tego samego powodu konwersja z const char *na char*jest przestarzała.

char* constjest niezmiennym wskaźnikiem (nie może wskazywać na żadną inną lokalizację), ale zawartość lokalizacji, na którą wskazuje, jest zmienna .

const char* constjest niezmiennym wskaźnikiem do niezmiennego znaku / ciągu.


4
Nieporozumienia można wyjaśnić za pomocą zmiennej po stwierdzeniach wspomnianych powyżej i podając odniesienie do tej zmiennej.
ankit.karwasra

3
@ ankit.karwasra, Przegapiłeś jeszcze jednego:char const *
Pacerier

Przypuszczam, że dwie opcje ze zmiennymi znakami / ciągami są bardzo niebezpieczne, ponieważ możesz zrobić pamięć błędów segmetacji, a jeśli jesteś naprawdę sprytny, możesz zhakować komputer. To dlatego kompilatory zawsze pokazywały ostrzeżenia w tych implementacjach, jak myślę
Daniel N.

1
Czy mutacja nie spowoduje char *błędu segmentacji podczas pracy?
Divyanshu Maithani

1
Więc używam, constjeśli chcę, aby kompilator dał błąd, jeśli zapomniałem i przez pomyłkę zmieniłem dane, prawda?
Księgowy م

43
char *name

Możesz zmienić znak, na który namewskazuje, a także znak, na który wskazuje.

const char* name

Możesz zmienić znak, na który namewskazuje, ale nie możesz zmodyfikować znaku, na który wskazuje.
korekta: Możesz zmienić wskaźnik, ale nie znak, który namewskazuje na ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , zobacz „Przykłady” ). W tym przypadku constspecyfikator dotyczy char, a nie gwiazdki.

Zgodnie ze stroną MSDN i http://en.cppreference.com/w/cpp/language/declarations , constprzed *jest częścią sekwencji specyfikatora deklaracji , podczas gdy constpo *jest częścią deklaratora.
Po sekwencji specyfikatora deklaracji może następować wiele deklaratorów, dlatego const char * c1, c2deklaruje się c1jako const char *i c2as const char.

EDYTOWAĆ:

Z komentarzy wynika, że ​​twoje pytanie dotyczy różnicy między tymi dwiema deklaracjami, gdy wskaźnik wskazuje na literał ciągu.

W takim przypadku nie należy modyfikować znaku, do którego namewskazuje, ponieważ może to spowodować niezdefiniowane zachowanie . Literały łańcuchowe mogą być alokowane w regionach pamięci tylko do odczytu (zdefiniowane w implementacji) i program użytkownika nie powinien w żaden sposób ich modyfikować. Każda taka próba skutkuje niezdefiniowanym zachowaniem.

Więc jedyną różnicą w tym przypadku (użycia z literałami łańcuchowymi) jest to, że druga deklaracja daje niewielką przewagę. Kompilatory zazwyczaj dają ostrzeżenie w przypadku próby zmodyfikowania literału ciągu w drugim przypadku.

Przykładowy przykład online:

#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;
}

Wynik:

cc1: ostrzeżenia traktowane jako błędy
prog.c: W funkcji „main”:
prog.c: 9: error: przekazanie argumentu 1 z „strcpy” usuwa kwalifikatory z typu docelowego wskaźnika

Zauważ, że kompilator ostrzega przed drugim przypadkiem, ale nie w pierwszym.


Dzięki ... mieszałem się ze stałym literałem łańcucha, który jest zdefiniowany jako: char * name = "String Literal"; Zmiana "String literal" jest niezdefiniowana ...
Iceman

@ user1279782: Err, czekaj! Czy mówisz tutaj o punktach wskazujących na literały strunowe? W takim przypadku nie powinieneś modyfikować znaku, na który namewskazują punkty w obu przypadkach, może to spowodować UB.
Alok Save

Tak, o to chodziło. Więc w takim przypadku char * name i const char * name zachowują się podobnie, prawda?
Iceman

4
Ta odpowiedź jest albo skrajnie niejednoznaczna, albo po prostu błędna. Zinterpretowałbym „Nie możesz zmienić znaku, na który wskazuje nazwa, ale możesz zmodyfikować znak, na który wskazuje”. Ponieważ nie jest w stanie zmodyfikować sam wskaźnik, ale jest w stanie zmienić lokalizację pamięci, który wskazuje na to, co jest nieprawidłowe: ideone.com/6lUY9s alternatywnie czystego C: ideone.com/x3PcTP
shroudednight

1
@shroudednight: Musisz dowiedzieć się trochę więcej o niezdefiniowanych zachowaniach i musisz dokonać rozróżnienia między: dozwolone i nie należy tego robić. :)
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
Żaden z twoich wskaźników nie wskazuje na „stałe literały łańcuchowe” zgodnie z pytaniem.
kawiarnia

Warto zauważyć, że zmiana char *wartości powoduje błąd segmentacji, ponieważ próbujemy zmodyfikować literał ciągu (który jest obecny w pamięci tylko do odczytu)
Divyanshu Maithani

10

W żadnym przypadku nie można modyfikować literału ciągu, niezależnie od tego, czy wskaźnik do tego literału ciągu jest zadeklarowany jako char *czy const char *.

Jednak różnica polega na tym, że jeśli wskaźnik jest, const char *kompilator musi podać diagnostykę, jeśli spróbujesz zmodyfikować wskazywaną wartość, ale jeśli wskaźnik jest, char *to nie.


1
„W żadnym przypadku nie możesz zmodyfikować literału ciągu, niezależnie od tego, czy ... [it] jest zadeklarowany jako char * czy const char *” Zgadzam się, że programista nie powinien próbować, ale czy mówisz, że każdy kompilator C na każdym platforma odrzuci kod, zaaranżuje niepowodzenie kodu w czasie wykonywania lub coś innego? Uważam, że jeden plik może mieć definicję i inicjalizację, a inny może zawierać extern ... namei mieć *name = 'X';. Na „odpowiednim systemie operacyjnym” może to się nie powieść, ale w systemach wbudowanych spodziewałbym się, że zrobi coś specyficznego dla platformy / kompilatora.
gbulmer

@gbulmer: Nie można modyfikować literału ciągu w poprawnym programie w C. Nie ma ani tu, ani tam, czego wynikiem może być niepoprawny program w C, który może powodować próby.
kawiarnia

@gbulmer: Jedną z przydatnych definicji jest program, który nie łamie żadnych ograniczeń określonych przez standard języka C. Innymi słowy, program modyfikujący literał łańcuchowy jest niepoprawny w taki sam sposób, jak program, który wyłuskuje wskaźnik zerowy lub wykonuje dzielenie przez 0, jest niepoprawny.
kawiarnia

caf - Pomyślałem, że to może być to, co masz na myśli. Wtedy „W żadnym przypadku nie można zmodyfikować literału ciągu” wydaje się być zbyt dużym określeniem. Prawidłowe byłoby stwierdzenie: „W obu przypadkach ograniczenia określone przez standard języka C zostały złamane, niezależnie od… Nie jest możliwe, aby kompilator lub system wykonawczy zidentyfikował naruszenia standardu we wszystkich przypadkach”. Zakładam, że standard przyjmuje stanowisko, że efekt jest nieokreślony?
gbulmer

1
Kiedy norma nie może niczego potwierdzić, myślę, że zdefiniowanie zachowania jako „niezdefiniowanego” wydaje się być właściwą granicą i pomocną. Aby potwierdzić relację, „poprawny program w C” „ nie może odwołać się do pustego wskaźnika”, brzmi równoważnie z udowodnieniem problemu zatrzymania. Ale nie mam nic przeciwko. Nie zrobiłbym tego i spodziewałbym się, że wyjdzie mi to na sucho
``

4

PRZYPADEK 1:

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

Powyższe ustawia str tak, aby wskazywało na wartość literału „Hello”, która jest zakodowana na stałe w binarnym obrazie programu, która jest oznaczona jako tylko do odczytu w pamięci, co oznacza, że ​​każda zmiana w tym literale String jest niedozwolona i powodowałaby błędy segmentacji.

PRZYPADEK 2:

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

PRZYPADEK 3:

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

2

Pierwszą możesz zmienić, jeśli chcesz, drugą nie możesz. Przeczytaj o constpoprawności (jest kilka fajnych poradników na temat różnicy). Jest też miejsce, w char const * namektórym nie możesz go zmienić.


Co dokładnie może się zmienić?
Antti Haapala

2

Pytanie brzmi, jaka jest różnica między

char *name

co wskazuje na stały literał ciągu i

const char *cname

To znaczy dane

char *name = "foo";

i

const char *cname = "foo";

Nie ma dużej różnicy między 2 i oba można uznać za poprawne. Ze względu na długą tradycję kodu C, literały łańcuchowe mają typ char[], nie const char[], i istnieje wiele starszych kodów, które podobnie akceptują char *zamiast const char *, nawet jeśli nie modyfikują argumentów.

Zasadnicza różnica między 2 w ogólności polega na tym, że *cnamelub cname[n]będzie oceniać do lwartości typu const char, podczas gdy *namelub name[n]będzie oceniać do lwartości typu char, które są modyfikowalnymi poziomami . Kompilator zgodny jest wymagany do wygenerowania komunikatu diagnostycznego, jeśli cel przypisania nie jest modyfikowalną lwartością ; nie musi generować żadnego ostrzeżenia o przypisaniu do lwartości typu char:

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

W żadnym przypadku kompilator nie musi zatrzymywać kompilacji; wystarczy, że wygeneruje ostrzeżenie przed przydziałem cname[0]. Wynikowy program nie jest poprawnym programem. Zachowanie konstrukcji jest nieokreślone . Może się zawiesić lub co gorsza, może się nie zawiesić i może zmienić literał ciągu w pamięci.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.