V úvodním dílu začínajícího výukového seriálu o jazyku C se Kosťa mj. věnoval celočíselným typům. Jak je vidět v následné diskuzi, problematika si zaslouží doplnění.

Děkuji za doplnění, to vypadá, že se to C snad už konečně naučím

Před dalším výkladem bohužel musíme udělat ještě menší odbočku.

Preprocesor
Vlastní jazyk C byl od počátku navrhován jako celkem minimalistický. Proto je těsně spjat s další nástrojem, tzv. preprocesorem. Ten slouží zejména ke třem účelům:
  • Vkládání části kódu z jiného souboru (#include).
  • Nahrazování části kódu předdefinovaným textem (#define) – makra.
  • Podmíněnému překladu části kódu (#if/#else/#endif).
Preprocesor se spouští před vlastním překladem. Meziprodukt je modifikovaný zdrojový kód, kde jsou všechna známá makra nahrazena svou definicí a z něhož jsou vypuštěny části, pro které podmínky příkazu #if nejsou splněny.

Pro definice (sdílených) odvozených typů a pro deklarace sdílených objektů se používají tzv. hlavičkové soubory jež mají běžně příponu .h. Norma jazyka předepisuje sadu standardních datových typů a knihovních objektů, jež jsou dostupné právě přes normalizovanou množinu hlavičkových souborů. Na tyto standardní soubory se odkazuje pomocí složených závorek:

#include <stdlib.h>

Uživatelsky definované jsou dostupné pomocí jména uzavřeného v uvozovkách:

#include "mojedeklarace.h"

Je zvykem nezapisovat konstanty přímo do kódu, ale pomocí symbolických jmen, maker. Výskyt těchto maker ve zdrojovém kódu preprocesor nahradí obsahem definice makra. Kromě parametrizace za překladu je častým místem definice maker právě hlavičkový soubor. Proto jsou symbolické konstanty podstatnou částí standardních hlavičkových souborů.

Takto je jazyk rozšířen o normalizované datové typy a o operace implementované ve standardní knihovně, které by měly být součástí každého překladače.

Preprocesor toho umí ještě mnohem více, ale to zas nechám pro další pokračování.

PS: Je třeba striktně rozlišovat deklaraci a definici objektu (proměnné/funkce), ale to jistě také bude součástí některého z budoucích dílů.

Typ char
Jedním z uvedených celočíselných typů je char. Jedná se opravdu o běžný numerický typ a jako s jinými čísly, i s objekty tohoto typu lze provádět běžné aritmetické aj. operace.

Typ long long int
Tento typ navazuje přirozeně na již uvedenou množinu celočíselných typů. Je reprezentován nejméně 64 bity a samozřejmě může být označen modifikátory signed, resp. unsigned. Konstanty se zapisují zdvojením modifikátoru L (např. 0x1234ULL).

Typ _Bool
K opačnému konci patří tento typ – jeho minimální šířka je jeden bit.

signed vs. unsigned
U celočíselných typů, pro které existují obě varianty, mají tyto varianty stejnou velikost (šířku). Norma přitom nepředepisuje způsob implementace čísla se znaménkem.

Celočíselné znakové konstanty
Pro zápis znakových konstant je možno použít tyto konstrukce:
  • Jeden nebo více znaků zapsaných v apostrofech, např. 'a'. Takto lze zapsat většinu běžných znaků ve zdrojovém kódování. Výjimkou jsou řídicí znaky (v ASCII mají kódy 0…31d) a některé další.
  • Zápis pomocí tzv. escape sekvence uzavřené v apostrofech:
    • Jednoduchá sekvence pro viditelné znaky: \' \" \? \\. Výsledkem je znak za obráceným lomítkem a umožňuje zapsat apostrof, uvozovky a další znaky.
    • Jednoduchá sekvence pro řídicí znaky: \a \b \f \n \r \t \v pro alert, backspace, new line, carriage return, horizontal tab a vertical tab.
    • Šestnáctková \x<řada hexadecimálních číslic> a osmičková \<řada číslic v osmičkové soustavě> pro přímé zadání numerického kódu znaku. Číslice jsou načítány, dokud vyhovují zápisu čísla v příslušné soustavě. Hodnota musí být v rozsahu daném příslušným typem (viz níže).(Text uvedený <kurzívou v lomených závorkách> je zástupný popis a skobky nejsou součástí výsledného zápisu.)

    Příklady úplného zápisu:
    '\''
    '\n'
    '\x40'
    '\0377'
  • Zápisu v apostrofech může předcházet jeden z modifikátorů L, u nebo U. V tom případě se jedná o vícebajtové kódování znaků; wide char pro L nebo 16- a 32bitové znaky (v uvedeném pořadí), např. u'Hi'. Kromě uvedeného se objevují i varianty s prefixem u8 explicitně označujícím kódování UTF-8, případně jako escape sekvence ve tvaru \u/\U.

    Pozn.: V této oblasti však nemám kromě obligátní formy L'A' (resp. ve variantě string) z Win32 příliš praktických zkušeností, možná to někdo doplní či opraví.
Normální znakové konstanty jsou typu int, prefixy vedou k typům wchar_t (ze <stddef.h>), resp. char16_t nebo char32_t z <uchar.h>.

Interpretace znakových konstant, jejichž hodnota přesahuje příslušný typ (např. 'AB'), jsou implementačně závislé.

Standardní odvozené celočíselné typy
Tato oblast prodělala od vytvoření jazyka C počátkem 70. let minulého století nemalý vývoj. V mezifázi se objevovaly například pokusy používat typy BYTE či WORD, ale ty také nenesou jednoznačný údaj o šířce daného typu, který je nutný pro mnohá použití (tvorba embedded aplikací je zářným příkladem).

Dříve uvedené typy (plus opomenutý [signed|unsigned] long long int) jsou typy vestavěné do dnešní formy jazyka. Pomocí zatím nepoznaných mechanizmů (klíčové slovo typedef) lze z nich samozřejmě vytvářet i uživatelské typy.

Sadu užitečných celočíselných typů nabízí standardní hlavičkový soubor <stdint.h>. Tyto typy spadají do několika kategorií:
  • Typy s přesnou šířkou jsou tvořeny podle schématu intN_t, kde N představuje počet bitů, kterými je typ reprezentován.
  • Typy s minimální zadanou šířkou (int_leastN_t).
  • Nejrychlejší typy s minimální šířkou (int_fastN_t).
  • Typ pro uložení ukazatele (adresy) intptr_t.
  • Nejširší typ (intmax_t).
Tyto typy také mají variantu s prefixem u pro označení čísla bez znaménka.

Příklady:
int8_t
uint24_t
uint_least8_t

Kromě uvedeného jsou také definována makra, poskytující informace o rozsahu příslušných typů:
  • INTN_MIN, INTN_MAX, UINTN_MAX
  • INT_LEASTN_MIN, INT_LEASTN_MAX, UINT_LEASTN_MAX
  • INT_FASTN_MIN, INT_FASTN_MAX, UINT_FASTN_MAX
  • INTPTRN_MIN, INTPTRN_MAX, UINTPTRN_MAX
  • INTMAXN_MIN, INTMAXN_MAX, UINTMAXN_MAX
Tento výčet není úplný, více lze najít v dokumentaci nebo normě. K dispozici jsou také makra pro generování konstant příslušného typu.

Informace o standardních typech (int, …) poskytuje soubor <limits.h>.

Upozornění: Text neprošel jazykovou ani faktickou korekturou. Za případné chyby se omlouvám.

Odkazy: