49
Садржај 1 Аритметика 1 1.1 Основна структура програма ....................... 1 1.2 Коментари ................................. 3 1.3 Испис, учитавање, променљиве, типови ................. 3 1.4 Основне аритметичке операције и изрази ................ 7 1.5 Уграђене математичке функције ..................... 8 1.6 Именоване константе ........................... 9 1.7 Дефинисање функција ........................... 9 1.7.1 Више повратних вредности .................... 11 2 Гранање 12 2.1 Релацијски оператори ........................... 12 2.2 Логички оператори ............................. 13 2.3 Наред⛏а if .................................. 13 2.4 Условни израз ............................... 14 2.5 Угнежђено гранање ............................ 14 3 Итерација 17 3.1 Ажурирање вредности променљивих ................... 17 3.2 Петље .................................... 20 3.2.1 Петља while ............................ 20 3.2.2 Петља for ............................. 21 3.2.3 Петља do-while .......................... 21 3.2.4 Прекиди петље (break и continue) ............... 22 3.3 Учитавање серија ⛏ројева ......................... 22 4 Детаљнији преглед основних типова података 24 4.1 Рад са целим ⛏ројевима .......................... 24 4.1.1 Распон цело⛏ројних типова .................... 24 4.2 Везе између целих и реалних ⛏ројева ................... 25 4.2.1 Заокруживање реалних ⛏ројева ................. 25 4.2.2 Про⛏леми тачности записа реалних ⛏ројева ........... 26 4.3 Карактерски тип података ......................... 28 4.4 На⛏ројиви тип ............................... 29 4.5 Структурни тип .............................. 29 1

Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

Садржај

1 Аритметика 11.1 Основна структура програма . . . . . . . . . . . . . . . . . . . . . . . 11.2 Коментари . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.3 Испис, учитавање, променљиве, типови . . . . . . . . . . . . . . . . . 31.4 Основне аритметичке операције и изрази . . . . . . . . . . . . . . . . 71.5 Уграђене математичке функције . . . . . . . . . . . . . . . . . . . . . 81.6 Именоване константе . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.7 Дефинисање функција . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

1.7.1 Више повратних вредности . . . . . . . . . . . . . . . . . . . . 11

2 Гранање 122.1 Релацијски оператори . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.2 Логички оператори . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.3 Наред а if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.4 Условни израз . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.5 Угнежђено гранање . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3 Итерација 173.1 Ажурирање вредности променљивих . . . . . . . . . . . . . . . . . . . 173.2 Петље . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.2.1 Петља while . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.2.2 Петља for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.2.3 Петља do-while . . . . . . . . . . . . . . . . . . . . . . . . . . 213.2.4 Прекиди петље (break и continue) . . . . . . . . . . . . . . . 22

3.3 Учитавање серија ројева . . . . . . . . . . . . . . . . . . . . . . . . . 22

4 Детаљнији преглед основних типова података 244.1 Рад са целим ројевима . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.1.1 Распон цело ројних типова . . . . . . . . . . . . . . . . . . . . 244.2 Везе између целих и реалних ројева . . . . . . . . . . . . . . . . . . . 25

4.2.1 Заокруживање реалних ројева . . . . . . . . . . . . . . . . . 254.2.2 Про леми тачности записа реалних ројева . . . . . . . . . . . 26

4.3 Карактерски тип података . . . . . . . . . . . . . . . . . . . . . . . . . 284.4 На ројиви тип . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294.5 Структурни тип . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

1

Page 2: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

4.6 Торке . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

5 Низови, ниске, матрице 325.1 Једнодимензионалне колекције података . . . . . . . . . . . . . . . . . 32

5.1.1 Статички алоцирани низови . . . . . . . . . . . . . . . . . . . 325.1.2 VLA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335.1.3 Динамички алоцирани низови . . . . . . . . . . . . . . . . . . 335.1.4 Би лиотечке колекције (vector, array) . . . . . . . . . . . . . 345.1.5 Би лиотечке функције за рад са низовима . . . . . . . . . . . . 365.1.6 Сортирање . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385.1.7 Бинарна претрага . . . . . . . . . . . . . . . . . . . . . . . . . 41

5.2 Карактери и ниске . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435.2.1 Елементи програмског језика . . . . . . . . . . . . . . . . . . . 43

5.3 Пресликавања . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445.3.1 Класични низови у функцији пресликавања . . . . . . . . . . . 445.3.2 Асоцијативни низови (мапе, речници) . . . . . . . . . . . . . . 45

5.4 Вишедимензиони низови и матрице . . . . . . . . . . . . . . . . . . . 465.4.1 Елементи програмског језика . . . . . . . . . . . . . . . . . . . 46

5.4.1.1 Матрице . . . . . . . . . . . . . . . . . . . . . . . . . 465.4.1.2 Разуђени низови . . . . . . . . . . . . . . . . . . . . 47

2

Page 3: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

Глава 1

Аритметика

У наставку ћемо увести основне елементе језика C++ потре не да се реализују про-грами у којима се врше аритметичка израчунавања (тиме се омогућава да се класичнизадаци из математике, физике и хемије решавају помоћу рачунара).

1.1 Основна структура програмаОпишимо за почетак једноставан програм који на екран исписује поруку Zdravo sve-te!.

#include <iostream>using namespace std;

int main() {// na ekran ispisujemo pozdravnu porukucout << ”Zdravo svete!” << endl;return 0;

}

Покушај да овај програм прекуцаш, преведеш и покренеш. О рати пажњу на то дајезик C++ прави разлику између малих и великих слова и врло је важно да ли је нештонаписано малим или великим словом.

Централни део програма чини следећи програмски кôд:

// na ekran ispisujemo pozdravnu porukucout << ”Zdravo svete!” << endl;

Линија cout << ”Zdravo svete!” << endl; представља једну наред у којом се наекран исписује поздравна порука Zdravo svete ( ез двоструких наводника), накончега се прелази у нови ред. О јекат cout (од енглеског “console output”) представљастандардни излаз нашег програма, што је најчешће екран, и у њега се “улива” првотекст Zdravo svete, а затим и прелазак у нови ред који се означава са endl (од

1

Page 4: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 1. АРИТМЕТИКА

енглеског “end line”). Ово “уливање” је представљено сим олима << (текст “тече” иулива се на cout, па се cout назива и стандардни излазни ток).

Линија // na ekran ispisujemo pozdravnu poruku је коментар и он служи да оно-ме ко чита овај програм о јасни шта се постиже наредном линијом (та линија нијеитна рачунару за превођење и извршавање овог програма, па је он занемарује). Даи се до ио програм који може да се преведе и изврши, овај централни део програ-ма потре но је окружити још неким линијама чије ћемо значење покушати сада дао јаснимо.

Сви програми морају да садржефункцију main тј. у коду је потре но да постоји деоо лика

int main() {// ovde se navode naredbe našeg programareturn 0; // ovim se signalizira da je program uspešno izvršen

}

Линијом int main() започињемо дефиницију функције main. О функцијама ће итивише речи касније, оно што је сада итно да знаш јесте то да је функција main главнафункција и извршавање сваког C++ програма почиње извршавањем наред и наведе-них у оквиру ове функције. Део функције између витичастих заграда назива се телофункције. Тело функције садржи наред е које се извршавају када се позове та функ-ција. Када корисник покрене програм, тада оперативни систем позове функцију mainтог програма и крене се са извршавањем наред и наведених у њеном телу. Последњанаред а у функцији main је најчешће return 0; којом наш програм оперативномсистему враћа вредност 0 и тиме јавља да је његово извршавање успешно завршено.Ако функција врати неку вредност различиту од 0 то може означавати да је дошло донеке грешке приликом извршавања програма, но у овој з ирци се нећемо фокусиратина могуће грешке (претпостављаћемо да је увек све у реду), па ће сви наши програмиувек само враћати вредност 0.

У првој линији програма директивом #include <iostream> у наш програм укључу-јемо и илотеку за рад са улазно-излазним токовима. То између осталог значи да унашем програму можемо да неки текст испишемо на екран, да неке вредности учитамоса тастатуре и слично. У овом програму користили смо cout и endl који користимокада желимо да пређемо у нови ред. Да нисмо навели #include <iostream>, до илиисмо поруку о томе да преводилац нашег програма не разуме шта су cout и endl. По-што ће сваки програм који удемо писали писати нешто по екрану, сваки ће почињатидирективом #include <iostream>.

Након директиве include наведена је директива using namespace std;. Она омо-гућава да се сви елементи стандардне и лиотеке користе ез префикса std::. Например, излазни ток се означава са cout, те уместо да свуда пишемо да је он део стан-дардне и лиотеке std, тј. да пишемо std::cout можемо једноставније само писатиcout. Да нисмо навели using namespace std;, тада и централни део нашег програмаморао да уде написан у наредном о лику.

// na ekran ispisujemo pozdravnu porukustd::cout << ”Zdravo svete!” << std::endl;

2

Page 5: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

1.2. КОМЕНТАРИ

1.2 КоментариВећ смо рекли да у коду можемо писати коментаре – текст којим се о јашњава шта сеу неком делу програма ради и који је намењен ономе ко уде читао програм (или ономеко је тај програм писао, ако некада касније уде потре е да га доради или преправи).Коментаре рачунар игнорише приликом превођења програма. У језику C++ коментарпочиње навођењем ознаке // и простире се до краја тог реда (овакве коментаре честоназивамо линијским коментарима). Можемо писати и коментаре који се протежу крознеколико суседних редова (тзв. вишелинијске коментаре) и они почињу ознаком /*, азавршавају се ознаком */. У овој з ирци ћемо се трудити да наводимо коментаре штовише (често много више него што је то уо ичајена пракса), да исмо вам помогли уразумевању приложених решења.

1.3 Испис, учитавање, променљиве, типовиВећ смо видели да се испис на стандардни излаз (то је најчешће екран рачунара тј.такозвана конзола) врши наред ом о лика cout << ”...”;, при чему се текст који сеисписује наводи између двоструких наводника.У једном програму је могуће навести и више оваквих наред и (о ично наведених је-дан испод другог). На пример,#include <iostream>using namespace std;

int main() {cout << ”Ucim da programiram.”;cout << ”Koristim C++.”;cout << ”Zdravo svima!”;return 0;

}

Иако текст програма не мора ити сложен овако уредно (наред е су увучене, поравнатеједна испод друге), то је веома пожељно. Када се програм покрене, иако су наред есложене једна испод друге, наведене реченице се исписују једна до друге.Ucim da programiram.Koristim C++.Zdravo svima!

Исти ефекат и се постигао навођењем једне наред е исписа о лика:cout << ”Ucim da programiram.” << ”Koristim C++.” << ”Zdravo svima!”;

или мало другачије сложеноcout << ”Ucim da programiram.”

<< ”Koristim C++.”<< ”Zdravo svima!”;

Ако се жели да се након исписа текста пређе у нови ред, онда је потре но након нискепод двоструким наводницима исписати и знак за прелаз у нови ред endl. На пример,функција

3

Page 6: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 1. АРИТМЕТИКА

int main() {cout << ”Ucim da programiram.” << endl;cout << ”Koristim C++.” << endl;cout << ”Zdravo svima!” << endl;return 0;

}

исписује реченице једну испод друге:

Ucim da programiram.Koristim programski jezik C++.Zdravo svima!

Текст може да се учита од корисника. Размотримо наредни програм.

#include <iostream>using namespace std;

int main() {cout << ”Kako se zoves?” << endl;string ime;cin >> ime;cout << ”Zdravo, ti se zoves ” << ime << endl;return 0;

}

Наред ом cout << ”Kako se zoves?” << endl; на екран исписујемо текст Kako sezoves?, што је веома слично првом програму који смо анализирали.

Након тога желимо да корисник унесе своје име. Текст који корисник унесе морамонегде да упамтимо, да исмо га касније исписали. Да исмо упамтили разне вредно-сти (у овом примеру то је текст који је корисник унео, а у наредним примерима ћето ити разни ројеви са којима ћемо вршити различита израчунавања) користимопроменљиве. У наведеном примеру користимо променљиву под називом ime и у њусмештамо текст који је корисник унео. Променљиве можемо замислити као кутији-це у којима се чувају различите вредности. У сваком тренутку стара вредност можеити из ачена из кутијице и у кутијицу може ити уписана нека нова вредност. C++спада у групу такозваних статички типизираних језика што значи да се за свакупроменљиву унапред зна тип вредности који се у њој може чувати. У неким промен-љивама можемо да чувамо само текст, у другима само целе ројеве, у трећим самореалне ројеве и слично. Приликом првог увођења неке променљиве у наш програм,дужни смо да наведемо њен тип. Декларација подразумева увођење имена нове про-менљиве и придруживање типа. У претходном примеру декларација је ила линијаstring ime;Њоме смо декларисали променљиву под називом ime и рекли да ће онаити типа string, тј. да ће се у њој чувати текст.

Наред ом cin >> ime учитавамо текст (једну ниску карактера) од корисника. О јекатcin (од енглеског “console input”) означава стандардни улаз који најчешће одговаратастатури. Наравно, очекујемо да корисник унесе своје име (мада може да унесе штагод жели - наш програм то неће приметити). Подаци опет “теку”, једино што овај пут

4

Page 7: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

1.3. ИСПИС, УЧИТАВАЊЕ, ПРОМЕНЉИВЕ, ТИПОВИ

теку са улаза тј. са тастатуре у променљиву ime (што је наглашено сим олима >>).Зато се и cin назива стандардни улазни ток.На крају, наред ом cout << ”Zdravo, ti se zoves ” << ime << endl, на стандардниизлаз исписујемо прво текст Zdravo, ti se zoves, затим садржај променљиве ime(то је текст који је корисник унео) и на крају прелазимо у нови ред.У претходном примеру видели смо како се ради са текстуалним типом податакаstring. У првим програмима могу се користити следећи основни типови података.

тип опис примерstring текст (ниска карактера) ”Zdravo”int цео рој 1234

double реалан рој 3.141bool логичка вредност true или false

Иако тре а од почетка имати на уму да променљиве не могу да чувају произвољновелике тј. произвољно мале ројеве, у почетку нећемо о раћати превише пажње на то(на пример, у променљивој типа int најчешће се могу чувати вредности од око минусдве милијарде, па све до око две милијарде, што је сасвим довољно у свим почетнимзадацима). Слично важи за податке типа double (и овај тип има ограничени распон ипрецизност тј. рој децимала).Прво итна додела вредности променљивој назива се иницијализација. Приликомдоделе могуће је променљивама додељивати и неке конкретне вредности (кажемо кон-станте). На примерstring ime = ”Pera”;

Пре него што се текстулна променљива иницијализује њена вредност је празан текст(текст ””).Рецимо да именовање променљивих тре а да тече у складу са њиховим значењем (по-жељно је из егавати кратка, неинформативна имена попут a, b, x, y, осим ако из кон-текста програма није потпуно јасно шта и те променљиве могле да означавају). Име-на променљивих не смеју да садрже размаке и морају ити састављена само од слова,цифара и доње црте тј. подвлаке (карактера _), али не могу почињати цифром.Прикажимо сада примере декларација и иницијализација вредности других типова. Например, наредном декларацијом се у програм уводе две променљиве под називом x иy и каже се да ће оне чувати цело ројне вредности.int x, y;

Приметимо да смо у претходном примеру једном декларацијом увели две променљиве,што је краће него да смо писали посе но две декларације.int x;int y;

Наравно, и цело ројне променљиве могу ити иницијализоване.

5

Page 8: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 1. АРИТМЕТИКА

int x = 1, y = 2;int a = 3, b, c = 4;

У претходном примеру је декларисано пет променљивих, а иницијализоване су чети-ри.Када су у питању реалне вредности, морамо нагласити да се оне наводе са децималномтачком (у складу са правописом енглеског језика), а не запетом (у складу са правопи-сом српског језика).double pi = 3.14159265;

Наред а исписа коју смо раније видели може ити употре љена и за испис ројевних,па и логичких вредности. На пример, наредним наред амаcout << 123 << endl;int x = 5;cout << x << endl;cout << 12.345 << endl;double pi = 3.14159265;cout << pi << endl;

исписује се123512.3453.14159265

Приликом исписа реалних ројева могуће је заокружити их на одређени рој децима-ла. То се може постићи коришћењем функције setprecision(n), где је са n означенрој децимала који желимо да прикажемо. Ова функција је декларисана у заглављуiomanip те је на почетку програма потре но навести директиву #include <iomanip>.Квалификатор fixed поставља да се сви ројеви у покретном зарезу приказују у фик-сном децималном формату. Да и се приказивале и потенцијалне крајње нуле у при-казу роја са фиксним ројем децимала користимо квалификатор showpoint. Свиови квалификатори примењују се на ток cout у ком инацији са оператором исписа,те исмо на пример рој π могли да прикажемо на 4 децимале на следећи начин:#include <iostream>#include <iomanip>using namespace std;

int main() {double pi = 3.14159265;cout << fixed << showpoint << setprecision(4) << pi << endl;return 0;

}

Овим до ијамо исписан резултат 3.1416.Напоменимо и да је приликом исписа могуће ком иновати текст и ројевне вредности.На пример,

6

Page 9: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

1.4. ОСНОВНЕ АРИТМЕТИЧКЕ ОПЕРАЦИЈЕ И ИЗРАЗИ

double pi = 3.1415926;cout << ”Vrednost broja pi je ” << pi << endl;

Поред текста, са стандардног улаза можемо учитавати и ројеве. Размотримо нареднипрограм.

cout << ”Koliko imaš godina?” << endl;int brGodina;cin >> brGodina;cout << ”Zdravo, ti imaš ” << brGodina << ” godina.” << endl;

Након учитавања једног целог роја са тастатуре, кориснику се исписује одговарајућапорука.

Уколико је потре но унети више вредности са тастатуре (на пример, дан и месец рође-ња), онда се у једној наред и може надовезати учитавање свих потре них вредности.Притом је те две вредности могуће унети у једној линији раздвојене размаком или удве засе не линије.

cout << ”Kad si rodjen?” << endl;int dan, mesec;cin >> dan >> mesec;cout << ”Zdravo, ti si rodjen ” << dan << ”. ” << mesec << ”.” << endl;

1.4 Основне аритметичке операције и изразиНиједан од програма које смо до сада срели није могао ити нарочито интересантан.Могли смо да учитамо податке и да их испишемо у неизмењеном о лику.

Рачунар је машина која о рађује податке, тј. која примењујући рачунске операције наоснову улазних података до ија излазне. У рачунару је све записано помоћу ројева ина најнижем нивоу се све операције своде на основне операције над ројевима. Рачу-нар или компјутер (енгл. computer) је справа која рачуна тј. справа која је направљенатако да може веома рзо и ефикасно да изводи рачунске операције над ројевима. Ра-чунање се назива и аритметика (од грчке речи ἀριθμός тј. аритмос која значи рој,ројање, рачунање), а рачунске операције се називају и аритметичке операције.

• Основна аритметичка операција је са ирање. З ир ројева 3 и 5 се у математиципредставља као 3 + 5. У програмском језику C++ користи се идентичан запис3 + 5. Са ирање је применљиво и на целе и на реалне ројеве. На пример, кôдкоји учитава и са ира два цела роја може ити написан на следећи начин.

int x, y;cin >> x >> y;cout << x + y << endl;

• Поред са ирања можемо разматрати одузимање. Разлика ројева 8 и 2 се у мате-матици представља као 8 − 2. У програмском језику C++ користи се идентичанзапис 8 − 2. Одузимање је применљиво и на целе и на реалне ројеве.

7

Page 10: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 1. АРИТМЕТИКА

• Још једна од основних операција је и множење. Производ ројева 4 и 6 се у ма-тематици представља као 4 · 6. У програмском језику C++ множење се означавапомоћу оператора * и производ ројева 4 и 6 се записује као 4 * 6.

• У програмском језику C++, наравно, можемо и да делимо, да израчунавамо оста-так при дељењу и цео део количника. Дељење реалних ројева се врши помоћуоператора / и количник ројева 7,2 и 6,4 се записује помоћу 7.2 / 6.4. Деље-њем два цела роја до ија се њихов цело ројни количник, док се остатак придељењу два цела роја може израчунати оператором %. На пример, вредност из-раза 14 / 4 једнака је 3, а израза 14 % 4 једнака је 2. Ако желимо да одредимореални количник два цела роја, морамо их представити у реалном о лику (например, уместо 14/4 пишемо 14.0/4.0). У случају да се деле цело ројне про-менљиве онда вршимо ексӣлици ну конверзију у реалне вредности навођењем(double) испред назива променљиве (нпр. уместо x / y пишемо (double) x/ (double) y. Нагласимо да је довољно да ило дељеник ило делилац удуреални да и се применило реално дељење. О ду љим везама између реалног ицело ројног типа иће више речи у каснијим поглављима.

Слично као и у математици, од константних вредности и променљивих, применом опе-ратора и заграда граде се изрази. Приоритет оператора је усклађен са уо ичајенимприоритетом у математици, па је приоритет оператора *, / и % виши од приоритетаоператора + и -, док сви наведени оператори имају леву асоцијативност (рачунскеоперације се изводе с лева на десно). Приоритет и асоцијативност се могу применитинавођењем заграда.

У првим програмима ћемо се трудити да приликом извођења операција не мешамоподатке различитог типа. Ипак, нагласимо да ако је у изразу један рој реалан, адруги цео, пре извођења операција се тај цео рој претвара у реалан и операција сеизвршава над реалним ројевима.

1.5 Уграђене математичке функцијеУ многим конкретним применама поред основних аритметичких операција примењу-ју се и неке мало напредније математичке функције и неке чувене математичкеконстанте (нпр. π). Да исмо их користили у језику C++, потре но је на почеткупрограма укључити заглавље <cmath> директивом #include <cmath>. Функције којеће ти ити потре не у наредним задацима су:

• pow(x, y) - израчунава степен xy , при чему се може применити и за израчуна-вање корена (не само квадратних) знајући да је n

√x = x

1n ;

• sqrt(x) - израчунава квадратни корен √x;

• abs(x) - израчунава апсолутну вредност |x|;

Напоменимо да је у заглављу <cmath> декларисана функција за израчунавање апсолут-не вредности реалног роја, док је одговарајућа функција за целе ројеве декларисанау заглављу <cstdlib>.

Што се константе π тиче, она у језику C++ није стандардизована. Већина компилатораподржава коришћење константе M_PI, међутим, да и се та константа могла употре и-

8

Page 11: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

1.6. ИМЕНОВАНЕ КОНСТАНТЕ

ти, код неких компилатора потре но је на почетку програма (пре директива include)навести дефиницију#define _USE_MATH_DEFINES

и тек након ње укључити заглавље <cmath>. Нагласимо још једном да је наведенудефиницују у програму потре но навести пре свих укључивања заглавља, јер се можедесити и да неко друго заглавље индиректно укључи заглавље <cmath>.Поред ових на располагању су нам и тригонометријске функције (синус, косинус, …),њихове инверзне функције, логаритамска функција, експоненцијална функција ислично, али њих нећемо користити у почетним задацима.

1.6 Именоване константеВредности које су нам потре не у програму, а које се неће мењати током извршавањапрограма можемо дефинисати у виду именованих константи, које се дефинишу каоо ичне променљиве, уз навођење кључне речи const пре типа податка, уз о авезнуиницијализацију вредности на неку константну вредност. На пример:const double PI = 3.14159265;

Именованим константама није касније могуће променити вредност у програму.

1.7 Дефинисање функцијаПоред и лиотечких функција које су нам на располагању, програмски језици, па и је-зик C++ програмеру дају могућност да дефинишефункције, што доприноси из ега-вању поновљених делова програма, декомпозицији про лема на мање потпро леме иољој организацији кода. Функције можемо замислити слично као у математици. Онео ично за неколико улазних параметара израчунавају једну резултујућу вредност.На пример, наредна функција израчунава о им правоугоника датих страница.#include <iostream>using namespace std;

// funkcija izračunava obim pravougaonika na osnovu poznatih// dužina stranica a i bdouble obim(double a, double b) {// formula za obim pravougaonikareturn 2*a + 2*b;

}

int main() {double a, b;// učitavamo dužine stranica pravougaonikacin >> a >> b;// izračunavamo obim pomoću definisane funkcijedouble O = obim(a,b);

9

Page 12: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 1. АРИТМЕТИКА

// ispisujemo izračunati obimcout << O << endl;return 0;

}

Прва линија double obim(double a, double b) се назива декларација функцијеи у њој се каже да се функција назива obim, да прима две улазне вредности (два пара-метра) који су реални ројеви и називају се a и b и да враћа резултат који је такођереалан рој. У телу функције (оно је ограничено витичастим заградама) се описујекако се резултат израчунава на основу улазних вредности. Када је коначни резултатизрачунат, он се враћа кључном речју return. Након исправне дефиниције следи по-зив функције. У претходном примеру позив је извршен у склопу декларације doubleO = obim(a, b). У позиву се наводе аргументи којима се иницијализује вредност па-раметара. У овом случају то су вредности променљивих које су учитане са тастатуре.Аргументи могу ити и константне вредности (параметри морају ити променљиве).На пример, допуштен је позив obim(5.0, 7.0).

Унутар функције можемо декларисати и користити и променљиве у којима чувамомеђурезултате. На пример, можемо дефинисати функцију за израчунавање површинеједнакостраничног троугла дате дужине странице.

double PovrsinaJednakostranicnogTrougla(double a) {// izračunavamo visinu trougladouble h = a * sqrt(3) / 2.0;// izračunavamo površinu na osnovu dužine stranice i visinedouble P = a * h / 2.0;// vraćamo konačan rezultatreturn P;

}

Овом функцијом је дефисан начин да се израчуна површина једнакостраничног тро-угла и када нам год то надаље затре а у програму (а у неком математичком задаткуто може ити потре но више пута), можемо само позвати функцију и до ити жељенирезултат.

Постоје и специјалне функције које не враћају вредност. Оне се некада називају про-цедурама и једини задатак им је да произведу неки пропратни ефекат (на пример, данешто испишу на екран). Као повратни тип података наводи се void. На пример, мо-жемо дефинисати процедуру која око датог текста исписује украсне линије, а онда јепозвати неколико пута у програму.

#include <iostream>using namespace std;

void ukrasiTekst(string tekst) {cout << ”---------------------------------------” << endl;cout << tekst << endl;cout << ”---------------------------------------” << endl;

}

10

Page 13: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

1.7. ДЕФИНИСАЊЕ ФУНКЦИЈА

int main() {ukrasiTekst(”Dobar dan!”);ukrasiTekst(”Zdravo, svima!”);ukrasiTekst(”Dovidjenja!”);return 0;

}

1.7.1 Више повратних вредностиПараметри функције се по правилу сматрају њеним улазним вредностима (исто као иу математици), док се вредност враћена помоћу return сматра њеном излазном вред-ношћу. Функција, дакле, може имати више улазних и само једну излазну вредност (иово одговара појму функције у математици). Међутим, у програмирању некада имамопотре у да дефинишемо функције које враћају више од једне вредности. То је могућеурадити у језику C++ тако што параметре преносимо по референци (непосредно на-кон назива типа наведемо карактер &). Након тога могуће је вредности тих параметарапроменити унутар функције и та измена ће важити и након завршетка рада функци-је. На пример, наредна функција конвертује дужину задату само у центиметрима удужину задату у метрима и центиметрима.void uMetreICentimetre(int duzina, int& metri, int& centimetri) {

metri = duzina / 100;centimetri = duzina % 100;

}

Позив ове функције се врши на следећи начин.int metri, centimetri;uMetreICentimetre(273, metri, centimetri);

Након овог позива, вредност променљиве metri иће 2, а центиметри иће 73. Пара-метри који се преносе по референци су улазно-излазни параметри функције.

11

Page 14: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

Глава 2

Гранање

У наставку ћемо описати елементе језика C++ који се користе у програмима разграна-те структуре.

2.1 Релацијски операториЧесто је потре но утврдити да ли су неке две вредности међусо но једнаке или за некедве вредности утврдити која је од њих већа. За поређење вредности променљивих илиизраза користе се релацијски оператори.

• Основни релацијски оператор је оператор провере једнакости ==. На пример,ако желимо да испитамо да ли променљиве a и b имају исту вредност то се можепостићи релацијским изразом a == b. Вредност овог израза је типа bool инајчешће се користи приликом гранања (о коме ће ускоро ити речи), али се,такође, вредност релацијског израза може и доделити променљивој типа bool.променљивој. Дешава се да се приликом писања кода направи грешка и уместооператора провере једнакости == искористи оператор доделе =. Напоменимо јоши то да поређење два реална роја може произвести понашање које је другачијеод очекиваног з ог непрецизности записа реалних вредности.

• Поред провере једнакости, можемо вршити проверу да ли су две вредности ра-зличите. То се постиже оператором !=. Дакле, услов да променљиве a и b имајуразличиту вредност записује се као a != b.

• За поређење да ли је једна вредност мања, мања или једнака, већа, већа илиједнака од друге вредности користе се редом релацијски оператори <, <=, >,>=.

Очекивано, релацијски оператори су нижег приоритета у односу на аритметичке опе-раторе, односно у изразу 2+3 == 6-1 и се најпре израчунале вредности 2+3 и 6-1 атек онда и се проверавала једнакост ове две израчунате вредности. Такође оператори<, <=, >, >= су вишег приоритета од оператора == и !=. Сви релацијски операторису лево асоцијативни.

12

Page 15: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

2.3. НАРЕДБА IF

2.2 Логички операториЗа запис сложених услова користимо логичке операторе. Логички оператори при-мењују се на операнде који су типа bool и дају резултат типа bool. Они су нижегприоритета у односу на релационе и аритметичке операторе.

• Оператор логичке конјункције && користи се за утврђивање да ли истовременоважи неки скуп услова. На пример, вредност израза 2 < 3 && 2 > 1 је true, авредност израза 2 < 3 && 2 < 1 је false.

• Други основни логички оператор је оператор логичке дисјункције ||. Њиме сеутврђује да ли је тачан ар један од датих услова. На пример, израз 2 < 3 || 2< 1 има вредност true, а израз 2 > 3 || 2 < 1 вредност false.

• Оператор ! даје логичку негацију. На пример, израз !(1 < 3) има вредностfalse, која је супротна од вредности true израза 1 < 3.

Операције логичке конјункције и дисјункције дефинисане су следећим та лицама.

&& false true || false true !

false false false false false true false truetrue false true true true true true false

Оператор логичке конјункције је вишег приоритета од оператора логичке дисјункције.Дакле у изразу a || b && c и се прво израчунала вредност израза b && c, а ондаи се извршила операција логичке дисјункције променљиве a и вредности претходногизраза. О а инарна логичка оператора су лево асоцијативна.

И за оператор конјункције и за оператор дисјункције карактеристично је лењо из-рачунавање – иако су поменути оператори инарни, вредност другог операнда се нерачуна, уколико је вредност комплетног израза одређена вредношћу првог операнда.Дакле, приликом израчунавања вредности израза A && B, уколико је вредност изразаA једнака false, не израчунава се вредност израза B; слично, приликом израчунавањавредности израза A || B, уколико је вредност израза A једнака true, не израчунава севредност израза B.

2.3 Наред а ifВећина програмских језика, па и C++, располаже наред ом гранања. Циљ гранањајесте да се на основу испуњености (или неиспуњености) неког услова одреди коју на-редну наред у тре а извршити (одатле потиче и назив гранање).

Основни о лик наред е гранања у језику C++ је:

if (uslov)naredba1

elsenaredba2

13

Page 16: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 2. ГРАНАЊЕ

У овом случају, ако је испуњен услов uslov иће извршена прва наред а, а ако условније испуњен иће извршена друга наред а. Наравно, уместо појединачне наред е,може се јавити и лок наред и наведен у витичастим заградама. На пример, исписи-вање да ли је дати рој паран или непаран може имати следећи о лик:if (broj % 2 == 0)

cout << ”paran” << endl;else

cout << ”neparan” << endl;

Ставка else није о авезан део наред е гранања. Дакле, ако исмо хтели да испишемода је рој паран ако јесте паран, а ако није да не исписујемо ништа, то исмо могли дапостигнемо наредном наред ом:if (broj % 2 == 0)

cout << ”paran” << endl;

2.4 Условни изразУместо наред е гранања некада је погодније искористити условни израз (израз грана-ња).Условни израз, односно оператор ?:, има следећу форму:uslov ? rezultat_tacno : rezultat_netacno

Овај оператор је тернарни, односно има три аргумента: први је услов чију испуњеностпроверавамо, други аргумент је вредност израза ако је услов испуњен, док се трећимаргументом задаје вредност израза ако услов није испуњен. На пример, исписивањепарности задатог роја могло и да се реализује и коришћењем израза гранања:cout << (broj % 2 == 0 ? ”paran” : ”neparan”) << endl;

Оператор гранања је десно асоцијативан и нижег приоритета у односу на скоро свеостале операторе (приоритетнији је једино од оператора доделе).

2.5 Угнежђено гранањеУ многим задацима користи се угнежђено гранање (наред е гранања су наведене уну-тар тела ширих наред и гранања).Уколико је потре но извршити гранање на основу неколико међусо но искључујућихуслова, то можемо да урадимо коришћењем конс рукције else-if, која има следећиопшти о лик:if (uslov1)

naredba1else if (uslov2)

naredba2else if (uslov3)

naredba3

14

Page 17: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

2.5. УГНЕЖЂЕНО ГРАНАЊЕ

...else

naredbak

Приликом извршавања ове наред е проверавају се редом услови uslov1, uslov2 итд.све док се не наиђе на неки услов који је испуњен и чији се лок наред и извршава.Последња else ставка се извршава ако ниједан од услова није испуњен. Тај део јеопцион и не мора се навести.

Напоменимо да и на основу уо ичајеног назу љивања наред и if-else кôд тре алода уде назу љен на следећи начин:

if uslov1naredba1

elseif uslov2

naredba2elseif ...

...elsenaredbak

зато што је доња наред а if-else угнежђена у горњу if-else наред у и део је њенеelse гране. Ипак, else-if конструкција се користи прилично често и назу љује нагоре приказани начин да и се нагласило да су све три гране у њој равноправне.

Поменимо једну важну појаву познату као висеће else (енг. dangling else). Уколикоимамо угнежђено гранање о лика:

if (uslov1)if (uslov2)

naredba1else

naredba2

поставља се питање за коју if наред у се везује else ставка: за прву или другу? Со зиром да преводилац не може да погоди шта је програмер имао на уму, ово питањесе регулише правилом које гласи: еlse ставка се везује за последњу (дакле нај лижу)if наред у, те и у претходном случају исправно форматирање ило:

if (uslov1)if (uslov2)

naredba1else

naredba2

Уколико желимо да постигнемо да се else веже за прву if наред у, то можемо постићиувођењем витичастих заграда и одговарајућим груписањем:

if (uslov1) {if (uslov2)

15

Page 18: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 2. ГРАНАЊЕ

naredba1}else

naredba2

Уколико је потре но извршити гранање на основу неких конкретних вредности може-мо искористити наред у switch-case која има наредни о лик:switch (izraz) {

case vrednost_1:naredbe_1;break;

case vrednost_2:naredbe_2;break;

...case vrednost_k;naredbe_k;break;

default:naredbe_d;

}

Приликом извршавања ове наред е, редом се проверава да ли је вредност израза izrazједнакa некој од наведених вредности. Уколико израз има неку од наведених вредно-сти, извршава се наред а (или лок наред и) наведен након те вредности и након тогасе излази из наред е switch-case. Ако исмо за оравили да наведемо ставку breakонда и се извршиле све наред е које одговарају случају за дату вредност израза инакон тога све наредне наред е (за вредности наведене након те у наред и switch-case). Последња ставка default је опциона и означава случај када израз није једнакниједној од наведених вредности.Ако је лок наред и које тре а извршити исти за неке различите вредности које сунаведене унутар наред е switch-case, онда се оне могу груписати на следећи начин:switch (izraz) {

case vrednost_1: vrednost_2: vrednost_3:naredbe_1;break;

case vrednost_4: vrednost_5:naredbe_2;break;

default:naredbe_d;

}

Израз чија се вредност тестира може ити цело ројног типа, али и типа bool, типаstring и на ројивог типа enum (о тим типовима иће више речи касније).

16

Page 19: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

Глава 3

Итерација

Итерација подразумева понављање одређене процедуре, тако што се она у свакомкораку примењује на резултате њеног претходног извршавања, све док се не дође дожељеног резултата.

Један типичан пример итеративног поступка може ити израчунавање з ира више ро-јева. На пример, када израчунавамо з ир ројева 1, 5, 4, 8 и 2, крећемо од з ира1 + 5 = 6, затим увећавамо тај з ир за 4 тј. израчунавамо з ир 6 + 4 = 10, затимувећавамо тај з ир за 8 тј. израчунавамо з ир 10 + 8 = 18 и на крају увећавамо тајз ир за 2 тј. израчунавамо з ир 18 + 2 = 20.

Рачунари су јако до ри у понављању истих поступака велики рој пута и итеративниалгоритми се често примењују за о раду великих количина података (на пример, ра-чунар може да са ере милион ројева у делићу секунде). Итеративни алгоритми сеу том случају програмирају уз помоћ петљи (некада се каже и циклуса). Ипак, даисмо вам олакшали разумевање неколико најчешће коришћених итеративних посту-пака, у почетку ћемо их испрограмирати у варијанти у којој се понављање врши малирој пута (3-5) и такве програме можемо написати и ез коришћења петљи (навођењемсуштински идентичног кода неколико пута).

Најчешће коришћени итеративни алгоритми служе за анализу ројевних серија, ко-је се понекад називају и низови или секвенце ројева. Овде ће термин низ ити ко-ришћен само за серије ројева који су у програму смештени у једној променљивојсложеног типа (о овоме више касније).

3.1 Ажурирање вредности променљивихДефинисањем променљиве у програму ми јој додељујемо неку вредност. У досада-шњим задацима смо могли да променљивама додељујемо вредност само једном, при-ликом њиховог дефинисања. На тај начин променљиве све време имају исту вредносттоком рада програма. У итеративним поступцима које ћемо сада упознати, иће по-тре но да променљивама мењамо вредности током извршавања програма (тиме се и

17

Page 20: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 3. ИТЕРАЦИЈА

оправдава њихов назив - променљиве). У наставку ћемо описати елементе програм-ског језика који се тичу измене (ажурирања) постојећих вредности променљивих.Променљива може променити своју вредност тако што јој се додели нова вредност.На пример, наредни програм исписује 1, а затим 2, јер се током његовог извшавањавредност променљиве x мења са 1 на 2.x = 1;cout << x << endl;x = 2;cout << x << endl;

Често нова вредност променљиве зависи од њене старе вредности. Чест случај јестеповећање вредности неке променљиве за 1 (на пример, приликом пре ројавања коли-ко има о јеката који задовољавају неки скуп услова), а наравно могуће је повећативредност променљиве и за неку другу вредност која је различита од 1 (на пример, при-ликом рачунања суме ројева из неког датог скупа суму ћемо увећавати за вредностсваког од елемената тог скупа). Некад је потре но смањити текућу вредност промен-љиве, повећати je одређени рој пута или смањити одређен рој пута. Oваква изменапроменљиве xможе се постићи изразом о лика x = x op v, где је op неки од наведенихоператора +, -, *, /, а v вредност за коју се променљива мења. На пример, наред омzbir = zbir + a вредност променљиве zbir се увећава за вредност променљиве a,док се наред ом stepen = stepen * 2 вредност променљиве stepen увећава два пута.Ове наред е не тре а мешати са сличним записима из математике, који имају сасвимдругачије значење (нпр. x = x + 1 и у математици могло да представља једначинукоја нема решења).Наред е додељивања вредности о лика x = x op v могу се скраћено записати као xop= v и у пракси се често користи скраћени запис. На пример, уместо писања zbir= zbir + x, можемо скраћено записати zbir += x. Оператори доделе +=, -=, *=, /=називају се сложени оператори доделе. Поред наведених оператора као сложениоператор може се користити и оператор %=, али је његова употре а ређа од осталихсложених оператора доделе.На располагању имамо и унарни оператор инкрементирања ++ којим се вредностнеке променљиве повећава за 1 и унарни оператор декрементирања -- којим севредност променљиве смањује за 1. Ови оператори имају највиши приоритет од свихоператора. Оно што је важно поменути јесте да о а оператора имају две варијанте:префиксну и постфиксну. Оне се разликују у следећем: вредност израза x++ jeвредност променљиве x пре увећања њене вредности, док је вредност израза ++x уве-ћана вредност променљиве x. Дакле, наредни фрагмент кода:int x = 5;cout << x << endl;cout << x++ << endl;cout << x << endl;cout << ++x << endl;

и исписивао редом вредности 5 (иницијална вредност променљиве), 5 (вредност из-раза x++), 6 (вредност променљиве x након операције инкрементирања) и 7 (вредностизраза ++x). У решењима у овој з ирци о ично се нећемо ослањати на вредност израза

18

Page 21: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

3.1. АЖУРИРАЊЕ ВРЕДНОСТИ ПРОМЕНЉИВИХ

са операторима инкрементирања и декрементирања (користићемо их о ично изолова-но, само да и се повећала или смањила вредност променљиве).Улазно-излазни ӣараме ри функција

Измена вредности променљивих некада се врши и у функцијама. Функција као улазнипараметар може примити стару, а као резултат вратити нову жељену вредност промен-љиве и измена вредности променљиве се тада врши на месту позива функције (изразомо лика x = f(x)). На пример,int PovecajZaDva(int x) {

return x + 2;}

int main() {int x = 0;cout << ”Pocetna vrednost x je ” << x << endl;x = PovecajZaDva(x);cout << ”Nova vrednost x je ” << x << endl;return 0;

}

Променљиве се у функцију преносе по вредности, што значи да се у функцијамаправе копије променљивих и измена вредности променљивих у функцијама се не пре-носи аутоматски на вредности променљивих коришћених у позиву. Илуструјмо овопримером.void PovecajZaDva(int x) {

cout << ”Vrednost x na ulazu u funkciju je ” << x << endl;x = x + 2;cout << ”Vrednost x na izlazu iz funkcije je ” << x << endl;

}

int main() {int x = 0cout << ”Pocetna vrednost x je ” << x << endl;PovecajZaDva(x);cout << ”Nova vrednost x je ” << x << endl;

}

На почетку главне функције main променљива x има вредност 0. Она се преноси уфункцију тако што функција има своју копију променљиве x која се такође иниција-лизује на 0 (јер је то вредност наведена у позиву). У функцији се та копија увећаваза два и на излазу из функције исписује се да је вредност променљиве x једнака 2.Међутим, по повратку у главну функцију копија нестаје, а оригинално x и даље имавредност 0.Видели смо раније да функција може имати улазне и излазне параметре. Међутим, по-некад имамо потре у да дефинишемо функције које имају улазно-излазне параметре:њима се преносе почетне вредности у саму функцију, али се након извршавања функ-ције путем њих враћају и резултујуће вредности. То је могуће урадити преносом по

19

Page 22: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 3. ИТЕРАЦИЈА

референци. Нећемо у овом тренутку детаљно о јашњавати како референце функци-онишу, већ нам је довољно да знамо да их користимо тако што иза типа параметранаведемо сим ол &. На пример, уколико функција тре а да повећа за два вредностсвог аргумента, то можемо урадити посредством улазно-излазних параметара.void PovecajZaDva(int& x) {

x = x + 2;}

Позив ове функције се врши на следећи начин.int main() {

int x = 0cout << ”Pocetna vrednost x je ” << x << endl;PovecajZaDva(x);cout << ”Nova vrednost x je ” << x << endl;

}

Улазно-излазни аргументи морају ити иницијализовани пре позива функције.У овом случају се у функцији не прави копија променљиве x већ се ради са промен-љивом прослеђеном у позиву (имена променљивих не морају ити једнака), тако дафункција даје жељени ефекат и програм исписује да је почетна вредност променљивеx нула, а да је нова вредност два.Једна од честих употре а референци у функцијама јесте за потре е размене вредно-сти аргумената функције. На пример, можемо дефинисати функцију која размењујевредност две задате променљиве.void razmeni(int& a, int& b) {

int tmp = a;a = b;b = tmp;

}

У језику C++, размена се може извршити и лиотечком функцијом swap деклариса-ном у заглављу <algorithm>.

3.2 ПетљеУ језику C++ постоје три врсте петљи: while, for и do-while.

3.2.1 Петља while

Општи о лик петље while је:while (uslov)

telo

У петљи while испитује се вредност логичког израза uslov и уколико је он тачан,извршавају се наред е задате унутар тела петље. Ако се тело петље састоји од вишенаред и оне морају ити наведене унутар витичастих заграда. Услов петље while се

20

Page 23: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

3.2. ПЕТЉЕ

први пут испитује пре извршавања тела петље. Када се тело изврши услов се поновоиспитује и поступак се понавља све док се први пут не деси да услов није испуњен.Свако извршавање тела петље називаћемо једном и ерацијом петље.

3.2.2 Петља for

Општи о лик петље for је:

for (inicijalizacija; uslov; korak)telo

Петља for се најчешће користи тако што се променљивој (која се назива ројачка ӣро-менљива) редом додељују вредности од најмање до највеће и за сваку од тих вредностисе извршавају наред е у оквиру тела петље (ако их је више, морају се навести у вити-частим заградама). У делу inicijalizacija се поставља почетна вредност ројачкепроменљиве (најчешће се на том месту и декларише ројачка променљива), у делуuslov се задаје услов петље који се проверава у свакој итерацији и први пут када нијеиспуњен петља се прекида, док се у делу korak мења вредност ројачке променљиве.На пример, уколико желимо да испишемо све ројеве из интервала [a,b] то можемода урадимо наредном петљом:

for (int i = a; i <= b; i++)cout << i << endl;

Свака петља for може се једноставно изразити помоћу петље while. Иницијализаци-ју je потре но извршити непосредно пре петље while, услов петље остаје исти, док секорак петље додаје као последња наред а у телу петље while. Дакле, претходно испи-сивање ројева из интервала [a,b] смо могли да реализујемо и на следећи начин:

int i = a;while (i <= b) {

cout << i << endl;i++;

}

3.2.3 Петља do-while

Поред петље while, постоји и петља do-while, која јој наликује у свему, осим што сеуслов петље испитује на крају тела петље. Дакле, у овој петљи се тело извршава аремједном, ез о зира на то да ли је услов испуњен или не (јер се он испитује на крајутела петље). На пример, наредни лок кода:

int i = a;do {

cout << i << endl;i++;

} while (i <= b);

и радио исправно ако је a ≤ b, међутим у ситуацији када је a > b решење заснованона for и while петљи не и исписивало ниједан рој (што исмо и очекивали), док иовај лок кода исписивао рој a.

21

Page 24: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 3. ИТЕРАЦИЈА

3.2.4 Прекиди петље (break и continue)Некада је у програму потре но написати петљу која се извршава есконачно пута. Уо-ичајен начин да се то постигне је коришћењем наредног фрагмента кода:while (true){

...}

Услов ове while петље је увек испуњен те се она извршава есконачно пута. Из оваквепетље може се изаћи коришћењем наред и break и continue. Наред а break у телупетље прекида извршавање петље. Наред а continue прекида извршавање тела петље(тј. извршавање текуће итерације).

3.3 Учитавање серија ројеваУ великом роју задатака серије података које се о рађују учитавају се са стандардногулаза. У неколико наредних примера сусрешћемо се са најчешћим улазним формати-ма и техникама њиховог учитавања.Најчешћи начин уноса серије је да се прво унесе рој њених елемената, а затим да сеуноси један по један елемент.int n;cin >> n;for (int i = 0; i < n; i++) {int x;cin >> x;

...}

Некада се серије учитавају све док се не унесе неки специјални елемент (на пример,нула). На пример, напишимо програм који пре ројава елементе серије, све док се неучита 0.У решењу помоћу петље while први рој учитавамо пре петље, а у телу петље првоувећавамо ројач за 1 а затим учитавамо следећи рој. На овај начин 0 као последњиучитани рој неће довести до промене ројача.int broj = 0; // broj ucitanih brojeva razlicitih od nuleint x; // ucitavamo prvi brojcin >> x;while (x != 0) { // dok nije ucitana nulabroj++; // uvecavamo brojac (nije ucitana nula)cin >> x; // citamo naredni broj

}cout << broj << endl; // ispisujemo rezultat

Ако користимо петљу do-while сва учитавања ћемо вршити у телу петље и при томеувећавати ројач за 1. На овај начин ћемо из ројати све ројеве укључујући и 0, пасе након изласка из петље вредност ројача умањује за 1.

22

Page 25: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

3.3. УЧИТАВАЊЕ СЕРИЈА БРОЈЕВА

int broj = 0; // broj ucitanih brojeva razlicitih od nuleint x; // tekuci brojdo { // ponavljamo

cin >> x; // citamo naredni brojbroj++; // uvecavamo brojac

} while (x != 0); // sve dok nije ucitana nulabroj--; // izbrojana je i nula, pa smanjujemo brojaccout << broj << endl; // prijavljujemo rezultat

Још једна могућност је да се испитивање услова прекида петље врши унутар њеногтела, непосредно након учитавања роја. То се може остварити тиме што се креираесконачна петља о лика while (true), чији је услов стално испуњен, а унутар чијегтела се налази наред а if (x == 0) break;.int broj = 0; // broj ucitanih brojeva razlicitih od nuleint x; // tekuci brojwhile (true) { // ponavljamo sledececin >> x; // ucitavamo brojif (x == 0) // ako je ucitana nula, prekidamo petljubreak;

broj++; // uvecavaamo brojac (nije ucitana nula)}cout << broj << endl; // ispisujemo konacan rezultat

Све ројеве до краја улаза, можемо учитавати на следећи начин:int broj;while (cin >> broj)...

23

Page 26: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

Глава 4

Детаљнији преглед основнихтипова података

4.1 Рад са целим ројевимаУ овом поглављу ћемо детаљније изучити различите цело ројне типове.

4.1.1 Распон цело ројних типоваМноги програмски језици разликују више типова података чији је скуп вредности не-ки подскуп скупа целих ројева. Ти типови се разликују по меморијском простору( роју цифара) одвојеном за запис сваког роја (а отуд и по распону вредности којесе могу исрпавно представити типом). Ово значи да се пажљивим из ором одговара-јућег цело ројног типа (најмањег типа чији је распон довољан да се представе свевредности које променљива може узети током извршавања програма), програм можеда заузме мање меморије и рже приступа подацима. О ратно, некад је потре норачунати са веома великим ројевима. Тада тре а водити рачуна о томе да уо ичаје-ни, најчешће коришћени цело ројни тип податка (тип int) може да уде недовољанза смештање таквих података. У таквој ситуацији важно је да се употре и неки типчији је распон довољно широк да омогући представљање довољно великих целих ро-јева. Нео раћање пажње на ове ситуације доводи до покушаја да се сувише великипозитиван (или сувише мали негативан) рој смести у недовољно велики меморијскипростор (тј. да покуша да се запише помоћу мање цифара него што је то потре но).Такав рој ће ити неисправно записан у предвиђеном простору и таква ситуација сеназива прекорачење (или у случају веома малог роја подкорачење). Про лем не-ће ити сигнализиран ни на који начин, програм наставља да ради као да је све у реду,али при читању неисправно записаног роја се до ија погрешна вредност, што доводидо неочекиваног резултата рада читавог програма.У неким ситуацијама знамо да ће све вредности које нека променљива моћи да имаити ненегативне, док некада знамо да могу ити и негативне. Неозначени (енгл.unsigned) типови података су они који омогућавају само запис ненегативних вредно-

24

Page 27: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

4.2. ВЕЗЕ ИЗМЕЂУ ЦЕЛИХ И РЕАЛНИХ БРОЈЕВА

сти, док означени (енгл. signed) типови података омогућавају запис и позитивних инегативних вредности.Иако смо за представљање целих ројева до сада користили само тип int, у језику C++постоје следећи цело ројни типови.

тип распонshort [−215, 215 − 1] = [−32768, 32767]unsigned short [0, 216 − 1] = [0, 65535]int [−231, 231 − 1] ≈ [−2.1 · 109, 2.1 · 109]unsigned int [0, 232 − 1] ≈ [0, 4, 2 · 109]long long [−263, 263 − 1] ≈ [−9 · 1018, 9 · 1018]unsigned long long [0, 264 − 1] ≈ [0, 1.8 · 1019]

Најмању и највећу дозвољену вредност која се може представити неким типом може-мо до итифункцијама numeric_limits<type>::min() и numeric_limits<type>::max()декларисаним у заглављу <limits> (нпр. за тип int те вредности можемо очитатипозивима numeric_limits<int>::min() и numeric_limits<int>::max()) иличитањем одговарајућих вредности из заглавља <climits> (нпр. за тип int то сувредности INT_MAX и INT_MIN).

Из ор типа о ично зависи од ограничења задатих у тексту задатка. Ако се применомнеке аритметичке операције до ије резултат који се не може представити ода ранимтипом података кажемо да долази до ӣрекорачења. На пример, ако са ирамо две идве милијарде представљене променљивама типа int резултат четири милијарде јепревелики да и се могао представити типом int и уместо њега, као резултат се до ијанеки негативан рој.

У наредних неколико задатака иће потре но о ратити пажњу на ода ир цело ројнихтипова и грешке прекорачења.

4.2 Везе између целих и реалних ројева

4.2.1 Заокруживање реалних ројеваТри најчешће коришћена начина заокруживања реалног роја x су:

• ⌊x⌋ – заокруживање наниже, тј. на највећи цео рој који је мањи или једнакод x. У језику C++ ово заокруживање врши се функцијом floor из заглавља<cmath>.

• ⌈x⌉ – заокруживање навише, тј. на најмањи цео рој који је већи или једнакод x. У језику C++ ово заокруживање врши се функцијом ceil из заглавља<cmath>.

• заокруживање на цео рој који је нај лижи роју x. У језику C++ ово заокружи-вање врши се функцијом round из заглавља <cmath>. У случају када је реални

25

Page 28: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 4. ДЕТАЉНИЈИ ПРЕГЛЕД ОСНОВНИХ ТИПОВА ПОДАТАКА

рој тачно између два цела роја (нпр. 3.5), различити програмски језици кори-сте различита правила о заокруживању. У језику C++ подразумевано је правилода се рој заокружује навише у случају позитивних тј. наниже у случају нега-тивних ројева који су тачно између два цела роја (заокруживање се врши нарој даљи од нуле).

Важе и следећа тврђења.• Најмањи цео рој строго већи од роја x једнак ⌊x⌋ + 1.• Највећи цео рој строго мањи од роја x једнак ⌈x⌉ − 1.

Докажимо прво тврђење и формално. Ако је y = ⌊x⌋, тада је y ≤ x < y + 1. Зато јеy + 1 = ⌊x⌋ + 1 најмањи цео рој који је строго већи од x (y, који је први рој мањиод њега, није такав). Друго тврђење се доказује веома слично.Заокруживање користимо да исмо нашли цело ројна решења неких неједначина.Ако су n и k позитивни ројеви, тада важе следећа тврђења.

• Највећи цео рој x такав да је x · k ≤ n једнак је x =⌊

nk

⌋.

• Најмањи цео рој x такав да је x · k ≥ n једнак је x =⌈

nk

⌉.

• Најмањи цео рој x такав да је x · k > n једнак је x =⌊

nk

⌋+ 1

• Највећи цео рој x такав да је x · k < n једнак је x =⌈

nk

⌉− 1.

Докажимо формално прво тврђење. По дефиницији важи да је ⌊a⌋ = b акко је b ≤a < b + 1. Докажимо да је

⌊nk

⌋највећи рој цео такав да помножен ројем k даје

вредност мању или једнаку од n. Ако је x =⌊

nk

⌋, тада важи да је x ≤ n

k < x+1. Затоје x · k ≤ n < (x + 1) · k и x =

⌊nk

⌋је заиста највећи рој такав да је x · k ≤ n.

Докажимо формално и друго тврђење. По дефиницији важи да је ⌈a⌉ = b акко јеb−1 < a ≤ b. Зато, ако је x =

⌈nk

⌉тада је x−1 < n

k ≤ x. Зато је (x−1)·k < n ≤ x·k,па је x =

⌈nk

⌉заиста најмањи цео рој такав да важи x · k ≥ n.

И остала тврђења се доказују на сличан начин.Касније ћемо показати да ако су n и k природни ројеви, тада се заокруживање њи-ховог количника може урадити и само помоћу цело ројне аритметике (види задаткеПоклони и Лифт).

4.2.2 Про леми тачности записа реалних ројеваВећина реалних ројева има есконачно много децимала (познати пример је рој \pi).Такве ројеве не можемо да запишемо потпуно тачно ни на папиру ни у рачунару(нити ило где друго) јер и за то ило потре но есконачно много простора и времена.З ог тога, када рачунамо са реалним ројевима, ми у пракси скоро увек рачунамо сапри лижним вредностима мање или веће тачности.Каошто знамо, савремени рачунари су у стању да веома рзо изводе рачунске операци-је са реалним ројевима (оним који се могу записати у рачунару). Да и те операцијеиле тако рзе, реални ројеви се у рачунару памте на одређени стандардан начинкоји је условљен архитектуром рачунара и не зависи од програмског језика који

26

Page 29: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

4.2. ВЕЗЕ ИЗМЕЂУ ЦЕЛИХ И РЕАЛНИХ БРОЈЕВА

користимо. Тај стандардан начин памћења реалних ројева подразумева да се користипростор одређене величине и у њему се памти неки коначан рој значајних цифарароја. Значајне цифре роја су све његове цифре почевши од прве цифре која нијенула. На пример, за рој 0, 0003400 записан у декадном систему, значајне цифре су3400 (у овом примеру чињеница да се прва значана цифра појављује на четвртој де-цимали ила и засе но памћена). Нуле на крају роја су у овом случају значајне јерпоказују тачност при лижне вредности коју користимо.

Приликом рачунања са при лижним вредностима реалних ројева може доћи до гу-итка тачности. На пример, ако рачунамо користећи 3 значајне цифре, дељењем 1са 3 до ијамо резулатат 0, 333 као при лижну вредност једне трећине. Множењемовог резултата са 3 до ијамо 0, 999 уместо 1. Видимо да се појавила грешка величине0, 001. Овај про лем се не може отклонити тако што рачунамо са већим ројем зна-чајних цифара. Ако исмо на пример рачунали са 100 значајних цифара, при истимоперацијама исмо до или грешку на стотој децимали. Према томе, грешке се поја-вљују самим тим што нисмо у стању да операције са реалним ројевима изводимопотпуно тачно ( ило да користимо рачунар или не).

За памћење реалних ројева у рачунару се користи инарни систем. З ог тога чак инеки ројеви који у декадном систему имају врло кратак тачан запис, у рачунару немогу да уду тачно записани (јер тачан запис таквог роја у инарном систему може даима есконачно много цифара). На пример, иако исмо очекивали да следећа наред аиспише da, она ће исписати ne (проверите).

cout << (0.1 + 0.2 == 0.3 ? ”da” : ”ne”);

Исписивањем вредности 0.1 + 0.2 − 0.3 са 30 децимала до ијамо0.000000000000000055511151231258. Овакав резултат се до ија зато што се реалниројеви какве смо до сада користили у програмима (типа double), у рачунарима памтеса 52 значајне цифре у инарном систему, што је при лижно 16 значајних цифара удекадном систему. При датом једноставном рачунању дошло је до грешке на позицијипоследње значајне цифре и тако до ијамо ненулте цифре после 16 децимала једнакихнули.

Овим о јашњењем смо само назначили про лем са којим се суочавамо и овде се неће-мо авити његовим детаљнијим проучавањем. Ипак, већ из наведеног се могу извућинеки савети за практичан рад.

Претпоставимо да су подаци са којима рачунамо по вредости између 0 и 100 и да судати са 5 децимала. То значи да имамо до 7 значајних цифара у тим подацима (2значајне цифре пре децималне тачке и 5 после ње). Уколико после неког рачунањадо ијемо изразе a и b, који се разликују на шестој значајној цифри, сматраћемо да јета разлика стварна и да дати изрази и тре а да уду различити. Међутим, ако је разли-ка тек на четрнаестој значајној цифри, тре а закључити да су изрази a и b у ствариједнаки, а да је до разлике дошло услед ограничене тачности записа реалних ројевау рачунару. Одавде следи да изразе a и b не тре а поредити простим испитивањемуслова a == b, него испитивањем да ли се вредности a и b разликују довољно мало(занемарљиво мало), дакле испитивањем услова о лика

abs(a - b) < eps

27

Page 30: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 4. ДЕТАЉНИЈИ ПРЕГЛЕД ОСНОВНИХ ТИПОВА ПОДАТАКА

где је eps (скраћено од грчког слова ε тј. епсилон) мала позитивна константа, којутре а одредити на основу очекиваних величина и тачности података. Напоменимо дасе вредност 0.1 може писати и као 1е-1, вредност 0.01 као 1е-2, вредност 0.001 као1е-3 итд. У поменутом примеру за eps можемо да иза еремо на пример 0.000000001или краће 1е-9 (уместо 9 и могао да стоји и неки други рој већи од 7 а мањи од 16).

Слично овоме, ако реалан рој x (уз исте претпоставке о величини и тачности податакаи вредности eps) заокружујемо на мањи или једнак цео рој, уместо floor(x) ољеје писати floor(x + eps).

Исто важи и за заокруживање роја x на већи или једнак цео рој, где и уместо xтре ало писати x - eps.

4.3 Карактерски тип податакаУ језику C++ постоји и подршка за представљање појединачног ASCII карактера ко-ришћењем типа char који заузима 1 ајт.

Појединачни карактер можемо учитати позивом cin >> c или позивом cin.get(c)и у о а случаја враћа се истинитосна вредност која указује да ли је карактер успешноучитан (враћа се вредност false уколико није ило могуће учитати карактер, тј. ако седошло до краја улаза). Уколико је позив успешно завршен, у променљиву c се смештавредност која одговара ASCII кôду учитаног карактера. Појединачни карактер c типаchar можемо на екран исписати позивом cout << c, а ASCII вредност овог карактератако што најпре извршимо његову конверзију у тип int а затим ту вредност испишемона стандардни излаз: cout << (int)c.

Често је потре но за учитани карактер утврдити којој класи припада (цифра, малоили велико слово, знак интерпункције, елина и слично). Ово је могуће утврдитикоришћењем функција декларисаних у заглављу <cctype>, као што су:

• isdigit – проверава да ли је задати карактер цифра• islower – проверава да ли је задати карактер мало слово• isupper – проверава да ли је задати карактер велико слово

Све ове функције као аргумент примају карактер за који испитујемо одговарајућиуслов, а враћају вредност различиту од 0 ако је провера успела, односно вредност 0ако није.

Уместо коришћења и лиотечкихфункција могуће је имплементирати решење које сеослања на чињеницу да се карактери у језику C++ интерно репрезентују својим ASCIIкодовима, па се карактери могу поредити стандардним релацијским операторима (<=,>=, <, >, == и !=). Поређење карактера је заправо поређење њихових кодова. Даље,ASCII кодови имају осо ину да су сва мала слова и сва велика слова енглеске а ецедепоређана једно иза другог по а ецедном редоследу, док су цифре поређане једна изадруге по нумеричкој вредности. То омогућава да се провера да ли је карактер малослово енглеске а ецеде изврши тако што се провери да ли је његов кôд између кодовакарактера 'a' и 'z' тј. да ли припада интервалу од 'a' до 'z' (ако је учитани карактерc, провера се врши условом 'a' <= c && c <= 'z'). Слично важи и за велика слова

28

Page 31: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

4.4. НАБРОЈИВИ ТИП

и за цифре (провера да ли је карактер c цифра може се формулисати условом '0' <=c && c <='9').

Поред наведених функција, у заглављу <cctype> декларисане су и функције које кон-вертују мала у велика слова и о ратно. Прецизније, постоје наредне функције којераде над појединачним карактерима:

• toupper – малo слово трансформише у велико, док остале карактере не мења• tolower – велико слово трансформише у мало, док остале карактере не мења

Мало слово се може трансформисати у велико слово и тако што се прво одреди које јето по реду слово у а ецеди (нај оље је да ројање креће од нуле), тако што се од тогкарактера тј. његовог кода одузме карактер 'a' тј. његов кôд, а затим се нађе великослово које има тај редни рој у а ецедном реду свих великих слова, тако што се накарактер 'A' тј. његов кôд дода редни рој карактера. Дакле, ако је променљива cсадржи мало слово, његову трансформацију у велико слово можемо извршити изразом'A' + (c - 'a') тј. c - 'a' + 'A'. Трансформација великих у мала слова вршилаи се потпуно аналогно, изразом c - 'A' + 'a'.

4.4 На ројиви типПоред већ наведених уграђених типова података, могуће је дефинисати и нове типовеподатака. У језику C++ можемо дефинисати на ројиве типове података, са коначномного вредности. За то се користи кључна реч enum. На пример, наредном конструк-цијом исмо дефинисали тип који има вредности дана у недељи.

enum DanUNedelji {PON, UTO, SRE, CET, PET, SUB, NED};

Променљиве овог типа даље декларишемо на уо ичајени начин (нпр. DanUNedeljidan;). На вредности на ројивог типа реферишемо тако што наведемо саму вредносттог типа. На пример, SRE. Исти ефекат и се могао постићи и дефинисањем нумерич-ких константи (нпр. 1 за понедељак, 2 за уторак итд.). Ако и се у програму користилеконстантне вредности, програм и ио мање разумљив (јер се лакше разуме шта значивредност SRE, него константна вредност 3). То се може решити именованим константа-ма, међутим, остаје про лем да се променљивима типа int могу доделити и вредностикоје нису исправне (нпр. вредност 8 не означава ни један дан), док код на ројивог ти-па тако нешто није могуће.

4.5 Структурни типНекада желимо да неколико података третирамо као целину. На пример, опис јед-ног такмичара чине подаци о његовом имену, разреду, школи из које долази и ројуосвојених поена. Уместо дефинисања неколико појединачних променљивих, можемодефинисати нови, с рук урни (некада се каже и слоīовни) тип и затим једну промен-љиву која садржи све потре не податке. За дефинисање структурних типова користи-мо кључну реч struct. На пример, тип такмичара можемо дефинисати на следећиначин.

29

Page 32: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 4. ДЕТАЉНИЈИ ПРЕГЛЕД ОСНОВНИХ ТИПОВА ПОДАТАКА

struct Takmicar{

string ime;int razred;string skola;double poeni;

};

Када смо дефинисали структурни тип, променљиве тог типа можемо декларисати науо ичајени начин. Појединачним подацима (ӣољима структуре) приступамо навође-њем имена променљиве, сим ола . и затим назива поља. Нпр.Takmicar Pera;pera.ime = ”Petar Petrović”;pera.razred = 6;pera.skola = ”OŠ Jovan Dučić”;pera.poeni = 95.5;

У структури можемо дефинисати и функцију (тзв. ме о у), која онда може присту-пати пољима те структуре. На пример,struct Takmicar{

string ime;...void predstaviSe(){

cout << ”Zovem se ” << ime << ”. ”<< ”Idem u školu ” << skola << ”.” << endl;

}};

У језику C++ структуре се, као и основни типови, преносе у функцију по вредности.Дакле, уколико желимо да се у функцији измени вредност неког податка структурногтипа потре но је пренети га у функцију по референци. Штавише, да исмо из еглискупа копирања, до ра пракса је и податке структурног типа које не желимо да ме-њамо у функцији прослеђивати у функцију по референци; у том случају потре но јенагласити да се ти о јекти неће мењати у функцији што се постиже навођењем кључнеречи const испред типа променљиве у листи параметара функције.

4.6 ТоркеУ математици се често помињу уређени парови и уређене n-торке.Ако желимо да о јединимо неколико података истог типа, тада уместо дефинисањановог структурног типа можемо употре ити и лиотечки тип орки.У језику C++ n-торке се могу представити типом tuple дефинисаним у заглављу <tu-ple>. На пример, тип tuple<int, int, int> представља тројку целих ројева. Tор-ке се могу иницијализовати помоћу витичастих заграда или креирати функцијом ma-

30

Page 33: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

4.6. ТОРКЕ

ke_tuple. На пример, датум можемо представити торком тако да прво ставимо годину,па затим месец и на крају дан.tuple<int, int, int> datum1 {2000, 1, 1};tuple<int, int, int> datum2 = make_tuple(1999, 12, 31);

Поља торке се једноставно могу очитати функцијама get<i> где је са i означен реднирој компоненте торке који желимо да прочитамо. На пример, get<0>(datum1) враћавредност 2000, а get<2>(datum1) вредност 1. Постоји и начин да се комплетна торкараспакује на појединачне вредности, на пример позив: tie(godina,mesec,dan) =datum1 и у променљиве godina, mesec и dan сместио вредности одговарајућих пољаторке datum1.Основна предност торки у односу на структуре је то што се могу једноставно лексико-графски поредити коришћењем уо ичајених релацијских оператора <, <=, > и >=.

31

Page 34: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

Глава 5

Низови, ниске, матрице

Умногим задацима потре но истовремено у меморији запамтити целу серију података,да и се затим на тим подацима могле вршити различите о раде. За то се користеразличите врсте низова (класични низови, вектори, листе, матрице итд.).

5.1 Једнодимензионалне колекције податакаНајједноставнија колекција која се може користити за истовремено складиштење целесерије података је класичан низ.

5.1.1 Статички алоцирани низовиУ језику C++ величина класичних статичких низова мора ити позната у фази прево-ђења програма и зато је потре но унапред знати њихов максимални рој елемената. Утакмичарским задацима ово је о ично саставни део формулације задатка, међутим, уреалној програмерској пракси оваква ограничења често или нису позната или су доставећа од роја елемената које програм о ично користи тако да се меморија непотре нозаузима. Статички низови у језику C++ се о ично користе на следећи начин.

const int MAX = 50000;int a[MAX];int n;cin >> n;for (int i = 0; i < n; i++)cin >> a[i];

...

Приметимо да је MAX константа која је позната у време превођења програма.

Постоји значајна разлика између статички алоцираних низова који су декларисаниунутар функција (локално) и оних декларисаних ван свих функција (гло ално). Гло-ални низови о ично могу ити знатно већи и вредности свих елемената су иницијал-

32

Page 35: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.1. ЈЕДНОДИМЕНЗИОНАЛНЕ КОЛЕКЦИЈЕ ПОДАТАКА

но постављене на нулу (код локалних низова иницијалне вредности елемената нисудефинисане).

Димензија статички алоцираног низа (максимални рој елемената) често је већа одстварног роја елемената уписаних у низ. Стога је увек потре но у посе ној промен-љивој памтити колико елемената низ стварно садржи (у наведеном примеру то је илапроменљива n). Приликом преноса низа у функцију, потре но је уз низ проследити иту променљиву.

Функције којима су низови параметри примају само меморијску адресу почетка низа.Све модификације низа унутар функције врше се над оригиналним низом (оним ко-ји је наведен као аргумент, приликом позива). Стога су низови увек улазно-излазнипараметри функција (за разлику од, на пример, података типа int или структура којису улазни параметри, осим ако се не проследе по референци). Статички низови којису дефинисани у функцијама се осло ађају по завршетку извршавања тела функцијеи није их могуће вратити као резултујућу вредност функције (ако функција тре а даврати статички низ, њега је неопходно проследити као параметар функције и функци-ја може само да попуни његове елементе).

5.1.2 VLAНеки компилатори подржавају о лик низова који се назива VLA (engl. variable lengtharray), где се као димензија низа наводи променљива чија је вредност позната тек уфази извршавања програма.

int n;cin >> n;int a[n];for (int i = 0; i < n; i++)cin >> a[i];

...

VLA никада нису или део стандарда језика C++ (једно време или су део стандардајезика C, па су након тога из ачени) и стога их никако није препоручљиво користити.

5.1.3 Динамички алоцирани низовиАлокација низова у језику C++ у тренутку извршавања програма (тзв. динамичка ало-кација) је могућа (оператором new), али захтева рад са показивачима и ручно осло а-ђање меморије (оператором delete []) и сматра се прилично напредном темом, такода ту врсту низова нећемо користити.

int n;cin >> n;int *a = new int[n];for (int i = 0; i < n; i++)cin >> a[i];

...delete[] a;

33

Page 36: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

У језику C++ за динамичку алокацију низова могуће је користити и и лиотеку зарад са меморијом језика C и функције malloc и free, међутим и тај о лик рада санизовима је непримерен језику C++ и препоручујемо да се у њему не користи.

int n;cin >> n;int *a = (int*)malloc(n * sizeof(int));for (int i = 0; i < n; i++)cin >> a[i];

...free(a);

5.1.4 Би лиотечке колекције (vector, array)

Нај оље решење долази у форми динамички алоцираних и лиотечких колекција тј.колекција за чије се складиштење меморија заузима тек током извршавања (а не токомпревођења) програма и које програмеру пружају веома удо ан интерфејс за рад садинамички алоцираном меморијом. У језику C++ најчешће коришћена динамичкаколекција, која одмењује класичне, статички алоциране низове је век ор (vector иззаглавља <vector>).

• Вектор је параметризован типом елемената које чува тако да се користеvector<int>, vector<double>, vector<string>, али и, на пример, vec-tor<vector<int>>.

• Ако је рој елемената вектора n познат пре његове декларације (а у току извр-шавања програма), тада је вектор нај оље декларисати уз навођење димензијеу склопу декларације (на пример, vector<double> a(n)). Могуће је навестии почетну вредност свих елемената (на пример, декларацијом vector<double>a(n, 1.0) гради се вектор од n елемената постављених на вредност 1.0).

• Након декларације у којој је наведен рој елемената, вектор се користи на пот-пуно исти начин као и низ (елементу на позицији i приступа се са a[i]). Ди-мензију ( рој елемената) вектора a можемо одредити помоћу методе a.size()(ме о а је посе ан о лик функције која се позива над неким о јектом тако штосе име тог о јекта наводи испред имена функције и тачкице и која нам даје некеинформације о том о јекту или проузрокује одређене измене тог о јекта).

• Једна од најзначајнијих предности вектора у односу на класичне низове је тошто им се димензија може мењати током извршавања програма. Проширивањевектора a додавањем елемента x на његов крај могуће је уз помоћ позива методеa.push_back(x) (тиме се увећава рој елемената за 1).

• Промена димензије вектора се може урадити методом resize - након позиваa.resize(n) вектор a има димензију n (под условом да је ило довољно мемо-рије).

• Простор за смештање елемената у вектор можемо резервисати и ез променењегове димензије коришћењем методе reserve. Након позива a.reserve(n)резервише се простор за смештање n елемената, али величина вектора остајенепромењена (у почетку 0), све док се елементи не додају помоћу push_back.

Најчешћи о лик коришћења вектора у овој з ирци је следећи.

34

Page 37: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.1. ЈЕДНОДИМЕНЗИОНАЛНЕ КОЛЕКЦИЈЕ ПОДАТАКА

int n;cin >> n;vector<int> a(n);for (int i = 0; i < n; i++)cin >> a[i];

...

Ако рој елемената није задат у посе ној променљивој, могуће је очитати га методомsize.

for (int i = 0; i < a.size(); i++)// obradjuje se a[i]

СавремениC++ (почевши од стандарда C++-11) допушта и краћи о лик петље којом сео рађује један по један елемент вектора (али и низа и других и лиотечких колекција).У многим језицима оваква петља назива се петља foreach.

for (int x : a)...

Вектори се у функције преносе по вредности (осим ако се експлицитно не наведе дасе преносе по референци), што значи да се у функцију преноси копија вектора који јенаведен као аргумент у позиву. Ако желимо да функција модификује садржај вектора,неопходно га је пренети по референци (навођењем сим ола &). Тада се у функцију ша-ље само меморијска адреса тог вектора и све операције се спроводе над оригиналнимвектором (не врши се копирање). Копирањем се непотре но троши меморија и време,па има смисла векторе проследити по референци и функцијама које само анализира-ју њихов садржај и не мењају их. Тада се о ично наводи кључна реч const чиме сео ез еђује да се вектор не може променити у функцији иако је пренет по референци.На пример,

int zbir(const vector<int>& a) {int z = 0;for (int x : a)

z += a;return z;

}

Функција може да врати вектор као своју резултујућу вредност. Захваљујући тзв. се-мантици премештања (енгл. move semantics) уведеној у савременим верзијама језикаC++ не врши се никакво копирање садржаја, па се овим не умањују перформансе про-грама.

Рецимо и да у језику C++, од стандарда C++-11 постоји колекција array која је вео-ма слична класичном статичком низу (фиксне је величине и та величина се наводи уоквиру типа), међутим, нуди одређене методе и гло алне функције које омогућавајуда се до ије колекција која има ефикасност класичних низова, а удо ност коришћењавектора (на пример, величину увек можемо сазнати методом size, док код о ичнихнизова пренетих у функцију немамо могућност да сазнамо величину, већ је морамопрослеђивати као параметар функције). Ипак, у овој з ирци array нећемо користити.

35

Page 38: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

Слика 5.1: Итератори begin и end

const int MAX = 100;array<int, MAX> a;for (int i = 0; i < n; i++)cin >> a[i];

...

5.1.5 Би лиотечке функције за рад са низовимаУ овом поглављу ћемо приказати решења заснована на употре и и лиотечких колек-ција (низова, листи, вектора и сл.) и функција. Нагласимо да се већина задатака изовог поглавља може решити и ез употре е колекција. Иако је програмски кôд таквихрешења о ично дужи, она су свакако меморијски ефикаснија. Такође, таква решењасе могу сматрати илустративнијим, јер се кроз њих изучавају веома важни, фунда-ментални алгоритми. Са друге стране, у неким ситуацијама у програмирању ћемоелементе морати сместити у низ или ћемо их из неког разлога већ имати у том о лику.Тада се применом и лиотечких функција могу до ити веома кратка и елеганта реше-ња. Би лиотечке функције ћемо у овом поглављу приказати кроз веома једноставнезадатке у којима њихова примена не мора да уде оптималан из ор.

Би лиотечке функције у језику C++ су дизајниране тако да се могу примењивати надразличитим колекцијама података (статичким и динамичким низовима, векторима,листама и слично). Колекција (или неки њен део тј. опсег узастопних елемената) за-дају се итераторима. Итератори се најчешће користе у пару и ограничавају опсегузастопних елемената колекције тако што први итератор указује на почетни елементтог опсега, а други указује непосредно иза последњег елемента опсега.

Неке од најчешће коришћених функција декларисане у заглављу <iterator> су:

• begin, end – враћају итераторе који ограничавају опсег датог низа, вектора ислично. Функција begin враћа итератор који указује на први елемент, а endвраћа итератор који указује непосредно иза последњег елемента (на пример be-gin(v) враћа итератор који указује на почетак вектора v). Многе колекцијеподржавају и методе begin и end (на пример, v.begin() враћа итератор којиуказује на почетак вектора v). Име низа је могуће употре ити као итератор којиуказује на почетак низа;

• distance – враћа растојање ( рој елемената) у опсегу ограниченом са два ите-ратора која се прослеђују као аргументи функције (први итератор показује напочетак тј. на први елемент опсега, а други непосредно иза краја тј. послед-њег елемента опсега). На пример, ако је it итератор који указује на неки еле-мент унутар вектора v, тада се његов индекс може одредити помоћу distan-ce(begin(v), it);

• next, prev – враћа итератор који показује на елемент дате колекције који је

36

Page 39: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.1. ЈЕДНОДИМЕНЗИОНАЛНЕ КОЛЕКЦИЈЕ ПОДАТАКА

испред тј. иза прослеђеног итератора, на датом растојању; ако се као други ар-гумент не проследи растојање, подразумевано се тражи итератор на наредни тј.претходни елемент колекције. На пример, next(begin(v)) је итератор којиуказује на други елемент вектора v (ако такав постоји), док је prev(end(a), 2)итератор који указује на претпоследњи елемент низа a (ако такав постоји).

• Над итераторима се могу примењивати и аритметичке операције: it + n одгова-ра итератору који се до ија када се итератор it помери надесно n пута (исто каои next(it, n)). На пример, ако низ a има n елемената тада се његов опсег мо-же задати итераторима a и a+n. Разлика два итератора одређује рој елеменатаизмеђу њих (укључујући први и не укључујући последњи (исто као и функцијаdistance).

Неке од најчешће коришћених функција декларисане у заглављу <algorithm> су:

• min_element и max_element – враћа итератор на елемент са најмањом тј. најве-ћом вредношћу у опсегу задатом помоћу два дата итератора; као трећи аргументсе функцији може проследити и функција која врши поређење два елемента(подразумевано се користи поређење оператором <);

• transform – пресликавају се сви елементи из опсега задатог помоћу два итера-тора; као трећи аргумент се прослеђује итератор на почетак колекције у коју серезултат смешта, а као четврти функција (најчешће анонимна) којом се елемен-ти пресликавају;

• count – враћа рој појављивања елемента у опсегу задатом помоћу два итерато-ра;

• copy_if – колекција се филтрира тако што се копирају они елементи из опсегазадатог помоћу два итератора који задовољавају услов који се испитује функци-јом која се задаје као последњи аргумент функције copy_if (та функција примаелемент колекције и враћа true ако он тре а да уде копиран); филтрирани еле-менти смештају се у колекцију чији се итератор почетка прослеђује функцијиcopy_if као њен трећи аргумент и подразумева се да је она алоцирана тако даможе да прихвати све елементе који се копирају. Ако рој тих елемената нијеунапред познат, могуће је проследити back_inserter(it) где је it итераторкоји указује на почетак неалоцираног вектора (тада се приликом копирања еле-менти у вектор у ацују помоћу push_back).

• remove, remove_if – колекција се филтрира тако што се из ње уклањају они еле-менти из опсега задатог помоћу два итератора који задовољавају неки услов; услучају функције remove задаје се вредност и уклањају се сви елементи једнакитој вредности, док се у случају функције remove_if услов испитује функцијомкоја се задаје као последњи аргумент функције remove_if (та функција примаелемент колекције и враћа true ако он тре а да уде о рисан); преостали еле-менти се премештају на почетак оригиналне колекције, а функција remove_ifвраћа итератор непосредно иза последњег задржаног елемента; након уклањањаелемената, колекција се често скраћује функцијом erase;

• erase – рише елемент из колекције задат итератором или опсег колекције задатпомоћу два итератора;

• any_of, all_of – проверава да ли неки елемент тј. да ли сви елементи опсегазадатог помоћу два итератора задовољавају услов који се проверава bool функ-цијом која се задаје као трећи аргумент функције any_of; функција any_of тј.

37

Page 40: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

all_of такође враћа вредност типа bool;• find, find_if – враћа итератор који показује на прво појављивање елементакоја се тражи у опсегу задатом помоћу два итератора; ако се елемент не јавља уовом опсегу елемената враћа се итератор који показује на позицију иза краја да-тог опсега; функција find прима вредност која се тражи, док функција find_ifприма функцију која проверава да ли текући елемент има захтевано својство ипроналази први елемент који то својство задовољава.

• equal – проверава да ли су два опсега једнака; као аргументе прима итераторекоји ограничавају први опсег и итератор који показује на почетак другог опсега;

• reverse – о рће редослед елемената опсега задатог помоћу два итератора.• sort – сортира елементе опсега задатог помоћу два итератора. Они се подра-зумевано пореде оператором <, а могуће је као трећи аргумент функцији sortпроследити и функцију којом ће се два елемента поредити (она тре а да вратиtrue ако и само ако први тре а да се нађе испред другога у сортираном редосле-ду).

У неким примерима ћемо користити и наредне функције декларисане у заглављу <nu-meric>:

• iota – попуњава елементе колекције (низа или вектора) из датог опсега задатогпомоћу два итератора узастопним ројевима почев од вредности коју наводимокао трећи аргумент функције;

• accumulate – рачуна з ир елемената у опсегу задатом помоћу два итератора;као трећи параметар наводи се иницијална вредност з ира – најчешће је то 0или 0.0 у зависности од типа елемената који се са ирају. Функција accumula-te израчунава з ир тако што о рађује један по један елемент опсега у свакомкораку текућу вредност з ира са ира са текућим елементом опсега. То се можепроменити ако се као четврти аргумент функције accumulate зада функција ко-ја прихвата досадашњу акумулирану вредност и текући елемент опсега и којаописује како се нова акумулирана вредност до ија на основу њих (та функцијатре а да врати нову вредност).

5.1.6 СортирањеСортирање података један је од најпроучаванијих про лема у рачунарству. Прикажи-мо како се у језику C++ може сортирати низ целих ројева.

Препоручен начин да се у савременим програмским језицима нешто сортира је да сеупотре и и лиотечка функција сортирања (то је најчешће најефикаснији, најпоузда-нији и најједоставнији начин да се сортирање о ави).

У језику C++ то је функција sort која прима два итератора - први који указује напочетак дела низа тј. вектора који се сортира, а други указује иза последњег елемен-та у делу низа који се сортира. Када се сортира цео вектор a ти итератори се могудо ити помоћу a.begin() и a.end(). Када се сортира низ a са n елемената почетниитератор је a, а завршни се може до ити као next(a, n) или евентуално a+n. Овафункција се може употре ити и за сортирање на основу неког другог критеријума (несамо неопадајуће) и сортирање других врста података (не о авезно ројева) и тада се

38

Page 41: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.1. ЈЕДНОДИМЕНЗИОНАЛНЕ КОЛЕКЦИЈЕ ПОДАТАКА

као трећи аргумент прослеђује функција која се користи за поређење елемената токомсортирања.// ucitavanje brojevaint n;cin >> n;vector<int> a(n);for (int i = 0; i < n; i++)cin >> a[i];

// sortiranjesort(a.begin(), a.end());

// ispis rezultatafor (int i = 0; i < n; i++)cout << a[i] << endl;

Сортирање је могуће спровести и над низом ниски и тада се ниске сортирају по а ецед-ном лексикографкском редоследу (као речи у речнику). Ако се сортира низ структура,тада је потре но дефинисати њихов жељени редослед и то се о ично ради функцијомпоређења.Претпоставимо да сортирамо низ такмичара и да је за сваког такмичара познато јењегово име и рој поена на такмичењу. Напишимо програм којим се сортира низученика нерастуће по роју поена, а ако два такмичара имају исти рој поена, онда ихуредити по имену у нерастућем поретку.Најприродније је да се подаци о такмичару памте у структури Takmicar чији су еле-менти име такмичара (податак типа string) и рој поена (податак типа int). За чува-ње информација о свим такмичарима можемо онда употре ити неку колекцију струк-тура. Друге могућности су да се подаци чувају у два низа (низу имена и низу поена)или да се уместо структура користе парови тј. торке.Нај ољи и уједно и најједноставнији начин сортирања је употре ити и лиотечкуфункцију сортирања. У језику C++ то је функција sort ( ез о зира на то да ли секористи вектор или низ).Функцији сортирања је потре но доставити и функцију поређења којом се заправоодређује редослед елемената након сортирања. Прво се пореди рој поена и ако јерој поена првог такмичара мањи функција поређења враћа false (јер он тре а да идеиза другог такмичара), ако је већи враћа се true (јер он тре а да иде испред другогтакмичара), а ако су ројеви поена једнаки, прелази се на поређење имена. За то семоже упоредити и лиотечко а ецедно (лексикографско) поређење ниски. У језикуC++ за то се могу употре ити оператори поређења.У језику C++ постоји неколико начина да дефинишемо функцију поређења.Именована функција ӣоређења

Први је да се дефинише класична (гло ална) функција која прима две структуре (по-датке о два такмичара) и која врача вредност типа bool и то вредност true ако првитакмичар тре а да уде испред другог у коначном сортираном низу, тј. вредност false

39

Page 42: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

у супротном. Ефикасности ради, структуре које се пореде има смисла пренети прекоконстантних референци. Та функција до ија своје име (на пример, poredi) и то имесе онда наводи као трећи параметар у позиву и лиотечке функције sort (након дваитератора који одређују распон који се сортира).

bool porediTakmicare(const Takmicar& a, const Takmicar& b) {...

}

sort(begin(a), end(a), porediTakmicare);

#include <iostream>#include <algorithm>#include <string>using namespace std;

struct Takmicar {string ime;int brojPoena;

};

bool uporedi(const Takmicar& A, const Takmicar& B) {if (A.brojPoena > B.brojPoena)return true;

if (A.brojPoena < B.brojPoena)return false;

return A.ime <= B.ime;}

int main() {int n;cin >> n;

Takmicar a[50000];for (int i = 0; i < n; i++)cin >> a[i].ime >> a[i].brojPoena;

sort(a, a+n, uporedi);

for (int i = 0; i < n; i++)cout << a[i].ime << ” ” << a[i].brojPoena << endl;

return 0;}

Анонимна функција ӣоређења

Други начин је да се уместо именоване функције, функција поређења дефинише каоанонимна функција тј. као λ-израз. Параметри и повратна вредност те функције су ис-ти као у случају именоване функције, при чему се повратни тип не мора експлицитно

40

Page 43: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.1. ЈЕДНОДИМЕНЗИОНАЛНЕ КОЛЕКЦИЈЕ ПОДАТАКА

навести. Пошто функција поређења у овом случају не мора да користи друге податкеосим података о такмичарима који се пореде, као параметре затворења те функције(параметри који се наводе у заградама []) нема потре е наводити ништа. Анонимнафункција се наводи директно у позиву функције sort као њен трећи аргумент.

sort(a.begin(), a.end(),[](const Takmicar& a, const Takmicar& b) {

...});

#include <iostream>#include <algorithm>#include <vector>#include <string>using namespace std;

struct Takmicar {string ime;int brojPoena;

};

int main() {int n;cin >> n;

vector<Takmicar> a(n);for (int i = 0; i < n; i++)cin >> a[i].ime >> a[i].brojPoena;

sort(a.begin(), a.end(),[] (const Takmicar& A, const Takmicar& B) {if (A.brojPoena > B.brojPoena)return true;

if (A.brojPoena < B.brojPoena)return false;

return A.ime <= B.ime;});

for (int i = 0; i < n; i++)cout << a[i].ime << ” ” << a[i].brojPoena << endl;

return 0;}

5.1.7 Бинарна претрагаБинарна претрага је веома ефикасан алгоритам који се примењује за претрагу сорти-раних низова.

41

Page 44: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

Прикажимо њену употре у на једном задатку. У продавници се налази пуно врстапроизвода и познати су њихови ар-кодови. Произвођач жели да сазна колико се врстањегових производа продаје у тој продавници. Ако је списак свих кодова производа упродавници дат у сортираном о лику, а списак свих кодова производа произвођача једостављен несортиран, напиши програм који одређује тражени рој.

Чињеница да је низ сортиран нам даје начин да задатак решимо веома ефикасно. Иа-ко инарну претрагу можемо имплементирати ручно, најједноставније је употре итии лиотечке функције.

Би ило ечке функције

У језику C++ функција binary_search изводи инарну претрагу неког сегмента (ни-за или вектора). Аргументи су два итератора (један на први елемент сегмента којисе претражује, а други непосредно иза последњег елемента) и елемент који се тражи.Функција враћа bool чија је вредност true ако и само ако елемент постоји у низу.

#include <iostream>#include <algorithm>#include <vector>

using namespace std;

int main() {ios_base::sync_with_stdio(false);

// ucitavamo nizint n;cin >> n;vector<int> a(n);for (int i = 0; i < n; i++)cin >> a[i];

// broj onih koji postoje u nizuint broj = 0;// ucitavamo broj po broj do kraja ulazaint x;while (cin >> x) {// ako je broj sadrzan u nizu, uvecavamo brojacif (binary_search(a.begin(), a.end(), x))

broj++;}// ispisujemo rezultatcout << broj << endl;

return 0;}

42

Page 45: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.2. КАРАКТЕРИ И НИСКЕ

5.2 Карактери и ниске5.2.1 Елементи програмског језикаОпишимо мало детаљније како се могу представити подаци текстуалног типа у про-граму. Текст се представља у о лику ниске (engl. string) која се састоји од карактера.У језику C++ је нај ољи из ор да користимо податак типа string (из заглавља<string>), јер тада на располагању имамо многе и лиотечке операције и функције(већина су методе класе string).Постоји могућност да се уместо података типа string користи низ карактера (низ еле-мената типа char) у коме се за ознаку краја користи карактер чији је ASCII кôд нула(за такве ниске се каже да су ерминисане завршном нулом, односно, на енглескомјезику null terminated strings). Тај начин рада са текстом је типичан за програмски је-зик C и много га је теже користити него рад са типом string, тако да га у решењимазадатака у овој з ирци нећемо користити.Константне ниске се записују између двоструких наводника. На пример,string s = ”Zdravo svima”;

Ниску која садржи једно или више појављивања неког карактера можемо до ити наследећи начин.string s(5, 'a'); // s sadrži tekst ”aaaaa”

Ако је str променљива типа string, наред а cin >> str; учитава једну реч са стан-дардног улаза (до позиције прве елине, занемарујући елине на почетку речи). Сверечи, до краја улаза, можемо учитавати на следећи начин:string rec;while (cin >> str)...

Наред ом getline(cin, str); се у ниску str учитава цела линија текста (текст допреласка у нови ред или до краја улаза). Прелазак у нови ред се не смешта у ниску str.Функција враћа вредност која се када се употре и унутар услова наред е while илиif аутоматски конвертује у вредност true ако је читање ило успешно тј. вредностfalse ако није (то се дешава ако се пре покушаја читања дошло до краја улаза). Такосе читање линија до краја улаза може остварити са:string str;while (getline(cin, str))

...

У заглављу <string> декларисано је мноштво метода помоћу којих се може радити санискама (о јектима типа string). Најважније су следеће:

• size, length – враћа дужину ниске тј. рој њених карактера;• find, rfind – враћа позицију првог тј. последњег појављивања неког карактераили подниске унутар ниске тј. специјалну вредност string::npos ако траженикарактер или подниска не постоје;

43

Page 46: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

• substr – издваја подниску дате ниске, као параметре прихвата позицију на којојпочиње подниска која се издваја и дужину подниске. Ако се не наведе дужинаподниске, издваја се подниска која почиње на задатој позицији и протеже се докраја ниске;

• replace – мења део ниске другом ниском;• insert – умеће ниску на дату позицију унутар друге ниске;• erase – из текуће ниске рише се њена подниска која почиње на задатој пози-цији и задате је дужине.

Две ниске се могу надовезати коришћењем оператора + и резултат је нова ниска. Про-ширивање ниске неким суфиксом (додавање текста на њен десни крај) се може врши-ти оператором +=. Нагласимо да проширивање наред ом о лика str += s може итизнатно ефикасније него наред ом о лика str = str + s, јер се у другој понекадпрво креира нова ниска у коју се копира садржај ниске str и ниске s, након чега и сетај нови садржај додељује ниски s, што доводи до велике неефикасности (ово је чуве-ни пример лошег решења у програмирању познато под именом Schlemiel the Painter’salgorithm).

Подаци типа string имају двојако тумачење. Са једне стране можемо их сматратиелементарним, атомичким подацима (на пример, видели смо да се могу “са ирати”,слично ројевима). Са друге стране ниске се могу третирати и као колекције (низови)карактера и над њима је могуће спроводити исте алгоритме које смо спроводили наднизовима и векторима. На пример, иако је s о јекат типа string, а не низ или вектор,изразом s[i] може се одредити његов i-ти карактер (под претпоставком да је i мањеод дужине ниске). Методама begin и end, односно истоименим функцијама могуће једо ити итераторе на почетак и непосредно изнад краја ниске, које је могуће проследи-ти функцијама које о рађују опсеге. На пример, sort(s.begin(), s.end()) сортирасве карактере ниске s (у а ецедном поретку). Такође, кроз све карактере ниске могућеје проћи петљом о лика

for (char c : str)...

5.3 ПресликавањаЧесто у програму имамо потре у да представимо пресликавање тј. функцију која одре-ђеним кључевима једнозначно придружује одређене вре нос и. На пример, желимода сваком ученику придружимо просек оцена или да сваком месецу придружимо ројдана у том месецу. Таква пресликавања можемо представити посе ном врстом низовакоји се називају асоција ивни низови (каже се и речници или маӣе), а у неким посе -ним случајевима и класничним низовима.

5.3.1 Класични низови у функцији пресликавањаКада је скуп кључева интервал природних ројева о лика [0, n) или када се кључевиједноставно могу трансформисати у тај о лик, тада је осим речником пресликавањемогуће представити и класичним низом. Вредност придружену кључу i памтићемона позицији i у низу. У наредних неколико задатака приказаћемо употре у низова у

44

Page 47: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.3. ПРЕСЛИКАВАЊА

улози пресликавања.

5.3.2 Асоцијативни низови (мапе, речници)Програмски језик C++ пружа подршку за креирање мапа (речника, асоцијативних ни-зова) који представљају колекције података у којима се кључевима неког типа придру-жују вредности неког типа (не о авезно истог). На пример, именима месеци (подациматипа string) можемо доделити рој дана (податке типа int). Речници се представља-ју о јектима типа map<TipKljuca, TipVrednosti>, дефинисаном у заглављу <map>.На пример,

map<string, int> brojDana ={

{”januar”, 31},{”februar”, 28},{”mart”, 31},...

};

Приметимо да смо иницијализацију мале извршили тако што смо навели листу паровао лика {kljuc, vrednost}. Иницијализацију није неопходно извршити одмах токомкреирања, већ је вредности могуће додавати (а и читати) коришћењем индексног при-ступа (помоћу заграда []).

map<string, int> brojDana;brojDana[”januar”] = 31;brojDana[”februar”] = 28;brojDana[”mart”] = 31;...

Мапу, дакле, можемо схватити и као низ тј. вектор у коме индекси нису о авезно изнеког цело ројног интервала о лика [0, n), већ могу ити произвољног типа.

Претрагу кључа можемо остварити методом find која враћа итератор на пронађениелемент тј. итератор иза краја мапе (који до ијамо методом или функцијом end), акоелемент не постоји. На пример,

string mesec; cin >> mesec;auto it = brojDana.find(mesec);if (it != end(brojDana))

cout << ”Broj dana: ” + *it << endl;else

cout << ”Mesec nije korektno unet” << endl;

Све елементе речника могуће је исписати коришћењем петље for. На пример,

for (auto& it : brojDana)cout << it.first << ”: ” << it.second << endl;

Алтернативно, можемо експлицитно користити итераторе

45

Page 48: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

ГЛАВА 5. НИЗОВИ, НИСКЕ, МАТРИЦЕ

for (auto it = brojDana.begin(); it != brojDana.end(); it++)cout << it->first << ”: ” << it->second << endl;

Итерација се врши у сортираном редоследу кључева.

Постоји и о лик неуређене мапе (unordered_map из истоименог заглавља), која можеити у неким ситуацијама мало ржа него уређена (сортирана) мапа, но то је о ичнозанемариво. Кључеви сортиране мапе могу ити само они типови који се могу пореди-ти релацијским операторима, док кључеви неуређене мапе могу ити само они типовикоји се могу лако претворити у рој (тзв. хеш-вредност). Ниске, које ћемо најчешћекористити као кључеве, задовољавају о а услова.

5.4 Вишедимензиони низови и матрице5.4.1 Елементи програмског језика

5.4.1.1 Матрице

Матрица може да се посматра као низ низова: имамо низ врста (редова), а свака врстаје низ елемената. Позиција сваког елемента у матрици је дефинисана са два роја, тј.две димензије – у којој врс и тј. ре у, и у којој колони се елемент налази.

Статичка матрица се у језику C++ алоцира слично као што се алоцира статички низ.Димензија статички алоциране матрице мора ити позната већ у фази превођења изато је потре но унапред знати максимални рој врста и колона те матрице. Тако, акорецимо хоћемо да дефинишемо матрицу, која има 50 врста и 100 колона, а елементису цело ројне променљиве, то исмо урадили на следећи начин:

int brojevi[50][100];

Овим се декларише матрица од 5000 елемената под називом brojevi. Врсте ове ма-трице су нумерисане од 0 до 49, а колоне су нумерисане од 0 до 99.

Матрице је могуће и динамички алоцирати, слично као и низове, али се као напреднијатема неће о рађивати у овом материјалу.

Једном елементу матрице приступамо такошто у угластoj загради, након назива матри-це наведемо рој врсте, а након тога у другој угластој загради наведемо и рој колонеу којој се тај елемент налази, нпр. brojevi[1][4] означава елемент који се налазиу другој врсти и петој колони матрице. Када приступимо елементу матрице, можемоса њим радити све што можемо и са о ичном променљивом: користити га у рачуници,постављати му вредност, итд.

На пример, дигитална, црно- ела слика може да се представи помоћу цело ројне ма-трице тако што ће рој 1 да означава црно, а рој 0 ело поље, на следећи начин:

int slika[10][15] = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},{ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},{ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},

46

Page 49: Садржај - University of Belgradepoincare.matf.bg.ac.rs/~filip/mnrb/uvod-u-cpp.pdf// formula za obim pravougaonika return 2*a + 2*b;} int main() {double a, b; // učitavamo dužine

5.4. ВИШЕДИМЕНЗИОНИ НИЗОВИ ИМАТРИЦЕ

{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},{ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

};

У овом случају смо меморију за матрицу резервисали истовремено са попуњавањемвредности. Уколико при алокацији матрице вршимо и њену иницијализацију, можемоизоставити прву димензију матрице, односно писати int slika[][15] = ...

5.4.1.2 Разуђени низови

Свака врста матрице мора да има исти рој елемената. Међутим, могуће је правитидводимензионе низове код којих то не мора ити случај. Такви низови се називајуразуђени низови (енгл. jagged array) који су низови низова у правом смислу те речи.Најједноставније их је креирати помоћу и лиотечке колекције <vector>, као векторвектора. На пример, уколико желимо да алоцирамо разуђени низ цело ројног типа, томожемо да урадимо на следећи начин:vector<vector<int>> razudjenNiz(3);razudjenNiz[0].resize(5);razudjenNiz[1].resize(4);razudjenNiz[2].resize(2);

Овим је направљен разуђен низ који има три врсте, у првој се чува пет елемената, удругој четири и у трећој два.Елементу се приступа помоћу двоструких угластих заграда. На пример, razudjen-Niz[1][3] означава четврти елемент у другој врсти.

47