2 TYPY, OPERATORY I WYRAŻENIA_______
W tablicy 2.1 na końcu tego rozdziału podano priorytety i łączność wszystkich operatorów.
Relacje i operatory logiczne
Operatorami relacji są
>>=<<=
Wszystkie mają ten sam priorytet. Tuż za nimi - ze względu na priorytet - występują operatory przyrównania I
I
Operatory relacji mają priorytety niższe niż priorytety operatorów arytmetycznych, zatem wyrażenie typu i<lim-1 jest traktowane zgodnie z oczekiwaniami jako i<(lim-1).
Bardziej interesujące są operatory logiczne && i 11. Wyrażenia połączone tymi operatorami oblicza się od lewej strony do prawej, przy czym koniec obliczania następuje natychmiast po określeniu wyniku jako „prawda” lub „fałsz”. Wiele programów bazuje na tych właściwościach. Przykładem jest pętla wyjęta z wejściowej funkcji getline, którą napisaliśmy w rozdz. 1: ■
for (i=0; i<lim-1 && (c=getchar()) != ’\n’ && c != EOF; ++i) *
s[i] = c; 1
Przed przeczytaniem następnego znaku należy sprawdzić, czy jest miejsce na zapamiętanie go w tablicy S. Wobec tego sprawdzenie, czy klim-1, musi być wykonane jako pierwsze. Co więcej, jeśli zabraknie miejsca, to po prostu musimy się zatrzymać, by nie przeczytać następnego znaku. ■
Podobnie niefortunne byłoby porównanie wartości zmiennej c ze stałą EOF prz# wywołaniem funkcji getchar, zatem wywołanie to i przypisanie wartości zmienne c musi pojawić się przed sprawdzeniem znaku w c. I
i < lim—1 && (c = getchar()) != V && c != EOF ł!
nie potrzebują dodatkowych nawiasów. Ponieważ jednak priorytet operatora != j# wyższy niż priorytet operatora przypisania, więc na przykład w zwrocie
2.7 PRZEKSZTAŁCENIA TYPÓW__I poweredby I
Z definicji numeryczną wartością wyrażenia logicznego lub rrIaryj**ęrmTfłJ—11 jest ono prawdziwe, bądź 0 - jeżeli jest fałszywe.
Jednoargumentowy operator negacji ! (wykrzyknik) zamienia argument różny od zera na wartość 0, a argument równy zero - na wartość 1. Na ogół operator ! jest stosowany w konstrukcjach podobnych do tej:
if (!valid)
zamiast
if (valid == 0)
Trudno orzec, który z tych sposobów jest lepszy. Zdanie z !valid czyta się milej („jeśli niepoprawne”), bardziej skomplikowane konstrukcje mogą jednak być niezrozumiałe.
Ćwiczenie 2.2. Napisz pętlę równoważną z powyższą pętlą for, nie używając operatorów logicznych && i ||.
Jeżeli argumentami operatora są obiekty różnych typów, to są one przekształcane do jednego wspólnego typu według kilku reguł. Ogólna zasada mówi, że automatycznie wykonuje się tylko takie przekształcenia, w których argument „ciaśniejszy” jest zamieniany na „obszerniejszy” bez utraty informacji, jak przy przekształceniu liczby całkowitej na zmiennopozycyjną w wyrażeniu f + i. Wyrażenia, które nie mają sensu, np. używanie obiektu typu float do indeksowania tablicy, są niedozwolone. Wyrażenia, w których może wystąpić utrata informacji, jak po przypisaniu wartości o dłuższym typie całkowitym zmiennej krótszego typu czy po zamianie wartości zmiennopozycyjnej na całkowitą, mogą powodować wypisanie komunikatu ostrzegawczego, ale nie są zabronione.
Obiekty typu char są po prostu niewielkimi liczbami całkowitymi, można je więc swobodnie stosować w wyrażeniach arytmetycznych. Zapewnia to znaczną elastyczność przy różnego rodzaju przekształceniach znaków. Ilustracją jednego z nich jest następująca prościutka wersja funkcji atoi, która zamienia ciąg cyfr na jego numeryczny odpowiednik.
/* atoi: zamień s na wartość całkowitą */ int atoi(char s[ ])
{
int i, n;
69