...
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 |
---|
esimerkki] \#include <stdio.h> 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 |
---|
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 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 nbsp; 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. 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(); } 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. |
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]; 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 |
---|
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 |
---|
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 |
---|
int ---- 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\!\!\! } |