kurs napisany przez monsttera, dobry poczatek

Transkrypt

kurs napisany przez monsttera, dobry poczatek
C++
Monstter
Spis treści
1 Podstawy
1.1 Pierwszy program . . . . . . . . . . . . .
1.2 Zmienne . . . . . . . . . . . . . . . . . .
1.2.1 Definiowanie . . . . . . . . . . .
1.2.2 Operacje na zmiennych . . . . .
1.2.3 Wyświetlanie wartości zmiennych
1.3 Instrukcje warunkowe . . . . . . . . . .
1.3.1 Ćwiczenia . . . . . . . . . . . . .
1.4 Pętle . . . . . . . . . . . . . . . . . . . .
1.4.1 Ćwiczenia . . . . . . . . . . . . .
1.5 Tablice . . . . . . . . . . . . . . . . . . .
1.5.1 Ćwiczenia . . . . . . . . . . . . .
1
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
i wczytywanie
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
danych
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
3
4
6
8
9
9
12
12
13
Rozdział 1
Podstawy
1.1
Pierwszy program
Mniej więcej tak może wyglądać najprostszy, coś-robiący program w C++:
001
002
003
004
005
006
007
008
#include <cstdio>
using namespace std;
int main()
{
printf(“Udziaba!nn“);
return 0;
}
Co oznaczają poszczególne linijki? Najważniejszym elementem, występującym w każdym programie napisanym w C++, jest funkcja int main(). Jest to miejsce, w którym zaczyna się
działanie naszego tworu. Para nawiasów { i } tworzy blok instrukcji, będący ciałem funkcji.
W tym przypadku blok ten składa się z dwóch instrukcji.
Pierwsza z nich wypisuje na ekran tekst "Udziaba!". \n to znak nowego wiersza (newline).
printf jest funkcją dającą nam możliwość wypisywania różnych rzeczy na konsolę, nazywaną
także standardowym wyjściem.
Funkcja printf ani inne, których będziemy używać w przyszłości, nie biorą się z powietrza.
Aby ich używać, trzeba dołączyć do programu odpowiedni nagłówek, informujący kompilator,
gdzie danej funkcji ma szukać. Jest za to odpowiedzialna linia #include <cstdio>. Użyta tutaj
biblioteka cstdio (C STanDard Input-Output) służy do zapisywania i odczytywania danych na/z
konsoli czy plików. C++ zawiera wiele innych, mniej bądź bardziej przydatnych bibliotek, ale
o nich później. Dokładne działanie linii using namespace std; nie jest teraz istotne, a ogólnie
rzecz w tym, żeby się kompilator nie czepiał. :)
Druga z linii naszej fukcji głównej kończy jej działanie (a tym samym działanie programu),
zwracając do systemu wartość 0, mówiącą, że wszystko jest w porządku. Oczywiście zwrócenie
zera nie wystarczy, aby program bezbłędnie wykonywał to, czego od niego oczekujemy.
1.2
Zmienne
Wypisywanie różnych rzeczy na ekran mamy już za sobą. Co dalej? Przydałoby się wczytywać
dane od użytkownika, aby można było na nie reagować. I tu pojawia się problem: jeśli będziemy
mieli jakieś dane, to trzeba je gdzieś trzymać. Do tego właśnie służą zmienne.
Komputer, operując na różnych informacjach, trzyma je w pamięci. Aby możliwe było odróżnianie od siebie poszczególnych obszarów pamięci, obszary te mają swoje ściśle określone adresy.
Używanie adresów w takiej postaci, w jakiej robi to komputer jest niewygodne. Tworząc zmienną, nazywamy pewien kawałek pamięci w sposób dla nas czytelny, na przykład wynik zamiast
2
0xA18BF20C. Określamy również, na co chcemy ją przeznaczyć – czy będzie to liczba, kawałek
tekstu, czy może jeszcze coś innego – czyli typ zmiennej. Dzięki temu kompilator może zadbać
o sprawy typu gdzie i ile tej pamięci potrzeba.
1.2.1
Definiowanie
Zanim zaczniemy używać zmiennej, musimy ją zdefiniować, czyli po prostu utworzyć. Zmienne
definiuje się, używając składni typ_zmiennej nazwa_zmiennej;. Można to zrobić w dowolnym
miejscu w programie, byle przed pierwszym jej użyciem. Nazwa może być dowolną kombinacją małych i wielkich liter, cyfr oraz podkreślników(_), pod warunkiem, że nie zaczyna się
od cyfry i nie jest słowem kluczowym C++. Za to typ_zmiennej musi być nazwą określonego
typu, będącego albo typem prostym (wbudowanym), albo złożonym (o złożonych typach danych
później).
Typy wbudowane w C++ przedstawia tabela 1.1. Domyślnie typy całkowitoliczbowe są
ze znakiem. Chcąc użyć wersji bez znaku, dopisujemy przed nazwą typu słowo unsigned. Taki
natłok typów zmiennych do przechowywania liczb może się wydawać niepotrzebny, przecież w
zmiennej typu long long można zmieścić wszystko to, co w mniejszych. Nie jest to jednak takie
bezsensowne. Jeśli program musi pamiętać 100000000 liczb dwucyfrowych, stosując zmienne typu
char zamiast long long, oszczędzamy ponad 600MB pamięci, a to nie jest mało. Na szczęście
najczęściej nie trzeba się tym przejmować i zwyczajny int świetnie się spisuje w większośći
zastosowań.
Nazwa
char
Rodzaj danych
znak lub liczba całkowita
short
liczba całkowita
int
liczba całkowita
long
liczba całkowita
long long
liczba całkowita
float
double
bool
liczba rzeczywista
liczba rzeczywista
wartość logiczna
Zakres
ze znakiem: -128..127
bez znaku: 0..255
ze znakiem: -32768..32767
bez znaku: 0..65535
ze znakiem: -2147483648..2147483647
bez znaku: 0..4294967295
jak int bądź long long,
w zależności od systemu
ze znakiem: 263 ::263 1
bez znaku: 0..264 1
( )3; 4 10( )38 , do 7 cyfr
( )1; 7 10( )308 , do 15 cyfr
true lub false (prawda/fałsz)
Tablica 1.1: Proste typy danych
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
//wszystko, co występuje po podwójnym ukośniku, do końca linii,
//to komentarz i jest ignorowane przez kompilator
int main()
{
int a; //zmienna całkowita a
int x, y, z; //możemy zdefiniować kilka zmiennych na raz
float f; //dla odmiany liczba rzeczywista
//możemy również od razu przypisać zmiennej wartość
double d = 2.4;
bool b = true;
unsigned long long ull; //duża liczba naturalna
//przypisanie można zrobić także po utworzeniu zmiennych
3
Rozmiar
1 bajt
2 bajty
4 bajty
4B lub 8B
8 bajtów
4 bajty
8 bajtów
1 bajt
016
017
018
019
020
021
022
023
024
025
026 }
1.2.2
a = 10; //podajemy już tylko nazwę, bez typu
f = 0.01;
//albo i kilka przypisań na raz
x = y = z = 1; //wszystkie trzy zmienne zostają ustawione na 1
//zamiast stałej wartości, przypisać można wartość innej zmiennej
float f2 = f; //teraz f2 ma wartość 0.01
char c = ’*’; //i na koniec znak
return 0;
Operacje na zmiennych
Zmienne nie byłyby zbyt przydatne, gdyby wszystko, co można z nimi robić, sprowadzało się
do definiowania i przypisywania wartości, nawet różnych typów. Co jeszcze da radę wykombinować? W zasadzie wszystko — działania matematyczne, bitowe, porównania, modyfikacje. . . Jak?
Za pomocą operatorów. Ich spis, według priorytetu, przedstawia tabela 1.2.
Operator przypisania zwraca przypisywaną wartość. Przypisania modyfikujące zwracają
wartość zmodyfikowanej zmiennej. Poniższy kod przedstawia różne przykłady użycia operatorów
i ich najrozmaitszych kombinacji. Uwaga na różnicę między zapisem porównania i przypisania.
Porównanie, podwójny znak równości, daje nam wartość logiczną, a przypisanie, pojedynczy
znak równości, zmienia wartość zmienej po lewej stronie i zwraca przypisywaną wartość. Błędy
wynikające z użycia niewłaściwego operatora mogą być dość trudne do wykrycia, więc warto
pamiętać, który jest który. W razie wątpliwości co do priorytetu operatorów, zawsze możemy
użyć nawiasów okrągłych (działają jak w matematyce — działania w nawiasach wykonywane są
najpierw). Chyba, że chcemy się zająć głównie odpluskwianiem kodu.
001 int main()
002 {
003
int r = 10;
004
float pi = 3.14;
005
float pole = pi*r*r;
006
007
double suma_wszystkiego = pole;
008
009
float obw = 2*pi*r;
010
011
suma_wszystkiego += obw;
012
013
--r;
014
int nowe_r;
015
bool b = r > (nowe_r = --r);
016
017
suma_wszystkiego += nowe_r;
018
019
int i=1, j=0, k=0;
020
j = i++;
021
k = ++i;
022
023
suma_wszystkiego += i+j+k;
024
025
return 0;
4
Kolejność
1
1
1
1
2
2
2
3
3
4
4
5
5
5
5
6
6
7
8
9
10
11
12
12
12
12
Symbol
++
-!
~
*
/
%
+
>>
<<
<
<=
>=
>
==
!=
&
^
|
&&
||
=
*=, /=, +=, -=, %=
>>=, <<=
|=, &=, ^=
Opis
inkrementacja(zwiększenie o 1)
dekrementacja(zmiejszenie o 1)
negacja logiczna
negacja bitowa
mnożenie
dzielenie
modulo(reszta z dzielenia)
dodawanie
odejmowanie
przesunięcie bitowe w prawo
przesunięcie bitowe w lewo
mniejszy niż. . .
mniejszy lub równy
większy lub równy
większy niż. . .
równy
różny
bitowa koniunkcja(AND)
bitowa różnica symetryczna(XOR)
bitowa alternatywa(OR)
logiczna koniunkcja(AND)
logiczna alternatywa(OR)
przypisanie
przypisanie z działaniem matematycznym
przypisanie z przesunięciem bitowym
przypisanie z działaniem bitowym
Tablica 1.2: Operatory
5
026 }
Należy zwrócić uwagę na dwie możliwości użycia operatora inkrementacji (to samo dotyczy też
dekrementacji). Od tego czy postawimy operator przed, czy po nazwie zmiennej, zależy jego
zachowanie. W pierwszej wersji wartość zmiennej zostanie najpierw zwiększona (zmniejszona),
a potem użyta. W wersji drugiej kolejność jest odwrotna. Ilustrują to linie 20. i 21. z przykładu.
Zmiennej j zostaje przypisana stara wartość i, czyli 1. Po jej wykorzystaniu, zmienna i
zostaje zwiększona. W następnej instrukcji najpierw jest inkrementowana zmienna i, a dopiero
później zmiennej k przypisywana jest nowa wartość, w tym wypadku 3. Jeśli używamy inkrementacji lub dekrementacji jako samodzielnej operacji (jak w linii 13.), nie ma znaczenia, który
sposób wybierzemy.
Zapisywanie wartości w kodzie
Możemy przypisywać zmiennym różne wartości i dane. A jak je zapisać, żeby były zrozumiałe
dla komputera?
Liczby całkowite dziesiętne zapisujemy, pisząc po prostu cyfry, np. -7, 1337. Kompilatory,
oprócz systemu dziesiętnego, rozpoznają także ósemkowy i szesnastkowy. W przypadku pierwszym poprzedzamy wprowadzaną wartość zerem (np. 017), w drugim znakami 0x(np. 0x17).
W przypadku liczb rzeczywistych możliwości są dwie. Jedna jest zwyczajnym zapisem
z kropką w roli separatora dziesiętnego (np. 3.14). Druga to zapis wykładniczy. Wygląda to tak:
5.1e2 i oznacza 5; 1 102 . Wykładnik musi być liczbą całkowitą (może być ujemny).
Stałe tekstowe piszemy w podwójnym cudzysłowie. Dla przykładu "Zuo i udziaba". Jeśli
chcemy wstawić znak \ lub ", poprzedzamy go backslashem, np. "to jest backslash: \\",
"to jest cudzysłów: \"". Za pomocą backslasha możemy również otrzymać znaki niedrukowalne
— nowy wiersz, powrót kursora do początku wiersza czy znak tabulacji (odpowiednio \n, \r i \t).
Zostały jeszcze pojedyncze znaki. Te zapisuje się między pojedynczymi apostrofami, np. ’a’.
Tu również można używać znaków niedrukowalnych za pomocą backslasha. Ale na białych
znakach jego możliwości się nie kończą. Wpisując ’\num’, gdzie num zastępujemy kodem ASCII
znaku, możemy uzyskać dowolny znak. Ta metoda działa także w tekście umieszczonym w podwójnym cudzysłowie. Tylko mała uwaga — kod znaku należy zapisać ósemkowo, ale bez poprzedzającego zera (np. ’\101’ dla A – komuś się musiało nudzić. . . ).
1.2.3
Wyświetlanie wartości zmiennych i wczytywanie danych
Poznana już funkcja printf ma pewną ciekawą właściwość. Mianowicie można za jej pomocą prezentować nie tylko wklepane “na twardo” w kod napisy, ale także wartości zmiennych.
Zeby to zrobić, trzeba w ciągu znaków, będącym pierwszym argumentem funkcji, umieścić specjalne symbole, które zostaną zastąpione wartościami zmiennych, przekazanych w kolejnych argumentach funkcji.
Najczęściej używane symbole, które można przekazać funkcji printf i odpowiadające im typy
danych przedstawia tabelka 1.3, a poniższy kod pokazuje przykłady użycia. Tablica znaków to
po prostu tekst między podwójnymi cudzysłowami.
Symbol
%d
%lld
%u
%llu
%c
%s
%f
Typ danych
int
long long int
unsigned int
unsigned long long
char
tablica znaków
float
Tablica 1.3: Symbole funkcji printf i scanf
6
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
#include <cstdio>
using namespace std;
int main()
{
int a = 10;
int b = 20;
char c = ’v’;
float e = 2.718;
printf(“%d“, a);
printf(“%d“, b);
//wszystkie znaki poza %coś są wyświetlane bez zmian
printf(“nn%cnn“, c);
// %% wyświetla znak %
printf(“Procent: %%nn“);
//ta linia spowodowałaby błąd wykonania - zbyt mało argumentów
//printf(“%dnn“);
//można też podać konkretną wartość bądź działanie,
//nie tylko zmienną
printf(“e=%f, %d*a*b=%dnn“, e, 3, 3*a*b);
return 0;
}
Wykonanie programu powinno dać taki oto efekt:
1020
v
Procent: %
e=2.718000, 3*a*b=600
Tych samych symboli, co printf używa także funkcja scanf, służąca do wczytywania danych
z konsoli do zmiennych. Obie funkcje są bardzo podobne do siebie w użyciu, z dwiema różnicami.
Używając scanf zawsze podajemy symbole i zmienne, do których wczytujemy (przy printf
mogliśmy podać sam kawałek tekstu). Poza tym, podając nazwę zmiennej, do której chcemy
zapakować dane, trzeba ją poprzedzić znakiem &.
001
002
003
004
005
006
007
008
009
010
011
012
013
#include <cstdio>
using namespace std;
int main()
{
printf(“Masz do wyboru opcje:nn“);
printf(“Opcja 1.nnOpcja 2.nnOpcja 3.nn“);
printf(“Co wybierasz? “);
unsigned wybor;
scanf(“%u“, &wybor);
printf(“Twój wybór to %u.nn“, wybor);
return 0;
}
Powyższy kod nie robi w zasadzie niczego konkretnego, więcej kodów z wykorzystaniem funkcji
scanf w następnych podrozdziałach. Tym, co ten kod robi, jest danie użytkownikowi 3 opcji
7
do wyboru. A co jeśli użtkownik wybierze na przykład nieistniejącą opcję czwartą? Żeby móc
zareagować na taką sytuację, potrzebne będą instrukcje warunkowe.
1.3
Instrukcje warunkowe
Ogólnie rzecz biorąc, instrukcje warunkowe umożliwiają różne zachowanie programu, w zależności
od. . . praktycznie wszystkiego. Począwszy od danych wejściowych, przez wyniki wykonanych
obliczeń, na wersji systemu operacyjnego kończąc.
Poniższy pseudokod pokazuje, jak takie instrukcje mogą wyglądać. Jeśli warunek jest prawdziwy,
czyli ma wartość logiczną true, instrukcja (lub blok instrukcji w nawiasach klamrowych) po
słowie kluczowym if jest wykonywana. Jeśli występuje słowo kluczowe else, instrukcja (bądź
blok) po nim zawarta będzie wykonana, kiedy warunek będzie fałszywy (false).
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
//instrukcja warunkowa może mieć dwie formy:
//jeśli, to
if(warunek)
instrukcja;
if(warunek) {
instrukcje;
jeszcze trochę instrukcji;
}
//jeśli, to, a jak nie, to coś innego
if(warunek)
instrukcja;
else
instrukcja alternatywna;
if(warunek) {
instrukcje;
} else {
inne instrukcje;
}
//można również zagnieżdżać warunki czy tworzyć ich ciągi
if(warunek1) {
jakieś instrukcje;//wykonywane, gdy warunek1 jest prawdziwy
if(warunek2)
inna instrukcja;//gdy warunki 1 i 2 są prawdziwe
if(warunek3)
jeszcze inna;//1 i 3 są prawdziwe
else
kolejna możliwość;//warunek1 prawdziwy, 3 nie
następne instrukcje;//warunek1 prawdziwy
} else if(warunek4) {
instrukcje;//warunek1 nieprawdziwy, 4 prawdziwy
} else {
jeszcze jeden zestaw instrukcji;//nieprawdziwe warunki 1 i 4
}
Nie ma limitu zagnieżdżania warunków, możemy zastosować ich tyle, ile potrzebujemy. Wewnątrz
8
nawiasów klamrowych można wpisać dowolnie wiele instrukcji, bez nich słowa kluczowe if i else
dotyczą tylko jednej instrukcji. Kiedy chcemy użyć słowa else, musi ono wystąpić od razu
po instrukji (instrukcjach) należącej do słowa if.
A jak formułować warunki? Warunkiem może być każde wyrażenie, dające wartość logiczną
(lub liczbę — 0 jest traktowane jak fałsz, każda inna jako prawda). Od najprostszych, np.
a==10, do skomplikowanych, np. ((a < b) && (b == --r)) != c (oczywiście zmienne a, b i r
muszą być liczbami, a c zmienną logiczną, żeby takie wyrażnie miało sens). W warunkach można
używać zmiennych, operatorów i stałych wartości wpisanych w kod (a także funkcji oraz stałych,
o których potem).
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
#include <cstdio>
using namespace std;
int main()
{
printf(“Masz do wyboru opcje:nn“);
printf(“Opcja 1.nnOpcja 2.nnOpcja 3.nn“);
printf(“Co wybierasz? “);
unsigned wybor;
scanf(“%u“, &wybor);
if(wybor < 1 || wybor > 3) {
printf(“Nie ma takiej opcji!nn“);
printf(“Wybierz jeszcze raz: nn“);
scanf(“%u“, &wybor);
if(wybor < 1 || wybor > 3)
printf(“Znowu wybierasz nieistniejącą opcję.nn“);
else
printf(“Teraz lepiej. “);
}
printf(“Twój wybór to %u.nn“, wybor);
return 0;
}
1.3.1
Ćwiczenia
1. Napisz program, który pyta użytkownika o trzy liczby i wypisuje największą.
2. Napisz program wczytujący liczbę i wyświetlający czy jest ona podzielna przez 2, 3 i 5.
1.4
Pętle
Powiedzmy, że chcemy wyświetlić tyle gwizdek, ile poda użytkownik. Poznane do tej pory elementy języka C++ wystarczają do napisania takiego programu, jednak nie byłoby to najwygodniejsze. Spójrzmy na kod:
001
002
003
004
005
006
007
008
#include <cstdio>
using namespace std;
int main()
{
printf(“Podaj liczbę z przedziału 0..5: “);
int n;
scanf(“%d“, &n);
9
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024 }
if(n>5 || n<0) {
printf(“Liczba spoza przedziału.nn“);
return 0;
}
if(0 < n--)
printf(“*nn“);
if(0 < n--)
printf(“*nn“);
if(0 < n--)
printf(“*nn“);
if(0 < n--)
printf(“*nn“);
if(0 < n)
printf(“*nn“);
return 0;
Jeśli chcielibyśmy, żeby maksimum gwiazdek było większe, należałoby dodać kolejne warunki.
Pisanie wielokrotnie tych samych instrukcji nie jest najlepszym pomysłem, zwłaszcza jeśli potrzebujemy wykonać coś nie 10 czy 20 razy, a na przykład 20000 razy.
I tu przychodzą z pomocą pętle. Umożliwiają one w prosty sposób powtarzanie pewnego
bloku instrukcji. W C++ występują trzy typy pętli: for (wykonuje instrukcje określoną ilość
razy), while i do-while (wykonujące polecenia dopóki dany warunek jest prawdziwy).
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
//najczęściej stosowana forma pętli for wygląda tak
for(int i=0; i!=liczba_powtórzeń; ++i) {
instrukcje;
}
//chociaż pętla ta jest bardzo elastyczna i można jej używać na wiele
różnych sposobów
for(inicjalizacja; warunek; inkrementacja)
instrukcja;
//przed pierwszym wykonaniem pętli wykonywana jest inicjalizacja
//przed każdym wykonaniem pętli (zwanym iteracją) sprawdzany jest warunek
//i jeśli jest on fałszywy, pętla zostaje przerwana
//po każdej iteracji następuje inkrementacja, która de facto
//może być jakąkolwiek instrukcją, nie koniecznie inkrementacją
//pętle while i do-while
while(warunek)
instrukcja;
do {
instrukcje;
} while(warunek);
//różnią się tym, że pętla while sprawdza warunek
//przed wykonaniem instrukcji, a pętla do while po
//gdy warunek jest fałszywy, pętla nie jest więcej powtarzana
Warunki w pętlach rządzą się takimi samymi prawami jak te z instrukcji warunkowych. Pętle for
i while sprawdzają warunek przed wykonaniem, co oznacza, że jeśli warunek będzie od początku
fałszywy, polecenia zawarte w pętli nie zostaną wykonane ani razu. W przypadku do-while
10
zawsze zostanie wykonana przynajmniej jedna iteracja. Część inicjalizacyjną pętli for na ogół
wykorzystuje się do zdefiniowania zmiennej licznikowej (najczęściej typu int) i przypisania jej
wartości początkowej.
Pętle można zagnieżdżać bez ograniczeń, podobnie jak instrukcje warunkowe. Powinniśmy
przy tym uważać, żeby nie stworzyć zbyt powolnego programu. Na przykład, jeśli umieścilibyśmy
jedna w drugiej 4 pętle, z których każda wykonywałaby się 1000 razy, zawartosć tej najbardziej
zagnieżdżonej zostałaby powtórzona aż bilion razy.
Z zastosowaniem pętli, program wyświetlający gwiazdki skraca się:
001
002
003
004
005
006
007
008
009
010
011
012
#include <cstdio>
using namespace std;
int main()
{
printf(“Podaj liczbę gwiazdek: “);
int n;
scanf(“%d“, &n);
for(int i=0; i!=n; ++i)
printf(“*nn“);
return 0;
}
Poniższy kod używa pętli do-while do odpytywania użytkownika o liczbę, póki ten nie poda
właściwej. Używamy funkcji generującej liczby losowe, żeby nie pytać o tę samą liczbę przy
każdym uruchomieniu programu. Funkcje rand i srand są zadeklarowane w nagłówku cstdlib,
więc go dołączamy.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
#include <cstdio>
#include <cstdlib>//zawiera między innymi rand() i srand()
using namespace std;
int main()
{
int zarodek;
printf(“Podaj dowolną liczbę: “);
scanf(“%d“, &zarodek);
srand(zarodek);//inicjalizuje generator liczb losowych
int liczba, strzal;
liczba = rand() % 1000;//liczba losowa z przediału <0,999>
do {
printf(“Zgadnij szukaną liczbę: “);
scanf(“%d“, &strzal);
if(strzal < liczba)
printf(“Więcejnn“);
else if(strzal > liczba)
printf(“Mniejnn“);
} while(liczba != strzal);
printf(“Gratulacjenn“);
return 0;
}
11
1.4.1
Ćwiczenia
1. Napisz program wczytujący liczby naturalne i podający n-tą liczbę nieparzystą (np. jeśli
wczytamy 2, wypisujemy 3). Ostatnią liczbą na wejściu będzie 0.
2. Napisz program wczytujący liczbę testów, a dla każdego testu liczby N , M , D oraz N
kolejnych liczb całkowitych L0 ; : : : ; LN 1 i wypisujący, ile spośród liczb L0 ; : : : ; LN 1 jest
jednocześnie mniejszych od M oraz podzielnych przez D.
1.5
Tablice
Często musimy przechowywać wiele danych tego samego typu. Aby nie trzeba było tworzyć
mnóstwa pojedynczych zmiennych, język C++ pozwala na używanie tablic, czyli zestawów zmiennych tego samego typu. Tablice deklaruje się bardzo podobnie do zwyczajnych zmiennych:
typ_danych nazwa_tablicy[rozmiar];. Rozmiar określa ilość elementów tworzonej tablicy.
Do konkretnych elementów odwołujemy się, używając składni nazwa_tablicy[indeks_elementu].
Indeksowanie tablic w C++ zaczyna się od 0, tak więc 10-elementowa tablica ma indeksy od 0
do 9. W n-elementowej tablicy nigdy nie występuje element o indeksie n! Każdego elementu
możemy używać jak osobnej zmiennej, jednak tablice dają coś więcej, niż tylko łatwość definiowania wielu zmiennych jedną linią kodu. W połączeniu z poznanymi już pętlami, tablice
pozwalają bardzo łatwo operować na dużych ilościach danych. Spójrzmy na przykłady:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
#include <cstdio>
using namespace std;
int main()
{
int tab[3];
tab[0] = 5;
tab[1] = 10;
tab[2] = tab[0] + tab[1];
float arr[6];
arr[0] = 3.14;
arr[1] = 2.718;
arr[2] = 1.0;
arr[3] *= arr[0] * arr[1];
//ponieważ tab[0] ma wartość 5, zapis ten znaczy tyle co arr[5] = 13.
37;
arr[ tab[0] ] = 13.37;
int nat[10];
for(int i=0; i!=10; ++i)
nat[i]=i;
for(int i=0; i!=10; ++i)
printf(“%d “, nat[i]);
printf(“nnnn“);
int m=4, n=5;
//tablice mogą być wielowymiarowe, na przykład
//ta tablica ma 4 wiersze, po 5 pól w każdym
int kw[m][n];
for(int i=0; i!=m; ++i) {
for(int j=0; j!=n; ++j) {
12
033
034
035
036
037
038
039
040 }
kw[i][j] = i*j;
printf(“%d “, kw[i][j]);
}
printf(“nn“);
}
return 0;
Wykonanie tego kodu powinno dać wynik jak poniżej:
0 1 2 3 4 5 6 7 8 9
0
0
0
0
0
1
2
3
0
2
4
6
0
3
6
9
0
4
8
12
A ten kod wypisuje podane liczby w odwrotnej kolejności:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
#include <cstdio>
using namespace std;
int main()
{
int rozmiar;
printf(“Podaj, ile liczb chcesz wpisać: “);
scanf(“%d“, &rozmiar);
if(rozmiar > 10 || rozmiar < 1) {
printf(“Bez przesady...nn“);
return 0;
}
printf(“Podaj liczby:nn“);
int tab[rozmiar];
for(int i=0; i!=rozmiar; ++i)
scanf(“%d“, &tab[i]);
printf(“Wpisane liczby to:nn“);
for(int i=0; i!=rozmiar; ++i)
printf(“%d “, tab[rozmiar-i-1]);
printf(“nn“);
return 0;
}
1.5.1
Ćwiczenia
1. Napisz program wczytujący liczby, a następnie sumujący je parami, pierwsza z ostatnią,
druga z przedostatnią itd. Jeśli zostanie jedna bez pary, podnieś ją do kwadratu. Wyniki
wypisz wiersz po wierszu.
13

Podobne dokumenty