LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

> C/C++ (15) - Proměnné

V dnešním díle probereme podrobněji proměnné. Ukážeme si, jak se definují ukazatele na funkce a na co jsou dobré. Dojde na modifikátory proměnných a to i v souvislosti s projekty z více souborů.

23.2.2005 15:00 | Jan Němec | Články autora | přečteno 44524×

Funkce jako typ a proměnná

Funkci již umíme definovat a volat, umíme také deklarovat jejich hlavičky. Céčko navíc umožňuje definovat ukazatel na funkci. Jedná se skutečně o ukazatel do paměti, označuje místo v kódu, kde začíná přeložená funkce. S ukazatelem na funkci lze pracovat jako s běžnou proměnnou, můžeme do něj přiřadit nějakou hodnotu nebo jej předat jako parametr funkce, lze také vypsat jeho hodnotu. Pokud architektura umožňuje číst programu vlastní kód (běžně ano), můžeme ukazatel na funkci přetypovat na ukazatel na unsigned char a naučit se strojový kód. Pamětníci z dob MS-DOSu zavzpomínají i na sebemodifikaci spuštěného kódu, ale v chráněném módu na Linuxu podobné pokusy skončí špatně. Především však lze funkci, na niž ukazatel míří zavolat.

/* p je ukazatel na funkci s parametrem typu const char *
   a vracející int */
int (* p)(const char *);

/* Identifikátor funkce bez kulatých závorek znamená
   ukazatel na funkci */
p = puts;
printf("Funkce puts je v paměti na adrese %p.\n", (void *) p);
/* Ukazatel na puts lze normálně zavolat */
p("Ahoj světe!");

Nejsložitější na celém příkladu je určitě konstrukce typu ukazatele. Nejdřív se píše typ návratové hodnoty funkce, potom v kulatých závorkách hvězdička a identifikátor - jméno definovaného ukazatele a nakonec následují opět v kulatých závorkách typy parametrů funkce. Všimněte si, že název funkce je v C kódu pouze konstantní ukazatel na tuto funkci. Volání funkce přes ukazatel, definovaný jako proměnná, je už stejné, jako běžné volání funkce.

Ukazatele na funkce se nejčastěji používají v souvislosti s knihovnami, kdy je třeba nějakým dostatečně obecným způsobem parametrizovat algoritmus z knihovny. Pomocí ukazatele na funkci lze říci nějaké GUI knihovně, co se má stát při stisknutí tlačítka (zavolat uživatelskou funkci předanou jako parametr). Časově náročné funkce z knihoven občas umožní zadat jako parametr funkci, která se bude jednou za čas během výpočtu volat. Například programátor obecně užitečné knihovny na násobení velkých matic neví, jakým způsobem (a zda vůbec) informovat uživatele během výpočtu. To ví až aplikační programátor. Proto programátor knihovny přidá jako poslední parametr funkce na násobení matic ukazatel na funkci (tu už píše aplikační programátor), která se zavolá po vypočtení každého prvku výsledné matice. V grafické aplikaci se pak může pohybovat teploměr od 0% ke 100%, v interaktivním konzolovém programu se bude vypisovat na standardní výstup atd., ačkoli oba programy používají stejnou knihovnu.

#include <stdio.h>
/* Tohle je jako časově náročná funkce z knihovny */
void NapisStoTecek(void (* callback)(int)) {
  int i;

  for (i = 0; i < 100; i++) {
    /* Jednou za deset teček zavolej parametr */
    if (!(i % 10)) callback(i);
    /* Vypisuj tečky */
    putchar('.');
  }
}

/* Funkce volaná z knihovny, ale definovaná v aplikačním kódu */
void parametr(int procent) {
  printf("\nHotovo na %i%%\n", procent);
}

/* Hlavní program */
int main(void) {
  NapisStoTecek(parametr);
  return 0;
}

Modifikátory proměnných

Když nepočítáme parametry funkcí, zná Céčko dva druhy proměnných. Lokální, definované uvnitř funkce, a globální definované mimo funkci. Lokální proměnná má vyhrazené místo na zásobníku, a je tedy svázána nejen s konkrétní funkcí, ale přímo s její instancí. V případě rekurze tak pro každé volání funkce vzniknou i instance jejích lokálních proměnných a přiřazení do lokální proměnné proto ovlivní hodnotu pouze příslušné instance. Toto chování lze změnit klíčovým slovem static. Lokální proměnná definovaná jako static má vždy jen jednu instanci uloženou stejně jako globální proměnné, navíc její případná inicializace v definici proběhne jen jednou.

void funkce(void) {
  static int i = 0;

  printf("Celkový počet volání funkce = %i\n", ++i);
}

U globálních proměnných má static poněkud jiný význam. Jedná se o omezení platnosti proměnné na příslušný modul. Z ostatních souborů je pak tato proměnná nedostupná, jde tedy o jakousi ochranu identifikátorů podobně jako v případě static funkcí.

Globální proměnná z jiného zdrojového souboru se zpřístupní pomocí extern. Pokud v jednom souboru definujeme

int pocet;

můžeme tuto proměnnou používat z jiného souboru, pokud v něm deklarujeme

extern int pocet;

Jde tedy jen (podobně jako v případě hlaviček funkcí) o jakousi informaci pro překladač, že pocet je známý identifikátor, proměnná typu int, která je definovaná někde jinde. Pokud v celém projektu není nikde definovaná bez extern, ale je používaná, projde vlastní překlad jednotlivých souborů, ale při linkování dojde k chybě.

Pokud nám záleží na efektivitě programu, můžeme označit lokální proměnnou jako register.

void funkce(void) {
  register int i;
  /* ... */
}

Je to doporučení pro překladač, aby umístil tuto proměnnou do registru procesoru a nikoli na zásobník. Práce s registry je pochopitelně podstatně rychlejší. Překladač může toto doporučení ignorovat (u dnešních překladačů běžné) a může také proměnnou dát do registru sám od sebe (i to je obvyklé). Označení register má tedy smysl pro dobrovolně spolupracující překladače se špatnou optimalizací, například staré dosovské verze Borland C++.

Překladač se obvykle pokouší kód zjednodušit tak, aby na cílovém procesoru běžel co nejrychleji. V tom mu podstatně pomáhá znalost hodnot proměnných, například po

i = 5;

může předpokládat, že v proměnné i bude skutečně hodnota 5 a to až do nějakého příkazu, který to může změnit. Problém nastane u programů s obsluhou asynchronních událostí (signály) nebo více vlákny. Cyklus

int i = 0;

while (!i);

tj. čekej, dokud signál nebo jiné vlákno nezmění hodnotu i, smí překladač "vyoptimalizovat" na

while (1);

tj. čekej do nekonečna. Z tohoto důvodu existuje modifikátor volatile, který překladači zakazuje provádět jakékoli předpoklady o hodnotě proměnné.

volatile int i = 0;

while (!i);

Teď je příklad správný, pouze poněkud neefektivní, neboť aktivní čekání zbytečně vytíží procesor.

Modifikátor const již známe. Jde o označení proměnných, které nelze jednoduchým způsobem měnit. Nejedná se však o skutečné konstanty, neboť je nelze použít např. při definici mezí polí. Tento modifikátor je rozumné použít zejména u vstupních parametrů funkce předávaných pomocí ukazatelů. Je-li parametrem funkce const char *, je zřejmé, že funkce nemá v úmyslu řetězec měnit. Užití const tak zvyšuje čitelnost programu.

Pokračování příště

V příštím dílu se naučíme psát hlavičkové soubory.

Verze pro tisk

pridej.cz

 

DISKUZE

Pokračování......... ??? 4.3.2005 11:43 Herfik
  L Re: Pokračování......... ??? 4.3.2005 15:45 Jan Němec




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

28.11.2018 23:56 /František Kučera
Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1

12.11.2018 21:28 /Redakce Linuxsoft.cz
22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář

6.11.2018 2:04 /František Kučera
Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

4.10.2018 21:30 /Ondřej Čečák
LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář

18.9.2018 23:30 /František Kučera
Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

9.9.2018 14:15 /Redakce Linuxsoft.cz
20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business. Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář

12.8.2018 16:58 /František Kučera
Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář

16.7.2018 1:05 /František Kučera
Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

31.7.2023 14:13 / Linda Graham
iPhone Services

30.11.2022 9:32 / Kyle McDermott
Hosting download unavailable

13.12.2018 10:57 / Jan Mareš
Re: zavináč

2.12.2018 23:56 / František Kučera
Sraz

5.10.2018 17:12 / Jakub Kuljovsky
Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze