2008-10-20 14 views
26

ja często wykorzystywane wskaźniki do const obiektów, tak jak ...Co to są wskaźniki const (w przeciwieństwie do wskaźników do obiektów const)?

const int *p; 

To po prostu oznacza, że ​​nie można zmienić liczbę całkowitą, która wskazuje na p przez p. Ale widziałem też odniesienie do const wskaźniki, stwierdził tak ...

int* const p; 

Jak rozumiem, co oznacza, że ​​zmienna wskaźnik sama jest stała - można zmienić to wskazuje na całkowitą przez cały dzień, ale nie możesz wskazać na coś innego.

Jakie to może mieć zastosowanie?

Odpowiedz

25

Podczas projektowania programów w języku C dla systemów wbudowanych lub programów specjalnego przeznaczenia, które muszą odwoływać się do tej samej pamięci (wieloprocesorowa pamięć współużytkowania aplikacji), potrzebne są stałe wskaźniki.

Na przykład, mam 32 bit MIPs processor, który ma dołączony little LCD. Muszę zapisać swoje dane LCD do określonego portu w pamięci, który następnie zostanie wysłany do kontrolera LCD.

Mogę # zdefiniować tę liczbę, ale muszę również rzucić ją jako wskaźnik, a kompilator C nie ma tylu opcji, kiedy to robię.

Co więcej, być może potrzebuję, aby był niestabilny, co również może być rzutowane, ale łatwiej i bardziej przejrzyste jest użycie podanej składni - wskaźnika stałego do ulotnej lokacji pamięci.

W przypadku programów na komputery PC, przykład może wyglądać następująco: Jeśli projektujesz gry DOS VGA (istnieją tutoriale online, które są zabawne, aby nauczyć się podstawowych grafik niskiego poziomu), musisz napisać do pamięci VGA, która może być określane jako przesunięcie od wskaźnika const.

-Adam

+3

Drobna nitpick: w przypadku urządzeń mapowanych na pamięć, na pewno (nie "może") trzeba mieć element oznaczony jako lotny - w przeciwnym razie nie można mieć pewności, czy lub kiedy kompilator faktycznie emituje operację odczytu lub zapisu. –

+0

Możesz "potrzebować" niestabilności jest poprawny. Możesz użyć 'barrier()' i innej semantyki w zależności od urządzenia. To prawda, że ​​musisz ostrożnie postępować z wartościami. Tzn. Cache/no-cache, bariera itp. Zależy od typu urządzenia, a ** volatile ** nie zawsze jest najlepszą opcją. –

+0

@MichaelBurr: Zgadzam się z tobą, że 'volatile' powinno z pewnością tam być; z drugiej strony wiele nagłówków dostawców kompilatorów nie wydaje się kłopotać - fakt, który może być nieco denerwujący (ponieważ nawet jeśli kompilatory zwykle robią to, co trzeba, mogą ignorować próby odczytu rejestru, ale nie robią nic z wynik). – supercat

3

Tak samo jak "const int" ... jeśli kompilator wie, że nie ulegnie zmianie, może to być oparte na nim założenia optymalizacji.

struct MyClass 
{ 
    char* const ptr; 
    MyClass(char* str) :ptr(str) {} 

    void SomeFunc(MyOtherClass moc) 
    { 
     for(int i=0; i < 100; ++i) 
     { 
       printf("%c", ptr[i]); 
       moc.SomeOtherFunc(this); 
     } 
    } 
} 

Teraz kompilator może zrobić sporo do optymalizacji pętli --- pod warunkiem, że wie, że SomeOtherFunc() nie zmienia wartości ptr. Z const, kompilator wie o tym i może dokonać założeń. Bez niego kompilator musi założyć, że SomeOtherFunc zmieni ptr.

+0

Czy nie zostanie zadeklarowany obiekt SomeOtherFunc z argumentem o stałym const dla tego kompilowania? Czy nie będzie to również sposób, w jaki kompilator będzie wiedział, że SomeOtherFunc nie zmieni wskaźnika? Zatem deklarowanie wskaźnika lokalnego jako const nie wydaje się pomagać. –

+0

Andrew: Myślę, że wprowadzasz w błąd ptr z tym –

+0

@Andrew: No.SomeOtherFunc może całkowicie zmienić dowolną inną część obiektu MyClass. –

7

inny przykład: jeśli wiesz, gdzie została zainicjowana, można uniknąć przyszłych kontroli NULL. Kompilator gwarantuje, że wskaźnik nigdy się nie zmienił (do NULL) ...

+1

W C. W C++, w twoim szczególnym przypadku (tj. Wskaźniki nie-NULL), zamiast tego używasz referencji. – paercebal

2

Widziałem kod OLE tam, gdzie był obiekt przekazany spoza kodu i do pracy z nim, trzeba było uzyskać dostęp do określonej pamięci że to minęło. Dlatego użyliśmy wskaźników const, aby upewnić się, że funkcje zawsze manipulowały wartościami, które pojawiły się w interfejsie OLE.

5

W każdym const C++ funkcji składowej, wskaźnik this jest typu C * const, gdzie C jest typem klasy - można zmienić, co wskazuje na to (tzn jej członków), ale nie można go zmienić wskaż inną instancję modelu C.Dla funkcji składowych const jest typu const C * const. Istnieją również (rzadko spotykane) funkcje składowe, dla których this ma także kwalifikator volatile.

3

Zawsze używałem ich, gdy chciałem uniknąć niezamierzonych modyfikacji wskaźnika (takich jak arytmetyczna wskazówka lub wewnątrz funkcji). Możesz także użyć ich do wzorców Singleton.

"ten" jest zakodowanym stałym wskaźnikiem.

26

Pozwala zabezpieczyć wskaźnik przed zmianą. Oznacza to, że można chronić założenia sprawiają, że jesteś w oparciu o wskaźnik nie zmienia lub niezamierzonej modyfikacji, na przykład:

int* const p = &i; 

... 

p++;  /* Compiler error, oops you meant */ 
(*p)++; /* Increment the number */ 
+1

Nigdy wcześniej tego nie widziałem i zgadzam się, że jest to bardzo przydatna sztuczka. – Rob

+1

To nie jest sztuczka :). Staram się używać const tam, gdzie to możliwe, z argumentami funkcji, aby było jasne, że funkcja nie zmieni ciągu ani struktury, która jest przekazywana. –

+0

Mylące jest to, że const często pojawia się dwa razy w deklaracji i może być w rzeczywistości kilka różnych pozycji. Zwykle może to wyglądać jak coś w stylu 'const uint8 * const value'. To deklaruje, że wskaźnik i wartość, na którą wskazuje, nie mogą być modyfikowane (ale mogą być, jeśli są rzutowane). –

-2

zawsze myśleć o wskaźnik jako int. Oznacza to, że

object* var; 

rzeczywiście można traktować jako

int var; 

tak, const wskaźnik oznacza po prostu, że:

const object* var; 

staje

const int var; 

i stąd u nie można zmienić adresu, który th Wskaźnik wskazuje też i to wszystko. Aby zapobiec zmianie danych, musisz uczynić go wskaźnikiem do obiektu const.

+0

Wskaźniki są ** nie ** 'int's, na wiele sposobów. Również pytanie jest bardzo wyraźnie określone jako dotyczące _'stopu 'pointers_, a nie _pointpointów do 'const' objects_. Ale i tak złapałeś ich w niewłaściwy sposób. –

1

Kilka dobrych powodów zostało udzielonych jako odpowiedzi na to pytanie (urządzenia z mapami pamięci i zwykłe stare kodowanie obronne), ale byłbym skłonny założyć się, że większość przypadków, w których widzisz, to w rzeczywistości błąd i że zamiarem było, aby element był wskaźnikiem na stały.

Z pewnością nie mam żadnych danych, aby potwierdzić ten przeczucie, ale mimo to nadal zakładałem.

+0

używam go dużo do optymalizacji. – DavidG

+0

@DavidG Jak dokładnie "const" ma znaczenie dla optymalizacji? Każdy sprytny kompilator może określić, czy zmodyfikujesz zmienną, i zoptymalizować, czy może na podstawie tego, czy tak, czy nie. –

+0

Zrobiłem to w 2008 roku na niektórych urządzeniach, kiedy pisałem ten komentarz ... ale świat bardzo się zmienił od tego czasu, a teraz kompilatory znacznie się poprawiły. – DavidG

5

Jednym z zastosowań jest kod niskiego poziomu (sterownik urządzenia lub osadzony), w którym należy odnieść się do konkretnego adresu, który jest odwzorowany na urządzenie wejścia/wyjścia, takie jak pin sprzętowy. Niektóre języki umożliwiają łączenie zmiennych pod określonymi adresami (np. Ada ma numer use at). W C najbardziej idiotycznym sposobem jest zadeklarowanie stałego wskaźnika. Zwróć uwagę, że takie zwyczaje powinny mieć także kwalifikator volatile.

Innym razem jest to tylko defensywne kodowanie. Jeśli masz wskaźnik, który nie powinien zmienić, to mądrze jest zadeklarować, że nie może zmienić. Pozwoli to kompilatorowi (i narzędziom lint) wykryć błędne próby modyfikacji.

0

Pomyśl typ * i const typ * jako typy sami. Wtedy możesz zobaczyć, dlaczego możesz chcieć mieć stałą tego typu.

+0

tak, a ten typ to w zasadzie int. – DavidG