Co oznacza "dereferencja" wskaźnik?

Proszę załączyć przykład z wyjaśnieniem.

Author: Rakete1111, 2011-02-10

6 answers

Przegląd podstawowej terminologii

Jest to zazwyczaj wystarczająco dobre - chyba, że programujesz assembly - aby wyobrazić sobie pointer zawiera numeryczny adres pamięci, z 1 odnoszącym się do drugiego bajtu w pamięci procesu, 2 trzecim, 3 czwartym i tak dalej....

  • co się stało z 0 i pierwszym bajtem? Cóż, przejdziemy do tego później-Zobacz wskaźniki null poniżej.
  • dla dokładniejszej definicji tego, co pointery przechowują i jak pamięć i adresy odnoszą się, zobacz "więcej o adresach pamięci i dlaczego prawdopodobnie nie musisz wiedzieć" .

Jeśli chcesz uzyskać dostęp do danych / wartości w pamięci, do której wskazuje wskaźnik - zawartość adresu z tym indeksem liczbowym-wtedy dereferencja wskaźnik.

Różne języki komputerowe mają różne notacje, aby powiedzieć kompilatorowi lub interpreterowi, że jesteś teraz zainteresowany pointed-to value-I focus below on C and C++.

Scenariusz pointera

Rozważmy w C, biorąc pod uwagę wskaźnik taki jak p poniżej...

const char* p = "abc";

...cztery bajty z wartościami numerycznymi używanymi do kodowania liter "A", "b", " c " i Bajt 0 oznaczający koniec danych tekstowych są przechowywane gdzieś w pamięci, a adres numeryczny tych danych jest przechowywany w p.

Na przykład, jeśli literał ciągu miał adres 0x1000 i p 32-bitowy wskaźnik 0x2000, zawartość pamięci wynosiłaby:

Memory Address (hex)    Variable name    Contents
1000                                     'a' == 97 (ASCII)
1001                                     'b' == 98
1002                                     'c' == 99
1003                                     0
...
2000-2003               p                1000 hex

Zauważ, że nie ma nazwy zmiennej/identyfikatora dla adresu 0x1000, ale możemy pośrednio odnosić się do ciągu znaków za pomocą wskaźnika przechowującego jego adres: p.

Derefering the pointer

Aby odnieść się do znaków p wskazuje na, dereferencję p używając jednej z tych notacji (ponownie, Dla C):

assert(*p == 'a');  // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
                     // p and 1 times the size of the things to which p points:
                     // In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b');  // Another notation for p[1]

Możesz również przenosić wskaźniki przez dane wskazywane, zmieniając je tak, jak ty przejdź:

++p;  // Increment p so it's now 0x1001
assert(*p == 'b');  // p == 0x1001 which is where the 'b' is...

Jeśli masz jakieś dane, do których można zapisać, możesz zrobić takie rzeczy:

int x = 2;
int* p_x = &x;  // Put the address of the x variable into the pointer p_x
*p_x = 4;       // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4

Powyżej, musisz wiedzieć w czasie kompilacji, że będziesz potrzebować zmiennej o nazwie x, A kod prosi kompilator o ustalenie, gdzie powinna być przechowywana, zapewniając, że adres będzie dostępny przez &x.

Dereferencja i dostęp do elementu danych struktury

W C, Jeśli masz zmienną, która jest wskaźnikiem do struktury z członami danych, można uzyskać dostęp do tych członków za pomocą operatora -> dereferencing:

typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159;  // Dereference and access data member x.d_
(*p).d_ *= -1;    // Another equivalent notation for accessing x.d_

Wielobajtowe typy danych

Aby użyć wskaźnika, program komputerowy potrzebuje również pewnego wglądu w typ danych, na które są wskazywane - jeśli ten typ danych potrzebuje więcej niż jednego bajtu do reprezentowania, to wskaźnik zwykle wskazuje na najniższy numer bajtu w danych.

Tak więc, patrząc na nieco bardziej złożony przykład:]}
double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3);  // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4);  // Actually looks at bytes from address p + 1 * sizeof(double)
                       // (sizeof(double) is almost always eight bytes)
assert(++p);           // Advance p by sizeof(double)
assert(*p == 13.4);    // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8;       // Change sizes[3] from 19.4 to 29.8
                       // Note: earlier ++p and + 2 here => sizes[3]

Wskaźniki dynamicznie przydzielane pamięć

Czasami nie wiesz, ile pamięci potrzebujesz, dopóki twój program nie uruchomi się i nie zobaczy, jakie dane są na niego rzucane... następnie można dynamicznie przydzielać pamięć za pomocą malloc. Powszechną praktyką jest przechowywanie adresu w wskaźniku...

int* p = malloc(sizeof(int)); // Get some memory somewhere...
*p = 10;            // Dereference the pointer to the memory, then write a value in
fn(*p);             // Call a function, passing it the value at address p
(*p) += 3;          // Change the value, adding 3 to it
free(p);            // Release the memory back to the heap allocation library
W C++ alokacja pamięci odbywa się zwykle za pomocą operatora new, a dealokacja za pomocą delete:
int* p = new int(10); // Memory for one int with initial value 10
delete p;

p = new int[10];      // Memory for ten ints with unspecified initial value
delete[] p;

p = new int[10]();    // Memory for ten ints that are value initialised (to 0)
delete[] p;

Patrz również C++ Inteligentne Wskaźniki poniżej.

Przegrywanie i przeciekanie adresy

Często wskaźnik może być jedyną wskazówką, gdzie w pamięci znajdują się dane lub bufor. Jeśli potrzebne jest ciągłe korzystanie z tych danych / bufora lub możliwość wywołania free() lub delete w celu uniknięcia wycieku pamięci, programista musi operować na kopii wskaźnika...

const char* p = asprintf("name: %s", name);  // Common but non-Standard printf-on-heap

// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
    if (!isprint(*q))
        *q = '_';

printf("%s\n", p); // Only q was modified
free(p);

...lub starannie zorganizować odwrócenie wszelkich zmian...

const size_t n = ...;
p += n;
...
p -= n;  // Restore earlier value...

C++ Inteligentne Wskaźniki

W C++ najlepszą praktyką jest używanie inteligentnego wskaźnika obiektów do przechowywania i zarządzaj wskaźnikami, automatycznie zmniejszając ich alokację po uruchomieniu destruktorów inteligentnych wskaźników. Od C++11 Biblioteka Standardowa udostępnia DWA, unique_ptr gdy dla przydzielonego obiektu jest jeden właściciel...

{
    std::unique_ptr<T> p{new T(42, "meaning")};
    call_a_function(p);
    // The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete

...oraz shared_ptr dla własności udziałów (przy użyciu zliczanie referencji )...

{
    std::shared_ptr<T> p(new T(3.14, "pi"));
    number_storage.may_add(p); // Might copy p into its container
} // p's destructor will only delete the T if number_storage didn't copy

Wskaźniki Null

W C, NULL i 0 - a dodatkowo w C++ nullptr - można wskazać, że wskaźnik nie obecnie przechowuje adres pamięci zmiennej i nie powinien być dereferowany ani używany w arytmetyce wskaźników. Na przykład:

const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
char c;
while ((c = getopt(argc, argv, "f:")) != EOF)
    switch (c) {
      case f: p_filename = optarg; break;
    }
if (p_filename)  // Only NULL converts to false
    ...   // Only get here if -f flag specified

W C i c++, tak jak wbudowane typy liczbowe nie muszą być domyślnie ustawione na 0, ani bools na false, wskaźniki nie zawsze są ustawione na NULL. Wszystkie te wartości są ustawione na 0/false / NULL, gdy są zmiennymi static lub (tylko w C++) bezpośrednimi lub pośrednimi zmiennymi składowymi obiektów statycznych lub ich baz lub przechodzą inicjalizację zerową (np. new T(); i new T(x, y, z); wykonują zero-inicjalizacja na członach T włącznie ze wskaźnikami, podczas gdy new T; nie).

Dalej, gdy przypisujesz 0, NULL i nullptr do wskaźnika bity w wskaźniku niekoniecznie są resetowane: wskaźnik może nie zawierać "0" na poziomie sprzętowym lub odnosić się do adresu 0 w wirtualnej przestrzeni adresowej. Kompilator ma prawo przechowywać tam coś innego, jeśli ma ku temu powód, ale cokolwiek robi - jeśli się pojawi i porównasz wskaźnik do 0, NULL, nullptr lub inny wskaźnik, który został przypisany któregokolwiek z tych, porównanie musi działać zgodnie z oczekiwaniami. Tak więc, poniżej kodu źródłowego na poziomie kompilatora, "NULL" jest potencjalnie trochę "magiczne" w językach C i C++...

Więcej o adresach pamięci i dlaczego prawdopodobnie nie musisz wiedzieć

Ściślej, inicjalizowane wskaźniki przechowują wzorzec bitowy identyfikujący NULL lub (częstowirtualny ) adres pamięci.

Prosty przypadek, gdzie jest to przesunięcie liczbowe do Cała wirtualna przestrzeń adresowa procesu; w bardziej złożonych przypadkach wskaźnik może być względny do określonego obszaru pamięci, który procesor może wybrać na podstawie rejestrów "segmentu" PROCESORA lub pewnego rodzaju identyfikatora segmentu zakodowanego w bit-pattern i / lub patrząc w różnych miejscach w zależności od instrukcji kodu maszynowego używającego adresu.

Na przykład, int* prawidłowo zainicjowana tak, aby wskazywała na zmienną int może - po przeniesieniu do float* - uzyskać dostęp do wartości w pamięci "GPU" całkiem w odróżnieniu od zmiennej int, wtedy po oddaniu do wskaźnika funkcji może odnosić się do odrębnej pamięci przechowującej kody opcodów maszyny dla funkcji.

Języki programowania 3GL, takie jak C i C++, mają tendencję do ukrywania tej złożoności, tak że:]}
  • Jeśli kompilator daje Ci wskaźnik do zmiennej lub funkcji, możesz ją dowolnie dereferować (o ile zmienna nie jest w międzyczasie destrukowana/dealokowana) i to kompilator ma problem, czy np. dany rejestr procesora należy wcześniej przywrócić lub użyć odrębnej instrukcji kodu maszynowego

  • Jeśli otrzymasz wskaźnik do elementu w tablicy, możesz użyć arytmetyki wskaźnika do przeniesienia w dowolnym miejscu tablicy lub nawet utworzyć adres jeden-ostatni-koniec tablicy, który jest legalny do porównania z innymi wskaźnikami do elementów w tablicy( lub które podobnie zostały przeniesione przez arytmetykę wskaźnika do tej samej wartości jeden-ostatni-koniec); ponownie w C i C++, to do kompilatora, aby zapewnić to "just works"

  • Mapowanie pamięci współdzielonej może dać ci wskazówki i będą one "po prostu działać" w zakresie adresów, które mają dla nich sens.]}

  • Próby przeniesienia legalnych wskaźników poza te granice lub rzucania dowolnych liczb do wskaźników lub używania wskaźników rzucanych do niepowiązanych typów zwykle mają nieokreślone zachowanie, więc należy unikać w bibliotekach i aplikacjach wyższego poziomu, ale kod dla Osy, sterowniki urządzeń itp. być może trzeba polegać na zachowaniach niezdefiniowanych przez C lub C++, które są jednak dobrze zdefiniowane przez ich specyficzny sprzęt.

 581
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-03 00:08:18

Dereferencja wskaźnika oznacza uzyskanie wartości zapisanej w pamięci wskazywanej przez wskaźnik. Operator * jest używany do tego celu i nazywany jest operatorem dereferencyjnym.

int a = 10;
int* ptr = &a;

printf("%d", *ptr); // With *ptr I'm dereferencing the pointer. 
                    // Which means, I am asking the value pointed at by the pointer.
                    // ptr is pointing to the location in memory of the variable a.
                    // In a's location, we have 10. So, dereferencing gives this value.

// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.

 *ptr = 20;         // Now a's content is no longer 10, and has been modified to 20.
 82
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-29 16:15:54

Wskaźnik jest "odniesieniem" do wartości.. podobnie jak numer wywołania biblioteki jest odniesieniem do książki. / Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center /

int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;

// The * causes pA to DEREFERENCE...  `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4.. 
Jeśli książki nie ma, bibliotekarz zaczyna krzyczeć, zamyka bibliotekę, a kilka osób jest ustawionych, aby zbadać przyczynę, że ktoś znajdzie książkę, której nie ma.
 14
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-10 18:08:39

W prostych słowach dereferencja oznacza dostęp do wartości z określonego miejsca pamięci, na które wskazuje ten wskaźnik.

 9
Author: Fahad Naeem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-11 19:50:55

Kod i Wyjaśnienie z Podstawy wskaźnika:

Operacja dereferencji rozpoczyna się od wskaźnik i podąża za strzałką nad aby uzyskać dostęp do jego punktu. Celem może być aby spojrzeć na stan pointee lub na Zmień stan pointee. Na operacja dereferencji na wskaźniku działa tylko wtedy, gdy wskaźnik ma pointee -- the pointee must be przydzielone i wskaźnik musi być ustawiony aby wskazać na to. Najczęstszy błąd w kodzie pointera zapominamy o ustawieniu up the pointee. Najczęstsze Runtime crash z powodu tego błędu w kod jest nieudaną dereferencją operacja. W Javie niepoprawne dereferencja będzie grzecznie oznaczona przez system runtime. W opracowaniu języki takie jak C, C++ i Pascal, nieprawidłowa dereferencja spowoduje czasami krach, a innym razem uszkodzona pamięć w jakiejś subtelnej, przypadkowej sposób. Błędy w kompilacji języki mogą być trudne do wyśledzenia z tego powodu w dół.

void main() {   
    int*    x;  // Allocate the pointer x
    x = malloc(sizeof(int));    // Allocate an int pointee,
                            // and set x to point to it
    *x = 42;    // Dereference x to store 42 in its pointee   
}
 7
Author: ash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-10 09:21:46

Myślę, że wszystkie poprzednie odpowiedzi są błędne, ponieważ stwierdzić, że dereferencja oznacza dostęp do rzeczywistej wartości. Wikipedia podaje poprawną definicję: https://en.wikipedia.org/wiki/Dereference_operator

Działa na zmiennej wskaźnika i zwraca wartość l odpowiadającą wartości pod adresem wskaźnika. To się nazywa "dereferencing" wskaźnik.

To powiedziawszy, możemy dereferować wskaźnik bez dostęp do wartości, którą wskazuje za. Na przykład:

char *p = NULL;
*p;

Dereferujemy wskaźnik NULL bez dostępu do jego wartość. Or we could do:

p1 = &(*p);
sz = sizeof(*p);

Znowu dereferencja, ale nigdy nie dostęp do wartości. Taki kod nie ulegnie awarii: Awaria dzieje się, gdy faktycznie dostęp dane przez nieprawidłowy wskaźnik. Jednak, niestety, zgodnie z standard, dereferencja nieprawidłowego wskaźnika jest niezdefiniowana zachowania (z kilkoma wyjątkami), nawet jeśli nie starasz się dotknij rzeczywistych danych.

Więc w krótko: dereferencja wskaźnika oznacza zastosowanie operator dereferencyjny do niego. Ten operator po prostu zwraca l-wartość do wykorzystania w przyszłości.

 3
Author: stsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-11 19:52:13