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.
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.
constchar* 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.
#include<string.h>int main(){char*str1 ="string Literal";constchar*str2 ="string Literal";char source[]="Sample string";
strcpy(str1,source);//No warning or error, just Undefined Behavior
strcpy(str2,source);//Compiler issues a warningreturn0;}
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";constchar* constcharp = mystring;// (1)charconst* 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 literalschar* lcharp ="My string literal";constchar* lconstcharp ="My string literal";
lcharp[0]='X';// Segmentation fault (crash) during run-time
lconstcharp[0]='X';// compile error// *not* a string literalconstchar astr[101]="My mutable string";
astr[0]='X';// compile error((char*)astr)[0]='X';// ok
Ż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.
„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:
constchar*str ="Hello";
str[0]='M'//Compile time error
PRZYPADEK 3:
char str[]="Hello";
str[0]='M';// legal and change the str = "Mello".
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ć.
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.
Odpowiedzi:
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 zconst char *
nachar*
jest przestarzała.char* const
jest niezmiennym wskaźnikiem (nie może wskazywać na żadną inną lokalizację), ale zawartość lokalizacji, na którą wskazuje, jest zmienna .const char* const
jest niezmiennym wskaźnikiem do niezmiennego znaku / ciągu.źródło
char const *
char *
błędu segmentacji podczas pracy?const
jeśli chcę, aby kompilator dał błąd, jeśli zapomniałem i przez pomyłkę zmieniłem dane, prawda?Możesz zmienić znak, na który
name
wskazuje, a także znak, na który wskazuje.Możesz zmienić znak, na któryname
wskazuje, ale nie możesz zmodyfikować znaku, na który wskazuje.korekta: Możesz zmienić wskaźnik, ale nie znak, który
name
wskazuje na ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , zobacz „Przykłady” ). W tym przypadkuconst
specyfikator dotyczychar
, a nie gwiazdki.Zgodnie ze stroną MSDN i http://en.cppreference.com/w/cpp/language/declarations ,
const
przed*
jest częścią sekwencji specyfikatora deklaracji , podczas gdyconst
po*
jest częścią deklaratora.Po sekwencji specyfikatora deklaracji może następować wiele deklaratorów, dlatego
const char * c1, c2
deklaruje sięc1
jakoconst char *
ic2
asconst 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
name
wskazuje, 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:
Wynik:
Zauważ, że kompilator ostrzega przed drugim przypadkiem, ale nie w pierwszym.
źródło
name
wskazują punkty w obu przypadkach, może to spowodować UB.źródło
char *
wartości powoduje błąd segmentacji, ponieważ próbujemy zmodyfikować literał ciągu (który jest obecny w pamięci tylko do odczytu)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 *
czyconst 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.źródło
extern ... name
i 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.PRZYPADEK 1:
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:
PRZYPADEK 3:
źródło
Pierwszą możesz zmienić, jeśli chcesz, drugą nie możesz. Przeczytaj o
const
poprawności (jest kilka fajnych poradników na temat różnicy). Jest też miejsce, wchar const * name
którym nie możesz go zmienić.źródło
Pytanie brzmi, jaka jest różnica między
co wskazuje na stały literał ciągu i
To znaczy dane
i
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[]
, nieconst char[]
, i istnieje wiele starszych kodów, które podobnie akceptująchar *
zamiastconst char *
, nawet jeśli nie modyfikują argumentów.Zasadnicza różnica między 2 w ogólności polega na tym, że
*cname
lubcname[n]
będzie oceniać do lwartości typuconst char
, podczas gdy*name
lubname[n]
będzie oceniać do lwartości typuchar
, 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 typuchar
: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.źródło
W rzeczywistości
char* name
nie jest wskaźnikiem do stałej, ale wskaźnikiem do zmiennej. Możesz mówić o tym innym pytaniu.Jaka jest różnica między char * const i const char *?
źródło