Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

C++, priorytety operatorów?

06 Wrz 2009 12:21 3498 23
  • Poziom 11  
    Proszę o wyjaśnienie, na jakiej zasadzie wyliczane są takie działania i te konkretne przykłady (niby wszystkie podobne, ale jednak...). Mam tablicę priorytetów operatorów i nadal nie mogę tego do końca rozgryźć.


    Code:
    x += x++ + (x=1) //daje 4
    

    x += x++ + (x=1)  + (x=2)//daje 7

    x += x++ + (x=1)  + (x=2)   + (x=3) //daje 11
  • Specjalista Automatyk
    Ten kod jest błędny. Wyniki mogą być różne w zależności od kompilatora, a dokładniej od kolejności w jakiej oblicza wyrażenia. Pisząc kod nigdy nie używaj takiej składni. Jeżeli zauważysz coś takiego w programie traktuj jako błąd.

    Natomiast jeżeli jest to przykład na kolokwium/egzamin, to musisz stosować się do informacji od Najwyższego Autorytetu, czyli notatek z wykładów ;)

    Przykład 1
    x += x++ + (x=1) //daje 4

    operatory mają wartość i możliwy efekt uboczny

    (x=1) ma wartość 1 i efekt uboczny przypisania 1 do x zapis ( x:=1 )
    post-fixowy x++ ma wartość x i efekt uboczny przypisania x := x+1
    itd.

    Jaki będzie wynik? Jeżeli kompilator będzie obliczał od prawej strony, mamy (efekty uboczne w nawiasach kwadratowych]:

    x += x++ + (x=1) [x := 1]
    x += x++ + 1
    x += 1 + 1 [x:=x+1, tj. x := 1+1]
    x += 2 [x:=2 + 2, tj. x := 4]
    wynik 4.

    Ale kolejność wykonywania działań nie jest określona. Dla dodawania, kompilator może wygenerować kod obliczający najpierw wartość z lewej strony znaku + a później z prawej. Wtedy wynik zależy od tego jaka była wartość x przed wykonaniem kodu.

    int x = 0;
    x += x++ + (x=1)
    x += 0 + (x=1) [x := 0 + 1 <- kiedy to przypisanie będzie wykonane jest także nieokreślone!]
    x += 0 + 1 [dwa efekty uboczne, x := 0 + 1 oraz x := 1, akurat przypadkiem takie same]
    x += 1 [x := x + 1, tj. x := 1 + 1]
    wynik x = 2

    int x = 10;
    x += x++ + (x=1)
    x += 10 + (x=1) [x := 10 + 1 <- kiedy to przypisanie będzie wykonane jest także nieokreślone!]
    x += 10 + 1 [dwa efekty uboczne, x := 11 oraz x := 1, kolejność wykonania nieokreślona]
    x += 11 [x := x + 11, tj. x := 11 + 11 lub x := 1 + 11]
    wynik x = 22 lub x = 12

    itd.
  • Poziom 11  
    Ja rozumiem. Tylko, w Dev-c++, wyniki są takie jak podałem, niezależnie od wartości początkowej x. Czyli, dla tego przykładu:

    x += x++ + (x=1) + (x=2) + (x=3)


    najpierw wykonują się przypisania, czyli x kolejno wartości z nawiasów (1+2+3...)
    w następnej kolejności mamy x++ czy dodajemy po raz kolejny 3 (1+2+3+3...) dokonujemy inkrementacji i wykonujemy +=. Czyli mamy (1+2+3+3+4=13) No i coś się tu nie zgadza ;/

    Jeśli kompilator działa od prawej strony (3+2+1+1+2=9) też coś nie gra ;/
  • Specjalista Automatyk
    Ale inny kompilator, a nawet inna wersja Devc++ mogą dać inne wyniki. Może być nawet tak, że inne wyrażenie będzie obliczane w innej kolejności. Kolejność jest NIEOKREŚLONA.

    Cytat:

    najpierw wykonują się przypisania, czyli x kolejno wartości z nawiasów

    To nie jest tak. Dodawanie jest lewostronnie łączne, czyli
    x+=((x++ + (x=1)) + (x=2)) + (x=3)
    x+=((x++ + 1 [x:=1]) + (x=2)) + (x=3)
    x+=((1 + 1) + (x=2)) + (x=3) [efekt uboczny x:=x+1, nie wiadomo kiedy zostanie zastosowany!]
    x+=(2 + 2) + (x=3) [kolejny efekt uboczny x:=2]
    x+=4 + 3 [kolejny efekt uboczny x:=3]
    x+=7 [x w tym momencie jest równy 3]
    wynik x:=10, ale mamy jeszcze oczekujący efekt uboczny x:=x+1, akurat kompilator zastosował go na samym końcu obliczeń, więc wynik x=11

    Równie dobrze kolejność operacji mogłaby być inna. Takie łamigłówki można rozwiązywać tylko znając odpowiedź.
  • Poziom 12  
    Cytat:
    Ale inny kompilator, a nawet inna wersja Devc++ mogą dać inne wyniki.

    Nie jedna osoba już narzekała na to jak dev działa, ja również swego czasu miałem problemy właśnie z obliczeniami na nim (ale już na VS czy C::B nie miałem ich).
  • Poziom 12  
    Cytat:
    Nie jedna osoba już narzekała na to jak dev działa, ja również swego czasu miałem problemy właśnie z obliczeniami na nim (ale już na VS czy C::B nie miałem ich
    Tyle tylko, że zarówno Dev, VS i C::B mają z tym "problemem" niewiele wspólnego, to tylko IDE, a nie kompilatory (a w przypadku Dev'a i C::B pewnie i tak korzystałeś z tego samego gcc/MinGW)
  • Poziom 15  
    I na dodatek jak wcześniej zauważono taki kod jest po prostu nieprzewidywalny, i nie jest winą żadnego kompilatora że wynik jest taki a nie inny - każdy w dokumentacji uprzedza, że taki miszmasz to zły pomysł. Nawet stosowane przez kompilator optymalizacje uniemożliwiają przewidzenie wyniku niezależnie od wersji/użytych opcji.

    A problemów z obliczeniami napisanymi poprawnie (a nie tak jak w tych przykładach) nie ma chyba żaden kompilator...
  • Specjalista Automatyk
    laurearel napisał:
    jestam napisał:
    Ale inny kompilator, a nawet inna wersja Devc++ mogą dać inne wyniki.

    Nie jedna osoba już narzekała na to jak dev działa, ja również swego czasu miałem problemy właśnie z obliczeniami na nim (ale już na VS czy C::B nie miałem ich).


    Nie narzekam na Dev C++. Błąd w programie nie jest powodem do narzekania na kompilator.

    Między innymi z takich powodów proponuję zaczynać naukę programowania od Javy lub C#.
  • Poziom 12  
    Airborn napisał:
    Cytat:
    Nie jedna osoba już narzekała na to jak dev działa, ja również swego czasu miałem problemy właśnie z obliczeniami na nim (ale już na VS czy C::B nie miałem ich
    Tyle tylko, że zarówno Dev, VS i C::B mają z tym "problemem" niewiele wspólnego, to tylko IDE, a nie kompilatory (a w przypadku Dev'a i C::B pewnie i tak korzystałeś z tego samego gcc/MinGW)

    No tak, masz rację, ale po prostu dla mnie dev to już stare ide, z którym miałem problemy własnie przy obliczeniach, wiec po prostu bym go omijał.

    Cytat:
    Między innymi z takich powodów proponuję zaczynać naukę programowania od Javy lub C#.

    Absolutnie się nie zgadzam.Takim podejściem można od razu powiedzieć że można by od Pythona zacząć, prosta przyjemna składnia, masę możliwości - błąd. C++ uczy programować, a Java czy Python to po prostu dobre narzędzia. Zmusza do wysiłku i uczy programować, a nie przeskakiwać do kolejnej strony kursu.
    A co do C# - no cóż, bawiłem się w to trochę, ale nie przemawia do mnie idea i polityka Microsoftu, generalnie nieładnie mówiąc "mam ją w czterech literach" i nie będę z niej korzystał, nie dlatego że jest słaba, tylko ze względu na politykę firmy, i tyle.
  • Specjalista Automatyk
    Cytat:
    Takim podejściem można od razu powiedzieć że można by od Pythona zacząć

    Akurat Python IMO nie jest dobry na start z powodu dynamicznego systemu typów. Składnia sterowana wcięciami też może być mało strawna na starcie. Mimo to sądzę że byłby lepszy niż C++ jako pierwszy język dla początkujących.

    Cytat:
    nie przemawia do mnie idea i polityka Microsoftu

    C# i CLR są zdefiniowane standardami ECMA. Istnieje Mono.
  • Poziom 12  
    Cytat:
    Akurat Python IMO nie jest dobry na start z powodu dynamicznego systemu typów. Składnia sterowana wcięciami też może być mało strawna na starcie. Mimo to sądzę że byłby lepszy niż C++ jako pierwszy język dla początkujących.

    Generalnie idea wypowiedzi była taka, że najpierw powinna osoba zrozumieć sam sens programowania pisząc w C++, a potem przesiąść się na coś innego. Sam zaczynałem od C++ i nawet gdybym dzisiaj od zera miał się męczyć, też bym zaczął, bo mnie nauczył język sporo.
    Cytat:
    C# i CLR są zdefiniowane standardami ECMA. Istnieje Mono.

    A jakie to ma znaczenie ? Mono używałem przez krótki okres, doszedłem do wniosku że to bez sensu, myślę że skoro MS nie chce iść nawet na tak oczywiste ustępstwo, jak udostępnienie kompilatorów/środowiska pod wszystkie systemy na których się dzisiaj programuje, to po prostu powinni być olani. Java przynajamniej jak mówi że jest multiplatformowa, to jest, a nie jak platforma .NET - niby multiplatformowa, czyli. na wszystkich urządzeniach z Windowsem (i ponoć Unixem, ale tego nie sprawdzałem).
  • Specjalista Automatyk
    Cytat:
    najpierw powinna osoba zrozumieć sam sens programowania pisząc w C++, a potem przesiąść się na coś innego

    I właśnie z tym się nie zgadzam. Sens programowania można łatwiej zrozumieć używając języków, które nie muszą być zgodne wstecz z C, mają prostszą składnię i generalnie utrudniają zrobienie sobie krzywdy, jak Java lub C#.

    Cytat:

    MS nie chce iść nawet na tak oczywiste ustępstwo, jak udostępnienie kompilatorów/środowiska pod wszystkie systemy

    Biznes jest biznes. Ich filozofia się zmienia, wymusza to ruch OpenSource. Tyle że zmienia się powoli. Jeżeli komuś nie podoba się podejście MS równie dobrze może zaczynać od nauki Javy.
  • Poziom 27  
    vws7 napisał:
    Proszę o wyjaśnienie, na jakiej zasadzie wyliczane są takie działania i te konkretne przykłady (niby wszystkie podobne, ale jednak...). Mam tablicę priorytetów operatorów i nadal nie mogę tego do końca rozgryźć.


    Code:
    x += x++ + (x=1) //daje 4
    

    x += x++ + (x=1)  + (x=2)//daje 7

    x += x++ + (x=1)  + (x=2)   + (x=3) //daje 11


    Wszystko przy założeniu, że na początku x = 1.

    Prosze bardzo:

    x += x++ + (x=1);
    jedziemy prawą stronę od lewej:
    x++ = 2;
    wartość podstawienia x = 1 wynosi 1;
    czyli prawa strona = 3;
    dalej:
    x += 3 to znaczy x = X +3 = 4

    kolejne:

    x += x++ + (x=1) + (x=2);
    jedziemy:
    x++ ma wartość 2;
    x =1 ma wartość 1;
    x = 2 ma wartość 2 i (uwaga!) x po tym przypisaniu ma wartosć = 2
    więc lewa strona:
    x += 5 ale x z lewj strony po rozwinięciu prawej ma wartość 2!
    czyli całość:
    x = x + 5 = 7

    Dalej spróbuj sam. Kolejność operatorów w C++ jest ściśle określona i nie powinna zależeć od kompilatora; niemniej stosowanie takich zapisów służy raczej celom dydaktycznym, bo w rzeczywistości mamy do dyspozycji nawiasy i inne sposoby na przejrzyste kodowanie.

    Pozdrawiam

    Mariusz
  • Specjalista Automatyk
    kulmar napisał:

    x += x++ + (x=1);
    jedziemy prawą stronę od lewej:
    x++ = 2;
    wartość podstawienia x = 1 wynosi 1;
    czyli prawa strona = 3;


    x++ nie daje wartości 2. Poczytaj czym się różni pre-inkrementacja od post-inkrementacji. Nie opowiadaj ludziom bajek.

    Cytat:

    Kolejność operatorów w C++ jest ściśle określona i nie powinna zależeć od kompilatora; niemniej stosowanie takich zapisów służy raczej celom dydaktycznym


    Poczytaj o sequence points. Czym się różnią wartość operatora od jego efektów ubocznych (side effects). To nie służy celom dydaktycznym. Co najwyżej oblewaniu na egzaminach.

    --
    Między innymi z takich powodów proponuję zaczynać naukę programowania od Javy lub C#.
  • Poziom 27  
    Tak, rzeczywiście wygląda na to, że zbyt lekko podszedłem do tematu. Niemniej kolejność wykonywania operacji w C++ jest (a przynajmniej powinna być) ściśle określona. Na razie bawię się tymi przykładami zamieniając kolejność podstawień w zapisie; np.

    x = x++ + (x=2) + (x=3);
    i
    x = x++ + (x=3) + (x=2);

    Wyniki są rzeczywiście zaskakujące. Ale może uda się dojść do konkretnych wniosków.

    Dodano:

    Po zabawie w moim C++ for DOS mam takie wnioski:

    wyrażenie:

    x += x++ + (x=5) + (x=10) + (x=20);

    daje wynik 61, tak samo jak:

    x += x++ + (x=20) + (x=10) + (x=5);

    tez 61.

    Wniosek jest taki:

    dla inkrementacji x++ jest brana wartość x z podstawienia najbardziej z lewej prawej strony czyli w pierwszym przypadku wartość wyrażenia x++ wynosi 6, w drugim 21.
    I teraz do obliczenia wartości wyrażenia x += pod x jest podstawiana wartość podstawienia z prawej strony. I to tłumaczy otrzymanie tego samego wyniku przy zamianie skrajnych wartości podstawień.
    Jeśłi koledzy mogą sprawdzić tą teorię w swoich kompilatorach, to mielibyśmy wnioski, co do przenoszenia kodu w C++ (przy takim zawiłym zapisie) pomiędzy kompilatorami.

    Pozdrawiam

    Mariusz
  • Specjalista Automatyk
    Konkretne wnioski są takie:
    Standard C++ mówi o "seqence points". W tym przypadku całe wyrażenie ma jeden punkt sekwencji. Kompilator ma prawo dowolnie zmieniać kolejnośc obliczeń pod warunkiem, że w punkcie sekwencji wszyskie obliczenia których dotyczy ten punkt sekwencji są zakończone, oraz żadne obliczenia z następnego punktu sekwencji nie zostały rozpoczęte.

    Czyli definicja języka mówi wprost, że takie wyrażenia jak w przykładzie mają NIEOKREŚLONE wyniki. Nie należy ich używać. To błąd programisty.

    --
    Między innymi z takich powodów proponuję zaczynać naukę programowania od Javy lub C#.
  • Poziom 27  
    jestam napisał:
    Czyli definicja języka mówi wprost, że takie wyrażenia jak w przykładzie mają NIEOKREŚLONE wyniki. Nie należy ich używać. To błąd programisty.

    --
    Między innymi z takich powodów proponuję zaczynać naukę programowania od Javy lub C#.


    Złe kodowanie programu nie może stanowić kryterium wyboru języka programowania. O ile te przykłady z wątku można traktować jako sztuczki w C (typu: oblicz co z tego wynika), to nie znaczy, że tak należy pisać program,który powinien się sam dokumentować. Przyznaję, ze sam się nabrałem.

    Pozdrawiam

    Mariusz
  • Poziom 12  
    Cytat:
    I właśnie z tym się nie zgadzam. Sens programowania można łatwiej zrozumieć używając języków, które nie muszą być zgodne wstecz z C, mają prostszą składnię i generalnie utrudniają zrobienie sobie krzywdy, jak Java lub C#.

    Ja tam uważam, że mimo iż takie rzeczy jak np. składniki nie są używane w Javie czy C#, to dają ważną podstawę do zrozumienia wielu rzeczy. Jasne, nie cofajmy się do Fortrana czy nawet C, ale C++ jest moim zdaniem idealnym wyważeniem.

    Swoją drogą, każdy ma swoje podejście i nie można znaleźć idealnego rozwiązania - każdy ma swoje plusy i minusy po prostu :)

    Cytat:
    Biznes jest biznes. Ich filozofia się zmienia, wymusza to ruch OpenSource. Tyle że zmienia się powoli. Jeżeli komuś nie podoba się podejście MS równie dobrze może zaczynać od nauki Javy.

    Nom, dla mnie na dzień dzisiejszy Java >> .NET, ale przyszłość zobaczy, a może i znajdzie się jakiś trzeci zawodnik : ] Niemniej ja z tworami MS nie wiążę przyszłości, źle bym się z tym czuł, nie ważne za jaką kasę, po prostu dla mnie MS to próba zmonopolizowania WSZYSTKIEGO, nawet języków programowania. A nie ma konkurencji, nie ma rozwoju, więc tym bardziej jestem anty nastawiono do MS.


    Pozdrawiam !
  • Specjalista Automatyk
    kulmar napisał:
    Złe kodowanie programu nie może stanowić kryterium wyboru języka programowania

    Łatwość tworzenia "złego" programu (C++) czy też ochrona programisty przed popełnianiem typowych błędów (Java/C#) jest całkiem dobrym kryterium wyboru języka programowania od którego rozpoczynać naukę.

    laurearel napisał:

    Ja tam uważam, że mimo iż takie rzeczy jak np. składniki nie są używane w Javie czy C#, to dają ważną podstawę do zrozumienia wielu rzeczy.

    Przepraszam, nie rozumiem tego zdania.
  • Poziom 27  
    jestam napisał:
    kulmar napisał:
    Złe kodowanie programu nie może stanowić kryterium wyboru języka programowania

    Łatwość tworzenia "złego" programu (C++) czy też ochrona programisty przed popełnianiem typowych błędów (Java/C#) jest całkiem dobrym kryterium wyboru języka programowania od którego rozpoczynać naukę.


    Jeśli zaczynamy pisać program z założeniem, że kompilator poprawi nam program (lub jeszcze lepiej - domyśli się), co chcielismy policzyć, to masz rację - najlepiej zacząć od Basic'a na ZX SPECTRUM, bo tam nawet nie można było zrobić literówki w komendzie. Ale nikt nie wymaga od programisty (początkującego), zeby wykorzystywał od razu wszystkie mozliwości C++. Nikt nie biega, zanim nie nauczy się chodzić. A złe nawyki - czyli pisanie programu bez całkowitego rozumienia tworzonego kodu są niezależne od stosowanego kompilatora.

    Pozdrawiam

    Mariusz
  • Poziom 12  
    Cytat:
    Przepraszam, nie rozumiem tego zdania.

    cholera, wskaźniki nie składniki :))

    Nie no, ja ostatnio jestem zbyt zmęczony i wypalony i zaczyna mi się mocno wszystko przekręcać xD Czas na wolne chyba...
  • Specjalista Automatyk
    kulmar napisał:

    Jeśli zaczynamy pisać program z założeniem, że kompilator poprawi nam program (lub jeszcze lepiej - domyśli się), co chcielismy policzyć, to masz rację - najlepiej zacząć od Basic'a na ZX SPECTRUM, bo tam nawet nie można było zrobić literówki w komendzie.


    Kompilator nie ma się "domyślać" czegokolwiek. Kompilator ma pilnować, żeby popełnione błędy były jasno i wyraźnie wskazywane. Natomiast język ma wymagać jak najdokładniejszego wyrażania myśli przez programistę. Nawet kosztem ograniczenia dostępnych środków wyrazu. Basic tego warunku nie spełnia. C++ również.

    Chiałbym żeby język nie traktował takich wyrażeń jak w pierwszych postach tego wątku jako poprawnych. Żeby rozróżniał typ całkowitoliczbowy od logicznego. I jeszcze kilka podobnych.

    Cytat:

    Ale nikt nie wymaga od programisty (początkującego), zeby wykorzystywał od razu wszystkie mozliwości C++. Nikt nie biega, zanim nie nauczy się chodzić. A złe nawyki - czyli pisanie programu bez całkowitego rozumienia tworzonego kodu są niezależne od stosowanego kompilatora.


    Ten argument się ciągle powtarza w takich dyskusjach. Pamiętasz jak uczyłeś się jeździć na rowerze? Zacząłeś od górala z 10 przerzutkami czy prostego roweru z kółkami po bokach? Przecież nikt Ci nie kazał używać tych przerzutek.

    To pierwsze to C++. To drugie Java.

    pozdrawiam
  • Poziom 27  
    A jednak... Zaczynałem od Basica, bo tylko taki był dostępny na ZX Spectrum. Potem Pascal - bo pojawił się kompilator i był dużo szybszy. C pojawiło się wraz z PC. I nie pamiętam już wiele z Basica, nie używam od lat Pascala, a w C korzystam z tych narzędzi, które znam - zwłaszcza, jak muszę napisać kod "na wczoraj". Potęga C polega na tym, że możemy napisać kod korzystając ze standardowych, liniowych sposbów kodowania, ale możemy też sięgnąć po obiekty i tu ogranicza nas tylko wyobraźnia i doświadczenie. Napisałem "kompilator się domyśla", bo tak właśnie korzysta z języków programowania większość młodocianych adeptów - przepisują kod bez zrozumienia istoty problemu, a potem zakładają, że "ktoś" (kompilator ?) wyłapie ich błędy. To są konsekwencje królowania systemu Windows z ich tzw. "intuicyjnym interfejsem użytkownika". Ilu widziałeś młodych "profesjonalistów" klikających na chybił-trafił po opcjach instalacji nowego urządzenia, bo przecież któraś opcja w końcu zadziała? I dlatego uważam, że C powinno być podstawowym językiem programowania wprowadzanym choćby na uczelniach dlatego, że uczy odpowiedzialności za to, co się robi. Kompilator C uważa, że "programista ma zawsze rację". I o to chodzi.

    Pozdrawiam

    Mariusz
  • Specjalista Automatyk
    To co piszesz dotyczy kilku zupełnie IMO rozdzielnych zagadnień.
    1. "Profesjonaliści" - widziałem nie tylko młodych, ale fakt że jest coraz gorzej. Ja akurat nie zwalam winy na Windows czy środowiska graficzne. Jest to moim zdaniem naturalna konsekwencja zejścia technologii pod strzechy. Oraz problemów z systemem edukacji w naszym kraju. Zresztą jakaś część tych "pro" stanie się pro.
    2. Wyłapywanie błędów - to nie poprawianie błędów. Od tego są komputery żeby sprawdzały. Im lepiej wyłapią tym łatwiej zrozumieć swój lub cudzy błąd i go poprawić.
    3. C / C++ mają oczywiście swoje miejsce. Tobie najwygodniej pisze się w C, komuś innemu w Perlu a jeszcze innemu w Erlangu. Każdy programista ma swoje preferencje - ale to nie temat tej dyskusji.
    4.
    Cytat:
    I dlatego uważam, że C powinno być podstawowym językiem programowania wprowadzanym choćby na uczelniach dlatego, że uczy odpowiedzialności za to, co się robi. Kompilator C uważa, że "programista ma zawsze rację". I o to chodzi.

    Zgadzam się w pełni. C/C++ powinny być (i chyba są) podstawowymi językami programowania na wszelkich studiach informatycznych. Podstawowymi nie musi znaczyć pierwszymi. Sądzę że zaczynanie od C#/Javy ma istotne zalety, nawet w kontekście uczelni z wykładami, ćwiczeniami, laborkami i pełnym akademikiem ludzi którzy mogą pomóc.
    Te zalety są znacznie bardziej widoczne gdy ktoś uczy się sam, z książki, eksperymentując. Takich osób jest wiele i do nich kieruję przesłanie: C#/Java.