Hyödyllisiä funktioita

Seuraavassa muutamia sekalaisia c-kirjastosta löytyviä funktioita.

Aika

Kirjastossa time.h löytyy useita funktioita ajan käsittelyyn. Seuraavassa ote ohjelmasta 4-1.c:
int main(int argc, char *argv[]){
  time_t aika;
  char *as;

  aika = time( NULL );          // pyydetään time-funktiolla aika
  printf("aika sekunteina: %d\n", aika);

  // ...

  // muutataan aika valmiiksi muotoilluksi merkkijonoksi
  as = ctime(&aika);
  printf("esimuotoiltu aikamerkkijono: %s", as);
Ensin ohjelma pyytää ajan funktiolta time, joka paluuarvo on tyyppiä time_t, man-sivun mukaan:
       time  returns the time since the Epoch (00:00:00 UTC, January 1, 1970),
       measured in seconds.
Ajan voi muuttaa lukijaystävälliseen muotoon monella tavalla. Helpoin on käyttää funktiota ctime, joka palauttaa osoittimen merkkijonoon joka sisältää ajan oletusarvoisesti muotoiltuna, seuraavaan tyyliin: Sat Sep 22 13:12:52 2007

Toinen tapa saada aika lukijaystävälliseen muotoon, on muuttaa sekunneissa oleva aikalukema struct tm -muotoiseksi tietueeksi, joka sisältää ajan pilkottuna eri kenttiin, joista on luettavissa tunnit, minuutit, sekunnit jne. Muutos tapahtuu localtime-funktiolla, joka saa parametrina sekunttimuotoisen ajan ja palauttaa osoittimen struct tm -tietueeseen.

  time_t aika;
  struct tm *at;

  aika = time( NULL );          // pyydetään time-funktiolla aika
  at = localtime( &aika );      // muutetaan aika tietuemuotoon

  // tulostetaan aika lukijaystävällisesti
  printf("kello on %02d:%02d:%02d\n", at->tm_hour, at->tm_min, at->tm_sec);
struct tm:stä löytyvät seuraavat kentät:
struct tm {
      int     tm_sec;         /* seconds */
      int     tm_min;         /* minutes */
      int     tm_hour;        /* hours */
      int     tm_mday;        /* day of the month */
      int     tm_mon;         /* month */
      int     tm_year;        /* year, alkaen vuodesta 1900 */
      int     tm_wday;        /* day of the week */
      int     tm_yday;        /* day in the year */
      int     tm_isdst;       /* daylight saving time */
};

Muistivarauksen kasvattaminen

Joskus on tarvetta kasvattaa mallocilla varatun dynaamisen muistialueen kokoa. Tätä varten on olemassa realloc-funktio.

Seuraavassa otteita ohjelmasta 4-2.c, joka kasvattaa aina tarpeen vaatiessa dynaamisen int-taulukon kokoa.

Aluksi taulukolle t varataan tilaa kahden intin verran mallocilla.

int main(int argc, char *argv[]){
  int *t;       // dynaaminen muistinvaraus taulukolliselle int-lukuja
  int koko = 2; // taulukon koko aluksi
  int lkm = 0;  // talletettujen lukujen määrä
  int x;

  // varataan tilaa
  t = malloc( sizeof(int) * 2 );
Taulukon täyttyessä, varataan lisää tilaa käyttäen reallocia. realloc saa parametrina osoittimen kasvatettavaan muistialueeseen sekä muistialueen uuden koon. Paluuarvona on osoitin kasvatettuun muistialueeseen.
    if ( lkm==koko ) {
      koko = koko+2;    // kasvatetaan kokoa

      // kasvatetaan varatun alueen kokoa
      // ensimmäinen parametri alkuperäinen osoitin, toinen muistialueen uusi koko
      int *tmp =  realloc( t, sizeof(int) * koko );
Operaation jälkeen tarkastetaan onnistuiko muistialueen kasvatus. Palautettu osoitin ei välttämättä ole sama kuin alkuperäisen muistialueen osoite, joten jos operaatio onnistui, kopioidaan reallocin palauttaman pointterin arvo käytetyn muistialueen pointterin arvoksi.
      if ( tmp==NULL ){
        printf("muistinvaraus epäonnistui, poistutaan\n");
        break;
      }
      else{
        t = tmp;
      }
Jos realloc epäonnistuu, alkuperäinen muistinvaraus säilyy. Tämän takia reallocin palauttama pointteri kannattaa aina sijoittaa ensin apumuuttujaan, kuten yllä tehdään. reallocilla on mahdollista myös pienentää dynaamisesti varattua muistialuetta.

Järjestäminen

Eri tyyppisten taulukoiden järjestäminen käy helposti qsort-funktiolla. man-sivu sanoo seuraavasti:
SYNOPSIS
       #include < stdlib.h>

       void qsort(void *base, size_t nmemb, size_t size,
                  int(*compar)(const void *, const void *));

DESCRIPTION
       The  qsort()  function sorts an array with nmemb elements of size size.
       The base argument points to the start of the array.

       The contents of the array are sorted in ascending order according to  a
       comparison  function  pointed  to  by  compar, which is called with two
       arguments that point to the objects being compared.

       The comparison function must return an integer less than, equal to,  or
       greater  than  zero  if  the first argument is considered to be respec-
       tively less than, equal to, or greater than the second.  If two members
       compare as equal, their order in the sorted array is undefined.
Määritelmä saattaa näyttää aluksi pelottavalle, vaikka qsortin käyttö on itseasiassa todella helppoa.

Seuraavassa otteita ohjelmasta 4-3.c, joka käyttää qsortia järjestämään int-taulukon sisällön.

  int t[KOKO];
  
  // täytetään taulukko

  // järjestetään taulukko qsort-funktiolla
  qsort(t, KOKO, sizeof(int), vertaa);
qsortin parametrit ovat
  • osoitin järjestettävän alueen alkuun, eli taulukon nimi
  • järjestettävien alkioiden lukumäärä eli taulukon koko
  • yhden alkion koko (tässä tapauksessa siis alkio on int)
  • funktio, jolla vertaillaan kahden alkion suuruusjärjestystä
  • Neljäs parametri on siis määritelty seuraavasti:
        int(*compar)(const void *, const void *)
    
    Tämä hiukan kummallinen määritelmä tarkoittaa, että kyseessä on funktio, joka palauttaa int:in ja jolla on parametrina kaksi geneeristä osoitinta. void * siis tarkoittaa, että parametri on osoitin johonkin, edessä oleva const lupaa, että funktio ei muuta osoittimen osoittamia muistialueita.

    Funktion tulee palauttaa negatiivinen luku, jos sen parametreistä (tarkemmin sanottuna parametrien osoittamista alkioista) ensimmäinen on pienempi, positiivinen luku, jos toinen pienempi ja jos parametrit ovat yhtä suuret, palauttaa funktio 0.

    Seuraavassa funktio vertaa, jota käytetään ohjelmassa 4-3.c:

    int vertaa(const void *a, const void *b){
      // parametrit tyyppiä const void * eli vakioarvoisia geneerisiäosoittimia
      // koska ne osoittavat oikeasti int:teihin, sijoitetaan niiden
      // arvot paikallisiin int osoitin -muuttujiin
      int *l1 = (int *)a;
      int *l2 = (int *)b;
    
      if ( *l1<*l2 ) return -1;
      else if ( *l1>*l2 ) return 1;
      else return 0;
    }
    
    Parametrin välityksessä käytetään geneeristä tyyppiä void *, mutta koska todellisuudessa kyse int:istä, muuntaa funktio parametrinsa lokaaleiksi int-osoittimiksi.

    Tilapäiset tiedostot

    Joskus ohjelmat tarvitsevat datan prosessointia varten tilapäisiä tiedostoja jotka ovat olemassa vain ohjelman suorituksen ajan. Tilapäiset tiedostot kannattaa luoda tmpfile-funktiolla, joka luo tilapäisen tiedoston ja samalla avaa streamin tiedostoon.

    Seuraavassa otteita ohjelmasta 4-4.c, joka avaa tilapäisen tiedoston, kirjoittaa tiedostoon, lukee tiedoston sisällön ja lopuksi sulkee tilapäisen tiedoston.

    int main(int argc, char *argv[]){
      char buf[MAX];
      FILE *tmp;            // FILE-pointteri tiedostoa varten
    
      // avataan tilapäinen tiedosto
      tmp = tmpfile();
    
    Tämän jälkeen siis tmp on FILE-osoitin, jonka avulla tiedostoa voidaan käsitellä stream I/O:n operaatiolla.

    Kirjoitettuaan tiedostoon merkkijonoja, siirtää tiedosto-osoittimen takaisi tiedoston alkuun käyttäen fseek-funktiota ja tulostaa tiedoston sisällön.

      fseek(tmp, 0, SEEK_SET);
    
      // luetaan tiedoston sisältö, huom: fgets palauttaa NULL kun ollaan lopussa
      while (  fgets( buf, MAX, tmp ) != NULL ){
        printf(buf);
      }
      // suljetaan tilapäinen tiedosto, tiedosto tuhoutuu samalla
      fclose(tmp);
    
    Lopuksi tilapäistiedosto suljetaan ja tiedosto häviää.