Signed, unsigned, long, short 1 страница
Модифікатори signed, unsigned, long і short можна застосовувати до цілочисельних базових типів. Окрім того, модифікатори signed і unsigned можна використовувати з типом char, а модифікатор long – з типом double. Всі допустимі комбінації базових типів і модифікаторів для 16- і 32-розрядних середовищ наведено в табл. 3.2 і 3.3. У цих таблицях також вказано типи і розміри значень в бітах і діапазони представлення для кожного типу. Безумовно, реальні діапазони, що підтримуються Вашим компілятором, необхідно уточнити у відповідній документації.
Вивчаючи ці таблиці, звернемо Вашу увагу на кількість бітів, що виділяються для зберігання коротких, довгих і звичайних цілочисельних значень. Зауважте: у більшості 16-розрядних середовищ розмір (у бітах) звичайного цілочисельного значення збігається з розміром короткого цілого. Також зверніть увагу на те, що в більшості 32-розрядних середовищ розмір (у бітах) звичайного цілочисельного значення збігається з розміром довгого цілого. "Собака заритий" в С++-визначенні базових типів. Згідно з стандартом мови програмування C++, розмір довгого цілого повинен бути не меншим за розмір звичайного цілочисельного значення, а розмір звичайного цілочисельного значення повинен бути не меншим від розміру короткого цілого. Розмір звичайного цілочисельного значення повинен залежати від середовища виконання. Це означає, що в 16-розрядних середовищах для зберігання значень типу int використовується 16 біт, а в 32-розрядних – 32. При цьому найменший допустимий розмір для цілочисельних значень в будь-якому середовищі повинен становити 16 біт. Оскільки стандарт мови програмування C++ визначає тільки відносні вимоги до розміру цілочисельних типів, то немає гарантії, що один тип буде більший (за кількістю бітів), ніж інший. Проте розміри, вказані в обох таблицях, справедливі для багатьох компіляторів.
Табл. 3.2. Всі допустимі комбінації базових типів і модифікаторів для 16-розрядного середовища
Тип | Розмір в бітах | Діапазон |
char | -128 ¸ +127 | |
unsigned char | 0 ¸ +255 | |
signed char | -128 ¸ +127 | |
int | -32 768 ¸ +32 767 | |
unsigned int | 0 ¸ +65 535 | |
signed int | Аналогічний типу int | |
short int | Аналогічний типу int | |
unsigned short int | Аналогічний типу unsigned int | |
signed short int | Аналогічний типу short int | |
long int | -2 147 483 648 ¸ +2 147 483 647 | |
signed long int | Аналогічний типу long int | |
unsigned long int | 0 ¸ +4 294 967 295 | |
float | 3,4E-38 ¸ 3,4E+38 | |
double | 1,7E-308 ¸ 1,7E+308 | |
long double | 3,4E-4932 ¸ 1,1E+4932 |
Незважаючи на дозвіл мови С++, використання модифікатора signedдля цілочисельних типів є надлишковим, оскільки оголошення за замовчуванням передбачає значення зі знаком. Строго кажучи, тільки конкретна реалізація визначає, яким буде char-оголошення: зі знаком чи без нього. Але для більшості компіляторів під оголошенням типу char розуміють значення зі знаком. Таким чином, у таких середовищах використання модифікатора signed або char-оголошення також є надлишковим. У цьому навчальному посібнику вважається, що char-значення мають знак.
Табл. 3.3. Всі допустимі комбінації базових типів і модифікаторів для 32-розрядного середовища
Тип | Розмір в бітах | Діапазон |
char | -128 ¸ +127 | |
unsigned char | 0 ¸ +255 | |
signed char | -128 ¸ +127 | |
int | -2 147 483 648 ¸ +2 147 483 647 | |
unsigned int | 0 ¸ +4 294 967 295 | |
signed int | Аналогічний типу int | |
short int | -32 768 ¸ +32 767 | |
unsigned short int | 0 ¸ +65 535 | |
signed short int | -32 768 ¸ +32 767 | |
long int | -2 147 483 648 ¸ +2 147 483 647 | |
signed long int | Аналогічний типу signed int | |
unsigned long int | Аналогічний типу unsigned int | |
float | 3,4Е-38 ¸ 3,4Е+38 | |
double | 1,7Е-308 ¸ 1,7Е+308 | |
long double | 3,4Е-4932 ¸ 1,1Е+4932 |
Відмінність між цілочисельними значеннями із знаком і без нього полягає в інтерпретації старшого розряду. Якщо задано цілочисельне значення із знаком, С++-компілятор згенерує код з урахуванням того, що старший розряд значення використовується як прапорець знаку. Якщо прапорець знаку дорівнює 0, то число вважається позитивним, а якщо він дорівнює 1 – негативним. Негативні числа майже завжди представляються в додатковому коді. Для отримання додаткового коду програми всі розряди числа беруться в зворотному коді, а потім отриманий результат збільшується на одиницю.
Цілочисельні значення із знаком ("+" чи "-") використовуються в багатьох алгоритмах, але максимальне число, яке можна представити із знаком, становить тільки половину від максимального числа, яке можна представити без знаку. Розглянемо, наприклад, максимально можливе 16-розрядне ціле число (32 767):
01111111 11111111
Якби старший розряд цього значення із знаком дорівнював 1, то воно б інтерпретувалося як -1 (у додатковому коді). Але, якщо оголосити його як unsigned int-значення, то після встановлення його старшого розряду в +1 ми отримали б число 65 535.
Щоб зрозуміти відмінність в С++-інтерпретації цілочисельних значень із знаком і без нього, виконаємо таку коротку програму.
Код програми 3.3. Демонстрація відмінності між signed- і unsigned-значеннями цілочисельного типу
#include <iostream>// Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
int main()
{
short int izm; // Коротке int-значення із|із| знаком
short unsigned int jzm; // Коротке int-значення без знаку
jzm = 60000;
izm = jzm;
cout << izm << " " << jzm;
getch(); return 0;
}
У процесі виконання програма виведе два числа:
-5536 60000
Йдеться про те, що бітова комбінація, яка представляє число 60000 як коротке цілочисельне значення без знаку, інтерпретується як коротке int-значення із знаком як число -5536.
У мові програмування C++ передбачено скорочений спосіб оголошення unsigned-, short- і long-значень цілочисельного типу. Це означає, що під час оголошення int-значень достатньо використовувати слова unsigned, short і long, не вказуючи тип int, тобто тип int мається на увазі. Наприклад, такі дві настанови оголошують цілочисельні змінні без знаку:
unsigned x; unsigned inty|в,біля|;
Змінні типу char можна використовувати не тільки для зберігання ASCII-символів, але і для зберігання числових значень. Змінні типу char можуть містити "невеликі" цілі числа в діапазоні -128 ¸ +127 і тому їх можна використовувати замість int-змінних, якщо Вас влаштовує такий діапазон представлення чисел. Наприклад, у наведеному нижче коді програми char-змінну використовують для керування циклом, який виводить на екран алфавіт англійської мови.
Код програми 3.4. Демонстрація можливості виведення алфавіту в зворотному порядку
#include <iostream>// Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
int main()
{
char letter;
for(letter = 'z'; letter >= 'A'; letter--) cout << letter;
getch(); return 0;
}
Якщо цикл for у наведеному прикладі здається дещо дивним, то врахуйте, що символ 'А' представляється в комп'ютері як число, а значення від 'z ' до 'А' є послідовними і розташовані в спадному порядку.
3.3. Поняття про літерали
Літерали (ще називаються константами) – це фіксовані значення, які не можуть бути змінені програмою.
Вище було використано літерали в усіх попередніх прикладах програм. А зараз настав час вивчити їх детальніше.
Константи можуть мати будь-який базовий тип даних. Спосіб представлення кожної константи залежить від її типу. Символьні константи поміщають в одинарні лапки. Наприклад 'а' і '%' є символьними літералами. Якщо необхідно присвоїти символ змінної типу char, використовується настанова, подібна до такої:
ch = 'z';
Щоб використовувати двобайтовий символьний літерал (тобто константу типу wchar_t), необхідно, щоби потрібному символу передувала буква L. Наприклад, так:
wchar_t wс;
wc = L'A';
У цьому записі змінній wс присвоюється двобайтова символьна константа, еквівалентна букві А.
Цілочисельні константи задають як числа без дробової частини. Наприклад, 10 і -100 – цілочисельні літерали. Дійсні літерали повинні містити десяткову крапку, за якою знаходиться дробова частина числа, наприклад 11.123. Для дійсних констант можна також використовувати експоненціальне представлення чисел.
Існує два основні дійсні типи: float і double. Окрім того, існує декілька модифікацій базових типів, які утворюються за допомогою модифікаторів типів. Цікаво, а як же компілятор визначає тип літерала? Наприклад, число 123.23 має тип float або double? Відповідь на це запитання складається з двох частин. По-перше, С++-компілятор автоматично робить певні припущення щодо літералів. По-друге, при бажанні програміст може безпосередньо вказати тип літерала.
За замовчуванням компілятор пов'язує цілочисельний літерал з сумісним і одночасно найменшим за займаною пам'яттю тип даних, починаючи з типу int. Отже, для 16-розрядних середовищ число 10 буде пов'язано з типом int, а 103 000 – з типом long int.
Єдиним винятком з правила "найменшого типу" є дійсні (з плинною крапкою) константи, яким за замовчуванням присвоюється тип double. У багатьох випадках такі стандарти роботи компілятора цілком прийнятні. Проте у програміста є можливість точно визначити потрібний тип.
Щоб задати точний тип числової константи, використовується відповідний суфікс. Для дійсних типів діють такі суфікси: якщо дійсне число завершити буквою F, воно оброблятиметься з використанням типу float, а якщо буквою L – типу long double. Для цілочисельних типів суфікс U означає використання модифікатора типу unsigned, а суфікс L – long. Для задавання модифікатора unsigned long треба вказати обидва суфікси U і L. Нижче наведено деякі приклади використання модифікаторів.
Табл. 3.4. Приклади використання модифікаторів для цілих і дійсних типів
Тип даних | Приклади|зразки| констант |
int | 1,123, 21000, -234 |
long int | 35000L, -34L |
unsigned int | 10000U, 987U, 40000U |
unsigned long | 12323UL, 900000UL |
float | 123.23F, 4.34e-3F |
double | 23.23, 123123.33, –0.9876324 |
long double | 1001.2L |
3.3.1. Шістнадцяткові та вісімкові літерали
Іноді зручно замість десяткової системи числення використовувати вісімкову або шістнадцяткову. У вісімковій системі основою слугує число 8, а для відображення всіх чисел використовуються цифри від 0 до 7. У вісімковій системі число 10 має те саме значення, що число 8 в десятковій. Система числення з основою 16 називається шістнадцятковою і використовує цифри від 0 до 9 плюс букви від А до F, що означають шістнадцяткові "цифри" 10, 11, 12, 13, 14 і 15. Наприклад, шістнадцяткове число 10 дорівнює числу 16 в десятковій системі. Оскільки ці дві системи числення (шістнадцяткова і вісімкова) використовуються у програмах достатньо часто, то у мові програмування C++ дозволено при бажанні задавати цілочисельні літерали не в десятковій, а в шістнадцяткової або вісімковій системі. Шістнадцятковий літерал повинен починатися з префікса 0х (нуль і буква х) або 0Х, а вісімковий – з нуля. Наведемо два приклади:
int hex = 0xFF; // 255 в десятковій системі
int oct = 011; // 9 в десятковій системі
3.3.2. Рядкові літерали
Мова програмування C++ підтримує ще один вбудований тип літерала, що називається рядковим. Рядок – це набір символів, поміщених у подвійні лапки, наприклад "це тест". Ви вже бачили приклади рядків у деяких cout-настановах, за допомогою яких виводився текст на екран. При цьому звернемо Вашу увагу ось на що. Хоча мова програмування C++ дає змогу визначати рядкові літерали, вона не має вбудованого рядкового типу даних. Рядки у мові програмування C++, як буде показано далі у цьому навчальному посібнику, підтримуються у вигляді символьних масивів[13].
Обережно! Не варто плутати рядки з символами. Символьний літерал поміщається в одинарні лапки, наприклад 'а'. Проте "а" – це вже рядок, що містить тільки одну букву.
3.3.3. Символьні керівні послідовності
З виведенням більшості друкованих символів чудово справляються символьні константи, поміщені в одинарні лапки, але є такими "примірники" (наприклад, символ повернення каретки), які неможливо ввести в початковий текст програми з клавіатури. Деякі символи (наприклад, одинарні та подвійні лапки) у мові програмування C++ мають спеціальне призначення, тому іноді їх не можна ввести безпосередньо. З цієї причини у мові програмування C++ дозволено використовувати ряд спеціальних символьних послідовностей (що містять символ "зворотна коса риска"), які також називаються символьними керівними послідовностями. Їх перелік наведено в табл. 3.5.
Використання керівних послідовностей продемонструємо на прикладі наведеної нижче програми. Під час її виконання буде виконано повідомлення про перехід на новий рядок, виведено символ зворотної косої риски і виконано повідомлення про повернення на одну позицію.
Код програми 3.5. Демонстрація механізму використання символьних керівних послідовностей
#include <iostream>// Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
int main()
{
cout << "\n\\b";
getch(); return 0;
}
Табл. 3.5. Символьні керівні послідовності
Код | Значення |
\b | Повернення на одну позицію |
\f | Подача сторінки (для переходу до початку наступної|такої| сторінки) |
\n | Новий рядок |
\r | Повернення каретки |
\t | Горизонтальна табуляція |
\" | Подвійна лапка |
\' | Одинарна лапка (апостроф) |
\\ | Зворотна коса межа |
\v | Вертикальна табуляція |
\а | Звуковий сигнал (дзвінок) |
\? | Знак, запитання |
\N | Вісімкова константа (де n –це сама вісімкова| константа) |
\xN | Шістнадцяткова константа (де N – це сама шістнадцяткова| константа) |
3.4. Ініціалізація змінних
Під час оголошення змінної можна присвоїти певне значення, тобто ініціалізувати її, записавши після її імені знак рівності та початкове значення. Загальний формат ініціалізації має такий вигляд:
тип ім'я_змінної = значення;
Ось декілька прикладів.
char ch = 'а'|;
int first = 0;
float balance = 123.23F;
Незважаючи на те, що змінні часто ініціалізувалися константами, мова програмування C++ дає змогу ініціалізувати змінні динамічно, тобто за допомогою будь-якого виразу, дійсного на момент ініціалізації. Як буде показано далі, ініціалізація має важливе значення під час роботи з об'єктами.
Глобальні змінні ініціалізувалися тільки на початку програми. Локальні змінні ініціалізувалися під час кожного входження у функцію, у якій вони оголошені. Всі глобальні змінні ініціалізувалися нульовими значеннями, якщо не вказані ніякі інші ініціалізації. Неініціалізовані локальні змінні матимуть невідомі значення до першої настанови присвоєння, у якій вони використовуються.
Розглянемо простий приклад ініціалізації змінних. У наведеному нижче коді програми використано функцію total(), яка призначена для обчислення суми всіх послідовних чисел, починаючи з одиниці і закінчуючи числом, переданим їй як аргумент. Наприклад, сума ряду чисел, обмеженого числом 3, дорівнює 1 + 2 + 3 = 6. В процесі обчислення підсумкової суми функція total() відображає проміжні результати. Звернемо Вашу увагу на використання змінної sum у функції total().
Код програми 3.6. Демонстрація|зразок| застосування можливості ініціалізації змінних
#include <iostream>// Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
void total(int x);
int main()
{
cout << "Обчислення суми чисел від 1 до 5.\n";
total(5);
cout << " Обчислення суми чисел від 1 до 6.\n";
total(6);
getch(); return 0;
}
void total(int x)
{
int sum = 0; // Ініціалізація змінної sum.
int i, pm;
for(i=1; i<=x; i++) {
sum = sum + i;
for(pm=0; pm<10; pm++) cout << '.';
cout << "Проміжна сумa = " << sum << "\n";
}
}
Результати виконання цієї програми є такими:
Обчислення суми чисел від 1 до 5.
..........Проміжна сума = 1
..........Проміжна сума = 3
..........Проміжна сума = 6
..........Проміжна сума = 10
..........Проміжна сума = 15
Обчислення суми чисел від 1 до 6.
..........Проміжна сума = 1
..........Проміжна сума = 3
..........Проміжна сума = 6
..........Проміжна сума = 10
..........Проміжна сума = 15
..........Проміжна сума = 21
Як видно з результатів розрахунку, під час кожного виклику функції total() змінна sum ініціалізується нулем.
3.5. Оператори С++-програми
3.5.1. Поняття про вбудовані оператори
У мові програмування C++ визначено широкий набір вбудованих операторів, які дають в руки програмісту потужні важелі керування під час створення і обчислення різноманітних виразів. Оператор (operator) – це символ, який вказує компіляторові на виконання конкретних математичних дій або логічних маніпуляцій. У мові програмування C++ є чотири загальні класи операторів: арифметичні, порозрядні, логічні та оператори відношення. Окрім них визначено інші оператори спеціального призначення. У цьому розділі розглядаються арифметичні, логічні оператори та оператори відношення.
3.5.2. Арифметичні оператори
У табл. 3.6 перераховано арифметичні оператори, дозволені для застосування у мові програмування C++. Дія операторів +; * і / збігається з дією аналогічних операторів у будь-якій іншій мові програмування (та і в алгебрі, якщо вже на те пішло). Їх можна застосовувати до даних будь-якого вбудованого числового типу. Після застосування оператора ділення (/) до цілого числа залишок буде відкинутий. Наприклад, результат цілочисельного ділення 10/3 буде дорівнювати 3.
Табл. 3.6. Арифметичні оператори
Оператор | Дія |
+ | Додавання |
- | Віднімання, а також унарний мінус |
* | Множення |
/ | Ділення|поділка,розподіл,поділ| |
% | Ділення|поділка,розподіл,поділ| за модулем |
-- | Декремент |
++ | Інкремент |
Залишок від ділення можна отримати за допомогою оператора ділення за модулем (%). Цей оператор працює практично так само, як у інших мовах програмування: повертає залишок від ділення без остачі. Наприклад, 10 % 3 дорівнює 1. Це означає, що у мові програмування C++ оператора "%" не можна застосовувати до типів з плинною крапкою (float або double). Ділення за модулем застосовано тільки до цілочисельних типів. Використання цього оператора продемонстровано у наведеному нижче коді програми.
Код програми 3.7. Демонстрація механізму використання оператора "ділення за модулем"
#include <iostream>// Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
int main()
{
int x, y;
x = 10;
y = 3;
cout << x/y; // Буде відображене число 3.
cout << "\n";
cout << x%y; /* Буде відображене число 1, тобто залишок
від ділення без остачі. */
сout << "\n";
х = 1;
y = 2;
cout << х/у << " " << х%y; // Будуть виведені числа 0 і 1.
getch(); return 0;
}
У останньому рядку результатів виконання цієї програми дійсно будуть виведені числа 0 і 1, оскільки при цілочисельному діленні 1/2 отримаємо 0 із залишком 1, тобто вираз 1%2 дає значення 1.
Унарний мінус, по суті, є множенням значення свого єдиного операнда на -1. Іншими словами, будь-яке числове значення, якому передує знак "-", змінює свій знак на протилежний.
3.5.3. Оператори інкремента і декремента
У мові програмування C++ є два оператори, яких немає в деяких інших мовах програмування. Це оператори інкремента (++) і декремента (--). Вони згадувалися в розд. 24.2, коли йшлося про настанову організації циклу for. Оператор інкремента виконує додавання до операнда число 1, а оператор декремента віднімає 1 від свого операнда. Це означає, що настанова
х = х + 1;
аналогічна такій настанові:
++x;
А настанова
x = x – 1;
аналогічна такій настанові:
--х;
Оператори інкремента і декремента можуть знаходитися як перед своїм операндом (префіксна форма), так і після нього (постфіксна форма). Наприклад, настанову
x = x + 1;
можна переписати у вигляді префіксної форми
++х; // Префіксна форма оператора інкремента.
або у вигляді постфіксної форми:
x++; // Постфіксна форма оператора інкремента.
У попередньому прикладі не мало значення, у якій формі було застосовано оператор інкремента: префіксній або постфіксній. Але, якщо оператор інкремента або декремента використовується як частина більшого виразу, то форма його застосування дуже важлива. Якщо такий оператор застосовується в префіксній формі, то мова програмування C++ спочатку виконає цю операцію, щоб операнд набув нового значення, яке потім буде використано іншою частиною виразу. Якщо ж оператор застосовується в постфіксній формі, то С# використовує у виразі його старе значення, а потім виконає операцію, в результаті якої операнд знайде нове значення. Для розуміння сказаного розглянемо такий фрагмент коду програми:
x = 10;
y = ++х;
У цьому випадку змінна y буде дорівнювати 11. Але, якщо у цьому коді префіксну форму запису замінити постфіксною, змінна y буде дорівнювати 10:
х = 10;
y = x++;
У обох випадках змінна х набуде значення 11. Різниця полягає тільки у тому, в який момент вона дорівнюватиме 11 (до або після присвоєння її значення змінній y). Для програміста надзвичайно важливо мати можливість керувати тривалістю виконання операції інкремента або декремента.
Більшість С++-компіляторів для операцій інкремента і декремента створюють ефективніший код порівняно з кодом, що генерується під час використання звичайного оператора додавання і віднімання одиниці. Тому професіонали вважають за краще використовувати (де це можливо) оператори інкремента і декремента.
Табл. 3.7. Порядок виконання дій арифметичними операторами
Пріоритет | Оператори |
Найвищий | ++ -- |
- (унарний мінус) | |
* / % | |
Нижчий | + – |
Оператори одного ієрархічного рівня обчислюються компілятором зліва направо. Безумовно, для зміни порядку обчислень можна використовувати круглі дужки, які обробляються у мові програмування C++ так само, як практично в усіх інших мовах програмування. Операції або набір операцій, поміщених у круглі дужки, набувають вищий пріоритет порівняно з іншими операціями виразу. Унарні оператори навпаки – виконуються справа наліво.
3.5.4. Історія походження імені мови програмування C++
Тепер, коли нам стало зрозумілим значення оператора інкремента "++", можна зробити припущення щодо походження імені мови програмування C++. Як було зазначено в попередніх розділах, мова програмування C++ побудована на фундаменті мови С, значно удосконаленої, переважно з метою підтримки об'єктно-орієнтованого програмування. Таким чином, мова програмування C++ є інкрементним удосконаленням мови С, а результат додавання символів "++" (оператора інкремента) до імені С виявився цілком відповідним іменем для нової мови.
Б'єрн Страуструп спочатку назвав свою мову "С з класами" (C with Classes), але, за пропозицією Ріка Маскітті (Rick Mascitti), він пізніше змінив цю назву на мову програмування C++. І хоча успіх нової мови ще тільки передбачався, ухвалення нової лаконічної назви (мови програмування C++) сприяло успіху проекту.
3.5.5. Оператори відношення та логічні оператори
Оператори відношення та логічні (булеві) оператори, які часто йдуть "рука в руку", використовуються для отримання результатів у вигляді значень ІСТИНА/ФАЛЬШ. Оператори відношення оцінюють за "двобальною системою" відношення між двома значеннями, а логічні визначають різні способи поєднання дійсних і помилкових значень. Оскільки оператори відношення генерують ІСТИНА/ФАЛЬШ – результати, то вони часто виконуються з логічними операторами. Тому вони і розглядаються у одному розділі.
Оператори відношення та логічні (булеві) оператори перераховано в табл. 3.8. Звернемо Вашу увагу на те, що у мові програмування C++ як оператор відношення "не дорівнює" використовує символ " !=", а для оператора "дорівнює" – подвійний символ рівності (==). Згідно з стандартом мови програмування C++, результат виконання операторів відношення і логічних операторів має тип bool, тобто у процесі виконання операцій відношення і логічних операцій виходять значення true або false. Під час використання старших компіляторів результати виконання цих операцій мали тип int (нуль або ненульове ціле, наприклад 1). Ця відмінність в інтерпретації значень має в основному теоретичну основу, оскільки мова програмування C++ автоматично перетворює значення true в 1, а значення false – в 0, і навпаки.