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

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ů

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

16.3.2018 22:01 /František Kučera
Kulatý OpenAlt sraz v Praze oslavíme klasicky: u limonády a piva! Přijďte si posedět, dát si dobré jídlo a vybrat z mnoha piv do restaurace Kulový blesk, který najdete v centru Prahy nedaleko metra I. P. Pavlova na adrese Sokolská 13, Praha 2. Sraz se koná ve čtvrtek 22. března a začínáme v 18:00. Heslo: OpenAlt. Vezměte s sebou svoje hračky! Uvítáme, když si s sebou na sraz vezmete svoje oblíbené hračky. Jestli máte nějaký drobný projekt postavený na Arduinu, nějakou zajímavou elektronickou součástku, či třeba i pěkný úlovek z crowdfundingové akce, neváhejte. Oslníte ostatní a o zábavu bude postaráno.
Přidat komentář

13.2.2018 0:41 /František Kučera
Únorový pražský sraz OpenAltu se koná 15. 2. 2018 a tentokrát se vydáme na návštěvu do jednoho pražského datacentra. Sejdeme se v 17:50 v severovýchodní části nástupiště tramvajové zastávky Koh-I-Noor. Po exkurzi se přesuneme do restaurace U Pštrosa (Moskevská 49), kde probereme tradiční témata (svobodný software a hardware, DIY, CNC, SDR, 3D tisk…) a tentokrát bude k vidění i IoT brána od The Things Network.
Přidat komentář

11.2.2018 23:11 /Petr Ježek
Hledáte lehký a rychlý prolížeč PDF souborů? Pokud vás již omrzelo čekat na načítání stránek či jiné nešvary, zkuste xreader.
Přidat komentář

11.2.2018 20:35 /Redakce Linuxsoft.cz
Třetí ročník odborné IT konference na téma Cloud computing v praxi proběhne ve čtvrtek 1. března 2018 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00 hod. dopoledne do cca 16 hod. odpoledne. Konference o trendech v oblasti cloud computingu nabídne i informace o konkrétních možnostech využívání cloudů a řešení vybraných otázek souvisejících s provozem IT infrastruktury.
Přidat komentář

15.1.2018 0:51 /František Kučera
První letošní pražský sraz se koná již tento čtvrtek 18. ledna od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Vítáni jsou všichni příznivci svobodného softwaru a hardwaru, ESP32, DIY, CNC, SDR nebo dobrého piva. Prvních deset účastníků srazu obdrží samolepku There Is No Cloud… just other people's computers. od Free Software Foundation.
Přidat komentář

14.11.2017 16:56 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tradičně první čtvrtek před třetím pátkem v měsíci: 16. listopadu od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.11.2017 11:06 /Redakce Linuxsoft.cz
PR: 4. ročník odborné IT konference na téma Datová centra pro business proběhne již ve čtvrtek 23. listopadu 2017 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00. Konference o návrhu, budování, správě a efektivním využívání datových center nabídne odpovědi na aktuální a často řešené otázky, např Jaké jsou aktuální trendy v oblasti datových center a jak je využít pro vlastní prospěch? Jak zajistit pro firmu či jinou organizaci odpovídající služby datových center? Podle jakých kritérií vybrat dodavatele služeb? Jak volit součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně spravovat datové centrum? Jak eliminovat možná rizika? apod.
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