Close

Celočíselné datové typy, výčtový typ enum

Arduino celočíselné datové typy (char, short, int, long, long long, unsigned)

V seriálu O programování obecně se zabýváme různými zákoutími programování nejen Arduina. V minulém článku jsme si popsali různé číselné soustavy. V dnešním článku a několika dalších na toto téma navážeme a řekneme si něco o datových typech.


Počítače a procesory, které je řídí, jsou věci poměrně hloupé. Samy o sobě nic neumí a pokud po nich něco chceme, musíme jim to popsat způsobem, kterému rozumí. K tomu slouží programovací jazyky, o kterých jsem psal již ve článku Paradigmata programovacích jazyků, historie. Součástí těchto jazyků je i domluva o tom, jakým způsobem budeme reprezentovat různé hodnoty, například čísla. Hovoříme poté o tzv. datových typech, což je předpis, jak se jednotlivé hodnoty zapisují, jak se ukládají do paměti a také jaké operace s nimi můžeme provádět.

Programovací jazyky jsou různé. Co jazyk, to jiná množina datových typů. Některé jazyky dokonce nevyžadují, abychom datové typy uváděli, nebo u nich nic jako je datový typ vůbec nenajdeme. My se budeme zabývat jazykem C, potažmo C++, ve kterém programujeme i naše Arduino. S datovými typy jste se jistě už potkali, ale nebude na škodu si do datových typů vnést trochu systém.

Datový typ přiřazujeme jednotlivým proměnným, popřípadě můžeme mít funkce, které vracejí hodnotu daného datového typu. Pokud zapisujeme hodnotu přímo (například: 100), mluvíme o takzvaných literálech. I u nich říkáme, že jsou jistého datového typu. Taková malá ukázka na začátek:

char a = 'A';
unsigned char b = 100;
int cislo = 20;
double d = 10.2;
int soucet(int a, int b) { return a + b; }

Číselné datové typy

Máme dvě skupiny číselných datových typů. Jsou to celá čísla a reálná čísla. Ty se dále dělí podle jejich rozsahu hodnot, kterých mohou nabývat, a tedy i místa, které zabírají v paměti.

Celočíselné datové typy v C/C++

V jazycích C a C++ najdeme několik datových typů pro uchovávání celých čísel, které se liší svojí velikostí, tedy místem, které zabírají v paměti. Nejčastěji se setkáme s datovými typy char, shortintlong, long long. Tyto typy dále dělíme na znaménkové a bezznaménkové. Pokud zapíšeme char/short/int/long, máme na mysli jeho znaménkovou variantu. Pokud chceme používat bezznaménkovou variantu, musíme před jméno datového typu přidat slovo unsigned. Pokud bychom chtěli uvést znaménkovou variantu, můžeme před číslo zapsat signed. Je to ale zbytečné, protože jsou tyto typy znaménkové ve výchozím stavu.

O velikosti typů platí, že char má velikost 1 byte. Typ long long by měl mít velikost minimálně 64 bitů, tedy 8 bytů. Velikost short, int a long není pevně daná. Norma udává jenom vztah mezi nimi:

short ≤ int ≤ long

Jejich skutečná velikost závisí mimo jiné i na architektuře použitého procesoru a také na překladači. Jejich velikost můžeme zjistit pokusem. V C/C++ existuje operátor, který vrací velikost jeho operandu v bytech. Je to operátor sizeof(x) – zápis sizeof(char) nám vrátí hodnotu 1, protože char má velikost 1 byte. Můžeme si proto na našem Arduinu pustit program, který vypíše velikosti známých datových typů. Pro char například takto:

Serial.print("char: ");
Serial.println(sizeof(char));

Abychom se neupsali, vytvořil jsem jednoduché makro INFO, které dostane jeden parametr – datový typ – a vypíše na sériovou linku jeho jméno a velikost. Více o makrech si povíme jindy. Dnes nám stačí vědět, že ho použijeme stejně, jako funkci. Program, který vypíše informace o představených datových typech ve všech kombinacích tedy může vypadat následovně:

#define INFO(type)              \
do {                            \
  Serial.print(#type ": ");     \
  Serial.println(sizeof(type)); \
} while(0);

void setup() {
  Serial.begin(9600);

  Serial.println("----------INFO----------");
  INFO(char);
  INFO(unsigned char);
  INFO(signed char);
  
  INFO(short);
  INFO(unsigned short);
  INFO(signed short);
  
  INFO(int);
  INFO(unsigned int);
  INFO(signed int);
  
  INFO(long);
  INFO(unsigned long);
  INFO(signed long);
  
  INFO(long long);
  INFO(unsigned long long);
  INFO(signed long long);
}

void loop() {}

Když program pustím na své desce Arduino Mega ADK, dostanu následující výpis:

----------INFO----------
char: 1
unsigned char: 1
signed char: 1
short: 2
unsigned short: 2
signed short: 2
int: 2
unsigned int: 2
signed int: 2
long: 4
unsigned long: 4
signed long: 4
long long: 8
unsigned long long: 8
signed long long: 8

Za zmínku stojí u Arduina také typy boolean, byte a word. Tyto typy pro nás „vytvořili“ tvůrci Arduina.

  • Boolean může nabývat dvou logických hodnot: true/false. Pokud zavoláme INFO(boolean), zjistíme, že má také jeden byte.
  • Byte je stejný, jako unsigned char
  • Word je stejný, jako unsigned int

Specifické vlastnosti má typ char. Ten se totiž používá pro uchovávání znaků. Znaky ale nejsou nic jiného, než čísla, kterým podle předem dohodnuté tabulky udělujeme význam. Tato tabulka se nazývá ASCII a nalezneme ji například zde. Zjistíme z ní, že znak ‚@‘ má hodnotu 64. Proto si můžeme následujícím způsobem vypsat tento znak po sériové lince:

char a = 64;
Serial.println(a);

Pokročilým pro zamyšlení: Zkuste si spustit následující kód. Jak se výsledek liší oproti předchozímu a proč se vypisuje to, co se vypisuje?

int a = 64;
Serial.println(a);

Rozsah datových typů

tedy hodnoty, kterých proměnné daného typu mohou nabývat

Pokud máme proměnnou o velikosti 1B (byte), tvoří ji osm bitů (jedniček či nul). Pomocí osmi bytů můžeme vyjádřit 2hodnot, což je 256. Pokud nechceme používat záporná čísla, použijeme typ unsigned char a můžeme tak použít hodnoty od 0 do 255 (celkem 256 různých hodnot. Pokud záporná čísla potřebujeme, použijeme char, u kterého můžeme použít hodnoty -128 až 127. Jedná se tedy o 128 záporných hodnot, 127 kladných hodnot a nula. Celkem tedy opět 256 hodnot.

Obecně platí, že do bezznaménkový typ může nabývat hodnot z intervalu

<0; 2n-1>

a znaménkový typ může nabývat hodnot z:

<-2n-1;2n-1-1>

kde n je počet použitelných bitů pro rezrezentací čísla.

V následující tabulce najdete všechny dnes probrané datové typy, společně s jejich velikostí a rozsahem hodnot, kterých mohou nabývat.

signed unsigned
char velikost 1 byte (8 bitů)
rozsah  -128 až 127 0 až 255
short velikost 2 byte (16 bitů)
rozsah -32 768 až 32 767 0 až 65 535
int velikost 2 byte (16 bitů)
rozsah -32 768 až 32 767 0 až 65 535
long velikost 4 byte (32 bitů)
rozsah -2 147 483 648 až -2 147 483 647 0 až 4 294 967 295
long long velikost  8 byte (64 bitů)
rozsah −9 223 372 036 854 775 808

9 223 372 036 854 775 807
0 až 18 446 744 073 709 551 615

Výčtový typ enum

Enum je taková specialita. Jeho pomocí si vytvoříme podmnožinu datového typu int. Tímto si zdánlivě omezíme hodnoty, kterých může nějaká proměnná nabývat. Tyto hodnoty si navíc pojmenujeme. Nejlepší bude si vše ukázat na příkladu.

enum stavy {
  OFF,
  ON
} stav1;

enum stavy stav = ON; // vytvoříme si proměnnou výčtového typu stav a uložíme do ni hodnotu OFF

Nyní máme proměnnou typu enum stavy. Do ní můžeme přiřazovat dvě konstanty – ON a OFF.

Jak už jsem ale zmínil – jedná se o podmnožinu datového typu int, takže i jednotlivé hodnoty budou pouhá čísla. Pokud si například vypíšeme konstantu ON po sériové lince, získáme hodnotu 1, pro OFF dostaneme hodnotu 0. Pokud bychom do enumu přidali ještě další konstanty, měly by hodnotu 2, 3, 4 atd. Čísla odpovídají pořadí, v jakém jsou uvedené při definici, počínaje nulou. Pokud bychom chtěli hodnoty změnit, můžeme to udělat následovně:

enum pismena {
  A,
  B,
  C = 20,
  D,
  E = 30
};

Přičemž platí, že jakmile už jednou „počitadlo“ posuneme, pokračuje dále od nastavené hodnoty, tedy bude platit, že A = 0, B = 1, C = 20, D = 21, E = 30. Pokud se chcete o enum dozvědět více, přečtěte si tento povedený článek.

 

To by bylo k celočíselným datovým typům vše. Máte-li dotazy, nebojte se zeptat v komentářích. Datové typy uchovávající reálná čísla a další si probereme v následujících článcích.

Zbyšek Voda

Napsat komentář