Jaka jest różnica między char * const a const char *?


279

Jaka jest różnica pomiędzy:

char * const 

i

const char *


8
Pierwszą rzeczą po lewej stronie „const” jest to, co stałe. Jeśli „const” jest najdalej na lewo, to pierwszą rzeczą na prawo jest stała.
Cupcake

4
Jako przyjazną wskazówkę, nigdy nie zapominaj, że cdecl jest rzeczą.
Braden Best

Istnieje inny char const *, który jest zwracanym typem wyjątku :: what ()
Zhang

Odpowiedzi:


363

Różnica polega na tym, że const char *jest wskaźnikiem do const char, podczas gdy char * constjest stałym wskaźnikiem do char.

Po pierwsze, wskazanej wartości nie można zmienić, ale wskaźnik może być. Po drugie, wskazywana wartość może ulec zmianie, ale wskaźnik nie może (podobnie jak odwołanie).

Istnieje również

const char * const

który jest stałym wskaźnikiem do stałego char (więc nic o tym nie można zmienić).

Uwaga:

Następujące dwie formy są równoważne:

const char *

i

char const *

Dokładny powód tego jest opisany w standardzie C ++, ale ważne jest, aby pamiętać i unikać zamieszania. Znam kilka standardów kodowania, które wolą:

char const

nad

const char

(ze wskaźnikiem lub bez), aby położenie constelementu było takie samo jak ze wskaźnikiem const.


6
Czy warto zauważyć, co się stanie, jeśli w tej samej deklaracji podano wiele zmiennych? Uważam, const int *foo,*bar;że zarówno zadeklarować fooi barbyć int const *, lecz int const *foo, *barby oświadczyć foosię int const *i barbyć int *. Myślę, typedef int * intptr; const intptr foo,bar;że zadeklarowałbym obie zmienne int * const; Nie znam żadnego sposobu użycia deklaracji połączonej do utworzenia dwóch zmiennych tego typu bez typedef.
supercat

1
@supercat I believe const int *foo,*bar; would declare both foo and bar to be int const *: Tak. but int const *foo, *bar would declare foo to be a int const * and bar to be int *: Nie! Byłoby dokładnie tak samo jak w poprzednim przypadku. (Zobacz ideone.com/RsaB7n, gdzie pojawia się ten sam błąd zarówno dla Foo, jak i dla paska). I think typedef int * intptr; const intptr foo,bar; would declare both variables to be int * const: Tak. I don't know any way to use a combined declaration to create two variables of that type without a typedefCóż, int *const foo, *const bar;. Składnia deklaratora C ...
gx_

@gx_: Więc się myliłem - moja niepewność była powodem, dla którego zasugerowałem, że pomocne może być powiedzenie, jakie są reguły. Co byś int const *foo, *volatile barzrobił bar? Zrób to jednocześnie consti volatile? Czyste rozdzielenie Tęsknię Pascala nazw zadeklarowane-zmiennych i ich rodzaje (wskaźnik do tablicy wskaźników do liczb całkowitych będzie var foo: ^Array[3..4] of ^Integer.;. `To byłoby trochę śmieszne zagnieżdżonych w nawiasach, co w C, myślę
SuperCat

3
@ superupcat (och, tylko C, przepraszam za link do kodu C ++, dostałem się tutaj z pytania w C ++) Chodzi o składnię deklaracji C , z częścią typu „czystego”, po której następuje deklarator . W „ int const *foo, *volatile bar” część typu to int const(zatrzymuje się przed *), a deklaratory to *foo(wyrażenie *foobędzie oznaczać an int const) i *volatile bar; czytanie od prawej do lewej (dobra zasada dla kwalifikatorów cv ), foojest wskaźnikiem do const int i barjest zmiennym wskaźnikiem do const int (sam wskaźnik jest zmienny, wskazany int jest [dostępny jako] const).
gx_

@supercat A jeśli chodzi o „wskaźnik do tablicy wskaźniki do liczb” (nie wiem, Pascal, nie jestem pewien co do [3..4]składni, więc weźmy tablicę 10 elementów) int *(*foo)[10];. Odzwierciedla swoje (przyszłe) użycie jako wyrażenia: *(*foo)[i](z iliczbą całkowitą w zakresie [0, 10)tj. [0, 9]) Najpierw wyrejestruje się, fooaby dostać się do tablicy, a następnie uzyska dostęp do elementu z indeksem i(ponieważ postfiks []wiąże się mocniej niż przedrostek *), a następnie wyrejestrowuje ten element, w końcu dając int(patrz ideone.com/jgjIjR ). Ale typedefto ułatwia (patrz ideone.com/O3wb7d ).
gx_

102

Aby uniknąć zamieszania, zawsze dołączaj kwalifikator const.

int       *      mutable_pointer_to_mutable_int;
int const *      mutable_pointer_to_constant_int;
int       *const constant_pointer_to_mutable_int;
int const *const constant_pointer_to_constant_int;

10
Czemu? „Aby uniknąć nieporozumień” nie wyjaśnia mi, co to za zamieszanie.
Andrew Weir,

14
@Andrew: Sugerowałem spójność, a tym samym czytelność. Piszę wszystkie kwalifikatory typu, aby modyfikowały to, co jest po ich lewej stronie, zawsze tego używam.
diapir

1
Właściwie to najlepsza odpowiedź na ten temat, którą znalazłem w SO
Trap

8
Jako standard kodu rzadko spotykałem się z tym stylem i dlatego raczej go nie przyjmę. Jednak jako narzędzie edukacyjne ta odpowiedź była bardzo pomocna! (Myślę, że szkoda, że ​​nie jest to bardziej powszechny styl.)
natevw

8
@Alla: pnie odnosi się do typu: (const int *const). Na lepsze lub gorsze (gorsze, jeśli mnie pytasz) kwalifikator const, zarówno w C, jak i C ++, ma być postfix: cf const funkcja składowa void foo(int a) const;. Możliwość zadeklarowania const intjest wyjątkiem, a nie regułą.
diapir

44

const zawsze modyfikuje rzecz, która jest przed nią (po lewej stronie), Z WYJĄTKIEM, gdy jest to pierwsza rzecz w deklaracji typu, gdzie modyfikuje rzecz, która pojawia się po niej (po prawej stronie).

Te dwa są takie same:

int const *i1;
const int *i2;

definiują wskaźniki do const int. Możesz zmienić miejsce i1i i2punkty, ale nie możesz zmienić wartości, na którą wskazują.

To:

int *const i3 = (int*) 0x12345678;

definiuje constwskaźnik do liczby całkowitej i inicjuje go, aby wskazywał na miejsce w pamięci 12345678. Możesz zmienić intwartość pod adresem 12345678, ale nie możesz zmienić adresu, który i3wskazuje.



18

const char*jest wskaźnikiem do stałego znaku
char* constjest stałym wskaźnikiem do znaku
const char* constjest stałym wskaźnikiem do stałego znaku


9

Ogólna zasada: przeczytaj definicję od prawej do lewej!


const int *foo;

Oznacza „ foowskazuje ( *) na coś int, czego nie można zmienić ( const)”.
Dla programisty oznacza to: „Nie zmienię wartości tego, co foowskazuje”.

  • *foo = 123;lub foo[0] = 123;byłby nieprawidłowy.
  • foo = &bar; jest dozwolone.

int *const foo;

Oznacza „ foonie można zmienić ( const) i wskazuje ( *) na int”.
Dla programisty oznacza to „Nie zmienię adresu pamięci, który foodotyczy”.

  • *foo = 123;lub foo[0] = 123;jest dozwolone.
  • foo = &bar; byłby nieważny.

const int *const foo;

Oznacza „ foonie można zmienić ( const) i wskazuje ( *) na„ intnie można zmienić ( const) ”.
Dla programisty oznacza to: „Nie zmienię wartości tego, na co foowskazuje, ani nie zmienię adresu, który fooodnosi się do”.

  • *foo = 123;lub foo[0] = 123;byłby nieprawidłowy.
  • foo = &bar; byłby nieważny.

8
  1. const char * x Tutaj X jest zasadniczo wskaźnikiem znaku, który wskazuje na stałą wartość

  2. char * const x odnosi się do wskaźnika znaku, który jest stały, ale położenie, które wskazuje, można zmienić.

  3. const char * const x jest kombinacją 1 i 2, co oznacza, że ​​jest stałym wskaźnikiem znaku, który wskazuje na stałą wartość.

  4. const * char x spowoduje błąd kompilatora. nie można tego zadeklarować.

  5. char const * x jest równy pkt 1.

ogólna zasada jest taka, że ​​jeśli const ma nazwę var, wówczas wskaźnik będzie stały, ale położenie wskazujące można zmienić , w przeciwnym razie wskaźnik będzie wskazywał stałe położenie, a wskaźnik może wskazywać inne położenie, ale zawartości wskazanego położenia nie można zmienić .


1
„char * const x odnosi się do wskaźnika znaku, który jest stały, ale położenie, które wskazuje, można zmienić.” Źle. Wartość w lokalizacji można zmienić, a nie samą lokalizację.
Proszę o pomoc

3

Pierwszy to błąd składniowy. Może miałeś na myśli różnicę między

const char * mychar

i

char * const mychar

W takim przypadku pierwszy to wskaźnik do danych, których nie można zmienić, a drugi to wskaźnik, który zawsze wskazuje ten sam adres.


3

Kolejną zasadą jest sprawdzenie, gdzie jest const :

  1. przed * => przechowywana wartość jest stała
  2. po * => sam wskaźnik jest stały

3

Wiele odpowiedzi zawiera konkretne techniki, kciuki itp., Aby zrozumieć ten konkretny przypadek deklaracji zmiennej. Ale istnieje ogólna technika rozumienia każdej deklaracji:

Reguła zgodna z ruchem wskazówek zegara / spiralna

ZA)

const char *a;

Zgodnie z regułą azgodną z ruchem wskazówek zegara / spirala jest stały wskaźnik do znaku. Co oznacza, że ​​znak jest stały, ale wskaźnik może się zmienić. tzn. a = "other string";jest w porządku, ale a[2] = 'c';się nie skompiluje

B)

char * const a;

Zgodnie z regułą ajest stałym wskaźnikiem do znaku. tzn. możesz zrobić, a[2] = 'c';ale nie możesza = "other string";


1
Znany również jako zasada prawa-lewa (przynajmniej tak się nauczyłem): jdurrett.ba.ttu.edu/3345/handouts/RL-rule.html
Tomas Pruzina

(Byłoby znacznie lepiej, gdyby istota odpowiedzi nie była ukryta za linkiem, a tekst tutaj nawet nie powoływał się, ani przynajmniej nie odwoływał się do żadnej z jego szczegółów, poza ogólną „zgodnie z regułą”.)
Sz.

@Sz. Czy masz jakieś konkretne zamieszanie, które mogę usunąć? Po znajomości reguły naprawdę niewiele w niej jest.
PnotNP

1

Zakładam, że masz na myśli const char * i char * const.

Pierwszy, const char *, jest wskaźnikiem do stałego znaku. Sam wskaźnik jest zmienny.

Drugi char * const jest stałym wskaźnikiem do znaku. Wskaźnik nie może się zmienić, znak, na który wskazuje, może.

A potem jest const char * const, w którym wskaźnik i znak nie mogą się zmienić.


Pierwsze dwa są w rzeczywistości takie same, a trzeci to błąd kompilatora :)
workmad3

1

Oto szczegółowe wyjaśnienie dotyczące kodu

/*const char * p;
char * const p; 
const char * const p;*/ // these are the three conditions,

// const char *p;const char * const p; pointer value cannot be changed

// char * const p; pointer address cannot be changed

// const char * const p; both cannot be changed.

#include<stdio.h>

/*int main()
{
    const char * p; // value cannot be changed
    char z;
    //*p = 'c'; // this will not work
    p = &z;
    printf(" %c\n",*p);
    return 0;
}*/

/*int main()
{
    char * const p; // address cannot be changed
    char z;
    *p = 'c'; 
    //p = &z;   // this will not work
    printf(" %c\n",*p);
    return 0;
}*/



/*int main()
{
    const char * const p; // both address and value cannot be changed
    char z;
    *p = 'c'; // this will not work
    p = &z; // this will not work
    printf(" %c\n",*p);
    return 0;
}*/

@reese moore Dziękuję.
Megharaj,

1
// Some more complex constant variable/pointer declaration.
// Observing cases when we get error and warning would help
// understanding it better.

int main(void)
{
  char ca1[10]= "aaaa"; // char array 1
  char ca2[10]= "bbbb"; // char array 2

  char *pca1= ca1;
  char *pca2= ca2;

  char const *ccs= pca1;
  char * const csc= pca2;
  ccs[1]='m';  // Bad - error: assignment of read-only location ‘*(ccs + 1u)’
  ccs= csc;    // Good

  csc[1]='n';  // Good
  csc= ccs;    // Bad - error: assignment of read-only variable ‘csc’

  char const **ccss= &ccs;     // Good
  char const **ccss1= &csc;    // Bad - warning: initialization from incompatible pointer type

  char * const *cscs= &csc;    // Good
  char * const *cscs1= &ccs;   // Bad - warning: initialization from incompatible pointer type

  char ** const cssc=   &pca1; // Good
  char ** const cssc1=  &ccs;  // Bad - warning: initialization from incompatible pointer type
  char ** const cssc2=  &csc;  // Bad - warning: initialization discards ‘const’
                               //                qualifier from pointer target type

  *ccss[1]= 'x'; // Bad - error: assignment of read-only location ‘**(ccss + 8u)’
  *ccss= ccs;    // Good
  *ccss= csc;    // Good
  ccss= ccss1;   // Good
  ccss= cscs;    // Bad - warning: assignment from incompatible pointer type

  *cscs[1]= 'y'; // Good
  *cscs= ccs;    // Bad - error: assignment of read-only location ‘*cscs’
  *cscs= csc;    // Bad - error: assignment of read-only location ‘*cscs’
  cscs= cscs1;   // Good
  cscs= cssc;    // Good

  *cssc[1]= 'z'; // Good
  *cssc= ccs;    // Bad - warning: assignment discards ‘const’
                 //                qualifier from pointer target type
  *cssc= csc;    // Good
  *cssc= pca2;   // Good
  cssc= ccss;    // Bad - error: assignment of read-only variable ‘cssc’
  cssc= cscs;    // Bad - error: assignment of read-only variable ‘cssc’
  cssc= cssc1;   // Bad - error: assignment of read-only variable ‘cssc’
}

1
  1. Stały wskaźnik : Stały wskaźnik może wskazywać tylko jedną zmienną odpowiedniego typu danych podczas całego programu. Możemy zmienić wartość zmiennej wskazywanej przez wskaźnik. Inicjalizacji należy dokonać podczas samego zgłoszenia.

Składnia:

datatype *const var;

char *const wchodzi w zakres tej sprawy.

/*program to illustrate the behaviour of constant pointer */

#include<stdio.h>
int main(){
  int a=10;
  int *const ptr=&a;
  *ptr=100;/* we can change the value of object but we cannot point it to another variable.suppose another variable int b=20; and ptr=&b; gives you error*/
  printf("%d",*ptr);
  return 0;
}
  1. Wskaźnik do stałej wartości : wskaźnik może wskazywać dowolną liczbę zmiennych odpowiedniego typu, ale nie możemy zmienić wartości obiektu wskazywanego przez wskaźnik w tym konkretnym czasie.

Składnia:

const datatype *varlub datatype const *var

const char* wchodzi w zakres tej sprawy.

/* program to illustrate the behavior of pointer to a constant*/

   #include<stdio.h>
   int main(){
       int a=10,b=20;
       int const *ptr=&a;
       printf("%d\n",*ptr);
       /*  *ptr=100 is not possible i.e we cannot change the value of the object pointed by the pointer*/
       ptr=&b;
       printf("%d",*ptr);
       /*we can point it to another object*/
       return 0;
    }

1

char * const i const char *?

  1. Wskazując na stałą wartość

const char * p; // wartości nie można zmienić

  1. Stały wskaźnik do wartości

char * const p; // adresu nie można zmienić

  1. Stały wskaźnik do stałej wartości

const char * const p; // oba nie mogą zostać zmienione.


1

constModyfikator stosuje się określenie natychmiast po jego lewej stronie. Jedynym wyjątkiem jest sytuacja, gdy nie ma nic po jego lewej stronie, to dotyczy to tego, co znajduje się bezpośrednio po jego prawej stronie.

Są to wszystkie równoważne sposoby powiedzenia „stały wskaźnik do stałej char”:

  • const char * const
  • const char const *
  • char const * const
  • char const const *

Czy to zależy od kompilatora? gcc produkuje dla „const char const *” i „const const char *” i „char const const *” ten sam wynik -> wskaźnik może wskazywać w inne miejsce.
cosinus0

1

Dwie zasady

  1. If const is between char and *, it will affect the left one.
  2. If const is not between char and *, it will affect the nearest one.

na przykład

  1. char const *. This is a pointer points to a constant char.
  2. char * const. This is a constant pointer points to a char.

1

Chciałbym zaznaczyć, że użycie int const *(lub const int *) nie polega na wskaźniku wskazującym na const intzmienną, ale że ta zmienna jest constprzeznaczona dla tego konkretnego wskaźnika.

Na przykład:

int var = 10;
int const * _p = &var;

Powyższy kod kompiluje się doskonale. _pwskazuje na constzmienną, chociaż varsama nie jest stała.


1

Pamiętam z czeskiej książki o C: przeczytaj deklarację, że zaczynasz od zmiennej i idziesz w lewo. Więc dla

char * const a;

możesz odczytać jako: „ azmienna typu stałego wskaźnika do char”,

char const * a;

możesz przeczytać jako: " ajest wskaźnikiem do stałej zmiennej typu char. Mam nadzieję, że to pomoże.

Premia:

const char * const a;

Będziesz czytał tak jak astały wskaźnik do stałej zmiennej typu char.

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.