C-ohjelmoinnin peruspalikat¶
Kuten jo aiemmin luvattiin, kurssilla edetään suurinpiirtein samassa järjestyksessä kuin Ohjelmoinnin alkeissa. Ainakin siinä määrin kuin se C:n kanssa on mahdollista. Tällä kertaa on siis luvassa ohjelmoinnin kahden peruskiven uusiksi hiominen:
muuttujat
ja funktiot
, joita ilman on hankala tehdä yhtään mitään. Erityisesti C:ssä jossa ei ole edes pääohjelmaa.C:n muotoinen kehikko¶
Osaamistavoitteet: Tämän osion jälkeen osaat kirjoittaa C-ohjelman jonka voi kääntää ja suorittaa.
Pakolliset symbolit¶
Toisin kuin Pythonissa, jossa kooditiedosto voi sisältää pelkästään yhden koodirivin joka tekee jotain, C:ssä vaaditaan tiettyjä perusasioita. Edellisen materiaalin lopussa nähtiin esimerkki hyvin yksinkertaisesta C-ohjelmasta:
#include <stdio.h>
int main() {
printf("aasisvengaa!\n");
return 0;
}
Varsinainen suoritettava koodi on edelleen vain yksi rivi, mutta ympärille on ilmestynyt vähän kaikenlaista. Näitä käytiin läpi alustavasti edellisessä materiaalissa. Todettakoon heti alkuun, että ensimmäisen rivin
#
-merkki ei tee rivistä kommenttia
(//
sen sijaan tekee!). Kaikki #
-merkillä alkavat rivit ovat sen sijaan rivejä, jotka on tarkoitettu esikääntäjän
käsiteltäväksi. Lyhyesti selitettynä esikääntäjä on kääntäjän
osa, joka muotoilee kooditiedostoa juuri näillä #
-merkeillä merkittyjen direktiivien
mukaan. Tässä vaiheessa näistä direktiiveistä käsitellään vain #include
, joka siis vastaa toiminnaltaan jotakuinkin Pythonin import-käskyä
. C:ssä tarvitaan includea heti opiskelun alussa, koska toisin kuin Python, C ei ei tarjoa tulostustfunktiota sisäänrakennettuna – funktio on erikseen otettava käyttöön lisäämällä ohjelmaan kirjasto
stdio (Standard Input / Output), josta se löytyy. Ohjelmassa tämä tehdään siis käskyllä #include <stdio.h>
. Kirjastojen nimen lopussa on tunnuksena
.h
, joka tulee sanasta header. Tämä .h
-tiedosto on kirjaston otsikkotiedosto
, joka sisältää kirjaston funktioiden, vakioiden ja mahdollisten muuttujien esittelyn. Varsinainen kirjaston koodi on sitten stdio.c-tiedostossa. Tosin sitä emme yleensä pääse näkemään, koska kääntäjät toimittavat standardikirjastot valmiksi käännettyinä binääreinä. Otsikkotiedostoista lisää myöhemmin kurssilla. Koodia tarkastellessa saatetaan huomata yksi ero Pythonin importiin: kun printf-funktiota kutsutaan, ei kirjoitetakaan stdio.printf, vaan pelkästään printf. C:ssä include toimii siis itse asiassa samalla tavalla kuin Pythonin from moduuli import *
eikä erillisiä nimiavaruuksia ole käytössä. Tämä tarkoittaa, että funktioiden nimeämisessä saa olla tavallista tarkempana. Tässä käytetty kirjasto,
stdio
, sisältää funktioita jotka liittyvät tulostukseen ja syötteen lukemiseen. Käytännössä tässä vaiheessa se tulee käyttöön kaikissa ohjelmissa. Muita tässä vaiheessa tai lähitulevaisuudessa tarpeellisia kirjastoja ovat ainakin math
ja stdlib
. Näiden tarpeesta kerrotaan erikseen kun ne astuvat mukaan kuvioihin, jolloin ne myös lyhyesti esitellään. C:lle ei ole oikein olemassa samanlaista, yhtä käyttäjäystävällistä dokumentaatiovarastoa kuin Pythonille, joten suosittelemme tiedon etsimiseen joko C-kielen oppikirjaa tai menetelmää ”kato vaikka googlesta”.Toinen merkittävä ero on se, että C-ohjelmaa ei voi tehdä ilman
funktioita
– pääohjelmaa ei siis ole. Sen sijaan on pääfunktio
, joka on oletuksena main
ja jonka paluuarvo on oletukseltaan muuttujatyyppiä int
. Paluuarvon muuttujatyyppi on int, koska ohjelma palauttaa päättyessään koodin, joka kertoo siitä miten se lopetettiin – tällä arvolla on sitten käyttöä esimerkiksi useita ohjelmia peräkkäin suorittavissa skriptoissa, jotka paluuarvon perusteella päättelevät muun muassa sen, onnistuiko ohjelman suoritus. Syvemmälle C-funktion sielunelämään mennään tässä materiaalissa, mutta ei ihan vielä. Tässä vaiheessa riittää, että ymmärrät mikä on tämä mystinen int main() {
-rivi – se siis määrittelee pääfunktion, jolla ei ole parametreja
. Rivin lopussa oleva aaltosulku aloittaa koodilohkon. Kaikki tämän sulun ja sitä vastaavan sulkevan sulun välissä oleva koodi kuuluu funktion sisälle. Muuttujien muodonmuutos¶
Osaamistavoitteet: Tämän osion jälkeen hallitset C-muuttujien perusteet. Tähän sisältyvät muuttujien tyyppien ymmärtäminen ja määrittely, sekä jonkinasteinen ymmärrys siitä millä tavalla muuttujat oleskelevat tietokoneen muistissa. Vilkaisemme myös miten muuttujia tulostetaan ja vaihdetaan tyypistä toiseen.
Kertaustuokio¶
Ennen
C:n muuttujien
sielunelämää, on hyvä palautella hieman mieleen mitä Ohjelmoinnin alkeissa sanottiin Pythonin muuttujista
. Perusajatus oli, että muuttuja yhdistää ohjelmassa esiintyvän nimen tietokoneen muistissa
olevaan arvoon. Hyvänä kertauksena toiminee tämä tuttu animaatio: Näin siis Pythonissa, josta myös useaan otteeseen painotettiin, että
muuttuja
on viittaus muistissa olevaan arvoon
eikä varsinaisesti sisällä arvoa. Tämä ilmenee animaatiossa kohdassa y = x
, jossa molemmat muuttujat osoittavat samaan arvoon. Tämä voitiin myös osoittaa Python-tulkissa id-funktiota käyttämällä: >>> a = 5
>>> id(a)
10894208
>>> b = a
>>> id(b)
10894208
Tässä nähdään että a ja b eivät pelkästään ole arvoltaan identtisiä, ne ovat myös sama
objekti
. Perusmuuttujatyyppien kuten numeroiden ja merkkijonojen kanssa tällä ei tosin ollut suuremmin merkitystä, koska nämä tietotyypit
ovat Pythonissa luonteeltaan muuntumattomia
. Sen sijaan listojen
kanssa nähtiin jo todellisia seurauksia:>>> a = [1, 2, 3]
>>> b = a
>>> b.append(4)
>>> a
[1, 2, 3, 4]
Tälle käyttäytymiselle oli ainakin yksi tyypillinen sovellus:
funktioille
annettiin argumentiksi
listoja, joita funktio muokkasi, mutta ei palauttanut niitä. Niitä ei palautettu, koska se ei ollut tarpeen – funktion muokkaama lista oli sama lista kuin se, joka sille annettiin. Esimerkkinä tästä toimikoon vaikka kokoelmaohjelmaesimerkin lisaa-funktio: def lisaa(kokoelma):
print("Täytä lisättävän levyn tiedot. Jätä levyn nimi tyhjäksi lopettaaksesi")
while True:
levy = input("Levyn nimi: ")
if not levy:
break
kokoelma.append({
"artisti": input("Artistin nimi: "),
"albumi": levy,
"n": kysy_luku("Kappaleiden lukumäärä: "),
"kesto": kysy_aika("Kesto: "),
"vuosi": kysy_luku("Julkaisuvuosi: ")
})
Vertaustuokio¶
C:ssä
muuttuja
on sidottu tiukemmin arvoonsa, siinä määrin että sen kohdalla todella voidaan sanoa että muuttuja sisältää arvon – muuttuja on ohjelmoijan asettama nimi muistialueelle
, jossa arvo sijaitsee. Kun luodaan uusi muuttuja, varataan myös sen käyttöön uusi muistialue. Palataan tähän tarkemmin hetken päästä. Katsotaan kuitenkin lyhyt animaatio asiasta.Toinen tapa jolla C:n muuttujat selkeästi eroavat Pythonista on se, että niillä on erikseen määritelty
tyyppi
. Tyyppi määritetään muuttujan esittelyssä
, joka on siis aina muotoa muuttujan_tyyppi muuttujan_nimi
. Esittely ylipäätään on jotain, mitä Pythonissa ei tehdä lainkaan, koska muuttujat luodaan lennosta silloin kun niihin ensimmäisen kerran sijoitetaan jotain. C:ssä sen sijaan muuttujat täytyy aina esitellä, eli varata niille muistista tilaa, ennen kuin niitä voidaan käyttää. Esittelyn yhteydessä muuttujaan voidaan sijoittaa arvo, puhutaan
alustamisesta
.Nämä molemmat ovat siis päteviä esittelyjä:
int i;
int x = 5;
Mitä tapahtuu jos muuuttujaa ei alusteta? Tällöin saadaan yleensä hyvin ennalta-arvaamattomia tuloksia, jotka ovat pahimmillaan tietoturvariski!
Tyyppejä kaikkialla¶
C:ssä siis
muuttujien
tyypit
ovat jokseenkin suuremmassa roolissa kuin Pythonissa. Siinä on myös enemmän perustyyppejä. Yllä nähtiin tuttu kokonaislukutyyppi
int
. Muita C-kielen kokonaislukutyyppejä ovat short
ja long
. Näille tyypeille on lisäksi olemassa määreet signed
tai unsigned
joilla voidaan määrätä muuttujalle etumerkki, oletuksena kaikki ovat tosin positiivisia kokonaislukuja. Kirjoitusmerkkejä varten C-kielessä on muuttujatyyppi char
, joka itseasiassa on kokonaislukumuuttuja sekin, kuten myöhemmin selviää. Toisin kuin Pythonissa, C:ssä ei ole lainkaan merkkijonomuuttujatyyppiä
. Merkkijonojen tallentamiseen muistiin palataankin vasta hieman myöhemmin. C:stä löytyvät myös
liukuluvut
, jotka toimivat aika lailla samalla tavalla kuin Pythonissa. Vähemmän yllättäen nekin ovat vain bittejä muistissa, jotka sitten tulkitaan eri tavalla. C-kielessä on kaksi liukulukumuuttujatyyppiä, eli float
ja double
. Liukulukujen esityksen ymmärtäminen tietokoneen muistissa ei ole tämän kurssin kannalta oleellista, mutta muistetaan tässä vaiheessa, että ne eivät koskaan ole täysin tarkkoja vaan arvioita. Muuttujiin ja lukujärjestelmiin palaamme vielä tarkemmin. Nyt kun tiedämme muuttujatyypeistä, voimme ottaa muutaman harjoitustehtävän jotta näistä jää jotain mieleenkin.
Tyyppejä tulostimessa¶
Kaiken muun hauskan lisäksi
muuttujien
tyypit
liittyvät myös tulostukseen. C-kielessä täytyy kääntäjälle kertoa, että missä tyypissa haluamme muuttujan tulostaa. Jotta asia ei olisi liian yksinkertaista, voimme tulostaa muuttujan eri tyyppisenä kuin se on esitelty. Tässä vaiheessa tarvitsee vain tietää, että C-kielessä annetaan tulostusfunktiolle printf
parametrinä, ei pelkästään tulostettavat muuttujat, mutta myös kuvaus siitä miten ne tulostetaan. Alla esimerkki.printf("%d\n", x);
Lyhyesti (palaamme kyllä asiaan myöhemmin), printf-funktiokutsussa yllä tulostetaan muuttuja x:n arvo ruudulle kokonaislukuna ja heti perään rivinvaihtomerkki. Funktion ensimmäinen parametri on merkkijono, joka kertoo miten tulostetaan, eli tässä
"%d\n"
. Nyt %+kirjain
-merkit vastaavat Pythonin format-metodin
yhteydessä nähtyjä paikanpitimiä
ja sen perään rivinvaihtomerkki \n
. Itse asiassa myös Python tuntee vastaavan syntaksin – format on vain modernimpi ja monipuolisempi tapa tehdä sama asia. Ajatus on siis, että kunkin paikanpitimen tilalle tulee printf-funktion
muut argumentit
annetussa järjestyksessä, eli esimerkissämme %d
:n kohdalle tulostukseen tulee muuttujan x arvo. Toisin kuin Pythonissa jossa voitiin käyttää tyhjää paikanpidintä tyyliin "Annoit numeron {}".format(luku)
, C:ssä paikanpitimet on sidottu muuttujien tyyppiin. Lista tällä kurssilla yleisimmistä:paikanpidin | muuttujatyyppi |
%d | int |
%i | int |
%hi | short |
%li | long |
%u | unsigned int |
%hu | unsigned short |
%lu | unsigned long |
%f | float, double |
%c | char |
%s | char[] |
Lisäksi standardissa on muitakin, joita tullaan näkemään kurssimateriaalissa, mutta eivät suoraan liity yhteen muuttujatyyppiin. Esimerkiksi
paikanpidin | merkitys |
%x | tulostaa muuttujan arvon heksadesimaalimuodossa |
Funktioiden muodonmuutos¶
Osaamistavoitteet: Tässä osiossa tulee tutuksi miten C:ssä määritellään funktioita ja mitkä ovat pääasialliset erot Pythonin-funktioihin (tyypitetyt parametrit sekä paluuarvot). Uutena käsitteenä tulee prototyyppi.
Vaara: Funktiot syövät tyyppejä!¶
Periaatetasolla
C-funktion
määrittely ja käyttö eivät oikeastaan eroa Python-funktiosta
. Funktio määritellään omalla määrittelyrivillään, jolla esitetään funktion nimi sekä parametrit
ja määrittelyä seuraa funktion koodi. Syntaksi eroaa hieman: määrittelyrivillä ei ole erillistä avainsanaa
kuten Pythonissa on def. Sen sijaan funktion määrittely näyttää alkuun samalta kuin muuttujan – funktioksi sen paljastavat nimen perässä alkavat sulut: int main() {
return 0;
}
def-avainsanan ”tilalla” on
muuttujatyyppi
. Tämä tyyppi määrittää mitä tyyppiä funktion palauttama arvo
on. Huomattavaa on, että main-funktion kohdalla paluuarvo on tyyppiä int – funktion kuuluu antaa statuskoodi sen mukaan menikö suoritus nappiin vai ei. Funktion lopussa kuuluu siis yleensä olla return 0;
, joka kertoo että kaikki meni ok. Virhetilanteissa paluuarvo on jokin muu numero, mutta näihin ei perehdytä tarkemmin tässä – juuri nyt on vain syytä muistaa, että pääfunktion loppuun tarvitaan tuo palautusrivi. Aivan samalla tavalla funktion parametreille täytyy määrittää tyyppi: float laske_matka(float nopeus, float aika) {
return nopeus * aika;
}
Jos funktiota yritetään kutsua vääräntyyppisillä arvoilla, seurauksena on
kääntäjän
antama virheilmoitus. Funktion myös täytyy palauttaa arvo, joka edustaa funktion määrittelyssä käytettyä tyyppiä – muuten saadaan jälleen käännösvirhe. Funktiolle, jonka ei ole tarkoitus palauttaa mitään, voidaan määrittää tyypiksi void
: void tulosta_ohjeet() {
printf("Tämä ohjelma ei tee yhtään mitään järkevää");
}
Merkittävin toiminnallinen ero
C-funktioiden
ja Python-funktioiden
välillä on se, että C:ssä funktiolla voi olla ainoastaan yksi paluuarvo. Jos halutaan palauttaa useita arvoja, käytetään viittauksia tai muuttujat voidaan esimerkiksi pakata tietorakenteen
sisälle. Tähän perehdytään myöhemmin.Prototyypit¶
C-kielessä myös funktiot tulee esitellä ennen käyttöä.
Prototyyppi
on funktion esittely ja kertoo kääntäjälle
, että ohjelmasta löytyy tämänniminen funktio, joka syö näitä muuttujatyyppejä ja palautusarvo on tätä muuttujatyyppiä. Prototyypit muodostavat siis ikäänkuin sisällysluettelon funktioista, jotka ohjelmasta löytyvät. Jos C-kielinen ohjelmamme koostuu useasta eri koodimodulista, pitää modulien jaettavat funktiot esitellä niiden otsikkotiedostoissa, jotta sisällysluettelomme olisi kattava. Otsikkotiedosto tarvitaan siis mukaan esikääntäjän
#include
-lauseella, koska muuten toiset modulit ei tiedä mitään toisissa moduleissa olevista funktioista. Tästäkin opimme lisää myöhemmin. Tietenkin koko funktion voi kirjoittaa valmiiksi koodissa ennen sen käyttöä, tällöin sen esittely tulikin näppärästi hoidettua samalla, paitsi että tällöin funktio ei näy modulin ulkopuolelle jos se ei ole otsikkotiedostossa.
Prototyypin määrittelyrivi on identtinen varsinaisen funktion määrittelyn kanssa:
int laske_summa(int a, int b);
int laske_summa(int a, int b) {
return a + b;
}
Yleensä siis prototyyppien lisääminen koodiin ei vaadi muuta kuin funktiomäärittelyjen kopioinnin koodin alkuun (
esikääntäjäohjeiden
alapuolelle). Prototyypit voivat myös sijaita erillisessä otsikkotiedostossa
, joihin tutustutaan myöhemmin. Prototyyppien käyttö tällä kurssilla ei ole valinnaista – tarkistimen kääntäjää on ohjeistettu tuottamaan virheilmoitus
puuttuvista prototyypeistä. Huomatkaa, että main-funktiolle ei kuulu määritellä prototyyppiä, mutta se on aina oltava nimeltään main.
C-kääntäjän perusteet¶
Osaamistavoitteet: Tässä osiossa opit perusteet C-kääntäjän käytöstä – riittävästi ainakin tätä C-ohjelmointiosuutta varten. Pääasiassa tämä tarkoittaa, että muutamat tärkeimmät asetusliput ovat tämän jälkeen tuttuja, ja että osaat lukea kääntäjän antamia virhe- ja varoitusilmoituksia.
Kurssilla tulemme käyttämään työasemissa virtuaalikonetta, jossa kääntäjäympäristö on valmiiksi asennettu, mutta kotikoneisiin joudutte itse sen asentamaan. Kurssin etusivulta löydät linkin asennusohjeisiin.
Kääntäjätaikuuden perusteet¶
Aiemmin esitettiin seuraavanlainen perusloitsu:
C:\polku\johonkin>gcc -Wall -o munohjelma.exe munkoodi.c
Tässä komennossa on kaiken kaikkiaan 4 osaa kuten ykkösmateriaalissa lyhyesti käytiin läpi. gcc on käytettävän ohjelman – eli
kääntäjän
– nimi (GNU C Compiler). Käännettävä kooditiedosto tai -tiedosto(t) löytyvät lopusta – niiden edessä ei ole viivalla alkavaa asetuslippua
. Näistä -o
määrittää nimen tiedostolle johon käännetty koodi tallentuu – Windowsissa nimelle annetaan päätteeksi .exe, Linuxissa taas suoritettavat ohjelmat ovat päätteettömiä. Viimeisen lipun -Wall
kohdalla olemme itse asiassa vähän huijanneet: sitä ei varsinaisesti tarvita ohjelman kääntämisessä. Se lisää kääntäjän antamien varoitusten
määrää liittämällä varoitettaviin asioihin useita kyseenalaisia ja helposti korjattavia rakenteita. Olemme sisällyttäeet lipun jokaiseen perusesimerkkiin siksi, että sen käyttö on erittäin suositeltavaa – oikeastaan jopa pakollista tällä kurssilla, koska tarkistimen kääntäjä käyttää sitä ja hylkää koodit jotka antavat varoituksia.Näiden osasten järjestyksellä ei ole merkitystä, kunhan vain
-o
ja sen perään tuleva ohjelman nimi pysyvät yhdessä.Vaihtoehtojen maailma¶
GNU Compiler Collectioniin sisältyvän C-kääntäjän lisäksi kääntäjiä löytyy lisääkin. Eräs hyvin suosittu vaihtoehto gcc:lle on LLVM-projektiin kuuluva clang, joka sekin on gcc:n tapaan avointa lähdekoodia ja löytyy asennettuna harjoitustehtävien ratkomiseen tarkoitetulta virtuaalikoneelta. clang kelpuuttaa pitkälti samat kääntövivut kuin gcc, joten voit käyttää sitä suoraan vaihtamalla komennoista
gcc
:n tilalle clang
. Itse asiassa clangin kokeilu gcc:n lisäksi on jopa suositeltavaa, sillä clang saattaa joissain tilanteissa tarjota virheet ja varoitukset selkeämmässä muodossa. Useammalla kuin yhdellä kääntyjällä kääntyvät ohjelmat kielivät myös koodin laadusta: fiksusti koodattu ohjelma kääntyy millä tahansa kääntäjällä ja on taatusti standardit täyttävä!Lisäksi muun muassa Inteliltä ja Microsoftilta löytyy omat C-kääntäjät. Näistä varsinkin Intelin kääntäjä on tunnettu hyvin optimoituja binäärejä tuottavana.
Lisää lippuja¶
Kuten jo ykkösmateriaalissa todettiin,
kääntäjällä
on asetuslippuja
kuin kissoja internetissä. Tässä osiossa käymme lyhyesti läpi muutamia joita tällä kurssilla käytetään. Kurssin kannalta erittäin tärkeä on
-Werror
, jota voi käyttää sellaisenaan tai =-merkin kanssa. Sellaisenaan se muuttaa kaikki varoitukset
kääntäjävirheiksi
jolloin koodia ei käännetä lainkaan suoritettavaksi tiedostoksi ennen kuin ne on korjattu. Tämäkin on suositeltava lippu käyttää. Lipulle voi myös määrittää, että vain tietty tai tietyt varoitukset tulkitaan virheiksi. Tällä kurssilla erityisesti tärkeä käyttö on -Werror=missing-prototypes
jota käytetään tarkistimessa. Tämä lippu aiheuttaa sen, että ohjelma josta puuttuu funktioiden prototyypit
ei käänny. Sitä kannattaa siis käyttää myös omalla koneella jolloin saat nämä virheet varmuudella karsittua ennen tarkistukseen lähetystä. Varoituksiin liittyy myös
-Wextra
, joka antaa vielä enemmän varoituksia kuin -Wall
– lippujen nimeäminen on hieman hämmentävää, kun all ei tosiasiassa sisälläkään aivan kaikkea…Linuxin käyttäjien on hyvä tietää vielä yksi peruslippu tässä vaiheessa:
-lm
. Jos yrität käyttää matematiikkakirjastoa (math.h) Linuxilla ilman tätä lippua, funktioita ei löydy ja ohjelmaa ei voida kääntää. Linuxissa matikkakirjasto on irrotettu muista C:n peruskirjastoista, ja -lm
kertoo, että se tulee ottaa mukaan – tämän kohdalla on myös tärkeää, että lipun tulee olla komennossa käännettävien tiedostojen jälkeen: /polku/johonkin$ gcc -Wall -Werror=missing-prototypes -o laskuohjelma laskuohjelma.c -lm
Mystiset käännösviestit¶
Kääntäjän
viestin lukeminen on keskeistä virheellisten ohjelmien korjaamisessa. Pythonin virheviestejä tulkinneille tässä ei ole varsinaisesti mitään erityisen uutta. Pitkälti sama informaatio löytyy siis näistäkin viesteistä. Alla on esitetty koodi jossa on joitain virheitä. Jos tätä koodia käännetään yllä esitetyllä komennolla, saadaan:
rikki.c:6:7: error: no previous prototype for ‘laske_sektorin_ala’ [-Werror=missing-prototypes] float laske_sektorin_ala(float sade, float kulma) { ^ rikki.c: In function ‘main’: rikki.c:11:20: error: too few arguments to function ‘laske_sektorin_ala’ printf("%f\n", laske_sektorin_ala(5)); ^ rikki.c:6:7: note: declared here float laske_sektorin_ala(float sade, float kulma) { ^ cc1: some warnings being treated as errors
C:tä käännettäessä kääntäjä normaalisti kaivaa koodista kaikki virheet siinä missä Pythonin kohdalla ohjelman suoritus loppuu ensimmäiseen ja loput paljastuvat vasta kun se on korjattu (toiminnan voi muuttaa antamalla
-Wfatal-errors
-lipun – tästä on lähinnä hyötyä jos koodin kääntäminen on pitkä prosessi). Puretaan tämä esimerkkituloste ilmoitus kerrallaan:rikki.c:4:7: error: no previous prototype for ‘laske_liike_energia’ [-Werror=missing-prototypes] float laske_liike_energia(float nopeus, float massa) { ^
Tämä virhe johtuu em.
-Werror=missing-prototypes
-lipusta
, minkä kääntäjä
itse asiassa myös kertoo ensimmäisen rivin lopussa hakasuluissa. Ensimmäinen rivi noin muuten koostuu ilmoituksen sijainnista joka on muotoa tiedoston nimi:rivinumero:sarakenumero
(sarakenumero siis tarkoittaa sitä monennenko merkin kohdalla virhe todettiin tapahtuneeksi). Sijainnin jälkeen seuraa ensin ilmoituksen tyyppi (error) ja sen jälkeen tekstimuotoinen selitys. Toinen rivi näyttää virheellisen rivin, ja sen alapuolella vielä ystävällinen apunuoli osoittaa kohdan jossa kääntäjä totesi virheen tapahtuneen. Seuraavassa viestissä on yksi rivi enemmän:rikki.c: In function ‘main’: rikki.c:9:20: error: too few arguments to function ‘laske_liike_energia’ printf("%f\n", laske_liike_energia(5)); ^
Ensimmäinen rivi on uusi – se kertoo missä funktiossa virhe tapahtui. Aiempi ilmoitus ei nimittäin syntynyt mistään funktiosta vaan määrittelyosiosta. Muuten tämä ilmoitus on samanlainen kuin edellinen, ja virhe johtuu siis tällä kertaa siitä, että olemme epähuomiossa koittaneet antaa funktiolle vain yhden argumentin kun se vaatii kaksi. Itse asiassa seuraavat kolme riviäkin liittyvät tähän virheeseen.
rikki.c:4:7: note: declared here float laske_liike_energia(float nopeus, float massa) { ^
Tämä ilmoitus on tyypiltään note. Se kertoo missä päin koodia määritellään funktio, johon edeltävä virhe liittyi. Eli kyse on vain lisätiedosta. Myös viimeinen rivi on lisähuomio:
cc1: some warnings being treated as errors
Tämä siis kertoo muistutuksena, että ainakin joitain varoituksia kohdeltiin virheinä.
Loppuyhteenveto¶
Tässä materiaalissa olemme käsitelleet C-kielen peruskäsitteistä muuttujat ja funktiot, ja esittäneet miten ne eroavat Pythonista. C:lle ominaista on, että muuttujien tyypillä on paljon enemmän merkitystä. Yleisesti ottaen C on hieman kankeampi kieli kirjoittaa, koska huomioitavia asioita on enemmän. Mutta tällä taas päästään lähemmäksi tietokoneen laiteasoa. Ongelmanratkaisun näkökulmasta kyse on kuitenkin pitkälti samanlaisesta ohjelmoinnista kuin Pythonin kanssa – edelleen kirjoitamme funktioita ja käsittelemme muuttujia. Ainoastaan yksityiskohdat ovat muuttuneet.
Anna palautetta
Kommentteja materiaalista?