[itk] Számítástechnika kezdőknek

C++ programozás kezdőknek - alaptípusok

[2020. november 22.] [ christo161 ]

Ebben a tananyagrészben az első példaprogramjainkban leggyakrabban használt alaptípusú változók jellemzőiről lesz szó.

Előző tananyagrész: változók, konstansok, literálok
Következő tananyagrész: whitespace, layout

Ez a tananyagrész jelenleg átdolgozás alatt áll.

Alapvető tudnivalók

A programozásban azért beszélünk típusokról, mert bizonyos hibák elkerülése érdekében számon kell tartanunk, hogy azok az adatok, amikkel a programunk dolgozik, milyen típusúak.

Programozási nyelvek típusrendszere

A C++ egy statikusan típusos- és erősen típusos programozási nyelv.

A statikusan típusos programozási nyelvekben fordítási időben ki kell derülnie minden kifejezés és részkifejezés típusának. Például egy változó létrehozásakor ki kell, hogy derüljön, hogy az a változó milyen típusú. Vagy a definícióban megadjuk a konkrét típust, vagy az auto kulcsszóval a fordító a kezdőértékből ki tudja következtetni a változó típusát, például: auto variable = 3.14;
Az erősen (vagy szigorúan) típusos programozási nyelvekben az adat típusa meghatározza az adattal végezhető műveleteket is (például stringeket nem lehet szorozni/osztani, akkor sem, ha számokat tartalmaznak), valamint ezekre a programozási nyelvekre jellemző, hogy egy változó típusa (pl. egy változó típusa) nem változhat meg a program futása közben. Ha egy változónak meg akarjuk változtatni a típusát, egy új változót kell létrehoznunk, és ennek a változónak tudjuk értékül adni az eredeti változó értékét, és a konvertálás sem biztos, hogy automatikusan működik. A C++ nyelvben például stringet számmá vagy számot stringgé alakítani nem lehet oly módon, hogy az egyik változónak értékül adjuk a másik változót, hanem segédfüggvényeket kell használnunk.

Léteznek dinamikusan típusos és gyengén típusos programozási nyelvek is, például a Javascript vagy a PHP. Ezekben a nyelvekben a változók létrehozásakor nem kell megadni a változók típusát, illetve a program futása közben egy változóban tárolhatóak különböző típusú értékek is.

Abból, hogy egy nyelv statikusan típusos, nem következik, hogy erősen típusos is vagy fordítva. Például a Python dinamikusan típusos és erősen típusos.

Típus

A számítástechnikában a típus technikailag azt jelenti, hogy a memóriában lévő adatokat hogyan értelmezzük. Például egy valós szám esetén valahogy így lehetne elképzelni: az első bit az előjel, a következő néhány bit a kitevő, a maradék bitek pedig a hatványozatlan szám. Ezekkel az alacsonszintű
Vagy például amikor egy karaktert tárolunk, valójában egész számokat tárolunk, és egy úgynevezett karaktertábla írja le, hogy melyik számnak milyen karakterérték felel meg (például az ASCII tábla szerint a 97 jelenti a kis a betűt). Egy szöveg valójában több karakterérték egymás mögé rakva a memóriában, amiket technikailag tömbökben tárolunk (a tömbökről későbbi tananyagrész szól), egy dátum pedig valójában három darab szám, amit általában osztályok/objektumok segítségével valósítunk meg, satöbbi.

Típusa nem csak változóknak lehet, hanem például kifejezéseknek, részkifejezéseknek, tömböknek, pointereknek, függvényeknek, és egyéb dolgoknak.

Alaptípusok és alaptípusú változók

Az alaptípusok (fundamental types), vagy más néven beépített típusok (built-in types), vagy egyszerű típusok (primitive types) olyan típusok, amik megtalálhatóak a nyelvben, anélkül, hogy bármit is includeolnunk kellene, illetve nem tartalmaznak egyszerre több értéket, azaz nem összetett típusok.
A C++ nyelvben az alaptípusú változók beépítettek is (nem kell őket includeolni) és egyszerűek is (nerm tartalmaznak egyszerre több értéket). Ez nem biztos, hogy más programozási nyelvekben is így van. Elképzelhető, hogy valamelyik nyelvben includeolás nélkül elérhető például a string típus, de ettől még a string típust összetett típusnak tekintjük.

A C++ nyelvben használható alaptípusok teljes listáját például itt találjuk meg:

Ezek közül nem mindegyik lehet változók típusa, például void típusú változót nem lehet létrehozni, a void típus pointerekre vagy függvények visszatérési értékére vonatkozhat.

A leggyakoribb alaptípusú változók, amiket az első példaprogramjainkban használunk:
bool, char, int, double.

A C++ nyelvben nem tekintjük alaptípusoknak az alaptípusokból létrehozott dolgokat, mint például tömbök, függvények, pointerek, referenciák, satöbbi. Például:

Az std::is_fundamental</*tipus*/>::value; utasítással vizsgálhatjuk meg, hogy a < > jelek között megadott típus alaptípusú-e, illetve az std::is_compound</*tipus*/>::value; utasítással pedig azt, hogy összetett típus-e. Ezeket az utasításokat akkor használhatjuk, ha includeoljuk a C++ standard library type_traits header fájlját. Például:

#include <iostream>
#include <string>
#include <type_traits>

int main() {
std::cout.setf(std::ios::boolalpha);

std::cout << "int type is fundamental?\n" << std::is_fundamental<int>::value << '\n';
std::cout << "std::string type is compound?\n" << std::is_compound<std::string>::value << '\n';
}

Típusminősítők (qualifiers)

Angolul CV qualifiersnek, illetve const and volatile qualifiersnek is szokták nevezni őket.

const

A const kulcsszóról már volt szó korábban, noha nem csak változók esetén használhatjuk, hanem többféle kontextusban, lényegében azt jelenti, hogy valaminek az értékét nem szeretnénk megváltoztatni, és ha ennek megkísérlésére mégis sor kerülne, akkor fordítási hibát kapunk.

volatile

A volatile kulcsszó segítségével arra utasítjuk a fordítót, hogy egy adott változóval kapcsolatban ne végezzen optimalizálást. Ebben a tananyagban ezzel nem foglalkozunk részletesen. Például itt találunk róla néhány tudnivalót:

Típusmódosítók (modifiers)

Méret

A számítógép memóriája véges, ezért például az egész szám-, vagy valós szám típusú változóknak nem lehet túl nagy vagy túl kicsi szám az értékük. Nagyon nagy számok (bignum, bigint) tárolása C++ nyelvben például libraryk segítségével oldható meg, bár ez elsőre zavarónak tűnhet, a helyzet az, hogy a programozásban általában nincs szükség nagyon nagy számok (például 100 vagy 1000 jegyű számok) tárolására. Ez kicsit ahhoz hasonlítható, hogy a mai átlagos számológépekbe sem tudunk bármennyi számjegyet beírni, a hétköznapi számításaink elvégzéséhez mégis megfelelőek.

A C++ nyelv alaptípusai között különböző méretű egész szám-, illetve valós szám típusok léteznek, amiknek a nagy részét típusmódosítókkal használhatjuk.

  • short int, long int, long long int
  • long double

Például egy short int típusú változó kevesebb helyet foglal a számítógép memóriájában, mint egy int típusú változó, de a legnagyobb szám, ami értékül adható egy short int típusú változónak, sokkal kisebb (a mai átlagos számítógépeken jó eséllyel 32767), mint amekkora szám egy int típusú változónak lehet az értéke (a mai átlagos számítógépeken jó eséllyel 2147483647).

A C++ nyelvben nem létezik short double, a double-nél szűkebb tartományon értelmezett valós szám típus a float. A C nyelv terminológiájában esetleg találkozhatunk a long float kifejezéssel is, ami a double megfelelője, például a printf és scanf függvények format specifier stringjei között.

Ezeket a típusmódosítókat a változók definiálásakor (létrehozásakor) kell megadni. Például:

short int variable_example_1 = 0;
long long int variable_example_2{4LL};
long double variable_example{1.3L};

Előjel

Egész szám típusú változók esetén az unsigned típusmódosítóval kizárólag nem negatív számok tárolhatóak egy adott változóban. Ennek előnye, hogy ha biztosan tudjuk, hogy nem kell negatív számokat tárolnunk, akkor a nagyobb pozitív számokat is tárolhatunk egy adott változóban. A mai átlagos számítógépeken egy int típusú változóban jó eséllyel 2147483647 a legnagyobb tárolható szám, egy unsigned int típusú változóban pedig 4294967295. Könnyen belátható, hogy ez miért van így, az int típusban a mai átlagos számítógépeken -2147483648 és 2147483647 közötti számértékek tárolhatóak, akkor ha egy ugyanekkora méretű változóban csak pozitív számokat tárolunk (és a nullát), akkor 0-tól nagyjából kétszer akkora számértékig, mint az int lehetséges legnagyobb értéke tárolhatjuk a számokat.

Az unsigned típusmodósítót a változók definiálásakor (létrehozásakor) kell megadni. Példa:

Az unsigned típusmódosító nem használható valós szám típusú változókhoz (float, double, long double).

Létezik signed típusmódosító is, ami ugyanazt jelenti, mintha nem írnánk semmit helyette az egész szám típusú változókhoz. Például a signed int ugyanazt jelenti, mint az int.

Fontos: hibát okoz, ha egy kifejezésben egyszerre használunk unsigned és signed típusokat.

#include <iostream>

int main() {
unsigned int unsigned_example = 1;
int signed_example = 2;
std::cout << unsigned_example - signed_example << '\n';
}

#include <iostream>

int main() {
std::cout << 1u - 2 << '\n';
std::cout << 1 - 2u << '\n';
}

Típusmódosítók a literálokban

A literálok esetén felmerülhet, hogy ha leírjuk például azt, hogy 128, akkor például az int típusra vagy az unsigned int típusra, vagy a long int típusra, stb gondoltunk. Az ezekre vonatkozó jelölések (suffixek) a következőek:

  • 0 - int típusú
  • 128 - int típusú
  • 128u - unsigned int típusú
  • 128L - long int típusú
  • 128LL - long long int típusú
  • 128uLL - unsigned long long int típusú

 

  • 1.2f - float típusú
  • 1.2 - double típusú
  • 1.0 - double típusú
  • 1. - double típusú
  • 0.0 - double típusú
  • 1.2L - long double típusú

A short intnek nem létezik jelölése literálok esetén.

Implementációfüggő méretek

A C++ nyelvben a leggyakrabban használt alaptípusok mérete implementációfüggő, azaz különböző számítógéptípusonként és különböző fordítók használata esetén eltérhet. Az átlagos felhasználói számítógépek között ebben jó eséllyel csak kisebb különbségek lehetnek. Az int, float, double, satöbbi típusok méretét illetően jelentős különbség például a 32 bites processzorú és 64 bites processzorú számítógépek között van, illetve jelentősen eltérhetnek a megszokott értékekről, ha például mikrokontrollerekre vagy mainframe számítógépekre írunk programokat C++ nyelven.

//

Túlcsordulás

//

Régi  tananyag:

Egyszerű típusú változók méretei, tartományai

Egyszerű típusoknak nevezzük azokat a típusokat, amik nem tartalmaznak további adattípusokat. Később lesz szó bővebben az összetett típusokról (struct és class adatszerkezetek segítségével definiált típusok, például a string típus). Az egyszerű típusok C++-ban a következők: bool, char, wchar_t, int, float (továbbá az int és float különböző méretű változatai, pl. short int vagy double).

C++-ban ilyen típusú változókat használhatunk:

Egy változó típusának mérete azt jelenti, hogy mettől meddig tárolhatjuk benne az értékeket. Például egy 4 bájtos intben a legkisebb tárolható érték -2147483648, a legnagyobb pedig 2147483647. Az egyes típusok pontos mérete ezen fejezet későbbi részében lesz részletezve.

(Az alábbiakat így értelmezzük:
egy adott típus: ami ilyen érték tárolására alkalmas)

short int: kisebb egész szám
int: egész szám (pl. -1, 0, 128)
long int: nagyobb egész szám
long long int: nagyobb vagy mégnagyobb egész szám

Implementációfüggő (fordító- és számítógép-architektúrafüggő), hogy a long long int mérete bővebb-e mint a long inté, mivel a C++ szabvány csak azt köti meg, hogy legalább ugyanakkora legyen.

unsigned short int: kisebb nem negatív egész szám
unsigned int: nem negatív egész szám (pl. 0, 1024)
unsigned long int: nagyobb nem negatív egész szám
unsigned long long int: nagyobb vagy mégnagyobb nem negatív egész szám

float: tizedestört (pl. -0.013, 0.0, 124.78)
double: nagyobb pontosságú tizedestört
long double: mégnagyobb pontosságú tizedestört

Előfordulhat, hogy a float, double és long double típusokat más tananyagokban valós, racionális vagy lebegőpontos számoknak nevezik. Ebben a tananyagban az egyszerűség kedvéért tizedestörteknek nevezzük őket, noha nem csak tizedestörteket tartalmaznak, hanem például a végtelen és mínusz végtelen jelölésére alkalmas értéket, valamint a NaN hibajelző értéket (mely például a 0.0/0.0 osztás eredménye).
Tizedestörtek esetén ne felejtsük el, hogy tizedesvessző helyett tizedespontot kell használnunk, az angol jelölésnek megfelelően.

bool: logikai (true és false értékek)

A logikai változókat a következő fejezetben tárgyaljuk.

char: egy karakter (pl. 'a', '2', '%')

A char típusú változókban valójában egész számokat tárolunk, és a kiíratáskor az egyes számértéknek megfelelő karakter íródik ki. Erről jelen fejezet későbbi részében lesz szó bővebben. Ezt azért érdemes tudni, mert ennek köszönhetően aritmetikai műveletek is elvégezhetők char típusú változókkal. Például értelmes művelet, ha egy karakter értékét megnöveljük: 'A' + 32 //értéke 'a'
Így lehet például kisbetűsíteni, vagy nagybetűsíteni.

string: szöveg/karakterlánc (pl. "szoveg", "tobb szo", "tobb\nsor", "")

Az itt felsorolt típusok egyszerű típusok, kivéve a stringet. A string valójában osztály/objektum, de lényegében ugyanúgy kezelhető, mint a többi típus, és a nyelvben alapból szerepel (a C nyelvvel ellentétben). Ebben a tananyagban az egyszerűség kedvéért a string típusú objektumokat string típusú változónak fogjuk nevezni. Az osztályokról és az objektumokról csak későbbi fejezetben lesz szó.

wchar_t: egy karakter (bővebb karakterkódolást támogat, de a mérete implementációfüggő). Ebben a tananyagban nem fogjuk használni.

Az eddig felsorolt típusok mérete implementációfüggő (különböző rendszereken eltérő lehet). A C++11-es szabványban bevezettek fix méretű típusokat is.

Néhány C++11-es szabványban elérhető típus:

Ne felejtsük el, hogy ezeket a típusokat csak akkor használhatjuk, ha a fordítót a C++11-es szabvánnyal paraméterezzük. Ennek mikéntjére az első fejezetben tértünk ki.

int8_t: minden rendszerben 8 biten (1 bájton) ábrázolt egész szám
int16_t: ugyanaz 16 biten
int32_t: ugyanaz 32 biten
int64_t: ugyanaz 64 biten
uint8_t: fixen 8 bites nem negatív egész szám

char16_t: egy karakter (16 bitbe beleférő karakterkódolásokat támogat)
char32_t: ugyanaz 32 biten

A C++11-es típusokat ebben a tananyagban nem fogjuk használni.

Az itt felsorolt adattípusokból adatszerkezeteket (pl. tömb, láncolt lista, sor, halmaz, fa, verem, satöbbi, illetve egyedi adatszerkezeteket) is létrehozhatunk, melyekről csak későbbi fejezetekben lesz szó.

A C++ szabvány nem határozza meg pontosan az egyszerű típusok méretét, így azok fordítótól és hardverkörnyezettől függően eltérőek lehetnek különböző rendszereken (másképpen fogalmazva implementációfüggő a méretük). A rendszerünkön érvényes méreteket, tartományokat a sizeof operátorral, illetve a C++ Standard Library limits header fájljában deklarált osztálysablonok tagfüggvényeivel és adattagjaival kérdezhetjük le.

Mindenesetre abban biztosak lehetünk, hogy ezek a megkötések minden rendszeren érvényesek C++-ban. (A <= jel jelentése: kisebb vagy egyenlő, legfeljebb akkora, mint...).

  • 1 == sizeof(char) <= sizeof(short int) <= sizeof(int) <= sizeof(long int)
  • 1 <= sizeof(bool) <= sizeof(long int)
  • sizeof(char) <= sizeof(wchar_t) <= sizeof(long int) <= sizeof(long long int)
  • sizeof(float) <= sizeof(double) <= sizeof(long double)
  • sizeof(int) == sizeof(signed int) == sizeof(unsigned int) és ugyanez short int, long int, valamint long long int esetén is érvényes

Fontos megjegyezni, hogy az 1 == sizeof(char) kifejezésben az 1 nem feltétlenül 1 bájtot jelent, hanem egy egységet, ami bizonyos rendszereken lehet például 2 vagy 4 bájt. Mindenesetre az egy egység ebben a kifejezésben az adott rendszeren a számítógép memóriájában lefoglalható legkisebb értéket jelenti.
Továbbá elő szokott fordulni, hogy egy adott rendszeren például a long int és a long long int mérete megegyező, figyeljük meg, hogy a fenti megkötésekben csak az van előírva, hogy a long long int mérete legalább akkora legyen, mint a long int-é, nem pedig az, hogy mindenképp nagyobb.

A következő utasításokat használhatjuk, ha le akarjuk kérdezni az egyes típusok méreteit:

std::cout << sizeof(short int) << std::endl;

Az én gépemen ezen utasítás végrehajtásakor 2-t ír ki a program. Ebből az következik, hogy az általam használt számítógépen, az általam használt fordító 2 bájtot foglal le a short int típusú változóknak a számítógép memóriájában.

A következő utasítások csak akkor működnek, ha a programkód elején szerepel az #include <limits> utasítás.

std::cout << std::numeric_limits<short int>::digits << std::endl;

Az én gépemen ennél az utasításnál 15-öt ír ki a program. A 2-ből úgy lesz 15, hogy 1 bájt = 8 bit, 2 * 8 = 16, és mivel a short int alapvetően pozitív és negatív számok tárolására is alkalmas, így a változó számára lefoglalt 16 bitnyi memóriaterületből 1 bit az előjel értékének tárolására van lefoglalva (16 - 1 = 15 ).

Ez az utasítás unsigned short int esetén már 16-ot írat ki, mert az unsigned short int típus nem tartalmaz negatív számokat, így nincs szükség az előjel értékének tárolására sem.

std::cout << std::numeric_limits<unsigned short int>::digits;

Egy adott típusnak megadható legalacsonyabb és legmagasabb értéket a következő utasításokkal írathatjuk ki. (Akár a 2. példaprogram értékeit is kicserélhetjük ezekre a kifejezésekre).

std::cout << std::numeric_limits<unsigned short int>::min();
std::cout << std::numeric_limits<unsigned short int>::max();

Ámde a min() tagvüggvény float, double és long double típusok esetén az adott típusnak megadható 0-hoz legközelebb lévő pozitív értéket írja ki. A legkisebb megadható negatív értéket a lowest() tagfügvénnyel írathatjuk ki, viszont ez csak C++11-es szabványtól érthető el.

std::cout << std::numeric_limits<unsigned short int>::lowest();

Ezeket az értékeket nem csak kíváncsiságból érdemes lekérdezni, hanem akkor is hasznosak lehetnek, ha olyan programot szeretnénk írni, amit különböző rendszereken szeretnénk szeretnénk fordítani, és számít az egyes változók értéktartománya közötti különbség (portábilis kódot szeretnénk írni).

Kiegészítő információ: a numeric_limits egyébként egy sablonosztály (típussal paraméterezhető osztály). A digits ennek az osztálynak egy statikus adattagja, a min() pedig egy statikus tagfüggvénye. A statikus adattagokat és tagfüggvényeket . helyett :: operátorral jelöljük. Ezt a témát szintén egy későbbi fejezetben fejtjük ki.

Sajnos a karakterlimit miatt nem tudom ide beilleszteni annak a programnak a forráskódját, amely minden típus méretét kiírja, de ezen a képernyőképen nagyjából láthatjuk, hogy ez hogyan oldható meg:

cpp_sizeof_minmax.png

A könnyebb áttekinthetőség érdekében egy táblázatot is készítettem ezekről az értékekről, viszont sokadszorra megemlítem, hogy ezek az értékek különböző rendszereken eltérőek lehetnek, így a táblázatban szereplő értékeket csak egy példának tekintsük.
A táblázat elkészítéséhez ezek a jellemzők voltak érvényesek: 64 bites processzor (Intel i5), CodeBlocks (16.01) fejlesztői környezet, 4.6.3 verziószámú g++, Ubuntu 12.04 operációs rendszer.

cpp_fundamental_variables.png

Néhány megjegyzés a táblázathoz:

  • A string nem szerepel a táblázatban, mert nem egyszerű típus, hanem osztály/objektum.
  • Csak a char és az int típusoknak van signed (előjeles) és unsigned (előjel nélküli) változata, a float, double és long double típusoknak nincs.
  • Azért létezik signed és unsigned char, mert a számítógépek hőskorában a különböző rendszereken a signed chart, vagy az unsigned chart gondolták optimálisabbnak, de mindkettő megmaradt a nyelvben, hogy azok a programkódok is használhatók legyenek, amikben alkalmazták őket. A legtöbb fordító a char típust signed char-ként definiálja. (A fentebb említett sablonosztályokkal lekérdezhetjük, hogy a fordítónk melyiket használja).
  • A táblázatban szereplő összes típushoz használhatók a következő típusminősítők: const, extern, volatile. Ezeket egy adott változó definiálásakor van lehetőségünk használni (pl. const int elemszam = 10;).
    Az extern és a volitale típusminősítővel csak később foglalkozunk.
  • Az int és a signed int ugyanazt takarja. Ennek alapján természetesen a short int és a signed short int is ugyanaz, illetve a long int és a signed long int is.
  • Short int helyett írhatunk csak simán short-ot, valamint long int helyett long-ot. Továbbá például unsigned short int helyett unsigned short-ot.

Ha unsigned típusokat használunk, ne felejtsük el, hogy lehetséges hibaforrás ha egy műveletben használjuk őket signed típusokkal, mert ekkor átkonvertálódnak signedre, és így értékvesztés történhet.

Túlcsordulásnak (vagy körbefordulásnak) nevezzük, ha egy változó (nem bekéréssel, hanem értékadással) olyan értéket kap, ami túlhaladja a típusának megfelelő tartományt. Például kipróbálhatjuk, mi történik, ha egy int típusú változónak értékül adjuk az int típusban értelmezett legnagyobb értéket (nálam ez 2147483647), majd a változó értékét megnöveljük eggyel. Az eredmény -2147483648 lesz, vagyis a legkisebb érték az int típus tartományában.

Implicit és explicit típuskonverziók

Implicit típuskonverziónak nevezzük az automatikusan történő konvertálásokat. Például ha egy int típusú változónak bekérjük az értékét, a felhasználó viszont tizedestörtet ad meg, akkor a megadott szám egész számmá konvertálódik, és az átkonvertált értéket kapja meg az adott int típusú változó. Hasonló a helyzet ha egy int típusú változónak mondjuk egy double típusú literált, vagy egy double típusú változó értékét adjuk értékül.
Akkor is implicit konverzió történik, ha műveletet végzünk egy int és egy double típusú literállal: 5 / 2.0, ekkor az int típusú literál double típusúvá konvertálódik, és az eredmény is double típusú lesz.

Explicit típuskonverziónak nevezzük amikor a programozó ad utasítást a konvertálásra.
Például ebben az esetben a 2.5 double típusú literál intté konvertálódik (ekkor természetesen veszít a pontosságából):

static_cast<int> (2.5);

Ugyanezt persze változók értékével is megtehetjük, akár a kiíratással egy utasításban:

double valtozo = 2.5; cout << static_cast<int>(valtozo) << endl;

Ezzel az utasítással természetesen csak hasonló típusokat konvertálhatunk. Például egy string típusú érték számmá konvertálása csak másképp valósítható meg, akkor is, ha az a string típusú érték konkrétan egy szám.

Typedef

A typedef-ek aliasok (álnevek, rövidítések) a típusokra és a típusminősítőkre nézve. Érdemes lehet használni őket, ha gyakran definiálunk a programunkban olyan változókat, amiket típusminősítőkkel együtt hosszú lenne kiírni (például extern unsigned long int).
Használatuk egyszerű, amikor definiálunk egy typedef-t, megmondjuk, hogy az milyen típusminősítő(ke)t és típust helyettesítsen, és onnantól kezdve a programunkban egy változó definíciójában nem kell mindig kiírni a típusminősítőket és a típust, hanem elég helyette leírni a typedef-et. Például:

typedef unsigned long int uli_t; //az uli_t ezentúl jelentse azt, hogy unsigned long int
uli_t a; //unsigned long int típusú változó definiálása

KIEGÉSZÍTŐ INFORMÁCIÓK

 

 

A char típusról bővebben

A char típusban valójában számok tárolódnak.

char c = 97;
cout << c << endl;

Ekkor nem 97 íródik ki, hanem egy a betű, mivel az 'a' karakter ASCII kódja 97. Az ASCII táblában nézhetjük meg, hogy melyik karakternek mi az ASCII kódja (a képen a Dec oszlopban). A gyakran használt karakterek 32-től 126-ig találhatóak, a 0 és 31 között lévő számokhoz, illetve a 127-hez speciális jelentés tartozik (például a 10-es az újsor karakter).

Az ASCII tábla:

ascii_table.gif

Ha azt szeretnénk, hogy karakter helyett a neki megfelelő számérték íródjon ki, ezt az utasítást használjuk:

char c = 97;
cout << static_cast<int>( c ) << endl;

Viszont ha a felhasználótól kérünk be értéket egy char típusú változóba, akkor csak az első karakter lesz beolvasva (pl. ha 97-et ír be a felhasználó, akkor 9 lesz beolvasva, pontosabban a 9 karakternek megfelelő ASCII érték, azaz 57).

char c;
cin >> c;

Ha a felhasználó által beírt többjegyű számot szeretnénk egy char típusú változónak értékül adni, akkor először egy int típusú változóba kérjük be az értéket, majd az int típusú változó értékét adjuk értékül a char típusúnak.

int temp;
char c;

cin >> temp;
c = temp;

Persze ha a felhasználó túl sok számjegyből álló számot ad meg, akkor nem lesz eltárolva a tényleges érték, hiszen a char típus csak 1 bájtos.

1 bájton 256 érték tárolható, tehát egy char típusban vagy -128 és 127, vagy pedig 0 és 255 közötti értéket tárolhatunk, attól függően, hogy a char típus signed, vagy unsigned.

Az alap char típus jó eséllyel signed, de a numeric_limits sablonosztály min() és max() tagfüggvényeivel lekérdezhetjük a szélső értékeit (ne felejtsük el, hogy most számként szeretnénk őket kiíratni, használjuk a static_cast operátort inttel paraméterezve):

std::cout << static_cast<int>( std::numeric_limits<char>::min() );

Ha számok tárolására szeretnénk char típust használni, akkor az alap char típus helyett érdemesebb signed vagy unsigned char-t használni.
Persze ekkora spórolásra a mai asztali számítógépeken nincs szükség, nyugodtan használjunk intet vagy short intet számok tárolásához.

Íme egy rövid példaprogram, amiben definiálunk egy signed és egy unsigned char típusú változót, majd az értéküket parancssorból kérjük be, és a beolvasott értékeket kiíratjuk.

#include <iostream>
using namespace std;

int main() {

	signed char b;
	unsigned char c;

	int temp;

	cout << "Kerem adjon meg egy egesz szamot (-128 es 127 kozott)" << endl;
	cin >> temp;
	b = temp;
	cout << "Kerem adjon meg egy egesz szamot (0 es 255 kozott)" << endl;
	cin >> temp;
	c = temp;

	cout << "A beolvasott ertekek: " << static_cast<int> ( b ) <<
", valamint: " << static_cast<int> ( c ) << endl;

	return 0;
}

Ha a felhasználó az int típus tartományán kívül eső számot ad meg, akkor az aktuális és az azt követő bekérések ki lesznek hagyva (így program természetesen nem fog rendeltetésszerűen működni).
Viszont ha a char típusok tartományán kívül eső számot ad meg, akkor túlcsordulás történik. Tekintsünk erre néhány példát.
A signed char típus tartománya a -128 és 127 közti egész számok. Ha a felhasználó 128-at ad meg a signed char változó értékének bekérésekor, akkor annak értéke -128 lesz. Ha 129-et, akkor -127.
Az unsigned char típus tartománya a 0 és 255 közti egész számok. Ha a felhaználó 256-ot ad meg az értékének bekérésekor, akkor 0 lesz az értéke. Ha -1-et, akkor 255.

Ékezetes karakterek tárolására használjuk inkább a string típust, mivel rendszerfüggő, hogy a char típusban az alap ASCII tábla értékein felül tárolhatunk-e még egyéb karaktereket. Persze lehet kísérletezni, hogy a rendszerünkön tudunk-e értékadással vagy bekéréssel ékezetes karaktereket char típusú változóknak értékül adni, de ez inkább haladóknak szóló téma, ha valaki kedvet érez hozzá, ezeken a linkeken olvashat erről bővebben: [1] [2] [3]

Felmerülhet a kérdés, hogy lehet-e bekérés segítségével egy char típusú változónak szóközt, tabulátort, újsort (összefoglaló néven whitespacek) értékül adni. Ehhez használjuk a cin.unsetf(ios::skipws); utasítást, például ily módon:

#include <iostream>
using namespace std;

int main() {
	char b, c;
	cin.unsetf(ios::skipws); //ezt kovetoen szokoz, tabulator ertekul adasa is elfogadott bekeresnel
	cin >> b;
	cin.setf(ios::skipws); //szokoz, tabulator ertekul adasa bekeres eseten ujra ervenytelen lesz
	cin >> c;
	cout << "Hello" << b << "world!\n" << c << endl;
	return 0;
}

Ebben a kis példaprogramban az első bekéréskor esetlegesen megadott szóköz, tabulátor, újsor értékül lesz adva a b változónak, ezt követően a cin.setf(ios::skipws); utasítással újból érvénytelenné tesszük a whitespacek bekérését, így a c változó esetén a program már csak egy "tényleges" karaktert fogad el a bekéréskor (whitespacek megadása esetén a program megismétli a bekérést).
(Ez a módszer nem helyettesíti a getline függvényt, hiába használjuk a cin.unsetf(ios::skipws); utasítást, ettől még string típusú változó értékének bekérésekor egy cin >> s; típusú utasítás csak az első szóközig tartó szöveget fogja értékül adni egy string típusú változónak).

A cin.unsetf(ios::skipws); utasítás online C++ fordítók (pl. rextester, C++ shell) esetén nem biztos, hogy megfelelően működik.

Egész osztás, valós osztás

Az egész osztás (más néven maradékos osztás) eredménye egész szám.
Például az 5 / 2 kifejezés eredménye 2
Az egész osztás maradékát a % operátorral kaphatjuk meg.
Például a 5 % 2 kifejezés eredménye 1

A valós osztás eredménye lehet tört szám is:
Például az 5.0 / 2.0 kifejezés értéke 2.5

Ha mind a két szám egész típusú (például int), akkor az osztás egész osztásként lesz értelmezve, ha legalább az egyik szám valós, akkor pedig valós osztásként. Ez kicsit becsapós, mert hiába tesszük bele az eredményt mondjuk egy double típusú változóba, ha az osztásban mindkét szám egész típusú, akkor az egész osztásként lesz értelmezve.

Ezen utasítás végrehajtását követően a div_result nevű változó értéke 2 lesz

double div_result = 5 / 2;

Ennek pedig 2,5:

A felhasználótól bekért két számmal végzett valós- és egész osztás eredménye:

#include <iostream>

int main() {
std::cout << "Kerem adjon meg ket szamot:\n";
double d_num1{1}, d_num2{1};
std::cin >> d_num1 >> d_num2;

std::cout << "valos osztas eredmenye:\n";
std::cout << d_num1 << " / " << d_num2 << " = " << d_num1/d_num2 << '\n';

int i_num1 = d_num1, i_num2 = d_num2;
std::cout << "egesz osztas eredmenye:\n";
std::cout << i_num1 << " / " << i_num2 << " = " << i_num1/i_num2 << '\n';
std::cout << "egesz osztas maradeka:\n";
std::cout << i_num1 << " % " << i_num2 << " = " << i_num1%i_num2 << '\n';
}

Előző tananyagrész: változók, konstansok, literálok
Következő tananyagrész: whitespace, layout

A bejegyzés trackback címe:

https://itkezdoknek.blog.hu/api/trackback/id/tr6616295640

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása