Programovanie (1) v C/C++
1-INF-127, ZS 2024/25

Úvod · Pravidlá · Prednášky · Softvér · Testovač
· Kontaktujte nás pomocou e-mailovej adresy E-prg.png (bude odpovedať ten z nás, kto má príslušnú otázku na starosti alebo kto má práve čas).
· Prosíme študentov, aby si pravidelne čítali e-maily na @uniba.sk adrese alebo aby si tieto emaily preposielali na adresu, ktorú pravidelne čítajú.


Prednáška 5: Rozdiel medzi revíziami

Z Programovanie
Skočit na navigaci Skočit na vyhledávání
(Vytvorená stránka „== Oznamy == * DÚ1 bude zverejnená pravdepodobne dnes večer * Budúcu stredu večer test * Príďte na doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť…“)
Riadok 3: Riadok 3:
 
* DÚ1 bude zverejnená pravdepodobne dnes večer
 
* DÚ1 bude zverejnená pravdepodobne dnes večer
 
* Budúcu stredu večer test
 
* Budúcu stredu večer test
* Príďte na dnešné doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť veľa príkladov. Pomôžeme vám s ďalšími príkladmi na tento týždeň
+
* Príďte na doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť veľa príkladov. Pomôžeme vám s ďalšími príkladmi na tento týždeň
 
** Aj ak ste nespravili rozcvičku, stále môžete z tohto týždňa dostať 8 bodov z 10
 
** Aj ak ste nespravili rozcvičku, stále môžete z tohto týždňa dostať 8 bodov z 10
 
* Skúste si niektoré príklady vyriešiť najskôr na papieri ako tréning na test
 
* Skúste si niektoré príklady vyriešiť najskôr na papieri ako tréning na test
 
* Dnes polia (nebudú na teste), budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte
 
* Dnes polia (nebudú na teste), budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte
 +
* Typy príkladov:
 +
** napíšte krátky program alebo funkciu (podobne ako na cvičeniach, ale na papieri)
 +
** zistite, čo program vypíše pre určitý vstup
 +
** doplňte chýbajúce časti programu alebo v ňom nájdite chyby
 +
* Písanie programu na papieri môže byť zo začiatku ťažké
 +
** skúste si niektoré príklady z cvičení napísať najskôr na papier, potom prepísať do počítača
 +
Body z cvičení, domácich úloh a písomiek budete vidieť na testovači
 +
* Momentálne body z cvičení 1 a písomky pre pokročilých
 +
==Organizačné poznámky k štúdiu==
 +
 +
* Poriadne si skontrolujte, či máte v AIS a v indexe to isté a či sú to tie predmety, ktoré chcete brať.
 +
** Prípadné problémy riešte na študijnom
 +
* Do štvrtka 11.10. je potrebné si prípadné zmeny v zápise dať potvrdiť na študijnom. Pred koncom tejto doby bývajú na študijnom dlhé rady, choďte čím skôr. Úradné hodiny mimoriadne aj 13:00-15:00.
 +
* Informácia na stránke fakulty: [http://zona.fmph.uniba.sk/zapis/]
 +
 +
== Záznam typu struct ==
 +
 +
V príkladoch s obvodom trojuholníka a stredom úsečky sme funkciám posielali veľa parametrov (súradnice x a y)
 +
<syntaxhighlight lang="C++">
 +
void stred(double x1, double y1, double x2, double y2, double &xs, double &ys) {
 +
    xs = (x1 + x2) / 2;
 +
    ys = (y1 + y2) / 2;
 +
}
 +
</syntaxhighlight>
 +
 +
 +
Program bude krajší, ak si údaje o jednom bode spojíme do jedného záznamu
 +
<syntaxhighlight lang="C++">
 +
struct bod {
 +
    double x, y;
 +
};
 +
</syntaxhighlight>
 +
* Pomocou <tt>struct</tt> vytvoríme nový dátový typ <tt>bod</tt>, ktorý má zložky x a y
 +
** V jednom struct-e môžu byť aj položky rôznych typov (napr. <tt>struct bod { double x,y; int id; bool visible; };</tt>)
 +
* Môžeme vytvárať premenné typu <tt>bod</tt>, napr. <tt>bod a, b;</tt>
 +
* K položkám bodu pristupujeme pomocou bodky, napr. <tt>a.x = 4.0; </tt>
 +
* Do funkcií body posielame radšej referenciou, aby sa zbytočne nekopírovalo veľa hodnôt
 +
 +
Nasledujúci program načíta súradnice troch bodov, spočíta obvod trojuholníka a stredy všetkých troch strán.
 +
<syntaxhighlight lang="C++">
 +
#include <iostream>
 +
#include <cmath>
 +
using namespace std;
 +
 +
struct bod {
 +
    double x, y;  // suradnice bodu v rovine
 +
};
 +
 +
double dlzka(bod &bod1, bod &bod2) {
 +
    // funkcia vrati dlzku usecky z bodu 1 do bodu 2
 +
    double dx = bod1.x - bod2.x;
 +
    double dy = bod1.y - bod2.y;
 +
    return sqrt(dx * dx + dy * dy);
 +
}
 +
 +
void stred(bod &bod1, bod &bod2, bod &stred) {
 +
    // funkcia do bodu stred spocita stred usecky z bodu 1 do bodu 2
 +
    stred.x = (bod1.x + bod2.x) / 2;
 +
    stred.y = (bod1.y + bod2.y) / 2;
 +
}
 +
 +
void vypisBod(bod &b) {
 +
    // funkcia vypise surandice bodu v zatvorke a koniec riadku
 +
    cout << "(" << b.x << "," << b.y << ")" << endl;
 +
}
 +
 +
int main(void) {
 +
    // nacitame suradnice vrcholov trojuholnika
 +
    bod A, B, C;
 +
    cout << "Zadaj suradnice vrcholu A oddelene medzerou: ";
 +
    cin >> A.x >> A.y;
 +
    cout << "Zadaj suradnice vrcholu B oddelene medzerou: ";
 +
    cin >> B.x >> B.y;
 +
    cout << "Zadaj suradnice vrcholu C oddelene medzerou: ";
 +
    cin >> C.x >> C.y;
 +
    // spocitame dlzky stran
 +
    double da = dlzka(B, C);
 +
    double db = dlzka(A, C);
 +
    double dc = dlzka(A, B);
 +
    // vypiseme obvod
 +
    cout << "Obvod trojuholnika ABC: " << da + db + dc << endl;
 +
 +
    // spocitame stredy stran
 +
    bod stredAB;
 +
    stred(A, B, stredAB);
 +
    bod stredAC;
 +
    stred(A, C, stredAC);
 +
    bod stredBC;
 +
    stred(B, C, stredBC);
 +
 +
    // vypiseme stredy stran
 +
    cout << "Stred strany AB: ";
 +
    vypisBod(stredAB);
 +
    cout << "Stred strany AC: ";
 +
    vypisBod(stredAC);
 +
    cout << "Stred strany BC: ";
 +
    vypisBod(stredBC);
 +
}
 +
</syntaxhighlight>
 +
 +
Príklad behu programu:
 +
<pre>
 +
Zadaj suradnice vrcholu A oddelene medzerou: 0 0
 +
Zadaj suradnice vrcholu B oddelene medzerou: 0 3
 +
Zadaj suradnice vrcholu C oddelene medzerou: 4 0
 +
Obvod trojuholnika ABC: 12
 +
Stred strany AB: (0,1.5)
 +
Stred strany AC: (2,0)
 +
Stred strany BC: (2,1.5)
 +
</pre>
 +
 +
== Spracovanie väčšieho množstva dát ==
 +
 +
Naše programy doteraz spracovávali len malý počet vstupných dát načítaných od užívateľa (napr. súradnice trocho bodov). Často však chceme pracovať s väčším množstvom dát
 +
* Na budúcej prednáške si ukážeme, ako uložiť väčšie množstvo dát do poľa
 +
* Na niektoré úlohy však pole nepotrebujeme - údaje môžeme spracovávať rovno ako ich užívateľ zadáva
 +
 +
V nasledujúcich príkladoch užívateľ zadá číslo ''N'' a potom ''N'' celých čísel
 +
* Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10)
 +
* Z týchto bodov chceme spočítať nejaké štatistiky
 +
 +
=== Priemer ===
 +
<syntaxhighlight lang="C++">
 +
#include <iostream>
 +
using namespace std;
 +
 +
int main(void) {
 +
    int N;
 +
    cout << "Zadaj pocet cisel: ";
 +
    cin >> N;
 +
 +
    int sucet = 0;
 +
    cout << "Zadavaj cisla: ";
 +
    for (int i = 0; i < N; i++) {
 +
        int x;
 +
        cin >> x;
 +
        sucet += x;
 +
    }
 +
 +
    double priemer = sucet / (double) N;
 +
    cout << "Priemer je " << priemer << "." << endl;
 +
}
 +
</syntaxhighlight>
 +
 +
* Čo by sa stalo, keby sme vo výpočte priemeru vynechali <tt>(double)</tt>?
  
 
== Opakovanie: spracovanie väčšieho množstva dát ==
 
== Opakovanie: spracovanie väčšieho množstva dát ==
Riadok 15: Riadok 160:
  
 
=== Priemer ===
 
=== Priemer ===
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 35: Riadok 180:
 
     cout << "Priemer je " << priemer << "." << endl;
 
     cout << "Priemer je " << priemer << "." << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
=== Maximum ===
 
=== Maximum ===
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 61: Riadok 206:
 
     cout << endl << "Maximum je " << max << endl;
 
     cout << endl << "Maximum je " << max << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
Ako ale začať? Ako nastaviť maximum na začiatok?
 
Ako ale začať? Ako nastaviť maximum na začiatok?
Riadok 68: Riadok 213:
 
* Ďalšia možnosť je si pamätať, že ešte nemáme správne nastavené maximum a po načítaní prvého čísla ho nastaviť alebo spracovať prvé číslo zvlášť (mimo cyklu)
 
* Ďalšia možnosť je si pamätať, že ešte nemáme správne nastavené maximum a po načítaní prvého čísla ho nastaviť alebo spracovať prvé číslo zvlášť (mimo cyklu)
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 91: Riadok 236:
 
     cout << endl << "Maximum je " << max << endl;
 
     cout << endl << "Maximum je " << max << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
'''Cvičenie:'''  
 
'''Cvičenie:'''  
Riadok 107: Riadok 252:
 
** pozor, <tt>a[20]</tt> neexistuje
 
** pozor, <tt>a[20]</tt> neexistuje
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 134: Riadok 279:
 
     }
 
     }
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
Na zamyslenie:
 
Na zamyslenie:
Riadok 144: Riadok 289:
  
 
Rozsah poľa je konštantný výraz väčší ako 0. Prvky sa indexujú od 0 po počet - 1
 
Rozsah poľa je konštantný výraz väčší ako 0. Prvky sa indexujú od 0 po počet - 1
<pre>
+
<syntaxhighlight lang="C++">
 
int a[10];
 
int a[10];
 
const int N=20;
 
const int N=20;
 
double b[N];
 
double b[N];
</pre>
+
</syntaxhighlight>
  
 
Ak nepoznáme vopred počet prvkov, ktoré chceme dať do poľa, môžeme odhadnúť, že ich nebude viac ako NMax, ktoré definujeme ako konštantu v programe.
 
Ak nepoznáme vopred počet prvkov, ktoré chceme dať do poľa, môžeme odhadnúť, že ich nebude viac ako NMax, ktoré definujeme ako konštantu v programe.
 
* Vytvoríme pole veľkosti NMax, použijeme z neho len prých N hodnôt
 
* Vytvoríme pole veľkosti NMax, použijeme z neho len prých N hodnôt
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 168: Riadok 313:
 
   cout << "Zadavajte " << N << " cisel: ";
 
   cout << "Zadavajte " << N << " cisel: ";
 
   ...
 
   ...
</pre>
+
</syntaxhighlight>
  
  
 
Čo ak ako veľkosť poľa použijeme premennú N, ktorú si prečítame od používateľa alebo inak spočítame za behu?
 
Čo ak ako veľkosť poľa použijeme premennú N, ktorú si prečítame od používateľa alebo inak spočítame za behu?
<pre>
+
<syntaxhighlight lang="C++">
 
     int N;
 
     int N;
 
     cout << "Zadaj pocet cisel: ";
 
     cout << "Zadaj pocet cisel: ";
 
     cin >> N;
 
     cin >> N;
 
     int p[N];
 
     int p[N];
</pre>
+
</syntaxhighlight>
 
* V starších verziách C resp. C++ to nefunguje, aj keď niektoré novšie kompilátory to zvládajú
 
* V starších verziách C resp. C++ to nefunguje, aj keď niektoré novšie kompilátory to zvládajú
 
* Na prednáškach tento spôsob nebudeme používať
 
* Na prednáškach tento spôsob nebudeme používať
Riadok 186: Riadok 331:
 
V definícii môžeme pole inicializovať zoznamom prvkov.  
 
V definícii môžeme pole inicializovať zoznamom prvkov.  
  
<pre>
+
<syntaxhighlight lang="C++">
 
int A[4]={3, 6, 8, 10}; //spravne
 
int A[4]={3, 6, 8, 10}; //spravne
 
int B[4]; //spravne
 
int B[4]; //spravne
 
B[4]={3, 6, 8, 10};  //nespravne, pole tu nedefinujeme
 
B[4]={3, 6, 8, 10};  //nespravne, pole tu nedefinujeme
 
B[0]=3; B[1]=6; B[2]=8; B[3]=10;  // spravne - menime prvky existujuceho pola
 
B[0]=3; B[1]=6; B[2]=8; B[3]=10;  // spravne - menime prvky existujuceho pola
</pre>
+
</syntaxhighlight>
  
 
=== Indexovanie hodnotou mimo intervalu ===
 
=== Indexovanie hodnotou mimo intervalu ===
  
 
Pozor, kompilátor nekontroluje indexy prvkov
 
Pozor, kompilátor nekontroluje indexy prvkov
<pre>
+
<syntaxhighlight lang="C++">
 
   int a[10];
 
   int a[10];
 
   a[10] = 1234;
 
   a[10] = 1234;
</pre>
+
</syntaxhighlight>
 
* Skompiluje, ale hodnota 1234 sa zapíše do pamäti na zlé miesto
 
* Skompiluje, ale hodnota 1234 sa zapíše do pamäti na zlé miesto
 
* Môže to mať nepredvídateľné následky: prepísanie obsahu iných premenných (chybný výpočet alebo „nevysvetliteľné“ správanie sa programu) alebo dokonca prepísanie časti kódu vášho programu
 
* Môže to mať nepredvídateľné následky: prepísanie obsahu iných premenných (chybný výpočet alebo „nevysvetliteľné“ správanie sa programu) alebo dokonca prepísanie časti kódu vášho programu
Riadok 208: Riadok 353:
 
Takýto príkaz však neskompiluje – nedá sa takto priraďovať, treba kopírovať prvok po prvku.
 
Takýto príkaz však neskompiluje – nedá sa takto priraďovať, treba kopírovať prvok po prvku.
  
<pre>
+
<syntaxhighlight lang="C++">
 
   for (i=0; i<10; i++) b[i]=a[i];
 
   for (i=0; i<10; i++) b[i]=a[i];
</pre>
+
</syntaxhighlight>
  
 
Polia sa tiež nedajú porovnávať pomocou operátora <tt>==</tt>. Podmienku <tt>if (a==b) cout << "Ok";</tt>.
 
Polia sa tiež nedajú porovnávať pomocou operátora <tt>==</tt>. Podmienku <tt>if (a==b) cout << "Ok";</tt>.
Riadok 216: Riadok 361:
 
Treba opäť porovnávať prvok po prvku.
 
Treba opäť porovnávať prvok po prvku.
  
<pre>
+
<syntaxhighlight lang="C++">
 
   bool rovne = true;
 
   bool rovne = true;
 
   for (i = 0; i < 10; i++) {  
 
   for (i = 0; i < 10; i++) {  
Riadok 222: Riadok 367:
 
   }
 
   }
 
   if (rovne) cout << "Rovnaju sa" << endl;  
 
   if (rovne) cout << "Rovnaju sa" << endl;  
</pre>
+
</syntaxhighlight>
  
 
Ten istý kúsok programu môžeme napísať napr. aj takto:
 
Ten istý kúsok programu môžeme napísať napr. aj takto:
  
<pre>
+
<syntaxhighlight lang="C++">
 
   bool rovne = true;
 
   bool rovne = true;
 
   for (i = 0; i < 10; i++) {  
 
   for (i = 0; i < 10; i++) {  
Riadok 235: Riadok 380:
 
   }
 
   }
 
   if (rovne) cout << "Rovnaju sa" << endl;  
 
   if (rovne) cout << "Rovnaju sa" << endl;  
</pre>
+
</syntaxhighlight>
  
 
== Výskyty čísel 0...9 ==
 
== Výskyty čísel 0...9 ==
Riadok 249: Riadok 394:
 
* vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla ''i''
 
* vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla ''i''
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 276: Riadok 421:
 
         cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis
 
         cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis
 
     }
 
     }
}</pre>
+
}</syntaxhighlight>
  
  
Riadok 284: Riadok 429:
 
[[Image:P5-svgdraw.png|frame|Výsledný obrázok]]
 
[[Image:P5-svgdraw.png|frame|Výsledný obrázok]]
 
* Aplikácie s grafickým rozhraním budeme programovať až budúci semester
 
* Aplikácie s grafickým rozhraním budeme programovať až budúci semester
* V tomto semestri, ale budeme kresliť obrázky v [https://en.wikipedia.org/wiki/Scalable_Vector_Graphics SVG formáte] pomocou jednoduchej knižnice [[SVGdraw]]
+
* V tomto semestri, ale budeme kresliť obrázky v [https://en.wikipedia.org/wiki/Scalable_Vector_Graphics SVG formáte] pomocou jednoduchej knižnice [[#SVGdraw]]
* Knižnicu si stiahnite a nainštalujte [[Netbeans#Pr.C3.A1ca_v_Netbeans_s_grafickou_kni.C5.BEnicou_SVGdraw|podľa návodu]]
+
* Knižnicu si stiahnite a nainštalujte [[#Netbeans#Pr.C3.A1ca_v_Netbeans_s_grafickou_kni.C5.BEnicou_SVGdraw|podľa návodu]]
 
* Na začiatku programu zapnite knižnicu pomocou <tt>#include "SVGdraw.h"</tt>
 
* Na začiatku programu zapnite knižnicu pomocou <tt>#include "SVGdraw.h"</tt>
 
* Tu je malý ukážkový program, ktorý vykreslí zelený štvorec a v ňom červený kruh:
 
* Tu je malý ukážkový program, ktorý vykreslí zelený štvorec a v ňom červený kruh:
<pre>
+
<syntaxhighlight lang="C++">
 
#include "SVGdraw.h"
 
#include "SVGdraw.h"
  
Riadok 310: Riadok 455:
 
   drawing.finish();
 
   drawing.finish();
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
== Kreslíme kruhy ==
 
== Kreslíme kruhy ==
Riadok 319: Riadok 464:
 
* Navyše o každom kruhu zistí, či sa pretína s iným kruhom, a ak áno, pri vykresľovaní ho orámuje červenou farbou.
 
* Navyše o každom kruhu zistí, či sa pretína s iným kruhom, a ak áno, pri vykresľovaní ho orámuje červenou farbou.
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include "SVGdraw.h"
 
#include "SVGdraw.h"
 
#include <cstdlib>
 
#include <cstdlib>
Riadok 390: Riadok 535:
 
     drawing.finish();
 
     drawing.finish();
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
== Eratostenovo sito ==
 
== Eratostenovo sito ==
Riadok 401: Riadok 546:
 
* Potom prechádzame v poli, kým nenájdeme najbližšiu ďalšiu hodnotu <tt>true</tt>. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky.  
 
* Potom prechádzame v poli, kým nenájdeme najbližšiu ďalšiu hodnotu <tt>true</tt>. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky.  
  
<pre>
+
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
Riadok 422: Riadok 567:
 
     cout << endl;
 
     cout << endl;
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
Výstup programu
 
Výstup programu
Riadok 457: Riadok 602:
 
** Bez <tt>&</tt> sa skopíruje hodnota
 
** Bez <tt>&</tt> sa skopíruje hodnota
 
** S <tt>&</tt> premenná dostane vo funkcii nové meno  
 
** S <tt>&</tt> premenná dostane vo funkcii nové meno  
<pre>
+
<syntaxhighlight lang="C++">
 
void f1(int x) {
 
void f1(int x) {
 
     x++; // zmena x sa neprenesie do main (y zostane rovnaká)
 
     x++; // zmena x sa neprenesie do main (y zostane rovnaká)
Riadok 475: Riadok 620:
 
     //zle: f2(y+1);
 
     //zle: f2(y+1);
 
}
 
}
</pre>
+
</syntaxhighlight>
 
* Polia odovzdávame bez <tt>&</tt>
 
* Polia odovzdávame bez <tt>&</tt>
 
** väčšinou potrebujeme poslať aj veľkosť poľa, ak nie je globálne známa
 
** väčšinou potrebujeme poslať aj veľkosť poľa, ak nie je globálne známa
 
** zmeny v poli zostanú aj po skončení funkcie
 
** zmeny v poli zostanú aj po skončení funkcie
<pre>
+
<syntaxhighlight lang="C++">
 
void f(int a[], int n) {
 
void f(int a[], int n) {
 
     for (int i = 0; i < n; i++) {
 
     for (int i = 0; i < n; i++) {
Riadok 490: Riadok 635:
 
     f(b, 3);
 
     f(b, 3);
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
* SVGdraw obrázky sú v skutočnosti objekty, väčšinou ich chcete posielať s <tt>&</tt>
 
* SVGdraw obrázky sú v skutočnosti objekty, väčšinou ich chcete posielať s <tt>&</tt>
 
** všetky zmeny na nich spravené pretrvávajú aj po skončení funkcie
 
** všetky zmeny na nich spravené pretrvávajú aj po skončení funkcie
<pre>
+
<syntaxhighlight lang="C++">
 
#include "SVGdraw.h"
 
#include "SVGdraw.h"
 
void kresli(SVGdraw &drawing, int n, int y, int rozostup, int velkost) {
 
void kresli(SVGdraw &drawing, int n, int y, int rozostup, int velkost) {
Riadok 506: Riadok 651:
 
     drawing.finish();
 
     drawing.finish();
 
}
 
}
</pre>
+
</syntaxhighlight>
 
* Štruktúry (<tt>struct</tt>) väčšinou tiež posielame pomocou <tt>&</tt>
 
* Štruktúry (<tt>struct</tt>) väčšinou tiež posielame pomocou <tt>&</tt>
 
* Návratové hodnoty:  
 
* Návratové hodnoty:  

Verzia zo dňa a času 11:57, 8. október 2019

Oznamy

  • DÚ1 bude zverejnená pravdepodobne dnes večer
  • Budúcu stredu večer test
  • Príďte na doplnkové cvičenia, ak sa vám včera nepodarilo vyriešiť veľa príkladov. Pomôžeme vám s ďalšími príkladmi na tento týždeň
    • Aj ak ste nespravili rozcvičku, stále môžete z tohto týždňa dostať 8 bodov z 10
  • Skúste si niektoré príklady vyriešiť najskôr na papieri ako tréning na test
  • Dnes polia (nebudú na teste), budúci pondelok hlavne ďalšie príklady a algoritmy s použitím tých častí Cčka, ktoré už poznáte
  • Typy príkladov:
    • napíšte krátky program alebo funkciu (podobne ako na cvičeniach, ale na papieri)
    • zistite, čo program vypíše pre určitý vstup
    • doplňte chýbajúce časti programu alebo v ňom nájdite chyby
  • Písanie programu na papieri môže byť zo začiatku ťažké
    • skúste si niektoré príklady z cvičení napísať najskôr na papier, potom prepísať do počítača

Body z cvičení, domácich úloh a písomiek budete vidieť na testovači

  • Momentálne body z cvičení 1 a písomky pre pokročilých

Organizačné poznámky k štúdiu

  • Poriadne si skontrolujte, či máte v AIS a v indexe to isté a či sú to tie predmety, ktoré chcete brať.
    • Prípadné problémy riešte na študijnom
  • Do štvrtka 11.10. je potrebné si prípadné zmeny v zápise dať potvrdiť na študijnom. Pred koncom tejto doby bývajú na študijnom dlhé rady, choďte čím skôr. Úradné hodiny mimoriadne aj 13:00-15:00.
  • Informácia na stránke fakulty: [1]

Záznam typu struct

V príkladoch s obvodom trojuholníka a stredom úsečky sme funkciám posielali veľa parametrov (súradnice x a y)

void stred(double x1, double y1, double x2, double y2, double &xs, double &ys) {
    xs = (x1 + x2) / 2;
    ys = (y1 + y2) / 2;
}


Program bude krajší, ak si údaje o jednom bode spojíme do jedného záznamu

struct bod {
    double x, y;
};
  • Pomocou struct vytvoríme nový dátový typ bod, ktorý má zložky x a y
    • V jednom struct-e môžu byť aj položky rôznych typov (napr. struct bod { double x,y; int id; bool visible; };)
  • Môžeme vytvárať premenné typu bod, napr. bod a, b;
  • K položkám bodu pristupujeme pomocou bodky, napr. a.x = 4.0;
  • Do funkcií body posielame radšej referenciou, aby sa zbytočne nekopírovalo veľa hodnôt

Nasledujúci program načíta súradnice troch bodov, spočíta obvod trojuholníka a stredy všetkých troch strán.

#include <iostream>
#include <cmath>
using namespace std;

struct bod {
    double x, y;  // suradnice bodu v rovine
};

double dlzka(bod &bod1, bod &bod2) {
    // funkcia vrati dlzku usecky z bodu 1 do bodu 2
    double dx = bod1.x - bod2.x;
    double dy = bod1.y - bod2.y;
    return sqrt(dx * dx + dy * dy);
}

void stred(bod &bod1, bod &bod2, bod &stred) {
    // funkcia do bodu stred spocita stred usecky z bodu 1 do bodu 2
    stred.x = (bod1.x + bod2.x) / 2;
    stred.y = (bod1.y + bod2.y) / 2;
}

void vypisBod(bod &b) {
    // funkcia vypise surandice bodu v zatvorke a koniec riadku
    cout << "(" << b.x << "," << b.y << ")" << endl;
}

int main(void) {
    // nacitame suradnice vrcholov trojuholnika
    bod A, B, C;
    cout << "Zadaj suradnice vrcholu A oddelene medzerou: ";
    cin >> A.x >> A.y;
    cout << "Zadaj suradnice vrcholu B oddelene medzerou: ";
    cin >> B.x >> B.y;
    cout << "Zadaj suradnice vrcholu C oddelene medzerou: ";
    cin >> C.x >> C.y;
    // spocitame dlzky stran
    double da = dlzka(B, C);
    double db = dlzka(A, C);
    double dc = dlzka(A, B);
    // vypiseme obvod
    cout << "Obvod trojuholnika ABC: " << da + db + dc << endl;

    // spocitame stredy stran
    bod stredAB;
    stred(A, B, stredAB);
    bod stredAC;
    stred(A, C, stredAC);
    bod stredBC;
    stred(B, C, stredBC);

    // vypiseme stredy stran
    cout << "Stred strany AB: ";
    vypisBod(stredAB);
    cout << "Stred strany AC: ";
    vypisBod(stredAC);
    cout << "Stred strany BC: ";
    vypisBod(stredBC);
}

Príklad behu programu:

Zadaj suradnice vrcholu A oddelene medzerou: 0 0
Zadaj suradnice vrcholu B oddelene medzerou: 0 3
Zadaj suradnice vrcholu C oddelene medzerou: 4 0
Obvod trojuholnika ABC: 12
Stred strany AB: (0,1.5)
Stred strany AC: (2,0)
Stred strany BC: (2,1.5)

Spracovanie väčšieho množstva dát

Naše programy doteraz spracovávali len malý počet vstupných dát načítaných od užívateľa (napr. súradnice trocho bodov). Často však chceme pracovať s väčším množstvom dát

  • Na budúcej prednáške si ukážeme, ako uložiť väčšie množstvo dát do poľa
  • Na niektoré úlohy však pole nepotrebujeme - údaje môžeme spracovávať rovno ako ich užívateľ zadáva

V nasledujúcich príkladoch užívateľ zadá číslo N a potom N celých čísel

  • Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10)
  • Z týchto bodov chceme spočítať nejaké štatistiky

Priemer

#include <iostream>
using namespace std;

int main(void) {
    int N;
    cout << "Zadaj pocet cisel: ";
    cin >> N;

    int sucet = 0;
    cout << "Zadavaj cisla: ";
    for (int i = 0; i < N; i++) {
        int x;
        cin >> x;
        sucet += x;
    }

    double priemer = sucet / (double) N;
    cout << "Priemer je " << priemer << "." << endl;
}
  • Čo by sa stalo, keby sme vo výpočte priemeru vynechali (double)?

Opakovanie: spracovanie väčšieho množstva dát

V nasledujúcich príkladoch užívateľ zadá číslo N a potom N celých čísel

  • Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10)
  • Z týchto bodov chceme spočítať nejaké štatistiky

Priemer

#include <iostream>
using namespace std;

int main(void) {
    int N;
    cout << "Zadaj pocet cisel: ";
    cin >> N;

    int sucet = 0;
    cout << "Zadavaj cisla: ";
    for (int i = 0; i < N; i++) {
        int x;
        cin >> x;
        sucet += x;
    }

    double priemer = sucet / (double) N;
    cout << "Priemer je " << priemer << "." << endl;
}

Maximum

#include <iostream>
using namespace std;

int main(void) {
    int max, x, N;

    cout << "Zadaj pocet cisel: ";
    cin >> N;

    cout << "Zadavajte cisla: ";

    max = ?
    for (int i = 0; i < N; i++) {
        cin >> x;
        if (x > max) {
            max = x;
        }
    }

    cout << endl << "Maximum je " << max << endl;
}

Ako ale začať? Ako nastaviť maximum na začiatok?

  • Jedna možnosť je nastaviť ho na nejakú veľmi malú hodnotu, aby sa iste neskôr zmenila. Kto nám ale zaručí, že používateľ nedá všetky čísla ešte menšie?
  • Riešením je použiť najmenšie možné číslo - ale je príliš viazané na konkrétny rozsah, nebude fungovať po zmene typu premenných.
  • Ďalšia možnosť je si pamätať, že ešte nemáme správne nastavené maximum a po načítaní prvého čísla ho nastaviť alebo spracovať prvé číslo zvlášť (mimo cyklu)
#include <iostream>
using namespace std;

int main(void) {
    int max, x, N;

    cout << "Zadaj pocet cisel: ";
    cin >> N;

    cout << "Zadavajte cisla: ";
    cin >> x;   // načítanie prvého čísla
    max = x;

    for (int i = 1; i < N; i++) {  // cyklus cez N-1 ďalších čísel 
        cin >> x;
        if (x > max) {
            max = x;
        }
    }

    cout << endl << "Maximum je " << max << endl;
}

Cvičenie:

  • Ako by sme program rozšírili tak, aby vedel vypísať aj koľké číslo v poradí bolo najväčšie?
  • Čo treba v programe zmeniť, ak chceme hľadať minimum namiesto maxima?
  • Spočítajte, koľkokrát sa na vstupe vyskytuje číslo 0

Podpriemer / nadpriemer

Chceme spočítať priemer a o každom vstupnom čísle vypísať, či je nadpriemerné alebo podpriemerné.

  • Priemer vieme až keď načítame všetky čísla, musíme si ich teda zapamätať
  • Na to používame tabuľky, ktoré sa volajú polia.
  • Na začiatok pre jednoduchosť predpokladajme, že vopred vieme, že počet údajov N je napr. 20 (N teda nenačítavame)
  • Príkaz int a[20]; vytvorí pole s 20 premennými typu int, ku ktorým pristupujeme a[0], a[1], ..., a[19]
    • pozor, a[20] neexistuje
#include <iostream>
using namespace std;

int main(void) {
    const int N = 20; // premennu N uz nebude mozne menit, ma konstantnu hodnotu 20
    int a[N];
    double sucet = 0;

    cout << "Zadavajte " << N << " cisel: ";
    for (int i = 0; i < N; i++) {
        cin >> a[i];
        sucet += a[i];
    }

    double priemer = sucet / N;
    cout << "Priemer je " << priemer << "." << endl;
    for (int i = 0; i < N; i++) {
        if (a[i] > priemer) { 
             cout << a[i] << ": vacsie ako priemer." << endl;
        } else if (a[i] < priemer) {
             cout << a[i] << ": mensie ako priemer." << endl;
        } else {
             cout << a[i] << ": priemer." << endl;
        }
    }
}

Na zamyslenie:

  • Pozor, chyby pri zaokrúhľovaní môžu spôsobiť, že niekedy priemerné číslo bude považované za nad/podpriemerné
    • Vedeli by ste program prerobiť tak, aby používal iba premenné typu int a nerobil žiadnu chybu v zaokrúhľovaní?
    • Môže aj po takejto zmene niekedy dať zlú odpoveď?

Polia

Rozsah poľa je konštantný výraz väčší ako 0. Prvky sa indexujú od 0 po počet - 1

int a[10];
const int N=20;
double b[N];

Ak nepoznáme vopred počet prvkov, ktoré chceme dať do poľa, môžeme odhadnúť, že ich nebude viac ako NMax, ktoré definujeme ako konštantu v programe.

  • Vytvoríme pole veľkosti NMax, použijeme z neho len prých N hodnôt
#include <iostream>
using namespace std;

int main(void) {
    const int NMax = 1000;
    int p[NMax];
    int N;
    cout << "Zadaj pocet cisel: ";
    cin >> N;
    if (N > NMax) {
        cout << "Prilis velke N" << endl;
        return 1;  // ukončíme funkciu main a tým aj program
    }
   cout << "Zadavajte " << N << " cisel: ";
   ...


Čo ak ako veľkosť poľa použijeme premennú N, ktorú si prečítame od používateľa alebo inak spočítame za behu?

    int N;
    cout << "Zadaj pocet cisel: ";
    cin >> N;
    int p[N];
  • V starších verziách C resp. C++ to nefunguje, aj keď niektoré novšie kompilátory to zvládajú
  • Na prednáškach tento spôsob nebudeme používať
  • Pole veľkosti N, ktorá nie je konštanta, sa naučíme vytvárať inak v druhej polovici semestra

Vytvorenie a inicializácia poľa

V definícii môžeme pole inicializovať zoznamom prvkov.

int A[4]={3, 6, 8, 10}; //spravne
int B[4]; //spravne
B[4]={3, 6, 8, 10};  //nespravne, pole tu nedefinujeme
B[0]=3; B[1]=6; B[2]=8; B[3]=10;  // spravne - menime prvky existujuceho pola

Indexovanie hodnotou mimo intervalu

Pozor, kompilátor nekontroluje indexy prvkov

  int a[10];
  a[10] = 1234;
  • Skompiluje, ale hodnota 1234 sa zapíše do pamäti na zlé miesto
  • Môže to mať nepredvídateľné následky: prepísanie obsahu iných premenných (chybný výpočet alebo „nevysvetliteľné“ správanie sa programu) alebo dokonca prepísanie časti kódu vášho programu

Kopírovanie a testovanie rovnosti

V prípade, že chceme vytvoriť pole, ktoré je kópiou už existujúceho poľa, ponúka sa možnosť príkazu priradenia b=a;. Takýto príkaz však neskompiluje – nedá sa takto priraďovať, treba kopírovať prvok po prvku.

  for (i=0; i<10; i++) b[i]=a[i];

Polia sa tiež nedajú porovnávať pomocou operátora ==. Podmienku if (a==b) cout << "Ok";. síce skompilujete, ale nikdy to nebude pravda – neporovná sa obsah poľa, ale niečo úplne iné (adresy polí v pamäti). Treba opäť porovnávať prvok po prvku.

  bool rovne = true;
  for (i = 0; i < 10; i++) { 
      rovne = rovne && a[i] == b[i]; 
  }
  if (rovne) cout << "Rovnaju sa" << endl;

Ten istý kúsok programu môžeme napísať napr. aj takto:

  bool rovne = true;
  for (i = 0; i < 10; i++) { 
      if(a[i] != b[i]) {
          rovne = false;
          break;
      }
  }
  if (rovne) cout << "Rovnaju sa" << endl;

Výskyty čísel 0...9

Na vstupe je číslo N' a N celých čísel od 0 do 9 a chceme vedieť, koľkokrát sa jednotlivé čísla na vstupe vyskytli.

Prvý prístupy:

  • vstupné čísla uložíme do poľa
  • pre každú hodnotu i od 0 po 9 prejdeme pole a spočítame počet výskytov i

Druhý prístup

  • samotné vstupné čísla neukladáme do poľa, spracovávame ich po jednom
  • vytvoríme si pole počítadiel dĺžky 10, v ktorom p[i] bude počet výskytov čísla i
#include <iostream>
using namespace std;

int main(void) {
    int p[10];  // pole dlzky 10
    int N;
    cout << "Zadajte pocet cisel: ";
    cin >> N;

    for (int i = 0; i < 10; i++) {
       p[i] = 0; // inicializácia poľa p[0]=0; p[1]=0; ... p[9]=0;
    }

    cout << "Zadavajte " << N << "cisel z intervalu 0-9: ";
    for (int i = 0; i < N; i++) {
        int x;
        cin >> x;
        if (x >= 0 && x < 10) { // test, či je číslo z požadovaného rozsahu
            p[x]++;             // zvýšime počítadlo pre hodnotu x
        }
    }

    cout << endl;
    for (int i = 0; i < 10; i++) {
        cout << "Pocet vyskytov " << i << " je " << p[i] << endl; // výpis
    }
}


Odbočka: grafická knižnica SVGdraw

Výsledný obrázok
  • Aplikácie s grafickým rozhraním budeme programovať až budúci semester
  • V tomto semestri, ale budeme kresliť obrázky v SVG formáte pomocou jednoduchej knižnice #SVGdraw
  • Knižnicu si stiahnite a nainštalujte podľa návodu
  • Na začiatku programu zapnite knižnicu pomocou #include "SVGdraw.h"
  • Tu je malý ukážkový program, ktorý vykreslí zelený štvorec a v ňom červený kruh:
#include "SVGdraw.h"

int main(void) {
  /* Vytvor obrázok s šírkou 150 a výškou 100 a 
   * ulož ho do súboru stvorec.svg*/
  SVGdraw drawing(150, 100, "stvorec.svg");

  /* Nastav farbu vyfarbovania na zelenú. */
  drawing.setFillColor("green");
  /* Vykresli štvorec s ľavým horným rohom v bode (20,10)
   * a s dĺžkou strany 80, t.j. pravým dolným rohom 100,90 */
  drawing.drawRectangle(20,10,80,80);
  
  /* Nastav farbu vyfarbovania na červenú. */
  drawing.setFillColor("red");
  /* Vykresli kruh so stredom v bode (60,50) a polomerom 40. */
  drawing.drawEllipse(60,50,40,40);

  /* Ukonči vypisovanie obrázka. */
  drawing.finish();
}

Kreslíme kruhy

P5-kruhy.png

Nasledujúci program náhodne vygeneruje súradnice niekoľkých kruhov a vykreslí ich pomocou kružnice SVGdraw.

  • Údaje o jednom kruhu si uloží do záznamu struct kruh, v programe používame pole takýchto kruhov.
  • Navyše o každom kruhu zistí, či sa pretína s iným kruhom, a ak áno, pri vykresľovaní ho orámuje červenou farbou.
#include "SVGdraw.h"
#include <cstdlib>
#include <ctime>
#include <cmath>

struct kruh {
    int x, y; /* suradnice stredu */
    int polomer; /* polomer kruhu */
    bool pretinaSa; /* pretína sa s iným kruhom? */
};

void generujKruh(kruh &k, int velkost, int polomer) {
    /* inicializuj kruh s nahodnou polohou a danym polomerom */
    k.x = rand() % (velkost - 2 * polomer) + polomer;
    k.y = rand() % (velkost - 2 * polomer) + polomer;
    k.polomer = polomer;
}

bool pretinajuSa(kruh &k1, kruh &k2) {
    /* zisti, ci sa dva kruhy pretinaju */
    int dx = k1.x - k2.x;
    int dy = k1.y - k2.y;
    double d2 = sqrt(dx * dx + dy * dy);
    return d2 <= k1.polomer + k2.polomer;
}

int main(void) {
    const int pocet = 10; /* počet kruhov */
    const int velkost = 300; /* veľkosť obrázku */
    const int polomer = 15; /* polomer kruhu */

    /* inicializácia generátora pseudonáhodných čísel */
    srand(time(NULL));

    /* inicializácia obrázku */
    SVGdraw drawing(velkost, velkost, "kruhy.svg");

    /* pole kruhov */
    kruh kruhy[pocet];

    for (int i = 0; i < pocet; i++) {
        /* kazdemu kruhu vygeneruj nahodnu polohu */
        generujKruh(kruhy[i], velkost, polomer);
    }

    /* zisti, ktoré kruhy pretínajú iné kruhy */
    for (int i = 0; i < pocet; i++) {
        kruhy[i].pretinaSa = false;
        for (int j = 0; j < pocet; j++) {
            if (i != j && pretinajuSa(kruhy[i], kruhy[j])) {
                kruhy[i].pretinaSa = true;
            }
        }
    }

    /* vykresluj kruhy */
    drawing.setFillColor("gray");
    for (int i = 0; i < pocet; i++) {
        if (kruhy[i].pretinaSa) {
            drawing.setLineColor("red");
        } else {
            drawing.setLineColor("black");
        }
        drawing.drawEllipse(kruhy[i].x, kruhy[i].y,
                            kruhy[i].polomer, kruhy[i].polomer);
    }

    /* ukoncenie vykreslovania */
    drawing.finish();
}

Eratostenovo sito

Chceme vypísať všetky prvočísla medzi 2 a N. Mohli by sme ísť cez všetky čísla a pre každé testovať, koľko má deliteľov (deliteľov sme už hľadali predtým), ale vieme to spraviť aj rýchlejšie. Použijeme algoritmus zvaný Eratostenovo sito.

  • Vytvoríme pole A pravdivostných hodnôt, kde A[i] nám hovorí, či je i ešte potenciálne prvočíslo.
  • Na začiatku budú všetky hodnoty true, lebo sme ešte žiadne číslo nevylúčili.
  • Začneme číslom 2 - toto je iste prvočíslo (tak ho vypíšeme). O jeho násobkoch však vieme, že iste nemôžu byť prvočísla - nastavíme preto pre každý násobok j=2*k pravdivostnú hodnotu A[j] na false.
  • Potom prechádzame v poli, kým nenájdeme najbližšiu ďalšiu hodnotu true. Toto číslo je prvočíslo (vypíšeme ho) a vyškrtáme jeho násobky.
#include <iostream>
using namespace std;

int main(void) {
    const int N = 25;
    bool A[N + 1];

    for (int i = 2; i <= N; i++) {
        A[i] = true;
    }
    for (int i = 2; i <= N; i++) {
        if (A[i]) {
            cout << i << " ";
            for (int j = 2 * i; j <= N; j = j + i) {
                A[j] = false;
            }
        }
    }
    cout << endl;
}

Výstup programu

2 3 5 7 11 13 17 19 23

Priebeh programu:

0  1  2  3  4  5  6  7  8  9 10 11 12 ...
?  ?  T  T  T  T  T  T  T  T  T  T  T ... na zaciatku
?  ?  T  T  F  T  F  T  F  T  F  T  F ... po vyskrtani i=2
?  ?  T  T  F  T  F  T  F  F  F  T  F ... po vyskrtani i=3
?  ?  T  T  F  T  F  T  F  F  F  T  F ... dalej sa uz skrtaju len vacsie cisla

Cvičenie: Napíšte funkciu, ktorá uloží prvočísla medzi 2 a N do poľa (ak by sme ich chceli použiť na ďalšie výpočty).

Ďalšie príklady na prácu s poľom

  • Načítajte pole čísel a vypíšte ho v opačnom poradí.
  • Skúste poradie povymieňať priamo v poli a nie iba pri výpise.
  • Načítajte pole čísel, náhodne ich premiešajte a zase vypíšte.

Polia: zhrnutie

  • Pole je tabuľka hodnôt. V poli dĺžky n máme hodnoty p[0], p[1], ..., p[n-1]
  • Kopírovanie a porovnávanie polí si musíme naprogramovať
  • C resp. C++ nekontrolujú, či index nie je mimo rozsahu poľa

Parametre funkcií - prehľad, opakovanie

  • Jednoduché typy, napr. int, double, bool
    • Bez & sa skopíruje hodnota
    • S & premenná dostane vo funkcii nové meno
void f1(int x) {
    x++; // zmena x sa neprenesie do main (y zostane rovnaká)
    cout << x << endl;
}

void f2(int &x) {
    x++; // zmena x sa prenesie ako zmena y v main
    cout << x << endl;
}

int main(void) {
    int y = 0;
    f1(y);
    f2(y);
    f1(y + 1);
    //zle: f2(y+1);
}
  • Polia odovzdávame bez &
    • väčšinou potrebujeme poslať aj veľkosť poľa, ak nie je globálne známa
    • zmeny v poli zostanú aj po skončení funkcie
void f(int a[], int n) {
    for (int i = 0; i < n; i++) {
        cout << a[i] << endl;
    }
}

int main(void) {
    int b[3] = {1, 2, 3};
    f(b, 3);
}
  • SVGdraw obrázky sú v skutočnosti objekty, väčšinou ich chcete posielať s &
    • všetky zmeny na nich spravené pretrvávajú aj po skončení funkcie
#include "SVGdraw.h"
void kresli(SVGdraw &drawing, int n, int y, int rozostup, int velkost) {
   for(int i=0; i<n; i++) {
       drawing.drawRectangle(i*rozostup+velkost, y, velkost, velkost);
   }
}
int main(void) {
    SVGdraw drawing(320, 100, "stvorce.svg");
    kresli(drawing, 10, 50, 30, 10);
    drawing.finish();
}
  • Štruktúry (struct) väčšinou tiež posielame pomocou &
  • Návratové hodnoty:
    • ak je výsledkom funkcie jedno číslo alebo pravdivostná hodnota, vrátime ju príkazom return
    • ak je výsledkom viac hodnôt, alebo niečo zložitejšie (pole, struct,...), odovzdáme ho ako parameter pomocou &, návratová hodnota môže zostať void
  • Tieto pravidlá súvisia so smerníkmi a správou pamäti, povieme si viac o pár týždňov