|
|
|
| C-kielen alkeet |
|
Varsinaisissa C-käsikirjoissa asioita
selostetaan usein niin perusteellisesti, että aloittelevalle
ohjelmoijalle olennaisen erottaminen epäolennaisesta voi tuottaa
ongelmia. Tästä tekstistä on tarkoituksella jätetty pois paljon
yksityiskohtia, koska niitä ei tarvita yksinkertaisten
C-ohjelmien toteutuksessa.
|
|
C-ohjelman yleisrakenne
|
|
C-ohjelma koostuu varsinaisesta
C-koodista ja ns. C-esikääntäjälle annettavista ohjeista, joita
kutsutaan direktiiveiksi ja makroiksi. Esikääntäjä muokkaa
ohjeiden perusteella alkuperäistä C-koodia. Kaikki esikääntäjän
ohjeet alkavat symbolilla # (risuaita).
C-ohjelma voi sisältyä yhteen ainoaan tiedostoon tai jakautua
usemman tiedoston kesken. Oletamme seuraavassa, ellei muuta kerrota,
että ohjelma sisältyy yhteen tiedostoon.
C-koodi koostuu määrittelyistä ja lauseista. Määrittelyissä
kuvataan käytettävät muuttujat, tyypit ja funktiot. Lauseissa
kerrotaan, mitä ohjelman tulee varsinaisesti tehdä. Yleisperiaate
on se, että jokainen muuttuja, tyyppi ja funktio on määriteltävä
ennen, kuin sitä voidaan käyttää ohjelmassa.
Seuraavassa on pieni esimerkkiohjelma, joka havainnollistaa yllä
selitettyjä asioita
#include <stdio.h>
#define PII 3.14159
void laske(float r, float *ala, float *tilavuus)
{
*ala = 4 * PII * r * r;
*tilavuus = 4 * PII * r * r * r / 3;
}
int main(void)
{
float pallon_sade, pallon_ala, pallon_tilavuus;
printf("Anna pallon säde: ");
scanf("%f", &pallon_sade);
if (pallon_sade < 0)
printf("Säde ei voi olla negatiivinen. \n");
else
{
laske(pallon_sade, &pallon_ala, &pallon_tilavuus);
printf("Ala: %f \n", pallon_ala);
printf("Tilavuus: %f \n", pallon_tilavuus);
}
return 0;
}
Ohjelman alussa on kaksi direktiiviä,
joista ensimmäinen ilmoittaa, että ohjelmassa tarvitaan
standardikirjastossa stdio.h määriteltyjä käsitteitä ja toinen
määrittelee luvulle 3.14159 tekstimuotoisen synonyymin PII. Näitä
seuraa kahden funktion, laske ja main, määrittelyt.
Ensimmäisessä funktiossa määritellään parametrit (tai
argumentit) r, ala ja tilavuus, jotka kaikki ovat tyypiltään
reaalilukuja (float). Jälkimmäisessä funktiossa määritellään
paikalliset muuttujat pallon_sade, pallon_ala ja pallon_tilavuus,
jotka ovat samoin tyypiltään reaalilukuja. Määrittelyjä seuraa
lauseosa kummassakin funktiossa. Ohjelmassa on lisäksi paljon
yksityiskohtia, joita selitetään myöhemmin tarkemmin.
C-kielessä ei ole rajoitettu sitä, miten määrittelyt ja lauseet
sijoitetaan ohjelmatekstin riveille. Samalle riville saa kirjoittaa
useita asioita ja tyhjiä rivejä saa käyttää. Tekstin saa
aloittaa ja lopettaa mihin sarakkeeseen tahansa. Poikkeuksena tähän
ovat esikääntäjän ohjeet, joiden tulee olla omalla rivillään. Hyvään
ohjelmointityyliin kuuluu kuitenkin se, että ohjelma on
ulkoasultaan siisti ja jonkin yhtenäisen periaatteen mukaan
sisennetty vasemmasta laidasta.
|
|
Tunnukset
|
|
Tunnus on ohjelmassa määritelty
nimi jollekin asialle, kuten muuttujalle, tyypille tai funktiolle.
Tunnuksia koskevat seuraavat säännöt.
- Tunnuksen tulee alkaa
kirjaimella tai alleviivausmerkillä _
- Tunnus saa sisältää
kirjaimia, numeroita tai alleviivausmerkkejä _
- Kirjaimilla tarkoitetaan tässä
englanninkielisiä aakkosia A..Z
- Tunnus kirjoitetaan aina yhteen
- Tunnuksen nimen tulee kuvata
jotenkin sen käyttötarkoitusta
Näiden sääntöjen mukaan
sallittuja tunnuksia ovat mm. Test_value, value_4, x, patarouva ja
LOPPUTULOS. Tunnus Test-value ei ole kelvollinen, koska siinä on
laiton väliviivamerkki. Tunnus 4_roses on laiton, koska se alkaa
numerolla, ja tunnus OPPILAAN NIMI sisältää puolestaan kaksi
sanaa, eikä siten ole sallittu. Sen sijaan tunnus OPPILAAN_NIMI
olisi sallittu.
Tunnus voi olla miten pitkä tahansa, mutta kääntäjästä
riippuen vain N ensimmäistä merkkiä ovat merkitseviä.
ANSI-standardin mukaan N on vähintään 6, käytännössä yleensä
vähintään 31. Käytännössä tämä tarkoittaa sitä, että jos
erotuskynnys N olisi vaikkapa 8 merkkiä, niin tunnukset
Taulukonalku ja Taulukonloppu olisivat ohjelmassa samaa asiaa
merkitseviä tunnuksia, mitä ohjelmoija ei ehkä ole tarkoittanut.
Pienet ja suuret kirjaimet käsitetään C-kielisissä tunnuksissa
eri merkeiksi. Näin ollen tunnukset jussi, Jussi ja JUSSI ovat eri
tunnuksia. Hyvään
ohjelmointityyliin kuuluu kuitenkin se, että ei käytä hyväkseen
tätä mahdollisuutta, koska se vaikeuttaa ohjelman ymmärtämistä.
C-kielessä on lisäksi 32 varattua sanaa, joita ei saa
käyttää tunnuksina. Näitä ovat eri ohjelmarakenteissa kiinteästi
esiintyvät sanat, kuten while, if, else, for, int, float, char...
(huomaa, että varatut sanat kirjoitetaan aina pienellä).
On olemassa joukko sääntöjä, milloin kahdelle eri C-ohjelman käsitteelle
saa käyttää samaa tunnusta. Seuraava yleissääntö on riittävä
useimpiin tilanteisiin: Samassa funktiossa ei tule määritellä
kahta samannimistä tunnusta. |
|
Tietotyypit
|
|
Jokaisella ohjelman käsittelemällä
tietoalkiolla on tyyppi, joka kertoo, miten kyseinen tietokoneen
muistissa oleva tieto on tulkittava. Sama bittiyhdistelmä voi
tarkoittaa esim. kokonaislukua, reaalilukua tai merkkijonoa. Tässä
tekstissä käsittelemme vain yksinkertaisia tyyppejä, joissa
jokainen tietoalkio on ohjelman kannalta yksittäinen atominen
kokonaisuus. Rakenteisia tyyppejä ovat taulukot ja tietueet, joita
käsitellään luentokurssin puolivälissä.
Perustietotyyppejä ovat kokonaisluvut, reaaliluvut ja merkkitieto.
Näillä on useita eri alityyppejä sen mukaan, mikä on kyseisen
tyypin fyysinen esitysmuoto tietokoneessa.
Kokonaislukutyyppejä C-kielessä esittävät tyypit int, long int
ja short int. Ne eroavat toisistaan siten, että niiden esittämät
lukualueet ovat erilaiset. Tavallisesti short int tarkoittaa 16
bitin kokonaislukuja, jolloin lukualue on -32768..32767, ja long int
32 bitin kokonaislukuja, jolloin lukualue on -2147483648..
2147483647. Tyyppi int voi tarkoittaa kumpaa tahansa, ja voi
vaihdella C-järjestelmästä toiseen. Siitä syystä on aina syytä
käyttää tyyppiä long int, jos muuttujan arvo mahdollisesti menee
alueen -32768..32767 ulkopuolelle.
Määritteet short int ja long int voidaan esittää myös lyhemmässä
muodossa short ja long. Esimerkiksi seuraavat määrittelyt ovat
merkitykseltään samoja.
short int laskuri;
short laskuri;
Reaalilukutyyppejä ovat float ja
double, joista edellinen tarkoittaa tyypillisesti 32-bitin
reaalilukuja ja jälkimmäinen 64 bitin (kaksoistarkkuuden)
reaalilukuja. Tyyppien arvoalueet voivat vaihdella riippuen
tietokoneesta ja C-järjestelmästä, mutta yleisesti arvoalue on vähintään
10-37..10+37 ja yksinkertaisen tarkkuuden luvut esitetään vähintään
6 numeron ja kaksoistarkkuuden luvut vähintään 10 numeron
tarkkuudella. Käytännössä käytetään yleensä tyyppiä double.
Reaalilukuvakiot esitetään C-kielessä aina käyttäen
desimaalipistettä, esim. 3.14159. Lukuun voi liittyä myös
eksponenttiosa, joka alkaa kirjaimella 'e' tai 'E' ja jota seuraa
kymmenen potenssi, jolla luvun alussa oleva desimaalinen mantissa
kerrotaan. Esimerkiksi luku 1.045E2 tarkoittaa lukua 1.045 kertaa
102 eli lukua 104.5. Reaalilukuvakiot ovat oletusarvoisesti tyyppiä
double.
Merkkityyppi char esittää yksittäisiä merkkejä tietokoneessa käytetyn
merkkikoodin, esim. ASCII-koodin mukaisesti. Jokaista koodissa
esitettyä merkkiä vastaa jokin kokonaisluku, jonka arvo
tallennetaan muistiin, kun halutaan esittää ko. merkkiä.
Esimerkiksi ns. 7-bittisessä ASCII-koodissa kirjainta 'A' vastaa
koodinumero 65, numeromerkkiä '8' koodi 56 ja merkkiä '+' koodi
43. Kaikkia koodiarvoja ei vastaa jokin näkyvä merkki.
Merkkityyppi char vastaa tietokoneen muistissa yleensä tavun käsitettä.
Sitä voidaan käyttää myös pienten kokonaislukujen esittämiseen,
jos tiedetään, että lukuarvot ovat välillä 0-127.
Merkkivakiot esitetään C-ohjelmassa yksinkertaisten
lainausmerkkien sisällä, esim. 'A', '='. Joillekin tärkeille
merkeille, joille ei ole olemassa näkyvää vastinsymbolia, on oma
esitysmuotonsa. Esimerkiksi '\n' tarkoittaa rivinvaihtomerkkiä ja
'\t' tabulointimerkkiä.
Merkkijonovakiot ovat useista merkeistä koostuvia kokonaisuuksia,
joita käsitellään yhtenä vakiona. Ne erotetaan koodista
kaksoislainausmerkeillä, esimerkiksi: "Otsikko". |
|
Kommentointi |
|
/* ... */ on
yleinen C:n kommentti. Varsinaisesti C kieleen ei kuulu
//-kommenttimerkki mutta nykyään ei ole olemassa ainuttakaan
kääntäjää joka ei sitä ymmärtäisi joten sitä voi myös käyttää.
Esimerkiksi
void main(void) // tämä
teksti on kommenttia
{
int sade, tilavuus; /* tämä on c-kielen kommentti */
printf("Oma maa mansikka jne.");
}
|
|
Muuttujien määrittelyt
|
|
Muuttuja määritellään siten,
että ensin esitetään sen tyyppi ja sitten muuttujalle annettava
tunnus. Määrittely päättyy puolipisteeseen.
int luku;
float keskiarvo;
char merkki;
Useita samantyyppisiä muuttujia
voi määritellä samassa määrittelyssä, kun niiden tunnukset
erotetaan pilkuilla:
short int laskuri, i;
long int luku, arvo, tulos;
double x,y,z;
Eräissä tilanteissa C-järjestelmä
asettaa automaattisesti muuttujille alkuarvoksi nollan. Muulloin
muuttujan alkuarvo voi olla mitä tahansa. Tästä syystä hyvään
ohjelmointityyliin kuuluu se, että ohjelmoija huolehtii aina itse
alkuarvon asettamisesta. Alustus voi tapahtua joko määrittelyn
yhteydessä tai ohjelman lauseosassa ennen, kuin muuttujan arvoa käytetään
mihinkään.
Jos muuttuja alustetaan määrittelynsä yhteydessä, tämä
tapahtuu asettamalla määrittelyssä muuttujan tunnuksen jälkeen
yhtäsuuruusmerkki ja haluttu alkuarvo. Esimerkki:
long luku = 10;
float x = 0.0, y = 0.0;
char merkki = 'A';
|
|
Lauseet ja lausekkeet
|
|
Lause on ohjelmassa oleva
toimintaohje, joka määrittelee, mitä tietyssä tilanteessa tulee
tapahtua. Lauseke on kaava, jonka arvon laskeminen tapahtuu tietyssä
järjestyksessä. Lausekkeita käytetään monien C-kielen lauseiden
osina, mutta niistä voidaan myös muodostaa itsenäisiä lauseita
asettamalla niiden perään puolipiste. |
|
Sijoituslause
|
|
Sijoituslausetta käytetään
muuttujien arvojen vaihtamiseen, esimerkiksi:
weight = 90;
Em. sijoitus luetaan "muuttuja
weight saa arvon 90" ja se EI siis tarkoita vertailua
"weight on yhtäsuuri kuin 90".
Sijoituslauseen yleinen muoto on:
<muuttuja> = <lauseke>;
missä <lauseke> voi olla monimutkainen kombinaatio
muuttujista, operaattoreista ym. operaatioista, esimerkiksi
x = (a + b) * sin(c);
Huomaa lauseen lopussa oleva
puolipiste.
Symboli '=' on operaattori, joka sijoittaa oikealla puolellaan
olevan lausekkeen arvon vasemmalla puolellaan olevan muuttujan
arvoksi. Tästä syystä alla oleva ilmaus on sallittu, vaikka se näyttää
matemaattisesti epätodelta. C-kielessä seuraava merkintä
merkitsee, että otetaan muuttujan vanha arvo, lisätään siihen 1
ja sijoitetaan tulos muuttujan weight uudeksi arvoksi.
weight = weight + 1;
Alla oleva ilmaus on kielletty,
koska 90 on vakio.
90 = weight;
Vastaavasti kielletty on seuraava
tilanne, koska arvo on määritelty vakioksi:
#define arvo 1
arvo = 0;
Samassa sijoituslauseessa voidaan
tehdä useita peräkkäisiä sijoituksia, esimerkiksi:
a = b = c = 0;
Sijoitukset tehdään tässä
tilanteessa oikealta vasemmalle. |
|
Yksinkertainen syöttö ja
tulostus
|
|
Muuttujien ja vakioiden arvoja voidaan tulostaa päätteelle
(kuvaruudulle) käyttäen funktiota printf, joka löytyy
standardikirjastosta stdio.h. Tätä varten tulee ohjelman alussa
olla määrittely
#include <stdio.h>
Funktion printf yleinen muoto on
seuraava:
printf("formaatti",
arvo_1, ..., arvo_n);
Argumentit arvo_1, arvo_2, ... ovat
tulostettavien muuttujien, vakioiden tai lausekkeiden arvoja.
Formaatti on tulostusta ohjaava merkkijono, joka määrittää,
miten argumenttien arvot tulostuvat. Usein formaatti sisältää myös
tekstiä, joka selittää tulostuksen sisältöä, esimerkiksi:
printf("Muuttujien arvot
ovat %d ja %f \n", luku, keskiarvo);
Formaatissa olevat tulostuksen
ohjauskomennot alkavat merkillä %. Sitä seuraava ohjauskirjain määrittää,
minkä tyyppisestä tulosteesta on kysymys. Eräitä vaihtoehtoja
ovat:
%d kokonaisluku
%ld long int tyyppinen kokonaisluku
%c merkki
%s merkkijono
%f reaaliluku ilman eksponenttiosaa
%e reaaliluku eksponenttiosan kanssa
%g reaaliluku joko eksponenttiosan kanssa tai ilman sitä riippuen
luvun arvosta
%-merkin ja ohjauskirjaimen välissä voi olla kokonaisluku, joka
kertoo kuinka leveään kenttään (kuinka monen merkin levyiselle
alueelle) tulostus sijoittuu. Tulostus tulee oletuksena kentän
oikeaan laitaan, eli jos kentän leveys ylittää tulosteen
pituuden, tulee kentän alkuun välilyöntejä. Jos pituusmäärettä
ei ole, tulostus vie juuri sen verran merkkejä kuin on tarpeen
luvun tai muun tulosteen esittämiseen. Esimerkkejä:
printf("Suureen arvo on
%d yksikköä \n", luku);
printf("Suureen arvo on %5d yksikköä \n", luku);
printf("Suureen arvo on %10d yksikköä \n", luku);
Jos muuttujalla luku on arvo 123,
tulostuu näkyviin
Suureen arvo on 123 yksikköä
Suureen arvo on 123 yksikköä
Suureen arvo on 123 yksikköä
Tulostettaessa reaalilukuja on usein tarpeen määritellä myös
tulostettavien desimaalien määrä. Se kerrotaan kentän pituuden jälkeen
muodossa .d . Esimerkkejä:
printf("Suureen arvo on
%f yksikköä \n", arvo);
printf("Suureen arvo on %5.1f yksikköä \n", arvo);
printf("Suureen arvo on %10.2e yksikköä \n", arvo);
Jos muuttujan arvo arvo on 12.345,
tulostuu näkyviin.
Suureen arvo on 12.345000 yksikköä
Suureen arvo on 12.3 yksikköä
Suureen arvo on 1.23e+01 yksikköä
Tulostettavien argumenttien tyyppien tulee vastata määriteltyjä
tulostustyyppejä. Jos näin ei ole, voi tulostua näkyviin mitä
tahansa tai ohjelma saattaa keskeytyä tai käyttäytyä muuten
kummallisesti.
Muuttujien arvo voidaan lukea päätteeltä (näppäimistöltä) käyttäen
standardi-funktiota scanf, mikä on määritelty C-kielen
kirjastossa stdio.h. Tämän funktion muoto on sama kuin
printf-funktiolla paitsi, että scanf joutuu myös muuttamaan
argumenttiensa arvoja. Siksi välitetäänkin argumenttien
osoitteet:
scanf("formaatti", &muuttuja1, ..., &muuttuja1);
Siis jokaisen muuttujan edessä, jolle aiotaan lukea arvo,
tulee olla merkki &, mikä kertoo, että kyseessä on muuttujan
muistiosoite eikä muuttujan arvo. Kaikkien argumenttien
tulee olla muuttujia. Vakioille tai lausekkeille ei voida lukea
uutta arvoa. Formaatissa annetun tyyppikirjaimen ja muuttujan tyypin
tulee vastata toisiaan.
Esimerkki:
int luku;
float arvo;
char ch;
scanf("%d %f %c", &luku, &arvo, &ch);
Väärin:
int i;
scanf("%d", i);
Scanf-funktion formaatissa voidaan
myös määritellä syötteen muotoa hieman samaan tapaan kuin
printf-funktiossakin. Yksinkertaisissa ohjelmissa riittää
kuitenkin se, että annetaan vain muuttujan tyyppiä vastaava
ohjauskirjain.
Scanf lukee syötevirtaa (päätteeltä annettavia merkkejä),
kunnes se on saanut kaikille muuttujille arvon. Syötettävät numeeriset
argumentit erotetaan toisistaan välilyönneillä tai
rivinvaihdoilla . Syötettäessä merkkitietoa scanf lukee
kuitenkin heti seuraavan merkin muuttujan arvoksi. Scanf-funktio
palauttaa arvonaan niiden muuttujien lukumäärän, joille
onnistuttiin lukemaan arvo. Tätä tietoa voi käyttää hyväksi
tutkittaessa, tapahtuiko syötteissä jokin virhe.
Jos luettava tiedosto loppuu (päätteellä annetaan ctrl-D) ennen,
kuin yhtää tietoa on pystytty lukemaan, scanf palauttaa
stdio.h-kirjastossa määritellyn vakioarvon EOF (= tiedoston
loppu).
Sekä printf- että scanf-funktioiden formaatit tarjoavat paljon
erilaisia mahdollisuuksia syötön ja tulostuksen tarkkaan
ohjaamiseen. Tässä on käsitelty näistä mahdollisuuksista vain
pieni osa. |
|
Lausekkeet
|
|
Operaattorit määrittävät, millä
tavalla lausekkeen operandien, kuten vakioiden ja muuttujien arvoja
yhdistellään lausekkeen arvon laskemisessa. C-kielessä on hyvin
runsaasti operaattoreita, mutta seuraavassa käsitellään vain tärkeimpiä,
joita ovat aritmeettiset operaattorit, vertailuoperaattorit ja
loogiset operaattorit.
Aritmeettisia operaattoreita ovat
+ yhteenlasku
- vähennyslasku
* kertolasku
/ jakolasku
% jakojäännös (vain kokonaislukujaossa)
Esimerkki:
weight = density * volume;
Kokonaislukujen jakolaskussa on
tuloksena kokonaislukuosamäärä, ei desimaalilukua. Siten
lausekkeen 3 / 2 arvona on 1, kun taas lausekkeen 3.0 / 2.0 arvona
on 1.5.
Eri operaattoreilla on määrätty prioriteetti: operaatiot *, / ja
% ovat samanarvoisia ja ne suoritetaan ennen + ja - -operaatioita.
Esimerkiksi sijoituksessa
weight = 2 + 5 * 6;
weight saa arvon 32 eikä 42, koska
kertolasku lasketaan ensin. Jos lausekkeessa on useita samanarvoisia
operaatiota, ne suoritetaan vasemmalta oikealle (jollei muuta määritellä).
Esimerkiksi sijoituksessa
weight = 11 * 2 % 3;
weight saa arvon 1 eikä 22.
Yhteen- ja vähennyslaskuoperaattoreita voidaan käyttää myös
yksioperandisena, etumerkin luonteisena operaattorina. Täten
sijoitus
negweight = - weight;
tarkoittaa samaa kuin
negweight = 0 - weight;
Oletuslaskujärjestyksestä voidaan
poiketa käyttämällä sulkumerkkejä ( ), esimerkiksi näin:
weight = (2+5) * 6;
Vertailuoperaattoreita
käytetään tiedon yhtäsuuruuden ja erisuuruuden toteamiseen,
jolloin tuloksena on looginen totuusarvo. Näitä ovat:
a == b tosi, jos a yhtäsuuri kuin b
a != b tosi, a erisuuri kuin b
a < b tosi, jos a pienempi kuin b
a > b tosi, jos a suurempi kuin b
a <= b tosi, jos a pienempi tai yhtäsuuri kuin b
a >= b tosi, jos a suurempi tai yhtäsuuri kuin b
Näissä a ja b voivat olla mielivaltaisia lausekkeita, kunhan
niiden vertailu on mielekästä. Huomaa, että kahdesta merkistä
koostuvat operaattorien symbolit kirjoitetaan yhteen.
Totuusarvoisia lausekkeita voidaan yhdistellä monimutkaisemmiksi
lausekkeiksi käyttämällä loogisia operaattoreita
EI, JA sekä TAI, jotka C-kielessä ilmaistaan seuraavasti (tässä
a ja b voivat olla mielivaltaisia totuusarvoisia lausekkeita.):
! a tosi, jos a on epätosi
a && b tosi, jos a ja b ovat tosia
a || b tosi, jos a tai b on tosi
Esimerkkejä:
Tutkitaan, onko muuttuja x välillä [0 ... 10)
(x >= 0) && (x
< 10)
Tutkitaan, onko muuttuja x välin
(0, 1) ulkopuolella
(x <= 0) || (x >= 1)
Sama asia voidaan ilmaista myös
seuraavasti:
! ((x > 0) && (x
< 1))
Huom. C-kielessä ei ole omaa
totuusarvoa esittävää tietotyyppiä, kuten Pascalin tietotyyppi
Boolean. C-ohjelmissa arvo 0 tulkitaan epätodeksi ja kaikki muut
arvot todeksi. Koska tämä on ohjelman ymmärrettävyyden kannalta
varsin hämäävää, kannattaa ohjelman alussa määritellä
totuusarvoille omat symbolit:
#define FALSE 0
#define TRUE 1
Aritmeettisten, vertailu- ja
loogisten operaattorien keskinäinen prioriteettijärjestys on
seuraava:
!
aritmeettiset operaattorit
<, >, <=, >=
==, !=
&&
||
C:ssä on paljon muitakin operaattoreita ja eri prioriteettitasoja
on paljon, siksi kannattaa käyttää
sulkuja.
Esimerkiksi:
a < b || a == 5 &&
average(i,j,k) < 12
on samanarvoinen lauseke kuin
(a < b) || ((a == 5)
&& (average(i,j,k) < 12))
mutta jälkimmäisen ymmärtäminen
on huomattavasti helpompaa.
Lauseke lasketaan pääsääntöisesti vasemmalta oikealle ja
laskenta lopetetaan heti, kun tulos voidaan todeta varmuudella.
Esimerkiksi seuraavassa lausekkeessa ei tapahdu nollalla jakoa, jos
b:n arvo on 0, koska osalausekkeen (b!=0) arvo on tällöin epätosi
ja silloin koko lausekkeen arvo on aina epätosi.
(b != 0) && (a/b >
20)
|
|
Hieman tyyppimuunnoksista
|
|
Usein aritmeettisissa lausekkeissa
esiintyy eri tyyppisiä operandeja, kuten kokonaislukuja ja
reaalilukuja tai eri pituisiksi määriteltyjä kokonaislukuja.
C-kieli määrittää, mikä tulee tällöin koko lausekkeen arvon
tyypiksi.
Perussääntö on se, että sijoituslauseen oikea puoli lasketaan
ensin valmiiksi ja lausekkeen arvo muunnetaan sitten vasemman puolen
edellyttämään muotoon. Tarkastellaan esimerkkeinä seuraavia
sijoituksia eri muuttujiin:
double d;
long i;
char c;
float f;
d = 0.123456789;
i = d;
Tapauksessa, jossa
kokonaislukumuuttujaan sijoitetaan reaalilukumuuttujan tai
-lausekkeen arvo, desimaaliosa yksinkertaisesti katkaistaan pois,
eli muuttuja i saa arvon 5. Jos tulos ei mahdu kokonaisluvun
lukualueeseen, tapahtuu virhe.
c = '0';
i = c;
Tässä sijoitetaan merkkimuuttujan
arvo kokonaislukumuuttujaan ja tulokseksi tulee merkin koodiarvo,
esimerkiksi käytettäessä ASCII-koodia muuttuja i saa arvokseen
48. Tällaista merkki- ja
kokonaislukutiedon yhdistelyä tulee kuitenkin välttää.
f = d; /* f=0.123456 */
Sijoitettaessa kaksoistarkkuuden
lukua yksinkertaisen tarkkuuden muuttujaan osa merkitsevistä
numeroista katoaa.
i = 100;
f = i;
Sijoitettaessa kokonaislukuarvo
reaalilukumuuttujaan, lukuarvo muunnetaan reaaliluvuksi, eli
muuttuja f saa arvon 100.0. Osa merkitsevistä numeroista voi
silloinkin kadota:
long i;
i = 123456789;
f = i; /* f = 1.234567E9 */
Lausekkeiden tuottaman arvon tyyppi
määräytyy seuraavien sääntöjen mukaan:
1. Tyypit char ja short int muunnetaan ensin int-muotoon. Esim. jos
c1 ja c2 ovat tyyppiä short, niin lausekkeen c1 + c2 tyyppi on int.
2. Jos molemmat operandit ovat samaa tyyppiä niin tämä on myös
tuloksen tyyppi. Esim. int + int tuottaa tulokseksi tyypin int,
float + float => float.
3. Jos operandit ovat erityyppisiä, arvoalueeltaan pienempi tyyppi
muunnetaan ennen operaatiota suuremmaksi tyypiksi. Esim. int + float
=> float + float => float, tai int + long => long + long
=> long.
Ohjelmoija voi myös itse määritellä lausekkeen tyypin. Tämä
tapahtuu asettamalla halutun tyypin nimi suluissa lausekkeen tai sen
osan eteen. Esimerkkejä:
double ratio;
ratio = 5 / 2;
Tulos on 2.0, koska kokonaisjako
tuottaa arvon 2 ja se muutetaan reaaliluvuksi.
ratio = (double) 5 / 2;
Tulos on 2.5, koska tyypin muunnos
suoritetaan ennen jakolaskua. Laskutoimitus ja muunnos tapahtuu
seuraavasti:
(double) 5 / 2 = ((double) 5)
/ 2 = 5.0 / 2 = 2.5;
Seuraavassa lasketaan
reaalukujakolaskussa osamäärä:
ratio = (int) 5.0 / (int)
2.0;
Kumpikin reaaliluku muutetaan ensin
kokonaisluvuksi ja sitten tehdään kokonaislukujako, jonka tulos on
2. Tämä sijoitetaan sitten reaalilukumuuttujaan, jonka arvoksi
tulee 2.0. |
|
Valintalauseet
|
|
Valintalauseet ovat lauseita,
joilla voidaan valita haluttu toimenpide kahdesta tai useammasta
toiminnosta. C-kielessä on kaksi valintarakennetta, if-else-rakenne
ja switch-rakenne, joista käsittelemme tässä vain edellistä.
If-lauseen yleinen muoto on esitetty alla. Jos suluissa oleva
lauseke on tosi, niin suoritetaan lause1, muuten suoritetaan lause2.
Huomaa puolipiste lauseen 1 lopussa ja sulut lausekkeen ympärillä.
if (lauseke)
lause1;
else
lause2;
Esimerkki:
if (x < 0)
printf("Muuttujan arvo ei saa olla negatiivinen \n");
else
laske_suureen_arvo(x);
Puhuttaessa if-rakenteesta käytetään
yleisesti seuraavia ilmauksia rakenteen eri osista: "if-lauseen
ehto", "if-haara" ja "else-haara".
If-lauseen yksinkertaisemmassa muodossa else-haara puuttuu.
if (x < 0)
x = -x;
Jos if-tai else-haarassa halutaan
suorittaa useita lauseita, ne kootaan yhteen lohkosulkeilla {}:
if (lauseke)
{
lause_1;
...
lause_n;
}
Jos ohjelman suoritus etenee tähän
haaraan, suoritetaan kaikki ko. haarassa oleva lauseet. Ne voivat
sisältää uusia if-lauseita, funktiokutsuja, jne. Ts. hyvinkin
monimutkaisten lauseiden rakentaminen on mahdollista. Esimerkki:
if (x < 0)
{
printf("Virhe. Muuttujan arvo on negatiivinen. \n");
virhelaskuri = virhelaskuri + 1;
}
else
{
if (x < 10)
kasittele_pieni_arvo(x);
else
kasittele_suuri_arvo(x);
laskuri = laskuri + 1;
}
Huomaa, että lohkosulkujen jälkeen
ei tule puolipistettä.
Jos if-haarassa on toinen if-lause, johon kuuluu myös else-haara,
on periaatteena se, että else-haara vastaa aina lähinnä edeltävää
if-haaraa. Esimerkki:
if (x < 1000)
if (x < 0)
printf("x on negatiivinen. \n");
else
printf("x on välillä 0-1000. \n");
Jos else-haaran tulee liittyä
nimenomaan ulompaan if-lauseeseen, sisempi lause täytyy laittaa
lohkosulkeiden sisään:
if (x< 1000)
{
if (x < 0)
printf("x on negatiivinen. \n");
}
else
printf("x on vähintään 1000. \n");
Laittamalla else-haaraan uusi
if-lause, voidaan muodostaa ns. ketjutettuja if-lauseita, jotka
soveltuvat vaikkapa aineiston luokitteluun:
if (luku < 0)
negatiiviset = negatiiviset + 1;
else if (luku < 10)
yksinumeroiset = yksinumeroiset + 1;
else if (luku < 100)
kaksinumeroiset = kaksinumeroiset + 1;
else
suuret = suuret + 1;
|
|
Toistolauseet
|
|
Toistolauseet suorittavat samaa
toimintoa uudelleen niin kauan, kuin toistoa kontrolloiva ehto on
voimassa. C-kielessä on kolme eri rakennetta, joiden avulla
toistolause voidaan rakentaa.
while-lause toistaa lausetta niin kauan, kuin ehtolauseke on tosi.
Lauseen yleinen muoto on seuraava (huomaa sulut lausekkeen ympärillä):
while(lauseke)
lause;
Jos toistettavia lauseita on useita, ne kootaan lohkosulkujen sisään.
Esimerkki: Seuraava koodi laskee syöterivin pituuden. Rivi päättyy
rivinvaihtomerkkiin '\n' ja standardifunktio getchar palauttaa
seuraavan merkin rivillä (huom, getchar palauttaa tyypiä int
olevan tiedon).
int count = 0;
int ch = ' ';
ch = getchar();
while (ch != '\n')
{
ch = getchar();
count = count + 1;
}
Jos while-lauseen lauseke on heti
alussa epätosi, niin lausetta ei suoriteta lainkaan. Esimerkiksi
seuraava rakenne varmistaa, että käyttäjän antama luku on
positiivinen. Jos annettu luku on heti positiivinen, ei silmukkaa
suoriteta lainkaan.
int luku;
printf("Anna uusi luku. \n");
scanf("%d", &luku);
while (luku < 0)
{
printf("Luku ei saa olla negatiivinen. \n");
printf("Anna uusi luku. \n");
scanf("%d", &luku);
}
Seuraava ohjelma laskee N:n ensimmäisen
neliöluvun summan:
#include <stdio.h>
int main(void)
{
int n, i, total = 0;
printf("Anna n:");
scanf("%d", &n);
i = n;
while (i > 0)
{
total = total + i * i;
i = i - 1;
}
printf("%d:n ensimmäisen neliöluvun summa: %d \n", n, total);
return 0;
}
do-lause on hyvin samantapainen
kuin while-lause. Siinä silmukan jatkamisehto testataan vasta
silmukan lopussa, eli silmukka suoritetaan aina vähintään
kerran . Do-lauseen yleinen muoto on seuraava (huomaa sulut
lausekkeen ympärillä):
do
lause
while (lauseke);
Silmukkaa suoritetaan niin kauan, kuin lausekkeen arvo on tosi.
Esimerkki:
int luku;
do
{
printf("Anna uusi luku. \n");
scanf("%d", &luku);
} while (luku < 0);
Myös for-lause on
toiminta-ajatukseltaan samantapainen kuin while-lause. Se on vain
hieman kompaktimpi esitysmuodoltaan.
for(lauseke1; lauseke2; lauseke3)
lause;
Ennen toistoa lasketaan lausekkeen 1 arvo, joka voi sisältää
esimerkiksi tarvittavan kierroslaskurin alustuksen. Toistettavaa
lausetta suoritetaan niin kauan kuin lausekkeen 2 määrittämä
ehto on voimassa ja jokaisen toiston jälkeen lasketaan lausekkeen 3
arvo. Käytännössä for-lause vastaa siis seuraavaa
while-lausetta.
lauseke1;
while(lauseke2)
{
lause;
lauseke3;
}
For-lausetta käyttämällä rivin
pituuden laskeminen näyttää seuraavalta. Huomaa, että laskuri
count alustetaan nyt arvoon -1, koska silmukka suoritetaan aina vähintään
kerran
int count;
int ch = ' ';
for (count = -1; ch != '\n'; count = count + 1)
ch = getchar();
Ohjelma neliölukujen summan
laskemiseen.
#include <stdio.h>
int main(void)
{
int n, i, total = 0;
printf("Anna n: ");
scanf("%d", &n);
for (i = n; i > 0; i = i - 1)
total = total + i*i;
printf("%d:n ensimmäisen neliöluvun summa: %d \n",
n, total);
return 0;
}
For-lauseen lausekkeet voidaan jättää
pois, jos ne ovat tarpeettomia. Edellisen ohjelman silmukka
voitaisiin kirjoittaa myös näin.
printf("Anna n: ");
scanf("%d", &n);
i = n;
for (; i > 0; i = i -1)
total = total + i*i;
Seuraava ohjelma tulostaa
muunnoksen Celcius-asteista Fahrenheit-asteiksi.
#include <stdio.h>
int main(void)
{
int alku, loppu, c_aste, f_aste;
printf("Tulostan lämpötilamuunnosasteikon \n");
printf("Anna muunnosasteikon ala- ja ylärajat. \n");
scanf("%d%d", &alku, &loppu);
printf("Celsius Fahrenheit \n\n");
for (c_aste = alku; c_aste <= loppu; c_aste = c_aste+1)
{
f_aste = (int) (9.0 / 5 * c_aste + 32);
printf("%d \t %d \n", c_aste, f_aste);
}
return 0;
}
|
|
Lohkot
|
|
Edellä on jo puhuttu
lohkosulkeista {}, joiden sisälle voidaan koota useampia lauseita
yhdeksi kokonaisuudeksi. Tälläisesta asiasta käytetään usein
termiä koottu lause. Lauseiden lisäksi lohko voi sisältää
myös uusia lohkoja ja muuttujien määrittelyjä. Viimeksi mainittu
on mielekästä, jos muuttujia tarvitaan vain tämän lohkon sisällä.
Seuraava ohjelma tekee muunnokset Celcius-ja Fahrenheit-asteiden välillä
molempiin suuntiin.
#include <stdio.h>
int main(void)
{
int muunnos;
printf("Lämpotilamuunnoasteikko Celcius-asteista \n");
printf("Fahrenheit-asteiksi tai päinvastoin.\n");
printf("Kumpi muunnos tehdään 1) C->F vai 2) F->C). \n");
printf("Vastaa 1 tai 2: ");
scanf("%d", &muunnos);
if (!((muunnos ==1) || (muunnos == 2)))
printf("Annoit väärän muunnoksen. \n");
else
{
int alku, loppu, c_aste, f_aste;
printf("Anna muunnosasteikon ala- ja ylärajat. \n");
scanf("%d%d", &alku, &loppu);
if (muunnos == 1)
{
printf("Celsius Fahrenheit \n\n");
for (c_aste = alku; c_aste <= loppu; c_aste = c_aste+1)
{
f_aste = (int) (9.0 / 5 * c_aste + 32);
printf("%d \t %d \n", c_aste, f_aste);
} /* for */
} /* if */
else
{
printf("Fahrenheit Celcius \n\n");
for (f_aste = alku; f_aste <= loppu; f_aste = f_aste+1)
{
c_aste = (int) (5 * (f_aste - 32) / 9.0);
printf("%d \t %d \n", f_aste, c_aste);
} /* for */
} /* else */
} /* else */
return 0;
} /* main */
Huomaa, että lohkojen lopussa ei
ole puolipistettä. Huomaa myös lohkojen lopussa olevat kommentit,
jotka helpottavat lohkojen erottumista toisistaan. Niitä kannattaa
käyttää.
|
|
Osoitinmuuttujat
|
|
Kutakin ohjelman muuttujaa vastaa
jokin osoite tietokoneen muistissa, johon muuttujan sisältämä
data on tallennettu. Eräissä tilanteissa on tarpeen saada selville
muuttujan osoite, ja tätä varten käytetään merkintää
&muuttuja. Symboli & on operaattori, joka palauttaa sitä
seuraavan muuttujan muistiosoitteen. Esimerkiksi, kun luetaan
ohjelman muuttujille arvoja, välitetään scanf-funktiolle
kyseisten muuttujien osoitteet, joihin scanf sitten tallettaa luetut
data-arvot. Huomaa, että &-operaattorin ja muuttujanimen väliin
EI tule välilyöntiä.
Seuraava kutsu lukee arvot muuttujille luku ja arvo.
scanf("%d%d",
&luku, &arvo);
Muuttujan tyyppinä voi olla myös
muistiosoite, joka osoittaa tietyn tyyppiseen muuttujaan. Tällöin
muuttujan määrittelyssä muuttujan tunnusta edeltää symboli '*'.
Seuraavassa määritellään kaksi muuttujaa luku ja p, joista
edellinen on tavallinen kokonaislukumuuttuja ja jälkimmäinen on
muuttuja, johon voidaan tallentaa kokonaislukumuuttujan
muistiosoite.
int luku, *p;
Seuraavalla sijoituksella muuttujan
luku muistiosoite voidaan tallettaa muuttujaan p:
p = &luku;
Kun halutaan viitata
osoitinmuuttujan osoittaman muistialueen sisältöön, käytetään
merkintää *muuttuja. Symboli * on operaattori, joka kertoo, että
sitä seuraavan osoitinmuuttujan sisällön asemasta otetaankin
data, joka on talletettu ko. osoitteeseen. Esimerkissämme
sijoitamme ensin muuttujaan luku arvon 3.
luku = 3;
Sen jälkeen voimme käsitellä
luku-muuttujan sisältöä myös muuttujan p avulla, koska p sisältää
saman muistipaikan osoitteen kuin johon tunnus luku viittaa.
Sijoitus
*p = 4;
saa aikaan sen, että ko.
muistipaikan sisällöksi tulee 4 (ja samalla siis muuttujan luku
arvoksi tulee 4).
|
|
Funktiot
|
|
Funktio on itsenäinen
ohjelmakoodin yksikko, joka on suunniteltu määrätyn tehtävän
suorittamiseen. Funktio saa tarvitsemansa tiedot argumentteinaan ja
palauttaa yleensä yhden arvon.
Funktiot selkeyttävät ohjelmaa ja vähentävät ohjelman
kirjoittamiseen kuluvaa aikaa, koska jokin monta kertaa tarvittava
toimenpide tarvitse toteuttaa vain yhden kerran, funktiona. Sitä
voidaan sitten kutsua useita kertoja ohjelman eri kohdista eri
argumenttien arvoilla. Usein silloinkin, kun tehtävä halutaan
suorittaa vain kerran, kannattaa sitä varten kirjoittaa funktio,
koska tämä selkeyttää ohjelman rakennetta. Funktiot tekevät
ohjelmasta modulaarisen ja helpottavat sen lukemista, muuttamista ja
korjaamista. Pääohjelman tulisi toimia lähinnä sisällysluettelona,
joka sisältää määrittelyjä ja funktiokutsuja. |
|
Funktion rakenne
|
|
Funktio koostuu otsikosta ja
lohkosta. Otsikko sisältää seuraavat tiedot: funktion palauttaman
arvon tyyppi, funktion tunnus, argumenttien tyypit ja tunnukset.
Funktion lohkossa voi määritellä funktion sisäisiä muuttujia,
joihin voi viitata vain funktion omassa lauseosassa. Tällaisten,
ns. paikallisten muuttujien arvot tuhoutuvat
kutsukertojen välillä, eli näihin sisäisiin muuttujiin ei voi jättää
mitään tietoa "varastoon" odottamaan funktion seuraavaa
kutsukertaa.
Lohko sisältää funktion toiminnan määrittävät lauseet. Niissä
määritellään myös, minkä arvon funktio palauttaa kutsujalleen.
Funktion muodollinen määrittelytapa on seuraava:
<tyyppi> <funktion_tunnus> ( <arg1 tyyppi>
<arg1 tunnus>, <arg2 tyyppi>...)
{
<paikallisten muuttujien määrittelyt>;
<lauseita>;
return <lauseke>;
}
Funktion palauttama arvo on return-komentoa seuraavan lausekkeen
arvo. return on tavallisesti funktion viimeinen lause, mutta se voi
sijaita muuallakin. Samassa funktiossa voi olla myös useita eri
return-lauseita, jolloin funktion toiminta voi päättyä useammassa
paikassa. |
|
Funktion kutsuminen
|
|
Funktiota kutsutaan muualta
ohjelmasta muodossa
<funktion_tunnus> (arg1, arg2, ...)
jossa arg1, arg2... ovat funktiolle välitettäviä argumentteja.
Esimerkiksi:
muuttuja = funktio(arg1,
arg2);
Argumenttien sisältämät tiedot
tai muistiosoitteet siirtyvät funktion omaan käyttöön kutsun
ajaksi. Kun funktio on suorittanut tehtävänsä, sen palauttama
arvo (return-lausetta seuraavan lausekkeen arvo) palautetaan
kutsukohtaan, jossa sitä voidaan käyttää laskennassa mukana.
Muun ohjelman suoritus jatkuu kutsukohdasta eteenpäin.
Funktion pitää olla määritelty ennen, kuin sitä kutsutaan. Tämän
vuoksi kannattaa kirjoittaa kaikki funktiot ennen pääohjelmaa. Ne
kannattaa kirjoittaa kuitenkin järkevässä järjestyksessä eli
siten, että ensiksi kutsuttava funktio on ensimmäisenä. Tällöin
suuremmatkin ohjelmat pysyvät helpommin hahmotettavina. Suuret
ohjelmat jaetaan useisiin tiedostoihin, ja tällöin funktioiden määrittelyissä
tarvitaan usein ns. prototyyppejä. Näistä enemmän oppikirjassa.
Funktion kutsussa argumentit on annettava samassa järjestyksessä,
kuin ne on määritelty funktion otsikossa. Lisäksi niiden tyyppien
on oltava samat kuin otsikossa.
Esimerkkiohjelma ilman funktioita:
#include <stdio.h>
int main(void)
{
int a, b, erotus;
printf("Anna kaksi lukua: ");
scanf("%d %d", &a, &b);
erotus = a - b;
printf("%d - %d = %d\n", a, b, erotus);
return 0;
}
Sama funktion avulla:
#include <stdio.h>
int laske(int c, int d)
{
int ero;
ero = c - d;
return ero; /* arvon palautus pääohjelmalle */
}
int main(void)
{
int a, b, erotus;
printf("Anna kaksi lukua: ");
scanf("%d %d", &a, &b);
erotus = laske(a, b); /* HUOM ei: laske(b, a); */
printf("%d - %d = %d\n", a, b, erotus);
return 0;
}
|
|
Proseduuri
|
|
Proseduureista puhutaan silloin,
kun funktio ei palauta mitään arvoa, jolloin sen tyyppi on void.
C-kielessä ei ole varsinaisia proseduureja toisin kuin monessa
muussa ohjelmointikielessä, vaan C:ssä on proseduurimaisia
funktioita.
Mikäli funktio ei palauta mitään arvoa, sitä voidaan kutsua
suoraan omana lauseenaan:
funktio2(arg1, arg2);
Esimerkki:
#include <stdio.h>
int laske(int c, int d)
{
return (c - d);
}
void tulosta(int yksi, int kaksi, int yhteensa)
{
printf("%d - %d = %d\n", yksi, kaksi, yhteensa);
/* ei palauteta mitään arvoa */
}
int main(void)
{
int a, b, erotus;
printf("Anna kaksi lukua: ");
scanf("%d %d", &a, &b);
erotus = laske(a, b); /* HUOM ei: laske(b, a); */
tulosta(a, b, erotus); /* HUOM argumentit vain tässä järj.*/
return 0;
}
|
|
Muuttujien näkyvyys
|
|
Paikalliset muuttujat näkyvät ainoastaan funktion sisällä, eikä niihin voida
viitata missään muualla. Tästä seuraa, että toisessa funktiossa
voidaan määritellä samanniminen ja saman- tai erityyppinen
muuttuja, mutta sillä ei ole mitään tekemistä toisen funktion
samannimisen paikallisen muuttujan kanssa.
Funktion paikallisen muuttujan arvo ei säily kahden kutsukerran välillä
muuttumattomana. Esimerkiksi, jos haluat tarkkailla, montako kertaa
jotakin funktiota kutsutaan, älä laita laskuria itse tutkittavaan
funktioon vaan sitä kutsuvaan funktioon.
Jos funktio fun1 kutsuu funktiota fun2, ei fun1:ssä voida viitata
fun2:en muuttujiin tai päinvastoin. Mikäli fun1:sta halutaan välittää
informaatiota fun2:lle, on käytettävä argumenttivälitystä.
Globaalit muuttujat määritellään ohjelmatiedostossa funktioiden
ulkopuolella, useimmiten tiedoston alussa. Niihin voidaan viitata
suoraan kaikista ohjelmatiedoston funktioista ja funktioissa voidaan
muuttaa niiden arvoja. Hyvään ohjelmointityyliin kuuluu,
että käytetään mahdollisimman vähän globaaleja muuttujia,
koska niiden runsas käyttö vaikeuttaa ohjelmakoodin ymmärtämistä. |
|
Argumenttien välitys
|
|
Funktion argumentteja
(=parametreja) voidaan välittää joko arvoparametreina tai
muuttuja-parametreina. Edellisessä tapauksessa välittyy vain
funktion kutsussa olevan argumenttilausekkeen - tai muuttujan arvo
(itse asiassa kopio siitä) funktion otsikossa määritellyn
muodollisen parametrin arvoksi. Sitä voidaan tällöin muuttaa
funktion sisällä ilman, että kutsuvassa ohjelmassa olevat arvot
muuttuvat. Esimerkki:
int summa(int a, int b)
{
a = a + b;
return (a);
}
Kutsuttaessa tätä funktiota
seuraavalla tavalla, säilyvät muuttujien x ja y arvot ennallaan,
koska argumentin a arvon muutos kohdistuu x:n arvon kopioon.
luku = summa (x, y);
Haluttaessa muuttaa kutsuvan
ohjelman muuttujien arvoja, välitetään argumentteina muuttujien
arvojen sijasta muuttujien osoitteet. Tällöin funktion otsikossa määritelty
muodollinen parametri viittaa samaan muistipaikkaan kuin todellinen
argumentti ja edellisen arvon muutos vaikuttaa suoraan jälkimmäisen
arvoon. Esimerkiksi scanf-funktio menettelee juuri näin.
Kun halutaan sallia tiedon siirtäminen muuttujaparametrien avulla,
asetetaan funktion määrittelyssä nämä parametrit
osoitinmuuttujiksi. Funktion kutsussa käytetään silloin
muuttujien osoitteita. Esimerkki:
#include <stdio.h>
void kysy(int *luku1, int *luku2)
{
int a, b;
printf("Anna kaksi lukua: ");
scanf("%d %d", &a, &b);
*luku1 = a;
*luku2 = b;
}
int laske(int c, int d)
{
return (c - d);
}
void tulosta(int yksi, int kaksi, int yhteensa)
{
printf("%d - %d = %d\n", yksi, kaksi, yhteensa);
}
int main(void)
{
int a, b, erotus;
kysy(&a, &b);
erotus = laske(a, b);
tulosta(a, b, erotus);
return 0;
}
Tämän C-ohjelman pääohjelma on
lähinnä sisällysluettelo ja kaikki muu tapahtuu funktioissa. Eli
tämä on esimerkki hyvästä C-ohjelmasta, vaikka ohjelma ei teekään
juuri mitään järkevää.
|
|