Popularny post ethanak Napisano Kwiecień 10, 2021 Popularny post Udostępnij Napisano Kwiecień 10, 2021 Ten artykuł jest częścią serii "Arduino dla początkujących: instrukcje sterujące w C/C++" #1 - if, bloki #2 - while i coś jeszcze #3 - for, break, switch W poprzednich częściach artykułu poznaliśmy dwie podstawowe instrukcje sterujące: if i while. Teraz kolej na coś bardziej rozbudowanego. Przyjrzyjmy się jak wygląda Pętla z efektami, czyli instrukcja "for" Zapis tej instrukcji jest następujący: for (START; WARUNEK; KROK) INSTRUKCJA; Wykonuje się ona w sposób następujący: Najpierw wykonywana jest instrukcja określona jako START. Następnie sprawdzany jest WARUNEK. Jeśli nie jest spełniony następuje wyjście z pętli. Jeśli jest - wykonywane są kolejno INSTRUKCJA i KROK, a następnie następuje powrót do sprawdzenia warunku. Wszystkie trzy wyrażenia (START, WARUNEK i KROK) mogą być pominięte, przy czym pominięcie WARUNEK jest równoznaczne z "prawda". Spróbujmy rozpisać to za pomocą goto: START; etykieta: if (WARUNEK) { INSTRUKCJA; KROK; goto etykieta; } Możemy to zobrazować również za pomocą diagramu: Możemy sobie napisać prosty program, pokazujący działanie instrukcji for: #define BUTTON_PIN 2 void setup() { Serial.begin(9600); pinMode(BUTTON_PIN, INPUT_PULLUP); // ustawiamy pin jako wejście z podciągnięciem do Vcc } void loop() { if (digitalRead(BUTTON_PIN) == LOW) { int i; for ( i=0; i<10; i += 1) { Serial.println(i); } } } Jako ćwiczenie możemy przewidzieć, jak będzie wyglądać działanie programu a potem, po uruchomieniu sprawdzić, czy zachowuje się zgodnie z naszymi oczekiwaniami. Obie instrukcje (while i for) mają pewną "zdegenerowaną" formę, czyli w skrócie "kręć się w kółko". Będą to odpowiednio: while(1) INSTRUKCJA; // nieskończona pętla while for(;;) INSTRUKCJA; // nieskończona pętla for Jak jednak wyjść z takiej nieskończonej pętli? Oczywiście, zawsze się znajdzie Wyjście awaryjne, czyli instrukcja "break" Wykonanie instrukcji break powoduje natychmiastowe przerwanie pętli i wznowienie wykonania od następnej instrukcji za pętlą. Czyli jeśli w programie mamy konstrukcję: while (WARUNEK) { INSTRUKCJE; if (przerywamy) break; INSTRUKCJE; } to możemy ją sobie wyobrazić jako: etykieta: if (WARUNEK) { INSTRUKCJE; if (przerywamy) goto etykieta2; INSTRUKCJE; goto etykieta; } } etykieta2: Za pomocą instrukcji break możemy oczywiście przerwać nie tylko nieskończone pętle ale również takie, które mają swój warunek kończący. Instrukcja ta może oczywiście zawierać się w zagnieżdżonym bloku - jej wykonanie spowoduje wyjście z bloku i zniszczenie należących do niego zmiennych, tak jakby było to normalne dojście programu do końca bloku. Tak więc pętle mamy już opanowane - pozostała nam jeszcze jedna instrukcja, a mianowicie if na sterydach, czyli instrukcja "switch" Za pomocą tej instrukcji możemy wybrać jedną z kilku możliwości, nie jesteśmy ograniczeni do dwóch. Przykładowy zapis będzie wyglądać w ten sposób: switch(WYRAŻENIE) { case STAŁA1: INSTRUKCJE1; case STAŁA2: INSTRUKCJE2; case STAŁA3: INSTRUKCJE3; default: INSTRUKCJE4; } Jak widać, nie jesteśmy tu ograniczeni do pojedynczej instrukcji jak poprzednio; INSTRUKCJE to po prostu ciąg dowolnej ilości instrukcji, z zerem włącznie, np. tak: switch(x) { case 1: case 2: // robimy coś case 3: // robimy coś innego default: // robimy coś jeszcze innego } Oczywiście i tu mamy pewne ograniczenia: WYRAŻENIE powinno zwracać wartość typu int, a argumentem case mpożę być wyłącznie stała tego typu. Tak więc konstrukcje typu: int n,x; n= cośtam(); x=cośtam(); switch(x) { case n: albo (widywana w próbach rozwiązań zadań z kursu): String s; s=cośtam(); switch(s) { case "zielona": będą nieprawidłowe; pierwsza ponieważ argumentem case nie jest stała, drugie ponieważ zarówno wyrażenie, jak i stała napisowa użyta w case nie jest typu int. I tu bardzo ważna uwaga. Dojście do kolejnej etykiety case nie przerywa toku programu; zostają wykonane następne instrukcje. Najlepiej wyjaśni to rozpisanie za pomocą goto: R=WYRAŻENIE; // gdzie R to wewnętrzna, niejawna zmienna instrukcji switch if (R == STAŁA1) goto etykieta1; // case STAŁA1 if (R == STAŁA2) goto etykieta2; // case STAŁA2 if (R == STAŁA3) goto etykieta3; // case STAŁA3 goto etykieta4; //default etykieta1: INSTRUKCJE1; etykieta2: INSTRUKCJE2; etykieta3: INSTRUKCJE3; etykieta4: INSTRUKCJE4; lub za pomocą diagramu: Klauzula default powinna zawsze wystąpić w instrukcji switch, jednak jej brak nie jest błędem. W takiej sytuacji kompilator uzna, że domyślnym zachowaniem w przypadku, gdy wartość wyrażenia nie jest równa żadnej z wartości w klauzulach case jest "nic nie rób" - tak jakby przed zamykającym nawiasem dopisywał sobie dodatkową, brakującą etykietę default. Zostało nam jedno: dojście do kolejnego case nie powoduje przerwania instrukcji switch, ale w większości przypadków jest nam to potrzebne. Tak jak w instrukcji if mamy dwie niezależne gałęzie, tak tu chcielibyśmy mieć ich więcej, równie niezależnych. Do tego celu służy nam znana już instrukcja break. Jej wykonanie powoduje natychmiastowe zakończenie wykonania instrukcji switch. Przykładowy kod może wyglądać tak: switch(WYRAŻENIE) { case STAŁA1: INSTRUKCJE1; break; case STAŁA2: INSTRUKCJE2; break; case STAŁA3: INSTRUKCJE3; break; default: INSTRUKCJE4; } W ten sposób zależnie od wartości wyrażenia zostanie wykonany jeden, konkretny ciąg instrukcji. Co więcej, kompilator (jeśli włączymy wszystkie ostrzeżenia) będzie spodziewał się, że instrukcja break jest w danym miejscu (czyli przed case/default) potrzebna i jej brak uzna za nasze przeoczenie. Czyli fragment typu: case 1: x = x+1; case 2: y = y-1; uzna za co najmniej podejrzany. Jak mu wyjaśnić, że o to nam chodziło i ma się nie wtrącać? Do tego służy specjalna postać komentarza. Jeśli przekształcimy nasz kod w sposób następujący: case 1: x = x+1; // pass thru case 2: y = y+1; kompilator uzna, że komentarz "pass thru" oznacza, że tak miało być i tu nie trzeba żadnego break. I to już wszystko - możemy zabrać się do pisania programów. I na koniec przypomnienie: co prawda instrukcja goto występuje w językach C i C++, ale umówmy się: nie będziemy jej używać w rzeczywistych programach. Ale... jeśli ktoś zetknął się z sytuacją, której nie można rozwiązać tylko za pomocą omawianych instrukcji (bez goto) albo przynajmniej jest to utrudnione - niech da znać w komentarzach! 6
Treker (Damian Szymański) Kwiecień 13, 2021 Udostępnij Kwiecień 13, 2021 @ethanak artykuł został właśnie zaakceptowany i jest już widoczny publicznie
BananWszyscy Kwiecień 13, 2021 Udostępnij Kwiecień 13, 2021 Przy okazji mogłeś wspomnieć o tajemniczym dla niektórzych enum ;p
ethanak Kwiecień 13, 2021 Autor tematu Udostępnij Kwiecień 13, 2021 5 minut temu, BananWszyscy napisał: Przy okazji mogłeś wspomnieć o tajemniczym dla niektórzych enum A jaki jest tytuł artykułu? Ale zapisuję sobie na przyszłość. 1
ethanak Kwiecień 13, 2021 Autor tematu Udostępnij Kwiecień 13, 2021 5 minut temu, _LM_ napisał: Czasami działa też //no break A, tego nawet nie wiedziałem, w dokumentacji do GCC znalazłem tylko pass thru. Zawsze dobrze wiedzieć...
_LM_ Kwiecień 13, 2021 Udostępnij Kwiecień 13, 2021 (edytowany) Hej a gdzie instrukcja continue? No i dla wielu następujących po sobie wartości w switch (które mają dać ten sam wynik) można zrobić switch(x) { case 0: printf("zero"); break; case 1 ... 9: printf("przedzial od jeden do 9" ); break; } Edytowano Kwiecień 13, 2021 przez _LM_ 1
Popularny post ethanak Kwiecień 13, 2021 Autor tematu Popularny post Udostępnij Kwiecień 13, 2021 Przed chwilą, _LM_ napisał: Hej a gdzie instrukcja continue? W aneksie (na razie zbieram co pominąłem, ale aneks będzie). 3
_LM_ Kwiecień 13, 2021 Udostępnij Kwiecień 13, 2021 5 minut temu, ethanak napisał: (na razie zbieram co pominąłem, ale aneks będzie). To jeszcze jeśli mogę podpowiedzieć, to dorzuć operator trójargumentowy 1
ethanak Kwiecień 13, 2021 Autor tematu Udostępnij Kwiecień 13, 2021 No, ale to nie jest instrukcja sterująca tylko operator. Bez przesady. Może go gdzieś "przy okazji" wcisnę. 1
BananWszyscy Kwiecień 14, 2021 Udostępnij Kwiecień 14, 2021 14 godzin temu, ethanak napisał: A jaki jest tytuł artykułu? Ale zapisuję sobie na przyszłość. ale jak to switch bez enuma?
ethanak Kwiecień 14, 2021 Autor tematu Udostępnij Kwiecień 14, 2021 32 minuty temu, BananWszyscy napisał: ale jak to switch bez enuma? A na przykład tak: int a = getchar_non_blocking(); // cokolwiek by to było if (a) { switch (tolower(a)) { case 'n': printf("Nacisnąłeś NIE\n"); break case 'y': case 't': printf("Nacisnąłeś TAK\n"); break; case '0' ... '9': printf("Nacisnąłeś cyfrę\n"); break; default: printf("Coś nacisnąłeś\n"); } } Gdzie tu widzisz dobre miejsce dla enumów?
Treker (Damian Szymański) Kwiecień 14, 2021 Udostępnij Kwiecień 14, 2021 4 minuty temu, BananWszyscy napisał: Widzę, że się nie zrozumieliśmy... eot @BananWszyscy może pokaż propozycję swojego kodu? Na pewno każdy skorzysta
ethanak Kwiecień 14, 2021 Autor tematu Udostępnij Kwiecień 14, 2021 @Treker Po prostu są tacy, co uważają że enumy zostały wynalezione specjalnie do switchy i to ich główne (jak nie jedyne) zadanie. A tymczasem można o tym cały długi artykuł napisać... i nawet o switchu nie wspomnieć 1
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »