Конструкція
if (вираз)
твердження
else if (вираз)
твердження
else if (вираз)
твердження
else if (вираз)
твердження
else
твердження
зустрічаються настільки часто, що вимагають невеликого обговорення. Ця форма if-виразів являється найбільш узагальненим способом написання виразів вибору з-поміж багатьох напрямків. Умови (вираз) розглядаються по-черзі, якщо якась з умов справджується, твердження, пов'язані з цією умовою буде виконано, і це покладе кінець усьому ланцюжку. Звичайно, що код кожного виразу може складатися як з одного рядка, так і з групи, включеної у фігурні дужки.
Останнє else має справу з випадками, що не збіглися з жодною умовою вище, це те що виконуватиметься поза вибором, якщо всі умови не справдилися. Іноді може не бути жодної дії яку потрібно би було виконати поза вибором, у таких випадках завершуючє
else
твердження
можна не включати, або його можна використати для вловлювання помилок на кшталт «неможливих» умов.
Для ілюстрації вибору у трьох напрямках, ось функція двійчастого пошуку, яка виявляє чи певне значення x з'являється у масиві v. Елементи v мають знаходитись у порядку зростання. Функція повертає позицію (число між 0 та n-1), якщо значення x знайдено у v, і -1 — якщо ні.
Двійчастий пошук спочатку порівнює (введене) значення x із середнім елементом масиву v. Якщо x менший за значення посередині, тоді пошук зосереджується на нижній частині списку, якщо навпаки — більший, то на верхній. В обох випадках, наступним кроком буде порівняння xіз середнім елементом вибраної половини. Цей процес поділу відрізків на два продовжується доти, доки значення не буде знайдено, або діапазон виявиться порожнім.
/* binsearch: знаходить x серед v[0] <= v[1] <= ... <= v[n-1] */
int binsearch(int x, int v[], int n)
{
int low, high, mid;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low+high) / 2;
if (x < v[mid])
high = mid - 1;
else if (x > v[mid])
low = mid + 1;
else /* знайдено збіг */
return mid;
}
return -1; /* збігу немає */
}
Фундаментальним рішенням є, чи x менший, більший, чи рівний середньому елементу v[mid]при кожному поступі; це дуже властиво else-if.
Вправа 3-1. Наш двійчастий пошук здійснює дві перевірки всередині циклу, тоді як однієї може вистачити (за умови додаткових перевірок поза циклом). Напишіть версію із одним тестуванням всередині циклу і порівняйте різницю в швидкості виконання програми.
Switch
Твердження switch здійснює вибір серед багатьох імовірностей шляхом перевірки, чи вираз збігається з одним із сталих цілочисельних значень, відповідно відгалужуючись.
switch (вираз) {
case сталий-вираз: твердження
case сталий-вираз: твердження
default: твердження
}
Кожний випадок case помічено однією або більше цілочисельною сталою або сталим виразом. У випадку збігу зі значенням виразу, відбудеться виконання пов'язане з даною умовою. Усі вирази умов мають бути різними. Випадок, позначений як default, буде виконано, якщо жодна із попередніх умов не виявилася дійсною. default не є обов'язковим, якщо його не вказано і, якщо жоден з випадків не зійшовся — нічого не відбудеться. Випадки й уставна дія (default) можуть знаходитись у довільній послідовності.
У Розділі 1 ми написали програму для підрахунку кожної цифри, пробілів та інших знаків у тексті, використовуючи послідовність if ... else if ... else. Ось та сама програма зі switch:
#include <stdio.h>
main() /* підраховує цифри, пробіли та інші знаки */
{
int c, i, nwhite, nother, ndigit[10];
nwhite = nother = 0;
for (i = 0; i < 10; i++)
ndigit[i] = 0;
while ((c = getchar()) != EOF) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ndigit[c-'0']++;
break;
case ' ':
case '\n':
case '\t':
nwhite++;
break;
default:
nother++;
break;
}
}
printf("digits =");
for (i = 0; i < 10; i++)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d\n", nwhite, nother);
return 0;
}
Вираз break спричиняє негайний вихід зі switch. Оскільки case-випадки служать тільки в якості ярликів, то, після того, як код одного випадку виконано, виконання спадає вниз до наступного case, хіба що ви явно вкажете вийти. break і return — це найпоширеніші способи виходу зі switch. Вираз break так само може вживатися для того, щоб негайно покинути цикли while, for і do (ми обговоримо це пізніше у цьому розділі).
Низпадання крізь випадки case має хорошу і погану сторону. Добре це тим, що дозволяє декілька випадків прикріпити до однієї дії, як це сталося з числами у попередньому прикладі. Але це також означає, що за звичайних обставин, кожний окремий випадок case повинен закінчуватись break, щоб запобігти спаданню вниз до наступного. Спадання від одного випадку до іншого не є надійним, і схильне до розладу після зміни програми. За винятком складених ярликів для одного обчислення, низспадання слід використовувати помірно і коментувати.
Хорошим стилем вважається додання break після останнього випадку case (в попередньому прикладі це default), навіть якщо логічно в цьому немає потреби. Одного дня, коли буде додано інший case вкінці, ця невеличка хитрість може вас врятувати.
Вправа 3-2. Напишіть функцію escape(s, t), яка перетворює такі знаки як новий рядок і крок табуляції на явні екрановані послідовності такі як \n та \t під час копіювання ланцюжка t до s. Застосуйте switch. Напишіть також зворотню функцію, яка би перетворювала екрановані послідовності знаків на дійсні знаки.