Перетворення типів
ALTE DOCUMENTE
Якщо у виразах зустрічаються операнди різних типів, то вони перетворюються до загального типу відповідно до невеликого набору правил. Загалом, автоматично виробляються тільки перетворення, що мають сенс, такі як, наприклад, перетворення цілого плаває у виразах типу f+i . Вирази ж, позбавлені сенсу, такі як використання змінної типу float як індекс, заборонені.
По-перше, типи char і int можуть вільно змішуватися в арифметичних виразах: кожна змінна типу char автоматично перетворюється на int. Це забезпечує значну гнучкість під час проведення певних перетворень символів.
Приклад 3-2. Прикладом може бути функція atoi , яка ставить у відповідність рядку цифр її чисельний еквівалент.
atoi (char s []) // Перетворення s на ціле
Як вже обговорювалося в розділі 2, вираз s[i]-'0' має чисельне значення символу, що знаходиться в s[i], тому що значення символів '0', '1' і т.д. утворюють зростаючу послідовність розташованих поспіль цілих позитивних чисел.
Приклад 3-3. Інший приклад перетворення char в int дає функція lower , що перетворює цю велику літеру в рядкову. Якщо виступаючий як аргумент символ не є великою літерою, то lower повертає його незмінним. Нижче наведена програма справедлива тільки для набору символів ASCII.
lower( int c) // Перетворення великих у малі
Ця функція правильно працює при коді ASCII, тому що чисельні значення, що відповідають у цьому коді великим і малим буквам, відрізняються на постійну величину, а кожен алфавіт є суцільним - між a і z немає нічого, крім букв. Це останнє зауваження для набору символівEBCDIC систем IBM 370 виявляється несправедливим, внаслідок чого ця програма на таких системах працює неправильно – вона перетворює не лише літери.
При перетворенні символьних змінних на цілі виникає один тонкий момент. Справа в тому, що сама мова не вказує, чи повинні змінним типу char відповідати чисельним значенням зі знаком або без знака. Чи може у разі перетворення char в int вийти негативне ціле? На жаль, відповідь це питання змінюється від машини до машини, відбиваючи розбіжності у тому архітектурі. На деяких машинах (PDP-11, наприклад) змінна типу char, крайній лівий біт якої містить 1, перетворюється на негативне ціле («знакове розширення»). На інших машинах таке перетворення супроводжується додаванням нулів з лівого краю, у результаті завжди виходить позитивне число.
Визначення мови C гарантує, що будь-який символ зі стандартного набору символів машини ніколи не дасть негативного числа, так що ці символи можна вільно використовувати у виразах як позитивні величини. Але довільні комбінації двійкових знаків, що зберігаються як символьні змінні деяких машинах, можуть дати негативні значення, але в інших позитивні.
Найбільш типовим прикладом виникнення такої ситуації є випадок, коли значення 1 використовується як eof. Розглянемо програму:
На машині, яка не здійснює знакового розширення, змінна c завжди позитивна, оскільки вона описана як char, а так як eof негативно, то умова ніколи не виконується. Щоб уникнути такої ситуації, ми завжди передбачливо використовували int замість char для будь-якої змінної, яка отримує значення від getchar .
Основна ж причина використання int замість char не пов'язана збудь-яким питанням про можливе знакове розширення. просто функція getchar повинна передавати всі можливі символи (щоб її можна було використовувати для довільного введення) і, крім того, значення eof, що відрізняється. Отже значення eof може бути представлено як char , а повинно зберігатися як int .
Іншою корисною формою автоматичного перетворення типів є те, що вирази відношення, подібні i>j , та логічні вирази, пов'язані операціями && і , за визначенням, мають значення 1, якщо вони істинні, і 0, якщо вони помилкові. Таким чином, привласнення:
isdigit = c >= '0' && c
Вважається, що цеdigit дорівнює 1, якщо c - цифра, і дорівнює 0 в іншому випадку. (У перевірочній частині операторів if, while, for і т.д. «істина» просто означає «не нуль»).
Неявні арифметичні перетворення працюють переважно, як і очікується. Загалом, якщо операція типу + чи * , яка пов'язує два операнда (бінарна операція), має операнди різних типів, перед виконанням операції «нижчий» тип перетворюється на «вищого» і виходить результат «вищого» типу. Точніше, до кожної арифметичної операції застосовується наступна послідовність правил перетворення.
1. Типи char і short перетворюються на int , а float на double .
2. Потім, якщо один з операнда має тип double , то інший перетворюється в double , і результат має тип double .
3. В іншому випадку, якщо один з операндів має тип long, то інший перетворюється на long, і результат має тип long.
4. В іншому випадку, якщо один з операнда має тип unsigned , то інший перетворюється в unsigned і результат має тип unsigned .
5. В іншому випадку операнди повинні бути типу int і результат має типint.
Підкреслимо, що всі змінні типу float у виразах перетворюються на double ; у мові «C» вся арифметика, що плаває, виконується з подвійною точністю.
Перетворення виникають і при присвоєння; значення правої частини перетворюється на тип лівої, що є типом результату. Символьні змінні перетворюються на цілі або зі знаковим розширенням, або без нього, як описано вище. Зворотне перетворення int в char поводиться добре - зайві біти високого порядку просто відкидаються. Таким чином, якщо:
значення символу c не змінюється. Це вірно незалежно від того, залучається знакове розширення чи ні.
Якщо х типу float, а i типу int, то як
призводять до перетворень; при цьому float перетворюється на int відкиданням дробової частини. Тип double перетворюється на float округленням. Довгі цілі перетворюються на більш короткі цілі і змінні типу char у вигляді відкидання зайвих бітів високого порядку.
Так як аргумент функції є виразом, то при передачі функцій аргументів відбувається перетворення типів: зокрема, char і short стають int , а float стає double . Ось тому ми описували аргументи функцій як int і double навіть тоді, коли зверталися до них зі змінними типу char і float .
Нарешті, у будь-якому вираженні може бути здійснено («примушено») явне перетворення типу за допомогою конструкції, що називається переклад (cast). У цій конструкції, що має вигляд:
(ім'я типу) вираз
вираз перетворюється на зазначений тип за правилами перетворення, викладеними вище. Фактично точний сенс операції перекладу можна описати так: вираз хіба що надається деякою змінною зазначеного типу, яка потім використовуєтьсязамість усієї конструкції. Наприклад, бібліотечна процедура sqrt очікує аргументу типу double і видасть безглузду відповідь, якщо до неї по недбалості звернуться з чимось іншим. Таким чином, якщо n - ціле, то вираз:
sqrt ((double) n)
до передачі аргументу функції sqrt перетворює n типу double .
Вправа 3-2. Складіть програму для функції htoi(s) , яка перетворює рядок шістнадцяткових цифр на еквівалентне їй ціле значення. При цьому допустимими цифрами є цифри від 1 до 9 та літери від а до f .