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á slouit 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ø. umisováním na datová média, na jiné internetové stránky (ani prostøednictvím odkazù) apod. redakce nakladatelství BEN technická literatura
[email protected]
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): LQWL SM W\SHGHILQW SLQW 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 (kadá 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\SHGHILQW SLQW W\SXND]DWHOQDFHOpþtVOR LQWL M FHORþtVHOQpSURP
SDP QQpLM
SLQWS SMHXND]DWHO SREVDKXMHDGUHVXSURP QQpL SXND]XMHQDL S L Obr. 14.2
S
L M
Pøíklad pouití 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
Obr. 14.3
SDP
QDVWDYSURP QQRXS HVXND]DWHOQDQL
]P
DGUHVXXORåHQRXYXND]DWHOLS QHO]HQHQtW\SXLQW
S
L M
Pøíklad pouití operátoru dereference *
Typ void* Obvyklé je pouívat ukazatel stejného typu, jakého je promìnná, na kterou ukazuje. Pokud ale vyadujeme adresu bez ohledu na typ dat, mùeme pouít tzv. obecný ukazatel typu void*. Ovem 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 kadá jiná promìnná, je ukazatel uloen do pamìti. Mìlo by nás tedy zajímat, kolik bajtù v pamìti zabírá. Zajímavé je, e vechny ukazatele mají stejnou velikost, která není závislá na jejich typu. Tato velikost je urèena adresovacími schopnostmi pouité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 pouití 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 naem 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
nyní ji mùeme obsah první promìnné (*pa) pøepsat hodnotou druhé promìnné (*pb),
n
nakonec do druhé promìnné (pøístup je pøes *pb) uloíme pùvodní hodnotu první promìnné (c). W\SXND]DWHOQDFHOpþtVOR
YRLGY\PHQDLQW SDLQW SE ^ SDP LQWF SRPRFQiSURP QQi XORå SDGRF F SD SD SE XORå SEGR SD XORåFGR SE SE F ` LQWD E Y\PHQD D E DGUHVDD
Obr. 14.5
SD SE
D E
SDP SD SE
D E
F """
F
S HG YêP QRX
SR YêP Q
DGUHVDE
Funkce soucet a její pouití
Vimnì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. Vimnì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 zuitkovat, 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 jetì 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 vech 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
SRþHWSUYN
LQWVRXFHWLQW SLQWQ ^ LQWVXPD LQWL IRUL LQL VXPD S>L@ S LþWLSUYHN UHWXUQVXPD ` LQWSROH>@ LQWV V VRXFHWS Qi]HYSROH MHMHKRDGUHVRX
Obr. 14.5
YUDFtVRXþHW
SRþHWSUYN
Funkce soucet a její pouití
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 ve 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 vdy od nuly. To znamená, e první znak øetìzce je uloen v prvku s indexem 0. Dalí znaky jsou pak uloeny 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 mono 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 vyuití souvislosti pole s ukazatelem. Níe uvedený zápis je rovnì moný, nepreferujeme jej vak: char *r="AT89S52"; Operace pøiøazení (=) není pro øetìzec definována. Take 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 pouití 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 pouití øetìzcových funkcí:
LQFOXGHVWULQJK! YRLGPDLQ ^ FKDUU>@ FKDUU>@ FKDUU>@ LQWG
KODYLþNRYêVRXERUVWULQJK
GHILQLFHW t HW ]F
VWUFS\UPLNURNRQWUROHU VWUFS\U$76
UREVDKXMHPLNURNRWUROHU UREVDKXMH$76
G VWUOHQU
GMHGpOND HW ]FHUWHG\
VWUFS\UU VWUFDWU VWUFDWUU
UREVDKXMHPLNURNRQWUROHUPH]HUD
G VWUFPSUU `
A
UREVDKXMHPLNURNRQWUROHU UREVDKXMHPLNURNRQWUROHU$76 GMHYêVOHGHNSRURYQiQtUU WHG\GSURWRåHUMHDEHFHGQ S HGU
C pro mikrokontroléry ATMEL AT89S52
167