Tässä oppaassa tutustutaan C-kielen alkeisiin ja mm. seuraaviin asioihin:
C-kielisiä ohjelmia kirjoittaessa käyttöjärjestelmänä voi olla Windows, Linux tai MacOs.
Ohjelmointi tarkoittaa tietokone-ohjelman kirjoittamista. Tietokone suorittaa vain konekielisiä ohjelmia. Konekieli on ihmiselle vaikeaselkoista, joten ihmiset eivät kirjoita ohjelmia konekielellä vaan jollakin ohjelmointikielellä. Tätä ihmisen kirjoittamaa "tekstiä" kutsutaan lähdekoodiksi (tai koodiksi). Tämän vuoksi ohjelman kirjoittamista kutsutaan myös koodaamiseksi.
Kuten sanottu tietokone ei siis osaa suorittaa ihmisen kirjoittamaa lähdekoodia, vaan se on ensin muutettava konekieliseksi. Muuttaminen voi tapahtua joko kääntäjän tai tulkin avulla. Kääntäminen tarkoittaa sitä, että kääntäjäksi kutsuttu tietokone-ohjelma lukee lähdekoodin sisältävän tiedoston ja muodostaa sen perusteella uuden tiedoston, joka on konekielinen. Tämä konekielinen tiedosto voidaan nyt suorittaa tietokoneella, eikä sen suorittamiseksi tarvita enää lähdekoodia, eikä kääntäjää. Kääntäjän muodostama konekielinen tiedosto voidaan siirtää toiseen koneeseen ja suorittaa siinä vaikkei tässä koneessa ole mainittua kääntäjää eikä lähdekoodia.
Tulkitseminen tarkoittaa sitä, että uuttaa erillistä tiedostoa (konekielistä) ei muodosteta, vaan aina kun ohjelma suoritetaan tulkki lukee lähdekoodin ja antaa konekielisen koodin tietokoneen suoritettavaksi ikään kuin lennosta. Tällä tavalla luodun sovelluksen voi suorittaa vain koneessa, jossa on mainittu tulkki.
Osa ohjelmointikielistä on käännettäviä ja osa tulkittavia. Käännettäviä ohjelmointikieliä ovat mm. C, C++, C#, Java. Tulkittavia ovat mm. Python, PHP, JavaScript.
Tietokone ohjelman eli sovelluksen kannalta tietokoneen keskeisimmät osat ovat prosessori, kiintolevy ja keskusmuisti (RAM). Käyttäjän kirjoittama lähdekoodi ja kääntäjän tuottama konekielinen tiedosto tallennetaan kiintolevylle, jossa tiedostot säilyvät myös koneen sammuttamisen jälkeen. Kun ohjelma käynnistetään se (tai osa siitä) ladataan keskusmuistiin. Keskusmuisti on jaettu tavun(8 bittiä) kokoisiin lohkoihin eli muistipaikkoihin. Suoritin suorittaa ohjelmaa lukemalla peräkkäisiä muistipaikkoja alueelta johon ohjelmakoodi on tallennettu, ja tulkitsemalla lukemansa bittijonot konekielisiksi käskyiksi. Kullakin muistipaikalla on osoite, joita tarvitaan kertomaan suorittimelle mistä muistipaikasta sen tulee kulloinkin lukea tietoa.
Oheinen kuva havainnollistaa keskumuistin rakennetta ja muistipaikkojen osoitteita. Kuhunkin muistipaikkaan voi siis tallentaa yhden tavun eli 8 bittiä.
Seuraavassa esimerkissä luodaan merkki tyyppinen muuttuja nimeltään myVariable ja sen arvoksi sijoitetaan a kirjain. Sitten tulostetaan
#include <stdio.h> int main() { char myVariable='a'; printf("Muuttujan myVariable arvo = %c\n",myVariable); printf("Muuttujan myVariable osoite = %p\n",&myVariable); printf("Muuttujan myVariable arvo desimaalimuodossa = %d\n",myVariable); return 0; }Ja ohjelma tulostaa seuraavat rivit
Muuttujan myVariable arvo = a Muuttujan myVariable osoite = 00000065eb7ff92f Muuttujan myVariable arvo desimaalimuodossa = 97Nyt tietokoneen muistissa on seuraavanlainen data
Huomaa:
Pinomuisti on alue RAM-muistissa, jota käytetään funktioiden kutsumiseen ja paikallisten muuttujien säilyttämiseen. Kun funktio kutsutaan, sen tiedot "pinotaan" muistiin, ja kun funktio päättyy, nämä tiedot poistetaan automaattisesti.
Pino toimii LIFO-periaatteella (Last In, First Out), eli viimeksi lisätty tieto poistetaan ensimmäisenä.
Pinomuistin koko on rajallinen, ja liiallinen käyttö (esimerkiksi liian syvä rekursio tai suurien taulukoiden luominen pinomuistiin) voi johtaa stack overflow -virheeseen. Ohjelmoijan tulee siksi olla tietoinen pinon käytöstä ja välttää sen loppumista.
Rekursio tarkoittaa ohjelmointitekniikkaa, jossa funktio kutsuu itseään suorittaakseen tehtävän osissa. Jokainen uusi kutsu varaa tilaa pinomuistista, ja jos kutsuja kertyy liikaa (esimerkiksi jos lopetusehto puuttuu tai on virheellinen), pinomuisti voi täyttyä ja aiheuttaa stack overflow -virheen. Rekursio on hyödyllinen mm. tietorakenteiden läpikäynnissä ja matemaattisissa ongelmissa, mutta sitä tulee käyttää harkiten.
Kekomuisti on toinen RAM-muistin osa, jota käytetään dynaamiseen muistinvaraukseen ohjelman ajon aikana. Tiedot kekomuistissa säilyvät niin kauan kuin ohjelmoija itse vapauttaa ne. Tämä on hyödyllistä esimerkiksi silloin, kun ei tiedetä etukäteen kuinka paljon muistia tarvitaan.
i#include <stdlib.h>
int* dyn = (int*)malloc(100 * sizeof(int)); // varataan 100 intin taulukko
free(dyn); // vapautetaan varattu muisti
Kekomuistin käyttö ei ole rajattu samalla tavalla kuin pinomuistin, ja sitä voidaan varata tarpeen mukaan. Kuitenkin, esimerkiksi C-kielessä, ohjelmoijan vastuulla on itse vapauttaa varattu muisti. Jos tämä unohtuu, seurauksena voi olla muistivuoto (memory leak), joka heikentää suorituskykyä ja voi lopulta johtaa muistin loppumiseen. Joissakin muissa ohjelmointikielissä, kuten Java ja Python, käytössä on roskienkeruu (garbage collection), joka huolehtii dynaamisen muistin vapauttamisesta automaattisesti.
Dynaamisesta muistinvarauksesta kerrotaan lisää kohdassa Dynaaminen muistinvaraus.
Swappaus (tai "sivutus") on muistinhallinnan tekniikka, jossa osa tietokoneen RAM-muistista siirretään väliaikaisesti kiintolevylle (swap-alueelle), kun fyysinen muisti ei riitä. Tämä mahdollistaa suurempien ohjelmien ajon, mutta voi hidastaa järjestelmää, koska levy on huomattavasti hitaampi kuin RAM.
Esimerkiksi Linux-järjestelmissä swap-tila näkyy järjestelmän resurssiseurannassa ja sitä voi säätää erikseen.
C-ohjelmointikieli on kehitetty jo 1970-luvulla ja on edelleen varsin laajalti käytetty. Myöhemmin luotiin uusi kieli lisäämällä olio-ominaisuudet ja tästä uudesta kielestä käytetään nimitystä C++.
Lähdekoodi on ihmisen kirjoittamaa ohjelmakoodia, joka on kirjoitettu jollain ohjelmointikielellä, kuten C, Python tai Java. Se sisältää ohjelman ohjeet ja logiikan ja se täytyy kääntää tai tulkata, jotta tietokone voi suorittaa sen.
C-kieliselle lähdekoodi-tiedostolle käytetään yleensä tarkenninta .c (.cpp on yleensä C++ koodia sisältävä tiedosto). Header-tiedostojen tarkennin on .h ja niissä on kuvattu ohjelmassa käytettävien funktioiden rakenne. Funktioiden varsinainen koodi on kuitenkin .c-tiedostoissa.
Windowsissa suoritettavien sovellusten tarkennin on .exe. Jos siis kirjoitetaan suoritettava sovellus nimeltäänmyApp
, on C-kielisen kääntäjän tuottama suoritettava tiedosto nimeltään myApp.exe
. Linuxissa ja MACissa suoritettava tiedosto on yleensä ilman päätettä eli se olisi myApp
Yksinkertaisesti voidaan kuvata c-sovelluksen luontia näin:
Header-tiedostot (pääte .h) ovat olennainen osa C-ohjelmointia. Ne sisältävät tyypillisesti funktioiden esijulistuksia, makromäärittelyjä, tietorakenteiden määrittelyjä sekä muita vakioita, joita voidaan käyttää useassa lähdekooditiedostossa.
Header-tiedosto sisällytetään lähdekoodiin #include
-komennolla:
#include "omaheader.h"
#include <stdio.h>
Kulmasulkeita (<>) käytetään järjestelmän vakioheaderien kanssa, kun taas lainausmerkkejä ("") käytetään omien tiedostojen kohdalla.
Jotta header-tiedoston sisältö ei tulisi sisällytetyksi useita kertoja, käytetään ns. include-vahtia:
#ifndef OMAHEADER_H
#define OMAHEADER_H
// Sisältöä
#endif
Tällainen rakenne estää kaksoismäärittelyt ja mahdolliset käännösvirheet.
Header-tiedostossa ei tule toteuttaa funktioita (paitsi mahdollisesti inline-funktioita).
Funktioiden toteutukset kuuluvat .c
-tiedostoihin.
Kirjastot eli kirjastotiedostot ovat valmiiksi käännettyjä tiedostoja. Ne eivät kuitenkaan ole itsenäisesti suoritettavia kuten exe-tiedostot. Ne voivat olla ohjelmoijan itsensä kirjoittamia tai C-ohjelmointiympäristön mukana toimitettuja tiedostoja. Windowsissa näiden kirjastotiedostojen tarkennin on dll.
Tiedostopääte | Tiedostotyyppi |
---|---|
.a | static library file |
.c | C language file |
.cpp | C++ language file |
.dll | Dynamic Linked Library file (Windows) |
.h | C Header file |
.lib | In UNIX a list of objects. In Windows a collection of objects. |
.o | UNIX Object file |
.so | Dynamic Shared Library file (UNIX ) |