{"id":1369,"date":"2019-03-27T10:49:23","date_gmt":"2019-03-27T09:49:23","guid":{"rendered":"https:\/\/www.kompikownia.pl\/?p=1369"},"modified":"2019-03-29T13:50:10","modified_gmt":"2019-03-29T12:50:10","slug":"czym-jest-undefined-behavior-w-c-c","status":"publish","type":"post","link":"https:\/\/www.kompikownia.pl\/index.php\/2019\/03\/27\/czym-jest-undefined-behavior-w-c-c\/","title":{"rendered":"Czym jest undefined behavior w C\/C++?"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">3<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span>\n<p>Je\u015bli troch\u0119 ju\u017c programowa\u0142e\u015b, z pewno\u015bci\u0105 s\u0142ysza\u0142e\u015b o czym\u015b takim jak undefined behavior &#8211; niezdefiniowane zachowanie. Jest to bardzo nieprzyjemna sytuacja, kiedy kompilator nie do ko\u0144ca wie, jak poprawnie si\u0119 zachowa\u0107. Wynika to g\u0142\u00f3wnie ze standardu, w kt\u00f3rym niekt\u00f3re operacje s\u0105 opisane jako niezdefiniowane. <a href=\"https:\/\/gist.github.com\/Earnestly\/7c903f481ff9d29a3dd1\">Standard C99 definiuje prawie 200 takich sytuacji<\/a>, Niezdefiniowane zachowanie mo\u017ce wynika\u0107 po\u015brednio tak\u017ce ze &#8222;sztuczek optymalizacyjnych&#8221; zastosowanych przez kompilator.<\/p>\n\n\n\n<p>Warto wiedzie\u0107 nie tylko o sytuacjach najcz\u0119\u015bciej prowadz\u0105cych do &#8222;undefined behavior&#8221;, ale tak\u017ce rozumie\u0107 co si\u0119 dzieje po stronie kompilatora. Przeprowadzimy kr\u00f3tk\u0105 analiz\u0119 tej paskudnej cechy j\u0119zyka C\/C++.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Z czego wynika undefined behavior?<\/h2>\n\n\n\n<p>Mo\u017cesz zada\u0107 pytanie &#8211; po co w standardzie zostawiono jakie\u015b niejasno\u015bci? Dlaczego nie wyja\u015bniono dok\u0142adnie ka\u017cdej mo\u017cliwej sytuacji?  Pozostawienie niekt\u00f3rych trudnych do rozpatrzenia niuans\u00f3w &#8222;niezdefiniowanymi&#8221; pozwala znacznie upro\u015bci\u0107 budow\u0119 kompilator\u00f3w. (Te i tak ze wzgl\u0119du na swoj\u0105 natur\u0119 s\u0105 bardzo skomplikowane). Innym powodem jest potrzeba zachowania kompatybilno\u015bci z jak najwi\u0119ksz\u0105 ilo\u015bci\u0105 architektur. r\u00f3\u017cne architektury procesor\u00f3w posiadaj\u0105 r\u00f3\u017cne mo\u017cliwo\u015bci i r\u00f3\u017cnie interpretuj\u0105 polecenia takie jak przesuni\u0119cie bitowe. Narzucenie z g\u00f3ry jakiego\u015b zachowania w konfliktowej sytuacji, takiej jak dzielenie przez zero mog\u0142oby spowodowa\u0107 du\u017cy narzut na niekt\u00f3rych architekturach CPU. Ponadto, programy s\u0105 szybsze. Dlaczego? <\/p>\n\n\n\n<p>Sp\u00f3jrzmy na zwyk\u0142\u0105 operacj\u0119 odczytu danego elementu z tablicy. Dzi\u0119ki temu, \u017ce kompilator j\u0119zyka C\/C++ nie sprawdza granic tablicy, odchodzi ca\u0142a zabawa zwi\u0105zana ze sprawdzaniem tego faktu. Program zajmuje mniej miejsca i szybciej si\u0119 wykonuje. Tak samo wygl\u0105da sytuacja z przepe\u0142nieniem zmiennych. Kompilator nie musi martwi\u0107 si\u0119 o to, \u017ce trzeba jako\u015b pozytywnie zareagowa\u0107 na t\u0119 sytuacj\u0119. Zabezpieczeniem przed takimi b\u0142\u0119dami powinien martwi\u0107 si\u0119 programista. <\/p>\n\n\n\n<p>Nie zapominajmy, \u017ce niekt\u00f3re j\u0119zyki takie jak Java, s\u0105 w tym wzgl\u0119dzie o wiele bardziej restrykcyjne ni\u017c C++. W Javie wszelkie niebezpieczne zachowania prowadz\u0105 do wyrzucenia odpowiednich wyj\u0105tk\u00f3w, kt\u00f3re najcz\u0119\u015bciej przerywaj\u0105 dzia\u0142anie programu (chyba \u017ce programista zadecyduje inaczej.  To by\u0142 jeden z powod\u00f3w przez kt\u00f3re na pocz\u0105tku swojej historii Java by\u0142a wolniejsza od j\u0119zyk\u00f3w kompilowanych do kodu maszynowego. W dzisiejszych czasach r\u00f3\u017cnica w wydajno\u015bci jest praktycznie niezauwa\u017calna. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Dlaczego ignorujemy undefined behavior?<\/h2>\n\n\n\n<p>Najcz\u0119\u015bciej z powodu braku \u015bwiadomo\u015bci, \u017ce takie a nie inne rozwi\u0105zanie mo\u017ce doprowadzi\u0107 do tragedii. Je\u015bli program wykonuje si\u0119 prawid\u0142owo &#8211; go on, przecie\u017c wszystko jest ok. No nie do ko\u0144ca. <\/p>\n\n\n\n<p>Co mo\u017ce si\u0119 wydarzy\u0107 kiedy napiszemy program wykorzystuj\u0105cy undefined behavior i spr\u00f3bujemy go skompilowa\u0107 a nast\u0119pnie uruchomi\u0107? Dos\u0142ownie wszystko! Program mo\u017ce dzia\u0142a\u0107 ca\u0142kowicie prawid\u0142owo, wyk\u0142adaj\u0105c si\u0119 jedynie w \u015bci\u015ble okre\u015blonych sytuacjach. Aplikacja mo\u017ce si\u0119 uruchomi\u0107. Lecz po uruchomieniu mo\u017ce dzia\u0142a\u0107 \u017ale, b\u0142\u0119dnie wykonuj\u0105c obliczenia. W najlepszej mo\u017cliwej sytuacji kompilator poinformuje nas za pomoc\u0105 ostrze\u017cenia lub b\u0142\u0119du, \u017ce zrobili\u015bmy co\u015b, co mo\u017ce zako\u0144czy\u0107 si\u0119 \u017ale. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Typy &#8222;zachowania&#8221;<\/h2>\n\n\n\n<p>Og\u00f3lnie zachowanie i spos\u00f3b wykonania funkcji\/metod mo\u017cemy podzieli\u0107 na trzy &#8222;g\u0142\u00f3wne&#8221; rodzaje: <\/p>\n\n\n\n<ul><li><strong>Typ 1<\/strong> &#8211; zachowanie jest zdefiniowane dla ka\u017cdego mo\u017cliwego zbioru parametr\u00f3w wej\u015bciowych<\/li><li><strong>Typ 2 <\/strong>&#8211; zachowanie jest zdefiniowane tylko dla niekt\u00f3rych zbior\u00f3w parametr\u00f3w wej\u015bciowych<\/li><li><strong>Typ 3<\/strong> &#8211; zachowanie jest niezdefiniowane dla wszystkich mo\u017cliwych parametr\u00f3w wej\u015bciowych<\/li><\/ul>\n\n\n\n<p>Aby nie la\u0107 wody, przeanalizujemy kr\u00f3tko i konkretnie ka\u017cd\u0105 z wymienionych mo\u017cliwo\u015bci. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Funkcje typu 1<\/h3>\n\n\n\n<p>Funkcje tego typu nigdy nie maj\u0105 problem\u00f3w ze sob\u0105 i zawsze si\u0119 prawid\u0142owo wykonuj\u0105. Sp\u00f3jrz np.: na taki przyk\u0142ad:<\/p>\n\n\n<div class=\"codecolorer-container cpp default\" style=\"overflow:auto;white-space:nowrap;width:90%;\"><table cellspacing=\"0\" cellpadding=\"0\"><tbody><tr><td class=\"line-numbers\"><div>1<br \/>2<br \/>3<br \/>4<br \/>5<br \/>6<br \/>7<br \/>8<br \/><\/div><\/td><td><div class=\"cpp codecolorer\"><span class=\"kw4\">int<\/span> safeDiv <span class=\"br0\">&#40;<\/span><span class=\"kw4\">int<\/span> a, <span class=\"kw4\">int<\/span> b<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; <span class=\"kw1\">if<\/span> <span class=\"br0\">&#40;<\/span><span class=\"br0\">&#40;<\/span>b <span class=\"sy1\">==<\/span> <span class=\"nu0\">0<\/span><span class=\"br0\">&#41;<\/span> <span class=\"sy3\">||<\/span> <span class=\"br0\">&#40;<\/span><span class=\"br0\">&#40;<\/span>a <span class=\"sy1\">==<\/span> <span class=\"kw2\">INT_MIN<\/span><span class=\"br0\">&#41;<\/span> <span class=\"sy3\">&amp;&amp;<\/span> <span class=\"br0\">&#40;<\/span>b <span class=\"sy1\">==<\/span> <span class=\"sy2\">-<\/span><span class=\"nu0\">1<\/span><span class=\"br0\">&#41;<\/span><span class=\"br0\">&#41;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw3\">puts<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Nie wolno tak dzielic!!!&quot;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">return<\/span> <span class=\"nu0\">0<\/span><span class=\"sy4\">;<\/span><br \/>\n&nbsp; <span class=\"br0\">&#125;<\/span> <span class=\"kw1\">else<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">return<\/span> a <span class=\"sy2\">\/<\/span> b<span class=\"sy4\">;<\/span><br \/>\n&nbsp; <span class=\"br0\">&#125;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><\/div><\/td><\/tr><\/tbody><\/table><\/div>\n\n\n\n<p>Z lekcji matematyki wiemy, \u017ce nie wolno dzieli\u0107 przez 0. Zabezpieczyli\u015bmy tak\u017ce program przed przepe\u0142nieniem zmiennej. Odpowiada za to druga cz\u0119\u015b\u0107 warunku. <\/p>\n\n\n\n<p>W sumie, omawiaj\u0105c temat niezdefiniowanego zachowania tego typu funkcje nie s\u0105 dla nas ciekawe. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Funkcje typu 3<\/h3>\n\n\n\n<p>Funkcje tego typu w ka\u017cdym wypadku mog\u0105 spowodowa\u0107 dziwne zachowanie. Prostym przyk\u0142adem jest niepoprawnie zastosowany operator przesuni\u0119cia arytmetycznego.<\/p>\n\n\n<div class=\"codecolorer-container cpp default\" style=\"overflow:auto;white-space:nowrap;width:90%;\"><table cellspacing=\"0\" cellpadding=\"0\"><tbody><tr><td class=\"line-numbers\"><div>1<br \/>2<br \/>3<br \/>4<br \/>5<br \/>6<br \/>7<br \/>8<br \/>9<br \/><\/div><\/td><td><div class=\"cpp codecolorer\"><span class=\"co2\">#include &lt;iostream&gt;<\/span><br \/>\n<span class=\"kw2\">using<\/span> <span class=\"kw2\">namespace<\/span> std<span class=\"sy4\">;<\/span><br \/>\n<span class=\"kw4\">int<\/span> main<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw4\">int<\/span> a <span class=\"sy1\">=<\/span> <span class=\"nu0\">1<\/span><span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; a<span class=\"sy1\">=<\/span>a<span class=\"sy1\">&lt;&lt;<\/span><span class=\"nu0\">32<\/span><span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw3\">cout<\/span><span class=\"sy1\">&lt;&lt;<\/span>a<span class=\"sy1\">&lt;&lt;<\/span>endl<span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">return<\/span> <span class=\"nu0\">0<\/span><span class=\"sy4\">;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><\/div><\/td><\/tr><\/tbody><\/table><\/div>\n\n\n\n<p>Teoretycznie program nie wykonuje \u017cadnych niepo\u017c\u0105danych operacji. Mo\u017cna rzec &#8211; jest prosty jak budowa cepa. Jego jedynym efektem powinno by\u0107 przesuni\u0119cie bitowe warto\u015bci zmiennej a o 32 bity w lewo. <\/p>\n\n\n\n<p>Jakiego wyniku si\u0119 spodziewamy? Wydaje si\u0119, \u017ce program powinien wy\u015bwietli\u0107 liczb\u0119 0. Przewidujemy taki rezultat, gdy\u017c skrajne bity s\u0105 tracone przy przesuni\u0119ciu. Przesuwamy o 32 bity, a wi\u0119c o wi\u0119ksz\u0105 ilo\u015b\u0107 ni\u017c wynosi rozmiar zmiennej int. Z tego za\u0142o\u017cenia wynika \u017ce wszystkie bity powinny by\u0107 stracone a ostateczna warto\u015b\u0107 zmiennej powinna wynosi\u0107 0. <\/p>\n\n\n\n<p>Kompilujemy i uruchamiamy program i &#8230; <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"695\" height=\"194\" src=\"https:\/\/www.kompikownia.pl\/wp-content\/uploads\/2019\/03\/image-8.png\" alt=\"wynik dzia\u0142ania programu przesuni\u0119cia bitowego o 32 bity.\" class=\"wp-image-1372\"\/><\/figure><\/div>\n\n\n\n<p>zonk. Warto\u015b\u0107 wynosi 1. Dlaczego? Zajrzyjmy do standardu j\u0119zyka C:<\/p>\n\n\n\n<p style=\"background-color:#fff8b4\" class=\"has-background\">The result is undefined if the right operand is negative, or greater than or equal to the number of bits in the left expression\u2019s type.<\/p>\n\n\n\n<p>A teraz zerknijmy jeszcze na dokumentacj\u0119 procesora 8086 &#8211; przodka wszystkich wsp\u00f3\u0142czesnych CPU architektury x86. <\/p>\n\n\n\n<p style=\"background-color:#fff8b4\" class=\"has-background\">The 8086 does not mask the shift count. However, all other IA-32 processors (starting with the Intel 286 processor) do mask the shift count to 5 bits, resulting in a maximum count of 31. This masking is done in all operating modes (including the virtual-8086 mode) to reduce the maximum execution time of the instructions.<\/p>\n\n\n\n<p>Jak widzisz, taki a nie inny wynik dzia\u0142ania programu zale\u017cy w tym wypadku wy\u0142\u0105cznie od zachowania procesora, a nie od woli programisty. Aby udowodni\u0107, \u017ce powy\u017cszy program mo\u017ce zachowywa\u0107 si\u0119 r\u00f3\u017cnie w zale\u017cno\u015bci od architektury CPU uruchommy go na Raspberry PI &#8211; typowym przedstawicielu architektury ARM. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"705\" height=\"191\" src=\"https:\/\/www.kompikownia.pl\/wp-content\/uploads\/2019\/03\/image-9.png\" alt=\"\" class=\"wp-image-1374\"\/><\/figure><\/div>\n\n\n\n<p>Tym razem <strong>identyczny<\/strong> program zwr\u00f3ci\u0142 warto\u015b\u0107 0. Dowodzi to, \u017ce musimy uwa\u017ca\u0107 na dzia\u0142ania, kt\u00f3re s\u0105 okre\u015blone jako &#8222;undefined&#8221;.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Funkcje typu 2<\/h3>\n\n\n\n<p>Dla tych funkcji &#8222;undefined behavior&#8221; mo\u017ce wyst\u0105pi\u0107 tylko dla niekt\u00f3rych danych wej\u015bciowych. Jest to najciekawszy przypadek dla nas. Jednocze\u015bnie takie b\u0142\u0119dy najtrudniej zauwa\u017cy\u0107. Przeanalizujmy co robi funkcja f w poni\u017cszym listingu. <\/p>\n\n\n\n\n<div class=\"codecolorer-container cpp default\" style=\"overflow:auto;white-space:nowrap;width:90%;\"><table cellspacing=\"0\" cellpadding=\"0\"><tbody><tr><td class=\"line-numbers\"><div>1<br \/>2<br \/>3<br \/>4<br \/>5<br \/>6<br \/>7<br \/>8<br \/>9<br \/>10<br \/>11<br \/>12<br \/>13<br \/><\/div><\/td><td><div class=\"cpp codecolorer\"><span class=\"co2\">#include &lt;iostream&gt;<\/span><br \/>\n<span class=\"co2\">#include &lt;limits.h&gt;<\/span><br \/>\n<span class=\"kw2\">using<\/span> <span class=\"kw2\">namespace<\/span> std<span class=\"sy4\">;<\/span><br \/>\n<span class=\"kw4\">int<\/span> f<span class=\"br0\">&#40;<\/span><span class=\"kw4\">int<\/span> i<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">return<\/span> i<span class=\"sy2\">+<\/span><span class=\"nu0\">1<\/span> <span class=\"sy1\">&gt;<\/span> i<span class=\"sy4\">;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><br \/>\n<span class=\"kw4\">int<\/span> main<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw4\">int<\/span> a <span class=\"sy1\">=<\/span> <span class=\"nu0\">1<\/span><span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw4\">int<\/span> b <span class=\"sy1\">=<\/span> f<span class=\"br0\">&#40;<\/span>a<span class=\"br0\">&#41;<\/span><span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw3\">cout<\/span><span class=\"sy1\">&lt;&lt;<\/span>b<span class=\"sy1\">&lt;&lt;<\/span>endl<span class=\"sy4\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">return<\/span> <span class=\"nu0\">0<\/span><span class=\"sy4\">;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><\/div><\/td><\/tr><\/tbody><\/table><\/div>\n\n\n\n\n<p>Potencjalnie nie widzimy nic niebezpiecznego ani wieloznacznego. Funkcja wykonuje bardzo prost\u0105 operacj\u0119. Sprawdzamy czy warto\u015b\u0107 parametru zwi\u0119kszona o jeden jest wi\u0119ksza od tego parametru. Wydaje si\u0119, \u017ce powinna ona zawsze zwr\u00f3ci\u0107 warto\u015b\u0107 1. W ko\u0144cu ka\u017cda liczba jest mniejsza od tej samej liczby powi\u0119kszonej o 1, prawda? Ale nie jest to takie proste m\u00f3j kamracie. Zauwa\u017c, \u017ce je\u015bli przeka\u017cemy jako argument INT_MAX (czyli maksymaln\u0105 warto\u015b\u0107, jak\u0105 mo\u017ce pomie\u015bci\u0107 typ int) i zwi\u0119kszymy j\u0105 o 1 otrzymamy liczb\u0119 du\u017co mniejsz\u0105. Wtedy funkcja ta zwr\u00f3ci false. <\/p>\n\n\n\n<p>Je\u015bli skompilujemy program z t\u0105 funkcj\u0105 bez w\u0142\u0105czonych optymalizacji za pomoc\u0105 kompilatora gcc, nie nast\u0105pi\u0105 \u017cadne problemy. Aplikacja b\u0119dzie dzia\u0142a\u0142a prawid\u0142owo dla ka\u017cdego przypadku. Nawet wtedy, gdy jako argument tej funkcji podamy warto\u015b\u0107 INT_MAX. Sp\u00f3jrzmy na kod assemblera, kt\u00f3ry zosta\u0142 wygenerowany w finalnym pliku wykonywalnym:<\/p>\n\n\n<div class=\"codecolorer-container asm default\" style=\"overflow:auto;white-space:nowrap;width:90%;\"><div class=\"asm codecolorer\"><span class=\"kw1\">push<\/span> &nbsp; <span class=\"kw4\">rbp<\/span><br \/>\n<span class=\"kw1\">mov<\/span> &nbsp; &nbsp;<span class=\"kw4\">rbp<\/span><span class=\"sy1\">,<\/span><span class=\"kw4\">rsp<\/span><br \/>\n<span class=\"kw1\">mov<\/span> &nbsp; &nbsp;<span class=\"kw6\">DWORD<\/span> PTR <span class=\"br0\">&#91;<\/span><span class=\"kw4\">rbp<\/span><span class=\"sy1\">-<\/span><span class=\"nu0\">0x4<\/span><span class=\"br0\">&#93;<\/span><span class=\"sy1\">,<\/span><span class=\"kw4\">edi<\/span><br \/>\n<span class=\"kw1\">mov<\/span> &nbsp; &nbsp;<span class=\"kw4\">eax<\/span><span class=\"sy1\">,<\/span><span class=\"kw6\">DWORD<\/span> PTR <span class=\"br0\">&#91;<\/span><span class=\"kw4\">rbp<\/span><span class=\"sy1\">-<\/span><span class=\"nu0\">0x4<\/span><span class=\"br0\">&#93;<\/span><br \/>\n<span class=\"kw1\">add<\/span> &nbsp; &nbsp;<span class=\"kw4\">eax<\/span><span class=\"sy1\">,<\/span><span class=\"nu0\">0x1<\/span><br \/>\n<span class=\"kw1\">cmp<\/span> &nbsp; &nbsp;<span class=\"kw4\">eax<\/span><span class=\"sy1\">,<\/span><span class=\"kw6\">DWORD<\/span> PTR <span class=\"br0\">&#91;<\/span><span class=\"kw4\">rbp<\/span><span class=\"sy1\">-<\/span><span class=\"nu0\">0x4<\/span><span class=\"br0\">&#93;<\/span><br \/>\n<span class=\"kw1\">setg<\/span> &nbsp; <span class=\"kw4\">al<\/span><br \/>\n<span class=\"kw1\">movzx<\/span> &nbsp;<span class=\"kw4\">eax<\/span><span class=\"sy1\">,<\/span><span class=\"kw4\">al<\/span><br \/>\n<span class=\"kw1\">pop<\/span> &nbsp; &nbsp;<span class=\"kw4\">rbp<\/span><br \/>\n<span class=\"kw1\">ret<\/span><\/div><\/div>\n\n\n\n<p>Kod wygl\u0105da tak jak przypuszczali\u015bmy. W rejestrze EAX przechowywany jest argument funkcji. Zwi\u0119kszamy ten argument o 1 i por\u00f3wnujemy. Zwracamy wynik por\u00f3wnania. Kompilator wygenerowa\u0142 zbi\u00f3r takich instrukcji, jakich oczekiwali\u015bmy. Jednak\u017ce co si\u0119 stanie, gdy w\u0142\u0105czymy optymalizacj\u0119 na maksymalnym poziomie: O3? Co si\u0119 zmieni\u0142o w kodzie ASM?<\/p>\n\n\n<div class=\"codecolorer-container asm default\" style=\"overflow:auto;white-space:nowrap;width:90%;\"><div class=\"asm codecolorer\"><span class=\"kw1\">mov<\/span> &nbsp; &nbsp;<span class=\"kw4\">eax<\/span><span class=\"sy1\">,<\/span><span class=\"nu0\">0x1<\/span><br \/>\n<span class=\"kw1\">ret<\/span><\/div><\/div>\n\n\n\n<p>Hmmm. Kompilator na pewno optymalizowa\u0142 ten kod. Mo\u017cemy powiedzie\u0107, \u017ce nawet przesadzi\u0142 z optymalizacj\u0105, gdy\u017c wywali\u0142 ca\u0142kowicie sprawdzanie warunku mniejsze\/wi\u0119ksze. Widocznie uzna\u0142, \u017ce jeden przypadek, kiedy funkcja zwraca inn\u0105 warto\u015b\u0107 ni\u017c 1 mo\u017cna pomin\u0105\u0107 &#8230; Ta optymalizacja powoduje, \u017ce program dla INT_MAX zwr\u00f3ci inny wynik ni\u017c ten kt\u00f3rego by\u015bmy oczekiwali. Mo\u017cesz nie rozumie\u0107 kodu ASM, wi\u0119c przedstawi\u0119 jak wygl\u0105da\u0142aby funkcja f w postaci zoptymalizowanej prze kompilator:<\/p>\n\n\n<div class=\"codecolorer-container cpp default\" style=\"overflow:auto;white-space:nowrap;width:90%;\"><div class=\"cpp codecolorer\"><span class=\"kw4\">int<\/span> f<span class=\"br0\">&#40;<\/span><span class=\"kw4\">int<\/span> i<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">return<\/span> <span class=\"nu0\">1<\/span><span class=\"sy4\">;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><\/div><\/div>\n\n\n\n<p>Prawda, \u017ce nie o to nam chodzi\u0142o? A jak standard zapatruje si\u0119 na to, co przed chwil\u0105 si\u0119 wydarzy\u0142o? Ot\u00f3\u017c &#8211; jest to standardowy przypadek tzw. &#8222;integer overflow&#8221;.<\/p>\n\n\n\n<p style=\"background-color:#fff8b4\" class=\"has-background\"> If an&nbsp;<em>exceptional condition<\/em>&nbsp;occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.<\/p>\n\n\n\n<p>No c\u00f3\u017c. Jedynym rozwi\u0105zaniem jest dodanie instrukcji warunkowej zabezpieczaj\u0105cej program przed podaniem argumentu INT_MAX, a tym samym przed przepe\u0142nieniem zmiennej integer. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Jak zapobiega\u0107 undefined behavior?<\/h2>\n\n\n\n<p>Undefined behavior jest czym\u015b takim, czego powinni\u015bmy unika\u0107 za wszelk\u0105 cen\u0119. B\u0142\u0119dy tego rodzaju s\u0105 nierzadko bardzo ci\u0119\u017ckie do zdebugowania i wykrycia. Zdarzaj\u0105 si\u0119 sytuacje, kiedy aplikacja pod debuggerem wykonuje si\u0119 prawid\u0142owo, a samodzielnie sypie b\u0142\u0119dami &#8230; Naprawd\u0119 zatruwa to \u017cycie programisty. Dlatego warto wiedzie\u0107 sk\u0105d bierze si\u0119 undefined behavior. A jak mo\u017cemy mu zapobiec? <\/p>\n\n\n\n<ul><li>Pozw\u00f3l kompilatorowi, aby informowa\u0142 ci\u0119 nawet o bardzo b\u0142ahych odst\u0119pstwach od standardu. W GCC w\u0142\u0105czysz t\u0119 opcj\u0119 za pomoc\u0105 prze\u0142\u0105cznik\u00f3w: -Wall i -Wextra. <\/li><li>Pozw\u00f3l programowi przerwa\u0107 swoje dzia\u0142anie kiedy wyst\u0105pi niezdefiniowana sytuacja wynikaj\u0105ca z operacji na liczbach, kt\u00f3rej nie przewidzia\u0142e\u015b. U\u017cyj prze\u0142\u0105cznika -ftrapv.<\/li><li>U\u017cywaj analizator\u00f3w pokroju &#8222;valgrind&#8221;. Pomog\u0105 ci nie tylko wykry\u0107 wycieki pami\u0119ci, ale tak\u017ce sytuacje prowadz\u0105ce do undefined behavior. <\/li><li>Kiedy tworzysz funkcje &#8222;typu drugiego&#8221; pami\u0119taj o stworzeniu warunk\u00f3w kt\u00f3re zapobiegn\u0105 powstaniu niezdefiniowanego zachowania.<\/li><li>U\u017cywaj asercji. To bardzo dobry wynalazek. Sprawia, \u017ce mo\u017cesz by\u0107 spokojny o to, \u017ce tw\u00f3j program wykonuje si\u0119 prawid\u0142owo. (podczas debugowania)<\/li><li>Nie wymy\u015blaj ko\u0142a na nowo. Nie implementuj list, vector\u00f3w, je\u015bli takie struktury s\u0105 dost\u0119pne w standardzie. Nie baw si\u0119 tablicami w stylu j\u0119zyka C (int tab[]) skoro mo\u017cesz u\u017cywa\u0107 typu vector lub w ostateczno\u015bci array.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Undefined behavior jest bardzo niebezpieczne<\/h2>\n\n\n\n<p>Undefined behavior jest nieod\u0142\u0105czn\u0105 cech\u0105 j\u0119zyka C\/C++, kt\u00f3rej nie pozb\u0119dziemy si\u0119 jeszcze d\u0142ugo. Musimy nauczy\u0107 si\u0119 z nim \u017cy\u0107. Undefined behavior powinni\u015bmy unika\u0107 za wszelk\u0105 cen\u0119. Inaczej nie mo\u017cemy by\u0107 pewni (co zosta\u0142o udowodnione powy\u017cej) jaki kod wynikowy wygeneruje kompilator \ud83d\ude42 <\/p>\n\n\n\n<p>Opracowanie stworzy\u0142em na podstawie artyku\u0142u  dost\u0119pnego<a href=\"https:\/\/blog.regehr.org\/archives\/213\"> pod tym linkiem:<\/a><\/p>\n\n\n\n<p>Je\u015bli artyku\u0142 ci si\u0119 podoba\u0142, <a href=\"https:\/\/www.facebook.com\/kompikownia\/\">polub m\u00f3j profil na Facebooku<\/a> \ud83d\ude42 Je\u015bli chcesz by\u0107 na bie\u017c\u0105co z wpisami na blogu, kliknij taki dziwny czerwono-bia\u0142y dzwonek w lewej dolnej cz\u0119\u015bci strony i zezw\u00f3l na wysy\u0142anie notyfikacji. <\/p>\n\n\n\n<p>Je\u015bli zauwa\u017cy\u0142e\u015b jakie\u015b b\u0142\u0119dy czy nie\u015bcis\u0142o\u015bci &#8211; \u015bmia\u0142o &#8211; napisz komentarz. Jestem otwarty na krytyk\u0119 \ud83d\ude42 <\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">3<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span> Je\u015bli troch\u0119 ju\u017c programowa\u0142e\u015b, z pewno\u015bci\u0105 s\u0142ysza\u0142e\u015b o czym\u015b takim jak undefined behavior &#8211; niezdefiniowane zachowanie. Jest to bardzo nieprzyjemna sytuacja, kiedy kompilator nie do ko\u0144ca wie, jak poprawnie si\u0119 &#8230;<\/p>\n","protected":false},"author":1,"featured_media":1399,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[31],"tags":[],"_links":{"self":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/1369"}],"collection":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/comments?post=1369"}],"version-history":[{"count":33,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/1369\/revisions"}],"predecessor-version":[{"id":1433,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/1369\/revisions\/1433"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media\/1399"}],"wp:attachment":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media?parent=1369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/categories?post=1369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/tags?post=1369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}