29. dubna 2004, 00.00 | Tento článek je určen těm, kdo v jazyce C příliš neprogramují a chtějí si jej zběžně oživit; snad i těm, kdo jsou zběhlí v jiných jazycích a snadno se dokáží adaptovat na mírně odlišnou syntaxi a sémantiku jiného programovacího jazyka.
Tento článek je určen těm, kdo v jazyce C příliš neprogramují a chtějí si jej zběžně oživit; snad i těm, kdo jsou zběhlí v jiných jazycích a snadno se dokáží adaptovat na mírně odlišnou syntaxi a sémantiku jiného programovacího jazyka. Pokud však někdo C nezná vůbec a k tomu není zkušeným programátorem, tento krátký přehled mu rozhodně nebude stačit: v takovém případě je zapotřebí si sehnat, přečíst a nastudovat některou z knih o jazyce C (nejraději některou z těch mých, samozřejmě ;)
Naopak zkušeným programátorům v "Céčku" tento díl nepřinese zhola nic, a mohou jej klidně přeskočit. Nebojte se, hned příště se pustíme do nadstavby Objective C, a ta již bude nová a zajímavá i pro vás. Zatím však prosím ještě mějte chvilku strpení s těmi, kdo v C zdaleka tak moc doma nejsou.
"Céčko" patří mezi nejjednodušší programovací jazyky vůbec; přesto se o něm dá napsat celá kniha. Zde ovšem neaspirujeme ani zdaleka na takovou důslednost – jen si stručně připomeneme základní principy a základní konstrukce jazyka, bez nároku na absolutní přesnost a úplnost. O rysech jazyka, jež se v Objective C běžně neužívají (kupříkladu unie či bitová pole) pak se nebudeme zmiňovat vůbec.
Co je nám vůbec do nějakého C?!?
Inu, to je jednoduché: Objective C je nadstavba jazyka C, libovolný korektní program v C je tedy zároveň korektním programem v Objective C (naopak to samozřejmě neplatí). To je ovšem zcela úmyslné, aby (a) se zkušení programátoři v C mohli Objective C naučit snadno a rychle (a jak uvidíme příště, sama nadstavba je opravdu velmi, velmi jednoduchá), (b) bylo snadné přenést do prostředí Objective C libovolný již existující kód či knihovnu.
Řada věcí se tedy v Objective C řeší přesně stejně jako v C: chceme naprogramovat podmínku, cyklus či využít celočíselné proměnné? Máme k dispozici příkazy if či for nebo deklaraci typu int přesně – skutečně úplně přesně – stejně, jako v klasickém jazyce C (tedy, "klasickém" – dnes překladač GNU ObjC++, užívaný v Mac OS X, podporuje dokonce i C99; na úrovni, na které se zde ale budeme jazykem zabývat, relativně nepodstatné rozdíly mezi variantami ANSI či C99 nejsou vůbec důležité).
V následujících několika odstavcích tedy "z rychlíku" projedeme nejdůležitější z vlastností jazyka C, s nimiž se v programech, psaných v Objective C běžně pracuje (všechny ostatní prvky C jsou samozřejmě také k dispozici; jen se jich užívá výjimečně – a my se jimi proto nebudeme zabývat).
Komentáře
Cokoli, co je mezi dvojicemi znaků /* ... */, nebo na řádku za dvojicí znaků //, se ignoruje.
Typy a proměnné
Základem každého jazyka jsou jeho příkazy a datové typy. Datové typy jazyka C jsou
- char, unsigned char, unichar, int, unsigned (int), long, unsigned long, long long, unsigned long long: celočíselné typy, všechny určují různě velká celé čísla, od jediného byte až po osmibytovou obludu long long. Modifikátor unsigned znamená číslo bez znaménka (libovolná hodnota je větší nebo rovna nule). Speciální typ unichar je dvoubytové číslo, běžně reprezentující jeden znak v kódu Unicode; namísto unsigned char lze používat pohodlnější zkratku u_char. Znak se v C vždy interpretuje jako odpovídající číslo v patřičné kódové tabulce; speciální typ "znak" tedy není zapotřebí, celočíselné typy char a unichar jej snadno nahradí. Není třeba také typ "boolean" – i na jeho místě lze užít libovolný jiný typ s konvencí "pravda==nenulová hodnota, nepravda==nula";
- float a double jsou typy užívané pro čísla s pohyblivou řádovou čárkou;
- speciální typ void znamená "nic";
- cokoli* je "ukazatel na cokoli": typ, representující adresu v paměti, na níž se nalézá hodnota typu "cokoli". Kupříkladu typ int* je tedy adresa, na níž je celé číslo; často se užívá void* pro adresu, k níž není žádná typová informace k dispozici.
Proměnné lze deklarovat uvedením jména typu a za ním seznamu jmen proměnných: "int a,b,c,d" deklaruje čtyři proměnné typu int. Modifikátor "*" pro ukazatel se týká pouze nejbližšího identifikátoru – "int *a,b,*c" tedy deklaruje dva ukazatele na int (a, c), a jeden prostý int (b). Deklarace se ukončuje středníkem a (v současné versi jazyka) může stát prakticky kdekoli.
Pro libovolný typ můžeme vytvořit nové jméno pomocí klíčového slova typedef – např. tedy "typedef int XXX" zajistí, že nadále bude XXX typ, ekvivalentní typu int; "typedef int *IntPtr" pak vytvoří typ IntPtr, representující ukazatel na int.
Konstanty a výrazy
Celočíselné konstanty jsou čísla v běžném desítkovém zápisu nezačíná-li nulou; pokud číslo obsahuje tečku nebo "e" (pro exponent) nebo obojí, jde o konstantu typu float či double. Celočíselné konstanty lze zapisovat i osmičkově (začínají-li nulou) nebo šestnáctkově (začínají-li znaky "0x" či "0X"). Znaková konstanta je prostě znak v apostrofech (třeba 'a').
Číselné výrazy lze spojovat pomocí standardních operátorů +, -, * (násobení), /, a jen pro celočíselné výrazy také % (zbytek po dělení). Pozor – dělení celočíselných výrazů je celočíselné, takže "5/3" je 1. Pro celá čísla lze použít také operátory & (operace AND bit po bitu), | (operace OR bit po bitu), ^ (operace XOR bit po bitu) a ~ (unární operátor, negace bit po bitu), << (posun levého operandu doleva o počet bitů, zadaný pravým operandem) a >> (posun doprava).
Unární operátor * je dereference ukazatele: je-li tedy např. "ip" ukazatel na proměnnou typu int, pak výraz "*ip" odpovídá obsahu této celočíselné proměnné.
Operátor = musí mít na levé straně proměnnou (či dereferenci ukazatele), a do ní uloží hodnotu výrazu na pravé straně. Celé přiřazení je výraz, jehož hodnotou je "to, co se přiřadilo" – díky tomu jsou korektní mj. i výrazy typu "a=(b=c+3)+1", kde se nejprve do proměnné "b" uloží součet proměnné "c" a 3, k tomu se přičte jednička, a výsledek se uloží do "a".
Pro pohodlí programátora jsou v C k dispozici kombinované operátory X=, jež užijí cílovou proměnnou zároveň jako prvý operand – kupříkladu "a+=3" je tedy totéž, jako "a=a+3"; "b<<=2" je totéž, jako "b=b<<2".
Nad proměnnými (a dereferencemi ukazatelů) máme k dispozici i speciální příkazy autoinkrementace a autodekrementace: je-li např. "i" proměnná, pak výraz "i++" má hodnotu "i", ale bezprostředně po jeho vyhodnocení se hodnota "i" o jedničku zvýší. Výraz "++i" má hodnotu "i", zvýšenou o 1. Obdobně je tomu s výrazy "--i" a "i--".
Operátor & může stát také jako unární před proměnnou; pak vytvoří ukazatel na tuto proměnnou (tj. deklarujeme-li např. "int i,*ip", můžeme do ukazatele ip uložit adresu proměnné i příkazem "ip=&i". Ukazatel vytvoří také speciální řetězcová konstanta: řada znaků v uvozovkách je typu char*, a hodnotou je adresa dané sekvence znaků v paměti. Speciální konstanta NULL je prázdný ukazatel – adresa, jež "neukazuje nikam" (samozřejmě, ukazuje na adresu 0, ta však je z definice nevalidní).
Speciální podmínkový operátor "v1?v2:v3" vyhodnotí výraz v1; je-li nenulový, je hodnotou celého výrazu v2, pokud byl nulový, je hodnotou celého výrazu v3. Náš překladač GNU C z Mac OS X navíc jako příjemné rozšíření oproti standardu umožňuje v2 vynechat – příkaz "v1?:v2" je ekvivalentní příkazu "v1?v1:v2".
Výrazy můžeme porovnávat pomocí operátorů ==, <=, >=, <, >, != (kde == zjišťuje rovnost, != nerovnost, a význam ostatních je zřejmý). Výsledkem je nula nebo nenula podle konvence "pravda==nenulová hodnota, nepravda==nula". Tyto výrazy pak můžeme spojovat do složitějších logických výrazů pomocí operátorů && (logické AND) a || (logické OR) a ! (logická negace).
Struktury
Více proměnných lze spojovat do bloků, jimž říkáme struktury. Jejich deklarace užívá klíčové slovo struct, za nímž je ve složených závorkách seznam všeho, co do struktury patří:
struct X { int a,b; float c; }
deklaruje strukturu jménem struct X (v praxi se častěji používá pojmenování pomocí výše zmíněného klíčového slova typedef), jež obsahuje dvě proměnné typu int a jednu typu float. K proměnným uvnitř struktury se přistupuje pomocí tečky – je-li kupříkladu foo proměnná typu struct X, pak "foo.b" je druhá z jejích celočíselných proměnných. V Cocoa se často struktury používají pro jednoduché složené hodnoty, např. "bod", složený z dvojice hodnot "x" a "y".
Příkazy
Libovolný výraz v C můžeme ukončit středníkem, a stane se příkazem. Libovolnou skupinu příkazů můžeme uzavřít do složených závorek, a stane se tzv. blokem – jednotkou, na niž můžeme pohlížet jako na celek (a např. rozhodnout, zda se celý blok příkazů provede nebo ne). Obecně může vždy blok stát kdekoli, kde může stát příkaz.
Kromě toho C nabízí několik málo speciálních příkazů:
if (výraz) then příkaz
if (výraz) then příkaz else příkaz2
Podmínkové příkazy; pokud je hodnota výrazu nenulová, provede se příkaz (a neprovede příkaz2, je-li uveden). Je-li výraz nulový, je tomu naopak.
while (výraz) příkaz
Výraz se vyhodnotí, a je-li nenulový, provede se příkaz. A tak pořád dokola, dokud není výraz nulový.
Velmi flexibilní je příkaz for – zápis
for (v1;v2;v3) příkaz
zhruba odpovídá následující sekvenci:
v1;
while (v2) {
příkaz
v3;
}
Běžně se užívá pro standardní cykly – např. "for (i=0;i<10;i++) foo" provede příkaz foo pro všechny hodnoty i od nuly do devíti –, avšak jeho flexibilita je velmi šikovná právě v Objective C: až se později seznámíme s třídami NSEnumerator či NSIndexSet, uvidíme, jak se nám bude obecnost příkazu for skvěle hodit.
V jazyce nízké úrovně je samozřejmostí klasický příkaz goto, který předá řízení na zadané návěští; návěštím je libovolný identifikátor, ukončený dvojtečkou.
Speciálním případem, který se velmi podobá využití goto, je příkaz switch; ten má jeden celočíselný argument, a předá řízení na návěští case N, kde N musí odpovídat skutečné hodnotě argumentu. Pro opuštění příkazu switch je nutné použít příkazu break:
switch (n) {
case 0: // následující kód se provede, je-li n nulové
...
case 1: // následující kód se provede je-li n==1
// a také je-li n nulové, protože jsme nepoužili break
...
break;
case 2: // následující kód se provede jen je-li n==2
...
}
V příkazu switch může být i speciální návěští default, jež znamená "jakákoli jiná hodnota".
Funkce a struktura programu
Příkazy lze sdružovat do funkcí: funkce je pojmenovaný blok (viz výše), jenž může užívat formálních parametrů. Navíc funkce buď vrací hodnotu nějakého typu (pomocí speciálního příkazu return), nebo je typu void. Funkce se deklaruje tak, že nejprve uvedeme návratový typ, pak jméno funkce, a za ním v závorkách seznam parametrů. Za seznamem je blok, jenž popisuje vlastní obsah funkce:
int max(int a,int b) {
if (a>b) return a;
return b;
}
Funkci můžeme kdykoli volat tím, že uvedeme její jméno a na místech parametrů konkrétní hodnoty – např. "i=max(2,3)".
Program v klasickém jazyce C se skládá z libovolného množství funkcí, mezi nimiž je jedna výjimečná; ta má jméno main a je deklarována speciálním způsobem. Po zavedení do paměti se automaticky spustí právě funkce main, a ta pak podle potřeby volá funkce ostatní.
Preprocesor
Jazyk C standardně obsahuje velmi výkonný preprocesor; my si z něj ukážeme jen ty nejzákladnější služby:
#define IDENTIFIKATOR cokoli
v následujícím kódu nahradí všechny výskyty řetězce IDENTIFIKATOR (jež nejsou v uvozovkách) řetězcem cokoli. Definice maker lze i parametrizovat, takže např. namísto funkce max z minulého odstavce jsme mohli definovat makro
#define MAX(a,b) ((a)>(b)?(a):(b))
jež by fungovalo (téměř) stejně dobře (existuje určitý drobný rozdíl, avšak na úrovni, na níž se pohybujeme, není podstatný).
Speciální direktiva include vloží zadaný soubor; to je proto, aby více různých zdrojových souborů mohlo snadno užívat společných deklarací:
#include <jméno_souboru>
#include "jméno_souboru"
V Objective C se na místě této direktivy používá důsledně direktiva import, jež funguje podobně, je však "chytřejší" v případech, kdy by se při vzájemném vkládání souborů mohlo stát, že by se jeden soubor interpretoval vícekrát za sebou. Objective C však není naše dnešní téma: do něj se podrobně ponoříme hned příště!