|
|
pl:dydaktyka:jimp2:2017:labs:wprowadzenie [2017/02/25 13:50] kkutt [Ćwiczenia] |
pl:dydaktyka:jimp2:2017:labs:wprowadzenie [2019/06/27 15:50] |
====== Wprowadzenie do programowania w C++ ====== | |
| |
===== IDE ===== | |
| |
Na zajęciach kod programów będzie pisany przy użyciu CLion IDE. IDE oprócz dostarczania podstawowego kolorowania składni i możliwości uruchamiania kodu, | |
dostarcza szereg przydatnych narzędzi, które znacznie potrafią przyspieszyć pisanie kodu. Są to między innymi: | |
* podpowiadanie składni (CTRL+SPACE) | |
* zautomatyzowanie nawigacji po projekcie (CTRL+B, CTRL+SHIFT+BACKSPACE, CTRL+ALT+HOME, CTRL+N, itd...) | |
* [[https://www.jetbrains.com/help/clion/2016.3/adding-deleting-and-moving-code-elements.html|łatwiejsze zarządzanie liniami kodu]] (TAB, SHIFT+TAB, CTRL+/, ALT+D, CTRL+Z, CTRL+Y, CTRL+D, CTRL+DEL, CTRL+BACKSPACE itd...) | |
* automatyczna generacja części kodu (ALT+INSERT, CTRL+SHIFT+D, ...) | |
* narzędzie do automatycznego formatowania kodu (CTRL+ALT+L) | |
* integracja z debuggerem | |
* integracja z systemem kontroli wersji git, | |
* integracja z narzędziem do automatycznego budowania kodu CMake | |
* integracja z frameworkiem do testów jednostkowych Google Test | |
* narzędzia do refaktoryzacji kodu, czyli poprawiania czytelności i jakości kodu bez modyfikacji jego zachowania | |
* i wiele innych | |
| |
W celu zmaksymalizowania produktywności należy zainstalować plugin [[https://plugins.jetbrains.com/clion/plugin/4455-key-promoter|Key promoter]] i wydrukować sobie [[https://resources.jetbrains.com/assets/products/clion/CLion_ReferenceCard.pdf|Clion Reference Card]]. | |
Również należy zainstalować sobie wersję CLiona na domowym sprzęcie należy skorzystać z darmowej [[https://www.jetbrains.com/student/|licencji studenckiej]]. | |
| |
===== CMake ===== | |
| |
Na zajęciach będziemy korzystać z systemu budującego CMake. W celu ułatwienia pracy | |
należy zapoznać się z elementami znajdującymi się w dokumencie [[https://www.jetbrains.com/help/clion/2016.3/quick-cmake-tutorial.html|CMake]], | |
w szczególności, | |
* jak ma nazywać się plik ze specyfikacją projektu w CMake :?: | |
* jak zdefiniować zmienną przechowującą nazwy plików cpp (z kodem źródłowym) | |
* jak dodać nowy cel (target) w postaci pliku wykonywalnego :?: | |
* jak dodać nowy cel (target) w postaci biblioteki :?: | |
* jak zdefiniować żeby stworzona wyżej biblioteka eksportowała pliki h (nagłówkowe) :?: | |
* jak w hierarchii projektu dołączyć podprojekt (podkatalog) z innym plikiem CMakeLists.txt :?: | |
* jak dołączyć bibliotekę do celu w postaci innego pliku wykonywalnego lub biblioteki :?: | |
Zagadnienia te będą obowiązywać na kolokwium :!: | |
| |
===== Git i automatyczne sprawdzanie zadań ===== | |
| |
Na zajęciach będziemy wykorzystywali system kontroli wersji git. | |
Pod [[https://gitlab.com/agh-jimp2/exercises|linkiem]] znajduje się adres repozytorium, gdzie zdefiniowane są testy do zadań | |
do laboratoriów. Każde laboratorium ma swój osobny katalog np. lab1, lab2, itd... w każdym laboratorium znajdują się podfoldery | |
dla każdego zadania, przykładowy factorial do pierwszego zadania z silnią już znajduje się repozytorium. Należy sklonować repozytorium | |
i zaimportować projekt do CLiona. | |
| |
Należy zapoznać się z plikami CMakeLists.txt w folderze lab1 i lab1/factorial. Polecenie **add_subdirectory_if_exists(reversestring)** nie jest | |
wbudowane w CMake, ale jest zdefiniowane w tym projekcie jako wariant **add_subdirectory**, ale nie powodujący błędu jeśli podkatalog | |
nie istnieje. Żeby rozwiązywać kolejne zadania należy nawiązać do konwencji nazwniczej użytej w projekcie tzn. kolejne foldery muszą się | |
nazywać tak samo jak to jest przedstawione w kolejnych deklaracjach add_subdirectory_if_exists. W przeciwnym wypadku podprojekt nie | |
będzie widoczny :!: | |
| |
W celu sklonowania repozytorium do lokalnego folderu wystarczy wpisać w linii komed polecenie (jako opcjonalny można podać ostatni argument | |
z nazwą folderu do którego ma zostać skolnowany projekt | |
<code bash> | |
git clone https://gitlab.com/agh-jimp2/exercises.git | |
</code> | |
| |
Należy sobie odświeżyć wiedzę na temat gita, w szczególności: | |
* git diff, | |
* git commit, | |
* git status, | |
* git add, | |
* git push, | |
* git pull, | |
* git log, | |
* git checkout | |
| |
Dostępny projekt jest wyposażony w automatyczne testy jednostkowe weryfikujące poprawność napisanego kodu. Same testy zostaną omówione pod koniec laboratoriów, | |
jednak teraz wystarczy wiedzieć, że są to mini programiki, których celem jest uruchomienie małego fragmentu kodu i sprawdzenie, czy zachowuje się on zgodnie z naszymi oczekiwaniami | |
i zaraportowanie użytkownikowi sytuacji gdy jest przeciwnie. | |
| |
Projekt został tak pomyślany, że każde zadania ma swoje osobne testy. Ogólna struktura kodu wygląda tak, że zdefiniowana jest biblioteka z kodem rozwiązania, która jest linkowana zarówno | |
do programu zdefiniowanego przez Ciebie, jak i mini programiku z testami. Stąd po naciśnięciu CTRL+F9 (Build) zostanie zbudowany projekt. Następnie można nacisnąć kombinację CTRL+ALT+F10 | |
żeby zobaczyć listę dostępnych celów (targets) zdefiniowanych w całym projekcie. Wśród nich powinny znajdować się następujące cele: | |
* factorial | |
* libfactorial | |
* lab1_factorial_tests | |
* lab1_all_tests | |
* i wiele innych | |
Cele zdefiniowane ze słowem tests powinny znajdować się w niższej sekcji i posiadać ikonę z czerwonym elementem. Są to zadania, które zostały rozpoznane przez IDE jako testy. | |
Wybranie zadania factorial uruchomi program z funkcją main zdefiniowaną w **lab1/factorial/main.cpp**. Natomiast uruchomienie zadania lab1_factorial_tests uruchomi testy jednostkowe | |
dotyczące zadania z silnią. Zadanie lab1_all_tests uruchamia wszystkie testy zdefiniowane dla laboratorium numer 1. W tym momencie jednak to zadanie nie powinno się dać skompilować | |
ponieważ brakuje kodu realizującego kolejne zadania. Uruchomienie libfactorial nie jest możliwe, dlatego, że jest to bilblioteka z kodem i nie ma punktu wejścia z funkcją main tak jak cały program. | |
| |
Uruchomienie zadania lab1_factorial_tests powinno zakończyć się jednak częściowym powodzeniem, tzn. część testów powinno przejść, a część nie. | |
{{ unit-test.png?600 |}} | |
Uzyskujemy informację, że z 16 testów wykonanych 12 zawiodło. Testy zostały zorganizowane w dwie grupy factorial_test i FactorialDataDriveTests, każdej z tych grup można się przyglądnąć osobno. | |
Wynik testu informuje nas, że na przykład uruchomienie factorial(5) powinno dać nam w rezultacie 120, ale otrzymany wynik to 0 :!: | |
W innym wypadku napisane jest, że oczekiwano p.second równego 1, ale uzyskany wynik factorial(p.first) był również równy 0 :!: | |
| |
Jeśli przeniesiemy się do definicji funkcji factorial (CTRL+SHIFT+ALT+N i wpisać początek nazwy factorial), a następnie naciśniemy (CTRL+B) by przeskoczyć do definicji funkcji wszystko będzie | |
jasne defnicja funkcji factorial jest następująca: | |
<code cpp> | |
int factorial(int value) { | |
return 0; | |
} | |
</code> | |
Nic dziwnego, że zawsze zwraca 0. | |
===== Typy ===== | |
Lista typów wbudowanych w standard C%%++%% | |
| |
^Nazwa^Opis^ | |
|char |Znak, albo mała liczba całkowita | | |
|short int \\ (short) |Mała liczba całkowita | | |
|int |Liczba całkowita | | |
|long int \\ (long)|Długa liczba całkowita | | |
|bool |Wartość **true** albo **false** | | |
|float | Liczba zmiennoprzecinkowa | | |
|double |Liczba zmiennoprzecinkowa podwójnej precyzji | | |
|long double |Duża liczba zmiennoprzecinkowa podwójnej precyzji | | |
|void | Pusty typ danych | | |
| |
Typy //char, short, int, long int// posiadają warianty **signed** i **unsigned**. | |
Standard nie gwarantuje rozmiaru typu i rozmiary podstawowych typów zmieniają | |
się w zależności dla jakiej architekturze jest kompilowany kod. | |
| |
Jeśli stawiamy pewne wymagania, np. chcemy przynajmniej 8 bajtową liczbę typu integer lepiej | |
skorzystać z typów zdefniowane w [[http://en.cppreference.com/w/cpp/header/cstdint|cstdint]]. | |
| |
Oprócz typów prymitywnych w C%%++%% można zdefiniować znacznie bogatszy system typów niż w C | |
w oparciu o klasy. Temat ten będzie zgłębiony na dalszych laboratoriach jednak w ćwiczeniach przyda się | |
znajomość klasy [[http://en.cppreference.com/w/cpp/string/basic_string|std::string]], przyglądnąć się jak | |
w dokumentacji przedstawione są metody znajdujące się w klasie string (własciwie to basic_string), np. | |
* c_str() | |
* length()/size() | |
* substr() | |
* find_first_of() | |
* replace() | |
===== Instrukcje sterujące ===== | |
W C%%++%% dostępne są następujące instrukcje sterujace: | |
* instrukcja warunkowa **if ... else ... ** | |
* instrukcja warunkowa **switch ... case** | |
* pętla **while** | |
* pętla **do ... while** | |
* pętla **for** | |
* **break** - przerywa aktualną pętlę | |
* **continue** - kontynuuje pętlę przechodząc do następnej iteracji | |
* **return** | |
===== Funkcje ===== | |
- Główną funkcją każdego programu jest funkcja //main()//. | |
* musi ona zwracać typ //int// | |
* może przyjmować dwa parametry: //int argc, char* argv[]//, oznaczające odpowiednio liczbę parametrów przekazanych do programu i listę tych parametrów. Zerowym parametrem jest zawsze nazwa programu. | |
- Pisanie funkcji można rozdzielić na dwie fazy: | |
* opcjonalna deklarację (tworzenie tzw. prototypu funkcji): <code cpp>double potega(double);</code> | |
* definicję: <code cpp> | |
double potega(double liczba){ | |
return liczba*liczba; | |
}</code> | |
- Domyślnym typem zwracanym (jeśli nie określono) jest //int//. | |
- Brak określenia typu zwracanego przez funkcję w jej definicji jest błędem składniowym jeśli prototyp określa typ zwracany inny niż int | |
- Zapomnienie o zwróceniu wartości z funkcji, która zadeklarowana została jako zwracająca wartość, jest błędem składniowym. | |
- Funkcje rozróżniane są po nazwie i liście parametrów, a nie po zwracanym typie. Dlatego nie można zdefiniować dwóch funkcji o takich samych nazwach i liście parametrów a innych zwracanych typach:<code cpp>// NIEPOPRAWNIE | |
void foo(int a, int b){...} | |
double foo(int c, int d){...} | |
</code> | |
| |
===== Tablice ===== | |
==== Tablice jednowymiarowe ==== | |
Tablice jednowymiarowe w C%%++%% można deklarować na cztery sposoby: | |
- Deklarując jedynie rozmiar tablicy, bez podawania jej elementów: <code cpp>int tab[100];</code> | |
- Deklarując rozmiar i podając elementy tablicy:<code cpp>int tab[5] = {1,2,3,4,5}; | |
</code> | |
- Deklarując rozmiar, ale nie podając wartości wszystkich elementów: <code cpp>int tab[10] = {1,2,3,4,5};</code> | |
- Podając listę elementów a nie podając rozmiaru tablicy:<code cpp>int tab[] = {1,2,3,4,5};</code> | |
| |
====Tablice wielowymiarowe==== | |
Tablice wielowymiarowe deklaruje się analogicznie do jednowymiarowych, jednakże podczas deklaracji tablicy wielowymiarowej konieczne jest podanie rozmiarów wymiarów, poza pierwszym: | |
<code cpp> | |
//Poprawnie | |
int tab[][4] = {{1,2,3,4},{5,6,7,8}}; | |
| |
//NIEPOPRAWNIE | |
int tab[][] = {{1,2,3,4},{5,6,7,8}}; | |
</code> | |
| |
| |
Definiując funkcję, której parametrem jest tablica wielowymiarowa, również konieczne jest podanie wszystkich rozmiarów wymiarów, poza pierwszym: | |
<code cpp> | |
void foo(int tab[][10]){ | |
... | |
} | |
</code> | |
| |
| |
===== Prosty program ===== | |
==== Przykład ==== | |
Program ma za zadanie wczytanie imienia i wyświetlenie powitania na ekranie. | |
<code cpp> | |
// W pliku o nazwie program.cpp | |
#include <iostream> // Dołączamy bibliotekę odpowiedzialna za | |
// operacje wejścia i wyjścia | |
| |
using namespace std; | |
| |
int main(int argv, char* arg[]){ | |
char imie[20]; | |
cout << "Podaj swoje imie: "; | |
cin >> imie; | |
cout << "Witaj " << imie << endl; | |
return 0; | |
} | |
</code> | |
==== Jak kompilujemy ==== | |
W konsoli <code>g++ program.cpp -o program</code> | |
| |
==== Omówienie ==== | |
Warto zauważyć: | |
* Nie jest wymagane podanie rozszerzenia biblioteki //iostream//. | |
* Biblioteka //iostream// jest ekwiwalentem znanej z C biblioteki //stdio//. C%%++%% wprowadza //strumienie// za pomocą których odbywają się wszystkie operacje wejścia/wyjścia. | |
* Przekierowanie danych do/ze strumienia możliwe jest dzięki operatorom //<<// oraz //>>//. Obiektem strumienia odpowiedzialnym za wyświetlanie na ekranie jest //cout// natomiast //cin// jest to strumień wejściowy. | |
* **using namespace std** określa, że korzystamy z przestrzeni nazw **std** z biblioteki //iostream//. | |
| |
====== Ćwiczenia ====== | |
- Skompiluj i uruchom przykład z sekcji [[#prosty_program_w_c|Prosty program w C++]] | |
- [1 plus] Napisz program obliczający silnię. Silnia powinna być obliczana przez funkcję <code cpp>int factorial(int);</code> Zdefiniowaną w katalogu factorial. Wykonaj dwie wersje funkcji: | |
* rekurencyjną | |
* iteracyjną | |
- [2 plusy] Wyświetlanie napisu wspak. Napisz funkcję która jako parametr będzie przyjmowała tablicę znaków i wyświetlała ją wspak. Funkcja powinna być rekurencyjna! Podpowiedź: przerwij rekurencję po napotkaniu zerowego znaku końcowego //\0//. | |
* Moduł: **reversestring** | |
* Pliki z implementacją: **ReverseString.h/cpp** | |
* Sygnatura metody: <code cpp>std::string reverse(std::string str)</code> | |
* Pliki nagłówkowe: <code cpp>#include <string></code> | |
* Wskazówki: <code cpp>const char *characters = str.c_str(); //uzyskanie z obiektu string wskaźnika na poszczególne znaki | |
size_t size = str.size(); //uzyskanie z obiektu string ilości znaków | |
//utworzenie nowego obiektu string na podstawie innego char*, char[], itp.. | |
return std::string(reversed_characters)</code> | |
* uruchomienie testów: ALT+SHIFT+F10 -> lab1_reverse_string_tests | |
- **[2 punkty]** Napisz funkcję palindrom, sprawdzającą czy podany jako parametr napis jest palindromem. Funkcja powinna zwracać **//true//** gdy napis jest palindromem, a **//false//** gdy nie jest. \\ Napisz proste menu posiadające dwie opcje: //Wyjście// i //Sprawdź palindrom//. Po wybraniu //Sprawdź palindrom// program powinien poprosić o wpisanie słowa a następnie sprawdzić i wyświetlić na ekranie czy podane słowo jest palindromem. Po wybraniu //Wyjście// program powinien kończyć działanie. | |
* Moduł: **palindrome** | |
* Pliki z implementacją: **Palindrome.h/cpp** | |
* Sygnatura metody: <code cpp>bool is_palindrome(std::string str);</code> | |
* Pliki nagłówkowe: <code cpp>#include <string></code> | |
* uruchomienie testów: ALT+SHIFT+F10 -> lab1_palindrome_tests | |
- [2 plusy] Napisz funkcję która będzie przyjmować tablicę dwuwymiarową o rozmiarach 10 na 10 i będzie uzupełniać ją iloczynami wierszy i kolumn, tworząc //tabliczkę mnożenia//. Napisz osobną funkcję do wyświetlania tej tablicy. | |
* Moduł: **multiplicationtable** | |
* Pliki z implementacją: **MultiplicationTable.h/cpp** | |
* Sygnatura metody: <code cpp>void MultiplicationTable(int tab[][10]);</code> | |
* uruchomienie testów: ALT+SHIFT+F10 -> lab1_multiplication_table_tests | |
- **[3 punkty]** [[http://projecteuler.net/index.php?section=problems&id=36|Palindromy liczbowe]] | |
* Moduł: **doublebasepalindromes** | |
* Pliki z implementacją: **DoubleBasePalindromes.h/cpp** | |
* Sygnatura metody: <code cpp>uint64_t DoubleBasePalindromes(int max_vaule_exculsive);</code> | |
* Pliki nagłówkowe: <code cpp>#include <cstdint></code> | |
* uruchomienie testów: ALT+SHIFT+F10 -> lab1_double_base_palindrome_tests | |
| |