Helsingin yliopisto Tietojenkäsittelytieteen laitos
 

Tietojenkäsittelytieteen laitos

Tietoa laitoksesta:

 
Helsingin yliopisto / Tietojenkäsittelytieteen laitos / Copyright © 2000 Jan Lindström. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

58127-1 C-ohjelmointi : 5 Merkkiliteraalit ja merkkijonot


  • 5 Merkkiliteraalit ja merkkijonot

    5 Merkkiliteraalit ja merkkijonot

    Tyyppiä char olevan suureen mahdolliset arvot ovat yksittäisiä merkkejä. Merkki voi olla esim. kirjain, numero, erikoismerkki taikka sellainen kontrollimerkki, jolla ei ole näkyvää esitysasua, esim. rivinvaihtomerkki. Teknisesti char on kokonaislukutyyppi, joka vastaa käytettävän tietokoneen tavun (byte) käsitettä. Standaridikirjastossa ctype.h on hyödyllisiä funktioita, joiden argumentti on merkkityyppinen. Ne saadaan käyttämällä direktiiviä #include<ctype.h>. Standardin mukaan ovat char-tyypin ohella käytettävissä tyypit unsigned char ja signed char, jotka vastaavat etumerkitöntä ja etumerkillistä tulkintaa. Tyyppi char on sama kuin jompikumpi em. tyypeistä. Merkit esitetään literaaleina:

    • Kirjoittamalla merkki heittomerkkeihin, esim. '0'; tätä esitystä voidaan käyttää kaikille kirjoittuville (printable) merkeille paitsi heittomerkille itselleen ja kenoviivalle.

    • Muodossa '\n', missä n on merkin koodiarvo oktaalilukuna, esim. '\014'.

    • Muodossa '\xn', missä n on merkin koodiarvo hexsadesimaalilukuna, esim. '\x12'.

    • Eräissä tapauksissa muodossa '\x', missä x on kirjain tai erikoismerkki:

      • '\n' = rivinvaihtomerkki
      • '\t' = tabulaattori
      • '\v' = pystysuora tabulointi
      • '\b' = peruutusmerkki
      • '\r' = rivinalkuunpalautusmerkki
      • '\f' = sivunvaihtomerkki
      • '\a' = hälytysmerkki, yleensä äänimerkki
      • '\\' = kenoviiva
      • '\'' = heittomerkki
      • '\"' = lainausmerkki
    Se, mitä esimerkiksi sivunvaihtomerkin tulostaminen jollekin laitteelle saa aikaan, riippuu laitteen ominaisuuksista ja asetuksista. Merkkijonon literaaliesityksessä käytetään rajoittimina lainausmerkkejä. Esimerkiksi 'A' on merkkivakio, kun taas "A" on merkkijonovakio. Tällainen yhden merkin mittaisten merkkijonojen erottaminen merkeistä ei ole vain käsitteellistä, sillä merkkijonojen sisäinen esitys on erilainen kuin yksittäisten merkkien: merkkijono on taulukko, jonka alkiot ovat merkkejä, mutta taulukon lopussa on ylimääräinen (itse merkkijonoon kuulumaton)

    NULL-merkki eli '\0'-merkki, josta merkkijonon loppu on tunnistettavissa. Tästä seuraa, että n:n merkin mittaisen merkkijonoliteraalin sisäinen esitys vie muistissa n+1 tavua.

    Merkkijono C:ssä on taulukko jonka elementit ovat tyyppiä char. Merkkijonotyyppiä ei ole. Viimeinen merkki pitää olla NULL-merkki, joka kertoo missä merkkijono päättyy. Lähes kaikki merkkijonoja käsittelevät funktiot olettavat tämän merkin olemassaolon. Siksi taulukon on aina oltava yksi merkki isompi kuin sinne haluttu teksti. Määritellään taulukkoja:

    char Teksti[] = { 'H', 'e', 'l', 'l', 'o', '\0' }; 
    char Aine[3];
    int Numeroita[5] = {0};
    Aine[0] = 'A': Aine[1] = 'u': Aine[2] = '\0':´
    
    Taulukko voidaan myös kätevämmin alustaa seuraavilla tavoilla:

    char Teksti [] = "Hello"; 
    char Aine [3] = "Au";  
    
    Ensimmäisessä tapauksessa kääntäjä varaa tarvittavan muistin (teksti + nolla) toisessa tapauksessa meidän on itse huolehdittava että taulukko on tarpeeksi iso. Kääntäjä varoittaa tavallisesti jos taulukko on liian pieni, mutta ei jos nolla ei mahdu.


    5.1 Merkkijonojen vertaaminen ja pituus

    • Merkkijonojen pituus pitää erikseen laskea merkeittäin, esim:

      #include<stdio.h>
      
      int pituus(char *jono)
      {
          int koko = 0;
      
          /* Läpikäy merkkijonoa kunnes NULL-merkki havaitaan */    
          for(koko=0;jono[koko] != '\0'; koko++);
      
          return koko;
      }
      
      int main(void)
      {
          char MerkkiJono[] = "Paljonmerkkejäperäkkäin";
          int koko;
          
          koko = pituus(MerkkiJono); /* Laske koko */
          
          printf("Merkkijonon pituus on %d merkkiä\n",koko);
          return 0;
      }
      

    • merkkijonon pituus saadaan myös funktiolla strlen(char *Merkkijono)

      • palauttaa merkkien määrän, ei laske mukaan nollaa
      • liitä mukaan otsikkotiedosto <string.h>

    • merkkijonoja ei voi verrata normaalisti operaattorilla ==
      • vertaa osoittimien arvoa
      • vertaaminen pitää suorittaa merkeittäin, esim:

        #include<stdio.h>
        #include<string.h>
        
        int vertaa(char *jono, char *jono2)
        {
            int i;
        
            /* Jos merkkijonot erimittaisia, eivät ole samoja */
            if ( strlen(jono) != strlen(jono2))
                return 0;
        
            for(i=0; jono[i] != '\0';i++)
            {
                /* Jos merkit eroavat, eivät ole samoja */
                if ( jono[i] != jono2[i] )
                    return 0;
            }
        
            /* Jos tänne asti päästiin, niin merkit ovat samoja */
        
            return 1;
        }
        
        int main(void)
        {
            char MerkkijonoA[] = "Suomi";
            char MerkkijonoB[] = "suomi";
        
            if ( vertaa(MerkkijonoA,MerkkijonoB) )
                printf("Merkkijonot ovat samoja\n");
            else
                printf("Merkkijonot eivät ole samoja\n");
        }
        

    • funktio strcmp(char *S1, char *S2) hoitaa saman asian

      • palauttaa kolme eri arvoa
        • 0 jos merkkijonot ovat identtisiä
        • negatiivisen arvo jos S1 < S2
        • positiivisen arvo jos S1 > S2

      • löytyy myöskin otsikkotiedostosta <string.h>
    • funktio strncmp(char *S1, char *S2, size_t Koko) vertaa vain Koko merkkiä

    5.2 Merkkijonojen kopiointi

    • kopioinnin voi suorittaa merkeittäin

    • helpompaa on käyttää funktiota strcpy(char *To, char *From)
    • kopioi koko merkkijonon From merkkijonoon To
    • kopioi myös nollan
    • ei varaa muistia, joten merkkijonon To pituus on oltava vähintään strlen(From) + 1 pitkä!

    • virheitä helppo tehdä

    • funktio strncpy(char *To, char *From, size_t Koko) kopioi vain Koko merkkiä
      #include <stdio.h>
      #include <string.h>
      
      int main(void) 
      {
      
          char Alkuperainen[] = "Tekstiä";
          char Kopio[11];
          char Lyhyt[3];
      
          /* kopioi merkkijono */
          strcpy( Kopio, Alkuperainen );
          strncpy( Lyhyt, Alkuperainen, 2);
      
          printf( "Alkuperäinen: %s, kopio: %s\n", Alkuperainen, Kopio);
          printf( "Alkuperäinen: %s, lyhyt: %s\n", Alkuperainen, Lyhyt);
          return 0;
      }
      


    5.3 Merkkijonojen luonti

    C:ssä ei ole hyvää tapaa konvertoida esim. int tai float merkkijonoksi. Funktiolla sprintf() voidaan kuitenkin tulostaa merkkijonoon. Toimii aiva kuten normaali printf(), ainoa ero on että annetaan lisäparametri joka on merkkijono johon "tulostus" halutaan.

    #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
         int Vuosi = 2000;
         char Tyyppi[] = "Auto";
         char SQL[100];
         
         /* luo SQL-lause */
         sprintf ( SQL, "SELECT * FROM Tuotteet WHERE Tyyppi='%s' AND Vuosi=%d",
                  Tyyppi, Vuosi );
    
         /* tulosta syntynyt lause */
         printf ( "%s\n", SQL );
         
         return 0;
    }
    
    ohjelman ajo antaa tuloksen: SELECT * FROM Tuotteet WHERE Tyyppi='Auto' AND Vuosi=2000
    • koko tulostuksen on mahduttava tulostettavaan merkkijonoon

    • voi syntyä muistivirhe (segmentation fault)

    • voi myös korruptoida muistia

    • kääntäjä ei varoita vaikka merkkijono on liian lyhyt

    Koska yllä oleva funktio on niin herkkä virheille suosittelen käyttämään funktiota snprintf(char *Merkkijono, size_t Koko, const char *Formatointi, Argumentit ). Funktio kuuluu C99 standardin laajennuksiin, mutta on toteutettuna kaikissa hyvissä kääntäjissä (esim Linux:sta löytyy: man snprintf). Funktio toimii kuten sprintf, mutta kopioi Merkkijono muuttujaan korkeintaan Koko merkkiä. Funktio palautta kuinka monta merkkiä kirjoitettiin.

    5.4 Merkkijonojen yhdistäminen

    Merkkijonoja ei voi suoraan yhdistää esim. operaattorilla +. Funktio strcat(char *Minne, char *Mista) hoitaa tehtävän. Parametri Minne on se merkkijono johon liitetään merkkijono Mista. Funktio löytyy otsikkotiedostosta <string.h>

    • merkkijono johon "konkatenoidaan" on oltava tarpeeksi pitkä

    • funktio strncat(char *Minne, char *Mista, size_t Pituus) kopioi maksimissaan Pituus merkkiä

    #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
         char ViestiJono[80];
    
         /* kopioi ensimmäinen osa viestistä */
         strcpy ( ViestiJono, "Maapallo" );
    
         /* lisää loppu */
         strcat ( ViestiJono, " on pyöreä" );
    
         printf ( "Viesti: %s\n", ViestiJono );
    
         return 0;
    }
    

    5.5 Esimerkki: Komentoriviparametrien listaus

    
    #include<stdio.h>
    #include<string.h>
    
    int main(int argc, char **argv,char **env)
    {
        unsigned int i;
    
        printf("Ohjelma sai %d argumenttia\n",argc-1);
    
        for( i = 0; i < argc ; i++ )
            printf("Parametrin %d on %s ja sen pituus oli %d\n",
                i, argv[i], strlen(argv[i]) );
    
        for ( i = 0; env[i] != 0; i++ )
            printf("Ympäristömuuttujan %d arvo on %s\n",i,env[i]);
       
        return 0;
    }
    
    Ohjelman käynnistys komentorivillä : par eka : saa aikaan esimerkiksi seuraavanlaisen tulostuksen:
    Ohjelma sai 1 argumenttia
    Parametrin 0 on par ja sen pituus oli 3
    Parametrin 1 on eka ja sen pituus oli 3
    Ympäristömuuttujan 0 arvo on ignoreeof=10
    Ympäristömuuttujan 1 arvo on COLORTERM=rxvt
    Ympäristömuuttujan 2 arvo on LOGNAME=jplindst
    Ympäristömuuttujan 3 arvo on JIKESPATH=/usr/local/java/jre/lib/rt.jar
    Ympäristömuuttujan 4 arvo on BIBINPUTS=:/home/group/rodin/documents//
    Ympäristömuuttujan 5 arvo on VISUAL=ue
    Ympäristömuuttujan 6 arvo on MAIL=/var/spool/mail/jplindst
    Ympäristömuuttujan 7 arvo on PAGER=less
    Ympäristömuuttujan 8 arvo on WWW_HOME=http://www.cs.Helsinki.FI/
    Ympäristömuuttujan 9 arvo on CDPATH=.:..:../..
    Ympäristömuuttujan 10 arvo on CLASSPATH=.
    Ympäristömuuttujan 11 arvo on TERM=xterm
    Ympäristömuuttujan 12 arvo on HOSTTYPE=i386
    Ympäristömuuttujan 13 arvo on PATH=/usr/local/ssh-bin:/usr/bin:/bin:/usr/local/bin:/usr/bin/X11:/usr/openwin/bin:/usr/andrew/bin:/usr/games:/home/jplindst/bin:.:/opt/cvs/bin:/home/group/saha/bin/:/opt/lyx-1.1.5fix1/bin/ 
    Ympäristömuuttujan 14 arvo on HOME=/home/jplindst
    Ympäristömuuttujan 15 arvo on SHELL=/bin/bash
    Ympäristömuuttujan 16 arvo on XAUTHORITY=/home/jplindst/.Xauthority
    Ympäristömuuttujan 17 arvo on PS1=\u@\h:\w\$
    Ympäristömuuttujan 18 arvo on PS2=>
    Ympäristömuuttujan 19 arvo on TEXINPUTS=:/home/group/rodin/documents/Styles//
    Ympäristömuuttujan 20 arvo on MANPATH=/usr/local/man:/usr/man:/usr/man/preformat:/usr/X11/man:/usr/openwin/man
    Ympäristömuuttujan 21 arvo on LESS=-MM
    Ympäristömuuttujan 22 arvo on LESSCHARSET=latin1
    Ympäristömuuttujan 23 arvo on HOSTDISPLAY=vaskiluoto:0.0
    Ympäristömuuttujan 24 arvo on JAVA_HOME=/usr/local/java
    Ympäristömuuttujan 25 arvo on DISPLAY=:0.0
    Ympäristömuuttujan 26 arvo on OSTYPE=Linux
    Ympäristömuuttujan 27 arvo on WINDOWID=58720259
    Ympäristömuuttujan 28 arvo on OPENWINHOME=/usr/openwin
    Ympäristömuuttujan 29 arvo on SHLVL=4
    Ympäristömuuttujan 30 arvo on EDITOR=ue
    Ympäristömuuttujan 31 arvo on TZ=EET-2EET DST,M3.5.0/3,M10.5.0/4
    Ympäristömuuttujan 32 arvo on CVSROOT=/home/group/rodin/Version1source
    Ympäristömuuttujan 33 arvo on _=./par
    
    Ympäristömuuttujat tulisi oikeastaan hakea esille getenv() nimisellä funktiolla. Palaamme tähän asiaan myöhemmin.

    5.6 Tekstin etsiminen merkkijonoista

    Merkkejä voi etsiä merkkijonoista funktiolla char *strchr(char * Teksti, int Merkki). Funktio palauttaa osoittimen ensimmäiseen löydettyyn merkkiin tai NULL jos ei löytynyt. Esimerkiksi:

       #include <stdio.h>
       #include <string.h>
    
       int main (int argc, char **argv) 
       {
         char *Paikka;
         
         if ( argc != 3 ) 
         {
           printf ("Käyttö: %s teksti merkki\n", argv[0] );
           exit ( 1 );
         }
    
         /* löydä merkki */
         Paikka = strchr ( argv[1], argv[2][0] );
    
         /* löytyikö?    */
         if ( Paikka == NULL ) 
         {
           printf ( "Merkkiä %c ei löytynyt merkkijonosta '%s'\n",
                    argv[2][0], argv[1] );
         }
         else 
         {
           printf ( "Merkki %c löytyi merkkijonosta '%s'", 
                    argv[2][0], argv[1]);
           printf ( ", indeksi %d\n", Paikka - argv[1]);
         }
         
         return 0;
       }
    
    Funktio char *strrchr(char *Teksti, int Merkki) toimii kuten strchr(), mutta palauttaa osoittimen viimeiseen löydettyyn merkkiin. Funktio strstr(char *Teksti, char *Etsitty) palauttaa osoittimen paikkaan josta merkkijono Etsitty löytyy merkkijonosta Teksti tai palauttaa myös NULL jos ei löytynyt. Kaikki edellämainitut funktiot löytyvät otsikkotiedostosta <string.h>.


    5.7 Merkkijonojen jakaminen osiin

    Usein on tarve jakaa merkkijono osiin, esim. kun tehdään parseri. Funktiolla char *strtok(char *Teksti, char *Rajoitin) voi hoitaa asian. Parametri Teksti on teksti, jota jaetaan osiin. Parametri Rajoitin on merkkijono joka sisältää rajoitinmerkkejä, eli merkkejä jonka kohdalla merkkijono katkaistaan. Funktio palauttaa löydetyn osan, tai NULL kun ei enää löydy. Funktiota on kutsuttava monta kertaa, mutta ainoastaan ensimmäisellä kerralla annetaan Teksti, muilla kerroilla parametriksi annetaan NULL. Esimerkiksi:

     #include <stdio.h>
     #include <string.h>
    
     int main (int argc, char **argv) 
     {
       char *Osa;
       int Index = 0;
       
       if ( argc != 3 ) 
       {
         printf ("Käyttö: %s teksti rajottimia\n", argv[0] );
         exit ( 1 );
       }
    
       /* aloita jakaminen */
       Osa = strtok ( argv[1], argv[2] );
    
       /* iteroi niin kauan kun osia riittää */
       while ( Osa != NULL ) 
       {
         /* kirjoita osa */
         printf ( "%d: '%s'\n", Index++, Osa );
    
         /* hae seuraava osa */
         Osa = strtok ( NULL, argv[2] );
       }
        
       return 0;
     }
    
    Ohjelman ajo voi antaa vaikka seuraavanlaisen tulostuksen:
    
     % ./mystrtok "d,e,f," ,
     0: 'd'
     1: 'e'
     2: 'f'
     % ./mystrtok "20 * 15 + 12 - 5" "*+- "
     0: '20'
     1: '15'
     2: '12'
     3: '5'
     %
    

    5.8 Merkkijonojen konvertointi

    Otsikkotiedostossa <stdlib.h> on määritelty muutama funktio jolla voi konvertoida merkkijonoja luvuiksi. Funktiot ovat:
    • strtod(char *Teksti, char **Lippu)

    • strtol(char *Teksti, char *Lippu, int Kanta)

    • strtoul(char *Teksti, char *Lippu, int Kanta)

    Ensimmäinen konvertoi tekstin tyypiksi double, toinen tyypiksi long ja kolmas tyypiksi unsigned long. Parametrit ovat jokaiselle funktiolle samat. Teksti on konvertoitava teksti. Lippu on osoitin osoittimeen johon tallennetaan osoitin ensimmäiseen virheelliseen merkkiin, tai NULL jos kaikki ok. Kanta on käytetty lukukanta, esim. 10. Funktiot palauttavat konvertoidun arvon. Esimerkiksi:

       #include <stdio.h>
       #include <stdlib.h>
    
       int main (int argc, char * argv[]) 
       {
         char *Lippu;
         long Arvo;
         
         if ( argc != 2 ) 
         {
           printf ("Käyttö: %s luku\n", argv[0] );
           exit ( 1 );
         }
    
         /* tee konversio. Lähetä lipun osoite */
         Arvo = strtol ( argv[1], &Lippu, 10 );
    
         /* menikö oikein? */
         if ( *Lippu == 0 ) 
         {
           printf ( "Ok %ld\n", Arvo );
         }
         else 
         {
           printf ("Not ok %c\n", *Lippu );
         }
        
         return 0;
       }
    
    Voi myös käyttää funktioita:
    • double atof(char *Teksti)

    • int atoi(char *Teksti)

    • long atol(char *Teksti)

    Konvertoivat merkkijonon luvuksi ja palauttavat sen. Eivät pysty raportoimaan virheitä mitenkään. Voi käyttää jos on varma että konvertoitava Teksti on luku. Löytyvät otsikkotiedostosta <stdlib.h>

    .


    5.9 Merkkien luokittelu

    Merkkejä voi luokitella tyyppinsä mukaan funktioilla:

    • int isalnum (int c) : tarkistaa jos merkki on kirjain tai numero

    • int isalpha (int c) : tarkistaa jos merkki on kirjain

    • int iscntrl (int c) : tarkistaa jos merkki on kontrollimerkki

    • int isdigit (int c) : tarkistaa jos merkki on numero

    • int isgraph (int c) : tarkistaa jos merkki on tulostettava (ei välilyönti)

    • int islower (int c) : tarkistaa jos merkki on pieni

    • int isupper (int c) : tarkistaa jos merkki on iso

    • int isprint (int c) : tarkistaa jos merkki on tulostettava (myös välilyönti)

    • int ispunct (int c) : tarkistaa jos merkki on tulostettava mutta ei kirjain, numero tai välilyönti

    • int isspace (int c) : tarkistaa jos merkki on "tyhjä", eli esim. välilyönti,'\t', '\n' jne.

    • int isxdigit (int c) : tarkistaa jos merkki on heksadesimaali numero

    Funktiot palauttavat 0 jos merkki ei kuulu luokkaan, muuten jonkun muun arvon. Funktioiden pitäisi myös käsitellä skandinaavisia merkkejä oikein. Funktioilla int tolower(int c) ja int toupper(int c) voidaan konvertoida merkkejä pieniksi tai isoiksi. Funktiot palauttavat konvertoidun merkin tai saman merkin jos konversiota ei voitu tehdä. Löytyvät otsikkotiedostosta <ctype.h>. Esimerkkejä:

     #include <stdio.h>
     #include <stdlib.h>
     #include <ctype.h>
    
     /**
      * Tarkistaa jos parametrina annettu merkkijono on kokonaisluku.
      * Palauttaa 1 jos on ja 0 jos ei. Numero saa olla positiivinen tai
      * negatiivinen. Merkkijonon alussa ja lopussa saa olla tyhjää.
      *
      * Parametri: char *Teksti - tarkistettava merkkijono
      */
     int isNumber (char *Teksti) 
     {
       int Index = 0;
       int Pituus = strlen ( Teksti );
       
       /* onko annettu osoitin edes kunnollinen? */
       if ( Teksti == NULL )     /* ei, joten se ei ole numerokaan */
         return 0;
    
       /* lue tyhjää merkkijono alusta */
       while ( Index < Pituus && isspace (Teksti[Index]) )
         Index++;
    
       /* onko seuraava merkki '-'? */
       if ( Teksti[Index] == '-' )
         Index++;
    
       /* lue numeroita */
       while ( Index < Pituus && isdigit (Teksti[Index]) )
         Index++;
    
       /* lue tyhjää merkkijono alusta */
       while ( Index < Pituus && isspace (Teksti[Index]) )
         Index++;
    
       /* nyt meidän olisi pitänyt saavuttaa merkkijonon viimeinen merkki */
       if ( Index == Pituus ) 
       {     /* koko merkkijono on numero */
         return 1;
       }
       else 
       {
         /* ei ole numero */
         return 0;
       }
     }
    
     int main (int argc, char * argv[]) 
     {
    
       /* tarkista parametrien määrä */
       if ( argc != 2 ) {
         printf ("Käyttö: %s merkkijono\n", argv[0] );
         exit ( 1 );
       }
       
       /* testaa merkkijonoa */
       if ( isNumber ( argv[1] ) == 1 ) 
       {
         printf ( "'%s' on numero\n", argv[1] );
       }
       else 
       {
         printf ( "'%s' ei ole numero\n", argv[1] );
       }
    
       return 0;
     }
    
    Ohjelman ajo voi antaa seuraavanlaisen tulostuksen:
     % ./IsNumber foo
     'foo' ei ole numero
     % ./IsNumber 10
     '10' on numero
     % ./IsNumber "   10"
     '   10' on numero
     % ./IsNumber "-10  "
     '-10  ' on numero
     % ./IsNumber "  - 10 "
     '  - 10 ' ei ole numero
     %
    

    Esimerkki 2:

     #include <stdio.h>
     #include <stdlib.h>
     #include <ctype.h>
    
     /**
      * Muuttaa parametrina lähetetyn merkkijonon kirjaimet pieniksi.
      * Palauttaa myös muutetun merkkijonon.
      *
      * Parametri: char* Teksti - muutettava merkkijono
      */
     char *stringToLower (char *Teksti)
     {
       int Index = 0;
       int Pituus = strlen ( Teksti );
       
       /* onko annettu osoitin edes kunnollinen? */
       if ( Teksti == NULL ) /* ei, joten palauta se suoraan */
         return 0;
    
       /* iteroi kaikkien merkkien yli */
       for ( Index = 0; Index < Pituus; Index++ ) 
       {
         Teksti [Index] = tolower ( Teksti [Index] );
       }
    
       /* palauta konvertoitu merkkijono */
       return Teksti;
     }
    
     /**
      * Muuttaa parametrina lähetetyn merkkijonon kirjaimet isoiksi.
      * Palauttaa myös muutetun merkkijonon.
      *
      * Parametri: char *Teksti - muutettava merkkijono
      */
     char *stringToUpper (char *Teksti)
     {
       int Index = 0;
       int Pituus = strlen ( Teksti );
       
       /* onko annettu osoitin edes kunnollinen? */
       if ( Teksti == NULL ) /* ei, joten palauta se suoraan */
         return 0;
    
       /* iteroi kaikkien merkkien yli */
       for ( Index = 0; Index < Pituus; Index++ ) 
       {
         Teksti [Index] = toupper ( Teksti [Index] );
       }
    
       /* palauta konvertoitu merkkijono */
       return Teksti;
     }
    
     int main (int argc, char * argv[]) 
     {
       /* tarkista parametrien määrä */
       if ( argc != 2 ) 
       {
         printf ("Käyttö: %s merkkijono\n", argv[0] );
         exit ( 1 );
       }
    
       /* kirjoita tulos */
       printf ( "Alkuperäinen: %s\n", argv[1] );
       printf ( "Pienillä:     %s\n", stringToLower ( argv[1] ) );
       printf ( "Isoilla:      %s\n", stringToUpper (argv[1] ) );
      
       return 0;
     }
    


    Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)