[WikiDyd] [TitleIndex] [WordIndex

Języki i metodyka programowania I

Przykłady z wykładów w semestrze jesiennym 2009

Uwaga: wykłady są ułożone według dat: najnowszy na górze, najstarszy na dole.

Wykład X: 8 grudnia 2009

Zagadnienia: podział programu na moduły:interfejs modułu, dynamiczne struktury danych

Rozpoczęliśmy przerabianie jednego z modułów poprzedniego programu: będziemy teraz używać listy liniowej do przechowywania studentów. Aby umożliwić zmianę implementacji modułów przerobiliśmy nieco funkcję main - zmiany miały na celu jak największe uniezależnienie tej funkcji od implementacji modułów.

Przerobiona funkcja main:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 #include <string.h>
   4 
   5 #include "stud_plik.h"
   6 #include "skala_ocen.h"
   7 #include "formater_wy.h"
   8 
   9 
  10 int main( int argc, char **argv ) {
  11         FILE *in = argc > 1 ? fopen( argv[1], "r" ) : stdin;
  12 
  13         skala_ocen_t *skala = zainicjuj_skala( argc > 2 ? argv[2] : "0:2;51:3;61:3.5;71:4;81:4.5;91:5;" );
  14 
  15         plik_t *p = zainicjuj_plik_t( 2 );
  16 
  17   char buf[1024];
  18 
  19         int i= 0;
  20   int n;
  21 
  22   while( fgets( buf, 1024, in ) != NULL )
  23                 if( dodaj_studenta( p, buf ) != 0 ) {
  24                         fprintf( stderr, "%s: brak pamięci po linii nr %i\n", argv[0], i );
  25       return EXIT_FAILURE;
  26                 } else {
  27                         i++;
  28         }
  29         if( in != stdin )
  30                 fclose( in );
  31 
  32   sortuj( p, por_naz_imi );
  33 
  34   wypisz_naglowek( stdout, "Posortowane alfabetycznie" );
  35   n= ile_stud( p );
  36   for( i= 0; i < n; i++ ) {
  37     char *imie;
  38     char *naz;
  39     int ile_p;
  40     if( dane_stud( p, i, &imie, &naz, &ile_p ) ) {
  41                         fprintf( stderr, "%s: kasza w grupie!", argv[0] );
  42                 }
  43                 wypisz_studenta( stdout, imie, naz, ile_p, ocen( ile_p, skala) );               
  44   }
  45   wypisz_stopke( stdout, "" );
  46 
  47         return EXIT_SUCCESS;
  48 }

Opracowaliśmy też plik makefile, w którym dodaliśmy fragment ułatwiający testowanie programu:

objects = main.o formater_wy.o skala_ocen.o stud_plik.o

all: $(objects)
        cc -o oceniaj $(objects)

test: all wzorzec ../w8/dane
        ./oceniaj ../w8/dane > tmp
        diff tmp wzorzec

main.o: main.c stud_plik.h skala_ocen.h formater_wy.h
formater_wy.o: formater_wy.c formater_wy.h
skala_ocen.o: skala_ocen.c skala_ocen.h
stud_plik.o: stud_plik.c stud_plik.h

skala: ts.o skala_ocen.o
        cc -o skala $^

test-skala: skala
        ./skala "0:2;51:3;61:3.5;71:4;81:4.5;91:5"  46 50 55 63 76 99 101
        ./skala "0:2;51:3;61:3.5;71:4;81:4.5;91:5;"  46 50 55 63 76 99 101

ts.o: ts.c skala_ocen.h

clean:
        -rm *.o skala oceniaj tmp

Mogliśmy teraz przystąpić do modyfikacji modułu stud_plik.

Zmodyfikowany plik nagłówkowy wygląda następująco:

   1 #ifndef _STUD_PLIK_H
   2 #define _STUD_PLIK_H
   3 
   4 typedef struct {
   5   char *nazw;
   6   char *imie;
   7   int l_pkt;
   8 } stud_t;
   9 
  10 typedef struct elem {
  11   stud_t d;
  12   struct elem *n;
  13 } *list_t;
  14 
  15 typedef list_t plik_t;
  16 
  17 int ile_stud( plik_t * );
  18 
  19 int dane_stud( plik_t *, int r, char **, char **, int * );
  20 
  21 plik_t *zainicjuj_plik_t( int wlk );  /* Inicjuje str. na wlk, zwraca wskaźnik */
  22 
  23 int dodaj_studenta( plik_t *p, char *opis_stud ); /* Dodaje studenta na koncu */
  24 
  25 void sortuj( plik_t *g, int (*f)( const void *, const void * ) );
  26 
  27 int por_pkt( const void *a, const void *b );
  28 
  29 int por_naz_imi( const void *a, const void *b );
  30 
  31 #endif /* _STUD_PLIK_H */
  32 

a implementacja tak:

   1 #include "stud_plik.h"
   2 #include <stdlib.h>
   3 #include <string.h>
   4 
   5 #ifdef DEBUG
   6 #include <stdio.h>
   7 #endif
   8 
   9 int ile_stud( plik_t *p ) {
  10   int ile= 0;
  11   list_t tmp= *p;
  12   while( tmp != NULL ) {
  13                 ile++;
  14     tmp= tmp->n;
  15         }
  16         return ile;
  17 }
  18 
  19 int dane_stud( plik_t *p, int numer, char **imie, char **naz, int *pkt ) {
  20   int i= 0;
  21   list_t tmp= *p;
  22   while( i < numer ) {
  23                 if( tmp == NULL ) {
  24                         return 1;
  25                 } else {
  26                         i++;
  27       tmp= tmp->n;
  28                 }
  29         }
  30         if( tmp != NULL ) {
  31     *imie= tmp->d.imie;
  32     *naz= tmp->d.nazw;
  33     *pkt= tmp->d.l_pkt;
  34     return 0;
  35         } else {
  36                 return 1;
  37   }
  38 }
  39 
  40 plik_t *zainicjuj_plik_t( int wlk ) {
  41   plik_t *n= malloc( sizeof *n );
  42   *n= NULL;
  43   return n;
  44 }
  45 
  46 list_t doklejnakoniec( list_t l, list_t n ) {
  47   if( l == NULL )
  48           return n;
  49   else {
  50           list_t i= l;
  51           while( i->n != NULL )
  52                 i= i->n;
  53     i->n = n;
  54     return l;
  55         } 
  56 }
  57 
  58 int dodaj_studenta( plik_t *p, char *opis_stud ) {
  59   list_t nowy = malloc( sizeof *nowy );
  60   char *imie= opis_stud;
  61   char *nazw= strchr( opis_stud, ' ' ) + 1;
  62   char *pkt= strchr( nazw, ' ' ) + 1;
  63   *(nazw-1)= '\0';
  64   *(pkt-1)= '\0';
  65   nowy->d.l_pkt = atoi( pkt );
  66   nowy->d.nazw = malloc( strlen( nazw )+1 );
  67   strcpy( nowy->d.nazw, nazw );
  68   nowy->d.imie = malloc( strlen( imie )+1 );
  69   strcpy( nowy->d.imie, imie );
  70   *p= doklejnakoniec( *p, nowy );
  71   return 0;
  72 }
  73 
  74 void sortuj( plik_t *g, int (*f)( const void *, const void * ) ) {
  75         int byla_zamiana= 0;
  76   list_t i;
  77   if( *g == NULL )
  78                 return;
  79         do {
  80                 byla_zamiana= 0;
  81                 i= *g;
  82     while( i->n != NULL ) {
  83                         int cmp= f( &(i->d), &(i->n->d) );
  84       if( cmp > 0 ) {
  85                                 stud_t tmp= i->d;
  86         i->d = i->n->d;
  87                                 i->n->d= tmp;
  88                                 byla_zamiana= 1;
  89                         }
  90       i= i->n;
  91                 }
  92         } while( byla_zamiana );
  93 }
  94 
  95 #include <string.h>
  96 
  97 int por_pkt( const void *a, const void *b ) {
  98    return ((stud_t*)a)->l_pkt - ((stud_t*)b)->l_pkt;
  99 }
 100 
 101 int por_naz_imi( const void *a, const void *b ) {
 102         stud_t *sa = (stud_t *)a;
 103         stud_t *sb = (stud_t *)b;
 104 
 105         int por_naz = strcmp( sa->nazw, sb->nazw );
 106 
 107         if( por_naz != 0 )
 108                 return por_naz;
 109   else
 110                 return strcmp( sa->imie, sb->imie );
 111 }

Ponieważ pozostałe moduły zależą tylko od interfejsu modułu stud_plik, a ten interfejs nie uległ zmianie, a więc nową implementację można ,,bezboleśnie" podłączyć do istniejącego kodu.

Wykład IX: 1 grudnia 2009

Zagadnienia: podział programu na moduły

Rozbudowaliśmy program z poprzedniego wykładu. Dodaliśmy moduł obsługujący wystawianie ocen i moduł wypisujący wyniki w postaci pliku HTMl (uproszczonego). Mówiliśmy dużo o zasadach rozsądnego podejścia do projektowania struktury programu i zaletach takiego postępowania:

Teraz kod. Najpierw moduł wystawiający oceny. Kod jest poprawiony w stosunku do tego, co robiliśmy 24 listopada, ale funkcjonalność pozostała niezmieniona - inne moduły nie wymagają poprawek.

   1 #ifndef _SKALA_OCEN_H
   2 #define _SKALA_OCEN_H
   3 
   4 #include <stdio.h>
   5 
   6 typedef struct {
   7   int min_pkt;
   8   double ocena;
   9 } prog_t;
  10 
  11 typedef struct {
  12         int n_prg;
  13   prog_t *progi;
  14 } skala_ocen_t;
  15 
  16 skala_ocen_t * zainicjuj_skala( char * opis_skali );
  17 
  18 double ocen( int liczba_pkt, skala_ocen_t *skala );
  19 
  20 void wypisz_skala( FILE *out, skala_ocen_t * s );
  21 
  22 #endif
  23 

   1 #include "skala_ocen.h"
   2 #include <stdio.h>
   3 #include <stdlib.h>
   4 #include <string.h>
   5 
   6 int fcmp( const void *a, const void *b ) {
   7         prog_t *pa = (prog_t * )a;
   8         prog_t *pb = (prog_t * )b;
   9   return pa->min_pkt - pb->min_pkt;
  10 }
  11 
  12 skala_ocen_t * zainicjuj_skala( char * opis_skali ) {
  13    skala_ocen_t *s; 
  14          int n;
  15    int i;
  16    n= 0;
  17    for( i= 0; opis_skali[i] != '\0'; i++ )
  18                         if( opis_skali[i] == ';' )
  19                                 n++;
  20    if( n < 1 )
  21                         return NULL;
  22    s = malloc( sizeof *s );
  23    s->progi = malloc( n * sizeof * s->progi );
  24    s->n_prg = n;
  25    for( i= 0; i < n; i++ ) {
  26                         if( sscanf( opis_skali, "%d:%lf",
  27                   &(s->progi[i].min_pkt),
  28                   &(s->progi[i].ocena) ) != 2 )
  29                                 return NULL;
  30       opis_skali = strchr( opis_skali, ';' ) + 1;
  31    }
  32 
  33    qsort( s->progi, s->n_prg, sizeof s->progi[0], fcmp );
  34 
  35    return s;
  36 }
  37 
  38 double ocen( int liczba_pkt, skala_ocen_t *skala ) {
  39   int i;
  40 
  41         for( i= skala->n_prg - 1; i >= 0; i-- )
  42                 if( liczba_pkt >= skala->progi[i].min_pkt )
  43             return skala->progi[i].ocena;
  44 
  45   return 0;
  46 }       
  47 
  48 void wypisz_skala( FILE *out, skala_ocen_t * s ) {
  49   int i;
  50 
  51   fprintf( out, "\nSkala ocen:"
  52                 "\n-----------\n" );
  53 
  54   for( i= 0; i < s->n_prg; i++ )
  55                 fprintf( out, "%d\t->\t%g\n", s->progi[i].min_pkt, s->progi[i].ocena );
  56 }

A teraz formater:

   1 #ifndef _FORMATER_WY_H
   2 #define _FORMATER_WY_H
   3 
   4 #include <stdio.h>
   5 
   6 void wypisz_naglowek( FILE *, char * );
   7 
   8 void wypisz_studenta( FILE *, char *i, char *n, int p, double o );
   9 
  10 void wypisz_stopke( FILE *, char * );
  11 #endif
  12 

   1 #include "formater_wy.h"
   2 #include <stdio.h>
   3 
   4 void wypisz_naglowek( FILE *w, char *n ) {
   5         fprintf( w, "<html>\n"
   6               "<head>\n"
   7               "<title>%s</title>\n"
   8               "</head>\n"
   9               "<body>\n", n );
  10   fprintf( w, "<h1>%s</h1>\n", n );
  11   fprintf( w, "<table border=1>\n"
  12               "<tr><th>Imie i nazwisko</th><th>Liczba punktow</th><th>Ocena</th></tr>" );
  13 }
  14 
  15 void wypisz_studenta( FILE *w, char *i, char *n, int p, double o ) {
  16   fprintf( w, "<tr><td>%s %s</td><td align=\"center\">%d</td><td align=\"center\">%g</td></tr>", i, n, p, o );
  17 }
  18 
  19 void wypisz_stopke( FILE *w, char *n ) {
  20         fprintf( w, "</table>\n"
  21               "</body>\n"
  22               "</html>" );
  23 }

Na koniec program główny:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 #include "stud_plik.h"
   4 #include "skala_ocen.h"
   5 #include "formater_wy.h"
   6 #include <string.h>
   7 
   8 int por_pkt( const void *a, const void *b ) {
   9    return ((stud_t*)a)->l_pkt - ((stud_t*)b)->l_pkt;
  10 }
  11 
  12 int por_naz_imi( const void *a, const void *b ) {
  13         stud_t *sa = (stud_t *)a;
  14         stud_t *sb = (stud_t *)b;
  15 
  16         int por_naz = strcmp( sa->nazw, sb->nazw );
  17 
  18         if( por_naz != 0 )
  19                 return por_naz;
  20   else
  21                 return strcmp( sa->imie, sb->imie );
  22 }
  23 
  24 int main( int argc, char **argv ) {
  25         FILE *in = argc > 1 ? fopen( argv[1], "r" ) : stdin;
  26 
  27         skala_ocen_t *skala = zainicjuj_skala( argc > 2 ? argv[2] : "0:2;51:3;61:3.5;71:4;81:4.5;91:5;" );
  28 
  29         plik_t *p = zainicjuj_plik_t( 2 );
  30 
  31   char buf[1024];
  32 
  33         int i= 0;
  34 
  35   while( fgets( buf, 1024, in ) != NULL )
  36                 if( dodaj_studenta( p, buf ) != 0 ) {
  37                         fprintf( stderr, "%s: brak pamięci po linii nr %i\n", argv[0], i );
  38       return EXIT_FAILURE;
  39                 } else {
  40                         i++;
  41         }
  42         if( in != stdin )
  43                 fclose( in );
  44 
  45   qsort( p->grupa, p->n, sizeof *p->grupa, por_naz_imi );
  46 
  47   wypisz_naglowek( stdout, "Posortowane alfabetycznie" );
  48   for( i= 0; i < p->n; i++ )
  49                 wypisz_studenta( stdout, p->grupa[i].imie, p->grupa[i].nazw, 
  50                      p->grupa[i].l_pkt, ocen( p->grupa[i].l_pkt, skala) );              
  51   wypisz_stopke( stdout, "" );
  52 
  53         return EXIT_SUCCESS;
  54 }

Wykład IX: 24 listopada 2009

Zagadnienia: struktury, alokacja pamięci, uruchamianie programu: modułowość i możliwie wczesne testowanie

Dzisiejszy wykład jest wprowadzeniem do problematyki projektowania programu jako kolekcji możliwie niezależnych modułów. Będziemy się tym zajmować przez najbliższe 3 wykłady.

Po pierwsze chcemy zmienić sposób przechowywania w pamięci plików zawierających wyniki testu. Przypomnijmy, że taki plik składa się z linii, a każda linia zawiera imię, nazwisko i liczbę punktów uzyskanych przez studenta. Na poprzednim wykładzie trzymaliśmy w pamięci po prostu tablicę linii, teraz chcemy odwzorować coś bardziej zbliżonego do rzeczywistości:

   1 #ifndef _STUD_PLIK_H
   2 #define _STUD_PLIK_H
   3 
   4 typedef struct {
   5   char *nazw;
   6   char *imie;
   7   int l_pkt;
   8 } stud_t;
   9 
  10 typedef struct {
  11   stud_t *grupa;
  12   int wlk;
  13   int n;
  14 } plik_t;
  15 
  16 plik_t *zainicjuj_plik_t( int wlk );  /* Inicjuje str. na wlk, zwraca wskaźnik */
  17 
  18 int dodaj_studenta( plik_t *p, char *opis_stud ); /* Dodaje studenta na koncu */
  19 
  20 #endif
  21 

   1 #include "stud_plik.h"
   2 #include <stdlib.h>
   3 #include <string.h>
   4 
   5 #ifdef DEBUG
   6 #include <stdio.h>
   7 #endif
   8 
   9 plik_t *zainicjuj_plik_t( int wlk ) {
  10         plik_t *n = malloc( sizeof *n );
  11   if( n != NULL ) {
  12           if( (n->grupa = malloc( wlk * sizeof * n->grupa )) == NULL ) {
  13                         free( n );
  14       return NULL;
  15                 }
  16     n->wlk = wlk;
  17     n->n = 0;
  18   }
  19   return n;
  20 }
  21 
  22 int nie_mozna_powiekszyc( plik_t *p ) {
  23   int nwlk = 2*p->wlk;
  24 #ifdef DEBUG
  25         fprintf( stderr, "Akt. wlk = %d, proba realokacji na %d\n", p->wlk, nwlk );
  26 #endif
  27   stud_t *tmp = malloc( nwlk * sizeof *tmp );
  28   if( tmp != NULL ) {
  29     memcpy( tmp, p->grupa, p->wlk * sizeof p->grupa[0] );
  30     free( p-> grupa );
  31     p->grupa = tmp;
  32                 p->wlk *= 2;
  33     return 0;
  34         } else
  35                 return 1;
  36 }       
  37 
  38 int dodaj_studenta( plik_t *p, char *opis_stud ) {
  39   if( p->n == p->wlk && nie_mozna_powiekszyc( p ) ) {
  40     return 2;
  41         } else {
  42     char *imie= opis_stud;
  43     char *nazw= strchr( opis_stud, ' ' ) + 1;
  44     char *pkt= strchr( nazw, ' ' ) + 1;
  45     *(nazw-1)= '\0';
  46     *(pkt-1)= '\0';
  47     p->grupa[p->n].l_pkt = atoi( pkt );
  48     p->grupa[p->n].nazw = malloc( strlen( nazw )+1 );
  49     strcpy( p->grupa[p->n].nazw, nazw );
  50     p->grupa[p->n].imie = malloc( strlen( imie )+1 );
  51     strcpy( p->grupa[p->n].imie, imie );
  52     p->n++;
  53     return 0;
  54   }
  55 }

   1 #ifndef _SKALA_OCEN_H
   2 #define _SKALA_OCEN_H
   3 
   4 typedef struct {
   5         int n_prg;
   6   int *min_pkt;
   7   double *ocena;
   8 } skala_ocen_t;
   9 
  10 skala_ocen_t * zainicjuj_skala( char * opis_skali );
  11 
  12 double ocen( int liczba_pkt, skala_ocen_t *skala );
  13 
  14 #endif
  15 

   1 #include "skala_ocen.h"
   2 #include <stdio.h>
   3 #include <stdlib.h>
   4 #include <string.h>
   5 
   6 skala_ocen_t * zainicjuj_skala( char * opis_skali ) {
   7    skala_ocen_t *s; 
   8          int n;
   9    int i;
  10    n= 0;
  11    for( i= 0; opis_skali[i] != '\0'; i++ )
  12                         if( opis_skali[i] == ';' )
  13                                 n++;
  14    if( n < 1 )
  15                         return NULL;
  16    s = malloc( sizeof *s );
  17    s->min_pkt = malloc( n * sizeof * s->min_pkt );
  18    s->ocena = malloc( n * sizeof * s->ocena );
  19    s->n_prg = n;
  20    for( i= 0; i < n; i++ ) {
  21                         if( sscanf( opis_skali, "%d:%lf", &(s->min_pkt[i]), &(s->ocena[i]) ) != 2 )
  22                                 return NULL;
  23       opis_skali = strchr( opis_skali, ';' ) + 1;
  24    }
  25 
  26    return s;
  27 }
  28 
  29 double ocen( int liczba_pkt, skala_ocen_t *skala ) {
  30   int i;
  31 
  32         for( i= skala->n_prg - 1; i >= 0; i-- )
  33                 if( liczba_pkt >= skala->min_pkt[i] )
  34             return skala->ocena[i];
  35 
  36   return 0;
  37 }       

Wykład VIII: 17 listopada 2009

Zagadnienia: struktury, tablice wielowymiarowe, alokacja pamięci, zarządzanie pamięcią, biblioteka standardowa: string.h, qsort, malloc, realloc, uruchamianie programu: modułowość i możliwie wczesne testowanie

Jako przykład analizowaliśmy program przetwarzający listę wyników egzaminu. Przygotowaliśmy następujący plik z danymi testowymi:

Jan Kowalski 28
Adam Nowak 33
Jose Perezbardzodlugienazwiskojaktonapoludniu 78
Zosia Samosia 8
Mis Puchatek 123
Prosiaczek Beznazwiska 43
Tygrysek Tezbeznazwiskaaleinnego 89
Klapouchy Osiolek 0
Myszka Miki 12
Minnie Miki 99
Kaczor Donald 67
I Wystarczy 89

Program miałby przede wszystkim wczytywać listę z pliku i przechowywać ją w pamięci. Do tego celu zaproponowaliśmy strukturę danych składającą się z tablicy wskaźników na wiersze pliku, liczby całkowitej określającej pojemność tej tablicy i kolejnej liczby całkowitej określającej, ile elementów tablicy wskaźników jest zajętych:

   1 typedef struct {
   2   char **linie;   /* tablica wskaźników na linie pliku */
   3   int wlk;        /* pojemność tablicy linie */
   4   int n;          /* liczba zajętych elementów tablicy linie */
   5 } plik_t;

Rozpoczęliśmy od opracowania kodu, który pozwoli na zainicjowanie takiej struktury:

   1 plik_t *zainicjuj_plik_t( int wlk ) {
   2   plik_t *n = malloc( sizeof *n );
   3   if( n != NULL ) {
   4     if( (n->linie = malloc( wlk * sizeof * n->linie )) == NULL ) {
   5       free( n );
   6       return NULL;
   7     }
   8     n->wlk = wlk;
   9     n->n = 0;
  10   }
  11   return n;
  12 }

Następnie opracowaliśmy funkcję, która pozwoli wczytać do takiej struktury dane z pliku. Podstawowa sprawa, to dodawanie do struktury nowej linii:

   1 int dodaj_linie( plik_t *p, char *nowa_l ) {
   2   if( p->n == p->wlk && nie_mozna_powiekszyc( p ) ) {
   3     return 2;
   4   } else {
   5     int n = strlen( nowa_l );
   6     char *k = malloc( n+1 );
   7     if( k == NULL )
   8       return 1;
   9     strcpy( k, nowa_l );
  10     p->linie[p->n] = k;
  11     p->n++;
  12     return 0;
  13   }
  14 }

W trakcie realizacji powyższej funkcji pojawiła się konieczność zaimplementowania funkcji, która w razie potrzeby powiększy tablicę. Pozwoli to uzyskać ,,inteligentny" kontener, który automatycznie dopasuje swoją wielkość do danych, które ma przechowywać. Po eksperymentach z funkcją realloc (okazało się, że nie możemy dwukrotnie powiększać tej samej tablicy - wrócimy jeszcze do tego problemu) wykonaliśmy następującą funkcję:

   1 int nie_mozna_powiekszyc( plik_t *p ) {
   2   int nwlk = 2*p->wlk;
   3 #ifdef DEBUG
   4   fprintf( stderr, "Akt. wlk = %d, proba realokacji na %d\n", p->wlk, nwlk );
   5 #endif
   6   char **tmp = malloc( nwlk * sizeof *tmp );
   7   if( tmp != NULL ) {
   8     /*  alternatywa dla funkcji memcpy:
   9     int i;
  10     for( i= 0; i < p->wlk; i++ )
  11       tmp[i] = p->linie[i];
  12     */
  13     memcpy( tmp, p->linie, p->wlk * sizeof p->linie[0] );
  14     free( p-> linie );
  15     p->linie = tmp;
  16     p->wlk *= 2;
  17     return 0;
  18   } else
  19     return 1;
  20 }

Program testujący opracowane funkcje wygląda następująco:

   1 int main( int argc, char **argv ) {
   2   FILE *in = argc > 1 ? fopen( argv[1], "r" ) : stdin;
   3 
   4   plik_t *p = zainicjuj_plik_t( 2 ); /* inicjujemy mały kontener, aby przetestowac powiekszanie */
   5 
   6   char buf[1024];  /* bufor do czytania poszczegolnych linii - 1024 to maks. dlugosc linii */
   7 
   8   int i= 0;
   9 
  10   while( fgets( buf, 1024, in ) != NULL )
  11     if( dodaj_linie( p, buf ) != 0 ) {
  12       fprintf( stderr, "%s: brak pamieci po przeczytaniu linii nr %i\n", argv[0], i );
  13       return EXIT_FAILURE;
  14     } else {
  15       i++;
  16   }
  17   if( in != stdin )
  18     fclose( in );
  19 
  20   printf( "Wczytane dane:\n" );
  21   for( i= 0; i < p->n; i++ )
  22     printf( "%s", p->linie[i] );
  23 
  24   return EXIT_SUCCESS;
  25 }

Pod koniec wykładu zajęliśmy się problemem sortowania danych. Pokazaliśmy, jak posortować strukturę plik_t ze względu na rozmaite kryteria. Wykorzystaliśmy w tym celu funkcję qsort. Pokazaliśmy, że posortowanie listy ze względu na liczbę punktów wymaga stworzenia następującej funkcji pomocniczej:

   1 int funkcja_por( const void *a, const void *b ) {
   2   char *na = *(char**)a;   /* rzutujemy a na odpowiedni typ, pobieramy napis */
   3   char *nb = *(char**)b;   /* to samo robimy z b */
   4   int la;
   5   int lb;
   6   na = strrchr( na, ' ' ); /* ustawiamy na na ostatniej od konca spacji w napisie  wskazywanym przez a*/
   7   sscanf( na, "%d", &la ); /* odczytujemy z na liczbe punktów w linii wskazywanej przez a */
   8   nb = strrchr( nb, ' ' ); /* to samo z napisem b */
   9   sscanf( nb, "%d", &lb );
  10   return la-lb;  /* wynik bedzie ujemy jesli la < lb, 0 jesli la == lb, dodatni, gdy la jest wieksza od lb */
  11 }

i wywołania funkcji qsort w postaci:

   1    qsort( p->linie, p->n, sizeof *p->linie, funkcja_por );

Posortowanie alfabetyczne wymaga napisania funkcji

   1 int funkcja_por_alfabet( const void *a, const void *b ) {
   2   char *na = *(char**)a;
   3   char *nb = *(char**)b;
   4   return strcmp( na, nb );
   5 }

i wywołania qsort w postaci:

   1    qsort( p->linie, p->n, sizeof *p->linie, funkcja_por_alfabet );

Jako pracę domową powinni Państwo opracować sortowanie według nazwiska a następnie imienia.

Cały kod w załączniku

Rozwiązanie zadania domowego:

   1 int por_naz_imi( const void *a, const void *b ) {
   2   char *imiea = *(char**)a;
   3   char *imieb = *(char**)b;
   4   char *nazwa= strchr( imiea, ' ' ) + 1;
   5   char *nazwb= strchr( imieb, ' ' ) + 1;
   6   int por_naz = strcmp( nazwa, nazwb );
   7   if( por_naz == 0 )
   8        return strcmp( imiea, imieb );
   9   else
  10       return por_naz;
  11 }

Wykład VII: 10 listopada 2009

Zagadnienia: struktury, tablice wielowymiarowe, pliki nagłówkowe raz jeszcze

Po pierwsze przerobiliśmy program tablicujący funkcje tak, aby wykorzystać struktury. Zapewniło to ścisłe powiązanie nazwy funkcji z uchwytem (wskaźnikiem).

Oto przerobiony program:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 #include <math.h>
   4 #include <string.h>
   5 
   6 typedef double (*wfdd_t)(double);
   7 
   8 struct para {
   9   wfdd_t f; /* double (*f) ( double ); */
  10   char * n;
  11 };
  12 
  13 void tabelka( double (*f)(double), double a, double b, int n, FILE *out )
  14 {
  15         int i;
  16         double dx = ( b - a ) / ( n - 1 );
  17 
  18         for( i= 0; i < n; i++ )
  19                 fprintf( out, "%g %g\n", a+i*dx, f(a+i*dx) );
  20 }
  21 
  22 double moja( double x ) {
  23         return x*x+2*x;
  24 }
  25 
  26 wfdd_t
  27 wybierz_fun( char * fun_name )
  28 {
  29         wfdd_t wybrana_fun = NULL;
  30   
  31   struct para fn[5] = {
  32                 { cos, "cos" },
  33     { tan, "tan" },
  34                 { sin, "sin" },
  35     { moja, "moja" },
  36     { atan, "atan" }
  37   };
  38 
  39         int i;
  40 
  41 #ifdef DEBUG
  42         fprintf( stderr, "Wielkosc fn[4] to %d bajtow\n", sizeof fn[4] );
  43         fprintf( stderr, "Wielkosc fn[4].f to %d bajtow\n", sizeof fn[4].f );
  44         fprintf( stderr, "Wielkosc fn[4].n to %d bajtow\n", sizeof fn[4].n );
  45 #endif
  46 
  47   for( i= 0; i  < 5; i++ )
  48                 if( strcmp( fn[i].n, fun_name ) == 0 ) 
  49                         wybrana_fun = fn[i].f;
  50 
  51   return wybrana_fun;
  52 }
  53 
  54 int
  55 main( int argc, char **argv ) {
  56         char *fun_name = argv[1];
  57         double a = argc > 2 ? atof( argv[2] ) : 0.;
  58         double b = argc > 3 ? atof( argv[3] ) : 1.;
  59         int n    = argc > 4 ? atoi( argv[4] ) : 50;
  60 
  61         FILE *out = argc > 5 ? fopen( argv[5], "w" ) : stdout;
  62 
  63         wfdd_t wybrana_fun = wybierz_fun( fun_name );
  64 
  65   if( wybrana_fun != NULL )
  66                 tabelka( wybrana_fun, a, b, n, out );
  67         else
  68                 fprintf( stderr, "Nie znam funkcji \"%s\"\n", fun_name );
  69 
  70         return 0;
  71 }

Przy okazji wspomnieliśmy o strukturach i deklaracji typu za pomocą typedef.

Potem wydzieliliśmy moduł odpowiedzialny za przetwarzanie nazwy funkcji na wskaźnik. Wspomnieliśmy przy okazji o zastosowaniu plików nagłówkowych i możliwości alternatywnej konfiguracji kodu za pomocą dyrektyw preprocesora (#ifndef).

Oto plik nagłówkowy:

   1 #ifndef WYB_FUN_H_IS_INCLUDED
   2 #define WYB_FUN_H_IS_INCLUDED
   3 
   4 typedef double (*wfdd_t)(double);
   5 
   6 wfdd_t wybierz_fun( char * fun_name );  /* zamienia nazwe f. na wskaznik do f.
   7                                            zwraca NULL, gdy nie zna nazwy */
   8 
   9 #endif
  10 

I plik z kodem:

   1 #include <string.h>
   2 #include <math.h>
   3 #include "wyb_fun.h"
   4 
   5 struct para {
   6   wfdd_t f; /* double (*f) ( double ); */
   7   char * n;
   8 };
   9 
  10 wfdd_t
  11 wybierz_fun( char * fun_name )
  12 {
  13         wfdd_t wybrana_fun = NULL;
  14   
  15   struct para fn[] = {
  16                 { cos, "cos" },
  17     { tan, "tan" },
  18                 { sin, "sin" },
  19     { atan, "atan" }
  20   };
  21 
  22         int i;
  23 
  24   for( i= 0; i  < sizeof fn / sizeof fn[0]; i++ )
  25                 if( strcmp( fn[i].n, fun_name ) == 0 ) 
  26                         wybrana_fun = fn[i].f;
  27 
  28   return wybrana_fun;
  29 }

Na koniec rozpoczęliśmy rozważania o tablicach wielowymiarowych. Wyjaśniliśmy znaczenie definicji

   double a[2][3];

Powiedzieliśmy, że macierz przechowuje się wierszami i zademonstrowaliśmy, jak wyznacza się położenie pewnego elementu w pamięci (offset względem początku tablicy). Dowiedzieli się Państwo, że

   a[i][j] -> *( a + i * LICZBA_KOLUMN + j )

a więc kompilator musi znać liczbę kolumn macierzy, aby poprawnie ją indeksować.

Wykład VI: 3 listopada 2009

Zagadnienia: wprowadzenie do make, wskaźniki na funkcje

Omawialiśmy program z laboratorium - temat 2

Posiłkowaliśmy się też następującym kodem:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 #include <math.h>
   4 #include <string.h>
   5 
   6 void tabelka( double (*f)(double), double a, double b, int n, FILE *out )
   7 {
   8         int i;
   9         double dx = ( b - a ) / ( n - 1 );
  10 
  11         for( i= 0; i < n; i++ )
  12                 fprintf( out, "%g %g\n", a+i*dx, f(a+i*dx) );
  13 }
  14 
  15 double moja( double x ) {
  16         return x*x+2*x;
  17 }
  18 
  19 int
  20 main( int argc, char **argv ) {
  21         char *fun_name = argv[1];
  22         double a = argc > 2 ? atof( argv[2] ) : 0.;
  23         double b = argc > 3 ? atof( argv[3] ) : 1.;
  24         int n    = argc > 4 ? atoi( argv[4] ) : 50;
  25 
  26         int i;
  27 
  28         FILE *out = argc > 5 ? fopen( argv[5], "w" ) : stdout;
  29 
  30         double (*wybrana_fun)( double ) = NULL;
  31 
  32         double (*f[5])( double );
  33         char *nazwy[5] = { "cos", "sin", "tan", "moja", "atan" };
  34 
  35         f[0] = cos;
  36         f[1] = sin;
  37         f[2] = tan;
  38         f[3] = moja;
  39         f[4] = atan;
  40 
  41   for( i= 0; i  < 5; i++ )
  42                 if( strcmp( nazwy[i], fun_name ) == 0 ) 
  43                         wybrana_fun = f[i];
  44 
  45   if( wybrana_fun != NULL )
  46                 tabelka( wybrana_fun, a, b, n, out );
  47         else
  48                 fprintf( stderr, "Nie znam funkcji \"%s\"\n", fun_name );
  49 
  50         return 0;
  51 }

Wykład V: 27 października 2009

Zagadnienia: powtórka(wejście/wyjście - stdin, stdout, stderr, funkcje fgetc i fgets), debugger gdb

Wykład IV: 20 października 2009

Zagadnienia: wejście/wyjście - stdin, stdout, stderr, funkcje fgetc i fgets

Pisaliśmy funkcję, która czyta macierz z pliku o postaci:

3 x 4 [
  1 2 3 4
  10 20 30 40
  100 200 300 400
]

Oto biblioteka (troszkę poprawiona w stosunku do tego, co widzieli Państwo na tablicy ;-): bibmac.h

   1 #include <stdio.h>
   2 
   3 int czytaj_macierz( FILE *we, double *m, int *lw, int *lk, int maks_wlk );
   4 
   5 void wypisz_macierz( double *m, int lw, int lk, FILE *wy );

bibmac.c

   1 #include "bibmac.h"
   2 
   3 #define MAX_LINE_LENGTH 1024  /* Maksymalna długość linii w pliku wejściowym */
   4 
   5 int zjedz_spacje_tabulatory( FILE *str )
   6 /* Zjada spacje i tabulatory ze strumienia str
   7    zwraca pierwszy znak różny od spacji i tabulatora
   8 */
   9 {
  10   int c;
  11   while( (c= fgetc(str)) == ' ' || c == '\t' )
  12     ;
  13   return c;
  14 }
  15 
  16 #include <ctype.h> /* zawiera makro isdigit (==jest-cyfrą) */
  17 
  18 char *zjedz_liczbe( char *p )
  19 /* Przesuwa p za spacje, tabulatory i jedną liczbę */
  20 {
  21   while( *p == ' ' || *p == '\t' )
  22     p++;
  23   while( isdigit( *p ) || *p == '.' || *p == '-' || *p == '+' || *p == 'e' || *p == 'E' )
  24     p++;
  25   return p;
  26 }
  27 
  28 int czytaj_macierz( FILE *we, double *m, int *lw, int *lk, int maks_wlk )
  29 /* czyta macierz ze strumienia we w nastepującym formacie
  30  <w> x <k> [
  31    <a_11> ..... <a_1k>
  32    ...
  33    <a_w1> ..... <a_wk>
  34 ]
  35 
  36 Elementy <a_11> ... <a_wk> są pakowane wierszami do m */
  37 {
  38   int i,j;  /* zmienne robocze */
  39 
  40   if( we == NULL )
  41     return -1; /* nie da się czytać z NULL */
  42 
  43   if( fscanf( we, "%d x %d", lw, lk ) != 2 || *lw < 1 || *lk < 1 )
  44     return -2; /* brak wymiarów macierzy lub złe wymiary */
  45 
  46   if( *lw * *lk > maks_wlk )
  47     return -3; /* za mało miejsca w pamięci */
  48 
  49   if( zjedz_spacje_tabulatory( we ) != '[' )
  50     return -4; /* zła struktura pliku */
  51 
  52   if( zjedz_spacje_tabulatory( we ) != '\n' )
  53     return -4; /* zła struktura pliku */
  54 
  55   for( i= 0; i < *lw; i++ ) {
  56     /* czytaj i-ty wiersz */
  57 
  58     char buf[MAX_LINE_LENGTH];
  59     char *ptr;
  60 
  61     if( fgets( buf, MAX_LINE_LENGTH, we ) == NULL )
  62       return i; /* brakuje wiersza nr i (liczone od 0) */
  63 
  64     ptr= buf;
  65     for( j= 0; j < *lk; j++ ) {
  66       if( sscanf( ptr, "%lf", m+i*(*lk)+j ) != 1 )
  67         return *lw * *lk + i* *lk +j; /* brakuje elementu (i,j) */
  68       ptr= zjedz_liczbe( ptr );
  69     }
  70   }
  71 
  72   if( zjedz_spacje_tabulatory( we ) != ']' )
  73     return -4;
  74 
  75   return 0;
  76 }
  77 
  78 void wypisz_macierz( double *m, int lw, int lk, FILE *wy )
  79 /* symetryczna do czytaj_macierz */
  80 {
  81   int i,j;
  82 
  83   fprintf( wy, "%d x %d [\n", lw, lk );
  84   for( i= 0; i < lw; i++ ) {
  85     for( j= 0; j < lk; j++ )
  86       fprintf( wy, " %7g", m[i*lk+j] );
  87     fprintf( wy, "\n" );
  88   }
  89   fprintf( wy, "]\n" );
  90 }

Plij testujący - test1.c (uwaga: brakuje w nim #define MAX_SIZE - zostawiamy to do ustalenia przy kompilacji):

   1 #include <stdio.h>
   2 #include "bibmac.h"
   3 
   4 int
   5 main( int argc, char **argv )
   6 {
   7   FILE *we = argc > 1 ? fopen( argv[1], "r" ) : stdin;
   8   FILE *wy = argc > 2 ? fopen( argv[2], "w" ) : stdout;
   9 
  10   double v[MAX_SIZE];
  11   int w,k;
  12 
  13   int blad;
  14 
  15   if( we == NULL ) {
  16     fprintf( stderr, "Złe dane wejściowe!\n" );
  17     return 1;
  18   }
  19   if( wy == NULL ) {
  20     fprintf( stderr, "Nie mogę zapisac wyników!\n" );
  21     return 2;
  22   }
  23 
  24   if( (blad= czytaj_macierz( we, v, &w, &k, MAX_SIZE )) != 0 ) {
  25     fprintf( stderr, "Błędy przy czytaniu - kod błędu: %d\n", blad );
  26     return 3;
  27   }
  28 
  29   wypisz_macierz( v, w, k, wy );
  30 
  31   return 0;
  32 }

Przykładowe dane (jeden plik jest już na górze):

9 x 6 [
  .1 .2 .3 .4 .5 .6
  10 20 30 40 50 60
  100 200 300 400 500 600
  1e3 2e3 3e3 4e3 5e3 6e3
  1e4 2e4 3e4 4e4 5e4 6e4
  1e5 2e5 3e5 4e5 5e5 6e5
  1e6 2e6 3e6 4e6 5e6 6e6
  1e7 2e7 3e7 4e7 5e7 6e7
  1e8 2e8 3e8 4e8 5e8 6e8
]

Inny przykład (błędne dane):

3 x 3 [
  1.1 1.2 1.3
  2.1 2.2
  3.1 3.2
]

Proszę skompilowac program tak:

$ cc -Wall test1.c bibmac.c -DMAX_SIZE=12

a następnie tak:

$ cc -Wall test1.c bibmac.c -DMAX_SIZE=100

za każdym razem testując go na wszystkich danych.

Proszę spróbowac przygotować plik o złej strukturze, który zostanie zaakceptowany przez program.

Wykład III: 19 października 2009

Zagadnienia: funkcja jako abstrakcja algorytmu.

Wynikiem rozważań był następujący program: Plik nagłówkowy (bib.h)

   1 int napisy_na_liczby( char *napisy[], int ile_napisow, double liczby[], int maks_liczb );
   2 int wczytaj_liczby_z_pliku( char *nazwapliku, double liczby[], int maks_liczb );
   3 int zapisz_liczby_do_pliku( char *nazwapliku, char *frmt, double liczby[], int n );
   4 void wypisz_wektor( int, double [] );
   5 void odwroc_kolejnosc( double [], int );
   6 double maks_element_wektora( double [], int );
   7 double min_element_wektora( double [], int );

Biblioteka (bib.c)

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 #include "bib.h"
   5 
   6 
   7 int
   8 napisy_na_liczby (char *napisy[], int ile_napisow, double liczby[],
   9                   int maks_liczb)
  10 {
  11   int i;
  12 
  13   if (maks_liczb < ile_napisow)
  14     {
  15       return -1;                /* zbyt mała tablica */
  16     }
  17 
  18   for (i = 0; i < ile_napisow; i++)
  19     liczby[i] = atof (napisy[i]);
  20 
  21   return ile_napisow;
  22 }
  23 
  24 int
  25 wczytaj_liczby_z_pliku (char *nazwapliku, double liczby[], int maks_liczb)
  26 {
  27   FILE *we = fopen (nazwapliku, "r");
  28   int i = 0;
  29   double x;                     /* kolejna czytana liczba */
  30 
  31   if (we == NULL)
  32     {
  33       return 0;
  34     }
  35 
  36   while (fscanf (we, "%lf", &x) == 1)
  37     {
  38       if (i < maks_liczb)
  39         {
  40           liczby[i++] = x;
  41         }
  42       else
  43         {
  44           return -maks_liczb;
  45         }
  46     }
  47   if (feof (we))
  48     {
  49       fclose (we);
  50       return i;
  51     }
  52   else
  53     {
  54       fclose (we);
  55       return -i;
  56     }
  57 }
  58 
  59 int
  60 zapisz_liczby_do_pliku (char *nazwapliku, char *frmt, double liczby[], int n)
  61 {
  62   int i;
  63   FILE *wy = fopen (nazwapliku, "w");
  64 
  65   if (wy != NULL)
  66     {
  67       for (i = 0; i < n; i++)
  68         fprintf (wy, frmt, liczby[i]);
  69       fclose (wy);
  70       return 0;
  71     }
  72   else
  73     {
  74       return 1;
  75     }
  76 }
  77 
  78 void
  79 wypisz_wektor (int n, double w[])
  80 {
  81   int i;
  82 
  83   for (i = 0; i < n; i++)
  84     printf ("%g\n", w[i]);
  85 }
  86 
  87 double
  88 maks_element_wektora (double w[], int n)
  89 {
  90   double maks = w[0];
  91 
  92   while (--n)
  93     {
  94       if (w[n] > maks)
  95         {
  96           maks = w[n];
  97         }
  98     }
  99 
 100   return maks;
 101 }
 102 
 103 void
 104 odwroc_kolejnosc (double v[], int n)
 105 {
 106   int i;
 107 
 108   for (i = 0; i < n / 2; i++)
 109     {
 110       double tmp = v[i];
 111       v[i] = v[n - i - 1];
 112       v[n - i - 1] = tmp;
 113     }
 114 
 115 }
 116 
 117 double
 118 min_element_wektora (double w[], int n)
 119 {
 120   int i;
 121   double min = w[0];
 122 
 123   for (i = 1; i < n; i++)
 124     {
 125       if (w[i] < min)
 126         {
 127           min = w[i];
 128         }
 129     }
 130 
 131   return min;
 132 }

Programy używające biblioteki:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 #include "bib.h"
   5 
   6 #define MAKS_LICZBA_ARGUMENTOW 100000
   7 
   8 int
   9 main (int argc, char *argv[])
  10 {
  11   double v[MAKS_LICZBA_ARGUMENTOW];
  12 
  13   int n = napisy_na_liczby( argv+1, argc-1, v, MAKS_LICZBA_ARGUMENTOW );
  14 
  15   printf ("Mamy %d liczb\n", n);
  16 
  17   wypisz_wektor (n, v);
  18   odwroc_kolejnosc( v, n );
  19   printf( "po odwroceniu:\n" );
  20   wypisz_wektor( n, v );
  21 
  22   printf ("wartosc najwieksza to %g\n", maks_element_wektora (v, n));
  23   printf ("wartosc najmniejsza to %g\n", min_element_wektora (v, n));
  24 
  25   return 0;
  26 }

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 #include "bib.h"
   5 
   6 #define MAKS_LICZBA_ARGUMENTOW 100
   7 
   8 int
   9 main (int argc, char *argv[])
  10 {
  11   double v[MAKS_LICZBA_ARGUMENTOW];
  12 
  13   int n;
  14 
  15   if( argc == 1 ) {
  16     printf( "Prosze podac nazwe pliku!\n" );
  17     return 73;
  18   }
  19 
  20   n = wczytaj_liczby_z_pliku( argv[1], v, MAKS_LICZBA_ARGUMENTOW );
  21 
  22   if( n == -MAKS_LICZBA_ARGUMENTOW ) {
  23     printf( "Zbyt dużo liczb w pliku \"%s\" (>%d)\n", argv[1], MAKS_LICZBA_ARGUMENTOW );
  24     return 1;
  25   } else if( n < 0 ) {
  26     printf( "W pliku \"%s\" są jakieś smieci - wczytano %d liczb%c\n", argv[1], -n,
  27             (-n==1?'e':(-n>4?' ':'y')) );
  28     return 2;
  29   } else if( n == 0 ) {
  30     printf( "Nie moge otworzyc pliku \"%s\" do czytania\n", argv[1] );
  31     return 3;
  32   }
  33 
  34   printf ("Mamy %d liczb\n", n);
  35 
  36   printf ("wartosc najwieksza to %g\n", maks_element_wektora (v, n));
  37   printf ("wartosc najmniejsza to %g\n", min_element_wektora (v, n));
  38 
  39   return 0;
  40 }

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 #include "bib.h"
   5 
   6 #define MAKS_LICZBA_ARGUMENTOW 100
   7 
   8 int
   9 main (int argc, char *argv[])
  10 {
  11   double v[MAKS_LICZBA_ARGUMENTOW];
  12 
  13   int n;
  14 
  15   if( argc != 3 ) {
  16     printf( "Prosze podac nazwe plikow: wejsciowego i wynikowego!\n" );
  17     return 73;
  18   }
  19 
  20   n = wczytaj_liczby_z_pliku( argv[1], v, MAKS_LICZBA_ARGUMENTOW );
  21 
  22   if( n == -MAKS_LICZBA_ARGUMENTOW ) {
  23     printf( "Zbyt dużo liczb w pliku \"%s\" (>%d)\n", argv[1], MAKS_LICZBA_ARGUMENTOW );
  24     return 1;
  25   } else if( n < 0 ) {
  26     printf( "W pliku \"%s\" są jakieś smieci - wczytano %d liczb%c\n", argv[1], -n,
  27             (-n==1?'e':(-n>4?' ':'y')) );
  28     return 2;
  29   } else if( n == 0 ) {
  30     printf( "Nie moge otworzyc pliku \"%s\" do czytania\n", argv[1] );
  31     return 3;
  32   }
  33 
  34   odwroc_kolejnosc( v, n );
  35 
  36   if( zapisz_liczby_do_pliku( argv[2], "%g ", v, n ) )
  37     printf( "Nie udalo sie zapisac liczb do pliku \"%s\"\n", argv[2] );
  38 
  39   return 0;
  40 }

Powinni Państwo zapamiętać następujące zagadnienia:

Wykład II: 13 października 2009

Zagadnienia: funkcja - nagłówek, argumenty formalne, ciało, wywołanie funkcji - argumenty aktualne, przekazywanie argumentów przez wartość (kopię), prototypy funkcji, podział programu na kilka plików, opcja -Wall kompilatora.

Rozpoczynamy od przypomnienia ostatniego programu w Wykładu I. Troszkę go zmodyfikujemy, aby wypisywał wszystkie liczby, po jednej w linii:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 int main( int argc, char *argv[] ) {
   5         double v[100000];
   6         int n= 0;
   7 
   8   int i;
   9 
  10   for( i= 1; i < argc; i++ )
  11                 v[n++]= atof( argv[i] );
  12 
  13   printf( "Mamy %d liczb\n", n );
  14 
  15   for( i= 0; i < n; i++ )
  16     printf( "%d\n", v[i] );
  17 
  18   return 0;
  19 }

Przy próbie skompilowania niby wszystko jest ok, ale program działa źle:

$ cc p5.c
$ ./a.out 1 2 3 4 5.5 9.2
Mamy 6 liczb
-637538304
-637538304
-637538304
-637538304
-637538304
-637538304
$

Aby znaleźć ten błąd możemy zachęcić kompilator do większej skrupulatności w raportowaniu błędów:

$ cc -Wall p5.c
p5.c: In function ‘main’:
p5.c:18: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘double’
$

Kompilator wyraźnie wskazuje miejsce wystąpienia błędu (linia 18) i jego rodzaj - format %d nie pasuje do kolejnego argumentu funkcji printf (v[i]), który jest zmienną typu double.

Poprawka jest banalna i dlatego nie umieszczam tu poprawionego programu.

Kolejna modyfikacje będzie polegała na stworzeniu funkcji do wypisywania wektora zawierającego liczny double na standardowe wyjście, Poprawiony program wygląda tak:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 void wypisz_wektor( double w[], int n ) {
   5   int i;
   6   for( i= 0; i < n; i++ )
   7     printf( "%g\n", w[i] );
   8 }
   9 
  10 int main( int argc, char *argv[] ) {
  11   double v[100000];
  12   int n= 0;
  13 
  14   int i;
  15 
  16   for( i= 1; i < argc; i++ )
  17     v[n++]= atof( argv[i] );
  18 
  19   printf( "Mamy %d liczb\n", n );
  20 
  21   wypisz_wektor( v, n ); 
  22    
  23   return 0;
  24 }

Wprowadzając kolejną funkcję otrzymamy program, który wypisuje największą liczbę z wprowadzonych w linii poleceń:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 void wypisz_wektor( double w[], int n ) {
   5   int i;
   6   for( i= 0; i < n; i++ )
   7     printf( "%g\n", w[i] );
   8 }
   9 
  10 double maks_element_wektora( double w[], int n ) {
  11   int i;
  12   double maks= w[0];
  13  
  14   for( i= 1; i < n; i++ ) {
  15     if( w[i] > maks ) {
  16       maks= w[i];
  17     }
  18   }
  19   return maks;
  20 }
  21 
  22 int main( int argc, char *argv[] ) {
  23   double v[100000];
  24   int n= 0;
  25 
  26   int i;
  27 
  28   for( i= 1; i < argc; i++ )
  29     v[n++]= atof( argv[i] );
  30 
  31   printf( "Mamy %d liczb\n", n );
  32 
  33   wypisz_wektor( v, n );
  34 
  35   printf( "wartosc najwieksza to %g\n", maks_element_wektora( v, n ) );
  36 
  37   return 0;
  38 }

Dodajemy kolejną funkcję na wzór poprzedniej:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 void wypisz_wektor( double w[], int n ) {
   5   int i;
   6 
   7   for( i= 0; i < n; i++ )
   8     printf( "%g\n", w[i] );
   9 }
  10 
  11 double maks_element_wektora( double w[], int n ) {
  12   int i;
  13   double maks= w[0];
  14  
  15   for( i= 1; i < n; i++ ) {
  16     if( w[i] > maks ) {
  17       maks= w[i];
  18     }
  19   }
  20   return maks;
  21 }
  22 
  23 double min_element_wektora( double w[], int n ) {
  24   int i;
  25   double min= w[0];
  26  
  27   for( i= 1; i < n; i++ ) {
  28     if( w[i] < min ) {
  29       min= w[i];
  30     }
  31   }
  32   return min;
  33 }
  34 
  35 int main( int argc, char *argv[] ) {
  36   double v[100000];
  37   int n= 0;
  38 
  39   int i;
  40 
  41   for( i= 1; i < argc; i++ )
  42     v[n++]= atof( argv[i] );
  43 
  44   printf( "Mamy %d liczb\n", n );
  45 
  46   wypisz_wektor( v, n );
  47   printf( "wartosc najwieksza to %g\n", maks_element_wektora( v, n ) );
  48   printf( "wartosc najmniejsza to %g\n", min_element_wektora( v, n ) );
  49 
  50   return 0;
  51 }

Na koniec rozbijamy program na 3 osobne pliki:

bib.h - prototypy funkcji

   1 void wypisz_wektor( int, double [] );
   2 double maks_element_wektora( double [], int );
   3 double min_element_wektora( double [], int );

bib.c - definicje funkcji

   1 #include <stdio.h>
   2 
   3 #include "bib.h"
   4 
   5 void wypisz_wektor( int n, double w[] ) {
   6   int i;
   7 
   8   for( i= 0; i < n; i++ )
   9     printf( "%g\n", w[i] );
  10 }
  11 
  12 double maks_element_wektora( double w[], int n ) {
  13   double maks= w[0];
  14  
  15   while( --n ) {
  16     if( w[n] > maks ) {
  17       maks= w[n];
  18     }
  19   }
  20   return maks;
  21 }
  22 
  23 double min_element_wektora( double w[], int n ) {
  24   int i;
  25   double min= w[0];
  26  
  27   for( i= 1; i < n; i++ ) {
  28     if( w[i] < min ) {
  29       min= w[i];
  30     }
  31   }
  32 
  33   return min;
  34 }

p6.c - użycie funkcji

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 #include "bib.h"
   5 
   6 int
   7 main (int argc, char *argv[])
   8 {
   9   double v[100000];
  10   int n = 0;
  11 
  12   int i;
  13 
  14   for (i = 1; i < argc; i++)
  15     v[n++] = atof (argv[i]);
  16 
  17   printf ("Mamy %d liczb\n", n);
  18 
  19   wypisz_wektor (n, v);
  20 
  21   printf ("wartosc najwieksza to %g\n", maks_element_wektora (v, n));
  22   printf ("wartosc najmniejsza to %g\n", min_element_wektora (v, n));
  23 
  24   return 0;
  25 }

}

Wykład I: 6 października 2009

Uwaga: wszystkie poniższe progamiki można przekopiować do plików tekstowych, skompilować te pliki i uruchomić je. O tak:

$ vi p1.c 
$ cc p1.c
$ ./a.out
Hello
$

Pierwszy program to oczywiście (właściwie nie wiem, czemu oczywiście) ,,Hello":

   1 #include <stdio.h>
   2 
   3 int
   4 main ()
   5 {
   6     printf ("Hello\n");
   7     return 0;
   8 }

Zagadnienia: funkcja main, pliki nagłówkowe, funkcja printf i format. Wartość zwracana do systemu.

Druga wersja pokazuje, jak dobrać się do argumentów z linii poleceń:

   1 #include <stdio.h>
   2 
   3 int
   4 main (int argc, char *argv[])
   5 {
   6   if (argc > 1) {
   7     printf ("Hello %s\n", argv[1]);
   8     return 0;
   9   }
  10   else {
  11     printf ("Say your name!\n");
  12     return 1;
  13   }
  14 }

Zagadnienia: (argc,argv), char * to napis, tablice, if-else, wypisywanie napisów (%s)

Kolejny program wypisuje pojedyncze znaki z argumentów linii poleceń. Pokazuje dostęp do elementów tablic i pętlę for - najczęściej stosowaną w programach w C:

   1 #include <stdio.h>
   2 
   3 int 
   4 main( int argc, char *argv[] )
   5 {
   6 
   7         int i;
   8 
   9         for( i = argc-1; i > 0; i-- )
  10                 printf( "%c-%d\n", argv[i][0], argv[i][0] );
  11 
  12         return 0;
  13 
  14 }

Zagadnienia: tablice (także wielowymiarowe), napis to też tablica, znak to mała liczba całkowita, %c służy do wypisywania znaków, %d - liczb dziesiętnych, tablice indeksujemy od 0 do n-1.

Kolejny programik pokazywał, że znaki to naprawdę małe liczby całkowite:

   1 #include <stdio.h>
   2 
   3 int 
   4 main( int argc, char *argv[] )
   5 {
   6 
   7         int i;
   8 
   9         for( i = 65; i < 127; i++ )
  10                 printf( "%c-%x\n", i, i );
  11 
  12         return 0;
  13 
  14 }

I wreszcie ostatni program, który dopiero rozpoczęliśmy. Chcemy zrobić proste narzędzie, które wybierze z liczb podanych jako argumenty linii poleceń liczbę najmniejszą i liczbę największą. Rozpoczęliśmy od zamiany napisów podanych w linii poleceń na liczby:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 int main( int argc, char *argv[] ) {
   5         double v[100000];
   6         int n= 0;
   7 
   8   int i;
   9 
  10   for( i= 1; i < argc; i++ )
  11                 v[n++]= atof( argv[i] );
  12 
  13   printf( "Mamy %d liczb\n", n );
  14 
  15   printf( "v[%d]=%f\n", n/2, v[n/2] );
  16 
  17   return 0;
  18 }

2015-09-23 06:44