Programovanie (1) v C/C++
1-INF-127, ZS 2024/25
Prednáška 2
Obsah
Oznamy
Opakovanie ako fungujú cvičenia:
- Na začiatku cvičení v utorok sa na testovači objaví nová sada úloh.
- Prvá úloha je rozcvička, treba ju odovzdať do konca cvičenia, začnite teda touto úlohou.
- Zvyšok cvičenia riešte ďalšie úlohy.
- Čo nestihnete na cvičení, môžete dokončiť doma alebo na doplnkových cvičeniach v piatok, termín odovzdania je ďalší pondelok večer 22:00.
- Úlohy sa dajú odovzdávať aj po termíne, ale nedostanete za ne body, ak sme vám neudelili výnimku.
- Ak vám testovač k príkladu vypíše zelené OK, prešiel testami. Ak vypíše oranžový kód chyby, niečo je zle, treba opraviť a odovzdať znovu.
- Body za prvé cvičenia sa objavia na testovači koncom budúceho týždňa, ale zväčša dostanete body za všetky príklady, kde testovač dal OK.
Najbližšie dni na programovaní
- Dnes prednáška, ďalšie dve prednášky budúci týždeň
- Tento piatok budú namiesto doplnkových cvičení iba nepovinné konzultácie v miestnosti M-217 od 13:10. Príďte ak máte otázky k učivu alebo problémy s inštaláciou softvéru na váš notebook.
- Počas doplnkových cvičení nepovinný test pre pokročilých v posluchárni B (ak ste sa prihlásili).
- Budúci utorok ďalšie cvičenia s ďalšou sadou úloh (k prednáške dnes a v pondelok).
Technické záležitosti
- Na stránke Softvér nájdete príklady ďalších programov, ktoré môžete použiť namiesto Kate.
- Ak sa vám páči Kate, prečítajte si časť o Kate a práci na príkazovom riadku. Vo Windows si môžete nainštalovať podobný editor, napr. Notepad++.
- Ak by ste chceli viac klikacie prostredie, môžete skúsiť napríklad programy Netbeans alebo VS Code, dajú sa použiť aj na skúške a dajú sa nainštalovať aj vo Windows.
- Na svojom počítači máte širokú paletu možností, v čom programovať, aspoň občas ale skúste použiť Linux na cvičeniach, aby ste vedeli robiť skúšku (Kate alebo Netbeans alebo iné nástroje, nie však internetové prostredia).
Ďalšie upozornenia k štúdiu:
- V rozvrhu http://candle.fmph.uniba.sk/ vidíte všetky predmety, ktoré sú povinné, povinne voliteľné alebo výberové pre váš ročník, ale nie všetky si musíte zapísať a naopak, môžete si zapísať aj iné predmety.
- Dôležité je skontrolovať si, či to máte zapísané v AIS, sedí s tým, na čo aj reálne chodíte.
- Prvé dva týždne (do 1.10.) si môžete v AIS pridávať a uberať predmety, potom treba kontaktovať študijné emailom, aby vám uzatvorili zápisný list.
- Prvácke povinné predmety by ste si mali zapísať, z výberových si môžete vybrať.
- Pozor na to, aby ste získali aspoň 20 kreditov za zimný semester.
Opakovanie
Doteraz sme videli:
- Načítavanie pomocou cin, výpis pomocou cout.
- Celočíselné premenné typu int.
#include <iostream>
using namespace std;
int main() {
int x, y;
cout << "Please enter the first number: ";
cin >> x;
cout << "Please enter the second number: ";
cin >> y;
int result = x + y;
cout << x << "+" << y << "=" << result << endl;
}
Komentáre
Do zdrojových kódov programov v jazykoch C a C++ je možné pridávať komentáre, čo sú časti kódu ignorované kompilátorom.
- Komentár je časť programu začínajúca /* a končiaca */ (aj cez viac riadkov)
- Komentár je aj text od // až po koniec riadku. To je užitočné na písanie krátkych komentárov.
Do komentárov sa zvyknú písať poznámky k významu okolitých príkazov, čo zlepšuje orientáciu v kóde a jeho pochopenie inými programátormi.
Príklad:
#include <iostream>
using namespace std;
/* Tento program od pouzivatela nacita dve cele cisla
* a vypise ich sucet. */
int main() {
// vytvorime premenne x a y
int x, y;
// do premennych od pouzivatela nacitame cisla
cout << "Please enter the first number: ";
cin >> x;
cout << "Please enter the second number: ";
cin >> y;
// do novej premennej result spocitame vysledok
int result = x + y;
// vysledok vypiseme
cout << x << "+" << y << "=" << result << endl;
}
Podmienka (if)
Niekedy chceme vykonať určité príkazy len ak sú splnené nejaké podmienky. To nám umožňuje príkaz if.
- Nasledujúci program si vypýta od používateľa číslo a vypíše, či je toto číslo záporné alebo nezáporné.
#include <iostream>
using namespace std;
int main() {
int x;
cout << "Zadajte cislo: ";
cin >> x;
if (x < 0) {
cout << "Cislo " << x << " je zaporne." << endl;
} else {
cout << "Cislo " << x << " je nezaporne." << endl;
}
}
- Tu je príklad dvoch behov programu:
Zadajte cislo: 10 Cislo 10 je nezaporne.
Zadajte cislo: -3 Cislo -3 je zaporne.
- Ako vidíme, za príkazom if je zátvorka s podmienkou. V našom príklade podmienka je x < 0.
- Ak je podmienka v zátvorke splnená (t.j. ak x je menšie ako nula), vykonáme príkazy v zloženej zátvorke za príkazom if.
- Ak podmienka nie je splnená (t.j. ak je x väčšie alebo rovné nule), vykonáme príkazy v zloženej zátvorke za slovom else
- Časť else {...} je možné vynechať, ak nechceme vykonávať žiadne príkazy.
- Ak za if alebo else nasleduje iba jeden príkaz, zátvorky { a } môžeme vynechať. To však ľahko vedie k chybám, preto je lepšie ich vždy použiť.
Cvičenie:
- Pomocou podmienky vypíšte absolútnu hodnotu načítaného čísla.
- Namiesto vypísania uložte túto hodnotu do premennej y, ktorá by sa dala ďalej v programe použiť.
Dátové typy int, double a bool
Na začiatok budeme pracovať s troma dátovými typmi:
- typ int pre celé čísla – príkladmi konštánt typu int sú 1, 42, -2, alebo 0.
- typ double pre reálne čísla – príkladmi konštánt typu double sú 4.2, -3.0, 3.14159, alebo 1.5e3 (1.5e3 je tzv. semilogaritmický zápis znamenajúci 1,5 ⋅ 103, t.j. 1500).
- typ bool pre logické hodnoty – jedinými konštantami sú true a false.
Premenné typu int a double zaberajú pevne daný počet bitov, preto do nich nie je možné uložiť ľubovoľné celé alebo reálne číslo. Presný rozsah možných hodnôt môže závisieť od kompilátora, v súčasnosti však väčšinou platí:
- Typ int zvyčajne zaberá 4 bajty (32 bitov) a dajú sa ním reprezentovať celé čísla z intervalu <-2 147 483 648, +2 147 483 647>.
- Typ double zvyčajne zaberá 8 bajtov. Ním reprezentované reálne čísla sú v pamäti uložené vo forme z ⋅ a ⋅ 2b, kde z je znamienko, a je reálne číslo z intervalu <1,2) (mantisa) a b je celé číslo (exponent). Na uloženie mantisy sa používa 52 bitov a na uloženie exponentu 11 bitov. Typ double tak možno použiť na prácu s reálnymi číslami približne v rozsahu od 10-300 po 10300 s presnosťou na 15 až 16 platných číslic. Pri tejto reprezentácii sa nevyhradzujú pevné počty bitov na reprezentáciu celej a desatinnej časti; počet cifier pred a za rádovou čiarkou je určený exponentom. Hovoríme preto o pohyblivej rádovej čiarke.
Pretypovanie
Hodnotu niektorého z typov bool, int, double je možné skonvertovať na „zodpovedajúcu” hodnotu iného z týchto typov, tejto operácii hovoríme pretypovanie.
- Pretypovanie int na double: hodnota čísla zostáva tá istá
- Pretypovanie double na int: zaokrúhleniu smerom k nule (čiže nadol pri kladných číslach, nahor pri záporných), t.j. "urezanie" desatinných cifier
- Pretypovanie bool na int alebo double: true sa konvertuje na 1 alebo 1.0 a false na 0 alebo 0.0
- Pretypovanie z int alebo double na bool: nula sa skonvertuje na false, ľubovoľná nenulová hodnota na true
Pretypovanie je možné realizovať dvoma spôsobmi:
- Implicitne, napríklad priradením premennej jedného typu do premennej iného typu, alebo použitím premennej jedného typu v kontexte, kde sa očakáva premenná druhého typu.
- Explicitne, použitím pretypovacieho operátora: (nazov_noveho_typu) vyraz_stareho_typu.
Možnosti pretypovania sú ilustrované nasledujúcim ukážkovým programom.
#include <iostream>
using namespace std;
int main() {
bool b1 = true;
int n1 = 4;
double x1 = 1.234;
bool b2;
int n2;
b2 = n1;
n2 = x1;
cout << b2 << " " << n2 << endl; // Vypise 1 1
x1 = n2;
cout << x1 << endl; // Vypise 1
cout << (bool) 7 << " " << (bool) 0 << endl; // Vypise 1 0
cout << (int) 4.2 << " " << (int) -4.2 << endl; // Vypise 4 -4
}
Aritmetické operátory a výrazy
Na typoch int aj double sa dajú robiť základné aritmetické operácie
- sčítanie +
- odčítanie -
- násobenie *
- delenie /
Operátor / sa na argumentoch typu int správa ako celočíselné delenie – hodnota podielu sa zaokrúhli smerom k nule.
- Napríklad výraz 5/3 má hodnotu 1.
- Akonáhle je však aspoň jeden operand typu double, interpretuje sa / ako delenie reálnych čísel.
- Výrazy 5.0 / 3.0, 5.0 / 3, 5 / 3.0 a 5 / (double)3 teda majú všetky hodnotu 1.66667.
#include <iostream>
using namespace std;
int main() {
int a = 4;
int b = 3;
// Automaticky pretypuje cele cislo 3 na realne cislo 3.0
double d = 3;
// Celociselne delenie: 4 / 3 = 1
cout << a / b << endl;
// Necelociselne delenie: 4 / 3.0 = 1.33333
cout << a / d << endl;
// Necelociselne delenie: (1.0 * 4) / 3 = 4.0 / 3 = 1.33333
cout << (1.0 * a) / b << endl;
// Necelociselne delenie: 3.0 / 4 = 1.33333
cout << ((double)a) / b << endl;
// Do e je priradeny vysledok celociselneho delenia 4 / 3 = 1;
// po pretypovani je to rovne 1.0
double e = a / b;
// Vypise 1
cout << e << endl;
// Vypise 0.5, lebo 1.0 / 2 je necelociselne delenie
cout << e / 2 << endl;
}
- Na celých číslach je definovaný operátor %, ktorého výstupom je zvyšok po celočíselnom delení (modulo). Napríklad výraz 5 % 3 má hodnotu 2.
- Ďalšie matematické operácie a funkcie vyžadujú #include <cmath> (pre jazyk C++) v hlavičke programu:
- Napríklad cos(x), sin(x), tan(x) (tangens), acos(x) (arkus kosínus), exp(x) (ex), log(x) (prirodzený logaritmus), pow(x,y) (xy), sqrt(x) (odmocnina), abs(x) (absolútna hodnota), floor(x) (dolná celá časť), ...
- Viac detailov možno nájsť v dokumentácii.
Relačné operátory
Hodnoty typov int, double a bool možno porovnávať nasledujúcimi relačnými operátormi:
- == pre rovnosť;
- != pre nerovnosť;
- < pre reláciu „menší”;
- > pre reláciu „väčší”;
- <= pre reláciu „menší alebo rovný”;
- >= pre reláciu „väčší alebo rovný”.
Výstupom relačného operátora je logická hodnota true alebo false.
Logické operátory a výrazy
Na výrazoch typu bool sú definované logické operátory, ktoré sa správajú rovnako ako logické spojky známe z výrokovej logiky:
- || pre disjunkciu (or, alebo);
- && pre konjunkciu (and, a súčasne);
- ! pre negáciu (not, opak)
Logickým výrazom je napríklad !((x >= 2) && (x <= 4)) alebo !true.
Operátory priradenia, zvýšenie a zníženie hodnoty o 1
Operátor priradenia premenna = hodnota už poznáme. Často realizovanou operáciou na číslach je zvýšenie hodnoty o 1. To možno urobiť napríklad nasledujúcimi spôsobmi:
- x = x + 1;
- x += 1;
- x++;
Analogicky sú definované operátory ako --, -=, *=, atď.
Priorita a asociativita operátorov
Výrazy sa vyhodnocujú v nasledujúcom poradí preferencie jednotlivých operátorov. Operátory v jednom riadku majú rovnakú prioritu a operátory vo vyššom riadku majú vyššiu prioritu, než operátory v nižších riadkoch.
- ++ (inkrement), -- (dekrement), ! (logická negácia)
- *, /, %
- +, -
- <, >, <=, >=
- ==, !=
- && (logická konjunkcia)
- || (logická disjunkcia)
- = (priradenie)
Poradie vyhodnocovania je možné meniť zátvorkami, ako napríklad vo výraze 4*(5-3).
Uvedené operátory sa väčšinou vyhodnocujú zľava doprava (hovoríme, že sú zľava asociatívne) – napríklad 1 - 2 - 3 sa teda vyhodnotí ako (1 - 2) - 3, t.j. -4 a nie ako 1 - (2 - 3), t.j. 2. Výnimkou sú operátory !, ++, -- a =, ktoré sú sprava asociatívne. To umožňuje napríklad viacnásobné priradenie a = b = c, ktoré najprv priradí hodnotu c do b a následne hodnotu výrazu b = c – tou je nová hodnota premennej b, čiže hodnota premennej c –, do a.
Viac sa o operátoroch v C++ možno dočítať napríklad tu.
Viac o podmienkach
Vnorené podmienky
Príkazy if môžeme navzájom vnárať.
Príklad: načítaj číslo a zisti, či je kladné, záporné alebo nula.
#include <iostream>
using namespace std;
int main() {
int x;
cout << "Please enter some number: ";
cin >> x;
if (x == 0) {
cout << "Zero" << endl;
} else {
if (x > 0) {
cout << "Positive" << endl;
} else {
cout << "Negative" << endl;
}
}
}
Logické výrazy môžu byť efektívnym nástrojom na elimináciu množstva vnorených podmienok: napríklad konštrukcia typu
if (a == 0) {
if (b == 0) {
čokoľvek
}
}
je ekvivalentná konštrukcii
if (a == 0 && b == 0) {
čokoľvek
}
Upozornenie
Častou chybou je použitie priradenia namiesto porovnania. Nasledujúci kúsok programu do premennej x priradí nulu, ktorá sa premení na false pre účely vyhodnotenia podmienky.
if (x=0) cout << “zero” << endl;
Ďalšia bežná chyba je zabudnutie zložených zátvoriek
if (x==0) cout << “zero”; cout << endl;
Tento program vykoná cout << endl vždy, nezávisle od podmienky. V prípade, že chceme vykonať v podmienke viacero príkazov, nesmieme zabudnúť ich uzátvorkovať:
if (x==0) { cout << “zero”; cout << endl; }
Cvičenia
- Napíšte program, ktorý načíta čísla a,b,c a vypíše, či sú usporiadané vzostupne, t.j. či platí a<b<c
- Pozor, výraz a<b<c treba rozpísať na dve porovnania spojené logickou spojkou
Cyklus for
Dôležitou časťou programovania je schopnosť opakovanie vykonávať tie isté príkazy. Prvou možnosťou ako to robiť, je cyklus for.
Príklad 1: vypisovanie čísel od 1 po n
Nasledujúci program načíta zo vstupu číslo n a postupne vypíše prirodzené čísla od 1 po n (pred každé dá medzeru).
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cout << " " << i;
}
cout << endl;
}
Tu je výstup programu pre n = 9:
1 2 3 4 5 6 7 8 9
Cyklus for vyzeral v programe takto:
for (int i = 1; i <= n; i++) {
telo cyklu
}
Táto konštrukcia pozostáva z kľúčového slova for nasledovaným zátvorkou s troma časťami oddelenými bodkočiarkami:
- Príkaz int i = 1 vytvorí novú celočíselnú premennú i a priradí jej hodnotu 1.
- Podmienka i <= n určuje dokedy sa má cyklus opakovať. V tomto prípade to má byť kým je hodnota premennej i menšia alebo rovná n.
- Príkaz i++ hovorí, že po každom zopakovaní cyklu (t.j. po každej jeho iterácii) sa má hodnota premennej i zvýšiť o jedna.
- Medzi zloženými zátvorkami { a } je potom tzv. telo cyklu – čiže jeden alebo viac príkazov, ktoré sa budú opakovať postupne pre rôzne hodnoty premennej i.
V príklade 1 je telom cyklu iba príkaz cout << " " << i;, ktorý vypíše medzeru a hodnotu premennej i.
Príklad 2: vypisovanie čísel od 0 po n-1
Drobnou zmenou predchádzajúceho programu môžeme napríklad vypísať všetky čísla od 0 po n-1:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cout << " " << i;
}
cout << endl;
}
Tu je výstup programu pre n = 9:
0 1 2 3 4 5 6 7 8
Príklad 3: výpočet faktoriálu
Nasledujúci program si od používateľa vypýta číslo n a vypočíta n!, t.j. súčin celých čísel od 1 po n.
#include <iostream>
using namespace std;
int main() {
int n;
cout << "Zadajte n: ";
cin >> n;
int vysledok = 1;
for (int i = 1; i <= n; i++) {
vysledok = vysledok * i;
}
cout << n << "! = " << vysledok << endl;
}
Príklad behu programu pre n=4 (1*2*3*4=24):
Zadajte n: 4 4! = 24
- Program používa premennú vysledok, ktorú na začiatku inicializuje hodnotou 1 a postupne ju násobí číslami 1, 2, ..., n.
- Riadok vysledok = vysledok * i; zoberie pôvodnú hodnotu premennej vysledok, vynásobí ju hodnotou premennej i (t.j. jedným z čísel 1, 2, ..., n) a výsledok uloží naspäť do premennej vysledok (prepíše pôvodnú hodnotu). To isté sa dá napísať ako vysledok *= i;
Funkcia n! však veľmi rýchlo rastie a už pre n=13 sa výsledok nezmestí do premennej typu int. Dostávame nezmyselné hodnoty:
12! = 479001600 13! = 1932053504 14! = 1278945280 15! = 2004310016 16! = 2004189184 17! = -288522240
Správne hodnoty (ktoré možno získať zmenou typu premennej vysledok na long long int) sú:
12! = 479001600 13! = 6227020800 14! = 87178291200 15! = 1307674368000 16! = 20922789888000 17! = 355687428096000
Cvičenie: rozšírte program tak, aby okrem výpisu výsledku aj rozpísal faktoriál ako súčin:
Zadajte n: 4 4! = 1*2*3*4 = 24
Zhrnutie
Poznáme základné stavebné prvky, z ktorých sa dajú spraviť aj pomerne zložité programy:
- premenné typu int, double, bool a operátory, ktoré s nimi vedia počítať
- podmienku if, ktorá umožňuje vykonávať určité časti programu len za nejakých okolností
- cyklus for, ktorý umožnuje opakovať určité časti programu veľa krát
Cieľom najbližšej prednášky a cvičení je hlavne precvičiť si tieto stavebné prvky na veľa ďalších príkladoch.