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