Przeglądam kilka przykładowych programów, aby ponownie zapoznać się z C ++ i napotkałem następujące pytanie. Po pierwsze, oto przykładowy kod:
void print_string(const char * the_string)
{
cout << the_string << endl;
}
int main () {
print_string("What's up?");
}
W powyższym kodzie parametr print_string mógłby być zamiast tego const char * const the_string
. Który byłby bardziej odpowiedni do tego?
Rozumiem, że różnica polega na tym, że jeden jest wskaźnikiem do stałego znaku, a drugi jest stałym wskaźnikiem do stałego znaku. Ale dlaczego oba te rozwiązania działają? Kiedy miałoby to znaczenie?
const char *
jest znacznie lepszy, ponieważconst
znajduje się po zupełnie przeciwnej stronie.Zmienny wskaźnik do zmiennego znaku
Zmienny wskaźnik do stałego znaku
Stały wskaźnik do zmiennego znaku
Stały wskaźnik do stałego znaku
źródło
const char* p; --> constant pointer to mutable character
ichar *const p; --> mutable pointer to constant character
const char * p
jako: „p jest wskaźnikiem do stałej znakowej” lub zmiennym wskaźnikiem do stałego znaku, jak poprawnie stwierdza James. to samo z drugim :).const char * const
środki wyżeł, jak również dane wskazywały na wskaźnik, są zarówno const!const char *
oznacza, że tylko dane wskazywane przez wskaźnik to const. Jednak sam wskaźnik nie jest stałą.Przykład.
źródło
(Wiem, że to jest stare, ale i tak chciałem się nimi podzielić.)
Chciałem tylko rozwinąć odpowiedź Thomasa Matthewsa. Reguła Right-Left Rule deklaracji typu C prawie mówi: kiedy czytasz deklarację typu C, zacznij od identyfikatora i idź w prawo, kiedy możesz, i w lewo, kiedy nie możesz.
Najlepiej wyjaśnić to na kilku przykładach:
Przykład 1
Zacznij od identyfikatora, nie możemy iść w prawo, więc idziemy w lewo
foo jest stałą ...
Kontynuuj w lewo
foo jest stałym wskaźnikiem do ...
Kontynuuj w lewo
foo jest stałym wskaźnikiem do char ...
Kontynuuj w lewo
foo jest stałym wskaźnikiem do stałej char (Complete!)
Przykład 2
Zacznij od identyfikatora, nie możemy iść w prawo, więc idziemy w lewo
foo jest stałą ...
Kontynuuj w lewo
foo jest stałym wskaźnikiem do ...
Kontynuuj w lewo
foo jest stałym wskaźnikiem do char (Complete!)
Przykład 1337
Zacznij od identyfikatora, ale teraz możemy iść w prawo!
foo to tablica 8 ...
Naciśnij nawias, więc nie możesz już iść w prawo, idź w lewo
foo jest tablicą zawierającą 8 wskaźników do ...
Skończone w nawiasach, można teraz iść w prawo
foo to tablica 8 wskaźników do funkcji, która zwraca ...
Nic więcej w prawo, idź w lewo
foo to tablica 8 wskaźników do funkcji, która zwraca wskaźnik do ...
Kontynuuj w lewo
foo to tablica 8 wskaźników do funkcji, która zwraca wskaźnik do stałej ...
Kontynuuj w lewo
foo to tablica 8 wskaźników do funkcji, która zwraca wskaźnik do stałego wskaźnika do ...
Kontynuuj w lewo
foo to tablica 8 wskaźników do funkcji, która zwraca wskaźnik do stałego wskaźnika do znaku char ...
Kontynuuj w lewo
foo to tablica 8 wskaźników do funkcji, która zwraca wskaźnik do stałego wskaźnika do stałej char (Complete!)
Dalsze wyjaśnienie: http://www.unixwiz.net/techtips/reading-cdecl.html
źródło
Wiele osób sugeruje czytanie specyfikacji typu od prawej do lewej.
W obu formach wskaźnik wskazuje na dane stałe lub tylko do odczytu.
W drugiej formie nie można zmienić wskaźnika; wskaźnik będzie zawsze wskazywał to samo miejsce.
źródło
Różnica polega na tym, że bez tego dodatkowego
const
programista mógłby zmienić wewnątrz metody, gdzie wskazuje wskaźnik; na przykład:Byłoby to zamiast tego nielegalne, gdyby podpis był
void print_string(const char * const the_string)
Wielu programistów uważa, że dodatkowe
const
słowo kluczowe jest zbyt rozwlekłe (w większości scenariuszy) i pomija je, nawet jeśli byłoby to poprawne semantycznie.źródło
W tym drugim gwarantujesz nie modyfikowanie zarówno wskaźnika jak i znaku, w pierwszym gwarantujesz tylko, że zawartość się nie zmieni ale możesz przesuwać wskaźnik
źródło
Nie ma powodu, dla którego żaden z nich nie działałby. Wszystko co
print_string()
robi to wypisanie wartości. Nie próbuje go modyfikować.Dobrym pomysłem jest utworzenie funkcji, która nie modyfikuje argumentów oznaczania jako stałych. Zaletą jest to, że zmienne, których nie można zmienić (lub których nie chcesz zmieniać), mogą być przekazywane do tych funkcji bez błędów.
Jeśli chodzi o dokładną składnię, chcesz wskazać, które typy argumentów są „bezpieczne” przy przekazywaniu do funkcji.
źródło
Myślę, że rzadko jest to istotne, ponieważ funkcja nie jest wywoływana z argumentami takimi jak & * the_string lub ** the_string. Sam wskaźnik jest argumentem typu wartości, więc nawet jeśli go zmodyfikujesz, nie zmienisz kopii użytej do wywołania funkcji. Wersja, którą pokazujesz, gwarantuje, że ciąg się nie zmieni i myślę, że w tym przypadku jest to wystarczające.
źródło
const char *
oznacza, że nie możesz użyć wskaźnika do zmiany wskazywanego elementu. Możesz jednak zmienić wskaźnik, aby wskazywał coś innego.Rozważać:
Parametr jest niebędącym
const char *
stałym wskaźnikiem do const char, więc można go zmienić na inną wartość (jak stały ciąg). Gdybyśmy jednak omyłkowo napisali*text = '\0'
, wystąpiłby błąd kompilacji.Prawdopodobnie, jeśli nie zamierzasz zmieniać tego, na co wskazuje parametr, możesz wprowadzić parametr
const char * const text
, ale nie jest to powszechne. Zwykle zezwalamy funkcjom na zmianę wartości przekazywanych do parametrów (ponieważ przekazujemy parametry według wartości, żadna zmiana nie wpływa na wywołującego).Przy okazji: dobrą praktyką jest unikanie,
char const *
ponieważ często jest błędnieconst char *
odczytywany - oznacza to samo co , ale zbyt wiele osób czyta to jako senschar * const
.źródło
const char *
a podpisemchar const *
- sposób, w jaki sformułowałeś swoją BTW, naprawdę pomógł!Prawie wszystkie inne odpowiedzi są poprawne, ale
const
pomijają jeden aspekt tego: kiedy użyjesz extra parametru w deklaracji funkcji, kompilator zasadniczo je zignoruje. Przez chwilę zignorujmy złożoność twojego przykładu będącego wskaźnikiem i po prostu użyjint
.deklaruje tę samą funkcję co
Tylko w definicji funkcji ma dodatkowe
const
znaczenie:Ta definicja jest zgodna z którąkolwiek z powyższych deklaracji. Dzwoniącego to nie obchodzi
x
toconst
- to jest szczegół implementacji, który nie ma znaczenia w miejscu rozmowy.Jeśli masz
const
wskaźnik doconst
danych, obowiązują te same zasady:Niewielu programistów C ++ zadaje sobie trud tworzenia parametrów
const
, nawet jeśli mogą, niezależnie od tego, czy te parametry są wskaźnikami.źródło
const
do parametru funkcji w definicji, ale nie w deklaracji.Różnica między nimi polega na tym, że znak * może wskazywać na dowolny dowolny wskaźnik. Const char * natomiast wskazuje na stałe zdefiniowane w sekcji DATA pliku wykonywalnego. W związku z tym nie można modyfikować wartości znaków w łańcuchu const char *.
źródło
const char*
Może wskazać nigdzie się podoba.