Kirjautuminen

Haku

Tehtävät

Oppaat: C-ohjelmointi: Osa 2 - Muuttujat, taulukot ja tietueet

  1. Osa 1 - Ensimmäinen ohjelma
  2. Osa 2 - Muuttujat, taulukot ja tietueet
  3. Osa 3 - Ehdot, silmukat ja funktiot
  4. Osa 4 - Standardikirjasto

Kirjoittaja: Antti Laaksonen. Vuosi: 2004.

Opassarjan toisessa osassa puhutaan muuttujista. Muuttuja on tietokoneen muistiin tallennettu arvo, joka saattaa muuttua ohjelman suorituksen aikana. Esimerkiksi liikkuvan auton nopeus voisi olla muuttuja. Jos muuttujia on useampia yhdessä, niin ne muodostavat taulukoita ja tietueita. Taulukkoon voitaisiin esimerkiksi tallentaa kaikkien tiellä ajavien autojen nopeudet. Tietueessa on tarkempia tietoja yhdestä tietystä asiasta, esimerkiksi auton nopeuden lisäksi sen ostovuosi, malli ja rekisterinumero.

Tästä lähtien esimerkit eivät yleensä ole tilan säästämiseksi kokonaisia ohjelmia, vaan ne täytyy liittää opassarjan ensimmäisessä osassa olevaan runkoon. Esimerkeissä olevat muuttujat täytyy myös monesti määritellä itse.

Muuttujan määrittely

C-kielessä jokainen muuttuja täytyy määritellä ennen käyttöä. Muuttujan määrittely tehdään heti koodin tai funktion alussa. Koodin alussa määritelty muuttuja eli yleinen muuttuja on käytössä ohjelman joka osassa. Funktion alussa määritelty muuttuja taas on käytössä pelkästään samassa funktiossa. Yleisiä muuttujia kannattaa käyttää säästeliäästi.

Muuttujan määrittely alkaa muuttujan tyypillä, jonka jälkeen tulee yhden tai useamman muuttujan nimi pilkuilla erotettuina. Muuttujan nimessä voi olla kirjaimia a–z ja A–Z, numeroita ja alaviivoja. Nimi ei saa kuitenkaan alkaa numerolla eikä myöskään olla C-kielen varattu sana. Isot ja pienet kirjaimet eivät merkitse samaa muuttujan nimessä: testi ja Testi viittaavat eri muuttujaan.

Seuraavassa määritellään kaksi kokonaislukumuuttujaa, joiden nimet ovat nopeus ja matka:

int nopeus, matka;

Muuttujan arvo ei ole välttämättä 0 määrittelyn jälkeen. Itse asiassa näin käy ani harvoin, sillä muuttujan arvo on aluksi satunnaisen muistipaikan sisältö. Tämän vuoksi muuttuja pitää aina muistaa alustaa erikseen. Muuttujalle voikin antaa heti määrittelyn yhteydessä aloitusarvon. Seuraavassa nopeus-muuttujan aloitusarvoksi tulee 0 ja matka-muuttujan aloitusarvoksi tulee 150.

int nopeus = 0, matka = 150;

Muuttujatyypit

Kokonaislukutyypit ovat pienimmästä suurimpaan char, short, int ja long. Merkkitiedon tallentamiseen käytetään usein char-tyyppistä muuttujaa. Tavallisesti short-tyypin arvoalue on -32768 – 32767 ja long-tyypin arvoalue on -2147483648 – 2147483647. Kääntäjästä riippuen int-tyypin arvoalue on jompikumpi näistä.

Liukulukutyypit ovat pienimmästä suurimpaan float, double ja long double. Liukuluvut eroavat kokonaisluvuista siten, että niihin voidaan tallentaa myös luvun desimaaliosa. Myös näiden tyyppien arvoalue vaihtelee kääntäjän mukaan.

Jos muuttujan tyypin edessä on merkintä unsigned, muuttuja voi olla vain positiivinen, mutta lukujen arvoalue on kaksi kertaa suurempi. Esimerkiksi unsigned short -tyyppisen muuttujan arvoalue on 0 – 65535.

Merkkijonotyyppiä ei C-kielessä ole. Merkkijonojen tallentamiseen käytetään char-tyyppistä taulukkoa, josta lisää myöhemmin.

Muuttujan arvo

Muuttujan arvo asetetaan yhtäsuuruusmerkin avulla:

nopeus = 70;
matka = 140;

Asetettava arvo voi olla myös laskulauseke. Käytössä on tavalliset +, -, * ja /, sekä lisäksi %, joka tarkoittaa jakojäännöstä. Laskujärjestyksen muuttaminen suluilla on mahdollista.

nopeus = 5 * 15;         /* 75 */
nopeus = 7 * (20 - 3);   /* 119 */
matka = 19 % 5;          /* 4 */

Laskulausekkeessa voi olla mukana toisen muuttujan arvo.

nopeus = 10;
matka = 3 * nopeus;      /* 30 */

Seuraavat lyhennykset ovat mahdollisia, jos muuttujan uusi arvo lasketaan sen oman vanhan arvon perusteella.

pitkä muotolyhyt muoto
a = a + 1a++
a = a - 1a--
a = a + 5a += 5
a = a - 5a -= 5
a = a * 5a *= 5
a = a / 5a /= 5

Muuttujan tulostaminen

Muuttujan tulostaminen näytölle tapahtuu printf-funktiolla. Tulostettavan merkkijonon sisällä voi olla erikoismerkintöjä, joiden paikalle laitetaan järjestyksessä funktion muiden parametrien arvot. Nämä merkinnät alkavat prosenttimerkillä. Parametrien määrä printf-funktiossa voi siis vaihdella.

printf("Nopeus on %i ja matka on %i.", 130, 50);

/* tulostus: "Nopeus on 130 ja matka on 50." */

Yhtä hyvin lukujen tilalla voisi olla muuttujien nimet.

printf("Nopeus on %i ja matka on %i.", nopeus, matka);

Muuttujien merkintätapa merkkijonon sisällä riippuu ensinnäkin muuttujan tyypistä: %c tarkoittaa char-muuttujaa, %i, %hi ja %li ovat int, short ja long sekä %f, %lf ja %Lf ovat float, double ja long double. %s tarkoittaa merkkijonoa. Lisäksi on mahdollista määrittää tulostettavan kentän leveys sekä desimaalien määrä. Jos luvun pituus on pienempi kuin kentän leveys, niin luvun eteen tulee tarvittava määrä välilyöntejä. Puuttuvat desimaalit täydennetään nollilla.

Seuraavassa esimerkissä ensimmäinen luku tulostetaan niin, että kentän leveys on viisi merkkiä. Toinen luku tulostetaan kahden desimaalin tarkkuudella. Kolmannen luvun tulostuksessa on määritetty sekä kentän leveys että desimaalien määrä.

printf("%5i %.2f %5.3f", 12, 1.2345, 2.35);

/* tulostus: "   12 1.23 2.350" */

Prosenttimerkit, jotka eivät tarkoita muuttujan paikkaa, merkitään merkkijonoon %%.

Muuttujan lukeminen

Muuttujan lukeminen tapahtuu scanf-funktiolla, joka on tavallaan printf-funktion käänteisfunktio. Parametreina oleviin muuttujiin tallennetaan käyttäjän kirjoittamat tiedot. Seuraava esimerkki lukee kaksi int-tyyppistä lukua ja yhden float-tyyppisen luvun.

int eka, toka;
float kolmas;
scanf("%i %i %f", &eka, &toka, &kolmas);

Kun käyttäjä kirjoittaa "12 34 56.78", eka-muuttujan arvoksi tulee 12, toka-muuttujan arvoksi tulee 34 ja kolmas-muuttujan arvoksi tulee 56,78. Parametreina olevien muuttujien edessä täytyy olla &-merkki, joka tarkoittaa muuttujan osoitetta muistissa. Muuten scanf-funktion palauttamat arvot menevät ihan minne sattuu.

Taulukko

Taulukko määritellään muuten samalla tavalla kuin muuttuja, paitsi että nimen perässä on hakasulkujen sisällä taulukon alkioiden lukumäärä. Seuraavassa määritellään taulukko, johon mahtuu kymmenen int-tyyppistä muuttujaa.

int tiedot[10];

Taulukossa voi olla useampia ulottuvuuksia. Esimerkiksi seuraavaan kaksiulotteiseen taulukkoon on mahdollista tallentaa 10 * 10 eli 100 int-muuttujaa.

int ruudukko[10][10];

Taulukon arvoja käsitellään ohjelmassa muuttujien tapaan. Hakasuluissa on aina ilmoitettuna taulukon alkion indeksi eli järjestysnumero. Esimerkissä tallennetaan taulukkoon kolme lukua: ensimmäiseen alkioon luku 12, toiseen 34 ja kolmanteen 56. Sitten luvut tulostetaan näytölle.

int tiedot[3];
tiedot[0] = 12;
tiedot[1] = 34;
tiedot[2] = 56;
printf("%i %i %i", tiedot[0], tiedot[1], tiedot[2]);

Taulukon alkioiden numerointi alkaa siis nollasta. Jos taulukossa on 10 alkiota, ensimmäisen indeksi on 0 ja viimeisen indeksi on 9. Indeksin 10 kohdalle ei saa missään tapauksessa tallentaa, sillä sen osoittama muistipaikka ei kuulu taulukkoon, vaan siihen kohtaan saattaa esimerkiksi olla tallennettuna jokin toinen ohjelmassa käytettävä muuttuja. Kääntäjä ei valitettavasti huomaa tällaista virhettä.

Taulukon voi alustaa muuttujan tavoin jo määrittelyvaiheessa. Taulukon alkioiden arvot laitetaan aaltosulkujen sisään pilkuilla erotettuina. Moniulotteisissa taulukoissa aaltosulut ovat sisäkkäin. Seuraavassa esimerkissä taulukot on alustettu määrittelyvaiheessa; kommenteista selviää kunkin alkion tuleva sisältö.

int tiedot[3] = {4, 7, 2};
/* tiedot[0] = 4, tiedot[1] = 7, tiedot[2] = 2 */
int ruudukko[2][3] = {{3, 8, 2}, {1, 7, 7}};
/* ruudukko[0][0] = 3, ruudukko[0][1] = 8, ruudukko[0][2] = 2
   ruudukko[1][0] = 1, ruudukko[1][1] = 7, ruudukko[1][2] = 7 */

Kaikkien arvojen alustaminen nollaksi tapahtuu lyhyesti näin:

int tiedot[10] = {0};

Merkkijonot

Merkkijonojen tallentamiseen ei C:ssä ole omaa muuttujatyyppiä, vaan merkkijonot tallennetaan char-tyyppiseen taulukkoon. Yhteen char-muuttujaan mahtuu yksi merkki, joka kirjoitetaan koodissa heittomerkkien väliin.

char merkki = 'A';

/* vastaa: char merkki = 65; */

Merkkijonotaulukko päättyy nollatavuun, minkä vuoksi taulukon vaatima tila muistissa on yhtä suurempi kuin siihen mahtuvan merkkijonon pituus. Lainausmerkkien sisään kirjoitetun merkkijonon loppuun tulee automaattisesti nollatavu. "esimerkki"-merkkijonon pituus on 9 merkkiä, ja sen tallentamiseen tarvitaan siis 10-alkioinen taulukko.

char mjono[10] = "esimerkki";

Merkkijonon yksittäisiä merkkejä voi käsitellä aivan samalla tavalla kuin taulukon alkioita. Tämä koodi näyttää edellisen merkkijonon kolme ensimmäistä merkkiä, jotka ovat 'e', 's' ja 'i'.

printf("Merkit ovat %c, %c ja %c.", mjono[0], mjono[1], mjono[2]);

Muun muassa seuraavilla merkinnöillä on erikoismerkitys merkkijonon sisällä: \n on rivinvaihto, \r on rivinpalautus, \t on tabulaattori, \0 on nollatavu ja \", \' sekä \\ vastaavat merkkejä ", ' ja \.

Koska merkkijonot ovat taulukoita, niitä ei voi käsitellä samalla tavalla kuin lukumuuttujia. Standardikirjastossa on kuitenkin joukko tavallisimpia merkkijonoihin liittyviä funktioita, joiden käyttöön tutustutaan opassarjan seuraavissa osissa.

Tietueet

Tietuetyypin määrittely kannattaa kirjoittaa koodin alkuun ennen main-funktiota, jolloin tyyppi on käytettävissä koodin joka osassa. Seuraavassa on VALTIO-tyypin määrittely, johon kuuluu kaksi muuttujaa.

struct VALTIO {
    float asukasluku;
    long pinta_ala;
};

Tämän jälkeen ohjelmassa voi määritellä tietuemuuttujia näin:

struct VALTIO suomi = {5.16, 338127}, ruotsi;

Tietueen nimen ja muuttujan väliin laitetaan piste. Seuraavassa ohjelmassa asetetaan ruotsi-tietueen muuttujat asukasluku ja pinta_ala sekä tulostetaan suomi-tietueen muuttujat näytölle.

ruotsi.asukasluku = 8.9;
ruotsi.pinta_ala = 449964;

printf("Suomen pinta-ala on %li km² ja asukasluku on %.2f miljoonaa.",
        suomi.pinta_ala, suomi.asukasluku);

/* tulostus: "Suomen pinta-ala on 338127 km² ja asukasluku on 5.16 miljoonaa." */

Uuden tietotyypin, jonka eteen ei määrittelyssä tarvitse kirjoittaa struct-sanaa tai mitään muutakaan, voi määritellä koodin alussa typedef-komennolla. Seuraavassa esimerkissä VUOSI-tietotyyppi on sama kuin int-tyyppi ja KOHTA-tietotyyppi vastaa tietuetta, jossa on kaksi int-muuttujaa nimeltä x ja y.

typedef int VUOSI;
typedef struct {int x, y;} KOHTA;

Tällaisia tietotyyppejä voi käyttää ohjelmassa näin:

VUOSI nykyhetki;
KOHTA pallo;

nykyhetki = 2004;
pallo.x = 10;
pallo.y = 8;

Tehtäviä

  1. Mitkä näistä ovat mahdollisia muuttujanimiä?

    hinta, huone15, 3matka, oUTo, nimi, ylä_rivi, Alku, struct, vara_osoite

  2. Miten koodissa osoitetaan taulukon ensimmäiseen ja viimeiseen alkioon?

    1. int tiedot[5];
    2. int alue[10][2];
    3. int suuri[120][9][4][6];
  3. Mikä on tuloksena, jos printf-funktion muotoilumerkkijono on %7.3f ja tulostettava luku on

    1. 7,5
    2. 1670
    3. 3,14159
  4. Minkätyyppinen ja minkäkokoinen taulukko tarvitaan merkkijonon "puhelin" tallentamiseen

  5. Suunnittele tietotyyppi henkilön tietojen tallentamiseen.

  6. Kirjoita ohjelma, joka kysyy käyttäjältä kolme lukua, laskee lukujen keskiarvon ja tulostaa sen näytölle.

  7. Muuttujan viemä tila muistissa selviää sizeof-operaattorilla. Esimerkiksi sizeof(testi) palauttaa testi-muuttujan koon tavuina. Tutki erityyppisten ja -kokoisten muuttujien, taulukoiden ja tietueitten tilankulutusta.

Antti Laaksonen, 18.2.2004


Kommentit

rndprogy [21.02.2004 09:38:56]

Lainaa #

Jotain C opasta olen tässä jo odottanutkin.

KimmoKM [21.02.2004 10:03:18]

Lainaa #

Hyvä opas.

mamaze [23.02.2004 10:53:54]

Lainaa #

juu-u hyötyähän tuosta on.

olen tässä opettelemassa C++ ja C kieliä, koska ne ovat ainoita joilla voi tehdä kunnollisia pelejä.

progo [25.03.2004 16:42:32]

Lainaa #

Hmm.. tossa on puhetta, että c varaa char-taulukolle valmiiksi nollatavun ja "esimerkki" on 9 merkkiä pitkä. Kaikki täsmää niin kuin pitääkin, paitsi että C aloittaa laskemaan siitä nollasta, toisin sanoen teksti[9] olisi kymmenen merkin taulukko, joka sisältää sen nollatavunkin.

Antti Laaksonen [25.03.2004 16:55:59]

Lainaa #

Taulukossa on niin monta paikkaa kuin määrittelyssäkin on. Jos taulukko on määritelty teksti[9], siihen mahtuu yhdeksän merkkiä indekseillä 0 - 8. Samaiseen taulukkoon mahtuu siis kahdeksan merkin pituinen merkkijono, koska yhden merkin vie lopussa oleva nollatavu.

progo [25.03.2004 17:03:16]

Lainaa #

Öhm.. joo sori mun moka. Tässä on saanut väitellä kavereille miten kaikki/kaiken pitäisi alkaa oikeesti nollasta jne..

Walalauw [06.06.2004 16:58:43]

Lainaa #

Eihän näitä asioita erkkikään muista ulkoa :(
En ole oikein sitä sorttia joka muistaa mitä tahansa ulkoa, noi muuttujat on niin monimutkaisia että en opi varmasti niitä ulkoa

Matti Kaijomaa [31.07.2004 23:50:58]

Lainaa #

Ohjelmoi käyttäen noita hyödyksi niin muistat.

Cooper [10.11.2004 22:20:20]

Lainaa #

Kas kas. Tässä näkyy olevan aivan kunnollinen c-opas tälläiselle taaperolle kuin minä. 10+

malkkis [07.02.2005 19:22:01]

Lainaa #

totta turiset

Geneerinen [11.06.2005 02:52:15]

Lainaa #

eikö olisi loogisempaa jos VALTIO:n asukasluku olisi long-tyyppiä ja pinta-ala float?

aloitteleva [22.06.2005 21:34:05]

Lainaa #

Näyttää aika sekavalta en tajua paljoa tuosta ihan erillainen kun ensinmäinen.

arnold super [28.08.2005 17:25:02]

Lainaa #

ihan selkee opas :) vaikka 2 kertaa se pitiki lukea että kaiken tajus. ku en oo ennen koodannu mitään elämässäni :P

Webu [03.09.2005 12:44:33]

Lainaa #

Onhan tämä C paljon monimutkaisempaa kuin esim. PHP ja vastaavat. Mutta hyvä opas kun on, niin eiköhän tämä tästä selkeä.

ApTyp [05.09.2005 11:32:38]

Lainaa #

autakkaa tehä ohjelma joka kysyy käyttäjältä kolme lukua ja sen jälkeen ohjelm atulostaa merkit suuruusjärjestyksessä (pienin ensin)
tässä niin kun alkuosa

#include <iostream>
using namespace std;

int main()
{
	char a, b, c, apu;
	char merkki
		do
		{

TeeVee [05.11.2005 17:47:05]

Lainaa #

Aika helvetin selkeästi pyydät tekemään kotiläksyä, ilmeisesti ei läksy tullut tehtyä, kun kukaan ei vastannut ja olet lähettänyt viestin jo 5.9.

hrnt [30.12.2005 17:37:33]

Lainaa #

lainaus:

%c tarkoittaa char-muuttujaa, %i, %hi ja %li ovat int, short ja long sekä %f, %lf ja %Lf ovat float, double ja long double. %s tarkoittaa merkkijonoa.

Tämä ei pidä paikkaansa. %i on kyllä int (kuten myös %d),
mutta %f on double eikä float. %lf ei tarkoita mitään erikoista
(%lf on siis sama kuin %f, l length modifierilla ei ole vaikutusta
f conversioniin. [1])

Tämä siis printf():n yhteydessä. scanfissa kyllä %f = float, %lf double.

%i:n kanssa kannattaa olla varonainen, koska sen toiminta scanfissa voi yllättää. (010 = 8, koska %i skannaa tämän oktaalina).

%Lf on kyllä long double.

[1] ISO/IEC 9899:1999 7.19.6.1

Juhko [17.09.2006 11:01:02]

Lainaa #

Kiva mutta aika vaikea. Täytyykin käyttää tuohon aikaa.

Juhko [08.10.2006 18:10:59]

Lainaa #

Mutta alkaahan tässä oppia!

mOrienttes [13.03.2007 19:26:21]

Lainaa #

:o Yritin vääntää tätä joku 1,5 v sitten, kun totesin, kun jotkut suosittelevat C(++) ekaksi ohjelmointikieleksi, jotkut pythonia/PERLiä, niin totesin, että jos C ei ala luonnistumaan, kokeilen pythonia. Tää tuntu ihan utopialta, python alko sujumaan, mutta sitten tuli kiireitä, eikä siihen paneutumiseen riittäny aikaa eikä intoo. Ny ku aloin uusiks kattelee C ku pythonista en muista melkein mitään, ehkä jopa miten tulostetaan tekstiä ( :-D ), ja täähän ei tunnu yhtään utopialta. :o No, ny on ikää puoltoista vuotta enemmän, ja enemmän löytyy kiinnostustakin näitä asioita kohtaan, niin ei mikään ihme kun tuntuu helpommalta. ;)

Garrett79 [09.10.2007 11:29:58]

Lainaa #

Voisiko noihin tehtäviin saada myös oikeat vastaukset helpottamaan ymmärrystä. Kiitos.

Snowblind [18.11.2007 13:32:41]

Lainaa #

Koitin tehä tuon 6. kohdan:

// Konsoli.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int main(int argc, char* argv[])
{
	int eka = 0, toka = 0, kolmas = 0, tulos = (eka + toka + kolmas) / 3;
    printf ("Laita 3 lukua. Ohjelma laskee niiden keskiarvon\n");
	scanf("%i %i %i", &eka, &toka, &kolmas);
    printf("Tulos: %i\n", &tulos);
	return 0;
}

Ja kun laitan esim: 1, 1, 1 niin antaa vastauksesksi 12808 jtn. mitä tein väärin? (Microsoft Visaul Studio C++)

Metabolix [23.12.2007 00:45:16]

Lainaa #

Snowblind:
Teit väärin kaksi asiaa. Ensinnäkin printf:n kanssa ei käytetä &-merkkiä muuttujan edessä ja toiseksikin tulos pitää laskea vasta lukujen lukemisen jälkeen. Muuttuja ei millään tapaa "muista" laskulauseketta, vaan lauseke lasketaan tuolla rivillä ja silloisten arvojen mukainen tulos tallennetaan muuttujaan.

TsaTsaTsaa [29.02.2008 18:24:02]

Lainaa #

Onko C:ssä hyvää tyyliä kirjoittaa omat tietotyypit kokonaan isolla vai onko opas siinä suhteessa susi?

Pollapoju [10.04.2008 11:25:45]

Lainaa #

#include <stdio.h>
 int main ( void )
 {
   int a, b, c;
   printf ( "Anna luku 1: " );
   scanf ( "%i", &a );
   printf ( "Anna luku 2: " );
   scanf ( "%i", &b );
   c = a + b;
   printf ( "Antamiesi lukujen yhteenlaskettu tulos on %i\n", c );
   return 0;
 }

tää olis hyvä esimerkki

Pollapoju [11.04.2008 08:32:38]

Lainaa #

ApTyp kirjoitti:

#include <iostream>

Eikös iostreamiä käytetä vaan C++:ssa.

vehkis91 [16.06.2008 00:05:56]

Lainaa #

Käytetään koska se on c++ standardi kirjasto...
Muutenkin tossa pitäis olla int eikä char, noiden tyyppi.

Oho oliki vanha toi juttu. :S

JAMSUO93 [25.01.2009 05:45:12]

Lainaa #

ois ihan jees jos näissä oppaissa ois enemmän esimerkkejä käytännön sovelluksista. Voisi olla esimerkkiohjelma ja sen koodi, vaikka joku pieni windows härpäke

Kirjoita kommentti

Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.

Muista lukea keskustelun ohjeet.
Tietoa sivustosta