Hlavní
Hlavní strana
Seznam článků
Nejčtenější články
Progres e-mailem
Visual C++ FAQ

banner1.gif (2545 bytes)

Seriály
COM
ISAPI

Nenechte si ujít
Neobdélníková okna
Tisk bez Preview
MFC a DLL
Logo v MDI ploše
Kouzla s kombo-boxem
Výjimky v C++

banner.gif (3305 bytes)

Logické výrazy a jazyk C/C++ Radek Pavienský
11.11.1999
[Hlavní stránka]  |  [Rubrika]

Jazyk C používá pro vyhodnocení logických operátorů tzv. lenivé vyhodnocování, což znamená, že se vyhodnotí jen nejnutnější část logického výrazu.

Pokud máme například logický výraz

1 || a 

výsledek je vždy logická pravda a je jedno co je uloženo v proměnné a, neboť operátor logického součtu pravda a cokoliv je vždy pravda. Je tedy zbytečné vyhodnocovat obsah proměnné a, protože na její hodnotě nezáleží. A to je přesně to, co jazyk C dělá a čemu říkáme lenivé vyhodnocování.

Podobně se to má s operátorem logického součinu (and):

0 && a 

výsledek je vždy logická nepravda a opět nezáleží na tom, jakou hodnotu má proměnná a.

Možná se zdá, že znalost chování jazyka C je pro nás programátory irelevantní, ale není tomu tak (pokud by tomu tak bylo, tak bychom o tom asi nepsali :-).

Ukažme si nějaký kód:

// Deklarace nějaké struktury, která obsahuje
// jednu členskou proměnnou. 
typedef struct 
{
  int m_x;
} SOME_STRUCT;

void main()
{
  // Definujeme proměnnou typu struktura
  SOME_STRUCT oStruct;
  // Definujeme proměnnou typu ukazatel na strukturu
  
// a zároveň inicializujeme ukazatel
  SOME_STRUCT *poStruct = &oStruct;
  
  // Pokud je ukazatel inicializován
  if (poStruct != NULL)
    // a členská proměnná je nastavena na deset
    if (poStruct->m_x == 10)
      // tak to máme...
      printf("Je to tak!\n");
  
}

V tomto kódu testujeme, zda je nějaký ukazatel na strukturu inicializován a pokud ano, tak otestujeme, zda její členská proměnná je rovna desíti. Tuto podmínku se pokusíme zjednodušit:

  // Pokud je ukazatel inicializován
  // a členská proměnná je nastavena na deset
  if (poStruct != NULL && poStruct->m_x == 10)
    // tak to máme...
    printf("Je to tak!\n");

Zamysleme se nad tím, co by se stalo, kdyby jazyk C poctivě vyhodnocoval celý logický výraz a ukazatel poStruct byl nulový. První část podmínky je nepravdivá (z čehož plyne, že celý výraz je nepravdivý) a druhá část podmínky způsobí pád aplikace. Proč? Protože se pokusíme přistoupit na adresu nula, kam ani náhodou nesmíme. Jenže! ono to funguje, protože jazyk C není pitomec a když zjistí, že první část výrazu s operátorem and je nepravdivá, další vyhodnocování neprovádí. Této vlastnosti se využívá velmi často, protože to vede k přehlednějším podmínkám (srovnejte si obě dvě podmínky a doufám, že ta druhá se vám líbí více).

Další ukázka je spíše legrácka a nidky jsem se s ní u céčkových programů nesetkal, ale běžně se používá (alespoň v dokumentaci) v jazyce PHP, který je velmi podobný céčku:

// Nějaká funkce vracející 1 nebo 0. 
// V našem případě vždy nula, čímž simulujeme chybu.
int Funkce()
{
  printf("Vracim chybu...\n");
  return 0;
}

// Funkce zapouzdřuje volání funkce exit()
// a je zde kvůli tomu, že bude použita v 
// logickém výrazu
int Konec()
{
  exit(0);  // Provede ukončení běhu programu
  return 0// tento return se již neprovede
}

void main()
{
  // Volání Funkce, které pokud selže, vyvolá funkci Konec,
  // která ukončí program. Pokud se Funkce povede, vrátí
  // hodnotu logická pravda a díky lenivému vyhodnocování se
  
// funkce Konec volat nebude a program pokračuje dál.
  Funkce() || Konec();
  printf("Tak to je konec\n");  
}

Důležitý je logický výraz, který je ve funkci main. Zde se nejprve volá funkce Funkce, která vrací logickou pravdu při úspěchu (1) a nepravdu (0) při selhání. Chceme, aby po selhání došlo k okamžitému ukončení programu, které zajistí funkce Konec. Pokud Funkce vrátí hodnotu pravda, vyhodnocování se ukončí, neboť výsledek je již dán (logická pravda) a funkce Konec se nebude volat. Pokud funkce Funkce vrátí nepravdu, je nutné vyhodnotit i zbytek výrazu, zavolá se funkce Konec, která obsahuje volání funkce exit, která okamžitě ukončí běh programu.

Bohužel nemůžeme přímo psát následující výraz:

  Funkce() || exit(0);

protože funkce exit nevrací hodnotu a nelze ji tedy použít v logickém výrazu. Z toho důvodu jsme zapouzdřili volání exit do funkce Konec, která formálně nějakou hodnotu vrací. Formálně proto, protože k předání návratové hodnoty již nedojde.

Závěr

Možná bychom našli i další fígle, které využívají lenivého vyhodnocování logických výrazů, a je tedy nutné tuto vlastnost jazyka dokonale ovládat.


Podobné články:

Kdo Otázka nebo připomínka

Prohlížení příspěvků nebo nový příspěvek

O firmě... Kontakt Ostatní