Mikroprocesorowa ośla łączka, część 20
Kolejna część podsumowująca dotyczy instrukcji języka C. Większość z nich powinna być dobrze znana, gdyż były szeroko stosowane w przykładowych programach. Jednak sam język ma też instrukcje, które nie są zbyt często używane. Poza tym warto poznać ich cechy – dowiedzieć się jak działają.
Działanie programów będzie uruchamiane pod kontrolą symulatora wbudowanego w środowisko Atmel Studio, gdyż zwalnia to nas z budowania odpowiedniego środowiska sprzętowego, a możliwości symulatora są wystarczające żeby poznać szczegóły ich działania.
Warunki w instrukcjach
Jednym z istotnych elementów wielu instrukcji jest zapis warunku, który w zależności od wartości logicznej wyrażenia (typu tak/nie) będzie wpływał na wykonanie instrukcji. W wielu językach istnieje oddzielny typ reprezentujący typ logiczny. W języku C nie ma dedykowanego takiego typu i jest on „emulowany” przez typ o najmniejszej zajętości w pamięci, czyli uint8_t jako liczba 8-bitowa bez znaku (jeżeli mamy potrzebę utworzenia zmiennej przechowującej wartość logiczną, przyjmując, że 0 odpowiada nie/false oraz 1 to tak/true). Wyrażenie warunkowe służy do zbadania określonej relacji zachodzącej między zmienną a inną zmienną lub stałą. Zapis tego w języku C odpowiada intuicyjnemu podejściu i ma postać: <zmienna> <operator relacji> <zmienna>, gdzie <zmienna> jest identyfikatorem zmiennej lub stałą a <operator relacji> jest znakiem lub dwuznakiem identyfikującym relację (zapis <stała> <operator relacji> <stała> z logicznego punktu widzenia nie ma sensu, choć jest poprawny w sensie gramatyki języka). Tego typu operatory są dwuargumentowe (po obu stronach operatora musi w ogólnym przypadku wystąpić wyrażenie). Dostępne są następujące operatory:
• > to operator większe, ma wartość tak, jeżeli wartość wyrażenia z lewej strony jest większa od wartości wyrażenia z prawej strony operatora, w przeciwnym wypadku wynikiem jest nie,
• >= to operator większe lub równe, ma wartość tak, jeżeli wartość wyrażenia z lewej strony jest większa lub równa wartości wyrażenia z prawej strony operatora, w przeciwnym wypadku wynikiem jest nie,
• < to operator mniejsze, ma wartość tak, jeżeli wartość wyrażenia z lewej strony jest mniejsza od wartości wyrażenia z prawej strony operatora, w przeciwnym wypadku jest nie,
• <= to operator mniejsze lub równe, ma wartość tak, jeżeli wartość wyrażenia z lewej strony jest mniejsza lub równa z wartością wyrażenia z prawej strony operatora, w przeciwnym wypadku jest nie,
• == to operator równe, ma wartość tak jeżeli wartość wyrażenia z lewej strony jest równa wartości wyrażenia z prawej strony operatora, w przeciwnym wypadku jest nie,
• != to operator różne, ma wartość tak, jeżeli wartość wyrażenia z lewej strony jest inna niż wartość wyrażenia z prawej strony operatora, w przeciwnym wypadku jest nie.
Każde wyrażenie logiczne typu <coś> <operator> <coś> jest prostym wyrażeniem logicznym. Dwa takie wyrażenia połączone operatorem logicznym mogą tworzyć bardziej złożone wyrażenia warunkowe. Możliwe są następujące operatory łączące wyrażenia proste:
• || to operator logiczny or (alternatywa/suma logiczna/lub),
• && to operator logiczny and (koniunkcja/iloczyn logiczny/i).
Występuje jeszcze operator jednoargumentowy negacji (w języku C oznaczany znakiem !). Ten operator dotyczy wyrażenia zapisanego za operatorem.
Oprócz zwyczajowo rozumianego podejścia do wyrażeń logicznych przedstawionych wyżej, w języku C występuje mniej intuicyjna wersja, przykładowo if ( Zmienna ), gdzie zmienna jest dowolnego typu (nawet typu float). Taka konstrukcja w języku C jest dopuszczalna i przyjmuje wartość nie, jeżeli Zmienna przechowuje liczbę zero oraz tak, jeżeli wartość zmiennej jest różna od zera. Można taki zapis interpretować w sposób pokazany na rysunku 1.
W obliczaniu wartości złożonych wyrażeń logicznych, poszczególne operatory mają swoje priorytety (podobnie jak w wyrażeniach arytmetycznych w pierwszej kolejności są wykonywane mnożenia, w drugiej dodawania). W sytuacji, gdy występują wątpliwości co do kolejności działań, najlepiej jest użyć zapisu z nawiasami (nadmiar nawiasów jeszcze nigdy nie zaszkodził). Do ilustracji tej problematyki utworzyłem program (U105A, dostępny w materiałach dodatkowych), którego istotną część pokazuje listing 1:
int main ( void )
{
uint8_t Loop ;
for ( Loop = 0 ; Loop < 8 ; Loop ++ )
{
Set ( Loop ) ;
Res1 [ Loop ] = Arg1 || Arg2 && Arg3 ;
Res2 [ Loop ] = ( Arg1 || Arg2 ) && Arg3 ;
Res3 [ Loop ] = Arg1 || ( Arg2 && Arg3 ) ;
} /* for */ ;
while (1)
{
}
}
Zadaniem programu jest pokazanie różnicy w obliczaniu wartości wyrażenia z „domyślnym” priorytetem operatorów logicznych oraz z dwoma wariantami priorytetów „narzuconych” poprzez zastosowanie nawiasów. Program w pętli (osiem obrotów) poprzez wywołanie funkcji Set inicjuje zmienne „logiczne” na jedną z ośmiu możliwych kombinacji oraz oblicza wartość wyrażenia logicznego Arg1 || Arg2 && Arg3 (suma logiczna Arg1 i Arg2 oraz iloczyn logiczny z Arg3). Wyniki obliczeń dla różnych notacji zapisu wyrażenia są zachowane w tablicach Res1, Res2 i Res3. Po uruchomieniu programu w symulatorze, z pokazanych wyników (rysunek 2) można wysnuć wniosek, że wyrażenie zapisane jako Arg1 || Arg2 && Arg3 jest tożsame z wariantem w postaci Arg1 || ( Arg2 && Arg3 ). W pierwszej kolejności wykonywany jest iloczyn logiczny a w drugiej suma logiczna.
(…)
——– ciach! ——–
To jest tylko fragment artykułu, którego pełna wersja ukazała się w styczniowym numerze czasopisma Zrozumieć Elektronikę (ZE 1/2026). Pełną wersję czasopisma znajdziesz pod tym linkiem. Natomiast niepełna, okrojona wersja, pozwalająca zapoznać się z zawartością numeru ZE 1/2026 znajduje się tutaj.
Andrzej Pawluczuk
apawluczuk@vp.pl
Uwaga! Wskazówki, jak nabyć pełne wersje dowolnych numerów ZE znajdują się na stronie:
https://piotr-gorecki.pl/n11.

