Velké datové typy c. Datové typy a proměnné. Jiné datové typy

Kromě rozdělení dat na proměnné a konstanty existuje klasifikace dat podle typu. Popis proměnných spočívá především v deklaraci jejich typu. Datový typ charakterizuje rozsah jeho hodnot a formu reprezentace v paměti počítače. Každý typ je charakterizován sadou operací prováděných s daty. Tradičně v univerzálních programovacích jazycích takové existují standardní typy, jako celé číslo, reálné, symbolické a logické 3. Okamžitě si toho všimněme booleovský typ ne v C. Výraz (v konkrétním případě proměnná) je považován za pravdivý, pokud je nenulový, jinak je považován za nepravdivý.

Existence dvou číselných typů (integer a real) je spojena se dvěma možnými formami reprezentace čísel v paměti počítače.

Data celý typ uloženy ve formě prezentace pevný bod. Vyznačuje se absolutní přesností při reprezentaci čísel a provádění operací s nimi a také omezeným rozsahem číselných hodnot. Celočíselný typ používá se pro data, která v zásadě nemohou mít zlomkovou část (počet osob, aut atd., čísla a počítadla).

Napište skutečné odpovídá formě reprezentace čísla s plovoucí desetinnou čárkou, který se vyznačuje přibližným znázorněním čísla s daným počtem platných číslic (znaků mantisy) a velkým rozsahem řádu čísla, což umožňuje reprezentovat jak velmi velká, tak i velmi malá čísla v absolutní hodnotě. Vzhledem k přibližné reprezentaci dat reálného typu, jejich je nesprávné porovnávat pro rovnost.

V moderních implementacích univerzálních programovacích jazyků obvykle existuje několik celočíselných a několik reálných typů, z nichž každý je charakterizován vlastní velikostí paměti alokované pro jednu hodnotu a podle toho i rozsahem číselných hodnot a pro reálné typy - a její přesnost (počet číslic mantisy).

Data typ postavy brát hodnoty na celé množině přijatelných tohoto počítače postavy. Jeden bajt je přidělen pro uložení hodnoty jednoho znaku znaky jsou kódovány podle standardní kódovací tabulky (obvykle ASCII).

V C existují 4 základní typy:

char- typ postavy;

int- celý typ,

plovák- jediný přesný skutečný typ,

dvojnásobek- skutečný typ s dvojitou přesností.

Chcete-li definovat odvozené typy, použijte kvalifikátory:krátký(krátké) - používá se s typem int,dlouho(long) - používá se s typy int A dvojnásobek;podepsaný(se znakem), nepodepsaný(bez znaménka) - použitelné pro jakýkoli typ celého čísla. Při absenci slova unsigned se hodnota považuje za podepsanou, tzn. tj. výchozí hodnota je podepsána. Vzhledem k přípustnosti libovolné kombinace kvalifikátorů a názvů základních typů může mít jeden typ více označení. Informace o standardních typech C jsou uvedeny v tabulkách 1 a 2. Deskriptory synonym jsou uvedeny v buňkách prvního sloupce oddělené čárkami.

Tabulka 1. Standardní celočíselné datové typy C

Typ dat

Rozsah hodnot

char, podepsaný char

unsigned int, unsigned

int, sign int, short int, short

2147483648...2147483647

Zajímavé je, že v C lze typ char použít jako znak nebo jako celočíselný typ, v závislosti na kontextu.

Tabulka 2. Standardní typy reálných dat C

Komentář. K napsání programů pro první část manuálu budeme potřebovat hlavně dva typy:plovákAint.

Důležitým rozdílem mezi jazykem SI a ostatními jazyky (PL1, FORTRAN atd.) je absence výchozího principu, což vede k nutnosti explicitně deklarovat všechny proměnné použité v programu spolu s uvedením jejich odpovídajících typů. .

Deklarace proměnných mají následující formát:

[memory-class-specifier] deskriptor specifikátoru typu [=iniciátor] [,deskriptor [= iniciátor] ]...

Deskriptor je identifikátor jednoduché proměnné nebo složitější konstrukce s hranatými závorkami, závorkami nebo hvězdičkou (množinou hvězdiček).

Specifikátor typu je jedno nebo více klíčových slov, které definují typ deklarované proměnné. Jazyk SI má standardní sadu datových typů, pomocí kterých můžete vytvářet nové (jedinečné) datové typy.

Iniciátor - určuje počáteční hodnotu nebo seznam počátečních hodnot, které jsou přiřazeny k proměnné při deklaraci.

Specifikátor třídy paměti – určuje se jedním ze čtyř klíčových slov SI: auto, extern, register, static a udává, jak bude na jedné straně alokována paměť pro deklarovanou proměnnou, a na druhé straně rozsah této proměnné, tj. ze kterých částí programu je přístupný.

1.2.1 Kategorie datových typů

Klíčová slova pro definování základních datových typů

Typy celých čísel: Typy plovoucí: char float int double short long double long signed unsigned

Proměnná libovolného typu může být prohlášena za nemodifikovatelnou. Toho je dosaženo přidáním klíčového slova const do specifikátoru typu. Objekty s typem const představují data pouze pro čtení, tzn. této proměnné nelze přiřadit novou hodnotu. Všimněte si, že pokud za slovem const není žádný typový specifikátor, pak je implikován typový specifikátor int. Pokud se klíčové slovo const dostane před deklaraci kompozitních typů (pole, struktura, směs, enumerace), pak to vede k tomu, že každý prvek musí být také nemodifikovatelný, tzn. hodnotu lze přiřadit pouze jednou.

Const double A=2,128E-2;

konst B = 286; (předpokládá se const int B=286)

Příklady deklarování složených dat budou diskutovány níže.

1.2.2. Integer datový typ

Pro definování dat celočíselného typu se používají různá klíčová slova, která určují rozsah hodnot a velikost paměťové oblasti alokované pro proměnné (tabulka 6).

Tabulka 6 Všimněte si, že podepsaná a nepodepsaná klíčová slova jsou volitelná. Označují, jak je interpretován nulový bit deklarované proměnné, tj. pokud je specifikováno klíčové slovo bez znaménka, pak je nulový bit interpretován jako část čísla, jinak je nulový bit interpretován jako se znaménkem. Pokud klíčové slovo unsigned chybí, je celočíselná proměnná považována za podepsanou. Pokud se specifikátor typu skládá z typ klíče

podepsané nebo nepodepsané následované identifikátorem proměnné, bude považováno za proměnnou typu int. Například:

Nesignováno int n;

unsigned int b; int c; (podepsané int c je implikováno);. Takže na 16bitových strojích je velikost slova 2 bajty, na 32bitových strojích jsou 4 bajty, tzn. typ int je ekvivalentní typům short int nebo long int, v závislosti na architektuře použitého PC. Stejný program tedy může na jednom počítači fungovat správně a na jiném nesprávně. Pro určení délky paměti obsazené proměnnou můžete použít operátor sizeof jazyka SI, který vrací délku zadaného modifikátoru typu.

Například:

A = sizeof(int);

b = sizeof(long int);

Například:

c = sizeof(dlouhé bez znaménka);

d = sizeof(short);

Všimněte si také, že osmičkové a hexadecimální konstanty mohou mít také modifikátor bez znaménka. Toho je dosaženo uvedením předpony u nebo U za konstantou, konstanta bez této předpony je považována za podepsanou.

0xA8C (int signováno);

01786l (dlouho signováno);

0xF7u (int unsigned); 1.2.3. Plovoucí data;

Pro proměnné představující číslo s pohyblivou řádovou čárkou se používají následující modifikátory typu: float, double, long double (v některých implementacích jazyka long double není SI).

Hodnota s modifikátorem typu float zabírá 4 bajty. Z nich je 1 bajt přidělen pro znaménko, 8 bitů pro nadbytečný exponent a 23 bitů pro mantisu. Všimněte si, že nejvýznamnější bit mantisy je vždy 1, takže není vyplněn, takže rozsah hodnot pro proměnnou s plovoucí desetinnou čárkou je přibližně 3,14E-38 až 3,14E+38.

Dvojitá hodnota zabírá 8 bitů paměti. Jeho formát je podobný formátu float. Paměťové bity jsou rozděleny následovně: 1 bit pro znaménko, 11 bitů pro exponent a 52 bitů pro mantisu. S přihlédnutím k vynechanému vysokému bitu mantisy je rozsah hodnot od 1,7E-308 do 1,7E+308.

Typový specifikátor specifikuje typ objektu a může to být jakýkoli základní typ, typ struktury, směs (o tom bude řeč níže). Zadáním klíčového slova void místo specifikátoru typu můžete jedinečným způsobem odložit specifikaci typu, na který se ukazatel odkazuje. Proměnná deklarovaná jako ukazatel na typ void může být použita k odkazování na objekt jakéhokoli typu. Aby však bylo možné provádět aritmetické a logické operace s ukazateli nebo s objekty, na které ukazují, je nutné při provádění každé operace explicitně určit typ objektů. Takové definice typu lze provést pomocí operace odlévání typu.

Klíčová slova const, near, far, huge lze použít jako modifikátory při deklaraci ukazatele. Klíčové slovo const označuje, že ukazatel nelze v programu upravit. Velikost proměnné deklarované jako ukazatel závisí na architektuře počítače a na použitém paměťovém modelu, pro který bude program kompilován. Ukazatele na různé datové typy nemusí mít stejnou délku.

Chcete-li upravit velikost ukazatele, můžete použít klíčová slova blízko, daleko, obrovský.

Nesignováno int * a; /* proměnná a je ukazatel na typ unsigned int */ double * x; /* proměnná x označuje datový typ s plovoucí desetinnou čárkou s dvojitou přesností */ char * fuffer ; /* deklaruje ukazatel s názvem fuffer, který ukazuje na proměnnou typu char */ double nomer; void *adresy;, ale hodnota, na kterou ukazuje, není. */ unsigned char * const w = &obj.

/* Proměnná w je deklarována jako konstantní ukazatel na data bez znaménka.

To znamená, že w bude ukazovat na stejné paměťové místo v celém programu. Obsah této oblasti se může změnit. */

1.2.5. Výčtové proměnné

Proměnná, která může nabývat hodnoty z nějakého seznamu hodnot, se nazývá výčtová proměnná nebo výčet.

Deklarace výčtu začíná klíčovým slovem enum a má dva formáty prezentace.

Formát 1. enum [název-výčtového-tagu] (výčtový-seznam) deskriptor[,deskriptor...]; Format 2. enum enum-tag-name deskriptor [,descriptor..]; Deklarace výčtu určuje typ

proměnná enum

a definuje seznam pojmenovaných konstant, nazývaných výčtový seznam. Hodnota každého názvu seznamu je nějaké celé číslo. Proměnná typu výčtu může mít hodnotu jedné z pojmenovaných konstant seznamu. Konstanty pojmenovaného seznamu jsou typu int. Paměť odpovídající výčtové proměnné je tedy pamětí potřebnou k uložení hodnoty int. Proměnné typu enum lze použít ve výrazech indexu a jako operandy v

aritmetické operace

a ve relačních operacích.

V prvním formátu 1 jsou názvy a hodnoty výčtů specifikovány v seznamu výčtů. Volitelný název-příznaku výčtu je identifikátor, který pojmenovává příznak výčtu definovaný seznamem výčtů. Deskriptor pojmenovává proměnnou výčtu. V deklaraci lze zadat více než jednu proměnnou typu výčtu.

Výčtový seznam obsahuje jednu nebo více konstrukcí formuláře:

identifikátor [= konstantní výraz]

1. Proměnná může obsahovat duplicitní hodnoty.

2. Identifikátory ve výčtovém seznamu musí být odlišné od všech ostatních identifikátorů ve stejném rozsahu, včetně jmen regulárních proměnných a identifikátorů z jiných výčtových seznamů.

3. Názvy výčtových typů musí být odlišné od ostatních výčtových typů, struktur a směsí ve stejném rozsahu.

4. Hodnota může následovat za posledním prvkem výčtového seznamu.

Enum week ( SUB = 0, /* 0 */ VOS = 0, /* 0 */ POND, /* 1 */ VTOR, /* 2 */ SRED, /* 3 */ HETV, /* 4 */ PJAT /* 5 */ ) rab_ned ;

V v tomto příkladu je deklarován vyčíslitelný tag week s odpovídající sadou hodnot a je deklarována proměnná rab_ned typu week.

Druhý formát používá název značky enum k odkazování na typ výčtu definovaný někde jinde. Název výčtové značky musí odkazovat na již definovanou výčtovou značku v aktuálním oboru. Protože značka výčtu je deklarována někde jinde, seznam výčtů není v deklaraci zastoupen.

Při deklarování ukazatele na datový typ výčtu a deklarování definice typu pro výčtové typy můžete použít název výčtové značky předtím, než je tato výčtová značka definována. Definice výčtu však musí předcházet jakémukoli použití ukazatele deklarace typedef na typ. Deklarace bez následného seznamu deskriptorů popisuje značku nebo, abych tak řekl, vzor výčtu.

1.2.6. Pole

Pole jsou skupinou prvků stejného typu (double, float, int atd.). Z deklarace pole musí překladač získat informace o typu prvků pole a jejich počtu. Deklarace pole má dva formáty:

deskriptor typového specifikátoru [konst - výraz];

deskriptor typového specifikátoru ;

Deskriptor je identifikátor pole.

Specifikátor typu udává typ prvků deklarovaného pole. Prvky pole nemohou být funkcemi ani prvky void.

Konstantní výraz v hranatých závorkách určuje počet prvků v poli. Při deklaraci pole lze konstantní výraz vynechat v následujících případech:

Po deklaraci se pole inicializuje,

Pole je deklarováno jako formální parametr funkce,

SI definuje pouze jednorozměrná pole, ale protože prvkem pole může být pole, lze definovat i vícerozměrná pole. Jsou formalizovány seznamem konstantních výrazů za identifikátorem pole, přičemž každý konstantní výraz je uzavřen ve vlastních hranatých závorkách.

Každý konstantní výraz v hranatých závorkách určuje počet prvků podél daného rozměru pole, takže deklarace dvourozměrného pole obsahuje dva konstantní výrazy, trojrozměrné pole obsahuje tři atd. Všimněte si, že v SI má první prvek pole index 0.

Int a; /* znázorněno jako matice a a a a a a */ double b; /* vektor 10 prvků typu double */ int w = ( ( 2, 3, 4 ), ( 3, 4, 8 ), ( 1, 0, 9 ) );

V posledním příkladu je deklarováno pole w. Seznamy uzavřené ve složených závorkách odpovídají řetězcům pole, pokud žádné složené závorky nejsou, inicializace nebude provedena správně.

V SI můžete používat pole slice, stejně jako v jiných jazycích vysoká úroveň(PL1 apod.), na používání úseků je však uvalena řada omezení. Sekce se tvoří vynecháním jedné nebo více dvojic hranatých závorek. Dvojice hranatých závorek lze upustit pouze zprava doleva a přísně sekvenčně. Sekce polí se používají k organizaci výpočetního procesu ve funkcích SI vyvinutých uživatelem.

Pokud při volání funkce napíšete s, bude předán nulový řetězec pole s.

Při přístupu k poli b můžete napsat například b a vektor čtyř prvků se přenese a přístup k b poskytne dvourozměrné pole o velikosti 3 x 4. Nemůžete napsat b, což znamená, že vektor bude převést, protože to není v souladu s omezením uloženým na sekce pole použití.

Příklad deklarace znakového pole.

char str = "deklarace pole znaků";

Všimněte si, že ve znakovém literálu je ještě jeden prvek, protože posledním prvkem je escape sekvence "\0".

1.2.7. Struktury

Struktury jsou složený objekt, který obsahuje prvky libovolného typu, s výjimkou funkcí. Na rozdíl od pole, které je homogenním objektem, může být struktura heterogenní. Typ struktury je určen zadáním formuláře:

struct (seznam definic)

Ve struktuře musí být uvedena alespoň jedna komponenta. Definice struktur je následující:

deskriptor datového typu;

kde datový typ určuje typ struktury pro objekty definované v deskriptorech. Ve své nejjednodušší podobě jsou úchyty identifikátory nebo pole.

Struktura ( double x,y; ) s1, s2, sm;

struct (int rok; char moth, den; ) datum1, datum2;

Proměnné s1, s2 jsou definovány jako struktury, z nichž každá se skládá ze dvou složek x a y. Proměnná sm je definována jako pole devíti struktur. Každá ze dvou proměnných date1, date2 se skládá ze tří složek rok, měsíc, den. >p>Existuje další způsob, jak spojit jméno s typem struktury, je založen na použití tagu struktury. Značka struktury je podobná značce typu enum. Značka struktury je definována takto:

struct tag (seznam popisů; );

kde tag je identifikátor.

Níže uvedený příklad popisuje identifikátor studenta jako značku struktury:

Struct student ( char name; int id, age; char prp; );

Tag structure se používá k následné deklaraci struktur tohoto typu ve tvaru:

struct id-list tag;

struct studeut st1,st2;

Použití strukturních značek je nezbytné pro popis rekurzivních struktur. Následující pojednává o použití značek rekurzivní struktury.

Struct node ( int data; struct node * next; ) st1_node;

Uzel značky struktury je skutečně rekurzivní, protože se používá ve svém vlastním popisu, tj. při formalizaci dalšího ukazatele. Struktury nemohou být přímo rekurzivní, tzn. struktura uzlu nemůže obsahovat komponentu, která je strukturou uzlů, ale jakákoli struktura může mít komponentu, která je ukazatelem na její typ, jak je uvedeno v příkladu výše.

Ke komponentám struktury se přistupuje zadáním názvu struktury a následujícímu, odděleného tečkou, názvu vybrané komponenty, například:

St1.name="Ivanov";

st2.id=st1.id;

st1_node.data=st1.age;

1.2.8. Kombinace (směsi)

Ke členům odborů se přistupuje stejným způsobem jako ke strukturám. Union tag lze formalizovat přesně stejným způsobem jako tag structure.

Sdružení se používá k následujícím účelům:

Inicializace používaného paměťového objektu, pokud je v daném okamžiku aktivní pouze jeden z mnoha objektů;

Interpretujte základní reprezentaci objektu jednoho typu, jako by tomuto objektu byl přiřazen jiný typ.

Paměť, která odpovídá proměnné typu sjednocení, je určena množstvím potřebným k umístění nejdelšího prvku sjednocení. Při použití kratšího prvku může proměnná typu sjednocení obsahovat nevyužitou paměť. Všechny prvky sjednocení jsou uloženy ve stejné oblasti paměti počínaje stejnou adresou.

Union (char fio; char adresy; int vozrast; int telefon; ) informovat;

unie ( int ax; char al; ) ua;

Při použití objektu infor typu union můžete zpracovat pouze prvek, který hodnotu obdržel, tzn. Po přiřazení hodnoty prvku inform.fio nemá smysl přistupovat k dalším prvkům. Zřetězení ua umožňuje samostatný přístup k nízkým ua.al a vysokým ua.al bytům dvoubajtového čísla ua.ax .

1.2.9. Bitová pole Prvek struktury může být bitové pole, které poskytuje přístup k jednotlivým bitům paměti. Bitová pole nelze deklarovat mimo struktury. Také nemůžete uspořádat pole bitových polí a nemůžete na pole použít operaci určení adresy. V obecný případ

typ struktury s bitovým polem je specifikován takto:

Struktura ( identifikátor bez znaménka 1: délka pole 1; identifikátor bez znaménka 2: délka pole 2; )

délka - pole jsou specifikována celočíselným výrazem nebo konstantou. Tato konstanta udává počet bitů přidělených odpovídajícímu poli. Pole s nulovou délkou označuje zarovnání k další hranici slova.

Struktura ( bez znaménka a1: 1; bez znaménka a2: 2; bez znaménka a3: 5; bez znaménka a4: 2; ) prim;

Struktury bitových polí mohou také obsahovat podepsané komponenty. Takové komponenty jsou automaticky umístěny na vhodných hranicích slov a některé bity slov mohou zůstat nevyužity.

Velmi často některé objekty programu patří do stejné třídy, liší se pouze v některých detailech. Vezměme si například znázornění geometrických tvarů. Obecné informace o tvarech mohou zahrnovat prvky, jako je plocha, obvod. Odpovídající informace o geometrických rozměrech se však mohou lišit v závislosti na jejich tvaru.

Zvažte příklad, ve kterém jsou informace o geometrických tvarech reprezentovány na základě kombinovaného použití struktury a spojení.

Strukturní obrazec ( double area,perimetr; /* společné komponenty */ typ int; /* atribut komponenty */ union /* výčet komponent */ ( dvojitý poloměr; /* kruh */ double a; /* obdélník */ double b ; /* trojúhelník */ ) geom_obr ) obr1, obr2;

Obecně se každý objekt obrázku bude skládat ze tří složek: plocha, obvod, typ. Komponenta typu se nazývá štítek aktivní komponenty, protože se používá k označení, která komponenta spojení geom_fig je aktivní v momentálně. Této struktuře se říká variabilní struktura, protože její složky se mění v závislosti na hodnotě štítku aktivní složka(hodnota typu).

Všimněte si, že místo použití komponenty typu int by bylo vhodné použít výčtový typ. Například takto

Enum figurka_šachy ( KRUH, KRABICE, TROJÚHELNÍK ) ;

Konstanty CIRCLE, BOX, TRIANGLE obdrží hodnoty rovné 0, 1, 2, resp.

vyjmenovat typ figurky;

V tomto případě kompilátor SI upozorní programátora na potenciálně chybné přiřazení, jako je např

obrázek.typ = 40;

Obecně se strukturní proměnná bude skládat ze tří částí: sady společných komponent, štítku pro aktivní komponentu a části s měnícími se komponentami. Obecná forma proměnné struktury je následující:

Struktura (společné komponenty; štítek aktivní komponenty; sjednocení (popis komponenty 1; popis komponenty 2; ::: popis komponenty n; ) union-identifier; ) structure-identifier;

Příklad definování strukturní proměnné s názvem helth_record

Struktura ( /* obecné informace*/ název znaku; /* jméno */ int věk; /* věk */ char pohlaví; /* pohlaví */ /* označení aktivní složky */ /* ( rodinný stav) */ enum merital_status ins;

/* proměnná část */ union ( /* single */ /* žádná komponenta */ struct ( /* ženatý */ char datum_marripge; char jmeno_spouse; int no_children; ) svatba_info; /* rozvedena */ char datum_rozvodu; ) marital_info ;

) zdravotní_záznam;

enum manželský_stav ( SVEDLA, /* svobodná */ MARRIGO, /* vdaná */ ROZVODENÁ /* rozvedená */ ) ;

Ke komponentám struktury se dostanete pomocí odkazů:

Helth_record.neme, helth_record.ins, helth_record.marriage_info.marriage_date .

1.2.11. Definování objektů a typů

Jak bylo uvedeno výše, všechny proměnné používané v programech SI musí být deklarovány. Typ deklarované proměnné závisí na tom, které klíčové slovo je použito jako specifikátor typu a zda je specifikátorem jednoduchý identifikátor nebo kombinace identifikátoru s ukazatelem (hvězdička), polem (hranaté závorky) nebo modifikátorem funkce (závorky).

Při deklaraci jednoduché proměnné, struktury, směsi nebo unie nebo výčtu je handle jednoduchým identifikátorem. Chcete-li deklarovat ukazatel, pole nebo funkci, identifikátor se odpovídajícím způsobem upraví: vlevo hvězdička, vpravo čtverec nebo závorky.

Všimněme si důležité vlastnosti jazyka SI: při deklaraci lze použít více modifikátorů současně, což umožňuje vytvářet mnoho různých komplexních typových deskriptorů.

Musíme si však pamatovat, že některé kombinace modifikátorů jsou nepřijatelné:

Prvky pole nemohou být funkcemi.

Funkce nemohou vracet pole nebo funkce.

Při inicializaci komplexních deskriptorů mají hranaté závorky a závorky (napravo od identifikátoru) přednost před hvězdičkou (vlevo od identifikátoru). Čtverec nebo závorky mají stejnou prioritu a jsou rozbaleny zleva doprava. Specifikátor typu je zvažován v posledním kroku, kdy již byl specifikátor plně interpretován. Pomocí závorek můžete změnit pořadí výkladu podle potřeby.

3. Pokud se v jakékoli fázi na pravé straně objeví uzavírací závorka, pak musí být všechna tato pravidla nejprve aplikována uvnitř závorky a poté musí výklad pokračovat.

4. Interpretujte specifikátor typu.

Int * (* comp ) ();

6 5 3 1 2 4

V tomto příkladu je proměnná comp (1) deklarována jako pole deseti (2) ukazatelů (3) na funkce (4), které vracejí ukazatele (5) na celočíselné hodnoty (6).

Char * (* (*var) ());

7 6 4 2 1 3 5

Proměnná var (1) je deklarována jako ukazatel (2) na funkci (3), která vrací ukazatel (4) na pole (5) s 10 prvky, které jsou ukazateli (6) na hodnoty znaků. Kromě deklarace proměnných různých typů je možné deklarovat i typy. To lze provést dvěma způsoby. Prvním způsobem je zadat název tagu při deklaraci struktury, sjednocení nebo výčtu a pak tento název použít v deklaracích proměnných a funkcí jako odkaz na tento tag. Druhým je použití klíčového slova typedef k deklaraci typu. Při oznámení od

klíčové slovo

typedef, identifikátor stojící na místě popisovaného objektu, je název datového typu, který byl uvažován, a tento typ pak lze použít k deklaraci proměnných.

Všimněte si, že pomocí klíčového slova typedef lze deklarovat jakýkoli typ, včetně typů ukazatel, funkce nebo pole. Název s klíčovým slovem typedef pro typy ukazatel, struktura a sjednocení lze deklarovat před definováním těchto typů, ale v rozsahu deklarátoru.

Typedef double(*MATH)();

/* MATH - nový název typu představující ukazatel na funkci, která vrací dvojité hodnoty*/ MATH cos;

Abstract-handler je neidentifikátorový handle sestávající z jednoho nebo více ukazatelů, polí nebo modifikátorů funkce. Modifikátor ukazatele (*) je vždy uveden před identifikátorem v deskriptoru a modifikátory pole a funkce () jsou vždy uvedeny za ním. Pro správnou interpretaci abstraktního deskriptoru je tedy nutné začít interpretaci implikovaným identifikátorem.

Abstraktní deskriptory mohou být složité. Závorky u komplexních abstraktních deskriptorů specifikují pořadí interpretace podobně jako interpretace komplexních deskriptorů v deklaracích.

1.2.12. Inicializace dat

Když je proměnná deklarována, lze jí přiřadit počáteční hodnotu připojením iniciátoru k deklarátoru. Iniciátor začíná znakem "=" a má následující tvary.

Formát 1: = iniciátor;

Formát 2: = (seznam - iniciátory);

Formát 1 se používá při inicializaci proměnných základních typů a ukazatelů a formát 2 se používá při inicializaci složených objektů.

Proměnná tol je inicializována znakem "N".

const long megabute = (1024 * 1024);

Nemodifikovatelná proměnná megabute je inicializována konstantním výrazem, po kterém ji nelze změnit.

static int b = (1,2,3,4);

Je inicializováno dvourozměrné pole b celých čísel; prvkům pole jsou přiřazeny hodnoty ze seznamu. Stejnou inicializaci lze provést takto:

static int b = ( ( 1,2 ), ( 3, 4 ) );

Při inicializaci pole můžete vynechat jednu nebo více dimenzí

static int b == "."")
str[ ix ] = "_";

To je vše, co jsme teď chtěli říct o smyčcové třídě. Ve skutečnosti má tato třída mnohem zajímavější vlastnosti a schopnosti. Řekněme, že předchozí příklad je také implementován voláním jediné funkce replace():

Replace(str.begin(), str.end(), ".", "_");

nahradit() je jedním z obecných algoritmů, které jsme představili v sekci 2.8 a které budou podrobně popsány v kapitole 12. Tato funkce prochází rozsahem od begin() do end(), které vrací ukazatele na začátek a konec řetězec a nahradí prvky , které se rovnají jeho třetímu parametru, čtvrtým.

Cvičení 3.12

Najděte chyby v následujících prohlášeních:

(a) char ch = "Dlouhá a klikatá cesta"; (b) int ival = (c) char *pc = (d) string st(&ch); (e) pc = 0; (i) pc = "0";
(f) st = pc; (j) st =
(g) ch = pc; (k) ch = *pc;
(h) pc = st; (l) *pc = ival;

Cvičení 3.13

Vysvětlete rozdíl v chování následujících příkazů cyklu:

Zatímco (st++) ++cnt;
zatímco (*st++)
++cnt;

Cvičení 3.14

Jsou uvedeny dva sémanticky ekvivalentní programy. První používá vestavěný typ řetězce, druhý používá třídu string:

// ***** Implementace pomocí C řetězců ***** #include #zahrnout
int main()
{
int chyby = 0;
const char *pc = "velmi dlouhý doslovný řetězec";< 1000000; ++ix)
{
for (int ix = 0; ix
int len ​​​​= strlen(pc);
char *pc2 = nový char[ len + 1 ];
strcpy(pc2, pc);
if (strcmp(pc2, pc))
}
++chyby;<< "C-строки: "
<< errors << " ошибок.\n";
smazat pc2;
cout
int main()
{
int chyby = 0;
) // ***** Implementace pomocí třídy string ***** #include< 1000000; ++ix)
{
#zahrnout
string str("velmi dlouhý doslovný řetězec");
for (int ix = 0; ix
}
++chyby;<< "класс string: "
<< errors << " ошибок.\n;
}

int len ​​​​= str.size();
řetězec str2 = str;

if (str != str2)

Co tyto programy dělají?

Ukazuje se, že druhá implementace běží dvakrát rychleji než první. Čekal jste takový výsledek? jak to vysvětlíš?

Cvičení 3.15

Mohli byste něco zlepšit nebo přidat do sady operací třídy string uvedené v poslední části? Vysvětlete své návrhy< 512; ++index) ... ;

3.5. konst specifikátor
Vezměme si následující příklad kódu:
Pro (int index = 0; index

Při používání literálu 512 jsou dva problémy. První je snadnost vnímání textu programu. Proč by horní mez proměnné smyčky měla být přesně 512? Co se za touto hodnotou skrývá? Vypadá náhodně...< bufSize

Druhý problém se týká snadnosti úprav a údržby kódu. Řekněme, že program má 10 000 řádků a doslovných 512 se objeví ve 4 % z nich. Řekněme, že v 80 % případů se musí číslo 512 změnit na 1024. Dokážete si představit složitost takové práce a množství chyb, kterých se lze dopustit opravou špatné hodnoty? Řešíme oba tyto problémy současně: potřebujeme vytvořit objekt s hodnotou 512. Když mu dáme smysluplný název, například bufSize, uděláme program mnohem srozumitelnějším: je jasné, co přesně je proměnná cyklu ve srovnání s.

Index< bufSize; ++index)
// ...

V tomto případě změna velikosti bufSize nevyžaduje procházení 400 řádků kódu k úpravě 320 z nich. Jak moc se sníží pravděpodobnost chyb přidáním pouze jednoho objektu! Nyní je hodnota 512

lokalizované

Spuštění tohoto kódu způsobí, že hodnota bufSize bude 1, což může vést ke zcela nepředvídatelnému chování programu. Chyby tohoto druhu se většinou velmi obtížně odhalují, protože prostě nejsou vidět.
Tento problém řeší použití specifikátoru const. Prohlášením objektu jako

Const int bufSize = 512; // velikost vstupní vyrovnávací paměti

změníme proměnnou na konstantu s hodnotou 512, jejíž hodnotu nelze změnit: takové pokusy kompilátor potlačí: nesprávné použití operátoru přiřazení místo porovnání, jako v příkladu výše, způsobí chybu při kompilaci.

// chyba: pokus o přiřazení hodnoty konstantě if (bufSize = 0) ...

Protože konstantě nelze přiřadit hodnotu, musí být inicializována na místě, kde je definována. Definování konstanty bez její inicializace také způsobí chybu kompilace:

Const double pí; // chyba: neinicializovaná konstanta

Const double min.Mzda = 9,60; // Správně? chyba?
double *ptr =

Měl by kompilátor takové zadání povolit? Vzhledem k tomu, že minWage je konstanta, nelze jí přiřadit hodnotu. Na druhou stranu nám nic nebrání napsat:

*ptr+= 1,40; // změna objektu minWage!

Překladač zpravidla není schopen chránit před použitím ukazatelů a nebude schopen signalizovat chybu, pokud budou použity tímto způsobem. To vyžaduje příliš hlubokou analýzu logiky programu. Proto kompilátor jednoduše zakáže přidělování konstantních adres běžným ukazatelům.
Jsme tedy ochuzeni o možnost používat ukazatele na konstanty? Žádný. Pro tento účel existují ukazatele deklarované se specifikátorem const:

Const double *cptr;

kde cptr je ukazatel na objekt typu const double. Jemnost spočívá v tom, že samotný ukazatel není konstanta, což znamená, že můžeme změnit jeho hodnotu.

Například:
Const double *pc = 0; const double minMage = 9,60; // správně: nemůžeme změnit minWage pomocí pc
pc = double dval = 3,14; // správně: nemůžeme změnit minWage pomocí pc
// ačkoli dval není konstanta
pc = // správně dval = 3,14159; //Právo

*pc = 3,14159; // chyba

Adresa konstantního objektu je přiřazena pouze ukazateli na konstantu. Zároveň lze takovému ukazateli přiřadit adresu běžné proměnné:

Konstantní ukazatel neumožňuje upravit objekt, který adresuje, pomocí nepřímého adresování. Ačkoli dval ve výše uvedeném příkladu není konstanta, kompilátor nedovolí změnit dval přes pc. (Opět, protože není schopen určit, která adresa objektu může obsahovat ukazatel kdykoli během provádění programu.)
V reálných programech se jako formální parametry funkcí nejčastěji používají ukazatele na konstanty. Jejich použití zajišťuje, že objekt předaný funkci jako skutečný argument nebude touto funkcí modifikován. Například:

// V reálných programech se jako formální parametry funkcí // nejčastěji používají ukazatele na konstanty int strcmp(const char *str1, const char *str2);

(O konstantních ukazatelích si povíme více v kapitole 7, když mluvíme o funkcích.)
Existují také konstantní ukazatele. (Všimněte si rozdílu mezi ukazatelem const a ukazatelem na konstantu!). Konstantní ukazatel může adresovat konstantu nebo proměnnou. Například:

Int errNumb = 0; int *const currErr =

CurErr je zde ukazatel const na non-const objekt. To znamená, že mu nemůžeme přiřadit adresu jiného objektu, i když samotný objekt lze upravit.

Zde je návod, jak lze použít ukazatel curErr:
Udělej_něco(); if (*curErr) (
errorHandler();
}

*curErr = 0; // správně: resetujte hodnotu errNumb

Pokus o přiřazení hodnoty k ukazateli const způsobí chybu kompilace:

CurErr = // chyba

Konstantní ukazatel na konstantu je spojením dvou uvažovaných případů.

Const double pi = 3,14159; const double *const pi_ptr = π

V programu nelze změnit hodnotu objektu, na který ukazuje pi_ptr, ani hodnotu samotného ukazatele.

Cvičení 3.16

Vysvětlete význam následujících pěti definic. Je některý z nich špatný?

(a) int i; (d) int *const cpi; (b) const int ic; (e) const int *const cpic; (c) const int *pic;

Cvičení 3.17

Které z následujících definic jsou správné? Proč?

(a) int i = -1; (b) const int ic = i; (c) const int *pic = (d) int *const cpi = (e) const int *const cpic =

Cvičení 3.18

Pomocí definic z předchozího cvičení identifikujte správné operátory přiřazení. Vysvětlit.

(a) i = ic; (d) pic = cpic; (b) pic = (i) cpic = (c) cpi = pic; (f) ic = *cpic;

Referenční typ, někdy nazývaný alias, se používá k přidělení dalšího názvu objektu. Odkaz vám umožňuje nepřímo manipulovat s objektem, stejně jako pomocí ukazatele. Tato nepřímá manipulace však nevyžaduje speciální syntaxi požadovanou pro ukazatele. Obvykle se jako formální parametry funkcí používají odkazy. V této části se podíváme na samostatné použití objektů typu reference.
Typ odkazu je indikován uvedením operátoru adresy (&) před názvem proměnné. Odkaz musí být inicializován. Například:

Int ival = 1024; // správně: refVal - odkaz na ival int &refVal = ival; // chyba: odkaz musí být inicializován na int

Int ival = 1024; // chyba: refVal je typu int, ne int* int &refVal = int *pi = // správně: ptrVal je odkaz na ukazatel int *&ptrVal2 = pi;

Jakmile je odkaz definován, nemůžete jej změnit, aby fungoval s jiným objektem (proto musí být odkaz inicializován v bodě, kde je definován). V následujícím příkladu operátor přiřazení nemění hodnotu refVal, nová hodnota je přiřazena proměnné ival - té, která adresa refVal.

Int min_val = 0; // ival obdrží hodnotu min_val, // místo refVal změní hodnotu na min_val refVal = min_val;

RefVal += 2; přidá 2 k ival, proměnné, na kterou odkazuje refVal. Podobně int ii = refVal; přiřadí ii aktuální hodnotu ival, int *pi = inicializuje pi s adresou ival.

// jsou definovány dva objekty typu int int ival = 1024, ival2 = 2048; // definována jedna reference a jeden objekt int &rval = ival, rval2 = ival2; // je definován jeden objekt, jeden ukazatel a jedna reference
int inal3 = 1024, *pi = ival3, &ri = ival3; // jsou definovány dva odkazy int &rval3 = ival3, &rval4 = ival2;

Odkaz const může být inicializován objektem jiného typu (samozřejmě za předpokladu, že je možné převést jeden typ na jiný), stejně jako bezadresnou hodnotou, jako je doslovná konstanta. Například:

Double dval = 3,14159; // true pouze pro konstantní odkazy
const int &ir = 1024;
const int &ir2 = dval;
const double &dr = dval + 1,0;

Pokud bychom specifikátor const nezadali, všechny tři referenční definice by generovaly chybu kompilace. Důvod, proč kompilátor takové definice neprojde, je však nejasný. Zkusme na to přijít.
U literálů je to víceméně jasné: neměli bychom mít možnost nepřímo měnit hodnotu literálu pomocí ukazatelů nebo odkazů. Co se týče objektů jiných typů, překladač převede původní objekt na nějaký pomocný objekt. Pokud například napíšeme:

Double dval = 1024; const int &ri = dval;

pak to kompilátor převede na něco takového:

Int temp = dval; const int &ri = temp;

Pokud bychom mohli přiřadit novou hodnotu ri referenci, ve skutečnosti bychom nezměnili dval, ale temp. Hodnota dval by zůstala stejná, což je pro programátora naprosto samozřejmé. Proto kompilátor takové akce zakazuje a jediný způsob, jak inicializovat odkaz s objektem jiného typu, je deklarovat jej jako const.
Zde je další příklad odkazu, který je napoprvé obtížně pochopitelný. Chceme definovat odkaz na adresu konstantního objektu, ale naše první možnost způsobí chybu kompilace:

Const int ival = 1024; // chyba: je potřeba konstantní odkaz
int *&pi_ref =

Pokus o nápravu přidáním specifikátoru const také selže:

Const int ival = 1024; // stále chyba const int *&pi_ref =

jaký je důvod? Pokud si pozorně přečteme definici, uvidíme, že pi_ref je odkaz na konstantní ukazatel na objekt typu int. A potřebujeme nekonstantní ukazatel na konstantní objekt, takže následující záznam by byl správný:

Const int ival = 1024; // Správně
int *const &piref =

Mezi odkazem a ukazatelem jsou dva hlavní rozdíly. Nejprve musí být odkaz inicializován v místě, kde je definován. Za druhé, jakákoli změna odkazu netransformuje odkaz, ale objekt, na který odkazuje.

Podívejme se na příklady. Pokud napíšeme:

Int *pi = 0;

ukazatel pi inicializujeme na nulu, což znamená, že pi neukazuje na žádný objekt. Zároveň nahrávání
const int &ri = 0;
znamená něco takového:
int temp = 0;

const int &ri = temp;

Pokud jde o operaci přiřazení, v následujícím příkladu:

Int ival = 1024, ival2 = 2048; int *pi = &ival, *pi2 = pi = pi2;
proměnná ival, na kterou ukazuje pi, zůstane nezměněna a pi obdrží hodnotu adresy proměnné ival2. Pi i pi2 nyní ukazují na stejný objekt, ival2.

Pokud pracujeme s odkazy:

Int &ri = ival, &ri2 = ival2; ri = ri2;
// příklad použití odkazů // Hodnota je vrácena v parametru next_value

Int ival; while (get_next_value(ival)) ...

Int &next_value = ival;

(Použití odkazů jako formálních parametrů funkcí je podrobněji rozebráno v kapitole 7.)

Cvičení 3.19

Jsou v těchto definicích nějaké chyby? Vysvětlit. Jak byste je opravil?

(a) int ival = 1,01; (b) int &rvall = 1,01; (c) int &rval2 = ival; (d) int &rval3 = (e) int *pi = (f) int &rval4 = pi; (g) int &rval5 = pi*; (h) int &*prval1 = pi; (i) const int &ival2 = 1; (j) const int &*prval2 =

Cvičení 3.20

Jsou některé z následujících operátorů přiřazení chybné (s použitím definic z předchozího cvičení)?

(a) rval1 = 3,14159; (b) prval1 = prval2; (c) prval2 = rval1; (d) *prval2 = ival2;

Cvičení 3.21

Najděte chyby v uvedených pokynech:

(a) int ival = 0;
const int *pi = 0;
const int &ri = 0; (b)pi =

ri =

pí =

3.7. Zadejte bool
Objekt typu bool může nabývat jedné ze dvou hodnot: true a false. Například:
// inicializace řetězce string search_word = get_word(); // inicializace nalezené proměnné
bool nalezen = false; řetězec další_slovo; while (cin >> next_word)
if (další_slovo == hledané_slovo)
nalezeno = pravda;
++chyby;<< "ok, мы нашли слово\n";
// ... // zkratka: if (found == true)<< "нет, наше слово не встретилось.\n";

pokud (nalezen)

jinak cout

Přestože bool je jedním z celočíselných typů, nelze jej deklarovat jako podepsaný, nepodepsaný, krátký nebo dlouhý, takže výše uvedená definice je nesprávná:

// nalezena chyba short bool = false;
{
Objekty typu bool jsou implicitně převedeny na typ int. True se stane 1 a false se stane 0. Například:
Bool nalezen = false; int počet_výskytů = 0; zatímco (/* mumlá */)

nalezeno = hledat (/* něco */);

// nalezená hodnota se převede na 0 nebo 1
počet_výskytů += nalezeno; )

Stejným způsobem lze hodnoty celočíselných typů a ukazatelů převést na hodnoty typu bool. V tomto případě je 0 interpretováno jako nepravda a vše ostatní jako pravdivé:

// vrátí počet výskytů extern int find(const string&); bool nalezen = false; if (found = find("rosebud")) // správně: found == true // vrací ukazatel na prvek
extern int* find(int hodnota); if (found = find(1024)) // správně: found == true

3.8. Převody

Často musíte definovat proměnnou, která přebírá hodnoty z určité množiny. Řekněme, že soubor je otevřen v kterémkoli ze tří režimů: pro čtení, pro zápis, pro připojení.

Bool open_file(řetězec název_souboru, int otevřený_režim); //...
open_file("Phoenix_and_the_Crane", append);

Toto řešení je možné, ale ne zcela přijatelné, protože nemůžeme zaručit, že argument předaný funkci open_file() je pouze 1, 2 nebo 3.
Tento problém řeší použití typu enum. Když píšeme:

Enum open_modes( input = 1, output, append );

definujeme nový typ open_modes. Platné hodnoty pro objekt tohoto typu jsou omezeny na sadu 1, 2 a 3, přičemž každá ze zadaných hodnot má mnemotechnický název. Název tohoto nového typu můžeme použít k definování objektu tohoto typu i typu formálních parametrů funkce:

Void open_file(řetězec název_souboru, otevřené_režimy om);

vstup, výstup a připojení jsou výčtové prvky. Sada prvků výčtu určuje povolenou sadu hodnot pro objekt daného typu.

Proměnná typu open_modes (v našem příkladu) je inicializována jednou z těchto hodnot, lze jí také přiřadit kteroukoli z nich. Například:

Open_file("Fénix a jeřáb", append);

Pokus přiřadit proměnnou tohoto typu jinou hodnotu než jeden z prvků výčtu (nebo ji předat jako parametr funkci) způsobí chybu kompilace. I když se pokusíme předat celočíselnou hodnotu odpovídající jednomu z prvků výčtu, stále se zobrazí chyba:

// chyba: 1 není prvkem výčtu open_modes open_file("Jonah", 1);

Existuje způsob, jak definovat proměnnou typu open_modes, přiřadit jí hodnotu jednoho z prvků výčtu a předat ji jako parametr funkci:

Open_modes om = vstup;

// ... om = append;<< input << " " << om << endl;

open_file("TailTell", om);

Není však možné získat názvy takových prvků. Pokud napíšeme výstupní příkaz:

// ... om = append;<< open_modes_table[ input ] << " " << open_modes_table[ om ] << endl Будет выведено: input append

Cout

pak ještě dostaneme:

Chcete-li definovat výčet, použijte klíčové slovo enum a názvy prvků jsou uvedeny ve složených závorkách oddělených čárkami. Ve výchozím nastavení je první 0, další 1 a tak dále. Toto pravidlo můžete změnit pomocí operátoru přiřazení. V tomto případě bude každý následující prvek bez explicitně specifikované hodnoty o 1 větší než prvek, který je v seznamu před ním. V našem příkladu jsme explicitně zadali hodnotu 1 pro vstup a výstup a připojení se budou rovnat 2 a 3. Zde je další příklad:

// tvar == 0, koule == 1, válec == 2, mnohoúhelník == 3 enum Forms( share, spere, cylinder, polygon );

Celočíselné hodnoty odpovídající různým prvkům stejného výčtu se nemusí lišit. Například:

// bod2d == 2, bod2w == 3, bod3d == 3, bod3w == 4 enum Body ( bod2d=2, bod2w, bod3d=3, bod3w=4 );

Objekt, jehož typ je výčet, lze definovat, použít ve výrazech a předat funkci jako argument. Takový objekt je inicializován pouze hodnotou jednoho z prvků výčtu a je mu přiřazena pouze tato hodnota, buď explicitně, nebo jako hodnota jiného objektu stejného typu. Nelze k němu přiřadit ani celočíselné hodnoty odpovídající platným prvkům výčtu:

Void mumble() ( Body pt3d = bod3d; // správně: pt2d == 3 // chyba: pt3w je inicializováno typem int Body pt3w = 3; // chyba: polygon není zahrnut ve výčtu bodů pt3w = polygon; / / správně: oba objekty typu Body pt3w = pt3d )

V aritmetických výrazech však lze výčet automaticky převést na typ int. Například:

Const int array_size = 1024; // správně: pt2w se převede na int
int chunk_size = velikost_pole * pt2w;

3.9. Zadejte "pole"

Pole jsme se již dotkli v sekci 2.1. Pole je sada prvků stejného typu, ke které se přistupuje pomocí indexu – pořadového čísla prvku v poli. Například:

Int ival;

definuje ival jako proměnnou int a instrukce

Int ia[10];

určuje pole deseti objektů typu int. Ke každému z těchto objektů, popř prvky pole, lze přistupovat pomocí operace indexu:

Ival = ia[2];

přiřadí proměnné ival hodnotu prvku pole ia s indexem 2. Podobně

la[7] = ival;

přiřadí prvku na indexu 7 hodnotu ival.

Definice pole se skládá ze specifikátoru typu, názvu pole a velikosti.

Velikost určuje počet prvků pole (alespoň 1) a je uzavřena v hranatých závorkách. Velikost pole musí být známa ve fázi kompilace, a proto se musí jednat o konstantní výraz, i když nemusí být nutně specifikován jako literál.
Zde jsou příklady správných a nesprávných definic pole:
Externí int get_size(); // konstanty buf_size a max_files

const int buf_size = 512, max_files = 20;
int staff_size = 27; // správně: konstantní char input_buffer[ buf_size ]; // správně: konstantní výraz: 20 - 3 char *fileTable[ max_files-3 ]; // chyba: nejde o konstantní dvojnásobné platy[ velikost_personálu ]; // chyba: není konstantní výraz int test_scores[ get_size() ];
Objekty buf_size a max_files jsou konstanty, takže definice pole input_buffer a fileTable jsou správné. Ale staff_size je proměnná (i když inicializovaná konstantou 27), což znamená, že platy jsou nepřijatelné. (Kompilátor nemůže najít hodnotu proměnné staff_size, když je definováno pole platů.)

Výraz max_files-3 lze vyhodnotit v době kompilace, takže definice pole fileTable je syntakticky správná.< array_size; ++ ix)
Číslování prvků začíná na 0, takže pro pole 10 prvků není správný rozsah indexu 1 – 10, ale 0 – 9. Zde je příklad iterace přes všechny prvky pole:
}

Int main() ( const int velikost_pole = 10; int ia[ velikost_pole ]; for (int ix = 0; ix

ia[ ix] = ix;

Při definování pole jej můžete explicitně inicializovat uvedením hodnot jeho prvků ve složených závorkách, oddělených čárkami:

Const int velikost_pole = 3; int ia[ velikost_pole ] = ( 0, 1, 2 );

Pokud explicitně zadáme seznam hodnot, pak nemusíme specifikovat velikost pole: kompilátor sám spočítá počet prvků:

// pole velikosti 3 int ia = ( 0, 1, 2 );

Když jsou explicitně zadány jak velikost, tak seznam hodnot, jsou možné tři možnosti. Pokud se velikost a počet hodnot shodují, je vše zřejmé. Pokud je seznam hodnot kratší než zadaná velikost, zbývající prvky pole se inicializují na nulu.

Pokud je v seznamu více hodnot, kompilátor zobrazí chybovou zprávu:

Rozměr pole ca1 je 3, pole ca2 je 4 (v řetězcových literálech se bere v úvahu ukončovací null znak). Následující definice způsobí chybu kompilace:

// chyba: řetězec "Daniel" se skládá ze 7 prvků const char ch3[ 6 ] = "Daniel";

Pole nemůže být přiřazena hodnota jiného pole a inicializace jednoho pole jiným není povolena. Kromě toho není povoleno používat pole odkazů.

Zde jsou příklady správného a nesprávného použití polí:
{
Const int velikost_pole = 3; int ix, jx, kx; // správně: pole ukazatelů typu int* int *iar = ( &ix, &jx, &kx ); // chyba: referenční pole nejsou povolena int &iar = ( ix, jx, kx ); int main()
int ia3( array_size ]; // správně
// chyba: vestavěná pole nelze zkopírovat
ia3 = ia;
}

návrat 0;

Chcete-li zkopírovat jedno pole do druhého, musíte to udělat pro každý prvek zvlášť:
Const int array_size = 7; int ia1 = (0, 1, 2, 3, 4, 5, 6); int main() (< array_size; ++ix)
int ia3[ velikost_pole ];
}

for (int ix = 0; ix

ia2[ ix] = ia1[ ix];

návrat 0;

Index pole může být jakýkoli výraz, který vytváří výsledek typu celé číslo. Například:

Int someVal, get_index(); ia2[ get_index() ] = someVal;

Zdůrazňujeme, že jazyk C++ neposkytuje kontrolu nad indexy polí, ani ve fázi kompilace, ani ve fázi běhu. Programátor sám musí zajistit, aby index nepřesahoval hranice pole. Chyby při práci s indexem jsou poměrně časté. Bohužel není příliš těžké najít příklady programů, které se zkompilují a dokonce fungují, ale přesto obsahují fatální chyby, které dříve nebo později vedou k pádu.

Cvičení 3.22

Které z následujících definic pole jsou nesprávné? Vysvětlit.

(a) int ia[ buf_size ]; (d) int ia[ 2 * 7 - 14] (b) int ia[ get_size() ]; (e) char st[ 11 ] = "základní"; (c) int ia[4*7-14];<= array_size; ++ix)
Cvičení 3.23
}

Následující fragment kódu musí inicializovat každý prvek pole hodnotou indexu. Najděte chyby, které jste udělali:

Int main() ( const int velikost_pole = 10; int ia[ velikost_pole ]; for (int ix = 1; ix

ia[ia] = ix;

První hodnota (4) udává počet řádků, druhá (3) počet sloupců.

Objekt ia je definován jako pole čtyř řetězců po třech prvcích. Vícerozměrná pole lze také inicializovat:

Int ia[4][3] = ((0, 1,2), (3, 4, 5), (6, 7, 8), (9, 10, 11));

Vnitřní složené závorky, které rozdělují seznam hodnot na řádky, jsou volitelné a obecně se používají ke snadnějšímu čtení kódu. Inicializace níže je přesně stejná jako v předchozím příkladu, i když je méně jasná:

Int ia = (0,1,2,3,4,5,6,7,8,9,10,11);

Následující definice inicializuje pouze první prvky každého řádku.

Zbývající prvky budou nulové:

Int ia[4][3] = ((0), (3), (6), (9));

Pokud vynecháte vnitřní kudrnatá rovnátka, bude výsledek úplně jiný. Všechny tři prvky prvního řádku a první prvek druhého obdrží zadanou hodnotu a zbytek bude implicitně inicializován na 0.

Int ia[4][3] = (0, 3, 6, 9);< rowSize; ++i)
Při přístupu k prvkům vícerozměrného pole musíte použít indexy pro každou dimenzi (jsou uzavřeny v hranatých závorkách). Takto vypadá inicializace dvourozměrného pole pomocí vnořených smyček:< colSize; ++j)
Int main() ( const int rowSize = 4; const int colSize = 3; int ia[ rowSize ][ colSize ]; for (int = 0; i
}

for (int j = 0; j

ia[i][j] = i + j j;

Design

Ia[ 1, 2]

je platný z hlediska syntaxe C++, ale vůbec neznamená to, co nezkušený programátor očekává. Toto není deklarace dvourozměrného pole 1x2. Agregát v hranatých závorkách je čárkou oddělený seznam výrazů, jejichž výsledkem je konečná hodnota 2 (viz operátor čárky v sekci 4.2). Proto je deklarace ia ekvivalentní ia.

To je další příležitost udělat chybu.

3.9.2. Vztah mezi poli a ukazateli

Pokud máme definici pole:

Int ia = (0, 1, 1, 2, 3, 5, 8, 13, 21);

co tedy znamená pouhé uvedení jeho jména v programu?

Použití identifikátoru pole v programu je ekvivalentní zadání adresy jeho prvního prvku:

Podobně můžete přistupovat k hodnotě prvního prvku pole dvěma způsoby:

// oba výrazy vrátí první prvek *ia; ia;

Abychom získali adresu druhého prvku pole, musíme napsat:

Jak jsme již zmínili, výraz

také dává adresu druhého prvku pole. V souladu s tím je nám jeho význam dán následujícími dvěma způsoby:

*(ia+1); ia; Všimněte si rozdílu ve výrazech: než operace sčítání (priority operátora jsou popsány v části 4.13).

Proto první výraz nejprve dereferencuje proměnnou ia a získá první prvek pole a poté k němu přidá 1. Druhý výraz dodá hodnotu druhého prvku.

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: #zahrnout<< *pbegin <<; ++pbegin; } }

int main() ( int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 ); int *pbegin = ia; int *pend = ia + 9; while (pbegin != pend) ( cout

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: Ukazatel pbegin je inicializován na adresu prvního prvku pole. Každý průchod smyčkou zvýší tento ukazatel o 1, což znamená, že se posune na další prvek. Jak víte, kde se ubytovat? V našem příkladu jsme definovali druhý pend pointer a inicializovali jej s adresou následující za posledním prvkem pole ia. Jakmile se hodnota pbegin rovná pend, víme, že pole skončilo. Přepišme tento program tak, aby začátek a konec pole byly předány jako parametry určité zobecněné funkci, která dokáže vytisknout pole libovolné velikosti:
void ia_print(int *pbegin, int *pend) (
++chyby;<< *pbegin << " ";
while (pbegin != pend) (
}
++pbegin;
{
) int main()
int ia = (0, 1, 1, 2, 3, 5, 8, 13, 21);
}

ia_print(ia, ia + 9);

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: Naše funkce se stala univerzálnější, nicméně umí pracovat pouze s poli typu int. Existuje způsob, jak toto omezení odstranit: převeďte tuto funkci na šablonu (šablony byly krátce představeny v sekci 2.5): šablona<< *pbegin << " "; ++pbegin; } }

void print(elemType *pbegin, elemType *pend) ( while (pbegin != pend) ( cout

Nyní můžeme zavolat naši funkci print() k tisku polí libovolného typu:
Int main() ( int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 ); double da = ( 3,14, 6,28, 12,56, 25,12 ); string sa = ( "prasátko", " eyore", "pú" ); print(ia, ia+9);
print(da, da+4);
}

tisknout(sa, so+3); Psali jsme zobecněný

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: funkce. Standardní knihovna poskytuje sadu generických algoritmů (již jsme to zmínili v sekci 3.4) implementovaných podobným způsobem. Parametry takových funkcí jsou ukazatele na začátek a konec pole, se kterým provádějí určité akce. Zde je například to, jak vypadají volání zobecněného třídícího algoritmu:
int main() ( int ia = ( 107, 28, 3, 47, 104, 76 ); string sa = ( "prasátko", "eeyore", "pú" ); sort(ia, ia+6);
};

sort(sa, sa+3);
Standardní knihovna C++ obsahuje sadu tříd, které zapouzdřují použití kontejnerů a ukazatelů. (To bylo probráno v části 2.8.) V další části se podíváme na standardní vektor typu kontejneru, což je objektově orientovaná implementace pole.

3.10. Vektorové třídy

Použití třídy vector (viz oddíl 2.8) je alternativou k používání vestavěných polí. Tato třída poskytuje mnohem více funkcí, takže její použití je vhodnější. Jsou však situace, kdy se bez polí vestavěného typu neobejdete. Jednou z těchto situací je zpracování parametrů příkazového řádku předávaných programu, kterému se budeme věnovat v části 7.8. Třída vector, stejně jako třída string, je součástí standardní knihovny C++.
Chcete-li použít vektor, musíte zahrnout soubor záhlaví:

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například:

Existují dva zcela odlišné přístupy k použití vektoru, nazvěme je idiom pole a idiom STL. V prvním případě se vektorový objekt používá přesně stejným způsobem jako pole vestavěného typu. Vektor dané dimenze je určen:

Vektor< int >ivec(10);

což je podobné definování pole vestavěného typu:

Int ia[10];

Pro přístup k jednotlivým prvkům vektoru se používá operace index:

Void simp1e_examp1e() ( const int e1em_size = 10; vektor< int >ivec(e1em_velikost);< e1em_size; ++ix)
int ia[ e1em_velikost ];
}

for (int ix = 0; ix

ia[ ix ] = ivec[ ix ]; //...< ivec.size(); ++ix)
++chyby;<< ivec[ ix ] << " ";
}

Rozměr vektoru můžeme zjistit pomocí funkce size() a zkontrolovat, zda je vektor prázdný, pomocí funkce empty(). Například:

Vektor< int >Void print_vector(vektor

ivec) ( if (ivec.empty()) return; for (int ix=0; ix
Prvky vektoru jsou inicializovány s výchozími hodnotami. Pro číselné typy a ukazatele je tato hodnota 0. Pokud jsou prvky objekty třídy, pak je jejich iniciátor určen výchozím konstruktorem (viz část 2.3). Iniciátor však může být také specifikován explicitně pomocí formuláře:

ivec(10, -1);

Všech deset prvků vektoru se bude rovnat -1.

Pole vestavěného typu lze explicitně inicializovat pomocí seznamu:< int >Int ia[6] = (-2, -1, O, 1, 2, 1024);

Podobná akce není možná pro objekt třídy vector. Takový objekt však lze inicializovat pomocí vestavěného typu pole:

// Kopírují se 3 prvky: ia, ia, ia vector< int >ivec(&ia[2], &ia[5]);

Dalším rozdílem mezi vektorem a vestavěným polem je schopnost inicializovat jeden vektorový objekt druhým a ke kopírování objektů použít operátor přiřazení. Například:

Vektor< string >svec; void init_and_assign() ( // jeden vektor je inicializován jiným vektorem< string >user_names(svec);
// ... // jeden vektor je zkopírován do druhého
}

svec = uživatelská_jména;

Vektor< string >Když mluvíme o idiomu STL, máme na mysli úplně jiný přístup k použití vektoru. Místo okamžitého zadání požadované velikosti definujeme prázdný vektor:

text;

Pak do něj pomocí různých funkcí přidáváme prvky. Například funkce push_back() vloží prvek na konec vektoru. Zde je část kódu, která čte sekvenci řádků ze standardního vstupu a přidává je do vektoru:

Řetězcové slovo;

// ... om = append;<< "считаны слова: \n"; for (int ix =0; ix < text.size(); ++ix) cout << text[ ix ] << " "; cout << endl;

while (cin >> slovo) ( text.push_back(word); // ... )

// ... om = append;<< "считаны слова: \n"; for (vectorI když můžeme použít operaci indexu k iteraci prvků vektoru:<< *it << " "; cout << endl;

Typičtější použití iterátorů v tomto idiomu by bylo:
::iterator it = text.begin(); it != text.end(); ++it) cout

Iterátor je standardní třída knihovny, která je v podstatě ukazatelem na prvek pole.

Výraz

Vektor dereferencuje iterátor a dává samotný vektorový prvek. Instrukce

Přesune ukazatel na další prvek. Není třeba tyto dva přístupy míchat.

Pokud se při definování prázdného vektoru řídíte idiomem STL:

ivec;

Vektor Byla by chyba napsat:

Zatím nemáme jediný prvek vektoru ivec; Počet prvků se určuje pomocí funkce size().

Lze udělat i opačnou chybu. Pokud bychom definovali vektor nějaké velikosti, například:< int >ia(10);< size; ++ix) ivec.push_back(ia[ ix ]);

Pak vkládání prvků zvětšuje jeho velikost a přidává nové prvky ke stávajícím.
Ačkoli se to zdá zřejmé, začínající programátor může napsat:

Const int size = 7; int ia[velikost] = (0, 1, 1, 2, 3, 5, 8); vektor

ivec(velikost); for (int ix = 0; ix
To znamenalo inicializovat vektor ivec s hodnotami prvků ia, což vedlo k vektoru ivec o velikosti 14.

Podle idiomu STL můžete nejen přidávat, ale také odebírat prvky z vektoru.< vector< int >(Na to vše se podrobně a s příklady podíváme v kapitole 6.)
Cvičení 3.24< int >Jsou v následujících definicích nějaké chyby?
int ia[7] = (0, 1, 1, 2, 3, 5, 8);< int >(a)vektor
>ivec;< string >b) vektor
ivec = (0, 1, 1, 2, 3, 5, 8);< string >c)vektor

ivec(ia, ia+7);

(d)vektor
bool is_equal(const int*ia, int ia_size,
konstantní vektor &ivec);
Funkce is_equal() porovnává dva kontejnery prvek po prvku. U nádob různých velikostí se nebere v úvahu „ocas“ delší. Je jasné, že pokud jsou všechny porovnávané prvky stejné, funkce vrací true, pokud je alespoň jeden jiný, nepravda. K iteraci prvků použijte iterátor. Napište funkci main(), která volá is_equal().

3.11. třídní komplex

Třída komplexních čísel je další třídou ze standardní knihovny.

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například:

Jako obvykle, abyste jej mohli použít, musíte zahrnout soubor záhlaví:

Komplexní číslo se skládá ze dvou částí – reálné a imaginární. Imaginární část je druhá odmocnina záporného čísla. Komplexní číslo se obvykle zapisuje ve tvaru

kde 2 je skutečná část a 3i je imaginární část. Zde jsou příklady definic objektů typu komplex:< double >// čistě imaginární číslo: 0 + 7-i komplex< float >purei(0,7); // imaginární část je 0:3 + komplex Oi< long double >real1_num(3); // skutečná i imaginární část se rovná 0: 0 + 0-i komplex< double >nula; // inicializace jednoho komplexního čísla jiným komplexním číslem

purei2(purei);

Vzhledem k tomu, že komplex, stejně jako vektor, je šablona, ​​můžeme ji vytvořit instancí s typy float, double a long double, jako v uvedených příkladech. Můžete také definovat pole prvků typu complex:< double >Komplex< double >konjugovat[2] = (komplex< double >(2, -3) };

Vzhledem k tomu, že komplex, stejně jako vektor, je šablona, ​​můžeme ji vytvořit instancí s typy float, double a long double, jako v uvedených příkladech. Můžete také definovat pole prvků typu complex:< double >(2, 3), komplexní< double >*ptr = komplexní

&ref = *ptr;

3.12. směrnice typedef

Direktiva typedef umožňuje zadat synonymum pro vestavěný nebo vlastní datový typ. Například: Typedef dvojnásobné mzdy; typedef vektor

vec_int; typedef vec_int test_scores; typedef bool docházka; typedef int *Pint;

Názvy definované pomocí direktivy typedef lze použít přesně stejným způsobem jako specifikátory typu: // double hourly, weekly; mzda hodinová, týdenní; //vektor
vecl(10); vec_int vecl(10); //vektor< bool >test0(c1ass_velikost); const int c1ass_size = 34; test_scores test0(c1ass_size); //vektor< in_attendance >účast; vektor

docházka(c1ass_size); // int *tabulka[ 10 ]; Půllitr [10];
K čemu jsou názvy definovány pomocí direktivy typedef? Použitím mnemotechnických názvů pro datové typy můžete usnadnit pochopení svého programu. Kromě toho je běžné používat taková jména pro složité složené typy, které jsou jinak obtížně srozumitelné (viz příklad v části 3.14), k deklaraci ukazatelů na funkce a členské funkce třídy (viz část 13.6).
Níže je uveden příklad otázky, na kterou téměř všichni odpoví špatně. Chyba je způsobena nesprávným pochopením direktivy typedef jako jednoduché nahrazení textového makra.

Uvedená definice:

Typedef char *cstring;

Jaký je typ proměnné cstr v následující deklaraci:

Externí const cstring cstr;

Odpověď, která se zdá zřejmá, je:

Const char *cstr

To však není pravda. Specifikátor const odkazuje na cstr, takže správnou odpovědí je ukazatel const na char:

Char *const cstr;

3.13. těkavý specifikátor
Objekt je prohlášen za nestálý, pokud lze jeho hodnotu změnit, aniž by si toho kompilátor všiml, jako je proměnná aktualizovaná systémovými hodinami. Tento specifikátor říká kompilátoru, že pro práci s tímto objektem nepotřebuje optimalizovat kód.

Těkavý specifikátor se používá podobně jako specifikátor const:

Volatile int disp1ay_register; volatile Task *curr_task; volatile int ixa[ max_velikost ]; nestálá obrazovka bitmap_buf;
display_register je nestálý objekt typu int. curr_task je ukazatel na nestálý objekt třídy Task. ixa je nestabilní pole celých čísel a každý prvek takového pole je považován za nestabilní. bitmap_buf je nestálý objekt třídy Screen, každý jeho datový člen je také považován za nestálý.

Jediným účelem použití nestálého specifikátoru je sdělit kompilátoru, že nemůže určit, kdo a jak může změnit hodnotu daného objektu.

Kompilátor by proto neměl provádět optimalizace kódu, který používá tento objekt. 3.14. Třídní pár Třída pair standardní knihovny C++ nám umožňuje definovat pár hodnot s jedním objektem, pokud mezi nimi existuje nějaké sémantické spojení.

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například:

Tyto hodnoty mohou být stejné nebo

různé typy< string, string >. Chcete-li použít tuto třídu, musíte zahrnout soubor záhlaví:

Například návod
Pár

autor("James", "Joyce");
Joyce.second == "Joyce")
první kniha = "Stephen Hero";

Pokud potřebujete definovat několik objektů stejného typu této třídy, je vhodné použít direktivu typedef:

Typedef pár< string, string >Autoři; Autoři proust("marcel", "proust"); Autoři joyce("James", "Joyce"); Autoři musil("robert", "musi1");

Zde je další příklad použití páru. První hodnota obsahuje jméno nějakého objektu, druhá je ukazatel na prvek tabulky odpovídající tomuto objektu.

Class EntrySlot; extern EntrySlot* 1ook_up(string); typedef pár< string, EntrySlot* >SymbolEntry; SymbolEntry current_entry("autor", 1ook_up("author"));
// ... if (EntrySlot *it = 1ook_up("editor")) (
current_entry.first = "editor";
aktuální_vstup.sekunda = it;
}

(K párové třídě se vrátíme, až budeme mluvit o typech kontejnerů v kapitole 6 a o obecných algoritmech v kapitole 12.)

3.15. Typy tříd

Mechanismus třídy umožňuje vytvářet nové datové typy; s jeho pomocí byly představeny výše diskutované typy řetězců, vektorů, komplexů a párů. V kapitole 2 jsme na příkladu implementace třídy Array představili koncepty a mechanismy, které podporují objektově a objektově orientované přístupy. Zde na základě objektového přístupu vytvoříme jednoduchou třídu String, jejíž implementace nám pomůže pochopit zejména přetěžování operátorů – o tom jsme hovořili v části 2.3. (Třídy jsou podrobně popsány v kapitolách 13, 14 a 15.) Uvedli jsme stručný popis třídy, abychom uvedli zajímavější příklady. Čtenář nový v C++ může chtít tuto část přeskočit a počkat na systematičtější popis tříd v pozdějších kapitolách.)
Naše třída String musí podporovat inicializaci objektem třídy String, řetězcový literál a vestavěný typ řetězce, stejně jako operaci přiřazování hodnot k těmto typům. Používáme k tomu konstruktory tříd a přetížený operátor přiřazení. Přístup k jednotlivým znakům String bude implementován jako přetížená indexová operace. Dále budeme potřebovat: funkci size() pro získání informace o délce řetězce; operace porovnávání objektů typu String a objektu String s řetězcem vestavěného typu; stejně jako I/O operace našeho objektu. Nakonec implementujeme možnost přístupu k interní reprezentaci našeho řetězce jako vestavěného typu řetězce.
Definice třídy začíná klíčovým slovem class, za nímž následuje identifikátor – název třídy nebo typ. Obecně se třída skládá z částí, kterým předcházejí slova public (otevřená) a private (uzavřená). Veřejná sekce obvykle obsahuje sadu operací podporovaných třídou, nazývaných metody nebo členské funkce třídy. Tyto členské funkce definují veřejné rozhraní třídy, jinými slovy, sadu akcí, které lze provádět s objekty této třídy. Soukromá sekce obvykle zahrnuje datové členy, které zajišťují interní implementaci. V našem případě mezi vnitřní členy patří _string - ukazatel na znak, stejně jako _size typu int. _size bude ukládat informace o délce řetězce a _string bude dynamicky alokované pole znaků. Takto vypadá definice třídy:

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: třída String; istream& operátor>>(istream&, String&);
ostream & operátor<<(ostream&, const String&); class String {
veřejnost:
// sada konstruktorů
// pro automatickou inicializaci
// String strl; // String()
// String str2("doslovný"); // String(const char*);
// String str3(str2); // String(const String&);
String();
String(const char*);
String(const String&);
// destruktor
~String();
// operátory přiřazení
// strl = str2
// str3 = "řetězcový literál" String& operator=(const String&);
String& operator=(const char*);
// Operátoři testování rovnosti
// strl == str2;
// str3 == "řetězcový literál";
bool operator==(const String&);
bool operator==(const char*);
}

// přetížení přístupového operátora indexem

// strl[ 0 ] = str2[ 0 ]; char& operator(int);// přístup ke členům třídy

int size() ( return _size; )
char* c_str() ( return _string; ) private:

int_size;

char *_string;

Třída String má tři konstruktory. Jak je uvedeno v části 2.3, mechanismus přetěžování vám umožňuje definovat více implementací funkcí se stejným názvem, pokud se všechny liší počtem a/nebo typem svých parametrů.

První konstruktér

Je výchozí konstruktor, protože nevyžaduje

explicitní označení

počáteční hodnota. Když píšeme:
Pro str1 se takový konstruktor nazývá.

Dva zbývající konstruktory mají každý jeden parametr. Ano, pro

To způsobí chybu kompilace, protože neexistuje konstruktor s parametrem typu int.
Deklarace přetíženého operátora má následující formát:

Operátor návratového_typu op(seznam_parametrů);

Kde operátor je klíčové slovo a op je jeden z předdefinovaných operátorů: +, =, == atd. (Přesnou definici syntaxe naleznete v kapitole 15.) Zde je deklarace přetíženého indexového operátoru:

Char& operator(int);

Tento operátor má jeden parametr typu int a vrací odkaz na znak.
Přetížený operátor může být sám přetížen, pokud se seznamy parametrů jednotlivých instancí liší. Pro naši třídu String vytvoříme dva různé operátory přiřazení a rovnosti.

Chcete-li volat členskou funkci, použijte operátory přístupu členů tečka (.) nebo šipka (->). Mějme deklarace objektů typu String:
String object("Danny");
String *ptr = new String("Anna");
Pole řetězců;
Takto vypadá volání funkce size() na těchto objektech: vektor

velikosti(3);
// přístupový člen pro objekty (.); // objekty mají velikost 5 velikostí[ 0 ] = object.size(); // přístup členů pro ukazatele (->)
// ptr má velikost 4
velikosti[ 1 ] = ptr->velikost(); // přístup k členu (.)
// pole má velikost 0

size[ 2 ] = array.size();
Vrací 5, 4 a 0 v tomto pořadí.

Přetížené operátory jsou aplikovány na objekt stejným způsobem jako běžné operátory:
String name("Yadie"); String name2("Yodie"); // bool operátor==(const String&)
if (jméno == jméno2)
návrat;
jiný
// String& operator=(const String&)

jmeno = jmeno2;

Deklarace členské funkce musí být uvnitř definice třídy a definice funkce může být uvnitř nebo vně definice třídy. (Funkce size() i c_str() jsou definovány uvnitř třídy.) Pokud je funkce definována mimo třídu, pak musíme mimo jiné určit, do které třídy patří.
V tomto případě se definice funkce umístí do zdrojového souboru, řekněme String.C, a definice samotné třídy se umístí do hlavičkového souboru (v našem příkladu String.h), který musí být zahrnut ve zdrojovém souboru. :
cout
// obsah zdrojového souboru: String.C // povolení definice třídy String
#include "String.h" // zahrnout definici funkce strcmp().
bool // návratový typ
String:: // třída, do které funkce patří
{
operator== // název funkce: operátor rovnosti
(const String &rhs) // seznam parametrů
if (_velikost != rhs._size)
vrátit false;
}

Připomeňme, že strcmp() je funkce standardní knihovny C, která porovnává dva vestavěné řetězce a vrací 0, pokud jsou řetězce stejné, a nenulové, pokud nejsou stejné. Podmíněný operátor (?:) testuje hodnotu před otazníkem. Pokud je true, je vrácena hodnota výrazu vlevo od dvojtečky, jinak je vrácena hodnota vpravo. V našem příkladu je hodnota výrazu nepravdivá, pokud strcmp() vrátila nenulovou hodnotu, a pravdivá, pokud vrátila nulovou hodnotu. (Podmíněný operátor je popsán v části 4.7.)
Porovnávací operace se používá poměrně často, funkce, která ji implementuje, se ukázala jako malá, proto je užitečné tuto funkci deklarovat jako zabudovanou (inline). Kompilátor nahradí text funkce místo jejího volání, takže se při takovém volání neztrácí čas. (Vestavěné funkce jsou popsány v části 7.6.) Členská funkce definovaná v rámci třídy je ve výchozím nastavení vestavěná. Pokud je definována mimo třídu, abyste ji mohli deklarovat jako integrovanou, musíte použít klíčové slovo inline:

Inline bool String::operator==(const String &rhs) ( // totéž )

Definice vestavěné funkce musí být in hlavičkový soubor obsahující definici třídy. Předefinováním operátoru == na vložený operátor musíme přesunout samotný text funkce ze souboru String.C do souboru String.h.
Následuje implementace operace porovnání objektu String s vestavěným typem řetězce:

Inline bool String::operator==(const char *s) ( return strcmp(_string, s) ? false: true; )

Název konstruktoru je stejný jako název třídy. Má se za to, že nevrací hodnotu, takže není potřeba uvádět návratovou hodnotu ani v její definici, ani v jejím těle. Konstruktorů může být několik. Jako každá jiná funkce mohou být deklarovány jako inline.

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: // výchozí vložený konstruktor String::String()
{
_velikost = 0;
_string = 0;
) inline String::String(const char *str) ( if (! str) ( _size = 0; _string = 0; ) else ( _size = str1en(str); strcpy(_string, str); ) // kopírovat konstruktor
inline String::String(const String &rhs)
{
velikost = rhs._size;
if (! rhs._string)
_string = 0;
jiný(
_string = nový znak[ _velikost + 1 ];
} }

Protože jsme dynamicky alokovali paměť pomocí operátoru new, musíme ji uvolnit voláním delete, když již nepotřebujeme objekt String. K tomu slouží další speciální členská funkce - destruktor, který je automaticky volán na objekt v okamžiku, kdy tento objekt přestane existovat. (Viz kapitola 7 o životnosti objektu.) Název destruktoru je tvořen znakem vlnovky (~) a názvem třídy. Zde je definice destruktoru třídy String. Zde voláme operaci odstranění, abychom uvolnili paměť alokovanou v konstruktoru:

Vložený řetězec: :~String() ( smazat _string; )

Oba přetížené operátory přiřazení používají speciální klíčové slovo this.
Když píšeme:

String name("orville"), name2("wilbur");
jméno = "Orville Wright";
toto je ukazatel, který adresuje objekt name1 v těle funkce operace přiřazení.
to vždy ukazuje na objekt třídy, přes který je funkce volána.
Li
ptr->velikost();

obj[ 1024];

Pak uvnitř size() bude jeho hodnota adresa uložená v ptr. Uvnitř operace indexu to obsahuje adresu obj. Dereferencováním tohoto (pomocí *this) získáme samotný objekt. (Tento ukazatel je podrobně popsán v části 13.4.)

Inline String& String::operator=(const char *s) ( if (! s) ( _size = 0; delete _string; _string = 0; ) else ( _size = str1en(s); delete _string; _string = new char[ _size + 1 ]; strcpy(_string, s);

Při implementaci operace přiřazení se často dělá chyba, že zapomínají zkontrolovat, zda kopírovaný objekt je stejný, do kterého se kopie vytváří. Tuto kontrolu provedeme pomocí stejného ukazatele:

Inline String& String::operator=(const String &rhs) ( // ve výrazu // name = *pointer_to_string // to představuje jméno1, // rhs - *pointer_to_string. if (toto != &rhs) (

Zde je úplný text operace přiřazení objektu stejného typu k objektu String:
_string = 0;
jiný(
_string = nový znak[ _velikost + 1 ];
Inline String& String::operator=(const String &rhs) ( if (toto != &rhs) ( smazat _string; _size = rhs._size; if (! rhs._string)
}
}
strcpy(_string, rhs._string);
}

vrátit *toto;

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: Operace převzetí indexu je téměř totožná s jeho implementací pro pole Array, které jsme vytvořili v sekci 2.3:
vložený znak&
{
String::operator (int elem)< _size);
tvrdit(elem >= 0 && el
}

Vstupní a výstupní operátory jsou implementovány jako samostatné funkce spíše než členové třídy.

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: (O důvodech si povíme v části 15.2. Části 20.4 a 20.5 hovoří o přetížení vstupních a výstupních operátorů knihovny iostream.) Náš vstupní operátor dokáže přečíst maximálně 4095 znaků. setw() je předdefinovaný manipulátor, čte daný počet znaků mínus 1 ze vstupního toku, čímž zajišťuje, že nepřetečeme náš vnitřní buffer inBuf. (Kapitola 20 podrobně popisuje manipulátor setw().) Chcete-li použít manipulátory, musíte zahrnout odpovídající soubor záhlaví:

inline istream& operator>>(istream &io, String &s) ( // umělý limit: 4096 znaků const int 1imit_string_size = 4096; char inBuf[ limit_string_size ]; // setw() je součástí knihovny iostream // omezuje velikost čitelný blok na 1imit_string_size -l io >> setw(1imit_string_size) >> inBuf s = mBuf // String::operator=(const char*);<< не является функцией-членом, он не имеет доступа к закрытому члену данных _string. Ситуацию можно разрешить двумя способами: объявить operator<< дружественным классу String, используя ключевое слово friend (дружественные отношения рассматриваются в разделе 15.2), или реализовать встраиваемую (inline) функцию для доступа к этому члену. В нашем случае уже есть такая функция: c_str() обеспечивает доступ к внутреннему представлению строки. Воспользуемся ею при реализации операции вывода:

Výstupní operátor potřebuje přístup k interní reprezentaci řetězce.<<(ostream& os, const String &s) { return os << s.c_str(); }

Od operátora

Pole můžete procházet pomocí indexu, jak jsme to udělali v předchozí části, nebo pomocí ukazatelů. Například: Inline ostream & operátor
Níže je uveden příklad programu používajícího třídu String. Tento program bere slova ze vstupního proudu a počítá jejich celkový počet, stejně jako počet slov „the“ a „it“ a zaznamenává samohlásky, se kterými se setkal.
#include "String.h" int main() ( int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, theCnt = 0, itCnt = 0, wdCnt = 0, notVowel = 0; / / Slova "The" a "It"
// zkontrolujeme pomocí operátoru==(const char*)
String but, the("the"), it("it");<<(ostream&, const String&)
++chyby;<< buf << " "; if (wdCnt % 12 == 0)
++chyby;<< endl; // String::operator==(const String&) and
// operátor>>(ostream&, String&)
while (cin >> buf) (
++wdCnt;
// operátor
// String::operator==(const char*);
if (buf == the | | buf == "The")
++theCnt;< buf.sizeO; ++ix)
{
jiný
if (buf == to || buf == "To")
{
++itCnt;
// vyvolá String::s-ize()
for (int ix = 0; ix
// vyvolá String::operator(int)
switch(buf[ ix ])
případ "a": případ "A": ++aCnt; přerušení;
}
}
případ "e": případ "E": ++eCnt; přerušení;<<(ostream&, const String&)
++chyby;<< "\n\n"
<< "Слов: " << wdCnt << "\n\n"
<< "the/The: " << theCnt << "\n"
<< "it/It: " << itCnt << "\n\n"
<< "согласных: " < << "a: " << aCnt << "\n"
<< "e: " << eCnt << "\n"
<< "i: " << ICnt << "\n"
<< "o: " << oCnt << "\n"
<< "u: " << uCnt << endl;
}

případ "i": případ "I": ++iCnt; přerušení;

Alice Emma má dlouhé rozpuštěné zrzavé vlasy. Její tatínek říká, že když jí vítr fouká do vlasů, vypadají skoro jako živé, jako ohnivý pták v letu. Krásný ohnivý pták, říká jí, kouzelný, ale nezkrotný. "Tati, šup, nic takového neexistuje," říká mu a zároveň chce, aby jí řekl víc. Plaše se zeptá: "Myslím, tati, je tam?" Slova: 65
the/The: 2
to/to: 1
souhlásky: 190
a: 22
e: 30
já: 24
o: 10
u: 7

Cvičení 3.26

Naše implementace konstruktorů a operátorů přiřazení obsahují mnoho opakování. Zvažte přesunutí duplicitního kódu do samostatné soukromé členské funkce, jak bylo provedeno v části 2.3. Ujistěte se, že nová možnost funguje.

Cvičení 3.27

Upravte testovací program tak, aby počítal i souhlásky b, d, f, s, t.

Cvičení 3.28

Napište členskou funkci, která počítá počet výskytů znaku v řetězci pomocí následující deklarace:

Class String ( public: // ... int count(char ch) const; // ... );

Cvičení 3.29

Implementujte operátor zřetězení řetězců (+) tak, aby zřetězil dva řetězce a vrátil výsledek v novém objektu String. Zde je deklarace funkce:

Class String ( public: // ... Operátor řetězce+(const String &rhs) const; // ... );

Tato část se bude zabývat hlavními datovými typy v C++, tyto datové typy se také nazývají vestavěné. Programovací jazyk C++ je rozšiřitelný programovací jazyk. Termín rozšiřitelný znamená, že kromě vestavěných datových typů můžete vytvářet vlastní datové typy. Proto je v C++ obrovské množství datových typů. Budeme studovat pouze ty hlavní.

Tabulka 1 - Datové typy C++
Typ byte Rozsah akceptovaných hodnot

datový typ integer (Boolean).

bool 1 0 / 255

datový typ celé číslo (znak).

char 1 0 / 255

celočíselné typy data

krátký int 2 -32 768 / 32 767
unsigned short int 2 0 / 65 535
int 4
nepodepsaný int 4 0 / 4 294 967 295
dlouhá int 4 -2 147 483 648 / 2 147 483 647
unsigned long int 4 0 / 4 294 967 295

datové typy s plovoucí desetinnou čárkou

plovák 4 -2 147 483 648.0 / 2 147 483 647.0
dlouhý plovák 8
dvojnásobek 8 -9 223 372 036 854 775 808 .0 / 9 223 372 036 854 775 807.0

Tabulka 1 ukazuje hlavní datové typy v C++. Celá tabulka je rozdělena do tří sloupců. První sloupec označuje vyhrazené slovo, které bude určovat každý svůj vlastní datový typ. Druhý sloupec udává počet bajtů přidělených pro proměnnou s odpovídajícím datovým typem. Třetí sloupec ukazuje rozsah přijatelných hodnot. Upozorňujeme, že v tabulce jsou všechny datové typy uspořádány od nejmenšího po největší.

datový typ bool

První v tabulce je datový typ bool celočíselný datový typ, protože rozsah platných hodnot je celá čísla od 0 do 255. Ale jak jste si již všimli, v závorkách je uvedeno Booleovský datový typ, a to je také pravda. Protože bool slouží výhradně k ukládání výsledků booleovských výrazů. Booleovský výraz může mít jeden ze dvou výsledků: true nebo false. true - pokud je logický výraz pravdivý, nepravda - pokud je logický výraz nepravdivý.

Protože ale rozsah platných hodnot datového typu bool je od 0 do 255, bylo nutné tento rozsah nějak sladit s logickými konstantami true a false definovanými v programovacím jazyce. Konstanta true je tedy ekvivalentní všem číslům od 1 do 255 včetně, zatímco konstanta false je ekvivalentní pouze jednomu celému číslu - 0. Uvažujme program využívající datový typ bool.

// data_type.cpp: Definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include pomocí jmenného prostoru std; int main(int argc, char* argv) ( bool boolean = 25; // proměnná typu bool s názvem boolean if (boolean) // podmínka operátoru if cout<< "true = " << boolean << endl; // выполнится в случае истинности условия else cout << "false = " << boolean << endl; // выполнится в случае, если условие ложно system("pause"); return 0; }

V řádek 9deklarovaná proměnná typu bool , který je inicializován na 25. Teoreticky pořádky 9, v booleovské proměnné by měla obsahovat číslo 25, ale ve skutečnosti tato proměnná obsahuje číslo 1. Jak jsem řekl, číslo 0 je nepravdivá hodnota, číslo 1 je pravdivá hodnota. Jde o to, že v proměnné jako bool může obsahovat dvě hodnoty - 0 (nepravda) nebo 1 (pravda). Kdežto pod datovým typem bool je přidělen celý bajt, což znamená, že proměnná typu bool může obsahovat čísla od 0 do 255. K určení nepravdivých a pravdivých hodnot jsou potřeba pouze dvě hodnoty 0 a 1 Vyvstává otázka: „K čemu je těch dalších 253 hodnot?

Na základě této situace jsme se dohodli, že použijeme čísla od 2 do 255 jako ekvivalent čísla 1, tedy pravdy. To je přesně důvod, proč booleovská proměnná obsahuje číslo 25 a ne 1. In řádky 10-13 deklarována, která přenáší kontrolu na provozovatele v řádek 11, pokud je podmínka pravdivá, a operátor v řádek 13, pokud je podmínka nepravdivá. Výsledek programu je na obrázku 1.

True = 1 Pokračujte stisknutím libovolné klávesy. . .

Obrázek 1 - datový typ bool

Typ dat char

Datový typ char je celočíselný datový typ, který se používá k reprezentaci znaků. To znamená, že každý znak odpovídá určitému číslu z rozsahu. Datovému typu char se také říká znakový datový typ, protože grafické znázornění znaků v C++ je možné díky char. Pro reprezentaci znaků v C++ je datovému typu char přidělen jeden bajt, jeden bajt obsahuje 8 bitů, poté dva umocníme na 8 a získáme hodnotu 256 – počet znaků, které lze zakódovat. Pomocí datového typu char tedy můžete zobrazit kterýkoli z 256 znaků. Všechny kódované znaky jsou zastoupeny v .

ASCII (American Standard Code for Information Interchange) je americký standardní kód pro výměnu informací.

Zvažte program používající datový typ char.

// symbols.cpp: Definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include pomocí jmenného prostoru std; int main(int argc, char* argv) ( symbol char = "a"; // deklarace proměnné typu char a její inicializace se symbolem "a" cout<< "symbol = " << symbol << endl; // печать символа, содержащегося в переменной symbol char string = "сайт"; // объявление символьного массива (строки) cout << "string = " << string << endl; // печать строки system("pause"); return 0; }

Takže dovnitř řádek 9proměnná s názvem symbol , je mu přiřazena hodnota symbolu"a" ( ASCII kód). V řádek 10 cout operátor vypíše znak obsažený v proměnné symbol V řádek 11deklaroval pole řetězců s názvemřetězec a velikost pole je určena implicitně. Řetězec je uložen v poli řetězců"webová stránka" . Vezměte prosím na vědomí, že když jsme uložili symbol do proměnné jako char , pak za rovnítko vložíme jednoduché uvozovky, do kterých jsme symbol napsali. Při inicializaci pole řetězců s určitým řetězcem jsou za rovnítko, ve kterém je zapsán určitý řetězec, umístěny dvojité uvozovky. Stejně jako běžný znak se řetězce vypisují pomocí operátoru cout , řádek 12. Výsledek programu je na obrázku 2.

Symbol = řetězec = web Chcete-li pokračovat, stiskněte libovolnou klávesu. . .

Obrázek 2 - datový typ char

Integer datové typy

K reprezentaci čísel se používají celočíselné datové typy. V tabulce 1 jich je šest: short int, unsigned short int, int, unsigned int, long int, unsigned long int . Všechny mají svou vlastní velikost paměti a rozsah akceptovaných hodnot. V závislosti na kompilátoru se velikost obsazené paměti a rozsah akceptovaných hodnot mohou lišit. V tabulce 1 jsou uvedeny všechny rozsahy akceptovaných hodnot a velikostí obsazené paměti pro kompilátor MVS2010. Kromě toho jsou všechny datové typy v tabulce 1 uspořádány v rostoucím pořadí podle velikosti obsazené paměti a rozsahu akceptovaných hodnot. Rozsah akceptovaných hodnot tak či onak závisí na velikosti obsazené paměti. V souladu s tím, čím větší je velikost obsazené paměti, tím větší je rozsah akceptovaných hodnot. Rozsah akceptovaných hodnot se také změní, pokud je datový typ deklarován s předponou bez znaménka. Předpona bez znaménka znamená, že datový typ nemůže ukládat hodnoty se znaménkem, pak se rozsah kladných hodnot zdvojnásobí, například datové typy short int a unsigned short int.

Celočíselné předpony datových typů:

krátký prefix zkracuje datový typ, na který je aplikován, tím, že zmenšuje velikost paměti, kterou zabírá;

dlouho prefix rozšiřuje datový typ, na který je aplikován, zvětšením velikosti paměti, kterou zabírá;

bez znaménka – předpona zdvojnásobuje rozsah kladných hodnot, zatímco rozsah záporných hodnot nelze v tomto datovém typu uložit.

Takže v podstatě máme jeden celočíselný typ pro reprezentaci celých čísel: datový typ int. Díky prefixům short, long, unsigned se objevuje určitá rozmanitost datových typů int, lišících se velikostí obsazené paměti a (nebo) rozsahem akceptovaných hodnot.

Datové typy s plovoucí desetinnou čárkou

V C++ existují dva typy dat s pohyblivou řádovou čárkou: float a double. Datové typy s pohyblivou řádovou čárkou jsou navrženy pro ukládání čísel s pohyblivou řádovou čárkou. Datové typy float a double mohou ukládat kladná i záporná čísla s pohyblivou řádovou čárkou. Datový typ float má paměťovou stopu, která je poloviční než u typu double, což znamená, že rozsah akceptovaných hodnot je také menší. Pokud je datový typ float deklarován s dlouhou předponou, pak se rozsah akceptovaných hodnot bude rovnat rozsahu akceptovaných hodnot typu double. V zásadě jsou datové typy s pohyblivou řádovou čárkou potřebné k řešení problémů s vysoce přesnými výpočty, například peněžními transakcemi.

Podívali jsme se tedy na hlavní body týkající se hlavních datových typů v C++. Zbývá pouze ukázat, odkud pocházejí všechny tyto rozsahy akceptovaných hodnot a velikosti obsazené paměti. A za tímto účelem vyvineme program, který vypočítá hlavní charakteristiky všech typů dat diskutovaných výše.

// data_types.cpp: Definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include // I/O manipulační knihovna #include // hlavičkový soubor matematických funkcí #include pomocí jmenného prostoru std; int main(int argc, char* argv) ( cout<< " data type " << "byte" << " " << " max value "<< endl // záhlaví sloupců <<"bool = " << sizeof(bool) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных bool*/ << (pow(2,sizeof(bool) * 8.0) - 1) << endl << "char = " << sizeof(char) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных char*/ << (pow(2,sizeof(char) * 8.0) - 1) << endl << "short int = " << sizeof(short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных short int*/ << (pow(2,sizeof(short int) * 8.0 - 1) - 1) << endl << "unsigned short int = " << sizeof(unsigned short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned short int*/ << (pow(2,sizeof(unsigned short int) * 8.0) - 1) << endl << "int = " << sizeof(int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных int*/ << (pow(2,sizeof(int) * 8.0 - 1) - 1) << endl << "unsigned int = " << sizeof(unsigned int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned int*/ << (pow(2,sizeof(unsigned int) * 8.0) - 1) << endl << "long int = " << sizeof(long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long int*/ << (pow(2,sizeof(long int) * 8.0 - 1) - 1) << endl << "unsigned long int = " << sizeof(unsigned long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных undigned long int*/ << (pow(2,sizeof(unsigned long int) * 8.0) - 1) << endl << "float = " << sizeof(float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных float*/ << (pow(2,sizeof(float) * 8.0 - 1) - 1) << endl << "double = " << sizeof(double) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных double*/ << (pow(2,sizeof(double) * 8.0 - 1) - 1) << endl; system("pause"); return 0; }

Tento program je zveřejněn, abyste si mohli prohlédnout charakteristiky datových typů ve vašem systému. Není třeba rozumět kódu, protože program používá řídicí příkazy, které pravděpodobně ještě neznáte. Pro povrchní seznámení s programovým kódem vysvětlím některé body níže. Operátor sizeof() Vypočítá počet bajtů přidělených pro datový typ nebo proměnnou. Funkce pow(x,y) povznáší význam x na mocninu y , tato funkce je dostupná z hlavičkového souboru . pevné a setprecision() manipulátory dostupné ze souboru záhlaví . První je opravený , předává hodnoty v pevné formě do výstupního proudu. Manipulátor setprecision(n) zobrazí n desetinná místa. Maximální hodnota určitého datového typu se vypočítá pomocí následujícího vzorce:

Max_val_type = 2^(b * 8 - 1) - 1; // pro datové typy se zápornými a kladnými čísly // kde b je počet bajtů přidělených v paměti pro proměnnou s tímto datovým typem // vynásobte 8, protože v jednom bajtu je 8 bitů // odečtěte 1 v závorce, protože čísla rozsahu musí být rozdělena na dvě pro kladné a záporné hodnoty // na konci odečtěte 1, protože rozsah čísel začíná od nuly // datové typy s předponou unsigned max_val_type = 2^(b * 8) - 1; // pro datové typy pouze s kladnými čísly // vysvětlení vzorce jsou podobné, pouze jednotka se neodečítá od závorky

Příklad fungování programu je vidět na obrázku 3. První sloupec ukazuje hlavní datové typy v C++, druhý sloupec ukazuje velikost paměti přidělené pro každý datový typ a třetí sloupec ukazuje maximální hodnotu, kterou odpovídající datový typ může obsahovat. Minimální hodnota je podobná maximální. Pro datové typy s předponou bez znaménka je minimální hodnota 0.

Datový typ byte max. hodnota bool = 1 255,00 char = 1 255,00 short int = 2 32767,00 unsigned short int = 2 65535,00 int = 4 2147483647,00 unsigned long int = 4 4 42954966,00 0 unsigned long int = 4 4294967295,00 float = 4 2147483647,00 double = 8 9223372036854775808.00 Chcete-li pokračovat, stiskněte libovolnou klávesu. . .

Obrázek 3 - Datové typy C++

Pokud je například proměnné typu short int přiřazena hodnota 33000, pak bitová mřížka přeteče, protože maximální hodnota v proměnné typu short int je 32767. To znamená, že do proměnné bude uložena nějaká jiná hodnota typu short int, s největší pravděpodobností bude záporný. Protože jsme se dotkli datového typu int, stojí za zmínku, že klíčové slovo int můžete vynechat a napsat například jen krátké . Kompilátor bude takový záznam interpretovat jako short int . Totéž platí pro předpony dlouhé a bez znaménka. Například:

// zkratka pro datový typ int short a1; // stejné jako short int long a1; // stejné jako long int unsigned a1; // stejné jako unsigned int unsigned short a1; // stejné jako unsigned short int




Nahoru