Programmeerimine keeles C++
Praktikum 1: Keele ja töövahendite tutvustus
Programmeerimiskeelest üldiselt
C++ on üldotstarbeline programmeerimiskeel, mida kasutatakse peamiselt süsteemse tarkvara arendamisel (operatsioonisüsteemid, draiverid, kontoritarkvara, mängud jne). C++ on kompileeritav keel, mis tähendab seda, et lähtekoodist tehakse käivitatav masinkood. See tagab programmidele suurema jõudluse, kuid tähendab paraku ka seda, et erinevatel platvormidel tuleb programmid uuesti kompileerida (nt Windowsi EXE-fail Linuxi all ei käivitu).
C++ sisaldab endas alamhulgana ka osa C-keelest, seega saab C programme üldiselt kompileerida ka C++ kompilaatoriga. C++ on muuhulgas ka üks Java esivanematest.
C++ keelt kontrollib standard ISO/IEC 14482, mis määrab nõuded liidestele, mida kompilaatorid peaksid järgima. Samas ei määra standard, kuidas keelekonstruktsioonid peavad realiseeritud olema. Materjali kirjutamise ajal oli C++ keele standardi värskeim versioon avaldatud 2017. aasta lõpus. C++ standardite jaoks on eraldi ISO alamkomitee.
Kompilaatorite tegijatel läheb tüüpiliselt veidi aega, enne kui nad standardi kõiki nõudeid toetama jõuavad hakata. Samas on mitmed kompilaatoritegijad juba mustandite põhjal funktsioonide realiseerimist alustanud ning levinumad kompilaatorid realiseerivad enamuse standardist.
Programmi struktuur ja kompileerimine
Lähtekood jaotub päisteks ja koodiks. C++ programmides peavad kõik kasutatavad elemendid
(muutujad, funktsioonid jne) olema eelnevalt defineeritud. Päised on mõeldud deklaratsioonide
(elementide olemasolu ja omaduste kirjelduste) kogumiseks, sinna üldjuhul käivituvat koodi ei
paigutata. Lähtekoodifailid peavad viitama päistele, kui soovivad kasutada viimaste poolt
deklareeritud muutujaid ja funktsioone. C++ puhul on levinuim faililaiend cpp
, kuid kasutatakse ka laiendit cc
. Päiste faililaiend on üldjuhul h
(näiteks: 'queue.h
').
Kompileerimine koosneb mitmest sammust. Lihtsustatud järjestus on järgmine:
- Iga koodifaili (
cpp
) jaoks:- Eeltöötlemise käigus kopeeritakse kasutatud päised koodi sisse.
- Kompileerimise käigus leitakse süntaksivead ning nende puudumisel genereeritakse objektfail, mis sisaldab antud lähtekoodi teisendust masinkoodi.
- Kõik saadud objektfailid „lingitakse“ (ühendatakse kokku) ning leitakse funktsioonide omavahelised sõltuvused. Linkimisvead „
undefined reference
“ tähendavad seda, et kompilaator nägi küll funktsiooni deklaratsiooni, kuid ei leidnud selle sisu ühestki objektfailist.
Töövahendid
Kõige tähtsam töövahend on kompilaator. Kompilaator sisaldab tavaliselt ka eeltöötlejat ja linkijat. Oma praktikumides kasutame me GCC (GNU C/C++ Compiler) ja clang kompilaatoreid. UNIX-il põhinevate OS-ide koosseisus on GCC vaikimisi olemas. Kompilaator clang on kasutusel Mac OS X, kus ta tarnitakse koos Xcode arenduskeskkonnaga. clang on kogumas populaarsust ka mujal tänu oma tugevamale koodianalüüsile ja selgematele veateadetele. Windowsi jaoks pakendavad GCC kompilaatorit näiteks projektid MinGW ja Cygwin. Mõlemal on oma head ja vead – MinGW on paremini ühilduv Windowsiga, Cygwin emuleerib paremini UNIX-tüüpi süsteeme. Selles aines oleme töötanud peamiselt MinGW süsteemiga.
GCC C++ kompilaatori käivitab käsk g++
. Clang C++ kompilaatori käivitab käsk clang++
.
Teine oluline töövahend C++ programmeerija jaoks on siluja. Siluja abil saab jälgida programmi
täitmist ning näha andmeid töö käigus. Seda kasutatakse vigade otsimiseks ning programmide
paremaks mõistmiseks. Klassikaline käsureal siluja on gdb. clang kompilaatoriga tuleb kaasa lldb.
Programmide dokumenteerimiseks kasutame süsteemi nimega Doxygen, mis lähtekoodi ja päistesse lisatud kommentaaride põhjal koostab programmi dokumentatsiooni. Doxygeni kasutamine koduülesannete esitamisel on kohustuslik.
Kompileerimise organiseerimiseks ja automatiseerimiseks kasutame tööriista nimega make, mis kuulub samuti UNIXi-põhiste süsteemide standardvahendite sekka. Make aitab paika panna sõltuvused päiste, lähtekoodi, objektfailide ja valmisprogrammi vahel. Igal käivitamisel töödeldakse kõik muudetud failid ja nendest sõltuvad failid.
Make'il on mitmeid konkurente ja alternatiive – autoconf, cmake, ninja, meson, scons, jam, Boost.Build jne. Mõned neist genereerivad projektifaile erinevatele töökeskkondadele (Integrated Development Environment, IDE) ja teised on make'i asendajad.
Üks väga oluline tööriist keerukamate süsteemide arendajale on Valgrind. Valgrind aitab leida vigu mälukasutuses ning leida keerukaid vigu ootamatult krahhivatest programmidest. Soovitan kindlasti see tööriist enda jaoks standardiks muuta.
Ära tasub märkida paljude seas näiteks need keskkonnad:
- Microsoft Visual C++ 2010 Express – kasutab Microsofti kompilaatorit. Siin aines ei kasuta, muidu kvaliteetne töökeskkond.
- Eclipse CDT – C++ laiendused Eclipse'i jaoks.
- Qt Creator – sobiv keskkond graafilise kasutajaliidese raamistikku Qt kasutavate rakenduste loomiseks, muidu ka hea mitmeplatvormne töövahend.
- Xcode – Mac OS X / iOS arenduskeskkond mitmete keelte (ka Objective-C) jaoks, kasutab clang kompilaatorit.
- Code::Blocks – gcc ja gdb toega töökeskkond Windowsi, Linuxi ja Mac OS X jaoks.
- KDevelop, Anjuta jne – vabalt levitatavad visuaalsed töökeskkonad, mis samuti saadaval erinevatele platvormidele.
- CLion - C ja C++ IDE JetBrains-ilt.
- Visual Studio Code - tekstiredaktor mille saab edukalt laiendada täisväärtustlikuks C++ arendusvahendiks.
- vim, emacs jne – töökeskkonnad tekstirežiimis (näiteks terminalil) töötamiseks.
Sinu (võib-olla) esimene C++-keelne programm
#include <iostream> /* Sisendi/väljundi päis */ #include <cstdlib> /* EXIT_SUCCESS konstant */ using namespace std; /* Nimeruumi valik (selgitatakse praktikumis) */ int main () { /* Peafunktsioon */ cout << "Hello, world!" << endl; /* Väljund (reavahetusega) */ return EXIT_SUCCESS; /* Funktsiooni väärtus */ }
Andmetüübid, muutujad ja massiivid
Kiire ülevaade põhilistest andmetüüpidest ja muutujate defineerimisest keeles C++.
Tüüp | Näide muutujast | Selgitus |
---|---|---|
char | char x = 7; char a = 'C'; | ühebaidine täisarv / tähemärk |
short | short port = 80; | vähemalt kahebaidine täisarv |
int | int i = 0; | vähemalt neljabaidine täisarv |
long | long id = 42534323; | vähemalt neljabaidine täisarv |
long long | long long h = 100ULL; | vähemalt kaheksabaidine täisarv |
float | float pi = 3.14159F; | ujukomaarv |
double | double delta = 0.000001; | topelttäpsusega ujukomaarv |
long double | long double d = 2.0e5; | maksimaalse täpsusega ujukomaarv |
bool | bool ok = false; | tõeväärtus |
Tüüp void
tähistab tüübi puudumist. Täisarvud on vaikimisi märgiga (näiteks char
väärtusvahemik on -128 kuni 127). Kui soovime ainult mittenegatiivseid arve, peame lisama tüübitäpsustuse unsigned
. Näiteks märgita kahebaidise täisarvu väärtusvahemik on 0 kuni 65535. Märgiga täisarv oleks vahemikus -32768 kuni 32767.
Märkus: char-tüübi puhul tasub arvestada, et väljastusel kirjutatakse ekraanile vastav tähemärk, mitte arv (näiteks char x = 65; cout << x; tulemusena väljastatakse täht 'A').
Massiivid C-s algavad indeksiga 0. Seega, kui defineerite massiivi, kus on 50 elementi, on nende indeksid vahemikus 0...49. Massiivid võivad olla mitmemõõtmelised. Mõõtmed on globaalsed üle massiivi: näiteks tabeli reas on alati nii mitu elementi kui massiivi definitsiooni järgi olema peaks.
Tähelepanu! C++ ei kontrolli massiivide indekseid, seega peab programmeerija seda ise tegema!
Eraldi käsitletakse char
-tüübi massiive (märgijadasid), mille abil saab esitada sõnesid.
NB! Kuna sõne lõppu tähistav nil-sümbol on ka märk, siis saab 100-elemendilise märgimassiivi sees talletada kuni 99-märgilise sõne. Sõnede töötlemiseks on C++ keeles ka lihtsam võimalus – klass string
, aga sellega tegeleme juba järgmistes praktikumides.
Definitsioon | Selgitus |
---|---|
int tripstrapstrull[3][3]; | 3x3 maatriks, indeksid [0][0] kuni [2][2] |
char puhver[100]; | kuni 99-märgine sõne või 100-märgine massiiv |
string nimekiri[30]; | 30-elemendiline sõnemassiiv |
double polynoom[16]; | 16-elemendiline massiiv ujukomaarvudest |
Alamprogrammid
Alamprogrammidel (funktsioon) peab olema määratud andmetüüp, et oleks teada, milliseid
väärtuseid see tagastab. Kui me ei soovi, et funktsioon väärtust tagastaks, määrame ta andmetüübiks void
. Argumentide nimekiri lisatakse funktsiooni nime järele sulgudesse.
Näiteid funktsioonidest on tootud alljärgnevas tabelis:
Funktsiooni deklaratsioon | Selgitus |
---|---|
long min (long a, long b); | kahe long tüüpi argumendiga funktsioon, mis tagastab long tüüpi väärtuse |
float calculate_square (float x); | float tüüpi argumendiga funktsioon, mis tagastab float tüüpi väärtuse |
int get_error_code (); | ilma argumentideta funktsioon, mis tagastab int tüüpi väärtuse |
void do_stuff (); | ilma argumentideta funktsioon, mis ei tagasta midagi |
int main(int argc, char *argv[]); | int ja massiivsõne tüüpi argumendiga funktsioon, mis tagastab int tüüpi väärtuse |
Mitme koodifailiga programmi näidis
Fail: functions.h
/* See funktsioon tagastab kahest sisendist väiksema. Päises on selle funktsiooni deklaratsioon. */ long minimum (long a, long b);
Fail: functions.cpp
/* See funktsioon tagastab kahest sisendist väiksema. CPP failis on funktsiooni töötav kood. */ #include "functions.h" /* Kui mõni funktsioon kasutab teist, peab teise */ /* deklaratsioon sellele eelnema. */ long minimum (long a, long b) { if (a > b) return b; else return a; }
Fail: main.cpp
/* See on peaprogramm. Töö algab funktsioonis 'main'. */ #include <iostream> /* Sisend ja väljund */ #include <cstdlib> /* EXIT_SUCCESS konstant */ #include "functions.h" /* Meie oma funktsioonid */ using namespace std; /* Nimeruumi valik (selgitatakse praktikumis) */ int main(int argc, char *argv[]) { cout << "min (35, 40) = " << minimum (35, 40) << endl; return EXIT_SUCCESS; }
Fail: Makefile
# To compile, run 'make' in the same directory as this file # Declare the name of our program (in Windows, the compiler adds .exe) PROGRAM = program # The needed object files (we make these from the source code) OBJ = main.o functions.o # Compiler flags (-Wall shows all warnings when compiling, always use this!) CFLAGS = -Wall # This is the first target. It will be built when you run 'make' or 'make all' all: $(PROGRAM) # Rule for linking IMPORTANT! The space in front of $(CXX) is a TABULATOR! $(PROGRAM): $(OBJ) $(CXX) $(OBJ) -o $(PROGRAM) # Rules for compiling main.o: main.cpp $(CXX) -c main.cpp -o main.o $(CFLAGS) functions.o: functions.cpp $(CXX) -c functions.cpp -o functions.o $(CFLAGS)
Käsureaparameetrite lugemine
Koduülesannete tegemiseks läheb vaja käsureaparameetrite lugemise oskust.
#include <iostream> /* Sisend-väljund */ #include <cstdlib> /* C-keele abiteek (arvude teisendamiseks) */ #include <cstring> /* strcmp funktsioon */ #include "functions.h" /* Meie oma funktsioonide teek */ using namespace std; int main(int argc, char *argv[]) { if (argc != 4) { /* Kontrollime, kas on piisavalt parameetreid */ cerr << "Viga: vale arv käsureaparameetreid!" << endl; return EXIT_FAILURE; } long a = atol (argv[1]); /* Teisenda tekst arvuks */ long b = atol (argv[2]); if (strcmp (argv[3], "minimum") == 0) cout << "min (" << a << ", " << b << ") = " << minimum (a, b) << endl; return EXIT_SUCCESS; }
Oluline on meelde jätta, et programmil on alati vähemalt üks käsureaparameeter. Esimeseks
parameetriks on tema nimi (antud juhul „program
“). Seepärast kontrollimegi toodud näites, et
parameetreid oleks täpselt 4 (kodutöö mõnes osas võib vaja olla hakkama saada ka teistsuguse arvu parameetritega).
C++ sisendi/väljundi kohta loe siit: http://www.cppreference.com/cppio/index.html
Funktsiooni atol
kirjeldus: http://www.cppreference.com/stdstring/atol.html
Sõnede võrdlemine: http://en.cppreference.com/w/c/string/byte/strcmp
Materjale
Viiteid materjalidele (raamatud ja veebilehed) leiate aine veebilehelt.