home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 June / Chip_2001-06_cd1.bin / obsahy / Chip_txt / txt / 170-171.txt < prev    next >
Text File  |  2001-04-29  |  7KB  |  58 lines

  1. Jazyk C++
  2. KDYÄ TO CHOD═ JINAK...
  3. Za₧il to asi ka₧d² programßtor: n∞kdy jeho dφlko, jakkoli se v n∞m zdß vÜechno logicky i syntakticky v po°ßdku, p°i b∞hu produkuje zcela neΦekanΘ v²sledky. Pßtrßnφ po p°φΦinßch b²vß zdlouhavΘ a zejmΘna zaΦßteΦnφk∙m m∙₧e p°inΘst nejednu bezesnou noc. Snad vßm tedy p°ijde vhod upozorn∞nφ na n∞kterΘ ΦastΘ d∙vody podivnΘho chovßnφ program∙ - zde si je p°edvedeme v prost°edφ C++, ale podobnß ·skalφ Φekajφ snad v ka₧dΘm vyÜÜφm jazyku...
  4.  
  5. VedlejÜφ efekty
  6. Pro v∞tÜinu operßtor∙ v C++ platφ, ₧e po°adφ, ve kterΘm se vyhodnocujφ jednotlivΘ operandy, nenφ stanoveno. To nemß nic spoleΦnΘho s prioritou Φi asociativitou operßtor∙. Abychom snßze pochopili, oΦ jde, podφvejme se na jednoduch² p°φklad:
  7. a = f() + g() + h();
  8. Jak znßmo, operßtor + je asociativnφ zleva doprava, tak₧e se uveden² v²raz vyhodnotφ, jako kdyby byl uzßvorkovßn:
  9. a = (f() + g()) + h();
  10. To sice znamenß, ₧e se nejprve seΦtou v²sledky volßnφ f() a g() a k nim se p°iΦte v²sledek volßnφ h(), ale u₧ z toho nijak nevypl²vß, ₧e se bude nejprve volat funkce f(), pak g() a nakonec h(). Po°adφ volßnφ, tedy vyhodnocenφ operand∙, je ponechßno zcela na v∙li p°ekladaΦe; tφm se mu toti₧ otevφrajφ r∙znΘ mo₧nosti optimalizace. 
  11. Nemil²m d∙sledkem ovÜem je, ₧e pokud majφ n∞kterΘ operace vedlejÜφ efekty, m∙₧e se stßt, ₧e stejn² v²raz p°elo₧en² r∙zn²mi p°ekladaΦi, nebo dokonce t²m₧ p°ekladaΦem na r∙zn²ch mφstech programu, dß se stejn²mi operandy r∙znΘ v²sledky. 
  12. P°edstavme si t°eba, ₧e funkce f() a g() z p°edchozφho p°φkladu zvyÜujφ o 1 hodnotu globßlnφ prom∞nnΘ x a funkce h() tuto hodnotu vracφ:
  13. int x = 0;
  14. int f(){++x; return 0;}
  15. int g(){++x; return 0;}
  16. int h(){return x;}
  17. Jestli₧e pak napφÜeme 
  18. x = 0;
  19. a = f() + g() + h();
  20. bude hodnota prom∞nnΘ a rovna 0, 1 nebo 2 v zßvislosti na po°adφ, v jakΘm se operandy vyhodnotφ.
  21. M∙₧ete namφtnout, ₧e p°edchozφ p°φklad je samo·Φeln∞ vykonstruovanΘ programßtorskΘ "zv∞rstvo" a postrßdß jak²koli smysl. To je samoz°ejm∞ pravda; skuteΦnΘ p°φklady chyb tohoto druhu b²vajφ podstatn∞ slo₧it∞jÜφ, princip je ale podobn².
  22. Nejen funkce
  23. VedlejÜφ efekty funkcφ jsou nejnßpadn∞jÜφm p°φkladem, do problΘm∙ se ale m∙₧eme dostat i s operßtory ++ a --. Podφvejme se op∞t na jednoduch² p°φklad:
  24. int x = 5;
  25. int y = (x--)*(x--);
  26. Bude y obsahovat 20, nebo 25?
  27. Odpov∞∩ je tristnφ, ale u₧ ji nepochybn∞ uhodnete: Zßle₧φ na p°ekladaΦi. Nap°φklad archaick², ale tu a tam stßle jeÜt∞ pou₧φvan² p°ekladaΦ Borland C++ 3.1 vytvo°φ program, v n∞m₧ bude v²sledkem hodnota 20, ale nov∞jÜφ  p°ekladaΦe Borland C++ Builder nebo MS Visual C++ 6 vygenerujφ k≤d, kter² dß jako v²sledek 25. (Ve vÜech p°φpadech bude ovÜem x nakonec obsahovat hodnotu 3.)
  28.  
  29. SekvenΦnφ body
  30. V uvedenΘm p°φkladu by se mohlo zdßt, ₧e pravdu mß starÜφ p°ekladaΦ: nejprve vezme x - lhostejno, zda prvnφ, nebo druhΘ, nebo¥ jde o tou₧ prom∞nnou -, pou₧ije jeho aktußlnφ hodnotu a pak hodnotu ulo₧enou v tΘto prom∞nnΘ zmenÜφ o 1. Pak ud∞lß jeÜt∞ jednou totΘ₧. To znamenß, ₧e by m∞l pou₧φt jednou hodnotu 5, podruhΘ 4, a dostat tedy 20. Jen₧e nic takovΘho nenφ nikde p°edepsßno. 
  31. Standard jazyka toti₧ pouze stanovφ, ₧e v jist²ch mφstech programu jsou definovßny tzv. sekvenΦnφ body, ve kter²ch musφ b²t dokonΦeno vyhodnocenφ p°edchßzejφcφ Φßsti v²poΦtu vΦetn∞ vÜech vedlejÜφch efekt∙ a ₧ßdn² z vedlejÜφch efekt∙ nßsledujφcφch v²poΦt∙ jeÜt∞ nesmφ nastat. Takov²m sekvenΦnφm bodem je nap°. konec celΘho v²razu, vyhodnocenφ vÜech parametr∙ p°i volßnφ funkce p°ed vstupem do jejφho t∞la, okopφrovßnφ hodnoty vracenΘ funkcφ p°i nßvratu atd. V obecnΘm p°φpad∞ vÜak sekvenΦnφm bodem nenφ vyhodnocenφ souΦßsti v²razu.
  32. Standard takΘ v²slovn∞ uvßdφ, ₧e po°adφ, ve kterΘm nastanou vedlejÜφ efekty, nenφ specifikovßno. P°edepisuje jen, ₧e musφ nastat nejpozd∞ji p°i pr∙chodu sekvenΦnφm bodem, nic vφce. To znamenß, ₧e vedlejÜφ efekty - v naÜem p°φpad∞ zm∞ny hodnot prom∞nn²ch x a y - musφ nastat nejpozd∞ji po vyhodnocenφ celΘho v²razu. Nikde nenφ ale °eΦeno, zda nap°. zm∞na hodnoty x nastane v₧dy po vyhodnocenφ uzßvorkovanΘho podv²razu (a dostaneme v²sledek 20), nebo zda nastane dvakrßt za sebou a₧ po vyhodnocenφ celΘho v²razu (a dostaneme v²sledek 25).
  33. Ani jeden z p°ekladaΦ∙ tedy sv²m chovßnφm neodporuje standardu.
  34. Makra
  35. Zßpis (x--)*(x--) vypadß na prvnφ pohled podivn∞ a nepravd∞podobn∞. ProΦ bychom n∞co takovΘho psali? ╚eho tφm vlastn∞ chceme dosßhnout?
  36. Je asi jasnΘ, ₧e podobnΘ v∞ci programßtor b∞₧n∞ nenapφÜe. P°esto jejich v²skyt v programu nenφ tak nepravd∞podobn², jak by se mohlo zdßt; mohou toti₧ snadno vzniknout jako v²sledek vyhodnocovßnφ maker. StaΦφ, kdy₧ definujeme makro SQR(), kterΘ bude poΦφtat druhou mocninu:
  37. #define SQR(x) ((x)*(x))
  38. a pozd∞ji ho pou₧ijeme naprosto "logick²m" zp∙sobem
  39. int y = SQR(x--);
  40. Kdyby SQR() byla funkce, bylo by vÜe v po°ßdku - a prßv∞ podobnost pou₧itφ parametrickΘho makra s volßnφm funkce je Φastou p°φΦinou podobn²ch chyb.
  41.  
  42. V²jimky
  43. ╪ekli jsme, ₧e pro v∞tÜinu operßtor∙ nenφ po°adφ vyhodnocenφ operand∙ specifikovßno. Toto pravidlo vÜak mß svΘ v²jimky, kterΘ jist∞ dob°e znßte. Jde o operßtory ||, &&, ?: a Φßrka. Mezi vyhodnocenφm jednotliv²ch operand∙ ve v²razech a||b, a&&b, a?b:c a a,b je v₧dy sekvenΦnφ bod. To znamenß, ₧e v₧dy se nejprve vyhodnotφ prvnφ operand (vΦetn∞ mo₧n²ch vedlejÜφch efekt∙) a teprve pak druh².
  44. OvÜem pozor: vztahuje se to pouze na vestav∞nΘ operßtory, nikoli na p°etφ₧enΘ operßtory ||, && a Φßrka. (Operßtor ?: nelze, jak znßmo, p°et∞₧ovat.) Pou₧itφ p°etφ₧enΘho operßtoru p°edstavuje volßnφ funkce, ve kterΘm operandy vystupujφ jako parametry - a to znamenß, ₧e sekvenΦnφ bod nßsleduje a₧ po vyhodnocenφ vÜech parametr∙.
  45.  
  46. NedefinovanΘ v²razy
  47. Z p°edchozφho povφdßnφ plyne, ₧e hodnota n∞kter²ch v²raz∙ (nebo chovßnφ p°ekladaΦe p°i jejich vyhodnocovßnφ) nenφ definovßna; takov²m konstrukcφm je pochopiteln∞ t°eba se v programu vyhnout. JinΘ mo₧nΘ p°φklady v²raz∙, pro n∞₧ nenφ chovßnφ programu definovßno, jsou:
  48. i = v[i++];
  49. i = ++i+1;
  50. Nezapomφnejme, ₧e "·pln² v²raz" zahrnuje i p°i°azenφ a prom∞nnou na jeho levΘ stran∞ a ₧e mezi vyhodnocenφm podv²razu na pravΘ stran∞ a jeho p°i°azenφm levΘ stran∞ nenφ sekvenΦnφ bod. V prvnφm p°φpad∞ tedy nenφ jasnΘ, zda se bude nejprve inkrementovat hodnota i a pak se i p°epφÜe hodnotou danΘho prvku pole v nebo naopak. Inkrementace i pomocφ operßtoru ++ a zm∞na jeho hodnoty pomocφ operßtoru p°i°azenφ jsou toti₧ dva r∙znΘ vedlejÜφ efekty a ty mohou nastat v libovolnΘm po°adφ. PodobnΘ je to i ve druhΘm p°φkazu. (Poznamenejme, ₧e oba tyto p°φklady uvßdφ standard ISO 14882-1998 jazyka C++.)
  51. Na druhΘ stran∞ p°φkaz
  52. i = i+5;
  53. je naprosto v po°ßdku, nebo¥ zde nastßvß jedin² vedlejÜφ efekt - zm∞na hodnoty i v d∙sledku p°i°azenφ.
  54.  
  55. Miroslav Virius
  56.     2/1
  57.  
  58.