V zßv∞reΦnΘ Φßsti povφdßnφ o novinkßch, kterΘ p°inesla druhß verze standardu ISO jazyka C, bude °eΦ p°edevÜφm o ukazatelφch, polφch, funkcφch a makrech. PovÜimneme si vÜak i standardnφ knihovny a implementacφ novinek v b∞₧n²ch p°ekladaΦφch.
Ukazatele
V deklaraci ukazatele na objekt a v typech odvozen²ch lze nynφ pou₧φt modifikßtor restrict. (Termφn objekt zde znamenß jenom oblast pam∞ti slou₧φcφ k uklßdßnφ dat, nikoli pojem z objektov∞ orientovanΘho programovßnφ.) Po strßnce syntaxe se tento modifikßtor podobß modifikßtor∙m const a volatile; standard jazyka ho °adφ spolu s nimi do skupiny tzv. typov²ch kvalifikßtor∙. Nap°φklad deklaracφ
int * restrict rpa;
zavßdφme "restringovan²" ukazatel na typ int.
V²znam
Mezi objektem, s nφm₧ pracujeme pomocφ restringovanΘho ukazatele, a tφmto ukazatelem je vztah, kter² vy₧aduje, aby se p°i ka₧dΘm - p°φmΘm nebo nep°φmΘm - p°φstupu k onomu objektu vyu₧φvala hodnota danΘho ukazatele. Jestli₧e nap°. pou₧ijeme ukazatel rpa deklarovan² v p°edchozφm odstavci a p°i°adφme mu adresu pam∞ti alokovanΘ pomocφ funkce malloc(),
rpa = malloc(N*sizeof(int));
vznikne takov²to vztah mezi rpa a alokovanou pam∞tφ. P°i jakΘkoli manipulaci s alokovanou pam∞tφ bychom m∞li pou₧φvat pouze ukazatel rpa a hodnotu tohoto ukazatele bychom nem∞li p°i°azovat jin²m ukazatel∙m. TakΘ zm∞na hodnoty restringovan²ch ukazatel∙ je omezena; restringovan² ukazatel by m∞l v jednom bloku ukazovat stßle na stejn² objekt.
Modifikßtor restrict tedy znamenß, ₧e k objektu, kter² takov²to ukazatel zp°φstup≥uje, nep°istupujeme jin²m zp∙sobem, pomocφ jin²ch ukazatel∙. Pokud toto pravidlo poruÜφme, m∙₧e se program chovat nedefinovan²m zp∙sobem.
Smyslem vztahu vytvo°enΘho modifikßtorem restrict je umo₧nit p°ekladaΦi optimalizace, kterΘ by jinak nebyly mo₧nΘ s ohledem na aliasing, tj. modifikaci hodnot objekt∙ prost°ednictvφm jin²ch ukazatel∙.
P°φklady
Nßsledujφcφ p°φklady jsou p°evzaty p°φmo ze standardu ISO/IEC 9899:1999. Deklarujeme-li na ·rovni souboru ukazatele
int * restrict a;
int * restrict b;
extern int c[];
tvrdφme tφm, ₧e p°istoupφme-li k n∞jakΘmu objektu prost°ednictvφm jednoho z t∞chto ukazatel∙ a je-li tento objekt n∞kde v programu m∞n∞n, nebudeme k n∞mu p°istupovat prost°ednictvφm zb²vajφcφch dvou.
Tφm se zavazujeme, ₧e p°i ₧ßdnΘm volßnφ tΘto funkce nebude ukazatel p p°istupovat ke stejnΘmu objektu jako ukazatel q.
Tento p°φklad ukazuje ob∞ protikladnΘ strßnky pou₧φvßnφ modifikßtoru restrict. Jeho v²hodou je, ₧e umo₧≥uje p°ekladaΦi provΘst efektivnφ anal²zu zßvislostφ funkce f(), ani₧ by musel zkoumat vÜechna jejφ volßnφ v programu, a podle toho optimalizovat jejφ k≤d. OvÜem nic nenφ zadarmo. Toto zkoumßnφ se - alespo≥ zΦßsti - p°enßÜφ na bedra programßtora, kter² si musφ p°i ka₧dΘm volßnφ uv∞domit, zda pou₧itΘ parametry nepovedou k nedefinovanΘmu chovßnφ. Nap°φklad volßnφ
extern int A[100]; f(50, A, A+50); // OK
je v po°ßdku, zatφmco volßnφ
f(50, A, A+1); // NEDEFINOVAN╔ CHOV┴N═
m∙₧e zp∙sobit nedefinovanΘ chovßnφ programu. ProblΘm je v tom, ₧e s prvky A[1] a₧ A[49] pole A pracujeme v rßmci jedinΘho volßnφ funkce f() jak pomocφ restringovanΘho ukazatele p, tak i pomocφ restringovanΘho ukazatele q.
Na druhΘ stran∞ k objektu, kter² se v danΘm bloku nem∞nφ, lze p°istupovat i prost°ednictvφm n∞kolika restringovan²ch ukazatel∙. Definujeme-li nap°φklad funkci
void h(int n, int * restrict p, int
* restrict q, int * restrict r)
{
int i;
for(i = 0; i < n; i++)
p[i] = q[i]+r[i];
}
je volßnφ
h(100, a, b, b); // OK
zcela v po°ßdku, pokud a a b ukazujφ na r∙znß pole. Pole b se toti₧ ve funkci h() nem∞nφ, a proto nevadφ, ₧e k n∞mu p°istupujeme prost°ednictvφm dvou r∙zn²ch restringovan²ch ukazatel∙.
Hodnota restringovanΘho ukazatele by se nem∞la "vynßÜet" ven z bloku, v n∞m₧ byl ukazatel definovßn. OvÜem toto pravidlo mß svΘ v²jimky. Nßsledujφcφ p°φklad je v po°ßdku:
typedef struct {
int n;
double * restrict v
} vektor;
vektor Novej(int n)
{ vektor V;
V.n = n;
V.v = malloc(sizeof (double)*n);
return V;
}
P°φkazy
DrobnΘ, nikoli vÜak bezv²znamnΘ zm∞ny se dotkly i n∞kter²ch p°φkaz∙. Podφvejme se na jejich p°ehled.
P°φkaz if se chovß jako blok. P°φkazy, kterΘ jsou jeho souΦßstφ (tj. p°φkaz za if, a p°φpadn∞ i p°φkaz za else), p°edstavujφ takΘ samostatnΘ bloky, a to bez ohledu na to, zda jsou zapsßny ve slo₧en²ch zßvorkßch.
P°φkaz switch se chovß jako samostatn² blok.
Skok pomocφ p°φkazu goto nesmφ vΘst dovnit° bloku s dynamicky modifikovan²m typem (nap°. s polem o prom∞nnΘ dΘlce).
P°φkazy cyklu se chovajφ jako samostatnΘ bloky. TakΘ t∞lo cyklu p°edstavuje blok bez ohledu na to, zda je zapsßno ve slo₧en²ch zßvorkßch.
V inicializaΦnφ Φßsti p°φkazu for m∙₧eme deklarovat prom∞nnΘ s pam∞¥ovou t°φdou auto nebo register (podobn∞ jako v C++).
Poslednφ bod znamenß, ₧e jsme cyklus for v deklaraci funkce h() o n∞kolik odstavc∙ v²Üe mohli takΘ zapsat takto:
for(int i = 0; i < n; i++)
p[i] = q[i]+r[i];
Prom∞nnß i je lokßlnφ v t∞le p°φkazu for; m∙₧eme ji vedle inicializaΦnφ Φßsti pou₧φt v podmφnce, v reinicializaci a v t∞le tohoto p°φkazu. Po opuÜt∞nφ t∞la cyklu zanikne, nebo¥ p°φkaz for p°edstavuje blok.
Pole
Mo₧nosti deklarace pole v jazyce C novß norma podstatn∞ rozÜφ°ila:
Lokßlnφ pole m∙₧e mφt "prom∞nnou" dΘlku.
Ve specifikaci index∙ pole p°edßvanΘho jako parametr se m∙₧e objevit modifikßtor static nebo kvalifikßtor typu.
Specifikaci indexu pole p°edßvanΘho jako parametr lze nahradit znakem *.
Prom∞nnß dΘlka pole
Pole s prom∞nnou dΘlkou jsou pole, u nich₧ nenφ poΦet prvk∙ v deklaraci vyjßd°en konstantnφm v²razem. TakovΘ pole lze deklarovat pouze jako lokßlnφ nestatickou prom∞nnou. ╪eΦeno slovy standardu, je-li identifikßtor pole deklarovßn jako objekt se statickou dobou ₧ivota, nesmφ mφt prom∞nnΘ meze. Identifikßtor pole s prom∞nn²mi mezemi nesmφ mφt definovßn zp∙sob sestavovßnφ (linkage). Nem∙₧e to takΘ b²t slo₧ka struktury nebo unie. (Omezenφ je tedy spousta; i tak je to ale u₧iteΦnß novinka.)
Podφvejme se na p°φklad. Nßsledujφcφ konstrukce je sprßvnß, nebo¥ A deklarujeme jako lokßlnφ automatickou prom∞nou:
void f(int n)
{
int A[n+7];
// ...
int k = sizeof(A);
// ...
}
V r∙zn²ch volßnφch funkce f() bude mφt pole A r∙znou dΘlku; operßtor sizeof v tΘto situaci vrßtφ skuteΦnou velikost pole. (To znamenß, ₧e se bude v tΘto situaci vyhodnocovat dynamicky, za b∞hu programu, nikoli v dob∞ p°ekladu.)
Na druhΘ stran∞, nenφ-li m konstanta, bude deklarace
extern int B[m]; // CHYBA
nesprßvnß, a¥ se objevφ kdekoli, nebo¥ p°edepisuje poli B externφ sestavovßnφ, a to v p°φpad∞ pole s prom∞nnou dΘlkou nelze.
Jestli₧e v deklaraci pole vynechßme specifikaci poΦtu prvk∙, dostaneme deklaraci ne·plnΘho typu (incomplete type).
Hv∞zdiΦka v indexu
Uvedeme-li mφsto specifikace poΦtu prvk∙ znak *, dostaneme specifikaci pole prom∞nnΘ dΘlky s neznßm²m poΦtem prvk∙. Takovou deklaraci smφme pou₧φt jen v prototypu funkce. (Poznamenejme, ₧e takov²to typ se pova₧uje za kompletnφ, tedy pln∞ definovan².) Nßsledujφcφ prototypy jsou podle standardu kompatibilnφ:
double Min(int m, int n, double a[m][n]);
double Min(int m, int n, double a[*][*]);
double Min(int m, int n, double a[ ][*]);
double Min(int m, int n, double a[ ][n]);
Modifikßtor static v indexu
V deklaraci pole ve specifikaci parametr∙ funkce m∙₧eme v indexov²ch zßvorkßch pou₧φt klφΦovΘ slovo static. Tφm slibujeme, ₧e p°i ka₧dΘm volßnφ tΘto funkce bude skuteΦn²m parametrem nenulov² ukazatel na prvnφ prvek pole, je₧ bude mφt alespo≥ tolik prvk∙, kolik je jich uvedeno v deklaraci.
Nap°φklad
void f(double C[static 3]);
je prototyp funkce, kterΘ je t°eba p°i ka₧dΘm volßnφ p°edat jako skuteΦn² parametr ukazatel na prvnφ prvek pole s alespo≥ t°emi prvky typu double.
Kvalifikßtory typu v indexu
Parametry funkcφ deklarovanΘ jako pole se i podle novΘ verze standardu p°edßvajφ jako ukazatele na prvnφ prvek. To znamenß, ₧e deklarujeme-li jako parametr pole typu T, p°ebere si to p°ekladaΦ jako kvalifikovan² ukazatel na typ T. P°itom kvalifikßtory si vezme ze specifikace index∙. To znφ nejspφÜ nesrozumiteln∞, nicmΘn∞ p°φklad nßm ukß₧e, ₧e nejde o nic slo₧itΘho. Nßsledujφcφ prototypy funkcφ jsou podle standardu kompatibilnφ:
void f(double (* restrict a)[5]);
void f(double a[restrict][5]);
void f(double a[restrict 3][5]);
Prvnφ prototyp mß jako parametr restringovan² ukazatel na pole o p∞ti prvcφch typu double, tak₧e skuteΦn²m parametrem budou dvourozm∞rnß pole. DalÜφ dva prototypy majφ jako parametr pole, p°ekladaΦ si ho ovÜem p°ebere jako ukazatel uveden² v prvnφm prototypu. Podobn² v²znam bude mφt i prototyp
void f(double a[restrict static 3][5]);
kter² ovÜem navφc slibuje, ₧e p°i ka₧dΘm volßnφ bude skuteΦn²m parametrem nenulov² ukazatel na prvnφ z nejmΘn∞ t°φ polφ o p∞ti prvcφch typu double.
Funkce
O n∞kter²ch zm∞nßch, kterΘ se t²kajφ funkcφ, jsme ji₧ hovo°ili v p°edchozφm oddφlu v∞novanΘm polφm, v prvnφ Φßsti Φlßnku jsme se takΘ zmφnili o p°eddefinovanΘm identifikßtoru __func__.
Je zajφmavΘ, ₧e i novß norma stßle p°ipouÜtφ definici funkce podle Kernighana a Ritchieho, i kdy₧ ji oznaΦuje za zastaralou.
Modifikßtor inline
Jednou z v²razn²ch novinek je mo₧nost pou₧φt v deklaraci funkce modifikßtor inline. Jeho v²znam je podobn² jako v C++. Deklaracφ
inline void f() { /* ... */ }
°φkßme, ₧e volßnφ tΘto funkce mß b²t co nejrychlejÜφ. To m∙₧e znamenat, ₧e se t∞lo funkce dosadφ na mφsto volßnφ (podobn∞ jako p°i rozvoji maker), standard nicmΘn∞ nep°edepisuje, jak mß b²t rychlosti volßnφ dosa₧eno.
Modifikßtor inline lze pou₧φt pro jakoukoli funkci s vnit°nφm sestavovßnφm (internal linkage).
Pro funkce s modifikßtorem inline platφ mj. nßsledujφcφ omezenφ:
Funkce nesmφ obsahovat definici statickΘ lokßlnφ prom∞nnΘ a nesmφ takovΘ prom∞nnΘ pou₧φvat.
Definice funkce s modifikßtorem inline nep°edstavuje externφ definici, tak₧e v jinΘ samostatn∞ p°eklßdanΘ Φßsti programu ji lze definovat jinak.
V²pustka
Jak znßmo, v jazyce C m∙₧eme deklarovat funkce s prom∞nn²m poΦtem parametr∙; tuto skuteΦnost specifikujeme pomocφ v²pustky (...). Nßstroje pro prßci s v²pustkou najdeme v hlaviΦkovΘm souboru stdarg.h.
P°ipome≥me si, ₧e k zφskßnφ parametr∙ p°edan²ch na mφst∞ v²pustky slou₧φ typ va_list a makra va_start, va_arg a va_end. Novinkou je makro va_copy(va_list dest, va_list src), kterΘ umo₧≥uje kopφrovat obsah prom∞nnΘ typu va_list do jinΘ prom∞nnΘ tΘho₧ typu.
Preprocesor
Preprocesoru se t²kajφ dv∞ novinky: mo₧nost definovat makra s prom∞nn²m poΦtem parametr∙ a zavedenφ standardnφch direktiv #pragma.
Makra
V definici maker s parametry m∙₧eme nynφ specifikovat prom∞nn² poΦet parametr∙. Podobn∞ jako v p°φpad∞ funkcφ k tomu poslou₧φ v²pustka (...), kterß musφ b²t v seznamu formßlnφch parametr∙ uvedena jako poslednφ.
S parametry makra p°edan²mi na mφst∞ v²pustky zachßzφme jako s celkem pomocφ identifikßtoru __VA_ARGS__.
V implementaci vyhovujφcφ novΘmu standardu jsou #definovßna mj. makra __STDC_VERSION__, __STDC_IEC_559__ a __STDC_ISO_10646__.
Prvnφ z nich se rozvine v konstantu tvaru yyyymmL (nap°. 199901L), vyjad°ujφcφ rok a m∞sφc vydßnφ verze standardu, jemu₧ tato implementace odpovφdß. (Tato konstanta byla zavedena v dodatku ISO/IEC 9899:1990/AMD1 jako 199409L.) DruhΘ makro se rozvine v konstantu 1, pokud implementace vyhovuje standardu IEC 60559 pro poΦφtßnφ s reßln²mi Φφsly. T°etφ makro se rozvine v konstantu tvaru yyyymmL (nap°. 199712L), kterß vyjad°uje rok a m∞sφc vydßnφ standardu ISO/IEC 10646 pro UNICODE (vΦetn∞ dodatk∙ a oprav), jφm₧ se danß implementace °φdφ.
Standardnφ #pragma
SouΦßstφ standardu je nynφ n∞kolik direktiv #pragma. VÜechny majφ tvar #pragma STDC jmΘno p°epφnaΦ P°itom jmΘno je n∞kter² z identifikßtor∙ FP_CONTRACT, FENV_ACCESS nebo CX_LIMITED_RANGE a p°epφnaΦ je ON, OFF nebo DEFAULT. S prvnφmi dv∞ma jmΘny jsme se v naÜem povφdßnφ ji₧ setkali, t°etφ se t²kß pou₧itφ komplexnφch Φφsel. VÜechny zapφnajφ (ON) nebo vypφnajφ (OFF) urΦit² zp∙sob zpracovßnφ, pop°. nastavujφ implicitnφ stav (DEFAULT).
Prßce se znaky
Vzhledem k tomu, ₧e standard jazyka C nynφ poΦφtß nejen s "obyΦejn²mi", tedy jednobajtov²mi znaky, ale i s vφcebajtov²mi znaky a s k≤dovßnφm UNICODE ("Üirok²mi" znaky - wide characters), p°ibyly v knihovn∞ funkce pro prßci se znakov²mi °et∞zci slo₧en²mi z t∞chto znak∙. NovΘ jsou takΘ konverznφ funkce pro vzßjemnΘ p°evody r∙zn²ch druh∙ °et∞zc∙. Nßstroje pro prßci s vφcebajtov²mi znaky jsou obsa₧eny p°edevÜφm v hlaviΦkov²ch souborech wchar.h a wctype.h.
V souboru wchar.h najdeme mj. funkce pro p°evod °et∞zce se Üirok²mi znaky na Φφslo - wcstod(), wcstof(), wcstold() atd., dßle funkce pro kopφrovßnφ °et∞zc∙ se Üirok²mi znaky (wcscpy() a n∞kterΘ dalÜφ), pro spojovßnφ t∞chto °et∞zc∙ (wcscat() a dalÜφ) atd. Pro porovnßvßnφ °et∞zc∙ podle lokßlnφch zvyklostφ (p°esn∞ji podle kategorie LC_COLLATE v nastavenφ lokßlnφch zvyklostφ) slou₧φ funkce wcscoll().
V tΘto knihovn∞ najdeme analogie prakticky vÜech funkcφ pro prßci s "obyΦejn²mi" °et∞zci. V∞tÜina jmen t∞chto funkcφ vznikla ze jmen analogick²ch funkcφ pro obyΦejnΘ °et∞zce p°ipojenφm p°edpony wc; Φasto ale muselo b²t p∙vodnφ jmΘno zkrßceno, nebo¥ auto°i tΘto knihovny se sna₧ili zachovat maximßln∞ osmiznakovΘ identifikßtory.
Pro p°evod jednobajtov²ch znak∙ na vφcebajtovΘ slou₧φ funkce wint_t btowc(int c); poznamenejme, ₧e wint_t je celoΦφseln² typ schopn² pojmout krom∞ znak∙ takΘ hodnotu WEOF, kterß p°edstavuje "Üirokou" analogii konstanty EOF. Obrßcen² p°evod zajistφ funkce int wctob(wint_t c). Pro p°evody mezi vφcebajtov²mi a Üirok²mi znaky jsou urΦeny funkce wcrtomb() a mbrtowc().
Funkce a makra pro klasifikaci Üirok²ch znak∙ najdeme v souboru wctype.h. Jde nap°. o funkci iswalnum(), je₧ urΦφ, zda jde o alfanumerick² znak, towupper(), kterß p°evede malΘ pφsmeno na velkΘ atd.
DatovΘ proudy
TakΘ v prßci se vstupy a v²stupy pomocφ datov²ch proud∙ se pochopiteln∞ odrazilo zavedenφ vφcebajtov²ch a Üirok²ch znak∙. Ka₧d² datov² proud mß svoji orientaci (na ÜirokΘ nebo ·zkΘ znaky). Bezprost°edn∞ po otev°enφ je proud bez orientace; tu zφskß bu∩ p°i prvnφ vstupnφ nebo v²stupnφ operaci, nebo volßnφm funkce fwide().
Nßstroje pro vstupy a v²stupy se Üirok²mi znaky najdeme v hlaviΦkovΘm souboru wchar.h. Jde p°edevÜφm o funkci fwprintf(), je₧ obstarßvß "Üirokoznakov²" formßtovan² v²stup, a o funkci fwscanf()pro formßtovanΘ Φtenφ ze souboru se Üirok²mi znaky. Prßce s nimi je podobnß jako s jejich "·zkoznakov²mi" analogiemi. Dßle zde najdeme funkce swprintf() a swscanf() pro zßpis do Üirokoznakov²ch °et∞zc∙, pro Φtenφ z nich apod. Z nßzv∙ nßsledujφcφch funkcφ jist∞ poznßte, oΦ jde, nebo¥ se od sv²ch "·zkoznakov²ch" analogiφ liÜφ jen n∞jak²m tφm w navφc: fgetwc(), fgetws(), fputwc(), getwc(), putwchar(), ungetwc(). (V²Φet nenφ ·pln².)
DalÜφ zm∞ny
Z dalÜφch drobn²ch zm∞n stojφ za zmφnku p°emφst∞nφ deklarace funkcφ sprintf() a sscanf() z hlaviΦkovΘho souboru string.h do stdio.h. Novinkou je takΘ konverze %A (p°φp. %a), je₧ umo₧≥uje v²stup reßln²ch Φφsel pomocφ funkce fprintf() a podobn²ch v ÜestnßctkovΘ soustav∞ (s dvojkov²m exponentem vyznaΦen²m pomocφ P, resp. p). Nov∞ jsou ve specifikacφch konverzφ takΘ zavedeny specifikace dΘlky L (pro typ long double), ll (pro typ long long), hh (pro typ signed char nebo unsigned char), j (pro typ intmax_t nebo uintmax_t), z (pro typ size_t) a t (pro typ ptrdiff_t).
Pßr slov na zßv∞r
Od vydßnφ tohoto standardu uplynuly ji₧ t°i roky. P°esto se nejrozÜφ°en∞jÜφ p°ekladaΦe jazyka C pro pΘcΘΦka - p°ekladaΦe, kterΘ jsou souΦßstφ v²vojov²ch prost°edφ pro C a C++, nap°. Visual Studio .NET nebo Borland C++Builder 6 - k n∞mu zatφm jen blφ₧φ. Implementujφ nßstroje pro prßci se Üirok²mi znaky a n∞kterΘ dalÜφ drobnosti, jako je modifikßtor inline u funkcφ. Neobsahujφ vÜak zatφm nap°. pole prom∞nnΘ dΘlky, pojmenovanΘ inicializßtory, komplexnφ Φφsla a dalÜφ mo₧nosti.
D∙vod je, domnφvßm se, pom∞rn∞ jednoduch². Hlavnφ ·silφ v²vojov²ch t²m∙ t∞chto nßstroj∙ se soust°edilo na shodu s nov²m standardem jazyka C++, tak₧e jazyk C z∙stal jaksi v pozadφ.
I kdy₧ byl p°ehled novinek ve standardu jazyka C pom∞rn∞ rozsßhl², nebyl zcela vyΦerpßvajφcφ. NicmΘn∞ pokud jste si z n∞j odnesli alespo≥ povÜechn² p°ehled o mo₧nostech, kterΘ novß norma nabφzφ a kterΘ se bezpochyby Φasem objevφ v nov²ch verzφch p°ekladaΦ∙, splnil sv∙j ·Φel.