Programowanie w języku C/C++ » Artykuły

Wskaźniki

W tym artykule dowiesz się z niej o sposobach posługiwania się zmiennymi oraz wskaźnikami w C++.
A więc zaczynamy.

W języku C++ można zadeklarować: zmienne, funkcje oraz typy. Na początku zajmiemy się zmiennymi oraz stałymi.
Stała to taka zmienna, której wartość można przypisać tylko raz. Z punktu widzenia komputera niewiele się to różni, bo miejsce w pamięci i tak, stosownie do zadeklarowanego typu zarezerwować trzeba, umieścić w tablicy i zapamiętać sobie identyfikator i adres. Jedyna praktyczna różnica polega na tym, że zmiennej zadeklarowanej jako stała, np.:    

const float PI = 3.142;


nie można przypisać w programie żadnej innej wartości, innymi słowy zapis:

const float PI = 3.14;


jest jednocześnie DEKLARACJĄ, DEFINICJĄ i ZAINICJOWANIEM stałej PI.

Przykład :
float x,y,z; /*DEKLARACJA*/  
const float TEMP = 6.0; /*DEFINICJA*/ 
x = 11; /*ZAINICJOWANIE zmiennej*/ 


Definicja powoduje nie tylko określenie, jakiego typu wartościami może operować dana zmienna bądź funkcja, która zostaje od tego momentu skojarzona z podanym identyfikatorem, ale dodatkowo powoduje:
- w przypadku zmiennej - przypisanie jej wartości,
- W przypadku funkcji - przyporządkowanie ciała funkcji.
Zdefiniujmy dla przykładu własną funkcje:


Przykład 1.
 
int PokazTekst(void)  
{  
  printf("\\nPrzykładowy tekst\\n");  
  return 0;  
}  

 

PODSTAWOWE TYPY DANYCH



Język C/C++ operuje pięcioma podstawowymi typami danych:
  • char (znak, numer znaku w kodzie ASCII) - 1 bajt;
  • int (liczba całkowita) - 2 bajty;
  • float (liczba z pływającym przecinkiem) - 4 bajty;
  • double (podwójna ilość cyfr znaczących) - 8 bajtów;
  • void (nieokreślona) 0 bajtów.

Zakres wartości przedstawiono w Tabeli poniżej.

Podstawowe typy danych w C++.  


Typ         Znak      Zakres wartości  


char        signed    -128...+127  
int         signed    -32768...+32767  
float       signed    +-3.4E+-38 (dokładność: 7 cyfr)  
double      signed    1.7E+-308 (dokładność: 15 cyfr)  
void     nie dotyczy  bez określonej wartości.  


(signed - ze znakiem, unsigned - bez znaku.)

WSKAŹNIKI



Wskaźnik to zmienna, która zawiera adres innej zmiennej w pamięci komputera. Istnienie wskaźników umożliwia pośrednie odwoływanie się do wskazywanego obiektu (liczby, znaku, łańcucha znaków itp.) a także stosunkowo proste odwołanie się do obiektów sąsiadujących z nim w pamięci. Załóżmy, że:

x - jest umieszczoną gdzieś w pamięci komputera zmienną całkowitą typu int zajmującą dwa kolejne bajty pamięci, a

px - jest wskaźnikiem do zmiennej x.

Jednoargumentowy operator & podaje adres obiektu, a zatem instrukcja:

px = &x;

przypisuje wskaźnikowi px adres zmiennej x. Mówimy, że:
px wskazuje na zmienną x lub
px jest WSKAŹNIKIEM (pointerem) do zmiennej x.

Operator * (naz. OPERATOREM WYŁUSKANIA) powoduje, że zmienna "potraktowana" tym operatorem jest traktowana jako adres pewnego obiektu. Zatem, jeśli przyjmiemy, że y jest zmienną typu int, to działania:

y = x; 


oraz

px = &x; 
y = *px; 


będą mieć identyczny skutek. Zapis y = x oznacza:
"Nadaj zmiennej y dotychczasową wartość zmiennej x";
a zapis y=*px oznacza:
"Nadaj zmiennej y dotychczasową wartość zmiennej, której adres w pamięci wskazuje wskaźnik px" (czyli właśnie x !).
Wskaźniki także wymagają deklaracji. Poprawna deklaracja w opisanym powyżej przypadku powinna wyglądać tak:

int x,y; 
int *px; 


Zapis int *px; oznacza:
"px jest wskaźnikiem i będzie wskazywać na liczby typu int".

Zwróć uwagę, że operatory & i * mają wyższy priorytet niż operatory arytmetyczne, dzięki czemu
  • najpierw następuje pobranie spod wskazanego przez wskaźnik adresu zmiennej;
  • potem następuje wykonanie operacji arytmetycznej; (operacja nie jest więc wykonywana na wskaźniku, a na wskazywanej zmiennej!).

W języku C++ możliwa jest także sytuacja odwrotna:

Y = *(pX + 1); 


Ponieważ operator () ma wyższy priorytet niż * , więc: najpierw wskaźnik zostaje zwiększony o 1; potem zostaje pobrana z pamięci wartość znajdująca się pod wskazanym adresem (w tym momencie nie jest to już adres zmiennej X, a obiektu "następnego" w pamięci) i przypisana zmiennej Y.
Taki sposób poruszania się po pamięci jest szczególnie wygodny, jeśli pod kolejnymi adresami pamięci rozmieścimy np. kolejne wyrazy z tablicy, czy kolejne znaki tekstu.

Jeśli dwa wskaźniki wskazują zmienne takiego samego typu, np. po zadeklarowaniu:

int *pX, *pY; 
int X, Y; 


i zainicjowaniu:

pX = &X; pY = &Y; 


można zastosować operator przypisania:

pY = pX; 


Spowoduje to skopiowanie wartości (adresu) wskaźnika pX do pY, dzięki czemu od tego momentu wskaźnik pY zacznie wskazywać zmienną X. Zwróć uwagę, że nie oznacza to bynajmniej zmiany wartości zmiennych - ani wielkośc X, ani wielkość Y, ani ich adresy w pamięci NIE ULEGAJĄ ZMIANIE. Zatem działanie instrukcji:

pY = pX; i *pY = *pX;

jest RÓŻNE a wynika to z priorytetu operatorów:
najpierw * wyłuskanie zmiennych spod podanych adresów, potem = przypisanie wartości (ale już zmiennym a nie wskaźnikom!).

Shawk <[email protected]>

11 komentarzy

su_seba1 2009-07-27 22:19

Thx:) Dzięki artykułowi udało mi się w końcu zrozumieć idee wskaźników. Po lekturze podrozdziału z Thinking in C++ nie było to dla mnie zbyt jasne.

Intelli 2008-12-16 13:20

A typy short, long int, long long int, long double, long long double to już wywiało z C++? Czy ja o czymś nie wiem? Poza tym rozmiary typów podstawowych zależą od platformy i nie są (jak np. w Javie) wszędzie i zawsze takie same. No a poza tym, przedmówcy zauważyli już, że art słaby. To jest liźnięcie wskaźników i to na poziomie C, a nie C++. Szkoda słów, nawet krytycznych.

n0need 2005-07-25 23:01

progrmuje troche w C++, przeczytalem Symfonie C++, czytam pasjeC++ i kilka innych dobrych ksiazek teraz, i sadze ze jest to slaby kurs ;/ wskazniki sa pobieznie opisane... jest to jeden z najwazniejszych aspektow C++ (joke?) dla poczatkujacych i bardziej zaawansowanych, dlaczego jeden z najwazniejszych? DLA NAUKI! bo z tym sa najwieksze problemy, dlatego wskazniki powinno sie wykuc wprost na pamiec... zrozumiec ich definicje umiec ja czytac i rozumiec! nie mozna czegos umiec na pamiec tego nie rozumiejac, autor tego tekstu znany z dobrych ksiazek o Delphi opisal to POBIEZNIE! nie polecam czytania, mimo iz dobrze napisany poniewaz sa ksiazki i bdb. kursy internetowe w ktorych to wszystko jest napisane bardziej obszernie, czyli LEPIEJ!

sam kurs jest dobry pod wzgledem napisania, poniewaz nie ma wielu bledow, a co do deklaracji i definicji to :
int x; jest definicja ktora jest tez deklaracja... a nie definicja... specjalisci sie trzymaja strasznie definicji tych dwoch pojec ze az to sie gmatwa.. dlatego w skrocie sie mowi : ze jest to deklaracja :) (deklarujemy..)

gritz dla autora bo mial zapal chcial dobrze i to sie liczy :)

Dex 2004-04-02 09:11

Jak juz piszesz cos o wskaznikach to napisz tez cos o wskaznikach do tablic !!

TeMPOraL 2004-03-17 20:44

"inicjalizacja:
int x=11;
deklaracja i definicja:
int x;

deklaracje i definicje rozróżniamy przy tworzeniu funkcji."

Tak naprawdę, to:
int i; //definicja
int i = 1024; //definicja + inicalizacja
extern int i; //deklaracja
Deklaracje i definicje zmiennych także się rozróżnia, ta pierwsza jest z extern.

Artykuł jest ok, choć osobiście uważam, iż operator "wyłuskania" trochę brzydko brzmi :) . Bardziej poprawną nazwą jest operator dereferencji.

marioc64 2003-12-02 13:06

ponadto zapis "x=11" wcale nie jest inicjalizacją tylko przypisaniem.

inicjalizacja:
int x=11;

deklaracja i definicja:
int x;

deklaracje i definicje rozróżniamy przy tworzeniu funkcji.

marioc64 2003-12-02 13:01

Ten artykuł tyczy się rónież języka c, autor nie użył ani trochę nowości dodanych w c++.
Ponadto autor stwierdził, że wskaźnik służy do pośredniego dostawania się do zmiennych, a to zastosowanie wskaźników jest raczej mało przydatne, chyba że używamy tablic - czego autor nie opisał wystarczająco.
Głównym zastosowaniem wskaźników jest dynamiczna alokacja pamięci i obsługa dynamicznych struktur danych co obowiązkowo powino znaleźć się w takim arcie !!!!

yetihehe 2003-07-27 10:13

I też ważna jest sprawa, czy wskaźnik, jeśli dodamy do niego jeden, to przesuwa się o jeden bajt, czy o jeden rozmiar wskazywanej zmiennej? Kilka moich programów źle działało, bo tego nie wiedziałem. Jeśli mamy wskaźnik do jakiejś zmiennej, to dodając jeden, adres przesuwa się o tyle bajtów, ile zajmuje typ tej zmiennej w pamięci. Dla typu void przesuwa się o 1 bajt

Pirate 2003-05-24 14:54

Jest Spoko ;-)

algor 2003-01-22 23:50

jesli to ma byc text o wskaznikach to wypadaloby tez wspomniec chociaz o typie void *, stwierdzenie typu "void   nie dotyczy bez określonej wartości" nie jest do konca trafne i nie mozna bagatelizowac tego typu .Np.:

void *bufor;

Algor

Artur 2003-02-11 09:58

Mogłoby się znależć trochę informacji o wskażnikach zdolnych pokazywać na funkcje, ale poza tym artykuł jest oki