* Indeks rozdziaw

  15 Zstpniak
  28 Pierwszy program - wypiszemy na monitorze jakie bzdury
 111 Jestemy leniwi
 304 Zmienna zmiennej niepodobna, czyli...
 390 Co ma konwersja do dzielenia?
 426 Wariat-cje na temat tekstu
 594 Jak utrudni sobie ycie, czyli kilka wiadomoci o tablicach
 659 acuchy w okowach... czy jako tak
 911 Zagadka enigmy, czyli co znaczy znaczek & przy funkcji scanf
1131 Funkcjonowanie programu
1211 Nareszcie odpocz mona?

* Zstpniak

Po co komu jeszcze jeden kurs jzyka C, zwaszcza, e kady przyzwoity
programista pisze albo w Pascalu, albo w C++, albo innej Javie? Ano po nic, po
prostu naszed mnie taki drobny schiz, napisa nieco o podstawach C. Z reszt,
mam to na zajciach, wic od razu moe komu ze znajomkuff si przysu.
Zakadam oczywicie u czytelnika (czyt.: u ciebie) minimaln wiedz o
komputerze, konsoli (linii polece), programowaniu (znajomo paru terminw)
oraz obsugiwaniu edytora tekstu. Jeli tej wiedzy nie posiadasz, to nawet
najlepsza ksika o programmingu niewiele ci da, wic najpierw pobaw si
obsug kompa.
Tyle tytuem wstpu.

* Pierwszy program - wypiszemy na monitorze jakie bzdury

Na pocztek dwie sprawy:
- Kompilatorowi nie robi adnej rnicy, czy wadujesz jedn spacj, enter,
  tabulator czy dowoln kombinacj powyszych. Odstp to odstp. W zwizku
  z tym od razu naucz si odpowiednio wcina tekst programw, eby widzia,
  na jakim poziomie si znajdujesz.
- Jzyk C jest wraliwy na wielko liter, czyli 'int' nie jest tym samym,
  co 'INT'. Musisz dokadnie przyglda si, jakie wielkoci liter byy uyte.

Najkrtszym programem w C, ktry nie spowoduje adnych ostrzee kompilatora
(na gcc, na produktach Borlanda mona zrobi krtszy) jest takie c:

int main(void){return 0;}

Tylko 25 znakw. Jedna linijka. Ale program jest rednio czytelny, a robi w
zasadzie nic. A dokadnie: po wywoaniu od razu zwraca sterowanie systemowi
operacyjnemu, czyli koczy dziaanie. Z czytelnoci poradzimy sobie np. tak:
-------------------------------------------------------------------------------
int main (void)
{
return 0;
}
-------------------------------------------------------------------------------
Prawda, e adniejsze? To jest w zasadzie nasz szkielet programu, bdziemy go
uywa praktycznie zawsze.
( I jeszcze drobna uwaga: jeli otwierasz jakikolwiek nawias czy cudzysw, to
od razu go zamknij, przesu kursor midzy oba znaczki i dopiero tam pisz to,
co miao by wewntrz. Unikniesz wtedy zastanawiania si, czy ju zamkne
ju ten nawias, czy dopiero bdziesz musia. )
No dobra, ale on nic nie robi, ten nasz program. Dobrze by byo, eby program
jako usera (czyli uruchamiajcego program) powiadomi, e w ogle zadziaa.
Wypiszemy zatem jaki tekst (prawd mwic chyba jedyny sensowny sposb na
komunikacj z userem na konsoli - przez klawikatur :)).
Cay trik polega na tym, e C nie ma adnego sowa kluczowego na wypisywanie
czegokolwiek na ekran. Musiaa zosta napisana specjalna funkcja, ktra ten
tekst wyprowadza. Tylko jak si do niej dobra? S dwa sposoby: przepisa j
do naszego programu (niezbyt wygodne), albo wstawi plik z funkcj do naszego
programu:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
return 0;
}
-------------------------------------------------------------------------------
To znaczy mniej wicej tyle: znajd (w pewnym katalogu) plik 'stdio.h' i przy
kompilacji wstaw go tutaj. Czyli e ju moemy uy funkcji wypisujcej znaki:
-------------------------------------------------------------------------------
#include <stdio.h>
int main (void)
{
puts("Jakis bzdurny tekst zamiast oslawionego Hello world");

return 0;
}
-------------------------------------------------------------------------------
Funkcja puts robi tak: wypisuje zadany tekst (UWAGA! Tekst, czyli cig znakw,
musi by ujty w ", a nie w ') i  przechodzi do nowej linii. Moesz to
sprawdzi, umieszczajc dwie funkcje puts, jedna po drugiej.
Dobra, fajnie. Ale to nie wszystko, co moesz zrobi. Moesz wypisa
POJEDYNCZY ZNAK (!) :))). Moesz to zrobi funkcj putchar:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
puts("Teraz jakis przykladowy pojedynczy znaczek:");
putchar('z');
puts(" dalsza czesc bzdurnego tekstu");

return 0;
}
-------------------------------------------------------------------------------
UWAGA: wywoujc funkcj putchar chciae tylko jeden znak wypisa, a nie cay
cig, zatem ograniczamy go tylko pojedynczym czudzysowiem (nie tym pod tyld,
tylko tym pod podwjnym cudzakiem). Pniej wyjani rnic. Na razie zwracaj
uwag na to, jakiego cudzaka uyem.
Wracajc do funkcji putchar, to wypisuje ona JEDEN znaczek, po czym NIE
PRZECHODZI do nowej linii, czym si rni od puts (sprawd).
licznie.

* Jestemy leniwi, czyli jak nakoni program do odwalenia wielu
  powtarzajcych si operacji za nas

Teraz chcemy wypisa 25 gwiazdek w jednej linii. Moemy to zrobi na kilka
sposobw: {puts("*** ... *");} albo 25 razy wywoa {putchar('*');}. Jest
trzecia metoda, ktrej stosowanie w tak banalnych przykadach jest (prawie)
idiotyzmem, ale w kocu kto powiedzia, e pierwsze programy nie mog by
idiotyczne? Z gry wyjani, co chc zrobi: ot zrobi tak: :)
dwadziecia pi razy wykonam funkcj putchar. Proste, nie? ;) To popatrz:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int licznik;
for (licznik = 0; licznik < 25; licznik = licznik + 1)
  putchar('*');

return 0;
}
-------------------------------------------------------------------------------
I tu pojawia si par rzeczy nowych, ktrymi zajmiemy si przez par dalszych
ekranw. Najpierw linijka 'int licznik;'.
Widzisz, 'licznik' jest tzw. zmienn, ktra moe si zmienia w trakcie
dziaania programu. Kad zmienn naley zadeklarowa na pocztku programu
(jest to drobne uproszczenie, ale na razie wystarczy). Zadeklarowa, czyli
powiedzie, jakiego typu jest nasza zmienna: czy jest znakiem, czy liczb
cakowit, czy moe rzeczywist. Kompilator musi wiedzie, jak traktowa sowo
'licznik' - i po to jest deklaracja. Swko 'int' oznacza liczb cakowit,
liczba rzeczywista to 'float', natomiast znak to 'char'.
Teraz kilka sw o tym, co si dzieje po sowie 'for'. Instrukcja
'licznik = 0;' mwi, e do zmiennej licznik ma zosta zapisana warto 0.
Podobnie byoby dla 'licznik = 5;' - do zmiennej licznik wpisz warto 5.
Instrukcja 'licznik < 25;' jest chyba jasna. Ostatnie jest wyraenie
'licznik = licznik + 1;'. Znaczy tyle: do zmiennej licznik wpisz warto, jaka
ci wyjdzie po stronie prawej, a tam mamy wyraenie 'licznik + 1'. W efekcie
w zmiennej 'licznik' bdzie liczba o 1 wiksza, ni przed przypisaniem. To si
nazywa inkrementacja i dziki temu ptla w ogle dziaa.
Teraz struktura samej ptli for: 'for (inic; war; inkr) instrukcja;'
'inic' to inicjalizacja ptli, czyli nadajemy pewnej zmiennej jak warto
pocztkow, a pniej ju nie zagldamy do tej przegrdki. U nas inicjalizacj
byo nadanie zmiennej licznik wartoci 0.
'war' to warunek. Ptla bdzie wykonywana dopki warunek bdzie prawd (czyli
dopki licznik bdzie mniejszy od 25). Warunek wcale nie musi by zwizany ze
zmienn licznika, moe by jakimkolwiek wyraeniem (cho ze zrozumiaych
wzgldw najczciej zaley od wartoci licznika).
'inkr' to inkrementacja, czyli zwikszenie licznika. Mgby zapyta, po co tu
takie co, skoro zawsze bdziemy zwiksza licznik o 1. Ot nie zawsze.
Czasem chcielibymy licznik zmniejsza, a nie zwiksza, a czasem zwiksza
o na przykad 2, albo w ogle robi co innego.
Na koniec ptli nastpuje 'instrukcja'. To jest to, co ma by powtarzane przez
ptl. Musi by zakoczona rednikiem - jak kada instrukcja.
UWAGA: Ptla for jest traktowana jako polecenie w C, ale tylko RAZEM z
instrukcj, ktr ma powtarza. Oznacza to, e bro ci Panie Boe przed
stawianiem rednika zaraz po nawiasie for(;;). Chyba, e chcesz, aby ptla nic
nie robia.
UWAGA 2: Instrukcja po ptli for jest tylko jedna. Ale jako jedna instrukcja
traktowany jest te tzw. blok instrukcji, czyli par komend (zakoczonych
standardowym rednikiem) ograniczonych nawiasami klamrowymi (takimi, jak w
szkielecie programu):
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int licznik;
for (licznik = 0; licznik < 20; licznik = licznik + 1)
  {
  putchar('*');
  putchar('-');
  }

return 0;
}
-------------------------------------------------------------------------------
I oczywicie WCICIA!!! Jak mylisz, co nam wypisze program? Wypisze nam to:
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
Taki sobie szlaczek.
Po bloku instrukcji nie nastpuje rednik (chocia moe, to wcale nie
spowoduje bdu czy ostrzerzenia kompilatora) - przecie ostatnia instrukcja
w bloku ju jest zakoczona rednikiem.
Jak si pewnie domylasz (cho nie koniecznie, moe po prostu nie zwrcie na
to uwagi), kod naszego programu te jest blokiem instrukcji.
I drobna uwaga. W bloku moe by dowolna ilo instrukcji, od adnej, przez
jedn, do mnstwa (Kali umie liczy ;]]]). Jedn instrukcj TE MONA
umieci w klamrach!
No to mamy ptl for. Co by si jeszcze przydao. Moe instrukcja if?
Piszemy:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int licznik;
for (licznik = 0; licznik < 10; licznik = licznik + 1)
  {
  if (licznik == 2)
    puts("Teraz licznik jest rwny 2!");
  }

return 0;
}
-------------------------------------------------------------------------------
Przypominam o WCICIACH!!!
Efekt dziaania programu to wypisanie tekstu. Nieduo, a do tego mona byo
si oby bez ptli i if-a. Ale nie o to chodzi. Przyjrzyj si, co robi program
od kuchni. Dziesi razy wykonuje ptl, a za kadym razem sprawdza, czy
licznik nie jest czasem rwny 2. Instrukcja if jest nieco podobna do for,
jeli chodzi o traktowanie rednika, obie uwagi z for-a tycz si if-a
(zajrzyj tam). Zauwa jeszcze jedno: porwnanie dwch liczb to DWA znaki '='
NIE ODDZIELONE ADN PRZERW. Jeden znak to wstawienie liczby z prawej
do zmiennej z lewej. Inne tzw. operatory porwna to: < > <= >= !=. To chyba
wszystkie. Ostatni znaczy <nie rwne>, czyli polecenie {if (2 != 3)
puts("2 nie jest rwne 3");} wykona si (warunek 2 != 3 jest zawsze speniony).
A jak zrobi, eby program wypisywa ten sam komunikat dla liczb 0, 2, 4 i 8?
Ano tak:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int licznik;
for (licznik = 0; licznik < 10; licznik = licznik + 1)
  {
  if (licznik == 0 || licznik == 2 || licznik == 4 || licznik == 8)
    puts("Jedna z naszych liczb!");
  }

return 0;
}
-------------------------------------------------------------------------------
Znaczek '||' to logiczne OR, czyli caa instrukcja mwi:
jeli licznik jest rwny 0 ALBO l. jr. 2 ALBO l. jr. 4 ALBO l. jr. 8, to
wykonaj instrukcj wypisania tekstu.
Instrukcja puts("..."); zostanie wykonana, jeli KTRYKOLWIEK warunek bdzie
speniony. S jeszcze dwa inne operatory: '!' i '&&'. Najpierw &&. Dziaa
podobnie do ||, ale oznacza logiczne AND (wszystkie warunki musz by
spenione jednoczenie). Odmiecem jest '!'. Oznacza negacj.
[ !(3 <= 2) ] oraz [ (3 > 2) ] s sobie rwnowane. Pierwsze oznacza "nie
prawda, e (3 jest mniejsze lub rwne 2)", czyli 3 musi by wiksze. Z negacj
mona konstruowa duuuuuugie wyraenia logiczne. Wyprbuj wszystkie operatory
logiczne, pamitajc, e moesz do nich stosowa nawiasy '(' ')' (do ustalenia
kolejnoci dziaa).

Teraz pora na jeszcze jedn ptelk, bardzo podstawow.
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int zmienna;
zmienna = 10;
while (zmienna != 0)
  {
  puts("Wykonujemy ptl");
  zmienna = zmienna - 1;
  }

return 0;
}
-------------------------------------------------------------------------------
Ptla while zachowuje si ze rednikiem identycznie, jak for i if (zajrzyj do
opisw). Ptla while przypomina dziaaniem instrukcj if, z tym, e instrukcja
po 'while (warunek)'jest wykonywana, dopki warunek jest speniony. Ptla
sprawdza warunek, a potem, jeli jest speniony, to wykonuje instrukcj
i ponownie sprawdza warunek, po czym, jeli jest speniony, to wykonuje
instrukcj i ponownie sprawdza warunek ... itd.
W naszym przypadku wykona si 10 razy. Gdybymy nie dodali linijki
{zmienna = zmienna - 1;}, to kiedy ptla by si zakoczya? Nigdy, bo warunek
byby zawsze speniony (zmienna rwna 10, czyli rna od 0). A tak, to po
kadym wywoaniu zmienna zostaje zmniejszona o 1 (co ju byo omawiane, tylko
e dla dodawania jedynki).
Uff... sporo tego. A jeszcze nie omwiem paru rzeczy, mianowicie operatorw
"liczbowych" (+-*/%) i operatorw typowych dla C i jzykw na nim wzorowanych.
Pierwsze cztery (+-*/) to normalne operatory, jak w kalkulatorze. Oczywicie
kolejno dziaa jest przestrzegana. Operator '%' to tzw. dzielenie modulo,
co dla programmera oznacza, e zwraca reszt z dzielenia, np.
5 % 3 == 2; 8 % 2 == 0; 13 % 10 == 3; 10 % 3 == 1; itd. apiesz, o co chodzi?
Priorytet dzielenia modulo jest taki sam, jak zwykego dzielenia.
Jest par operatorw specyficznych dla C. S do dziwne, ale przy odrobinie
wprawy s BARDZO wygodne. Pierwsze to co takiego: {zmienna++;} Co to znaczy?
To znaczy to samo, co {zmienna = zmienna + 1;} Efektem jest zwikszenie o 1
zmiennej 'zmienna'. Analogicznie {zmienna--;}, oznacza zmniejszenie zmiennej
'zmienna' o 1. Teraz ciekawe operatory przypisania, gdy mamy zmienn 'zm':
zm -= 15;   znaczy   zm = zm - 15;
zm += 1;    znaczy   zm = zm + 1; (co mona zapisa jeszcze zm++;)
zm *= 2;    znaczy   zm = zm * 2;
zm /= 4;    znaczy   zm = zm / 4;
zm %= 5;    znaczy   zm = zm % 5;
Mam nadziej, e apiesz o co chodzi. Dla wyraenia 'zm *= 2;' robimy tak:
najpierw mnoymy aktualn zawarto zmiennej zm przez 2, a potem wstawiamy
do zmiennej zm. W zmiennej zm bdzie wic 2 razy wicej, ni przed operacj.

* Zmienna zmiennej niepodobna, czyli...

...conieco o rnych rodzajach danych i zamianach jednego typu na drugi.
W C mamy w zasadzie trzy podstawowe typy danych plus ich tablice i wskaniki.
Dwa ostatnie, zwaszcza wskaniki, omwi pniej.
W C s dane typu 'int', 'float' i 'char'. 'int' ju poznae. Tu mog by
liczby cakowite. 'float' to liczby rzeczywiste, czyli take uamki. 'char'
zawiera znak, a w zasadzie jego kod ASCII. Tak po prawdzie, to typ char mona
podcign pod int, skoro to liczba cakowita. Jakie s tego konsekwencje?
Pamitasz funkcj putchar() ? Moesz zamiast znaku poda funkcji jego kod
ASCII, dla funkcji nie bdzie to miao adnego znaczenia. Wywoanie
putchar('A'); oraz putchar(65); da ten sam efekt (litera "due a" ma kod ASCII
rwny 65). A co bdzie oznacza wyraenie {'A' * 2;} ? Oczywicie 65 * 2, czyli
130. Pojedynczy znak ujty w cudzysw ' jest traktowany przez kompilator
identycznie, jak liczba. Ujty w podwjny cudzysw ju nie bdzie pojedynczym
znakiem, ale tablic, ktra jest traktowana nieco inaczej, co jest powodem,
dlaczego rodzaj cudzysowia robi znaczenie, ale o tym pniej.
Teraz zrobimy tak: mamy zmienn 'znak' typu char, a chcemy tam umieci znak o
kodzie 97, czymkolwiek by ten znak nie by. Robimy to w ten sposb:

char znak;
znak = 97;

Proste, co? Podobnie robimy, gdy mamy zmienn 'liczba' typu int, a chcemy tu
umieci warto kodu ASCII znaku '%'.

int liczba;
liczba = '%';

Jeszcze jedno przypisanie. Mamy obie zmienne, 'znak' i 'liczba'. Zamy, e
w zmiennej znak ju co jest, np. 'g'. Przepiszemy to do zmiennej liczba:

int liczba;
char znak;
znak = 'g';
liczba = znak;

atwiej by nie moe. W Pascalu czy Basicu/VisualBasicu trzeba specjalnej
funkcji na konwersje znakw na kody i na odwrt. W C nie potrzeba takich
rzeczy.
Zagadka: czy wyraenie {'B' >= 68;} jest prawd?
Odpowied: nie, bo skoro {'A' == 65;}, to {'B' == 66;}, a 66 jest mniejsze od
68.
Konwersja liczby typu int na float jest rwnie banalna. Po prostu j wstawiasz
w odpowiedni zmienn. Konwersja odwrotna rwnie jest identyczna, tylko e
wtedy tracisz wszystko, co byo po przecinku:

float rzecz;
float rzecz2;
int calk;
rzecz = 18.2345;
calk = rzecz;
rzecz2 = calk;

UWAGA: znakiem pozycji dziesitnej jest KROPKA, nie przecinek!!! Przecinek
jest uywany do innych celw.
Najpierw wpisujemy zawarto zmiennej rzecz do calk i tracimy cz po
przecinku, nastpnie tak obcit liczb wpisujemy do drugiej rzeczywistej.
W tym momencie w 'rzecz' jest liczba 18.2345, a w 'rzecz2' jest 18 .
A tak na marginesie, zamiast dwch linijek

float rzecz;
float rzecz2;

mona wstawi jedn:

float rzecz, rzecz2;

Jeli deklarujesz par zmiennych tego samego typu, to zamiast pisa
par(nacie) osobnych swek 'int', 'float' czy innych moesz oddzieli
przecinkami nazwy zmiennych.

Misja dla ciebie: jak wywietli 20 kolejnych duych liter zaczynajc od 'A',
wiedzc, e 'A' ma kod 65, 'B' ma 66 itd. ? Podpowiem, e chodzi mi o ptl
for.
A teraz odpowied:

char znak;
for (znak = 'A'; znak < 'A' + 20; znak++)
  putchar(znak);

Moe ci zdziwi, czemu uywam zmiennej nie int, a char. A czemu nie? Przecie
formalnie char to te liczba, wic mona j zwiksza o 1 (przypominam, e
'znak++' znaczy zwiksz zmienn znak o 1), a dwadziecia pierwszych znakw
ma kody od 'A' do 'A' + 19. No a potem oczywicie wypisuj znak.

* Co ma konwersja do dzielenia?

Ot co ma. Mamy taki pseudoprogram (pseudo, bo bez adnych szkieletw)

int a, b, c;
a = 10;
b = 6;
c = (a / b) * b;

Ile bdzie wynosi 'c'? Bdzie wynosi 6. Dziwne. Ale obejrzyjmy to
dokadniej: dzielimy cakowit 'a' przez cakowit 'b', otrzymujemy liczb
cakowit rwn 1 (1.666 i obcinamy przecinek). Teraz nasze 1 mnoymy przez
'b' i otrzymujemy 6.
Naley ci si wyjanienie, czemu otrzymujemy liczb cakowit. Wszystkie dane
w wyraeniu s zamieniane na najwyszy typ, jaki wystpuje w tym wyraeniu
(najniszy to char, pniej int, najwyszy to float), a wynik jest wanie
tego typu. Skoro wic najwyszy typ to int, to wynik jest tego samego typu,
a zatem obcinamy cyfry po przecinku.
Teraz ju wiemy, gdzie ley bd, ale jak go usun, nie zmieniajc typu
zmiennych? Tu nam pomoe rzutowanie (konwersja) jednego typu na drugi. Jeli
zmusimy program, eby nam wynik poda jako liczb rzeczywist, to nie bdzie
problemu, 10 / 6 * 6 bdzie rwne 10.
Zastanwmy si, jak to zrobi. Moe tak?
c = (float)(a / b) * b;
Najpierw wyjani, co oznacza pierwszy nawias. To jest konstrukcja w stylu
[ (float)a ], ktra mwi, e zmienn 'a' ma traktowa, jakby 'a'bya typu
float (konwersja na char czy int wyglda identycznie, zmieniasz tylko typ).
Wracajc do linijki [ c = ... ], mamy takie co: podziel a przez b (wynik jest
liczb cakowit), a nastpnie zamie go na float. Czyli ju stracilimy
przecinek. Trzeba to zrobi wczeniej, czyli tak:
c = ((float)a / b) * b;
Teraz zmienna 'b' te jest zmieniona na float, podobnie jak wynik. Teraz, gdy
pomnoymy wynik przez 6, otrzymamy 10 (z drobnym haczkiem, bo liczby typu 1/3
zapisuj si tylko z pewn dokadnoci). Teraz jest dokonywana zamiana wyniku
z float na int, czyli w 'c' bdzie dokadnie 10.

* Wariat-cje na temat tekstu

Do tej pory omijaem nieco ten temat, bo chciaem najpierw wytumaczy tablice
i wskaniki, ale chyba nie bdzie to miao wikszego sensu. Niech bdzie zatem
rozdzia o tekcie i paru operacjach na nim. Najpierw dowiemy si, jak wypisa
linijk tekstu i nie przechodzi do nowej linii (puts przechodzi):
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
printf("Znowu jakis bzdurny tekst.");
puts("Teraz dalszy ciag");

return 0;
}
-------------------------------------------------------------------------------
Efekt dziaania bdzie taki:
Znowu jakis bzdurny tekst.Teraz dalszy ciag
Na kocu oczywicie nowa linia, bo uylimy puts (gdyby nie to, pod Linuksem
znak zachty byby przesunity, co daje niemiy oku obraz). Zwr uwag, e
przed 'Teraz dalszy ciag' NIE MA spacji. Kursor po uyciu funkcji printf
zostaje DOKADNIE w tym miejscu, w ktrym zakoczy pisanie. Pytanie wic
brzmi: czy funkcja printf moe schodzi do nowej linii? Odpowied brzmi: tak.
Notabene mona ten sam sposb zastosowa do puts, eby napisa kilka wierszy
jedn funkcj. A sposb wyglda tak:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
printf("Teraz pierwsza linia\nTeraz druga linia\n");

return 0;
}
-------------------------------------------------------------------------------
Znaczek '\n' mwi "zejd kursorem do nowej linii". Drugi znaczek, jaki si
stosuje, to '\t', czyli tabulator. Pojawia si pytanie: jak wobec tego napisa
pojedynczy backslash? Musisz uy kombinacji '\\'. Odpowiednio dwa backslashe
otrzymasz wpisujc '\\\\' itd. To si tyczy WSZYSTKICH tekstw w kodzie
rdowym w C, o czym czasem mona sobie zapomnie, a to daje rne ciekawe
(czyt. nieprzewidywalne) efekty.
Gdy chcesz uzyska na ekranie podwjny cudzysw, identyczny z tymi, jakie
odgraniczaj tekst, wpisujesz '\"'. W Pascalu trzeba byo si zdrowo
nakombinowa, eby dosta takie co. Przykad:
    "Przykladowy tekst \"w cudzyslowiu\" i poza nim."  zapis w kodzie
     Przykladowy tekst "w cudzyslowiu" i poza nim.     wydruk na ekranie
A jeli chcesz wstawi jak zmienn do tekstu, to musisz uy funkcji printf:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int licznik;
for (licznik = 0; licznik < 20; licznik++)
  printf("Licznik w ptli ma warto: %d\n", licznik);

return 0;
}
-------------------------------------------------------------------------------
Co nam wypisze ptla? Dwadziecia linijek (znak '\n' na kocu!!), w kadej
jaki tekst zakoczony liczb, jak aktualnie ma zmienna licznik, czyli
w pierwszej linii bdzie 0, w drugiej 1, w trzeciej 2, ... w ostatniej 19.
Tylko jak to si dzieje, e wstawia si warto zmiennej? W naszym tekcie
mamy jedno dziwne wyraenie: %d. Oznacza ono, e po naszym tekcie bdzie
zapisana jaka zmienna, ktrej zawarto wypiszemy, a wypiszemy j jako liczb
w systemie dziesitnym (decimal, std litera d po procencie) analogicznie
wypisanie liczby w formacie semkowym bdzie o - od Octal, a w systemie
szesnastkowym x - od heXadecimal (wielko litery po znaku '%' MA znaczenie)
Moesz te posuy si pierwsz liter typu, czyli jeli chcesz wypisa
zmienn cakowit (int), bdzie '%i', dla rzeczywistej '%f' (float), dla
znakowej (char) - '%c'. Znak procenta to (podobnie jak backslash) dwa procenty
'%%'. I jeszcze drobna uwaga: moesz poda inny typ, ni zmienna ma faktycznie,
ale bdziesz musia si pogodzi z konsekwencjami rzutowania (konwersji)
na ten typ, np.:

float rzecz;
rzecz = 18.255;
printf("Liczba 'rzecz': %i\n", rzecz);

wydruk: Liczba 'rzecz': 18

Dobrze jest pamita przy kadym zakoczeniu uywania funkcji printf dorzuci
znak nowej linii, bo wtedy nie ryzykujesz, e zaraz za dopiero wypisanym
tekstem pojawi si nowy tekst (np. w Linuksie znak zachty).
A jak si ma sprawa z typem znakowym? Skoro to liczba, to czemu wydzielono
specjalny typ? Ot wanie. Rnica midzy char i int ujawnia si dopiero
przu drukowaniu. Drukujc int dostaniesz na monitorze liczb. Drukujc char
dostaniesz na ekranie znak o takim kodzie. Przykad:

char znak;
int kod_znaku;
znak = 'A';
kod_znaku = znak;
printf("Znak %c ma kod %i\n", znak, kod_znaku);

wydruk: Znak A ma kod 65

Oczywicie moemy skorzysta z dobrodziejstw konwersji (czyli rzutowania):

char znak;
znak = 'A';
printf("Znak %c ma kod %i\n", znak, znak);

Wydruk na ekranie jest identyczny, jak poprzednio.
Zwr uwag, e zmienna znak zostaa wpisana DWA razy w funkcji printf.
Do KADEGO wstawienia zmiennej musi by osobny argument.
  {  Krtkie wyjanienie, o co mi chodzi.
  Ot funkcj wywouje si z argumentami. Dla puts by to tekst, ktry
  wypisywalimy, dla putchar by to znak (a w zasadzie jego kod ASCII),
  dla printf jest to tekst, jaki wypiszemy, wraz z paroma zmiennymi, ktre
  wstawimy do tekstu. To s argumenty. Funkcje puts i putchar musz mie
  dokadnie jeden argument, natomiast printf moe mie zmienn ich ilo.
  }
Jeszcze drobna sztuczka w funkcji printf: moesz zarezerwowa szeroko, na
jakiej bdzie wypisana liczba, wstawiajc przed liter typu szeroko w
znakach. Prawd mwic, niezbyt potrafi to wyjani sownie, wic odwoam si
do przykadu:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int licznik;
for (licznik = 0; licznik < 15; licznik++)
  printf("%2i,");
putchar('\n');

return 0;
}
-------------------------------------------------------------------------------
wydruk (od nowej linii)
 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14

Zauwa, e liczby jednocyfrowe s wydrukowane na DWCH miejscach, a nie na
dokadnie tylu, ile potrzebuj. To jest szeroko pola, w ktrym wpisane s te
liczby (przynajmniej ja to nazywam szerokoci pola). Pewnie si domylasz, e
gdybymy wpisali nie 2, a np. 3, to od przecinka do przecinka byoby trzy
miejsca. A jak ma si sprawa z liczbami, ktre si w naszym polu nie
zmieszcz? Czy zostan obcite z ktrej strony? Nie. Zostan wydrukowane w
zasadzie tak, jakby szeroko naszego pola nie bya podana, czyli od lewej do
prawej.

Teraz pewna sprawa z funkcj printf, ktrej zasadno objani pniej.
Spjrz na to:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
printf("Teraz krotki przyklad: %s, a tu dalszy tekst\n", "tu jest wstawienie");
return 0;
}
-------------------------------------------------------------------------------
Jak to wyjdzie na wydruku?
Teraz krotki przyklad: tu jest wstawienie, a tu dalszy tekst
Dziwactwo, nie? Przyjrzyjmy si temu z bliska:
Podajemy funkcji printf dwa parametry, oba to teksty. W pierwszym mamy tzw.
acuch formatujcy (tak samo si to nazywa w innych zastosowaniach tej
funkcji, ale mi to wczeniej umkno), w drugim tekst, ktry wstawimy
w odpowiednie miejsce. O tym, gdzie go wstawimy, mwi znaczek '%s' (s od
string, czyli po polskiemu acuch znakw). Na razie nie ma wikszego sensu
wstawia w ten sposb tekstu, atwiej po prostu wpisa ten brakujcy kawaek.
Ale co zrobi, gdy ten wanie kawaek moe si zmienia? Tu by si przyda
typ zmiennej przechowujcy tekst, a znaczek '%s' wanie ten typ wstawia.
A jeli chodzi o przechowywanie tekstu, to najpierw musisz si nieco
dowiedzie o tablicach, do czego niezwocznie przechodz.

* Jak utrudni sobie ycie, czyli kilka wiadomoci o tablicach

Mamy takie zadanie (nie wolno omija adnego warunku):
- program ma mie pi zmiennych typu int, indeksowanych od 0 do 4
- w kadej zmiennej ma mie podwojony numer indeksu
- ma wypisywa je wszystkie, od indeksu 0 do 4
A oto realizacja:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int a0, a1, a2, a3, a4;
a0 = 0; a1 = 2; a2 = 4; a3 = 6; a4 = 8;
printf("%d\n%d\n%d\n%d\n%d\n",
       a0, a1, a2, a3, a4);

return 0;
}
-------------------------------------------------------------------------------
Pamitaj o jednej rzeczy: to REDNIK koczy instrukcj, nie znak nowej linii!!
Kompilator nie widzi rnicy midzy "biaymi" znakami (tab, enter, spacja)!
Dlatego dozwolone jest napisanie w jednej linii kilku komend, oczywicie kada
zakoczona rednikiem. Dlatego rwnie mona zama wiersz przy pisaniu
funkcji (byle nie w rodku wyrazu, ale np. po przecinku czy zamiast spacji).
Wygodne, jeli parametry nie mieszcz si w linii (w przykadzie oczywicie
zmieciyby si, ale chodzio mi o to, eby pokaza, e takie co nie jest
bdem).
Teraz sowo o tym, co wypisze program. Wypisze liczby 0, 2, 4, 6 i 8, kada
w nowej linii. A teraz misja: napisz podobny program, ale wypisujcy 20
zmiennych! Masakra. Tu si przydadz tablice. Popatrz teraz na to:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int tablica[20];
int i;
for (i = 0; i < 20; i++)
  tablica[i] = i * 2;
for (i = 0; i < 20; i++)
  printf("%d\n", tablica[i] );

return 0;
}
-------------------------------------------------------------------------------
Prawda, e proste?
(Tym razem uyem zmiennej 'i', bo po pierwsze krcej si pisze, a po drugie
zmienna o nazwie 'licznik' zaciemniaby sposb odwoania do tablicy, co moesz
sam sprawdzi)
Deklaracja: deklarujemy co typu int, ma mie nazw 'tablica', ma by tablic
(znaki '[' i ']'), ma mie rozmiar 20. W tym miejscu musz zaznaczy, e
nieprzypadkowo w poprzednim zadaniu kazaem numerowa elementy od 0. W C
tablice s indeksowane wanie od ZERA, czyli ostatni indeks w naszej tablicy
ma warto 19!
Dobra, a tablicy uywa si tak, e w nawiasie klamrowym podajesz indeks
elementu, ktry chcesz uy. Poza tym, e wpisujesz ten indeks, to i-ty
element tablicy jest penoprawn liczb, nie trzeba adnych kombinacji na nim,
np. { tab[15] += 8; } jest analogiczne do { liczba += 8; }, a wyraenie
{ wynik = tab[15] * 2; } jest rwnowane wyraeniu { wynik = liczba * 2; },
oczywicie z dokadnoci do wartoci liczbowej samego wyraenia (w komrce
'tab[15]' moe by co innego, ni w zmiennej 'liczba').
To w zasadzie wszystko o tablicach w ogle. Zaraz przejdziemy do tablic
tekstowych (tablic typu char).

* acuchy w okowach... czy jako tak

W wielu jzykach programowania jest specjalny osobny rodzaj danych na tekst.
Jak si pewnie domylasz, w C czego takiego nie ma. Nie znaczy to oczywicie,
e nie da si pracowa na sporych ilociach tekstu. Po prostu, do takiej
zabawy trzeba troszk wicej praktyki. Zaczniemy moe od tego, e tekst mona
przechowywa w TABLICACH typu char. Ale zanim dojdziemy do takich dziwolgw,
to moe pewna krtka informacja: przy deklaracji zmiennej mona nada jej
warto (bo czemu nie?):

int liczba = 586;
liczba += 2;

Oczywicie w zmiennej 'liczba' bdzie warto 588 (586 + 2). Taka operacja
nazywa si inicjalizacj, cho sporo osb okrela to mianem inicjacja (ja
jestem osobicie za pierwsz wersj, do czego mnie przekonaa lektura sownika
jzyka polskiego; dalej w tekcie bd uywa tej wersji). Inicjalizacja to w
zasadzie nic innego, jak zapisanie w jednej linijce tego, co mona zapisa w
dwch, ale przy tablicach inicjalizacja ma pewne dodatkowe prawa. Moe poka
je na acuchu tekstowym:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{              /* 0123456789 123456789 1234 */
char tekst[25] = "Przypisujemy jakis tekst";
puts(tekst);
return 0;
}
-------------------------------------------------------------------------------
Deklarujemy tablic o rozmiarze o 1 wikszym, bo kady cig znakw, czyli nasz
tekst, musi w C koczy si znakiem o kodzie ASCII rwnym 0, oznaczajcym
koniec tekstu (uywany jest m.in. przez printf i puts). Oczywicie my tu nic
nie napisalimy, e chcemy taki znaczek, ale kompilator go wstawia za nas na
kocu kadego podwjnego cudzysowia.
Wrmy do przykadu. Funkcja puts wypisze nam dokadnie to, co jest w
podwjnym cudzysowiu. Natomiast taka operacja, e do tablicy wstawiamy
kawaek tekstu znakiem '=', jest moliwa TYLKO podczas inicjalizacji tej
tablicy, czyli w czasie jej deklaracji. Taka komenda ju nie bdzie poprawna:
{tekst = "Drugi tekst";} i oczywicie kompilator zgosi bd. Ale skoro to
tablica, to moemy zrobi inaczej, bdziemy kadej komrce tablicy przypisywa
odpowiedni liter:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
char tablica[6];
tablica[0] = 'T';
tablica[1] = 'e';
tablica[2] = 'k';
tablica[3] = 's';
tablica[4] = 't';
tablica[5] = 0;
puts(tablica);

return 0;
}
-------------------------------------------------------------------------------
Przypominam, e po pierwsze tablica jest numerowana od zera, czyli warto
ostatniego indeksu jest rwna wielkoci tablicy zmniejszonej o 1, a cay cig
znakw jest zakoczony znakiem o kodzie zero (inaczej liczb 0).
Funkcja puts wypisze na ekranie tekst 'Tekst'. Ale czy nie ma prostszej metody
na to, eby umieci tekst w tablicy? Jest. Wystarczy uy jednej
z bibliotecznych funkcji C:
-------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>

int main (void)
{
char tekst[50];
strcpy(tekst, "To sie powinno znalezc w tablicy 'tekst'");
puts(tekst);

return 0;
}
-------------------------------------------------------------------------------
Funkcja strcpy kopiuje znak po znaku z naszego cigu do tablicy. Jeli wanie
skopiowaa znak 0, to koczy swoje dziaanie (0 oznacza koniec testu).
Zwr jeszcze uwag na drug linijk: #include <string.h>
Niezbyt skomplikowane, nie? Oczywicie, w Basicu czy Pascalu s specjalne typy
zmiennych do przechowywania tekstu, ale C miao by jak najprostsze
w konstrukcji jzyka, czyli wycito takie typy, jak char odrniony od liczby
(wygodniej operowa ju na liczbie, ni specjalnie wywoywa osobn funkcj do
zmiany znaku na jego kod ASCII), czy te osobny typ zmiennej na cigi znakw
(w kocu tekst to uporzdkowany cig znakw, czyli do jego przechowywania
wietnie nadaje si zwyka tablica znakw). Co dziki temu zyskalimy my jako
programici? Na przykad takie co:
-------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>

int main (void)/*          0        10        20   30 */
{              /* 0123456789 123456789 123456789 123 */
char tekst[50] = "Przykladowy tekst, ktory zmienimy";
int i;
tekst[0] = 'p';
tekst[13] = 'T';
tekst[20] = 'K';
tekst[26] = 'Z';
puts(tekst); /* na ekranie: przykladowy Tekst, Ktory Zmienimy   */

for (i = 32; i >= 0; i--)
  putchar( tekst[i] );
putchar('\n');
/* na ekranie: ymineimZ yrotK ,tskeT ywodalkyzrp */

return 0;
}
-------------------------------------------------------------------------------
Jeszcze jedno, ktre pojawio si ze dwa przykady temu: tekst ograniczony
znaczkami /* i */ jest traktowany jako komentarz, czyli zupenie ignorowany
przez kompilator. Komentarz jest informacj TYLKO I WYCZNIE DLA PROGRAMISTY.
Kompilatora nie interesuje. Wewntrz komentarza moe si znale <Enter>,
kompilatorowi to wisi. Ignoruje cay tekst od '/*' a do '*/'. Jeli piszesz
jaki program, to czsto uywaj komentarzy, eby opisa, co robi i w jakim
celu dany kawaek kodu (zdarzyo mi si par razy, e zastanawiaem si,
co fragment napisany kilka dni wczeniej mia robi :))). Taki nadmiar
stukania w klawikatur naprawd potrafi zaoszczdzi sporo czasu.
Wrmy do przykadu: ptelka for wypisze nam cay tekst od tyu. Ale my znowu
jestemy leniwi, nie chemy liczy, ile ma nasz tekst liter. Jak w takiej
sytuacji dowiedzie si tego? Jest inna funkcja w bibliotece C:
-------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>

int main (void)
{
char tekst[50] = "Przykladowy tekst, ktory zmienimy";
int i = strlen(tekst) - 1;

for (; i >= 0; i--)
  putchar( tekst[i] );

putchar('\n');
  
return 0;
}
-------------------------------------------------------------------------------
Ale zamieszanie! Po kolei:
- inicjalizujemy tablic tekst
- inicjalizujemy zmienn 'i' tym, co policzy funkcja strlen zmniejszonym o 1
- w ptli for moemy opuci pocztkow cz administracyjn, czyli nadanie
  pocztkowej wartoci i (ju to wczeniej zrobilimy)
- wypisanie tekstu
- zejcie do nowej linii - dla ludzi pod Linuksem
W ptli for moemy opuci ktr z rzeczy w nawiasie, o ile oczywicie
zadbalimy o to, eby ptla si kiedy skoczya ;) Mona zatem zapisa
{for(;;);} (redniki w nawiasie MUSZ by oba!!!). Taka ptla nie robi nic,
jest to prawie najprostszy przykad ptli nieskoczonej.
W przykadzie moglimy zrobi jeszcze inaczej:

int i;
for (i = strlen(tekst) - 1; i >= 0; i--)
 ...

To jest z naszego punktu widzenia dokadnie to samo.

Czemu uyem {strlen(tekst) - 1} ? Bo funkcja policzy dugo acucha znakw,
dajc nam t wanie dugo. Ale my wiemy, e indeksy s numerowane od 0,
czyli ostatni nas interesujcy znak ma indeks (dugo - 1). Indeks zwrcony
przez strlen ma znak koca tekstu (o kodzie ASCII 0). Moesz to oczywicie
sprawdzi:
               /* 0123456789 1234567 */
char tekst[25] = "Przykladowy tekst";
printf("%i\n", (int)tekst[ strlen(tekst) ] );

Funkcja printf wypisuje kod liczbowy (czyli ASCII) znaku podanego dalej
(pamitasz konwersje?), a znak podany dalej to znak z tablicy 'tekst' majcy
indeks strlen(tekst). Mam nadziej, e jarzysz, o co biega. Na ten przykad,
ostatnia litera tego tekstu to { tekst[ strlen(tekst) - 1 ] }, przedostatnia
to { tekst[ strlen(tekst) - 2 ] } itd.

Ostatnie, co powiniene wiedzie o tekcie, to jak "pobra tekst z konsoli",
czyli jak przyj od uytkownika programu jaki tekst:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
char tekst[50];
puts("Wpisz teraz jakis tekst (do 50 znakow):");
gets(tekst);
printf("Wpisales \"%s\"\n", tekst);
/* ekran: Wpisales "..." */

return 0;
}
-------------------------------------------------------------------------------
Funkcja gets jest w pliku stdio.h, wic nie potrzebujemy pliku string.h.
Dlatego te nie wczylimy go tutaj do programu.
Na ekranie powinno pojawi si co jak w komentarzu, przy czym '...' oznacza
tekst wpisany przez uytkownika.
Funkcja gets pobiera od uytkownika jaki kawaek tekstu zakoczony klawiszem
<Enter>, wpisujc go do miejsca podanego przez parametr.
To jest cig znakw. A co, jeli chcemy liczb? Tu powinnimy uy innej
funkcji:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int liczba;
puts("Podaj jakas liczbe:");
scanf("%i", & liczba);
liczba += 10;
printf("Podales liczbe o 10 mniejsza od %i\n", liczba);

return 0;
}
-------------------------------------------------------------------------------
Funkcja scanf wyglda nieco dziwnie. Ta funkcja, podobnie jak printf, uywa
acucha formatujcego. Najczciej bdzie si ogranicza do dwch znakw: '%'
i litery (i trzeciego, koczcego acuch - przyzwyczajaj si do tego ;))).
Oczywicie jeli bdziesz chcia pobra od usera liczb rzeczywist (float),
to uyjesz odpowiedniej litery, znanej ci z funkcji printf (czyli tutaj 'f').
Podobnie, jeli chodzi o znak czy cig (acuch) znakw (odpowiednio 'c' i 's')
Wyglda bdzie to mniej wicej tak:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
float rzecz;
puts("Podaj dowolna liczbe rzeczywista:");
scanf("%f", & rzecz);
printf("Wpisales: %f\n", rzecz);
return 0;
}
-------------------------------------------------------------------------------
Do wytumaczenia zostao mi jeszcze jedno: czemu przed nazw zmiennej jest
znaczek. Ot funkcja ta, eby zapisa dane w naszej zmiennej, musi mie ten
znaczek (zwany "amperstand").
Zobaczmy, jak si pobiera tekst:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
char tekst[50];
puts("Podaj jakies slowo:");
scanf("%s", tekst);
printf("Wpisales: %s\n", & tekst);

return 0;
}
-------------------------------------------------------------------------------
Zauwa, e pobrany zostanie tylko kawaek przed pierwsz spacj. Funkcja scanf
nie jest zatem idealna do pobierania wikszych fragmentw tekstu, ale (w
odrnieniu od gets) nadaje si do pobrania liczb.

* Zagadka enigmy, czyli co znaczy znaczek & przy funkcji scanf

eby do tego doj, musimy najpierw wywiedzie si o wskanikach, czyli tym,
czym C i pochodne gruj nad innymi jzykami programowania.
Co to jest wskanik w rzeczywistym yciu? To co, co wskazuje. Podobnie jest
w C. Oczywicie nie mamy w komputerze adnego patyka, eby nim wskazywa. Mamy
natomiast liczby. Jak pewnie wiesz, kady bajt pamici operacyjnej (czyli RAMu)
ma przyporzdkowan liczb, mwic, ktry to bajt z kolei. To jest ADRES tej
komrki. Wskanik to w zasadzie te liczba, tyle e zawierajca ten wanie
adres. Drobna uwaga: przez cay ten "rozdzia" pamitaj, e wskanik to
wanie liczba. Z tego wynikaj pewne konsekwencje majce wpyw na uywanie
wskanikw.
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int liczba;
int *wskaznik_do_liczby;
wskaznik_do_liczby = & liczba;                                   /* 1 */
liczba = 10;

if (liczba == 10)
  puts("W zmiennej 'liczba' jest wartosc 10!");

if (*wskaznik_do_liczby == 10)                                   /* 2 */
  puts("W zmiennej wskazywanej przez wskaznik jest liczba 10!");

liczba = 15;
if (*wskaznik_do_liczby == 15)
  puts("W zmiennej wskazywanej przez wskaznik jest liczba 15!");

return 0;
}
-------------------------------------------------------------------------------
Czym si rni wskanik od zwykej zmiennej? Wskanik ma przed nazw gwiazdk.
Ta gwiazdka nie jest oczywicie czci nazwy, ale czasem si przydaje.
Jak ustawi wskanik, eby wskazywa na jak zmienn? Przykad jest w linijce
z numerem 1. Musi si pojawi amperstand ('&'). Oznacza pobranie adresu
zmiennej 'liczba'. Ten adres to jaka liczba, ktra zostanie wpisana do
wskanika (ktry TE jest liczb!). Teraz "wskanik wskazuje na zmienn", czyli
zawiera jej adres. Oczywicie z tego, e zawiera liczb bdc adresem,
jeszcze nic nie wynika. Gdybymy porwnywali w linijce z numerem 2 tak:

if (wskaznik_do_liczby == 10) ...

to instrukcja po 'if' wykonaaby si jedynie przy GIGANTYCZNYM szczciu.
Pamitaj, e wskanik to liczba z adresem zmiennej, czyli adres musiaby by
rwny 10 (co jest praktycznie niewykonalne). Musimy zatem poinformowa
kompilator, e nie chcemy porwnywa z dziesitk adresu, ale liczb spod tego
adresu. Do tego suy gwiazdka.
Teraz zmieniamy zawarto zmiennej 'liczba', na przykad na 15. Porwnujemy
to, co jest pod adresem ze wskanika z liczb 15. Jak mylisz, czy instrukcja
po 'if' si wykona? Pewnie, e tak! Po zmianie zmiennej 'liczba' zmieni si
jej zawarto, ale czemu miaby si zmienia adres? Czyli pod adresem, ktry
jest zapisany we wskaniku, jest dalej ta sama zmienna, ale z inn
zawartoci. Porwnanie tej wanie zawartoci z liczb 15 da wynik pozytywny
(dopiero co tam 15 wpisalimy). Przydugie i przynudne wyjanienie. Moe nowy
przykad:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int liczba = 10;
int * wsk;
wsk = & liczba;

*wsk = 25;
printf("Zawarto zmiennej 'liczba': %i\n", liczba);

return 0;
}
-------------------------------------------------------------------------------
Jak mylisz, co zostanie wypisane? 10? Nie. Zostanie wypisane 25. I znowu
przydugie wyjanienie. Nasz wsk wskazuje na zmienn liczba. Teraz zapisujemy
liczb 25 do tej komrki, ktra jest wskazywana przez wsk, czyli do zmiennej
liczba. Std wniosek, e warto zmiennej liczba zmieni si.
Oczywicie w naszym przypadku zapis {*wsk = 25;} jest rwnowany {liczba = 25;}
A teraz dwie zmienne i dwa wskaniki:
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int liczba1 = 10, liczba2 = 20;
int *wsk1, *wsk2;

wsk1 = &liczba1;
*wsk1 += 8;

wsk1 = &liczba2;
*wsk1 += 4;

wsk2 = wsk1;             /* 1 */
*wsk2 += 6;

printf("Wartosci zmiennych: %i, %i\n", liczba1, liczba2);

return 0;
}
-------------------------------------------------------------------------------
Chyba do jasny jest zapis funkcji printf ?
Program wypisze takye c:

Wartosci zmiennych: 18, 30

Chyba si domylasz, dlaczego.
Przed uyciem wskanika wsk2 (linia 1) komrki miay warto 18 i 24. Teraz do
wskanika wsk2 wpisujemy adres ze wskanika wsk1, czyli oba wskazuj na ten
sam element. Moemy oczywicie porwna dwa wskaniki:

if (wsk1 == wsk2) puts("Oba wskaniki wskazuj na ten sam element");

Taka instrukcja {if ... puts ...;} w naszym programie si wykona i wypisze
tekst.
Wskaniki mog rwnie dobrze wskazywa na komrki w tablicy:

int tablica[10];
int *wsk;
wsk = & ( tablica[4] );

Tutaj dla bezpieczestwa uyem nawiasw, a dlaczego, to wyjani si samo za
chwil.
Teraz wskanik 'wsk' wskazuje na (wskazywany element :))))))) pity element
(numerujemy od zera!) tablicy 'tablica'. A teraz pewien trick: jak najprociej
wskaza na pierwszy element tablicy? Ano tak:

wsk = tablica;  /* ?!? */

Ot tablica tak naprawd nie jest jakim specjalnym badziejstwem, ale wanie
wskanikiem do pierwszego elementu (czyli tego o indeksie 0). Z tego za
wynika, e mona zrobi takie cuda:

int tablica[10];
int *wsk;
wsk = tablica;
wsk[8] = 15;
printf("%i", tablica[8] );

co wypisze nam dopiero wstawion tam liczb 15. Mona te jeszcze inaczej (!):

int tablica[10];
*tablica = 100;

Wstawi nam to do elementu z indeksem 0 liczb 100.
Fajne, co?

Teraz jeszcze jedna fajna rzecz: tekst.
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{              /* 0123456789 123456789 123 */
char tekst[23] = "przykladowy test tekstu"
char *wsk;
wsk = & ( tekst[12] );

puts(wsk);

return 0;
}
-------------------------------------------------------------------------------
Skoro tablica jest wskanikiem, to funkcji puts (czy te printf) nie robi
rnicy, czy wadujemy wskanik, czy tablic. A teraz zagadka: co nam wypisze
puts?

test tekstu

A czemu? Bo odwouje si po kolei do wsk[0], wsk[1], wsk[2], ... wsk[n] itd.
dopki wsk[n] nie zawiera znaku koczcego (o kodzie 0). Czujesz?

A teraz pewien do czsto stosowany trick: skoro wskanik jest liczb, to
czemu nie doda do niego np. jedynki? Moemy i to zrobi!!!!
-------------------------------------------------------------------------------
#include <stdio.h>

int main (void)
{
int liczby[15], i;
int *wsk;
for (i = 0; i < 15; i++)        /* 1 */
  liczby[i] = 2 * i;
wsk = liczby;                   /* 2 */

wsk += 3;                       /* 3 */

/*******/                       /* 4 */

printf("%i\n", *wsk);

return 0;
}
-------------------------------------------------------------------------------
Par sw wyjanienia.
W linijce 1 i nastpnej mamy wpisanie do tablicy danych 0,2,4,6,...28,30 .
W linijce 2 ustawiamy wskanik wsk na pocztek tablicy. Zwyke czynnoci
administracyjne. cae pikno jzyka c mieci si w linijce 3: do wskanika wsk
dodajemy liczb 3! Co w tym piknego? Ano to, e teraz wsk wskazuje na element
tablicy 'liczby' z indeksem TRZY! Wypadaoby w tym miejscu zaznaczy, e
liczb dodajemy nie do zmiennej WSKAZYWANEJ przez wskanik, tylko wanie do
WSKANIKA (napisalimy nazw wskanika bez gwiazdki). Jeszcze jedno: waciwie
wskanik do liczby int nie moe wskazywa liczby float czy te znaku char.
Musi wskazywa liczb typu int (tyczy si to wszystkich rodzajw wskanikw).
A czemu to? Ot kady typ zmiennej ma inny rozmiar: char ma rozmiar 8b = 1B,
int ma wielko 32b = 4B, podobnie jak float (oczywicie na rnych typach
maszyn moe si to rni, ale na standardowym PC z procesorem
PENTIUM/CELERON/ATHLON/DURON bd to wanie takie wartoci). Liczba int musi
by zapisana w czterech bajtach w pamici, co oznacza, e w tablicy nastpna
liczba bdzie 4 bajty dalej. Wskanik wskazuje na pierwszy bajt w pamici,
wic tylko typ wskanika mwi nam, ile bajtw jest uytych do zapisania naszej
liczby. Podobnie dodawanie liczby do wskanika: jest ona najpierw pomnoona
przez rozmiar danego typu zmiennej, dopiero potem jest dodawana do adresu
ze wskanika. Dlatego wskaniki mog wskazywa tylko na dane swojego typu.
Ale do na tym. Ju pewnie wiesz, co napisa w linijce z numerem 4, eby
wskanik wskazywa na element z indeksem 8. Musisz wstawi tam {wsk += 5;},
bo ju wsk wskazuje na element 3, a element 8 jest 5 elementw dalej.
A teraz zagadka: jak przesun wskanik o 1 element? {wsk += 1;}, co nie? Ale
my chyba znamy krtszy zapis? {wsk++;} Prawda, e adny?

* Funkcjonowanie programu

Jeszcze nie napisaem o jednej rzeczy: o funkcjach. A to s ciekawe a wielce
przydatne rzeczy. Na szczcie nie ma tu nic specjalnego do powiedzenia.
Najpierw musi by deklaracja, potem definicja. Moe zademonstruj:
-------------------------------------------------------------------------------
#include <stdio.h>

void xxx(void)
{
puts("Wlasnie wywolales funkcje \"xxx\"");
// ekran: Wlasnie wywolales funkcje "xxx"
// gdy chcesz napisac na ekranie podwojny cudzyslow, poprzedzasz go znakiem \
}

int main (void)
{
xxx();

return 0;
}
-------------------------------------------------------------------------------
Odrobina wyjanie. Gdzie tu deklaracja? Od razu z definicj. Czsto to jest
wygodna praktyka. Funkcja nie jest szczeglnie interesujca, po prostu
wypisuje jaki bzdurny tekst na ekranie. Ale to w kocu pierwsza nasza funkcja
(no, druga - pierwsza bya main). Dwa void-y w "nagwku" mwi, e funkcja
nie zwraca nic i nic nie dostaje. Teraz przykad innych funkcji:
-------------------------------------------------------------------------------
int dodaj_5(int a)
{
return a + 5;
}

int dodaj_dwa_inty(int a, int b)
{
return a + b;
}

int potega(int podstawa, int wykladnik)
{
int i, wynik = 1;

for (i = 0; i < wykladnik; i++)
  wynik *= podstawa;

return wynik;
// ewentualne polecenia znajdujce si tutaj NIE ZOSTAN wykonane!!!
}
-------------------------------------------------------------------------------
Dwie pierwsze funkcje nie maj chyba nic trudnego. Nazwa trzeciej mwi sama za
siebie. Teraz co to jest to w okrgym nawiasie: to s parametry. Mog by
wszelkich moliwych typw (char, short, int, unsigned, float, a nawet
wskaniki). Instrukcja "retrun" kae wyj z funkcji i odda wynik jak stoi
dalej (znaczy za return-em). Parametry to takie specjalne zmienne, ktrych nie
trzeba deklarowa, samo ich wystpienie obok nazwy funkcji je deklaruje.
No to co innego:
-------------------------------------------------------------------------------
#include <stdio.h>

int dodaj_2(int a); // deklaracja funkcji

int main (void)
{
printf("7 + 2 = %i", dodaj_2(7) );

return 0;
}

int dodaj_2(int a)  // definicja funkcji
{
return a + 2;
}
-------------------------------------------------------------------------------
Najpierw funkcj zadeklarowalimy, pniej uylimy, a na kocu
zdefiniowalimy, co ma robi (wygodna praktyka, jeli funkcja ma znaczenie
poboczne, czyli jej wygld nas zupenie nie interesuje). Jak pewnie widzisz,
funkcji mona uy jak najzwyklejszej liczby. To te jest wygodna praktyka
(nie musisz deklarowa zmiennej na kady wynik). To by chyba byo na tyle,
jeli chodzi o funkcje.

* Nareszcie odpocz mona?

Niestety, nie mam adnego pomysu na to, co jeszcze mgbym dopisa do kursu,
eby nie by zbyt dugi, a eby mwi co jeszcze ciekawego (z zaoenia mia
by to koors krutki). Jeli dotrwae a do tego momentu i nie skasowae tego
pliku, to znaczy, e ju umiesz co nieco pisa w C. Brawo! Nauczye si
podstawowych podstaw w zaledwie kilka godzin! (tak, tak! spojrzyj na zegarek!)
Jeli natomiast jeszcze nie umiesz wszystkiego, co tu wpisaem, to si nie
przejmuj. Mdrzejsi od ciebie z lepszymi koorsami mczyli si duej z C. Nie
pozostaje mi ju nic innego, ni... zachci ci do przeczytania porzdnej
ksiki o C/C++. A dlaczemu? Bo ja tu napisaem tylko WPROWADZENIE,
w penoprawnym podrczniku jest znacznie wicej ciekawych sztuczek i komend
ni w moim skromnym koorsie. A programowanie naprawd moe si przyda!
Miej nauki!

                                                         Dozzie

