Vážení zákazníci, dovolujeme si Vás upozornit, že na tuto ukázku knihy se vztahují autorská práva, tzv. copyright. To znamená, že ukázka má sloužit výhradnì pro osobní potøebu potenciálního kupujícího (aby ètenáø vidìl, jakým zpùsobem je titul zpracován a mohl se také podle tohoto, jako jednoho z parametrù, rozhodnout, zda titul koupí èi ne). Z toho vyplývá, že není dovoleno tuto ukázku jakýmkoliv zpùsobem dále šíøit, veøejnì èi neveøejnì napø. umis ováním na datová média, na jiné internetové stránky (ani prostøednictvím odkazù) apod. redakce nakladatelství BEN technická literatura redakce@ben.cz
14 UKAZATELE A ØETÌZCE
V této kapitole budou probrány zbývající datové typy jazyka C: ukazatel a øetìzec. 14.1 UKAZATEL (POINTER) Obsahem promìnné typu ukazatel není hodnota èísla, znaku nebo nìco podobného, ale adresa jiné promìnné. Definice ukazatele Pro definici ukazatele se používá symbol *. Níže je uveden pøíklad definice promìnných i, j, p. Promìnné i, j jsou obyèejná celá èísla (typ int), p je typu int* (ukazatel na celoèíselnou promìnnou). Také mùžeme definovat nový typ ukazatel (zde je oznaèen pint): LQWLSM W\SHGHILQWSLQW Obr. 14.1 Definice ukazatele Unární operátor & (reference) Unární operátor & získá adresu promìnné. Zapisuje se v prefixové podobì, tedy &x. Tento operátor mùžeme použít na libovolnou promìnnou (každá promìnná má svou adresu v pamìti). Zápis p = &i získá adresu promìnné i a uloží ji do ukazatele p. Øíkáme: p ukazuje na i. Viz obr. 14.2. W\SHGHILQWSLQW W\SXND]DWHOQDFHOpþtVOR LQWL M SLQWS S L SMHXND]DWHO SREVDKXMHDGUHVXSURP QQpL SXND]XMHQDL FHORþtVHOQpSURP QQpLM S SDP L M Obr. 14.2 Pøíklad použití operátoru reference & Unární operátor * (dereference) Unární operátor * zajistí pøístup k promìnné pøes ukazatel. Zapisuje se v prefixové podobì, tedy *p. Tento operátor lze použít pouze na promìnné typu ukazatel. 162 C pro mikrokontroléry ATMEL AT89S52 A
S S QDVWDYSURP QQRXS HVXND]DWHOQDQL ]P DGUHVXXORåHQRXYXND]DWHOLS QHO]HQHQtW\SXLQW S SDP L M Obr. 14.3 Pøíklad použití operátoru dereference * Typ void* Obvyklé je používat ukazatel stejného typu, jakého je promìnná, na kterou ukazuje. Pokud ale vyžadujeme adresu bez ohledu na typ dat, mùžeme použít tzv. obecný ukazatel typu void*. Ovšem vzhledem k tomu, že typ void nemá urèenu velikost, nelze ukazatel typu void* dereferencovat! Hodnota NULL Existují situace, kdy potøebujeme stanovit, že ukazatel neobsahuje adresu žádné promìnné. Pro tyto pøípady je definován symbol NULL, který pøedstavuje adresu hodnoty 0. Nulovou adresu nemùže mít žádná promìnná. Když ukazatel obsahuje hodnotu NULL, bereme to tak, že není nastaven na žádnou promìnnou. Symbol NULL je definován napøíklad v hlavièkovém souboru stdlib.h. Velikost ukazatelù Jako každá jiná promìnná, je ukazatel uložen do pamìti. Mìlo by nás tedy zajímat, kolik bajtù v pamìti zabírá. Zajímavé je, že všechny ukazatele mají stejnou velikost, která není závislá na jejich typu. Tato velikost je urèena adresovacími schopnostmi použitého mikrokontroléru. Vzhledem k šíøi adresové sbìrnice (16 bitù) je jasné, že ukazatele jsou realizovány jako 2bajtové promìnné. Platí to i pro typ void*. Volání parametrù funkce pøes ukazatel Jedno ze základních použití ukazatelù spoèívá ve volání parametrù pøes ukazatel. Takto lze realizovat parametry funkcí, které lze mìnit. Jako první pøíklad si uvedeme funkci vymena, která má dva parametry typu ukazatel na celé èíslo. Jedná se o funkci, která má zajistit výmìnu obsahu dvou celoèíselných promìnných. Viz obr. 14.4. Pro pøípad funkce, do které se parametry pøedávají pøes ukazatel, musí být formální parametry typu ukazatel daného typu. V našem pøíkladu jsou parametry oznaèeny pa a pb a jsou typu int*. Pro vlastní výmìnu je nutné mít jednu pomocnou promìnnou typu celé èíslo, je definována jako c. Výmìna probíhá takto: n hodnotu první promìnné uložíme do c, pøístup k první promìnné pøes ukazatel pa obdržíme pomocí operátoru dereference (tedy *pa), A C pro mikrokontroléry ATMEL AT89S52 163
n n nyní již mùžeme obsah první promìnné (*pa) pøepsat hodnotou druhé promìnné (*pb), nakonec do druhé promìnné (pøístup je pøes *pb) uložíme pùvodní hodnotu první promìnné (c). YRLGY\PHQDLQWSDLQWSE ^ SDP LQWF SRPRFQiSURP QQi F SD SD SE SE F ` LQWD E Y\PHQD D E DGUHVDD W\SXND]DWHOQDFHOpþtVOR XORåSDGRF XORåSEGRSD XORåFGRSE DGUHVDE SD SE D E F """ S HG YêP QRX SD SE SDP D E F SR YêP Q Obr. 14.5 Funkce soucet a její použití Všimnìte si, jaký je význam promìnných pa, pb, a, b. Promìnné a, b jsou celá èísla a mají svou adresu v pamìti. Promìnné pa, pb na nì ukazují. Pøed výmìnou ukazuje pa na a, pb na b. Po výmìnì se situace nezmìní (pa stále ukazuje na a, pb na b), pouze obsahy promìnných a, b jsou vzájemnì vymìnìny. Všimnìte si také, že pøi volání funkce vymena musíme pøedat adresy promìnných a, b (uloží se do ukazatelù pa, pb). Adresa promìnné je získána pomocí operátoru &. Souvislost ukazatele s polem Název pole pøedstavuje ukazatel na jeho zaèátek (prvek s indexem 0). Adresu libovolného prvku lze získat pomocí operátoru reference (&). Výše uvedené informace lze zužitkovat, pokud zapisujeme funkci s parametrem typu pole. Pole bude pøedáváno pomocí ukazatele. Jelikož uvnitø funkce již nelze velikost pole zjistit (uvnitø pole máme pouze informaci o adrese pole, nikoli o poli samotném; i když mùžeme pøistupovat k jeho prvkùm), musíme ještì pøedávat maximální poèet prvkù pole. 164 C pro mikrokontroléry ATMEL AT89S52 A
Jako pøíklad si uvedeme funkci soucet, která stanoví souèet hodnot všech prvkù pole celých èísel. Prvním parametrem je ukazatel typu int*, druhým parametrem je celé èíslo poèet prvkù pole. Viz obr. 14.5. DGUHVDSROH LQWVRXFHWLQWSLQWQ ^ LQWVXPD LQWL IRUL LQL VXPD S>L@ S LþWLSUYHN SRþHWSUYN UHWXUQVXPD ` LQWSROH>@ LQWV V VRXFHWS YUDFtVRXþHW Qi]HYSROH MHMHKRDGUHVRX SRþHWSUYN Obr. 14.5 Funkce soucet a její použití Práce s polem uvnitø funkce je velmi jednoduchá. Dokonce nemusíme používat ani operátor dereference k prvkùm pøistupujeme indexováním. Také pøi volání funkce soucet, je vše také snadné. Neobjeví se dokonce ani zápis operátoru reference (název pole je rovnou jeho adresou). 14.2 ØETÌZEC Øetìzec je pole znakù, se kterými se pracuje najednou. Øetìzec je reprezentován jako jednorozmìrné pole znakù, èili jako typ: char[ ]. V souvislostmi s pomocnými funkcemi se bude objevovat i zápis char*. Již ale víme, že název pole odpovídá jeho adrese. Pøipomeòme, že pole se v jazyce C++ indexuje vždy od nuly. To znamená, že první znak øetìzce je uložen v prvku s indexem 0. Další znaky jsou pak uloženy v následujících prvcích. A C pro mikrokontroléry ATMEL AT89S52 165
Délka øetìzce není stanovena pøímo, ale pomocí tzv. zarážky. Zarážka je znak s ASCII kódem 0, tedy '\0'. Jedná se o znak, který nelze zobrazit na výstupním zaøízení nebo jej pøeèíst ze vstupního zaøízení. Pøi definici øetìzce je tøeba uvážit, že zarážka samotná zabírá také jednu pozici. Pokud chceme, aby promìnná r typu øetìzec obsahovala text AT89S52, musíme ji definovat jako alespoò 8prvkové pole znakù. Viz obr. 14.6. U $ 7 6? DGUHVD Obr. 14.6 Jednotlivé znaky øetìzce r Øetìzcový literál a inicializace øetìzce Øetìzcový literál se zapisuje mezi uvozovky (pøipomeòme, že znakový literál se zapisuje mezi apostrofy). Literál je chápán jako celý øetìzec, to znamená, že za poslední znak je automaticky pøipojena zarážka. Inicializaci øetìzce pøi definici je možno provádìt podobnì, jako u prostého pole: char r[20]="at89s52"; Také mùžeme využít skuteènosti, že pøekladaè poèet znakù spoèítá (nyní je fyzická délka 8 prvkù): char r[]="at89s52"; Poslední variantou je využití souvislosti pole s ukazatelem. Níže uvedený zápis je rovnìž možný, nepreferujeme jej však: char *r="at89s52"; Operace pøiøazení (=) není pro øetìzec definována. Takže pøiøadit hodnotu lze pouze v rámci inicializace. Jinak musíme používat funkci strcpy. Podobnì operátory <, <=, >, >= také nejsou definovány. Jejich použití vede k porovnání adres øetìzcù. Korektní porovnání musíme provést pomocí funkce strcmp. Pomocné funkce pro práci s øetìzci Pomocné funkce pro práci s øetìzci jsou k dispozici v podobì hlavièkového souboru string.h. Zde je seznam nejpoužívanìjších funkcí vèetnì krátkého popisu: n unsigned strlen(const char *s) vrátí délku øetezce s, n char* strcpy(char *dest, const char *src) zkopíruje znaky øetìzce src do øetìzce dest, n char* strcat(char *dest, const char *src) spojí dva øetìzce, tedy pøipojí øetìzec src za øetìzec dest, 166 C pro mikrokontroléry ATMEL AT89S52 A
n char* strchr(const char *s, int c) vyhledá první výskyt znaku c v øetìzci s, pokud je znak nalezen, vrátí jeho adresu, pøi nenalezení vrátí NULL, n char* strstr(const char *s1, const char *s2) vyhledá první výskyt podøetìzce s2 v øetìzci s1, pokud je podøetìzec nalezen, vrátí jeho adresu, pøi nenalezení vrátí NULL, n int strcmp(const char *s1, const char *s2) porovná abecednì dva øetìzce: je-li s1<s2, vrátí výsledek <0, je-li s1==s2, vrátí výsledek 0, je-li s1>s2, vrátí výsledek >0. Ukázka použití øetìzcových funkcí: LQFOXGHVWULQJK! KODYLþNRYêVRXERUVWULQJK YRLGPDLQ ^ FKDUU>@ FKDUU>@ FKDUU>@ LQWG GHILQLFHW t HW ]F VWUFS\UPLNURNRQWUROHU VWUFS\U$76 UREVDKXMHPLNURNRWUROHU UREVDKXMH$76 G VWUOHQU VWUFS\UU VWUFDWU VWUFDWUU G VWUFPSUU ` GMHGpOND HW ]FHUWHG\ UREVDKXMHPLNURNRQWUROHU UREVDKXMHPLNURNRQWUROHUPH]HUD UREVDKXMHPLNURNRQWUROHU$76 GMHYêVOHGHNSRURYQiQtUU WHG\GSURWRåHUMHDEHFHGQ S HGU A C pro mikrokontroléry ATMEL AT89S52 167