C/Pointteriluntti

< C

Tässä lunttilapussa käsitellään osoittimista eli pointtereista tärkeimmät asiat (ja varmasti riittävä määrä C-kurssien tenttien läpäisyyn).

Terminologia

muokkaa

Muuttuja tarkoittaa muistissa olevaa tilaa, johon voi tallentaa tietoa (C-standardissa lvalue). Muistiosoitteet ja muut arvot voivat myös olla pelkkiä laskutoimitusten tuloksia, joille ei tilaa ole varattu (rvalue).

Osoitin tai pointteri on muuttuja, johon voi tallentaa muistiosoitteen.

Literaali on lausekkeessa esiintyvä vakioarvo, esim. 123 tai "abc".

Operaattorit

muokkaa

Seuraavassa taulukossa arvojen tyypit on mainittu sulkeissa käyttäen merkintää T, joka voi olla mikä tahansa tyyppi (int, double, struct Foo, ...) tai jopa pointterityyppi (esim. double**, jolloin T* on double***).

Nimi Lauseke Operandit Tulos Huomioitavaa
Tähtioperaattori (dereferencing operator) *foo Muistiosoite foo (T*) Osoitteessa foo oleva muuttuja (T) Poistaa tyypistä yhden tähden.
Osoiteoperaattori (address-of operator) &foo Muuttuja foo (T) Muistiosoite, jossa foo sijaitsee (T*) Tähtioperaattorin vastakohta.
Muistissa liikkuminen foo + n
foo - n
n + foo
Muistiosoite foo (T*) ja kokonaisluku n Uusi muistiosoite (T*) n on alkioiden lukumäärä (ei tavuja).
Pointtereiden erotus foo - bar Muistiosoitteet foo (T*) ja bar (T*) Osoitteiden välinen etäisyys (ptrdiff_t). Alkioiden lukumäärä (ei tavuja), negatiivinen jos foo < bar. ptrdiff_t on etumerkillinen kokonaislukutyyppi.
Tyypin koko sizeof(T) Tyypin nimi T T:n viemä tila tavuina (size_t). Sulkeet tyypin ympärillä ovat pakolliset. size_t on etumerkitön kokonaislukutyyppi.
Tyypin koko (lausekkeelle) sizeof foo Muuttuja tai lauseke foo sizeof(T), jossa T on foon tyyppi. Vain foon tyyppi huomioidaan (arvoa ei), joten ei voi käyttää pointteriin merkkijonon tai taulukon koon selvittämiseksi (palauttaa muistiosoitteen koon).
Indeksointi foo[n]
n[foo]
Muistiosoite foo ja kokonaisluku n *(foo + n) Palauttaa alkion indeksillä n. Muotoa n[foo] ei yleensä käytetä.

Määrittelyt

muokkaa

Kaikki seuraavista määrittelevät muuttujan nimeltä foo, tyyppiä double* (ei siis muuttuja nimeltä *foo, tyyppiä double):

double* foo;
double *foo;
double * foo;

Määrittelyssä tyypin yhteydessä näkyvää tähteä ei pidä sekoittaa tähtioperaattoriin.

Useiden pointtereiden määrittelyä samalla kertaa kannattaa välttää, sillä tähdet pitää toistaa jokaiselle pointterille erikseen. Seuraavassa yleinen virhe:

int* foo, bar;  // foo on pointteri, bar kokonaisluku

Yleensä muuttuja kannattaa alustaa samalla kun sen määrittelee, esim:

double* foo = NULL;

NULL on esikääntäjän makro joka määritellään stdlib.h-otsaketiedostossa yleensä (void*) 0.

Merkkijonot

muokkaa

Merkkijonoliteraali "ab" on sama asia kuin taulukko { 'a', 'b', '\0' } ja sen tyyppi on char[3] (mutta periaatteessa char const[3], sillä siihen ei saa kirjoittaa). Merkkijonon pituuden voi selvittää funktiolla strlen(ptr), joka laskee ptr:stä alkaen merkki kerrallaan eteenpäin kunnes löytyy nollamerkki.

Implisiittiset muunnokset

muokkaa

Taulukko muuttuu yleensä laskutoimituksissa välittömästi ensimmäisen alkionsa muistiosoitteeksi. Esim. double[10]-tyyppisestä taulukosta array tulee double*-tyyppinen arvo, kun kirjoitat array + 0. sizeof(array) kertoo taulukon koon tavuina, mutta sizeof(array + 0) kertoo muistiosoitteen koon.

Myös funktion prototyyppiin kirjoitettu taulukkotyyppi muuttuu pointterityypiksi (moniulotteisilla taulukoilla vain ulommaisin taso eli ensimmäinen dimensio muuttuu pointteriksi). Esimerkiksi funktion esittely ja määrittely voidaan kirjoittaa eri tavoin, koska kumpikin kuitenkin tarkoittaa samaa:

void f(char* strings[]);
void f(char** strings) {}