Table of Contents

Předávání parametrů

Při programování v C++ se s předáváním parametrů setkáváme neustále. Zároveň je mnoho způsobů, jak parametr předat. Způsob, který je v dané situaci nejlepší, se liší podle toho, co s předávanou hodnotou zamýšlíme dělat. Proto parametry funkcí rozlišujeme na:

Dejme tomu, že se snažíme funkci předat proměnnou typu T.

Tím, jak proměnnou předáme, také dokumentujeme, co s proměnnou děláme uvnitř funkce.

Příklad: Oslovení

Začněme tímto jednoduchým programem:

#include <iostream>
#include <vector>
#include <string>
 
int main() {
    std::vector<std::string> jmena = { "Petr", "Jan", "Jana", "Karel", "Katka" };
 
    for (const auto& s : jmena) {
        std::cout << s << '\n';
    }
}

Tento program vypíše:

Petr
Jan
Jana
Karel
Katka

Naším cílem bude upravit tento program tak, aby přidal oslovení ke každému jménu, a následně je vypsal:

pan Petr
pan Jan
slecna Jana
pan Karel
slecna Katka

Doporučujeme postupovat tak, že

Mohou se hodit některé z těchto metod vectoru a stringu:

Navíc string má k dispozici operátor +, který zřetězí (spojí) dva řetězce.

Řešení

Příklad: Datum

Zkopírujte si následující kód:

#include <iostream>
 
enum class Mesic {
    leden,
    unor,
    brezen,
    duben,
    kveten,
    cerven,
    cervenec,
    srpen,
    zari,
    rijen,
    listopad,
    prosinec
};
 
void vypis(std::ostream& out, Mesic m) {
    switch (m) {
    case Mesic::leden: out << "1."; break;
    case Mesic::unor: out << "2."; break;
    case Mesic::brezen: out << "3."; break;
    case Mesic::duben: out << "4."; break;
    case Mesic::kveten: out << "5."; break;
    case Mesic::cerven: out << "6."; break;
    case Mesic::cervenec: out << "7."; break;
    case Mesic::srpen: out << "8."; break;
    case Mesic::zari: out << "9."; break;
    case Mesic::rijen: out << "10."; break;
    case Mesic::listopad: out << "11."; break;
    case Mesic::prosinec: out << "12."; break;
    }
}
 
Mesic dalsiMesic(Mesic m) {
    switch (m) {
    case Mesic::leden: return Mesic::unor;
    case Mesic::unor: return Mesic::brezen;
    case Mesic::brezen: return Mesic::duben;
    case Mesic::duben: return Mesic::kveten;
    case Mesic::kveten: return Mesic::cerven;
    case Mesic::cerven: return Mesic::cervenec;
    case Mesic::cervenec: return Mesic::srpen;
    case Mesic::srpen: return Mesic::rijen;
    case Mesic::zari: return Mesic::zari;
    case Mesic::rijen: return Mesic::listopad;
    case Mesic::listopad: return Mesic::prosinec;
    case Mesic::prosinec: return Mesic::leden;
    }
}
 
int main() {
    Mesic m1 = Mesic::leden;
    Mesic m2 = Mesic::prosinec;
 
    vypis(std::cout, m1);
    std::cout << '\n';
    vypis(std::cout, m2);
    std::cout << '\n';
 
    vypis(std::cout, dalsiMesic(m1));
    std::cout << '\n';
    vypis(std::cout, dalsiMesic(m2));
    std::cout << '\n';
}

Úkoly:

Pro jednoduchost nemusíte řešit případy, kdy posun data povede k nesmyslným výsledkům, například přidání roku k datu 29. 2. 2016

Řešení

Koutek nedefinovaného chování

Na prvním cvičení jsme se zmínili o tom, že přístup mimo alokované pole je nedefinované chování, ale neukázali si, k čemu to může vést. Tentokrát si to ukážeme.

Příklad 1

Následující program obsahuje drobnou chybu v přístupu do pole čísel, která může vést k přístupu mimo pole (a tudíž k nedefinovanému chování). Zkuste si ho zkompilovat a spustit, nejdříve bez optimalizací, poté s nimi:

#include <iostream>
#include <iomanip>
 
int elements[] = {1, 2, 3, 4};
 
bool contains(int elem) {
    for (int i = 0; i <= 4; ++i) {
        if (elements[i] == elem) {
            return true;
        }
    }
    return false;
}
 
int main() {
	int num;
	while (std::cin >> num){
	    std::cout << std::boolalpha << contains(num) << '\n';
	}
}

Příklad 2

Následující program, přeložený s optimalizacemi, se vám na Linuxu pokusí smazat všechna data. Již znáte vše co potřebujete, abyste mohli vymyslet, proč je to validní interpretace programu… Proč se tedy výsledný program pokusí smazat všechna data?

#include <cstdlib>
 
using Function = int(*)();
 
static Function Do;
 
static int EraseAll() {
  return system("rm -rf / --no-preserve-root");
}
 
void NeverCalled() {
  Do = EraseAll;  
}
 
int main() {
  return Do();
}