Оператори - втор дел

Во ова предавање ќе зборуваме за основните релациски и логички оператори во C++, за претворање на вредности од еден тип на податок во друг, за операторите 'sizeof', '?' и ',', како и за редоследот на извршување на операции.

Релациски оператори (==, !=, <, >, <=, >=)

Постојат 6 основни релациски оператори (оператори за вршење на споредба помеѓу две вредности):

операција израз резултат
еднакво a == b точно (true) ако a е еднакво на b, инаку неточно (false)
различно a != b точно (true) ако а е различно од b, инаку неточно (false)
помало a < b точно (true) ако а е помало од b, инаку неточно (false)
поголемо a > b точно (true) ако а е поголемо од b, инаку неточно (false)
помало или еднакво a <= b точно (true) ако а е помало или еднакво на b, инаку неточно (false)
поголемо или еднакво a >= b точно (true) ако а е поголемо или еднакво на b, инаку неточно (false)

Резултатот од извршувањето на било кој од релациските оператори споменати погоре е bool вредност (true или false). Забележете ја разликата помеѓу операторите '=' и '==': операторот '=' служи за доделување на вредност на променлива, додека операторот '==' служи за споредување на два изрази (изразот кој се наоѓа од неговата лева страна и изразот кој се наоѓа од неговата десна страна). Следнава програма го илустрира начинот на користење на релациските оператори:

Програма 9.1

#include <iostream>
using namespace std;

int main()
{
      bool result;
      
      int a = 5, b = 8;
      
      result = (a == b);                   //result = false
      result = (a != b);                   //result = true
      result = (a < b);                    //result = true
      result = (a <= b);                   //result = true
      result = (a > b);                    //result = false
      result = (a >= b);                   //result = false
      
                                           //VNIMAVAJTE: '=' NASPROTI '=='
      result = ((a=8) == b);               //result = true
      
      cout << a << " " << b << endl;       //pechati '8 8'
      
      return 0;
}

Во претходната програма, на линија 18, прво и доделуваме вредност 8 на променливата a, а подоцна ги споредуваме a и b. Оттаму, вредноста на изразот е true (и а и b содржат вредност 8). Една од најчестите грешки во C++ е пишување на '=' наместо '=='. Друга грешка која често се случува е наместо '<=' или '>=' да се напише '=<' или '=>' (да се променат местата на '<' и '='). Бидејќи не постојат оператори '=<' и '=>', ваквата замена ќе резултира со пријавување на грешка од страна на компајлерот.

Логички оператори (!, &&, ||)

Постојат 3 логички оператори во C++: операторот за негација '!', операторот за логичко И (AND) '&&' и операторот за логичко ИЛИ (OR) '||'. Операторот за негација служи за негирање на bool вредност: доколку x=true, тогаш !x ќе врати false и обратно, доколку x=false тогаш !x ќе врати true. Операторот '&&' (логичко И) враќа true ако и само ако двата операнди (a и b) над кои се извршува операцијата (a&&b) имаат вредност true, во спротивно резултатот е false. Операторот '||' (логичко ИЛИ) враќа true ако кој било од двата операнди (a и b) над кои се извршува операцијата (a||b) има вредност true. Логичките оператори И и ИЛИ ('&&' и '||') се слични како операторите '&' и '|', но се извршуваат врз bool вредности (не врз сите битови од една целобројна променлива).

операција израз резултат
негација точно (true) ако a е false, инаку неточно (false)
логичко И (AND) a && b точно (true) ако а==true и b==true, инаку неточно (false)
логичко ИЛИ (OR) a || b точно (true) ако а==true или b==true, инаку неточно (false)

Важно е да се наведе дека C++ ги врши само споредбите кои се задолжителни (т.н. short-circuit пресметување): доколку имаме израз од типот "a && b && c", и a има вредност false, каква и да е вредноста на b и c, изразот повторно ќе има вредност false (бидејќи, кај логичко И, изразот е true само ако сите операнди се true). Кај логичко ИЛИ важи обратното - ако еден од операндите е true, тогаш целиот израз е true (нема потреба да се проверува остатокот од изразот). Следнава програма го прикажува ефектот на логичките оператори:

Програма 9.2

#include <iostream>
using namespace std;

int main()
{
      bool result;
      
      bool b1 = true, b2 = false;
      
      result = !b1;                            //result = false
      result = !b2;                            //result = true
      result = !(b1);                          //result = false
      result = !(!b1);                         //result = true
      
      result = b1 && b2;                       //result = false
      result = b1 || b2;                       //result = true
      
      result = (5 > 3);                        //result = true
      result = !(5 > 3);                       //result = false
      
      result = (5>3) && (7>=7);                //result = true
      result = (2>3) || (1>2) || (4>=5);       //result = false
      
      int z = 0;
      result = (1>2) && (++z == 1);
      cout << "z=" << z << endl;               //pechati 'z=0'
      
      result = (1>2) || (++z == 1);
      cout << "z=" << z << endl;               //pechati 'z=1'
      
      return 0;
}

Програмата печати "z=0" и "z=1", иако на линиите 25 и 28 сме ја зголемиле вредноста на z за 1. Едноставно, нема потреба да се проверува целиот израз (линија 25) за да се додели точната вредност на променливата result. Без разлика на вредноста на (++z == 1), result секако ќе добие вредност false. Затоа овој дел воопшто нема да се проверува (односно, нема да биде пресметан). Најдобро решение е, во вакви изрази, да се избегнува доделување нови вредности на променливите.

Експлицитно претопување

Претопување претставува претворање на податок од еден тип во друг. Кога вршиме операции врз податоци од различен тип, C++ се обидува, самиот, да претвори еден од податоците во соодветниот податочен тип на другиот (поедноставниот тип се претвора во посеопфатниот тип). Оваа постапка се нарекува имплицитно претопување.

Во C++ постојат оператори за експлицитно претопување, кои овозможуваат рачно претворање на податок од еден тип во друг. Следнава програма ги прикажува трите стандардни начини на претопување:

Програма 9.3

#include <iostream>
using namespace std;

int main()
{
      double pi = 3.14;
      
      int a = (int) pi;
      int b = int(pi);
      int c = static_cast<int>(pi);
      
      cout << "a=" << a << endl;         //pechati 'a=3'
      cout << "b=" << b << endl;         //pechati 'b=3'
      cout << "c=" << c << endl;         //pechati 'c=3'
      
      
      int i = 1;
      
      double d = (double)i;
      double e = double(i);
      double f = static_cast<double>(i);
      
      cout << "d=" << d << endl;         //pechati 'd=1'
      cout << "e=" << e << endl;         //pechati 'e=1'
      cout << "f=" << f << endl;         //pechati 'f=1'
      
      return 0;
}

Во програмата дадена погоре, на линиите 8, 9 и 10 е извршено претворање на податок (кој е од тип double) во податок од тип int. Сите методи на експлицитно претопување имаат ист ефект. Првиот метод врши претопување со помош на операторот '()', каде типот во кој сакаме да го претвориме податокот се наоѓа во загради. Вториот метод врши претопување на начин кој наликува на повикување на функција: податокот го запишуваме како аргумент на функција. Третиот начин на претопување е најчитлив и се спроведува така што пишуваме "static_cast<tip>(podatok)", каде tip е податочниот тип во кој сакаме да го претвориме податокот podatok.

Оператор sizeof()

Операторот sizeof() овозможува откривање на големина на податочни типови - за дадената машина на која се извршува програмата. Операторот може да прими (како аргумент) или променлива или податочен тип. Следнава програма го демонстрира ефектот од користењето на sizeof():

Програма 9.4

#include <iostream>
using namespace std;

int main()
{
      cout << "tip: bool           golemina: " << sizeof(bool) << " bajt(i)" << endl;
      cout << "tip: char           golemina: " << sizeof(char) << " bajt(i)" << endl;
      cout << "tip: wchar_t        golemina: " << sizeof(wchar_t) << " bajt(i)" << endl;
      
      cout << "tip: short          golemina: " << sizeof(short) << " bajt(i)" << endl;
      cout << "tip: int            golemina: " << sizeof(int) << " bajt(i)" << endl;
      cout << "tip: long           golemina: " << sizeof(long) << " bajt(i)" << endl;
      cout << "tip: long long      golemina: " << sizeof(long long) << " bajt(i)" << endl;
      
      
      cout << "tip: float          golemina: " << sizeof(float) << " bajt(i)" << endl;
      cout << "tip: double         golemina: " << sizeof(double) << " bajt(i)" << endl;
      cout << "tip: long double    golemina: " << sizeof(long double) << " bajt(i)" << endl;
      
      return 0;
}

На компјутерот на авторот на овие предавања, програмата печати:

tip: bool           golemina: 1 bajt(i)
tip: char           golemina: 1 bajt(i)
tip: wchar_t        golemina: 2 bajt(i)
tip: short          golemina: 2 bajt(i)
tip: int            golemina: 4 bajt(i)
tip: long           golemina: 4 bajt(i)
tip: long long      golemina: 8 bajt(i)
tip: float          golemina: 4 bajt(i)
tip: double         golemina: 8 bajt(i)
tip: long double    golemina: 12 bajt(i)

Оператор ','

Операторот ',' (запирка) овозможува запишување на неколку изрази на локација каде што нормално би се очекувал само еден израз. Притоа, ефектот е таков што се извршуваат сите изрази и наредби, но се враќа единствено резултатот од последниот израз (оној кој се наоѓа најдесно). На пример, кај изразот "y=(x=3,x=x+1,x+10);" прво ќе се додели вредноста 3 на променливата x (x=3), па вредноста на x ќе се зголеми за 1 (x=x+1) со што ќе изнесува 3+1=4, и на крајот ќе се додели вредност 14 (x+10) на променливата y (бидејќи се враќа резултатот од последниот израз).

Скоро секогаш (единствен исклучок се for циклусите, за кои ќе зборуваме во посебно предавање) е подобро да се избегнува користењето на овој оператор и да се запишат посебни наредби.

Аритметички услов '?:'

Операторот '?:' (уште познат и под името "условен оператор") овозможува, зависно од исполнувањето или не на одреден услов, враќање на една од две можни вредности. Условниот оператор е единствен оператор во C++ кој прима 3 аргументи (uslov, izraz1 и izraz2):

(uslov) ? izraz1 : izraz2

Доколку uslov содржи вредност true, тогаш резултатот е izraz1; во спротивно резултатот е izraz2. На пример, изразот "pogolemo=(a>b) ? a : b;" ја сместува поголемата вредност од a и b во променливата pogolemo.

Предност и асоцијативност

Од математика ни е познато дека, при пресметување на одреден израз, множењето има предност во однос на собирањето. Правилниот начин на пресметка на изразот "2+3*5" е "2+(3*5) = 2+15 = 17". Во C++ постојат слични правила: секој оператор има дефинирано свој приоритет (предност) и, зависно од тој приоритет, одредени операции се извршуваат пред други. Покрај приоритет, за операциите се дефинирани и т.н. асоцијативни правила: правила кои кажуваат во кој редослед се извршуваат операциите со ист приоритет. На пример, од асоцијативните правила може да видиме дека изразот "2+3+4" се пресметува како "(2+3)+4", т.е. се пресметува од лево на десно. Операторите '*' и '/' имаат ист приоритет, и според асоцијативните правила тие се извршуваат од лево на десно, па изразот "2*2/4" се извршува како "(2*2)/4". Следнава табела ги содржи сите оператори дефинирани во C++, нивниот приоритет (почнувајќи од оние оператори со најголем приоритет) и дефинираните асоцијативни правила (немојте да ја учите табелава, само погледајте):

Оператор Опис Приоритет Асоцијативност
:: поглед 1 (највисок) лево-на-десно
() [] . -> ++ -- dynamic_cast static_cast reinterpret_cast const_cast typeid постфикс 2 лево-на-десно
++ -- ~ ! sizeof new delete * & + - (type) префикс, покажувачи, референци, знаци, дел унарни оператори, претопување 3 десно-на-лево
.* ->* покажувачи 4 лево-на-десно
* / % множење, делење, остаток 5 лево-на-десно
+ - собирање, одземање 6 лево-на-десно
<< >> поместување 7 лево-на-десно
< > <= >= споредби 8 лево-на-десно
== != споредби 9 лево-на-десно
& И, бит по бит 10 лево-на-десно
^ XOR, бит по бит 11 лево-на-десно
| ИЛИ, бит по бит 12 лево-на-десно
&& логичко И 13 лево-на-десно
|| логичко ИЛИ 14 лево-на-десно
?: условен оператор 15 десно-на-лево
= *= /= %= += -= >>= <<= &= ^= |= доделување на вредност 16 десно-на-лево
, оператор ',' 17 (најмал) лево-на-десно

Постојат многу оператори во C++ и учењето на приоритетот на секој од нив е тешка работа. Наместо да го учите приоритетот, научете се да додавате загради во сите ваши изрази. На тој начин и напишаните програми ќе ви бидат многу полесни за разбирање. Погледнете ја следната програма:

Програма 9.5

#include <iostream>
using namespace std;

int main()
{
      int a = 5, b = 10, result;
      
      result = (a>b) ? a : b;           //result = 10
      
      result = a+b+10;                  //result = 25
      result = a+b*b;                   //result = 105
      
      result = (a+b)*b;                 //result = 150
      
      cout << result << endl;           //pechati '150'
      
      return 0;
}

Дозвола за користење: CC BY-NC 2.5 ©