...
Osoitinmuuttujat
...
ja
...
muuttujan osoite
Muuttuja voi sisältää arvon tai osoitteen. Kun tietoa täytyy välittää funktioille suuri määrä yhdellä kertaa tai, kun tiedonkulun on oltava kaksisuuntaista, täytyy tieto välittää osoitteen avulla. Esimerkiksi haluttaessa välittää taulukko toiselle funktiolle, annetaan tyypillisesti kutsuttavalle funktiolle tiedoksi paikka, missä kohdassa muistia taulukko sijaitsee, eikä kaikkia taulukon alkioita erikseen. Kaikki merkkijonot välitetään funktioilta toiselle osoitteiden avulla. Osoitinmuuttuja esitellään seuraavasti:
Code Block |
---|
int* OsoitinKokonaisLukuun;
float* OsoitinReaalilukuun;
char* OsoitinMerkkiinTaiMerkkijonoon;
double* OsoitinPitkaanReaalilukuun;
void *Tavuosoitin; |
Tähti tyypin ja muuttujan nimen välissä kertoo, että kyseessä on osoitin. p kirjainta käytetään usein muuttujan edessä ilmoittamaan tätä (p = pointer = osoitin). Edellä esitellyillä osoitinmuuttujilla voidaan nyt osoittaa vastaavan tyypin muuttujiin. Osoittimen esittelyn jälkeen, osoitin on ns. "villi" osoitin (wild pointer), koska se ei osoita vielä mihinkään. Tällaisen osoittimen käyttö on erittäin vaarallista. Monesti osoitin laitetaan osoittamaan "ei minnekään" seuraavalla tavalla:
Code Block |
---|
int* pLuku=NULL; |
Edellä esitetty esittely takaa sen ettei ko. osoitinta voi käyttää ennen kuin se on alustettu osoittamaan jotain muuttujaa esim. seuraavasti:
Code Block |
---|
osoite Muuttuja voi sisältää arvon tai osoitteen. Kun tietoa täytyy välittää funktioille suuri määrä yhdellä kertaa tai, kun tiedonkulun on oltava kaksisuuntaista, täytyy tieto välittää osoitteen avulla. Esimerkiksi haluttaessa välittää taulukko toiselle funktiolle, annetaan tyypillisesti kutsuttavalle funktiolle tiedoksi paikka, missä kohdassa muistia taulukko sijaitsee, eikä kaikkia taulukon alkioita erikseen. Kaikki merkkijonot välitetään funktioilta toiselle osoitteiden avulla. Osoitinmuuttuja esitellään seuraavasti: int\* OsoitinKokonaisLukuun; float\* OsoitinReaalilukuun; char\* OsoitinMerkkiinTaiMerkkijonoon; double\* OsoitinPitkaanReaalilukuun; void \*Tavuosoitin; Tähti tyypin ja muuttujan nimen välissä kertoo, että kyseessä on osoitin. p kirjainta käytetään usein muuttujan edessä ilmoittamaan tätä (p = pointer = osoitin). Edellä esitellyillä osoitinmuuttujilla voidaan nyt osoittaa vastaavan tyypin muuttujiin. Osoittimen esittelyn jälkeen, osoitin on ns. "villi" osoitin (wild pointer), koska se ei osoita vielä mihinkään. Tällaisen osoittimen käyttö on erittäin vaarallista. Monesti osoitin laitetaan osoittamaan "ei minnekään" seuraavalla tavalla: int\* pLuku=NULL; Edellä esitetty esittely takaa sen ettei ko. osoitinta voi käyttää ennen kuin se on alustettu osoittamaan jotain muuttujaa esim. seuraavasti: int Luku;//esitellään muuttuja nimeltään Luku pLuku=&Luku;//Sijoitetaan muuttujan Luku osoite osoiteeseen pLuku if(pLuku==NULL) printf("VIRHE, LUVATON OSOITTIMEN KÄYtTÖKÄYTTÖ"); |
&-merkillä
...
haetaan
...
esitellyn
...
muuttujan
...
osoite.
Osoitetta vastaava muuttuja
Monessa tapauksessa tiedetään, missä tieto on eli tiedetään tiedon osoite. Esim. kaikki tietokoneeseen liitetyt laitteet ovat tietyissä osoitteissa. Osoitetta vastaava muuttuja saadaan *
merkillä. esim.
Code Block |
---|
char Merkki; char h2. Osoitetta vastaava muuttuja Monessa tapauksessa tiedetään, missä tieto on eli tiedetään tiedon osoite. Esim. kaikki tietokoneeseen liitetyt laitteet ovat tietyissä osoitteissa. Osoitetta vastaava muuttuja saadaan * merkillä. esim. char Merkki; char\* pSarjaportti=0x0238;//sarjaportti on liitetty osoitteeseen 0x238 Merkki= \*pSarjaportti; |
Esim.
Code Block |
---|
Esim. int Eka=5; int \*pLuku=NULL; int OmaLuku=0; pLuku = &Eka;//pLuku osoittamaan OmaLuku muuttujaa OmaLuku = \*pLuku;//pLuku osoitteen osoittaman muistipaikan sisältö OmaLuku muuttujalle |
Edellä
...
olevassa
...
esimerkissä
...
muuttujaan
...
Eka
...
sijoitetaan
...
arvo
...
viisi,
...
pLuku
...
laitetaan
...
osoittamaan
...
muuttujaa
...
Eka.
...
Seuraavaksi
...
haetaan
...
pLukua
...
vastaava
...
muuttuja
...
*:llä
...
ja
...
sijoitetaan
...
se
...
muuttujaan
...
OmaLuku
...
Osoitin
...
osoittaa
...
muuttujan
...
paikkaa
...
muistissa.
...
Osoitinmuuttuja
...
on
...
erityinen
...
muuttuja,
...
joka
...
sisältää
...
tiettyyn
...
tyyppiin
...
määritellyn
...
osoittimen.
...
Osoittimilla
...
on
...
C-kielessä
...
kolme
...
tärkeää
...
tehtävää:
...
niillä
...
voidaan
...
osoittaa
...
nopeasti
...
vektorin
...
tai
...
taulukon
...
elementteihin,
...
ne
...
mahdollistavat
...
kaksisuuntaisen
...
tiedon
...
siirron
...
funktioiden
...
välillä
...
ja
...
kolmantena
...
ne
...
tukevat
...
linkattuja
...
listoja
...
ja
...
dynaamisia
...
tietorakenteita.
...
Osoittimien
...
oikea
...
ymmärtäminen
...
on
...
erittäin
...
tärkeää
...
tehokkaan
...
C-ohjelmoinnin
...
kannalta.
...
Osoittimet
...
ovat
...
yksi
...
C:n
...
vahvimmista,
...
mutta
...
myös
...
vaarallisimmista
...
piirteistä.
...
Alustamaton
...
osoitin
...
saattaa
...
aiheuttaa
...
koko
...
ohjelman
...
romahtamisen
...
ja
...
mikä
...
pahinta
...
osoittimien
...
aiheuttamia
...
virheitä
...
on
...
erittäin
...
vaikea
...
löytää,
...
koska
...
ohjelman
...
antamat
...
virheilmoitukset
...
ko.
...
tapauksessa
...
osoittavat
...
tyypillisesti
...
väärään
...
paikkaan.
...
Lisäksi
...
ohjelma
...
saattaa
...
toimia
...
pitkiäkin
...
aikoja
...
näennäisen virheettömästi.
Code Block |
---|
virheettömästi. int Luku; int\* pLuku; int*\* ppLuku; Osoitteen osoite   |
Osoitteen osoite Osoite Muuttuja
&&Luku &Luku Luku
&pLuku pLuku *pLuku
ppLuku *ppLuku **ppLuku
Yllä oleva taulukko esittää, kuinka voidaan siirtyä muuttujasta sen osoitteeseen ja päin vas-
toin.
Tiedonvälitys osoitteiden avulla
Esimerkki tiedonvälityksestä osoittimen avulla.
Code Block |
---|
//esittelyt ; Osoite Muuttuja &&Luku &Luku Luku &pLuku pLuku *pLuku ppLuku \*ppLuku \**ppLuku Yllä oleva taulukko esittää, kuinka voidaan siirtyä muuttujasta sen osoitteeseen ja päin vas\- toin. h2. Tiedonvälitys osoitteiden avulla Esimerkki tiedonvälityksestä osoittimen avulla. //esittelyt void JokuFunktio(int*); void main(); void main() { { int Luku=5; JokuFunktio(&Luku); printf("Luvun arvo on %d",Luku); getch(); } void JokuFunktio(int\* pMuuttuja) { \{ *pMuuttuja = \*pMuuttuja * 2; } Tähdellä on kolme merkitystä: \* hakee osoitetta vastaavan muuttujan \* esittelee osoitinmuuttujan \* toimii myös kertomerkkinä h2. Osoittimen määrittäminen vektorille Osoitin vektorille on vektorin nimi ilman indeksiä. Esim.  } |
Tähdellä on kolme merkitystä:
* hakee osoitetta vastaavan muuttujan
* esittelee osoitinmuuttujan
* toimii myös kertomerkkinä
Osoittimen määrittäminen vektorille
Osoitin vektorille on vektorin nimi ilman indeksiä.
Esim.
Code Block |
---|
int p*; //kokonaislukuosoitin int testi[20]; //; int p*; /*kokonaislukuosoitin*/ int testi\[20\]; /*20:n kokonailuvun vektori*/ p = testi; /* //kokonaislukuvektorin ensimmäisen elementin osoitin sijoitetaan osoittimen p arvoksi*/ |
Edellenen
...
käsky
...
voidaan
...
toteuttaa
...
myös
...
& merkillä
Code Block |
---|
merkillä testi = &testi\[0\]; h2. |
Osoitinaritmetiikkaa
Osoittimiin voidaan kohdistaa ainoastaan kaksi laskutoimitusta, yhteen- ja vähennyslasku.
Laskenta on turvallisinta ++ ja -- operaattoreita käyttäen, jotta osoitin osoittaa aina muuttujan
alkuun.
Code Block |
---|
int *p; float *f; int lukuja[10]; float flukuja[10]; Osoitinaritmetiikkaa Osoittimiin voidaan kohdistaa ainoastaan kaksi laskutoimitusta, yhteen\- ja vähennyslasku. Laskenta on turvallisinta \+\+ ja \-\- operaattoreita käyttäen, jotta osoitin osoittaa aina muuttujan alkuun. int \*p; float \*f; int lukuja\[10\]; float flukuja\[10\]; p=lukuja;//laitetaan kokonaislukuosoitin osoittamaan vektorin alkuun f=flukuja; p++;//siirrytään seuraavaan alkioon f++; |
p:n
...
arvo
...
nousee
...
laskutoimituksessa
...
kahdella
...
ja
...
f:n
...
arvo
...
kahdeksalla(riippu
...
ympäristöstä,
...
tarkasta
...
sizeof
...
funktiolla
...
lukualueiden
...
koko),
...
koska
...
kokonaisluku
...
on
...
kaksi
...
tavua
...
ja
...
liukuluku
...
kahdeksan
...
tavua
...
pitkä.
Osoittimien vertailu
Osoittimia voidaan vertailla keskenään esim.
Code Block |
---|
h2. Osoittimien vertailu Osoittimia voidaan vertailla keskenään esim. if (p<q) printf("p osoittaa pienempään muistiosoitteeseen kuin q"); |
Osoitinvertailuja
...
joudutaan
...
käyttämään
...
erityisesti
...
tapauksissa,
...
joissa
...
tarvitaan
...
pinoa.
...
Tyypillisiä
...
sovellutuksia
...
ovat
...
kääntäjät,
...
tulkit
...
ja
...
taulukkolaskentaohjelmat.
...
(eli
...
ei
...
tavallista)
Osoittimet ja vektorit
Osoittimien ja vektoreiden välillä on läheinen yhteys esim.
Code Block |
---|
char strMerkkiJono[80], *psrtMerkkiJono; h2. Osoittimet ja vektorit Osoittimien ja vektoreiden välillä on läheinen yhteys esim. char strMerkkiJono\[80\], \*psrtMerkkiJono; psrtMerkkiJono = strMerkkiJono; |
p1
...
asetettiin
...
osoittamaan
...
ensimmäistä
...
vektorin
...
elementtiä.
...
Kun
...
halutaan
...
osoittaa
...
neljättä
...
el
...
-
...
ementtiä,
...
voidaan
...
kirjoittaa:
str[3]
tai
*(p1+3)
...
Vektoreita
...
voidaan
...
osoittaa
...
osoitinaritmetiikalla
...
tai
...
vektorin
...
indeksien
...
avulla.
...
Osoitin
...
arit
...
-
...
metiikka
...
on
...
ohjelman
...
kannalta
...
nopeampi
...
tapa.
...
...
void osoitin
void-tyyppinen
...
osoitin
...
on
...
ns.
...
geneerinen
...
osoitin.
...
Geneerinen
...
osoitin
...
voi
...
osoittaa
...
minkä
...
tahansa
...
tyypin
...
muuttujaan.
...
Esimerkiksi
...
seuraavat
...
ohjelmalauseet
...
ovat sallittuja.
Code Block |
---|
sallittuja. char MerkkiJono\[80\]; void\* OsoitinMihintahansa=NULL; OsoitinMihintahansa=Merkkijono; |
Geneerisen
...
osoittimen
...
inkrementointi
...
ja
...
degrementoinit
...
muuttavat
...
osoittimen
...
asemaa
...
yhden
...
tavun
...
verran.
...
Geneerisiä
...
osoittimia
...
käytetään
...
tyypillisesti
...
laiteläheisessä
...
ohjelmoinnissa.
...
Osoitin paluuarvona
Paikallisen muuttujan osoitetta ei saa palauttaa, koska paikallinen muuttuja häviää funktiosta poistuttaessa.
Code Block |
---|
---- h2. Osoitin paluuarvona Paikallisen muuttujan osoitetta ei saa palauttaa, koska paikallinen muuttuja häviää funktiosta poistuttaessa. int* funktio() { int a; // tilapäinen muuttuja, vapautetaan funktiosta poistuttaessa int *tulos = &a; // sijoitetaan tulos-osoittimeen a:n muistipaikan osoite return tulos; // VIRHE!!! a on paikallinen muuttuja } |
Oikea
...
ratkaisu
Code Block |
---|
int* funktio(int* a) //a on nyt kutsujan jonkin muuttujan osoite { int *tulos = a; // sijoitetaan tulos-osoittimeen a:n osoite return tulos; // OIKEIN } |
Toinen
...
mahdollinen
...
ratkaisu
Code Block |
---|
int* funktio() { static int a; // staattista muuttujaa ei vapauteta funktiosta poistuttaessa int *tulos = &a; // sijoitetaan tulos-osoittimeen a:n muistipaikkan osoite return tulos; // VIRHE!!! } |