Податочни типови. Променливи

Во претходните две предавања разгледавме многу едноставна програма за печатење текст на екранот. Но, каква е користа од таа програма? Сигурно, целта на пишувањето на програми не е компјутерот да отпечати "Hello world!" на екранот или да запише "Hello world!" во некоја датотека - побрзо ќе биде самите да отвориме датотека и да го запишеме потребниот текст. Целта на програмирањето е да пишуваме програми кои ќе ни помогнат во решавањето на вистински проблеми - да работиме со вистински податоци.

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

Променливите ќе ги објасниме преку следниот едноставен пример: замислете дека имате тетратка и некој од вас побара, со молив, да ги запишете броевите 1, 2 и 3 (секој на своја страна). На првата страна треба да запишете 1, на втората страна 2 и на третата страна 3. Сега, некој побара од вас бројот кој е запишан на првата страна да го промените во 4 - нормално, тоа ќе го направите така што, со гума, ќе го избришете бројот 1 (тоа е бројот запишан на првата страна) и потоа ќе запишете 4. Сега, на првата страна е бројот 4, на втората 2, а на третата 3. Понатаму, некој бара од вас да ги соберете броевите кои се наоѓаат на првата и третата страна од тетратката (4 + 3), и да го запишете резултатот на 3-тата страна - вие ќе го избришете, со гума, бројот кој се наоѓа на 3-тата страна (3) и ќе запишете 7 (4+3). Во C++, процесот кој го објаснивме може да се запише со следниве неколку наредби (x ја означува првата страна, у ја означува втората страна, додека z ја означува третата страна):

Извадок 6.1

int x = 1;            //deklariraj deka kje zapishuvame celi broevi vo x i zapishi 1
int y = 2;            //deklariraj deka kje zapishuvame celi broevi vo y i zapishi 2
int z = 3;            //deklariraj deka kje zapishuvame celi broevi vo z i zapishi 3
x = 4;                //zapishi 4 vo x (se prebrishuva starata vrednost)
z = x + z;            //zapishi 7 vo z (se prebrishuva starata vrednost)

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

Програмерот не го интересира мемориската локација - тој врши операции единствено врз променливата; идентификувајќи ја преку нејзиното име. Првиот пример можеме да го промениме така што наместо x користиме име prva, или prvaStrana, итн.

Именување на променливи

Од примерот даден погоре јасно се гледа дека за да декларираме една променлива, потребно е да се одлучиме за име. Во C++, валидно име за една променлива е секое име кое се состои од една или повеќе букви, цифри или знаци '_'. Имињата не смеат да содржат никакви други видови на симболи (празно место, точки, запирки, извичници, итн.). Покрај тоа што смеат да содржат само букви, знаци и '_', постојат и две додатни ограничувања во именувањето на променливите:

  • имињата не смеат да почнуваат со цифра ('1va', '5bukva', '0ime', '200simboli', итн. се примери за погрешно именувани променливи).
  • имињата не смеат да се поклопуваат (да се еднакви) со некој од резервираните (клучни) зборови* во C++. Доколку внесете некој од овие зборови во едиторот во Code::Blocks, тие ќе бидат задебелени (означени со bold).

*Клучни зборови (зборови кои веќе имаат своја дефиниција) во C++ се (немојте да сте пробале да ги запаметите овие имиња - доволно е што Code::Blocks ги знае):

auto, const, double, float, int, short, struct, unsigned, break, continue, else, for, long, signed, switch, void, case, default, enum, goto, register, sizeof, typedef, volatile, char, do, extern, if, return, static, union, while, asm, dynamic_cast, namespace, reinterpret_cast, try, bool, explicit, new, static_cast, typeid, catch, false, operator, template, typename, class, friend, private, this, using, const_cast, inline, public, throw, virtual, delete, mutable, protected, true, wchar_t, and, bitand, compl, not_eq, or_eq, xor_eq, and_eq, bitor, not, or и xor.

Иако не е задолжително и компајлерот не го бара тоа од вас, обидувајте се да се придржувате до следниве правила на именување:

  • променливите треба да имаат имињата кои означуваат што тие прават ('vkupno' е многу подобро име од 'y')
  • имињата на променливите започнуваат со мала буква ('rezultat' наместо 'Rezultat' или 'REZULTAT')
  • доколку името треба да содржи повеќе зборови, почетната буква на секој следен збор (по првиот) треба да е голема ('prvaBukva', 'rezultatOdSobiranjeto', 'imeNaCovekot', 'ushteEdnoImeNaPromenlivaSoPovekjeZborovi', итн.)

Дискусијата околу имињата ќе ја завршиме со потсетување дека C++ е јазик осетлив на големината на искористените букви - 'prva' е различно од 'Prva' и 'prVa', 'main()' е различно од 'Main()', итн.

Податочни типови

Кога читаме некоја книга или запишуваме податоци во тетратка, не е потребно да го дефинираме типот на податок кој го читаме/запишуваме. На пример, кога ќе прочитаме "781", нас (луѓето) веднаш ни е јасно дека тука станува збор за цел позитивен број. Ако некаде прочитаме "програмирање", јасно ни е дека сме прочитале збор, а не цел или децимален број, буква, или нешто друго.

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

Без поголеми напори, може да смислиме и начин на чување на децимални броеви - ќе дефинираме шема за нивно претставување (некои бинарни цифри ќе означуваат вредност, други знак - дали бројот е позитивен или негативен, трети каде се наоѓа запирката, итн). Слично и за симболите - може да направиме табела каде ќе дефинираме која буква на која бројка одговара (на пример, 'A' може да одговара на вредноста 65 – 01000001 бинарно). Ова всушност и се користи кај денешните компјутери – името на табелата е "American Standard Code for Information Interchange" и 'A' навистина одговара на вредноста 01000001.

Но, кога на компјутерот ќе му кажеме дека на одредена мемориска локација се наоѓа податок (1011101110), тој не може директно да го протолкува податочниот тип – 1011101110 може да означува вредност на позитивен цел број, децимален број, знак (симбол), или нешто друго. Тука стапуваат на сила податочните типови: дефиниции од страна на програмерот кои му кажуваат на компјутерот за каков податочен тип се работи – како да ја интерпретира низата од нули и единици.

Основните податочни типови во C/C++ се: bool (boolean – точно и погрешно), char (скратено од character - знак), int (integer - цел број), float (скратено од floating-point number – број со подвижна точка - децимален број) и double (double precision floating point number - децимален број со двојна прецизност).

Дефиницијата на некои од овие типови може да се измени/дообјасни. Тоа се прави со помош на зборовите: signed, unsigned, short и long. Сите расположливи основни податочни типови се дадени во следната табела:

име опис големина опсег
bool две вредности - точно (true) и погрешно (false) 1 бајт (8 бита, од кои 7 се игнорираат) true и false
char може да се користи за чување знаци или цели броеви 1 бајт Доколку се чуваат цели броеви, опсегот е од -128 до 127, но може да биде и 0-255 (кај некои архитектури). За правилно извршување, кога сакате да чувате цели броеви, секогаш користете unsigned char или signed char (види ги следните 2 податочни типови).
signed char исто како char, но гарантирано може да чува и негативни броеви 1 бајт -128 до 127
unsigned char исто како char, но гарантирано може да чува само позитивни броеви 1 бајт 0 до 255
short
short int
signed short
signed short int
(сите имаат ист ефект)
цели броеви (позитивни и негативни) 2 бајти -32768 до 32767
unsigned short
unsigned short int
позитивни цели броеви 2 бајти 0 до 65535
int
signed int
цели броеви (позитивни и негативни) 4 бајти -2147483648 до 2147483647
unsigned
unsigned int
позитивни цели броеви 4 бајти 0 до 4294967295
long
long int
signed long
signed long int
цели броеви (позитивни и негативни) 4 бајти -2147483648 до 2147483647
unsigned long
unsigned long int
позитивни цели броеви 4 бајти 0 до 4294967295
long long
long long int
signed long long
signed long long int
цели броеви (позитивни и негативни) 8 бајти -9223372036854775808 до 9223372036854775807
unsigned long long
unsigned long long int
позитивни цели броеви 8 бајти 0 до 18446744073709551615
float децимални броеви 4 бајти околу 7 точни цифри
double децимални броеви, двојна прецизност 8 бајти околу 15 точни цифри
long double децимални броеви 10 (или 12) бајти околу 22 точни цифри

Да ја објасниме погорната табела! Прво, да дефинираме што е тоа бајт. Бајт е единечната меморија со која може да располага компјутерот. Практично, бајт претставува збир од 8 бинарни цифри (на пример, 10010010 или 00010101). Бинарните цифри (0 и 1) уште се викаат и битови. Значи, бајт е збир од 8 битови. Компјутерите може да групираат повеќе бајти и да вршат операции врз овие групи од бајти. Така функционираат горенаведените податочни типови.

Поголеми величини од бајт се: килобајт (1024 бајти), мегабајт (1024 килобајти или 1024*1024=1048576 бајти), гигабајт (1024 мегабајти), терабајт, итн. За овие термини веќе сте слушнале.

Од исклучителна важност е да кажеме дека опсезите дадени во горната табела важат за повеќето денешни компјутери и софтверски алатки - но не за сите. Стандардот со кој е дефиниран C++ не опишува колкав треба да е опсегот на short, int или long. Единствената гаранција е дека long нема да има помал опсег од int и int нема да има помал опсег од short. int одговара на податочниот тип со кој процесорот работи најбрзо (кај 32-битни процесори, int има големина од 4 бајти = 32 бита). Слично се дефинирани и float и double. char секогаш има големина од 1 бајт.

short, int и long се основни типови за чување цели броеви. short и long може да се дефинираат со еден збор (short или long), или со два збора – short int и long int. Ефектот е ист. Понатаму, short, int и long може да бидат signed (со знак) или unsigned (без знак, т.е. да чуваат само позитивни броеви). Доколку, пред short, int или long не се наведе signed или unsigned, се претпоставува дека типот е signed (со знак). Затоа, во табелата беа дадени повеќе начини на дефинирање на истиот тип (види прва колона од табелата). На пример, signed short int може да се дефинира како short, short int или signed short int.

Во табелата дадена погоре, некои податочни типови беа означени со сива боја. Сигурно се прашувате што тоа значи. Накратко, тоа значи дека тие податочни типови многу ретко се користат - и, барем засега, нема потреба да ги учите. Во продолжение се дадени неколку аргументи за тоа:

  • Податочниот тип float има многу ограничена прецизност (може прецизно да чува само 7 цифри од децималниот број). Поради тоа, препорачливо е секогаш да користите double. Денешните компјутери имаат повеќе од доволно меморија.
  • Податочниот тип short има ограничен опсег, и за него важат сите аргументи дадени и за float. Дополнително, бидејќи int е дефиниран да одговара на архитектурата на процесорот, операциите (собирање, одземање, множење, ...) извршени врз int ќе бидат побрзи од оние извршени врз short.
  • Сигурно забележавте дека long зафаќа ист податочен простор и има ист опсег како и int (кај 32-битни процесори). Всушност, кај 32-битни процесори, long има ист ефект како int. Доколку сакате да користите 64 бита за претставување на број, користете long long.
  • Бидејќи е потребно многу повеќе меморија за чување на long double вредност, а добиената екстра прецизност е навистина мала, програмерите ретко креираат променливи за чување на long double вредности. Оние софтвери кои треба да гарантираат одредена прецизност (на пример, до 50-та децимала), креираат свои податочни типови и ги чуваат броевите како низи од цифри - она што ние, луѓето, го правиме кога запишуваме или вршиме операции врз цели или децимални броеви. Инаку, long double, често (зависно од компајлерот и процесорот), се користи интерно (од страна на процесорот) за извршување на одредени операции. На пример, кога собираме две променливи дефинирани како double, процесорот може интерно да ги собере овие броеви во long double (за поголема прецизност), а потоа да го врати резултатот како double вредност.
  • Избегнувајте да користите unsigned short/int/long. Некои програмски јазици не ни дозволуваат дефинирање на вакви податочни типови (без знак). Негативните броеви се дел од математиката - понекогаш очекуваме некој резултат да е позитивен, но можно е меѓурезултатот од пресметката (на пример a-b+c>0) да е негативен (a-b<0). Доколку ви е потребен поголем опсег од оној што го нуди int, користете long long.

Тоа е тоа. Значи, ви останува да ги научите податочните типови: bool (за чување на true/false вредности), char (за чување на знаци), int (за чување на цели броеви), long long (за чување на 64-битни цели броеви - вредности поголеми од 231=2147483647) и double (за чување на реални/децимални броеви).

Во некое од наредните предавања ќе зборуваме за низи и string-ови (текстуални низи), и како основните податочни типови (кои уште се среќаваат и под името примитивни податочни типови) може да се искористат за чување на низи од броеви, зборови или реченици (низа од char - знаци).

Декларирање на променливи. Иницијализација

Да го разгледаме следниот пример:

Програма 6.1

#include <iostream>
using namespace std;

int main()
{
      int a;
      int b, c;
      
      float f;
      double d;
      
      double d1, d2, d3, d4;
      
      unsigned int ui;
      
      char ch;
      
      long long ll;
      
      a = 3;
      b = 2;
      
      d = 3.5;
      
      cout << a << endl;
      cout << b << endl;
      cout << d << endl;
      
      return 0;
}

Од примерот даден погоре можете да видите како се декларираат променливи: најпрвин се наведува нивниот податочен тип (char, int, float, double, long long, ...), па веднаш потоа и името на променливата (a, b, c, f, d, d1, d2, d3, d4, ...). Наредбата "int a;" декларира променлива со име "a" и тип int. Декларацијата на една променлива може да се внесе на која било локација во програмата - но, мора да се внимава нејзината декларација да се наоѓа пред било која наредба која ја користи таа променлива (не можеме да пристапиме до вредност на променлива која не сме ја декларирале). C++ дозволува, преку една наредба, декларирање на повеќе променливи од еден ист податочен тип. На пример, наредбата "int b, c;" декларира две променливи (со име "b" и "c") од тип int. Наредбата "double d1, d2, d3, d4;" декларира четири променливи ("d1", "d2", "d3" и "d4") од тип double. Наредбата "double d1, d2, d3, d4;" е еквивалентна, има ист ефект, како и последователното извршување на четири наредби "double d1;", "double d2;", "double d3;" и "double d4;". Истото важи и за "int b, c;" и за кој било друг податочен тип.

Кога ќе декларираме една променлива (на пример, со наредбата "int a;") нејзината вредност е, по правило, неодредена (може да биде што било). Но, често сакаме да им поставиме вредност на променливите во истиот момент кога ги декларираме (овој процес се нарекува иницијализација). Постојат два начина на иницијализација во C++:

  • тип име_на_променлива = вредност;
  • тип име_на_променлива (вредност);

Да ја разгледаме следната програма:

Програма 6.2

#include <iostream>
using namespace std;

int main()
{
      int a = 0;
      int b(1);
      
      int c=2, d(3), e(4), f=5, g(6);
      
      double q(3.5);
      char m = 'B';
      
      cout << (a+b+c+d+e+f+g) << endl;
      
      return 0;
}

Во примерот даден погоре, променливите a, c, f и m се иницијализирани според првиот споменат начин (со користење на знакот '='), додека b, d, e, g и q според вториот (наведување на вредноста во загради). Програмата, на излез, печати 21 (21=a+b+c+d+e+f+g=0+1+2+3+4+5+6).

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

Единствен исклучок од погорното правило е декларирање на променливи кои ќе ги користиме за читање на податоци (од стандарден влез, датотека, итн.). Во тој случај, декларацијата на променливите и читањето може да се изведе во два последователни реда - во еден ред може да ги декларираме променливите, а веќе во следниот да им доделиме вредност:

int a, b;
cin >> a >> b;

Константи

Константи претставуваат вредности кои не може да се променат за време на извршување на програмата. C++ овозможува креирање на константи на два начина: со користење на #define наредба или со специфицирање на терминот const пред типот и името на константата. Да ја разгледаме следната програма:

Програма 6.3

#include <iostream>
using namespace std;

#define PI 3.141

int main()
{
      const int N = 5;
      
      cout << N*PI << endl;             //pechati '15.705'
      return 0;
}

Константата со име N е креирана на начин многу сличен со начинот на креирање на променливи - единствената разлика е што пред типот и името го наведовме терминот const.

Од друга страна пак, #define наредбата со која што ја креиравме константата PI има една важна карактеристика која ја разликува од сите останати наредби кои ги разгледавме досега: никаде нема знак за крај ';'. Тоа е така бидејќи наредбата #define се извршува за време на т.н. фаза на предпроцесирање - фаза која се извршува пред самиот процес на преведување на кодот во извршна датотека (потсетете се на дискусијата за "#include <iostream>"). Она што системот, буквално, го прави е замена на секое појавување на зборчето PI со 3.141. Доколку, кај #define наредбата, по 3.141 наведовме знак за крај ';' и тоа знакче ќе се најдеше во кодот (па наместо "N*PI" ќе имавме "N*PI;") што ќе предизвикаше појавување на грешка при преведување на програмата.

Вредности

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

Во C++, целите броеви може да се внесат/специфицираат во три основни бројни системи:

  • декаден броен систем (основа 10), преку едноставно наведување на бројот - на пример, 1911094
  • октален броен систем (основа 8), преку наведување на знакот '0' пред бројот - на пример, 0117523
  • хексадецимален броен систем (основа 16), преку наведување на '0x' пред бројот - на пример, 0x123af

По правило, специфираните цели броеви одговараат на податочниот тип int. Доколку сакаме внесениот број да е од некој друг тип (на пример, unsigned int) потребно е да искористиме некој (или некои) од следниве три спецификатори:

  • u (или U) - доколку сакаме да означиме број без знак, на пример 38U
  • l (или L) - доколку сакаме да означиме long вредност, на пример 129313L
  • ll (или LL) - доколку сакаме да означиме 64-битна вредност, на пример 1238129038108213LL

Што се однесува до реалните броеви, постојат два начини на кои тие може да се внесат: во стандардна форма (со специфицирање на самата вредност - на пример, 3.141), или во експоненцијална форма (со специфицирање на основен број и степен на бројот 10 - на пример, 3.141e8, што е еднакво на 3.141 * 108). Забележете го користењето на буквата 'e' (или 'E') за одделување на двата множитела.

По правило, специфираните реални броеви одговараат на податочниот тип double. Доколку сакаме внесениот број да е од некој друг тип (на пример, float) потребно е да искористиме еден од следниве два спецификатори:

  • f (или F) - доколку сакаме да означиме 32-битна float вредност, на пример 3.141f
  • l (или L) - доколку сакаме да означиме long double вредност, на пример 3.141L

Да ја разгледаме следната програма:

Програма 6.4

#include <iostream>
using namespace std;

int main()
{
      int i1 = 3;
      unsigned int i2 = 3U;
      long long i3 = 123456789123456LL;
      
      const float f1 = 8.15f;
      const double f2 = 3.141;
      const long double f3 = 3.141L;
      
      double f4 = 1e5;          //10^5
      cout << f4 << endl;       //pechati '100000'
      return 0;
}

Во програмата дадена погоре, f4 претставува променлива од тип double со вредност 105=100000. Тоа е единствената променлива чија вредност се печати од страна на програмата, па нејзиниот излез е '100000'. Останатите променливи се креирани за да ги илустрираат можните начини на специфицирање на цели и реални броеви.

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