LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> C/C++ (35) - Reference, funkce

Ukážeme si, co to jsou reference a jak se liší od ukazatelů. V druhé části dílu probereme dvě metody, které umožňují volat funkce se stejným identifikátorem, ale s různou sadou parametrů.

16.2.2006 08:00 | Jan Němec | Články autora | přečteno 33315×

Reference

Jazyk C zná běžné typy a ukazatele na ně. Parametry předávané odkazem, běžně užívané například v Pascalu, v C neexistují. Pokud chceme předat do funkce jako parametr proměnnou a tuto proměnnou (nikoli její kopii na zásobníku) ve funkci modifikovat, musíme použít ukazatel. To s sebou přináší určitá úskalí, neboť je jen na zodpovědnosti programátora ohlídat si, aby byl ukazatel inicializovaný, navíc práce s běžnou proměnnou je díky odlišné syntaxi přeci jen o něco pohodlnější. C++ se snaží problém řešit pomocí referencí. Referenci je nejjednodušší si představit jako neměnný inicializovaný ukazatel se syntaxí běžné proměnné. Lze ji používat jako každý jiný typ při definici proměnných a parametrů i návratové hodnoty funkcí. Reference se definuje stejně jako ukazatel, jen místo znaku * napíšeme &. Nejjednodušší příklad je asi prohození hodnot dvou proměnných.

#include <stdio.h>

// Nefunkční řešení - programátor si neuvědomil, že se vytvářejí kopie
// proměnných. Původní proměnné zůstanou nezměněné.

void prohodNefunguje(int a, int b) {
   int c = a;
   a = b;
   b = c;
}

// Řešení ve stylu C++ (v C neprojde překladem) s použitím referencí
void prohodCPlusPlus(int &a, int &b) {
   int c = a;
   a = b;
   b = c;
}

// Řešení ve stylu C (lze použít i v C++) s použitím ukazatelů
void prohodC(int *a, int *b) {
   int c = *a;
   *a = *b;
   *b = c;
}

int main(void) {
  int x = 1;
  int y = 0;

  printf("x = %i y = %i\n", x, y);
  
  prohodNefunguje(x, y);
  printf("x = %i y = %i\n", x, y);

  prohodCPlusPlus(x, y);
  printf("x = %i y = %i\n", x, y);

  prohodC(&x, &y);
  printf("x = %i y = %i\n", x, y);

  return 0;
}

Jak použití referencí, tak i ukazatelů má v tomto případě své výhody i nevýhody. U ukazatele nemá programátor nikdy jistotu, že je správně inicializovaný, a ani test na NULL neodhalí všechny chyby. Rovněž zápis implementace funkce je trochu krkolomnější. V případě referencí sice klesá (nikoli na nulu!) riziko paměťových chyb, ale programátor zase nepozná na první pohled v místě volání, zda funkce může své parametry modifikovat. V duchu jazyka C++ je v tomto případě užití referencí.

Občas se také hodí reference jako proměnná, například pokud budeme opakovaně přistupovat do pole na stejný index.

int pole[20];

// definice s inicializací
int &pole5 = pole[5];

// totéž jako pole[5] = 1;
pole5 = 1;

Důležité je, že reference musí být inicializovaná již v místě definice a není možné později znovu určit nebo změnit, na kterou proměnnou reference odkazuje. To je podstatná změno oproti ukazatelům, která činí používání referencí o něco bezpečnější.

int pole[20];

// definice bez inicializace - u reference nelze
int *pole5;

// (pravděpodobně) paměťová chyba, u reference nelze tak snadno
*pole5 = 0;

// inicializace
pole5 = pole + 5;

// totéž jako pole[5] = 1;
*pole5 = 1;

// změna ukazatele, u reference nelze
pole5++;

// totéž jako pole[6] = 2;
*pole5 = 2;

Referenci lze použít také jako návratovou hodnotu funkce. V implementaci funkce vrátíme nějakou l-hodnotu, která zůstane platná i po opuštění těla funkce, ne tedy například lokální proměnnou. Ve volajícím kódu lze návratovou hodnotu použít jako běžnou l-hodnotu, takže výsledek funkce lze použít i na levé straně přiřazení.

int data[10];

int & vektor(int index) {
  // Tady můžeme ošetřit meze polí.
  return data[index];
}

int main(void) {
  vektor(5) = 7;
  vektor(3) = vektor(5);
  return 0;
}

Kdybychom se pokusili ve funkci vektor vrátit například číselnou konstantu, došlo by k chybě při překladu funkce vektor. Stejně tak by selhal překlad main, kdybychom návratový typ funkce vektor deklarovali jako běžný int a nikoli referenci.

Je zřejmé, že reference jako návratová hodnota umožní programátorovi implementovat s poměrně jednoduchou syntaxí například bezpečné pole s hlídáním mezí nebo automatickou alokací a podobně. Standardní knihovna C++ skutečně takové nástroje nabízí.

Reference mají oproti ukazatelům ještě některá omezení, která jsme dosud nezmínili. Především nelze definovat ukazatel ani referenci na referenci, zatímco ukazatel na ukazatel se běžně používá a má rozumný smysl. Rovněž nelze definovat referenci na void ani pole referencí.

Z pohledu konzervativního céčkaře přemýšlejícího na úrovni polidštěného asembleru nejspíš reference zůstane bude jen zakukleným ukazatelem s omezenými možnostmi použití, který jen ztěžuje čitelnost programu. Pohled objektově orientovaného programátora, fanouška C++ a především jeho standardní knihovny, bude nejspíš zcela opačný.

Přetěžování funkcí

C++ umožňuje definovat více funkcí se stejným jménem, pokud se odlišují svými parametry. V místě volání překladač automaticky vybere funkci, která s použitím případných implicitních konverzí vyhovuje sadě parametrů. Je-li takových funkcí více, vybere tu, kde jsou konverze nejjednodušší. Teprve pokud ani zde není funkce určená jednoznačně, dojde k chybě. Norma C++ výběr popisuje poměrně striktně, nicméně spíše než detailní studium normy bych doporučil psát kód tak, aby výběr funkce nezávisel například na tom, zda je nějaký parametr typu const char * nebo char *.

#include <stdio.h>

void vypis(int i) {
  printf("%i\n", i);
}

void vypis(const char *s) {
  puts(s);
}

int main(void) {
  vypis(1);
  vypis("text");
  return 0;
}

Implicitní hodnoty parametrů

V C++ je možné definovat implicitní hodnotu jednoho nebo několika posledních parametrů funkce. Pokud nejdou uvedeny, překladač je automaticky doplní na implicitní hodnotu.

#include <stdio.h>
#include <math.h>

double logaritmus(double x, double zaklad = 10) {
  return log10(x) / log10(zaklad);
}

void vypis(const char *s, FILE *f = stdout) {
  fputs(s, f);
}


int main(void) {
  char s[64];

  sprintf(s, "%f, %f\n", logaritmus(100), logaritmus(100, 100));
  vypis(s);
  vypis(s, stderr);
  return 0;
}

Pokud má funkce uvedenou hlavičku v .h souboru, uvádí se hodnota implicitního parametru pouze tam.

// soubor.h
// ...
double logaritmus(double x, double zaklad = 10);
// ...

Implementace je potom již stejná, jako kdyby byly oba parametry povinné.

// soubor.cpp
// ...
double logaritmus(double x, double zaklad) {
  return log10(x) / log10(zaklad);
}
// ...

Je to logické, neboť implicitní parametr se je implementován na úrovni volaného kódu.

Nepoužité parametry

V matematice, stejně jako v C++ lze a občas i má rozumný smysl definovat funkci, která na nějakém svém parametru nezávisí. V obou případech je dobré zdůraznit, že opravdu víme, co děláme a že ten nevyužitý parametr opravdu potřebujeme. Pokud to neuděláme, hrozí v matematice zmatení kolegů a v C++ varování překladače o zbytečném parametru. V C++ stačí neuvést jméno parametru.

void funkce(int) {
}

Nevyužitý parametr má smysl například pokud potřebujeme rozlišit přetíženou funkci nebo pokud předáváme nějaké knihovně ukazatel na naší funkci zpětného volání, která má (pro naše potřeby) zbytečně obecný prototyp.

Domácí úkoly

  • Napište nějaký kód, kde přístup na referenci způsobí nekorektní přístup do paměti.
  • S pomocí implicitních parametrů a přetížených funkcí napište kód, kde si nebudete (až do experimentálního ověření nebo detailního studia normy C++) jistí, jaká funkce se vlastně zavolá. Zkuste to ještě zkomplikovat funkcemi s proměnným počtem parametrů ve stylu C. V praxi takhle neprogramujte.

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

Příště se podíváme na prostory jmen.

Verze pro tisk

pridej.cz

 

DISKUZE

konzervativni &quot;Ceckar&quot; 21.6.2006 11:54 camlost
Proměnná? 18.10.2006 11:42 Petr Kubizňák
  L Re: Proměnná? 19.10.2006 16:45 Jan Němec




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

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ář

18.6.2018 0:43 /František Kučera
Červnový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 21. 6. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát na téma: F-Droid, aneb svobodný software do vašeho mobilu. Kromě toho budou k vidění i vývojové desky HiFive1 se svobodným/otevřeným čipem RISC-V.
Přidat komentář

23.5.2018 20:55 /Ondřej Čečák
Od pátku 25.5. proběhne na Fakultě informačních technologií ČVUT v Praze openSUSE Conference. Můžete se těšit na spostu zajímavých přednášek, workshopů a také na Release Party nového openSUSE leap 15.0. V na stejném místě proběhne v sobotu 26.5. i seminář o bezpečnosti CryptoFest.
Přidat komentář

20.5.2018 17:45 /Redakce Linuxsoft.cz
Ve čtvrtek 31. května 2018 připravuje webový magazín BusinessIT ve spolupráci s Best Online Média s.r.o. pátý ročník odborné konference Firemní informační systémy 2018. Akce proběhne v kongresovém centru Vavruška (palác Charitas), Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00 hod. dopoledne do cca 15 hod. odpoledne. Konference je zaměřena na efektivní využití firemních informačních systémů a na to, jak plně využít jejich potenciál. Podrobnější informace na webových stránkách konfrence.
Přidat komentář

14.5.2018 7:28 /František Kučera
Květnový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 17. 5. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát na téma: Audio – zvuk v GNU/Linuxu.
Přidat komentář

7.5.2018 16:20 /František Kučera
Na stránkách spolku OpenAlt vyšla fotoreportáž Pražské srazy 2017 dokumentující srazy za uplynulý rok. Květnový pražský sraz na téma audio se bude konat 17. 5. 2018 (místo a čas ještě upřesníme).
Přidat komentář

17.4.2018 0:46 /František Kučera
Dubnový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 4. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tématem tohoto srazu bude OpenStreetMap (OSM) aneb svobodné mapy.
Přidat komentář

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

> Poslední diskuze

20.2.2018 18:48 / Ivan Majer
portal

20.2.2018 15:57 / Jan Havel
Jak využíváte služby cloudu v podnikání?

16.1.2018 1:08 / Ivan Pittner
verejna ip od o2 ubuntu

15.1.2018 17:26 / Mira Harvalik
Re: Jak udělat HTML/Javascript swiping gallery do mobilu?

30.12.2017 20:16 / Michal Knoll
odmocnina

Více ...

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