- •Передмова
- •Передмова до першого видання
- •Вступ
- •Вступний урок
- •Твердження for
- •Масиви
- •Аргументи - виклик за значенням
- •Типи, оператори та вирази
- •Константи
- •Оголошення
- •Оператори приросту та спаду
- •Оператори та вирази присвоєння
- •Вирази умов
- •Керування потоком
- •Вирази та блоки
- •If-else
- •Else if
- •Switch
- •Цикли while та for
- •Файли заголовка
- •Препроцесор C
- •Покажчики та масиви
- •Покажчики й адреси
- •Покажчики та масиви
- •Арифметика адрес
- •Аргументи командного рядка
- •Структури
- •Масиви структур
- •Покажчики на структури
- •Typedef
- •Сполуки
- •Ungetc
- •Виконання команд
- •Генератор випадкових чисел
- •Open, creat, close, unlink
50 РОЗДIЛ 2. ТИПИ, ОПЕРАТОРИ ТА ВИРАЗИ
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
/* srand: встановлює зерно для rand() */ void srand(unsigned int seed)
{
next = seed;
}
Вправа 2-3. Напишiть функцiю htoi(s), яка би обертала ланцюжок з шiстнадцяткових цифр (включаючи можливий 0x або 0X) на вiдповiдне цiле значення (int). Дозволеними цифрами є 0 до 9, a до f або A до F.
2.8Оператори приросту та спаду
C забезпечує двома незвичними операторами приросту та спаду змiнних. Оператор приросту ++ додає 1 до свого операнду, тодi як –, навпаки, вiднiмає 1. Ми вже не раз користувалися ++ для збiльшення значення змiнних, як, наприклад, у
if (c == ’\n’) ++nl;
Незвична сторона полягає в тому, що як ++, так i – можуть використовуватись як префiкснi оператори (перед змiнною, наприклад ++n), так i постфiкснi (пiсля змiнної: n++). В обох випадках, як наслiдок — збiльшується значення n. Але вираз ++n збiльшує n до того, як це значення буде використане, тодi як n++ збiльшує n пiсля того, як було використане початкове значення. Це означає, що в контекстi, де дiйсно використовується значення, а не тiльки самий ефект, ++n i n++ — вiдмiннi. Якщо n дорiвнює 5, тодi
x = n++;
присвоїть x значення 5, зате у випадку
x = ++n;
x дорiвнюватиме вже 6. В обох випадках, n стане рiвним 6. Оператори приросту та спаду можуть використовуватись тiльки зi змiнними; вирази на кшталт (i+j)++ забороненi.
У контекстi, коли значення не потрiбне, а тiльки ефект приросту, як наприклад
2.8. ОПЕРАТОРИ ПРИРОСТУ ТА СПАДУ |
51 |
if (c == ’\n’) nl++;
префiкс i постфiкс тотожнi. Але iснують випадки, коли треба звернутися тiльки до одного, або тiльки до iншого. Наприклад, розглянемо функцiю squeeze(s,c), яка вилучає всi знайденi знаки c з ланцюжка s.
/* squeeze: вилучає всi c з s */ void squeeze(char s[], int c)
{
int i, j;
for (i = j = 0; s[i] != ’\0’; i++) if (s[i] != c)
s[j++] = s[i]; s[j] = ’\0’;
}
Кожного разу, як знайдено не-c, його скопiйовано до поточної позицiї j, i тiльки пiсля цього j збiльшено, щоб бути готовим до наступного знака. Це точний еквiвалент
if (s[i] != c) { s[j] = s[i]; j++;
}
Iнший приклад подiбної конструкцiї походить з функцiї getline, яку ми написали в Роздiлi 1, де ми можемо замiнити
if (c == ’\n’) { s[i] = c; ++i;
}
на компактнiше
if (c == ’\n’) s[i++] = c;
52 |
РОЗДIЛ 2. ТИПИ, ОПЕРАТОРИ ТА ВИРАЗИ |
В якостi третього прикладу, розглянемо стандартну функцiю strcat(s,t), яка зчеплює ланцюжок t iз кiнцем ланцюжка s. strcat припускає, що s має досить мiсця, щоб зберегти комбiнацiю обох ланцюжкiв. Так, як ми це написали, strcat не повертає жодного значення; версiя зi стандартної бiблiотеки повертає покажчик на отриманий ланцюжок.
/* strcat: зчеплює t iз кiнцем s; s має бути досить великим */ void strcat(char s[], char t[])
{
int i, j;
i = j |
= 0; |
|
while (s[i] != ’\0’) |
/* знаходить кiнець s */ |
|
i++; |
|
|
while ((s[i++] = t[j++]) != ’\0’) |
/* копiює t */ |
|
; |
|
|
}
Одночасно з копiюванням t до s до них застосовано постфiксний ++, щоб упевнитись, що вони в позицiї для наступного проходження через цикл.
Вправа 2-4. Напишiть альтернативну версiю squeeze(s1,s2), яка би вилучала кожний знак iз s1, який збiгається iз будь-яким знаком s2.
Вправа 2-5. Напишiть функцiю any(s1, s2), яка повертає перше положення в ланцюжку s1 одного iз знакiв ланцюжка s2, або -1, якщо жодного не знайдено. (Функцiя strpbrk зi стандартної бiблiотеки здiйснює те саме, тiльки повертає покажчик на положення.)
2.9Розряднi оператори
Мова C забезпечує шiстьма операторами для обробки бiтiв; їх можна застосовувати лише з цiлочисельними операндами, тобто char, short, int i long, як зi знаком, так i беззнаковими. & розрядний I | розрядний включний АБО ˆ розрядний виключний АБО << лiве змiщення >> праве змiщення ˜ доповнення (унарний оператор) Розрядний I (оператор &) часто використовується для того, щоб приховати набiр бiтiв, наприклад
n = n & 0177;
обнулює всi бiти n крiм молодших 7-и.
Розрядний АБО (оператор |) використовується для ввiмкнення бiтiв:
x = x | SET_ON;
2.9. РОЗРЯДНI ОПЕРАТОРИ |
53 |
встановлює в одиницю всi бiти x, що дорiвнюють одному в SET_ON. Розрядний виключний АБО (оператор ˆ) встановлює в одиницю кожну позицiю, де операнди мають вiдмiннi бiти, i в нуль там де вони збiгаються.
Не слiд плутати розряднi оператори & та | з логiчними операторами && та ||, в яких iдеться про злiва направо оцiнку iстинного значення. Наприклад, якщо x дорiвнює 1 а y дорiвнює 2, тодi x & y оцiнюється як нуль, зате x && y — як один.
Оператори змiщення << й >> здiйснюють лiве та праве змiщення лiвого операнда на кiлькiсть бiтiв, вказаних правим операндом; останнiй має бути додатнiм числом. Таким чином, x << 2 змiщує значення x на два положення, заповнюючи вакантнi бiти нулями; це рiвнозначно множенню на 4. Праве змiщення беззнакової величини завжди заповнюватиме звiльненi бiти нулями. Праве змiщення величини зi знаком заповнюватиме бiтами знака ( арифметичне змiщення ) на деяких машинах i 0-бiтами ( логiчне змiщення ) на iнших.
Унарний оператор ˜ видає протилежне значення вказаного цiлого; тобто, вiн перетворює кожний 1-бiт на 0-бiт, i навпаки. Наприклад
x = x & ~077
встановлює останнi шiсть бiтiв x у нуль. Зауважте, що x & ˜077 не залежить вiд довжини слова , тому йому надається перевага над, скажiмо, x & 0177700, яке припускає, що x — 16-бiтна величина. Портабельна форма не забирає додаткових ресурсiв оскiльки ˜077 — це сталий вираз, який оцiнюється пiд час компiляцiї.
Як iлюстрацiя деяких розрядних операторiв розглянемо функцiю getbits(x,p,n), яка повертає (вирiвняне з правого боку) n-бiтне поле x, починаючи з положення p. Ми припустимо, що бiтове положення 0 знаходиться з крайнього правого боку i, що n iз p — це чиннi додатковi значення. Так, наприклад, getbits(x,4,3) повертає три бiти, що знаходяться у 4-iй, 3-iй i 2-iй позицiї, вирiвнянi справа.
/* getbits: добуває n бiтiв, починаючи з положення p */ unsigned getbits(unsigned x, int p, int n)
{
return (x >> (p+1-n)) & ~(~0 << n);
}
Вираз x >> (p+1-n) перемiщує потрiбне нам поле на правий край слова. ˜0 складається з одних 1-бiтiв; змiщення його лiворуч на n позицiй за допомогою ˜0<<n розмiстить нулi в n бiтiв з правого боку; додання до цього ˜ створює маску з одиниць для n бiтiв справа.
Вправа 2-6. Напишiть функцiю setbits(x,p,n,y), яка би повертала x, в якому n бiтiв, починаючи з положення p, дорiвнюють крайнiм правим n бiтам y, залишаючи решту бiтiв незмiнними.
