1. Podstawy informatyki, DOS-u, UNIX-a
1.1. Pojecia - jednostki informacji: bit, bajt/znak, rekord, plik
1.2. Plik (file): nazwa, podstawowe operacje na pliku jako calosci
1.3. Skorowidz (directory): moze zawierac pliki i skorowidze, ...
1.4. Pare elementow Novell-a (map, salvage, purge)

2. Edytowanie programu, uzywanie kompilatora
2.1. Jak wystartowac zintegrowane srodowisko TC lub BC
2.2. Najwazniejsze polecenia edytora
2.3. Opcje zintegrowanego srodowiska TC
2.4. Mamy napisany program i co dalej?
2.5. Najprostszy program w C
2.6. Nastepny program w C
2.7. Program przedstawia sie
2.8. Parametry programu
2.9. Zmienne srodowiska DOS

3. Dyrektywy preprocesora
3.1. #include
3.2. #define
3.2.1. bez argumentow lub z argumentami
3.2.2. dlaczego potrzeba uzywac nawiasow
3.2.3. operator # - definicja show(x)
3.2.4. operator ## - laczenie nazw
3.3. #undef
3.4. kompilacja warunkowa: #ifdef, #ifndef, #if, #else, #elif, #endif
3.5. #error
3.6. #line
3.7. #pragma

1.1. Pojecia - jednostki informacji: bit, bajt/znak, rekord, plik
    bit - najmniejsza jednostka informacji, odpowiedz tak/nie
    bajt (lub znak) - jednostka informacji, ktora moze zawierac znak -
      litere, cyfre, lub inny; zwykle bajt = 8 bitow - to pozwala miec
      256 znakow w "alfabecie"
    rekord - pojedynczy zapis, ktory cos oznacza; w systemach DOS i UNIX
      mozna traktowac bajt jako rekord, ale np. w pliku tekstowym zwykle
      traktuje sie linie jako rekord; system VMS ma rekordy "wbudowane"
      w system, i nie pozwala czytac informacji po bajcie
    plik - zbior rekordow, ustawionych w okreslonej kolejnosci, ktore
      tworza pewna calosc, z mozliwoscia operacji na nim jako calosci.


1.2. Plik (file): nazwa, podstawowe operacje na pliku jako calosci
    nazwa w DOS-ie (i paru innych starszych systemach, np. RSX-11) sklada
    sie z dwoch czesci oddzielonych kropka, pierwsza moze miec 8 znakow,
    druga 3 (wewnetrznie DOS pamieta 11 znakow - kropki nie), te druga
    czesc nazywa sie rozszerzeniem (extension) albo typem pliku, zwykle
    odpowiada ona jego zawartosci - np. program napisany w C ma typ .C,
    a skompilowany i gotowy do wykonania .EXE; w UNIX-ie kropka z nazwie
    pliku jest znakiem niemal rownie dobrym jak kazdy inny, jedynie jej
    uzycie na poczatku nazwy ma specjalne znaczenie, takie pliki nie sa
    normalnie widoczne, i nazwa moze byc duzo dluzsza, niemniej jednak
    tez zwykle nazwy plikow konczy sie nazwa typu; operacje na plikach:

    tworzenie       zalezy od typu pliku
    kopiowanie      copy (DOS) cp (UNIX)  nazwa nazwa-wynikowa
    usuwanie        del/delete/erase (DOS) rm (UNIX)  nazwa
    zmiana nazwy    ren/rename (DOS) mv (UNIX)  nazwa nowa-nazwa
    przenoszenie    move (DOS) mv (UNIX)  nazwa skorowidz-docelowy

    uwagi: ? i * uzyte w nazwie oznaczaja: ? - dowolny znak, * - dowolny
    ciag znakow, w DOS-ie * dziala tak, jak ciag ? do kropki, lub konca
    nazwy, a do ? moze pasowac spacja (do A?.TXT pasuja A1.TXT, A.TXT),
    w UNIX-ie do wszystkich znakow podanej nazwy cos musi byc dopasowane
    (do * pasuje rowniez 0 znakow, ale do ? nie; w DOS-ie np. do A*1.TXT
    pasuje A2.TXT, w UNIX-ie nie - 1.TXT musi byc na koncu znalezionej
    nazwy); DOS zezwala przy kopiowaniu i zmianie nazwy na uzycie znakow
    ? i * w nowej nazwie - oznacza to podstawienie w dane miejsce nazwy
    fragmentu oryginalnej nazwy, np. COPY A*.TX? B*.LS? skopiuje pliki,
    ktorych nazwy zaczynaja sie na A, a rozszerzenia na TX, i kopie beda
    mialy nazwy zaczynajace sie na B, z rozszerzeniem na LS, reszta nazw
    bez zmian; w UNIX-ie tego nie ma, za to mozna podawac wiele nazw tam,
    gdzie ma to jakis sens (kopiowanie i przenoszenie wielu plikow do
    skorowidzu, usuwanie wielu plikow), i ? i * moga byc w nazwie sciezki
    (pojecia skorowidz i sciezka sa w 1.3), a w DOS-ie nie.


1.3. Skorowidz (directory): moze zawierac pliki i skorowidze, mozna to
    traktowac jako drzewo, ktore ma jakis pien - to jest glowny skorowidz
    (root directory), z niego wyrastaja konary (inne skorowidze), z nich
    dalsze galezie (skorowidze zawarte w tych odpowiadajacych konarom),
    i wszedzie (rowniez na pniu) moga rosnac liscie (to sa pliki).

    Adresowanie na tym drzewie: pelny adres pliku podaje ktore drzewo
    (w DOS-ie - w UNIX-ie jest tylko jedno), i jak po kolei wybierac
    rozgalezienia (kazde ma nazwe), zeby dojsc na galaz z lisciem, i na
    koniec nazwa liscia. W DOSi-e nazwy sa oddzielone '\', w UNIX-ie '/',
    wyglada to (w DOS-ie) tak: H:\PROGRAMY\C\PROG1.C - tutaj drzewem jest
    dysk H:, pierwszym konarem PROGRAMY, na nim rosnie galaz C, i na niej
    lisc PROG1.C. Skorowidz aktualny (w DOS-ie jest okreslony oddzielnie
    dla kazdego dysku, w UNIX-ie jest jeden), jesli po nazwie dysku (w
    DOS-ie, tu H:), lub na poczatku adresu (w UNIX-ie, lub w DOS-ie jesli
    nie podaje sie nazwy dysku - oznacza to przyjecie dysku aktualnego)
    nie ma znaku oddzielajacego, to adres jest wzgledny - liczy sie od
    skorowidzu aktualnego (w DOS-ie dla podanego, lub aktualnego dysku).
    Czesc adresu miedzy nazwa dysku i pliku nazywa sie sciezka (path).

    Adres moze zawierac elementy specjalne - kropke lub dwie kropki;
    kropka oznacza ten sam skorowidz, dwie kropki oznaczaja cofniecie sie
    na drzewie o jedno rozgalezienie - jesli aktualnym dyskiem jest H:,
    i aktualnym skorowidzem na nim H:\PROGRAMY\BASIC, to nazwe pliku,
    ktory byl w poprzednim przykladzie, mozna podac jako ..\C\PROG1.C.
    Te dwie nazwy - . i .. - sa nazwami skorowidzow, ktore sa na poczatku
    kazdego skorowidzu (z wyjatkiem glownego, ale nawet tam . dziala).
    W Novell-u (taki system, ktory jest serwerem plikow dla DOS-u - udaje
    dyski, choc komputery ich nie maja) mozna podac wiecej kropek naraz,
    i oznacza to cofniecie sie o wiecej rozgalezien (np. ... - o 2).

    Operacje na skorowidzach:
    tworzenie         md (DOS) mkdir (DOS, UNIX)  nazwa (w UNIX-ie nazwy)
    usuwanie          rd (DOS) rmdir (DOS, UNIX)  nazwa (w UNIX-ie nazwy)
    wybor aktualnego  cd (DOS, UNIX) chdir (DOS)  nazwa
    jaki aktualny?    cd (DOS - bez parametru) pwd (UNIX)
                      (w UNIX-ie cd bez parametru wybiera skorowidz HOME)
    zmiana nazwy      move (DOS) mv (UNIX)  stara-nazwa nowa-nazwa
    przenoszenie      moze dzialac tak samo jak zmiana nazwy
    co zawiera?       dir (DOS) ls (UNIX)  nazwa  (nazwe mozna pominac)


1.4. pare elementow Novell-a (login, logout, map, salvage, purge)
    LOGIN (mozna podac jako parametr nazwe uzytkownika) - sluzy do
      poinformowania serwera kto jest uzytkownikiem, i zwykle wymaga
      podania hasla (password) uzytkownika; tego hasla nalezy uzywac
      wylacznie samemu, i nikomu go nie podawac; jesli podejrzewa sie,
      ze ktos mogl je podpatrzec, nalezy je zmienic poleceniem SETPASS;
      powoduje, ze serwer uznaje prawa uzytkownika do dostepu do jego
      (i innych udostepnionych mu) plikow
    LOGOUT - informuje serwer, ze uzytkownik zakonczyl prace - konczy
      dzialanie uprawnien uzytkowika uzyskane poleceniem LOGIN
    MAP - sluzy do okreslania jak maja byc udostepnione dyski serwera,
      samo "MAP" wypisze informacje o aktualnych definicjach wszystkich
      "dyskow", "MAP X:" wypisze definicje dysku X:, map "X:=definicja"
      zdefiniuje dysk X:; definicja sklada sie z: nazwy serwera (OKWF1,
      jesli podaje sie te nazwe, to po niej / lub \), nazwy dysku serwera
      (SYS, USR, HOME, HOME1, SWAP na OKWF1, po tej nazwie ma byc :),
      i sciezki (nazw skorowidzow oddzielonych / lub \; nie trzeba uzywac
      / ani \ na poczatku sciezki - dysk serwera jest zawsze na "root");
      dodatkowo mozna po MAP napisac ROOT - wtedy podany skorowidz bedzie
      widoczny jako "root" zdefiniowanego dysku, bez tego "root" dysku X:
      bedzie taki sam, jak dysku serwera, a jedynie zostanie ustawiony
      aktualny skorowidz na X:, MAP ROOT J:=HOME:STAFF/SYSTEM/EXAMPLES
      zdefiniuje dysk J: tak, by bylo na nim widac materialy do cwiczen;
    SALVAGE - sluzy do odzyskiwania skasowanych plikow
    PURGE - ostatecznie usuwa skasowane pliki; jesli sie tego nie zrobi,
      to Novell za jakis czas sam je usunie, gdy zabraknie miejsca.
    Prawa dostepu: uzytkownik ma prawo do wszystkich operacji na swoim
      skorowidzu (home directory), widocznym na dysku H:, i na swojej
      skrzynce pocztowej, widocznej na F:\MAIL\numer_uzytkownika, i na
      dysku C: (przewidzianego na obszar roboczy - nie nalezy go uzywac
      do przechowywania plikow); moze uzywac programow zainstalowanych
      na serwerze (zwykle moze tez je czytac - jakies programy moga byc
      zabezpieczone przed czytaniem), oraz moze zapisac plik do czyjejs
      skrzynki pocztowej (o ile zna jej nazwe - nie moze jej zobaczyc);
      ograniczeniem jest przydzial miejsca, i nalezy zachowac szczegolna
      ostroznosc przy pisaniu do czyjejs skrzynki pocztowej - zapisany
      tam plik uzywa miejsca przydzielonego temu, kto go zapisal, dopoki
      nie zostanie skasowany, a prawo do skasowania ma wlasciciel tej
      skrzynki, a nie ten, co go zapisal - jest to sposob na przeslanie
      komus pliku, ale pod warunkiem, ze mozna temu komus zaufac.



2.1. Jak wystartowac zintegrowane srodowisko TC lub BC
    (dotyczy tylko pracy na komputerach OKWF) po zalogowaniu sie (jesli
    nie mamy konta to mozna uzyc nazwy fizykxx gdzie za xx podstawia sie
    numer napisany na komputerze) nalezy najpierw napisac G:\P, a potem
    - dla TC:  INIT_TC i potem TC
    - dla BC:  INIT_BC3 i potem BC
    z tego G:\P i INIT_cos musza byc wpisane bez Norton Commandera


2.2. Najwazniejsze polecenia edytora

    Wpisywanie tekstu dziala w ten sposob, ze nowo wpisany znak pojawi
    sie tam, gdzie jest kursor; znak, ktory byl w tym miejscu, moze byc
    usuniety, lub przesuniety razem ze wszystkimi dalszymi w tej linii,
    to drugie nastapi przy wlaczonym trybie Insert (wstaw), i zwykle
    pracuje sie w tym trybie; do wlaczania i wylaczania trybu Insert
    sluzy klawisz Insert, tryb Insert jest sygnalizowany napisem nieco
    na lewo od srodka linii z informacjami (na gorze). Inne informacje,
    na ktore warto zwrocic uwage, to: numer linii i kolumny (z lewej),
    Indent i Tab (mozna je wlaczac i wylaczac przez Ctrl-O I, Ctrl-O T,
    Indent okresla zachowanie przy przejsciu do nowej linii przez Enter,
    Tab przy wcisnieciu klawisza Tab) na srodku, w prawo od Tab: Fill
    (Ctrl-O F, powoduje zastepowanie spacji tabulatorami jesli to mozliwe
    - skraca program), Unindent (Ctrl-O U, steruje dzialaniem Backspace -
    tak, ze moze kasowac kilka spacji, dopasowujac sie do linii powyzej),
    i w zasadzie mozna to wszystko miec wlaczone; dalej w prawo gwiazdka
    pokazuje, ze plik zostal zmieniony i nie zapisany na dysku (zapisuje
    sie przez F2), i na koniec nazwa pliku (z nazwa dysku - warto zwrocic
    uwage, czy jest to dysk na ktorym mamy prawo pisac, bo inaczej beda
    klopoty przy probie zapisania i nie tylko).

    Po tekscie mozna poruszac sie strzalkami - ale to czasem moze byc za
    wolno. Klawisze PageUp i PageDown sluza do przewijania tekstu o caly
    ekran, a Home i End do ustawienia kursora na poczatek/koniec linii.
    Przy wcisnietym Ctrl strzalki <- i -> przesuwaja kursor o cale slowo,
    Home i End w pionie zamiast w poziomie (gora/dol ekranu), a PageUp
    i PageDown na poczatek/koniec tekstu. Wiekszosc liter razem z Ctrl
    cos robi - W i Z przesuwaja tekst na ekranie o jedna linie, ale bez
    przesuwania kursora wzgledem tekstu, R i C dzialaja tak, jak PageUp
    i PageDown - przesuwaja o strone, nie zmieniajac polozenia kursora
    na ekranie (chyba, ze dojdzie sie do poczatku lub konca tekstu),
    S,D,E,X dzialaja jak strzalki, A i F jak strzalki z Ctrl, Q zaczyna
    sekwencje sterujace, np. Ctrl-Q S = Home, Ctrl-Q F - szukanie napisu
    w tekscie, Ctrl-Q A - szukanie i zamiana, to szukanie pyta o opcje:
    w-tylko cale slowa, u-nie rozroznia malych/duzych liter, b-do tylu,
    n-bez pytania, g-ile razy sie da (mozna podac liczbe), powtorzenie
    szukania (lub szukania z zamiana) uzywkuje sie przez Ctrl-L.

    Kasowanie - sa dwa kasowania pojedynczego znaku: tego, pod ktorym
    jest kursor (Delete lub Ctrl-G), i tego przed kursorem (Backspace
    lub Ctrl-H). Wiecej skasuje Ctrl-T (do konca slowa, i ewentualnie
    spacje do poczatku nastepnego), Ctrl-Q Y (do konca linii), Ctrl-Y
    (cala linie - wtedy nie dziala odzyskiwanie linii przez Ctrl-Q L).

    Operacje z blokiem - wszystkie polecenia zaczynaja sie od Ctrl-K:
    B i K - zaznaczanie poczatku i konca bloku (BloK), C - kopiowanie,
    V - przeniesienie, Y - kasowanie, H - ukrycie (hide - powtorne
    pokaze go znowu), R - przeczytanie z pliku, W - zapisanie na plik;
    1/2/3 - zaznacza miejsce - potem mozna uzyc np. Ctrl-Q 1 do powrotu
    do miejsca zaznaczonego przez Ctrl-K 1, mozna tez Ctrl-Q B lub K...


2.3. Opcje zintegrowanego srodowiska TC

    Zeby sie do nich dostac, trzeba wcisnac Alt-O. Interesujace sa:
    Compiler, i dalej
     Model           - na razie bedziemy uzywac Small
     Code generation, i dalej
      Instruction set - mozna ustawic 80186/80286, bo o 8088 juz trudno
      Floating point  - warto pomyslec co wybrac: None warto wybrac, bo
                        skroci program, jesli nie chce sie uzywac liczb
                        zmiennopozycyjnych; jesli sie ich uzywa, trzeba
                        wybrac albo Emulation - to bedzie dzialac nawet
                        jesli komputer nie ma koprocesora (czasem takie
                        sie trafiaja), albo 8087/80287 (wtedy koprocesor
                        bedzie potrzebny, zeby program mogl sie wykonac)
      Alignment       - dla Byte program bedzie nieco krotszy i nieco
                        wolniejszy niz dla Word
      Test stack overflow, Line numbers i OBJ debug information lepiej
      miec ustawione na On dopoki program nie jest przetestowany
     Optimization - warto wlaczyc (On) Register i Jump optimization
     Errors - tu sa 4 menu, w ktorych trzeba wszystko ustawin na On:
      Portability warnings, ANSI violations, Common errors, Less common
      errors - zeby wszystkie ostrzezenia byly pokazywane
    Environment - mozna wlaczyc Config auto save i Edit auto save, a jak
     sie chce widziec wiecej linii na ekranie, to ustawic Screen size
    i na koniec trzeba uzyc Save options, zeby zapisac zmienione opcje.


2.4. Mamy napisany program i co dalej?

    Alt-C wybiera menu kompilacji, w nim sa do wyboru:
     Compile   - kompilacja = robi .OBJ z .C
     Make      - "oszczedne" tworzenie .EXE (jesli cos zostalo juz raz
                 zrobione, to sie nie juz powtarza, kryterium oceny jest
                 czas, kiedy zostaly zapisane pliki - jesli np. .C jest
                 starszy niz .OBJ, to nie wykonuje sie kompilacji; bywa
                 to zawodne, jesli komputer ma zle ustawiony zegar, albo
                 jesli zmieni sie opcje kompilatora, np. Model)
     Link      - robi .EXE z .OBJ
     Build all - Compile+Link bezwarunkowo

    Alt-R wybiera menu wykonywania, w nim sa do wyboru:
     Run           - zwyczajnie wykonuje program
     Program reset - konczy program - mozna wykonac powtornie
     Goto cursor   - wykonuje program, zatrzymujac go kiedy dojdzie
                     do linii, w ktorej jest kursor (jak tam dojdzie)
     Trace into    - wykonuje jedna linie, pokazujac wejscie do funkcji
                     jesli linia zawiera wywolanie funkcji z programu
                     (na funkcje z biblioteki to nie zadziala)
     Step over     - wykonuje jedna linie, nie wchodzac do funkcji
     User screen   - pokaze ekran, na ktory pisze wykonujacy sie program
    z tych polecen wszystkie oprocz ostatniego automatycznie robia Make,
    wiec mozna po napisaniu programu nie kompilowac go, tylko od razu
    Alt-R i Run, ale... lepiej najpierw go zapisac, bo jesli zawiesi sie
    komputer, to nie bedzie nawet mozna zobaczyc od jakiego bledu; i do
    tego wykonujac od razu Run traci sie informacje o ostrzezeniach.
    Polecenia "Goto cursor", "Trace into" i "Step over" przydaja sie,
    jesli chce sie wykonywac program po trochu i obserwowac, ale zamiast
    wybierac je z menu wygodniej jest uzywac klawiszy F4, F7 i F8.
    Warto jeszcze zapamietac, ze Alt-F5 pokazuje ekran ("User screen").

    Reasumujac, mozna podejsc tak:
    - napisac program i zapisac go wciskajac F2
    - wcisnac Alt-C B i popatrzec na informacje o bledach
    - wcisnac Alt-R R i jak sie skonczy Alt-F5 i obejrzec wyniki

2.5. Najprostszy program w C

    Mozna wpisac taka linie: main(void) { printf("cos\n"); return 0; }
    a nastepnie to skompilowac i wykonac - program wypisze to "cos" na
    ekranie, i na tym sie zakonczy.


2.6. Nastepny program w C

    Jesli sa wlaczone wszystkie ostrzezenia, to kompilator zaalarmuje,
    ze nie wie, co to jest za funkcja printf (ze nie ma prototypu). Zeby
    wiedzial (nie musi, program dziala i bez tego), mozna mu napisac:
    int printf(const char *format,...);
    przed ta linia z "main", i bedzie wszystko w porzadku. Ale zeby nie
    trzeba bylo pamietac, co tam ma byc napisane, ta i podobne definicje
    zostaly umieszczone w pliku stdio.h, i mozna go wlaczyc do programu
    piszac na poczatku linie: #include <stdio.h>

    Jesli to, co program ma wypisac, ma byc nieco dluzsze, wygodniej
    bedzie zapisac to oddzielnie - mozna to zrobic tak:

    #include <stdio.h>
    const char napis[]=
    "Ala ma kota.\n"
    "To jest kot Ali.\n";
    main(void) {
      printf(napis);
      return 0;
    }

    Uwagi: tekst do wypisania jest w cudzyslowie, ale nie moze zajmowac
    wiecej niz jednej linii; mozna jednak napisac wiele linii z tekstem
    w cudzyslowie, i jesli nie ma na zewnatrz cudzyslowow innych znakow,
    to kompilator potraktuje to jako jeden dlugi tekst; jesli przy tym
    bedzie on podzielony na linie zgodnie z tym, jak ma byc wypisany, to
    jego tresc bedzie latwiejsza do czytania. W tekscie trzeba uwazac na
    znaki \ i %, ktore dzialaja nieco inaczej: \ w stalej znakowej sluzy
    do umieszczenia w niej znakow, ktorych nie mozna "normalnie" wpisac:
    jesli potrzeba \, to wpisuje sie \\, jesli ' lub ", to poprzedza sie
    je \-em, \n to znak przejscia do nowej linii, \t - tabulatora, \r -
    powrotu do poczatku linii, \a - alarm, \nnn (trzy cyfry <7) oznacza
    znak o kodzie nnn (osemkowo - najwiekszy kod 377) - teraz mozna by
    zamiast "Ala ma kota.\n" napisac "Ala ma kota.\a\a\a\r" - ten napis
    pojawi sie, bedzie slychac 3 razy "bip", a potem napis sie zmieni na
    "To jest kot Ali."; % ma specjalne znaczenie dla printf w formacie.


2.7. Program przedstawia sie

    Dla printf znaki %s uzyte w formacie oznaczaja polecenie wypisania
    napisu, a %d liczby calkowitej. Mozna tego uzyc, by program wypisal
    informacje, jak sie nazywa, i ile ma parametrow - w taki sposob:

    #include <stdio.h>
    main(int argc, char **argv) {
      printf("Jestem %s, mam %d parametrow.\n", argv[0], argc);
      return 0;
    }

    Program wypisuje informacje od systemu - nazwe, przez ktora zostal
    wywolany, i ilosc parametrow (otrzymuje on rowniez parametry, ale do
    ich wypisania trzeba juz wiecej roboty). Nazwa zwykle jest razem ze
    sciezka, i typem (.EXE), mozna by je wyrzucic, zeby bylo ladniej:

    #include <stdio.h>
    main(int argc, char **argv) {
      char *p; int i,n,t;
      p=argv[0]; for(i=n=t=0; p[i]!=0; i++) {
        if(p[i]==':' || p[i]=='/' || p[i]=='\\') { n=i+1; t=0; }
        if(p[i]=='.') t=i;
      }
      if(t==0) t=i;
      printf("Jestem %.*s, mam %d parametrow.\n", t-n, p+n, argc);
      return 0;
    }

    ale program juz sie zrobil bardziej skomplikowany - trzeba przejrzec
    nazwe, zapamietac gdzie sie zaczyna ostatnia nazwa po ':', '/', '\',
    zapamietac gdzie jest kropka (jesli jest) po tej nazwie, policzyc ile
    znakow ma nazwa, i tyle ich wypisac (zaczynajac od poczatku nazwy).


2.8. Parametry programu

    Skad sie biora: jesli wykonuje sie program jako polecenie w DOS-ie,
    i oprocz nazwy programu napisze sie cos jeszcze, to to cos jest do
    tego programu przekazywane jako parametry; jesli wykonuje sie program
    poleceniem Run (w TC lub BC), to w opcjach mozna ustawic Arguments.

    Nastepujacy program pokaze liste swoich parametrow:

    #include <stdio.h>
    main(int argc, char **argv) {
      int ai;
      for(ai=1; ai<argc; ai++)
        printf("a[%d]=\"%s\"\n", ai, argv[ai]);
      return 0;
    }

    Uwaga: parametrem z numerem 0 jest nazwa samego programu.


2.9. Zmienne srodowiska DOS

    Ustawia sie je w DOS-ie poleceniem SET, np. SET XX=dwa-Xy
    Sa one tez przekazywane do programu w C - mozna je wypisac tak:

    #include <stdio.h>
    main(int argc, char **argv, char **env) {
      int ei;
      for(ei=0; env[ei]!=NULL; ei++)
        printf("e[%d]=\"%s\"\n", ei, env[ei]);
      return 0;
    }


3. Dyrektywy preprocesora

  Wszystkie zaczynaja sie od znaku # na poczatku linii, w zasadzie ten
  znak nie powinien byc poprzedzany spacjami, chociaz wiele kompilatorow
  akceptuje to i rozpoznaje je mimo wszystko. Mozna natomiast, jesli to
  zwiekszy czytelnosc programu, uzyc spacji pomiedzy # i nazwa dyrektywy.


3.1. #include

    Mozliwe sa dwie formy: #include "nazwa" i #include <nazwa>; sluzy do
    wlaczenia pliku do kompilowanego programu tak, jakby sie go wczytalo
    przy edycji programu w miejsce tej dyrektywy; pierwsza forma szuka
    pliku najpierw w skorowidzu, w ktorym jest kompilowany program, potem
    w skorowidzach zdefiniowanych w "sciezce wlaczania" (include path),
    druga szuka wylacznie w sciezce wlaczania; nazwa musi zawierac typ
    (czyli np. stdio.h, a nie samo stdio), i moze zawierac sciezke.


3.2. #define

    Sluzy do zdefiniowania wartosci badz wyrazen, ktore nastepnie beda
    uzywane w programie; moze zdefiniowac nazwe jako pusta, co jednak
    nie jest tym samym, co brak definicji, i daje sie odroznic chocby
    przez dyrektywy #ifdef, #ifndef, i defined() w #if, #elif.


3.2.1. bez argumentow lub z argumentami

    #define nazwa definicja

    powoduje, ze nazwa, jesli wystapi w programie jako oddzielne slowo,
    zostanie zastapiona przez podana definicje (nie zostanie zastapiona
    w napisie umieszczonym w cudzyslowie - robi sie to nieco inaczej).
    
    #define nazwa(argumenty) definicja
    
    powoduje, ze nazwa, jesli wystapi w programie wraz z nawiasem i lista
    argumentow (musi sie zgadzac ich ilosc), zostanie zastapiona przez
    definicje, w ktorej nazwy argumentow zostana zastapione wartosciami
    podanymi przy uzyciu tej nazwy; rozpoznawanie argumentow w definicji
    nie dziala w napisie umieszczonym w cudzyslowie, argument musi byc
    wyodrebnionym slowem - dalej bedzie informacja jak mozna to obejsc.

3.2.2. dlaczego potrzeba uzywac nawiasow

    Wezmy taka definicje: #define badsqr(x) x*x
    Zadziala to sensownie, jesli za x podstawi sie liczbe, albo nazwe
    zmiennej - np. badsqr(2) zamieni sie na 2*2, badsqr(a) na a*a - niby
    sensownie, ale juz badsqr(a+b) zamieni sie na a+b*a+b - zupelnie nie
    to, co trzeba, bo mnozenie ma wyzszy priorytet niz dodawanie, i to,
    co wyszlo, nie jest suma a+b podniesiona do kwadratu; oczywiscie,
    mozna napisac badsqr((a+b)), ale zamiast tego lepiej napisac:
    #define sqr(x) ((x)*(x))
    i mozna sie nie przejmowac ani tym, ze x jest wyrazeniem, w ktorym
    sa operatory z priorytetem nizszym, niz mnozenie, ani tym, uzywa sie
    sqr w polaczeniu z operatorami o priorytecie wyzszym niz mnozenie.

3.2.3. jednoliniowa - kontynuacja linii przez \

      W zasadzie w jezyku C instrukcje mozna "ciagnac" przez wiele linii,
      ale dyrektyw preprocesora to nie dotyczy - musza "zmiescic sie"
      w jednej; poniewaz #define bywa dosc dlugie, czasem przydaje sie
      kontunuacja linii przez \ na koncu - powoduje on, ze nastepna linia
      zostaje potraktowana jako ciag dalszy biezacej (tak, jakby nie bylo
      znaku \ i przejscia do nowej linii) - z #define mozna tylko tak...
      

3.2.4. operator # - definicja show(x)

      Uzycie #argument w definicji jest zastepowane przez wartosc tego
      argumentu w cudzyslowie; jest to szczegolnie uzyteczne w takiej
      definicji: #define show(x) printf(#x "=%u\n",x)
      ktorej mozna potem uzyc tak: show(1+4)
      A mozna tez przerobic nasz pierwszy program na cos takiego:
      #define P(x) printf(#x "\n")
      main(void) {
        P(Ala ma kota.); P(To jest kot Ali.); return 0;
      }

3.2.5. operator ## - laczenie nazw

      Operator ## dziala po zastapieniu nazw argumentow w definicji ich
      wartosciami, i po prostu znika razem ze spacjami przed nim i po
      nim - moze wiec byc uzyty do laczenia nazw, na przyklad:
      #define PREFIX xx
      #define PP(a,b) a ## b
      #define P(x) PP(PREFIX,x)
      i teraz P(a) zostanie zastapione przez xxa.


3.3. #undef

    Sluzy do anulowania definicji #define; jest to wskazane, jesli cos
    zostalo zdefiniowane, i definicja ma byc zmieniona - inaczej byloby
    to traktowane jako blad przy kompilacji; jest tez potrzebne, jesli
    zdefiniowanie badz brak zdefiniowania czegos ma sterowac kompilacja;
    praktycznie przydaje sie przy duzych programach, podzielonych na
    wiele czesci, z ktorych jedna zawiera parametry konfiguracyjne.


3.4. kompilacja warunkowa: #ifdef, #ifndef, #if, #else, #elif, #endif

    Ogolnie konstrukcja wyglada tak:
    #if warunek1
    ...instrukcje1...
    #elif warunek2
    ...instrukcje2...
    #else
    ...instrukcje3...
    #endif
    i powoduje to, ze jesli jest spelniony warunek1, to kompiluja sie
    instrukcje1, jesli nie, a jest spelniony warunek2, to kompiluja sie
    instrukcje2, a jesli zaden nie jest spelniony, to instrukcje3; jest
    to sterowanie kompilacja, i instrukcje pomijane moga nawet nie byc
    poprawnymi instrukcjami w C, byleby tylko nie bylo wsrod nich tych
    dyrektyw, o ktorych tu mowa (a jak juz by byly, to w komplecie) -
    mozna tego uzyc do "zakomentowania" kawalka programu, na przyklad:
    #if 0
    ...cos, co ma byc zignorowane, na przyklad komentarz...
    #endif
    bywa to uzyteczne, jesli robi sie wieksze zmiany w programie, i chce
    sie zachowac gdzies poprzednia wersja - mozna tak zakomentowac czesc
    programu, skopiowac ja poza obszar #if...#endif, poprzerabiac, potem
    sprobowac, jak dziala, i moc latwo wrocic do poprzedniej, zwlaszcza
    jesli zrobi sie to tak:
    #if 0
    ...stare instrukcje...
    #else
    ...nowe instrukcje...
    #endif
    i zeby wrocic do starej wersji wystarczy zmienic 0 na inna liczbe,
    spowoduje to skompilowanie starych instrukcji zamiast nowych.
    Ale jeszcze wygodniej jest uzyc zamiast #if 0 innej dyrektywy -
    #if defined(nazwa) lub #ifdef nazwa - obie wersje dzialaja tak samo,
    instrukcje po tej dyrektywie zostana skompilowane jesli nazwa jest
    zdefiniowana przez #define nazwa (moze nie miec wartosci); #ifndef
    rozni sie od #ifdef odwroceniem warunku (nazwa nie zdefiniowana).
    I moze przyklad - program, ktory uzywa stdio.h lub conio.h:
    #ifdef USECONIO
    # include <conio.h>
    #else
    # include <stdio.h>
    #endif
    main(int argc, char **argv) {
    #ifdef USECONIO
      cprintf("Jestem %s\r\n",
    #else
      printf("Jestem %s\n",
    #endif
        *argv);
      return 0;
    }
    Mozna go skompilowac bez zdefiniowania USECONIO, i wtedy uzyje on
    funkcji printf do pisania na STDOUT (mozna to skierowac na plik
    wywolujac program przez: nazwa-programu.exe > nazwa-pliku, nazwa
    pliku moze byc np. NUL jesli nie chce sie tego nigdzie zapisywac),
    lub zdefiniowac USECONIO (wybrac w menu Options, Compiler, Defines
    i wpisac USECONIO), i wtedy uzyje cprintf do pisania bezposrednio
    na ekran (wypisze sie na ekranie nawet gdy uzyje sie >).

3.5. #error

    Sluzy do zasygnalizowania sytuacji, okreslonej przez definicje
    (#define, lub w opcjach kompilatora), ktora nie jest sensowna, dla
    ktorej program nie moze dzialac w sposob przewidziany przez autora.


3.6. #line

    Sluzy do podania numeru linii i ewentualnie nazwy pliku, ten numer
    i ta nazwa beda uzyte przy sygnalizacji bledu zamiast rzeczywistego
    numeru linii i nazwy pliku - ma zastosowanie, gdy tekst programu w C
    zostal przetlumaczony, i zmienila sie ilosc linii, i nazwa pliku.


3.7. #pragma

    Sluzy do przekazywania kompilatorowi dyrektyw specyficznych dla
    niego, na przyklad rodzajow bledow do sygnalizowanie lub nie.
    Parametry dyrektywy pragma moga byc dla kazdego kompilatora inne.
    Przyklad: #pragma warn -par - wylacza sygnalizowanie nieuzywanego
    parametru (main(int argc, char **avec) i nie uzywa sie argc).


