Topic outline

  • Topic 1

    Podstawowe typy danych

    Liczby całkowite:
    ciągi cyfr ze znakiem lub bez
    Przykłady:
    0, 1, 2, 10, -100
    Nazwa typu:
    int
    Format:
    %d
    Liczby rzeczywiste:
    liczby z przecinkiem i/lub wykładnikiem
    Przykłady:
    24.0, 2.4e1, 240e-1
    Nazwa typu:
    float
    (lub
    double
    )
    Format:
    %f
    lub
    %e
    Znaki:
    znaki w '' lub ich kody liczbowe
    Przykłady:
    'a', 'b', 'c', 'G'
    '\n', '\\', '\''
    '\107'
    Nazwa typu:
    char
    Format:
    %c
    Napisy:
    ciągi znaków w ""
    Przykłady:
    "napis"
    "długi napis"
    "napis w dwóch\nwierszach"
    Nazwa typu:
    char []
    Format:
    %s

    Komentarze

    Dowolne ciągi znaków zawarte między znakami

    /*
    i
    */
    . Są pomijane przez kompilator. Nie mogą zawierać znaków
    */
    .


    Nazwy (idetyfikatory)

    Nazwy zmiennych (a także funkcji i nowych typów) zwane też identyfikatorami. Dowolne ciągi liter (dużych i małych), cyfr i znaku

    _
    (podkreślenie) rozpoczynające się od litery. Muszą być różne od słow kluczowych (nazwy zastrzeżone).


    Słowa kluczowe

    auto, break, case, char, continue, default, do, double, else, entry, enum, extern, float, for, goto, if, int, long, register, return, short, sizeof, static, struct, switch, typedef, union, unsigned, void, while


    Przykładowe programy

    stale.c
    drukowanie stałych różnych typów
    format.c
    drukowanie stałych przy użyciu różnych formatów
    zmienne.c
    drukowanie wartości zmiennych różnych typów
    czytanie.c
    czytanie wartości zmiennych różnych typów

    Zadania

    1. W programie
      stale.c
      pozamieniać formaty na odpowiadające innym typom niż są drukowane. Co się stanie z liczbą całkowitą drukowaną jako rzeczywista i odwrotnie, z rzeczywistą drukowaną jako całkowita? Czy mozna wydrukować znak zamiast liczby?
    2. Pozmieniać szerokość pola i dokładność w programie
      format.c
      . Co się stanie, gdy podamy dokładność dla liczb całkowitych lub znaków?
    3. Zmodyfikować program
      zmienne.c
      tak, aby zmienne drukowane były przed (i po) nadaniu im wartości. Jakie wartości drukowane są najpierw? Co się stanie po powtórnym uruchomieniu programu?
    4. Jak zareaguje program
      czytanie.c
      gdy zamiast liczb podamy mu znak lub ciąg znaków?

  • Topic 2

    Operatory i wyrażenia

    Operacje arytmetyczne:
    *
    (mnożenie),
    /
    (dzielenie)
    +
    (dodawanie),
    -
    (odejmowanie)
    Typ argumentów:
    char
    ,
    int
    ,
    float
    ,
    double
    Typ wyniku:
    int
    ,
    double

    Reszta z dzielenia liczb całkowitych:
    %
    Typ argumentów:
    int
    Typ wyniku:
    int

    Operacje zwiększnia i zmniejszania o jeden:
    ++
    (zwiększanie),
    --
    (zmniejszanie)
    Typ argumentów:
    char
    ,
    int
    Typ wyniku:
    int

    Relacje równości:
    ==
    (równe),
    !=
    (różne)
    Typ argumentów:
    char
    ,
    int
    ,
    float
    ,
    double
    Typ wyniku:
    int

    Relacje porządku:
    <
    (mniejsze),
    >
    (większe)
    <=
    (mniejsze lub równe),
    >=
    (większe lub równe)
    Typ argumentów:
    char
    ,
    int
    ,
    float
    ,
    double
    Typ wyniku:
    int

    Operacje logiczne:
    &&
    (i)
    ||
    (lub)
    !
    (nie)
    Typ argumentów:
    int
    Typ wyniku:
    int

    Przykładowe programy

    Mathops.c
    operatory matematyczne
    AutoIncrement.c
    operacje zwiększania i zmniejszania o jeden
    Boolean.c
    operatory relacji i logiczne
    Guess.c
    zgadywanie ukrytej liczby (użycie pętli z warunkiem logicznym)

    Zadania

    1. Wykonać program
      Mathops.c
      dla różnych danych wejściowych. Czym różni się dzielenie liczb całkowitych od dzielenia liczb rzeczywistych?
    2. Uruchomić program
      AutoIncrement.c
      . Czy istotne jest, że operator zwiększania lub zmniejszania znajduje się przed bądź za zmnienną? Zmienić wartości zmiennych
      i
      i
      j
      , aby zobaczyć jak wpłynie to na wyniki.
    3. Uruchomić program
      Boolean.c
      . Jakiej wartości numerycznej odpowiada wartość logiczna prawda otrzymywana w wyniku operacji porównania i relacji?
    4. Zgadnąć liczbę ukrytą w programie
      Guess.c
      . Czy można przepisać go tak, aby zmienna
      proba
      nie musiała mieć nadawanej wartości?

    Przekształcenia typów

    Przed wykonaniem jakichkolwiek operacji dokonywane są automatycznie następujące przekształcenia typów zmiennych i stałych:

    1. Wartości typu
      char
      przekształcane są do typu
      int
      (zgodnie z kodem znaku), wartości typu
      float
      przekształcane są do typu
      double
      .
    2. Jeśli jedna z wartości jest typu
      double
      , to i druga jest przekształcana do tego typu. Wynik jest też typu
      double
      .
    3. Jeśli nie zachodzi żaden z powyższych przypadków, to obie wartości są typu
      int
      i taki typ będzie miał też wynik.

    Wartości logiczne

    Wartości logiczne przechowywane są w zmiennych typu

    int
    . Prawda logiczna odpowiada wartości niezerowej, zaś fałsz wartości zero.


    Priorytet operatorów

    Operacje wykonywane są w następującej kolejności:
    1. !
    2. ++ --
    3. * / %
    4. + -
    5. < > <= >=
    6. == !=
    7. &&
    8. ||
    W przypadku wątpliwości należy używać nawiasów
    ()
    .
  • Topic 3

    Instrukcje sterujące

    Instrukcja złożona:
    { I<sub>1</sub>;I<sub>2</sub>; ... I<sub>n</sub>; }
    następna instrukcja będzie wykonywana tylko wtedy, gdy zostanie zakończona instrukcja poprzednia
    block.gif

    Instrukcje warunkowe:
    if (W) I;
    instrukcja
    I
    jest wykonywana tylko wtedy, gdy prawdziwy jest warunek
    W
    if.gif
    if (W) I<sub>1</sub>; else I<sub>2</sub>;
    gdy prawdziwy jest warunek
    W
    , to wykonywana jest instrukcja
    I<sub>1</sub>
    ; w przeciwnym przypadku wykonywana jest instrukcja
    I<sub>2</sub>
    if-else.gif

    Instrukcje pętli:
    while (W) I;
    instrukcja
    I
    jest wykonywana dopóty, dopóki prawdziwy jest warunek
    W
    while.gif
    do I; while (W);
    instrukcja
    I
    jest wykonywana dopóty, aż warunek
    W
    stanie się fałszywy
    do-while.gif

    Instrukcja wyboru:
    switch (W) { case W<sub>1</sub>:I<sub>1</sub>;break; case W<sub>2</sub>:I<sub>2</sub>; ... case W<sub>n</sub>:I<sub>n</sub>;break; default:I; }
    jeśli wyrażenie
    W
    ma wartość
    W<sub>i</sub>
    , to wybiera się i wykonuje instrukcję
    W<sub>i</sub>
    ; w przeciwnym przypadku wykonuje się instrukcję
    I
    switch.gif

    Przykładowe programy

    Guess2.c
    użycie pętli
    do-while
    zamiast
    while
    Charlist.c
    wyświetla wszystkie znaki i ich kody ASCII
    Ifthen.c
    demonstracja instrukcji
    if-else
    Menu2.c
    proste menu korzystające z instrukcji
    switch
    Menu.c
    pokaz instrukcji
    break
    i
    continue

    Zadania

    1. Porównać program
      Guess2.c
      z programem
      Guess.c
      . Jaką korzyść dało zastosowanie instrukcji
      do-while
      zamiast
      while
      ?
    2. Zmienić program
      Charlist.c
      tak, aby zamisat znaków drukujących się w dziwny sposób (np. tabulator, czy tez znak końca wiersza) był wyświetlany komunikat xxx.
    3. W programie
      Menu.c
      zamienić instrukcję
      switch
      przez ciąg instrukcji
      if-else
      .
    4. Zapoznać się z programem
      Ifthen.c
      . Napisać podobny progran, który będzie sprawdzał czy wczytany znak jest małą literą, dużą literą bądż cyfrą i drukował stosowny komunikat.
    5. Czy można zmienić program
      Menu.c
      tak, aby zamiast instrukcji
      brak
      i
      continue
      korzystał jedynie ze zmiennych logicznych i dodatkowych instrukcji warunkowych?

  • Topic 4

    Zadania do zaprogramowania

    1. Następujący program oblicza iloczyn dwóch liczb naturalnych używając tylko działań dodawania i odejmowania. Argumentami są
      x
      i
      y
      . Wynik
      x*y
      umieszczany jest w zmiennej
      z
      . Program wykorzystuje zmienna pomocniczą
      u
      . Obejrzeć schemat blokowy i zapisać go w języku C przy użyciu pętli
      do-while
      . Sprawdzić działanie programu dla kilku par liczb.

      mnozenie.gif
      [ szkielet programu:
      mnozenie.c
      ]

    2. Powtórzyć poprzednie zadanie dla programu dzielącego liczby naturalne przy użyciu dodawania i odejmowania. Argumentami są liczby naturalne
      x
      i
      y
      wynikami zaś
      q
      (iloraz
      x/y
      ) oraz
      r
      (reszta
      x%y
      ). Użyć pętli
      while
      .

      dzielenie.gif
      [ szkielet programu:
      dzielenie.c
      ]

    3. Kolejny przykładowy program oblicza największy wspólny dzielnik dwóch liczb naturalnych
      x
      i
      y
      przy pomocy algorytmu Euklidesa. Końcowe wartości zmiennych całkowitych
      a
      i
      b
      zawierają wynik. Użyć pętli
      while
      i instrukcji
      if-else
      .

      euklides.gif
      [ szkielet programu:
      euklides.c
      ]

    4. Zapoznać się z programem przepisującym powtórnie na ekran wpisany wiersz tekstu. Dodać zmienną
      licznik
      zliczającą wprowadzone znaki. Dopisać drukowanie na ekranie stosownego komunikatu.

      [ szkielet programu:
      szkielet.c
      ]

    5. Zmodyfikować powyższy program tak, aby wiersz wejściowy drukowany był w postaci następującego trójkąta:
       
      w
      ii
      eee
      rrrr
      sssss
      zzzzzz
      
      wwwwwwww
      eeeeeeeee
      jjjjjjjjjj
      śśśśśśśśśśś
      cccccccccccc
      iiiiiiiiiiiii
      oooooooooooooo
      wwwwwwwwwwwwwww
      yyyyyyyyyyyyyyyy
      

    6. Dodać liczniki zliczające małe litery, duże litery oraz cyfry.

    7. Korzystając z instrukcji
      if-else
      sprawić, aby drukowany na ekranie tekst wejściowy był zakodowany szyfrem Cezara. Litera
      A
      ma być zastąpiona literą
      B
      , litera
      B
      literą
      C
      , litera
      C
      literą
      D
      , i tak dalej aż do litery
      Z
      , która ma być zastąpiona przez
      A
      .

    8. Zapoznać się z programem czytającym wpisany wiersz tekstu do tablicy znaków i drukującym go powtórnie na ekran:

      [ szkielet programu:
      szkielet2.c
      ]

      Zmodyfikować go tak, aby niewykorzystane miejsca w tablicy o numerach od
      dlugosc
      do
      MAKSDLUGOSC
      były drukowane jako kropki. Na przykład dla tablicy mieszczącej 15 znaków i tekstu
      tekst
      powinniśmy dostać następujący wydruk:
       
      tekst..........
      

    9. Dodać do programu pętle powtarzającą operacje wpisywania i drukowania aż do napotkania na wejściu pustego wiersza tekstu.

    10. Zmodyfikować powyższy program tak, aby wiersz wejściowy drukowany był w postaci następującego trójkąta:
       
      w..............
      wi.............
      wie............
      wier...........
      wiers..........
      wiersz.........
      wiersz ........
      wiersz w.......
      wiersz we......
      wiersz wej.....
      wiersz wejśc....
      wiersz wejści...
      wiersz wejścio..
      wiersz wejściow.
      wiersz wejściowy
      


    Wiadomości uzupełniające

    • Instrukcja
       for(numer = 0; numer < dlugosc; numer ++) printf("%c", wiersz[numer]);
      jest równoważna instrukcji
       numer = 0; while(numer < dlugosc) { printf("%c", wiersz[numer]); numer ++; }

    • Tablica zadeklarowana jako
       char wiersz[MAKSDLUGOSC];
      zawiera
      MAKSDLUGOSC
      znaków o nazwach:
      wiersz[0]
      ,
      wiersz[1]
      ,
      wiersz[2]
      , ...,
      wiersz[MAKSDLUGOSC-1]
      .

    • Instrukcja
       #define MAKSDLUGOSC 72
      powoduje, że przed kompilacją wszystkie wystąpienia indentyfikatora
      MAKSDLUGOSC
      zostaną zastąpione przez
      72
      . Ten wygodny sposób nazywania stałych stosowany jest, gdy występują one w więcej niż jednym miejscu kodu.


  • Topic 5

    Funkcje i struktura programu

    1. Oprócz funkcji
      main
      w programie w języku C mogą być zdefiniowane również inne funkcje. Oto przykładowa definicja funkcji
      translate
      :
      int translate(float x, float y, float z) { x = y = z; /* ... */ } 

    2. Jeśli funkcja jest używana przed swoją definicją, to użycie musi być poprzedzone podaniem prototypu, który określi typy argumentów i typ wyniku wykonania funkcji. Prototyp przypomina nagłówek definicji:
      int translate(float x, float y, float z);
      Nazwy argumentów można opuścić:
      int translate(float, float, float);

    3. Jeśli funcja nie zwraca żadnej wartości, to jako typ jej wyniku należy podać
      void
      . Podobnie określa się pustą listę argumentów.

    4. Przykłady prototypów funkcji:
      int f1(void); /* Zwraca liczbę całkowitą, nie ma argumentów */
      float f3(float, int, char, double); /* Zwraca liczbę rzeczywistą */
      void f4(void); /* Nie ma argumentów, nic nie zwraca */
      

    Przykładowe programy

    1. Argumenty funkcji oraz zadeklarowane w niej zmienne lokalne mogą być używane tylko wewnątrz tej funkcji. Oprócz tego na zewnątrz funkcji można deklarować zmienne globalne dostępne we wszystkich funkcjach. Ilustruje to następujący przykładowy program
      YourPets1.c
      :
      [ przykładowy kod:
      YourPets1.c
      ]

    2. Zazwyczaj argumenty funkcji przekazywane są przez wartość, to znaczy ich zmiana w funkcji wywoływanej (np.
      f
      ) nie wpływa na zmienne w funkcji wywołującej (np.
      main
      ):
      [ przykładowy kod:
      PassByValue.c
      ]

    3. Aby funkcja wywoływana (np.
      f
      ) mogła zmieniać wartości zmiennych w funkcji wywołującej (np.
      main
      ) należy je przekazać przez zmienną. W języku C odbywa się to przez przekazanie funkcji wywoływanej adresów zmiennych, które mają ulec zmianie:
      [ przykładowy kod:
      PassAddress.c
      ]
      Do pobrania adresu zmiennej służy tu operator
      &
      , natomiast dzięki operatorowi
      *
      uzyskujemy dostęp do wartości zmiennej reprezentowanej przez adres.

    4. Do określenia wartości zwracanej przez funkcję służy instrukcja
      return
      , która jednocześnie kończy wykonanie funkcji:
      [ przykładowy kod:
      Return.c
      ]

    5. Funkcja może również wywoływać sama siebie z innymi argumentami. Zwykle specjalna instrukcja
      if-else
      pozwala przerwać nieskończony ciąg rekurencyjnych wywołań:
      [ przykładowy kod:
      CatsInHats.c
      ]

    Zadania

    1. Uruchomić przykładowy program
      YourPets2.c
      . Czy widać jakąś różnicę między adresami zmiennych lokalnych, globalnych oraz funkcji?

    2. Napisać i przetestować funkcję
      void zamien (int *x, int *y);
      zamieniającą wartości dwóch zmiennych całkowitych. Wskazówka: posłużyć się przekazywaniem przez zmienną.

    3. Zmienić funkcję
      cfunc
      w programie
      Return.c
      tak, aby korzystała tylko z jednej instrukcji return (kosztem wprowadzenia zmiennej pomocniczej i instrukcji
      else
      ).

    4. Napisać rekurencyjną funkcję
      int silnia (int n);
      do liczenia funkcji silnia
      n!
      według wzoru:
      silnia(0) = 1 silnia(n ) = n * silnia(n-1) 
    5. Znaleźć rozwiązanie problemu Wież z Hanoi. Ta starożytna łamigłówka pochodzi z pewnego klasztoru w Tybecie:
      Przypuśćmy, że mamy trzy wieże lub, skromniej, trzy kołki, A, B i C. Na pierwszym kołku, A, znajdują się trzy krążki nanizane w porządku malejących wielkości, podczas gdy pozostałe kołki są puste. Chcemy przenieść krążki z kołka A na B, być może używając do tego kołka C. Według reguł gry krążki można przenosić po jednym na raz i w żadnej chwili krążek większy nie może być umieszczony na wierzchu mniejszego.
      Wskazówka: Przedstawiony tu algorytm radzi sobie z przeniesieniem N krążków z kołka A przec C na kołek B następująco. Najpierw sprawdza, czy N=1, w którym to przypadku po prostu przenosi na miejsce przeznaczenia ten jedyny krążek, z którym miał się uporać (lub, dokładniej, podaje opis jednego ruchu, który to zrobi) i natychmiast powraca. Jeśli N>1, algorytm najpierw przenosi górne N-1 krążków z kołka A na kołek "pomocniczy" C, używając rekurencyjnie tego samego podprogramu; potem bierze jedyny krążek pozostawiony na kołku A (musi to być krążek najwiekszy - dlaczego?) i przenosi go na ostateczne miejsce przeznaczenia, na kołek B; potem, znowu rekurencyjnie, przenosi N-1 krążków, które poprzednio "przechował" na kołku C, na ich ostateczne miejsce, na kołek B. Napisać rekurencyjną funkcję
      void hanoi (int N, char A, char B, char C);
      realizującą ten algorytm i wydrukować wynik dla trzech krążków.

  • Topic 6

    Tablice (przypomnienie)

    1. Tablica zadeklarowana jako
       int a[10];
      
      zawiera
      10
      liczb całkowitych o nazwach:
      a[0]
      ,
      a[1]
      ,
      a[2]
      , ...,
      a[9]
      .

       

    2. Instrukcja
       for(i = 0; i < 10; i++) {
       printf("a[%d] = %d\n", i, a[i]);
       }
      
      jest równoważna instrukcji
       i = 0;
       while(i < 10) {
       printf("a[%d] = %d\n", i, a[i]);
       i ++;
       }
      
      Oznacza to, że instrukcja wykonywana jest
      10
      razy dla wartości
      i
      od
      0
      do
      9
      (a więc wypisuje wartości wszystkich elementów tablicy
      a
      ).

       


    Zadania

    1. Skompilować i uruchomić przykładowy program
      Tablice.c
      :
      [przykładowy wydruk]
      [kod źródłowy]
      Dodać deklarację nowej zmiennej:
      int x;
      
      i zmienić powyższy program tak, aby po wczytaniu wartości zmiennej
      x
      :
      scanf("%d", &x);
      
      tablica
      a
      zawierała następujące liczby:
      a [0] = x % 10;
      a [1] = (x / 10) % 10;
      a [2] = ((x / 10) / 10) % 10;
      a [3] = (((x / 10) / 10) / 10) % 10;
      ...
      a [9] = ...;
      
      Co to są za liczby i jaki wynik da zmiana w powyższych relacjach liczby
      10
      na np.
      2
      ?

       


    Struktury

    1. Struktura jest obiektem złożonym z jednej lub kilku zmiennych, być może różnych typów.

       

    2. Struktury umożliwiają organizowanie skomplikowanych danych pod wspólną nazwą.

       

    3. Data składa się z dnia, miesiąca oraz roku. Te trzy zmienne można umieścić w jednej strukturze, na przykład takiej, jak ta:
      struct data
      {
       int dzien;
       int miesiac;
       int rok
      };
      

       

    4. Deklarację struktury rozpoczyna słowo kluczowe
      struct
      , po którym może występuje nazwa, zwana też etykietką struktury (u nas ). Zmienne występujące w strukturze będziemy nazywali składowymi struktury.

       

    5. Mając już deklarację struktury
      data
      , definiujemy zmienną
      d
      jako strukturę typu
      data
      :
      struct data d;
      

       

    6. W wyrażeniach dostęp do określonej składowej struktury umożliwia konstrukcja postaci:
      nazwa-struktury.składowa
      
      na przykład:
      d.dzien = 4;
      d.miesiac = 7;
      d.rok = 1776;
      

       

    7. Deklaracja
      struct date *pd;
      
      mówi, że
      pd
      jest wskaźnikiem do struktury typu . Ponieważ
      pd
      jest adresem struktury, to
      (*pd).nazwa-składowej
      
      odwołuje się do konkretnej składowej. Wskaźniki do struktur są tak często używane, że dla wygody wprowadzono następującą nową notację:
      pd->nazwa-składowej
      

    Przykładowe programy

    1. Następujący przykładowy program
      ProstaStruktura.c
      ilustruje deklarację i użycie prostej struktury:
      [przykładowy wydruk]

       

    2. Definicje konkretnych "wcieleń" struktury można uprościć, jeśli przy jej deklarację użyjemy deklaracji nowego typu
      typedef
      :
      [przykładowy wydruk]
      Pozwala to na opuszczanie słowa kluczowego
      struct
      . Upodabnia to deklarowanie zmiennych przechowujących struktury do deklarowania zmiennych typów prostych, takich jak
      int
      czy też
      char
      .

       

    3. Następujący przykładowy program
      ProstaStruktura3.c
      ilustruje dostęp do składowych struktury poprzez wskaźnik do tej struktury:
      [przykładowy wydruk]
      Metoda ta jest często stosowana przy przekazywaniu struktur poprzez wskaźnik do funkcji.

       

    4. Ostatni przykładowy program
      TablicaStruktur.c
      przedstawia tablicę, której elementami są struktury:
      [przykładowy wydruk]

       


  • Topic 7

    Zadania do zaprogramowania II

    1. Program szkielet2.c czytał wpisany wiersz tekstu do tablicy znaków i drukował go powtórnie na ekran. Dodaliśmy do tego programu pętle powtarzającą operacje wpisywania i drukowania aż do napotkania na wejściu pustego wiersza tekstu. Korzystając z wyników tamtego ćwiczenia zdefiniować trzy funkcje:
      void CzytajNapis (napis *wn)
      czyta wpisany wiersz tekstu do struktury
      napis


      void DrukujNapis (napis n)
      drukuje wiersz tekstu ze struktury
      napis


      int PustyNapis (napis n)
      sprawdza, czy wczytany wiersz tekstu jest pusty
      i przepisać program szkielet2.c przy użyciu następującego wzorca:
      [ przykładowy wydruk ]
      [ kod źródłowy ]

       

    2. Wyobraźmy sobie funkcję:
      int Znaleziony (char z, napis n)
      [ przykładowy wydruk ]
      [ kod źródłowy ]
      badającą, czy znak
      z
      znajduje się w napisie
      n
      (poprzez przeglądanie wszystkich znaków napisu aż do znalezienia szukanego znaku). Opierając się na tym przykładzie zdefiniować funkcję:
      int IdentyczneNapisy (napis n1, napis n2)
      sprawdzającą, czy dwa napisy
      n1
      n2
      są sobie równe (tzn. funkcja zwraca wartość
      1
      , jeśli napisy są równe i wartość
      0
      , jeśli nie są). Wskazówka: zadanie to jest równoważne jednoczesnemu przeglądaniu obu napisów aż do znalezienia pierwszej pary elementów różnych.

       

    3. Używając definicji funkcji:
      int IdentyczneNapisy (napis n1, napis n2)
      void CzytajNapis (napis *wn)
      void DrukujNapis (napis n)
      z zadań 1 i 2 napisać wersję programu
      Guess.c
      działającą na napisach zamiast liczb (zgadywanie "ukrytego" napisu).

       

    4. Zmodyfikujmy strukturę
      napis
      dodając do niej niej nowe pole
      licznik
      , które przechowywać będzie liczbę wystąpień każdej z liter alfabetu (od 'a' do 'z') w tablicy
      znak
      :
      typedef struct{
       char znak[MAKSDLUGOSC];
       int dlugosc;
       int licznik[26];
      } napis;
      
      Literze 'a' odpowiada tu zmienna
      licznik[0]
      , literze 'b' odpowiada
      licznik[1]
      , i tak dalej, aż do 'z', której odpowiada
      licznik[25]
      . Tak więc, jeśli wiemy, że
      znak
      jest małą literą, to odpowiada mu zmienna
      licznik[znak-'a']
      . Zmienić funkcję
      void CzytajNapis (napis *wn)
      tak, aby oprócz wczytywania znaków napisu nadawała też wartości elementom tablicy
      licznik
      . Wskazówka: wykorzystać standardowe funkcje:
      int islower(char c)
      int isupper(char c)
      sprawdzające, czy znak
      c
      jest odpowiednio małą i dużą literą, oraz funkcję:
      char tolower (char c)
      przekształcającą duże litery na małe. Funkcje te zdefiniowane są w pliku
      "ctype.h"
      (należy więc dodać do programu stosowną dyrektywę
      #include<ctype.h>
      ).

       

    5. Napisać funkcję:
      int JestAnagramem (napis n1, napis n2)
      sprawdzającą czy napis
      n1
      jest anagramem napisu
      n2
      (to znaczy, czy rożni się od niego jedynie przestawieniem liter). Przy pomocy tej funkcji przerobić wersję programu
      Guess.c
      z zadania 3 tak, aby kończyła pracę, gdy wpisany napis okaże się anagramem "ukrytego" napisu (a nie gdy jest mu równy jak w zadaniu 3). Wskazówka: napisy będące anagramami mają identyczne pola
      licznik
      .

       

    6. Wyobraźmy sobie tablicę obiektów typu
      napis
      :
      napis wyraz [100];
      i zmienną
      int liczba;
      określającą liczbę elementów w tej tablicy. Przerobić program szkielet3.c z zadania 1 tak, aby wczytywał wpisywane wyrazy do tablicy
      wyraz
      oraz nadawał zmiennej
      liczba
      wartość równą liczbie wpisanych wyrazów. Wskazówka: struktura pętli wczytującej wyrazy jest bardzo podobna do struktury pętli z funkcji
      CzytajNapis
      (ale zamiast znaków wczytujemy teraz całe napisy).

       

    7. Opierając się na wynikach dwóch poprzednich zadań napisać program, który wyszuka i wydrukuje wszystkie pary anagramów z wpisanego ciągu wyrazów. Wskazówka: do przeglądania wszystkich par wyrazów można użyć następującej podwójnej pętli:
      for (i = 0; i < liczba - 1; i ++)
       for (j = i + 1; j < liczba; j ++)
      

       

    8. Napisać funkcję:
      int PolaczNapisy (napis *wn, napis n1, napis n2)
      łączącą dwa napisy
      n1
      i
      n2
      oraz umieszczającą wynik w napisie wskazywanym przez adres
      wn
      (proszę pamiętać o polu
      licznik
      ). Użyć jej do napisania programu, który sprawdzi, czy połączenie jakichś dwóch nazw państw jest anagramem nazwy trzeciego państwa. Wskazówka: lista państw znajduje się w pliku panstwa.txt. Zamiast przepisywania ich można wykorzystać tak zwane przekierunkowywanie standardowego wejścia. W tym celu należy z linii komend uruchomić napisany (i skompilowany) program poleceniem:
      program.exe < panstwa.txt
      (z punktu widzenia programu działa to tak samo jak przepisanie zawartości pliku panstwa.txt z klawiatury).