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 33809×

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ů

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

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?

20.9.2018 10:04 / Jan Ober
Jaký kurz a software by jste doporučili pro začínajcího kodéra?

20.9.2018 10:00 / Jan Ober
Re: Gimp

Více ...

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