КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Типи даних
Дані, з якими працює програма, зберігаються в оперативній пам'яті. Природно, що компілятору необхідно точно знати, скільки місця вони займають, як саме закодовані і які дії з ними можна виконувати. Все це задається при описі даних за допомогою типу. Тип даних однозначно визначає: § внутрішнє представлення даних, а, отже, і множину їх можливих значень; § допустимі дії над даними (операції і функції). Наприклад, цілі та дійсні числа, навіть якщо вони займають однаковий об'єм пам'яті, мають абсолютно різні діапазони можливих значень; цілі числа можна множити один на одного, а, наприклад, символи - не можна. Кожен вираз в програмі має певний тип. Величин, що не мають ніякого типу, не існує. Компілятор використовує інформацію про тип при перевірці допустимості описаних в програмі дій. Пам'ять, в якій зберігаються дані під час виконання програми, ділиться на дві області: стек (stack) і динамічна область, або хіп (heap). Динамічну область називають іноді “купою”. Стек використовуєтьсядля зберігання величин, пам'ять під яких виділяє компілятор, а в динамічній області пам'ять резервується і звільняється під час виконання програми за допомогою спеціальних команд. Основним містом для зберігання даних в С# є хіп.
2.2.1. Класифікація типів
Перш ніж перейти до вивчення конкретних типів мови С#, розглянемо їх класифікацію. Всі типи можна розділити на прості (не мають внутрішньої структури) і структуровані (складаються з елементів інших типів). Типи можна розділити на вбудовані (стандартні) і визначених програмістом (рис. 2.1). Для даних статичного типу пам'ять виділяється у момент оголошення, при цьому її необхідний об'єм відомий. Для даних динамічного типу розмір даних у момент оголошення може бути невідомий, і пам'ять під них виділяється за запитом в процесі виконання програми. Вбудовані типи не вимагають попереднього визначення. Для кожного типу існує ключове слово, яке використовується при описі змінних, констант і т.д. Якщо ж програміст визначає власний тип даних, він описує його характеристики і сам дає йому ім'я, яке потім застосовується точно так, як і імена стандартних типів. Опис власного типу даних повинен включати всю інформацію, необхідну для його використання, а саме внутрішнє уявлення і допустимі дії. Рис. 2.1. Різні класифікації типів даних С#
Вбудовані типи С# приведені в таблиці. 2.5. Вони однозначно відповідають стандартним класам бібліотеки.NET, визначеним в просторі імен System. Як видно з таблиці, існують декілька варіантів представлення цілих і дійсних величин. Програміст вибирає тип кожної величини, яка використовується в програмі, з урахуванням необхідного йому діапазону і точності представлення даних. Цілі типи, а також символьний, дійсний і фінансовий типи можна об'єднати під назвою арифметичних типів. Внутрішнє представлення величини цілого типу - ціле число в двійковому коді. У знакових типах старший біт числа інтерпретується як знаковий (0 додатне число, 1 – від’ємне). Від’ємні числа найчастіше представляються в так званому додатковому коді. Для перетворення числа в додатковий код всі розряди числа, за винятком знакового інвертуються, потім до числа додається одиниця, і знаковому біту теж привласнюється одиниця. Беззнакові типи дозволяють представляти тільки додатні числа, оскільки старший розряд розглядається як частина коду числа. Для дійсних і фінансового типів в таблиці приведені абсолютні величини мінімальних і максимальних значень. Дійсні типи, або типи даних, з плаваючою крапкою зберігаються в пам'яті комп'ютера інакше, ніж цілочисельні. Внутрішнє представлення дійсного числа складається з двох частин - мантиси і порядку, кожна частина має знак. Довжина мантиси визначає точність числа, а довжина порядку - його діапазон. У першому наближенні це можна уявити собі так: наприклад, для числа 0,381•104 зберігаються цифри мантиси 381 і порядок 4, для числа 560,3•102 - мантиса 5603 і порядок 5 (мантиса нормалізується), а число 0,012 представлено як 12 і -1. Звичайно, в даному прикладі не враховані система числення і інші особливості. Всі дійсні типи можуть бути представлені як додатні, так і від’ємні числа. Таблиця 2.5. Вбудовані типи С#
Найчастіше в програмах використовується тип double, оскільки його діапазон і точність задовольняють більшість потреб. Цей тип мають дійсні літерали і багато стандартних математичних функцій. При однаковій кількості байтів, що відводяться під величини типу float і int, діапазони їх допустимих значень сильно розрізняються із-за внутрішньої форми уявлення. Те ж саме відноситься до long і double. Тип decimal призначений для грошових обчислень, в яких критичні помилки округлення. Як видно з таблиці 2.5, тип float дозволяє зберігати одночасно всього 7 значущих десяткових цифр, тип double - 15-16. При обчисленнях помилки округлення накопичуються, і при певному поєднанні значень навіть може привести до результату, в якому не буде жодної вірної значущої цифри! Величини типу decimal дозволяють зберігати 28-29 десяткових розрядів. Тип decimal не відноситься до дійсних типів, у них різне внутрішнє уявлення. Величини грошового типу навіть не можна використовувати в одному виразі з дійсними без явного перетворення типу. Використання величин фінансового типу в одному виразі з цілими допускається. Будь-який вбудований тип С# відповідає стандартному класу бібліотеки.NET, визначеному в просторі імен System. Скрізь, де використовується ім'я вбудованого типу, його можна замінити ім'ям класу бібліотеки. Це означає, що у вбудованих типів даних С# є методи і поля. З їх допомогою можна, наприклад, набути мінімальних і максимальних значень для цілих, символьних, фінансових і дійсних чисел: § double.MaxValue (или System.Doublе.MaxValue) - максимальне число типу double; § uint.MinValue (или System.UInt32.MinValue)- мінімальне число типа uint.
2.2.2. Типи літералів
Літерали (константи) теж мають тип. Якщо значення цілого літерала знаходяться усередині діапазону допустимих значень типу іnt, літерал розглядається як int, інакше він відноситься до найменшого з типів uint, long або ulong, в діапазон значень якого він входить. Дійсні літерали за умовчанням відносяться до типу double. Наприклад, константа 10 відноситься до типу int (хоча для її зберігання достатньо одного байта), а константа 2147483648 буде визначена як uint. Для явного завдання типу літерала служить суфікс, наприклад, 1.1f, 1UL, 1000m (суфікси показані в таблиці. 2.3). Явне завдання застосовується в основному для зменшення кількості неявних перетворень типу, що виконуються компілятором. 2.2.3. Типи-значення і посилальні типи
Найчастіше типи С# розділяють за способом зберігання елементів на типи-значення і посилальні типи (рис. 2.2). Елементи типів-значень, або значущих типів (value types), є просто послідовністю бітів в пам'яті, необхідний об'єм якої виділяє компілятор. Іншими словами, величини значущих типів зберігають свої значення безпосередньо. Величина посилального типу зберігає не самі дані, а посилання на них (адреса, по якій розташовані дані). Самі дані зберігаються в хіпові. Не дивлячись на відмінності в способі зберігання, і типи-значення, і посилальні типи є нащадками загального базового класу object. Рисунок 2.3 ілюструє різницю між величинами значущого і посилального типів. Одні і ті ж дії над ними виконуються по-різному. Розглянемо як приклад перевірку на рівність. Величини значущого типу рівні, якщо рівні їх значення. Величини посилального типу рівні, якщо вони посилаються на одні і ті ж дані (на рисунку b і с рівні, але а не рівне b навіть при однакових значеннях). З цього виходить, що якщо змінити значення однієї величини посилального типу, це може відбитися на іншій.
Рис. 2.2. Класифікація типів даних C# за способом зберігання
Рис. 2.3. Зберігання в пам'яті величин значущого і посилального типів
Всі значущі типи є простими. По іншій класифікації структури і перелічення відносяться до структурованих типів, що визначаються програмістом. Деталізація типів даних, приведених на рис. 2.2 буде розглянута в подальших розділах. 2.2.4. Упаковка і розпаковування
Для того, щоб величини посилального і значущого типів могли використовуватися спільно, необхідно мати можливість перетворення з одного типу в інший. Мова С# забезпечує таку можливість. Перетворення з типу-значення в посилальний тип називається упаковкою (boxing), зворотне перетворення - розпаковуванням (unboxing). Якщо величина значущого типу використовується в тому місці, де потрібний посилальний тип, автоматично виконується створення проміжної величини посилального типу: створюється посилання, в хіпові виділяється відповідний об'єм пам'яті і туди копіюється значення величини, тобто значення ніби упаковується в об'єкт. При необхідності зворотного перетворення з величини посилального типу «знімається упаковка», і в подальших діях бере участь тільки її значення.
2.3. Рекомендації по програмуванню
Поняття, введені в цьому розділі, є базою для всього подальшого матеріалу. На перший погляд, вивчення видів лексем може здаватися зайвим (нехай їх розрізняє компілятор!), проте це абсолютно не так. При розробці програми необхідно розуміти, з яких елементів мови вона складається. Це допомагає і при пошуку помилок, і при зверненні до довідкової системи, і при вивченні нових версій мови. Більш того, вивчення будь-якої нової мови рекомендується починати саме з лексем, які в ній підтримуються. Поняття типу даних лежить в основі більшості мовних засобів. При вивченні будь-якого типу необхідно розглянути дві речі: його внутрішнє уявлення (а отже, множина можливих значень величин цього типу), а також що можна робити з цими величинами. Типи даних є найважливішими характеристиками мови. Вибір найбільш відповідного типу для представлення даних - одна з необхідних умов створення ефективних програм. Нові мови і засоби програмування з'являються безперервно, тому програміст вимушений вчитися все життя і повинен уміти: § грамотно поставити завдання; § вибрати відповідні мовні засоби; § вибрати найбільш відповідні для представлення даних структури; § розробити ефективний алгоритм; § написати і документувати надійну і таку, що легко модифікується програму; § забезпечити її вичерпне тестування.
РОЗДІЛ 3. ЗМІННІ, ІМЕНОВАНІ КОНСТАНТИ, ОПЕРАЦІЇ І ВИРАЗИ
3.1. Змінні і іменовані константи
Змінна - це іменована область пам'яті, призначена для зберігання даних певного типу. Під час виконання програми значення змінної можна змінювати. Всі змінні, що використовуються в програмі, мають бути описані явним чином. При описі змінною задаються ім'я і тип. Ім'я змінної служить для звернення до області пам'яті, в якій зберігається значення змінної. Ім'я повинне відповідати правилам іменування ідентифікаторів С#, відображати сенс величини, що зберігається, і бути легко розпізнаваним. Тип змінної вибирається, виходячи з діапазону і необхідної точності представлення даних. Наприклад, немає необхідності заводити змінну дійсного типу для зберігання величини, яка може набувати тільки цілих значень, - хоч би тому, що цілочисельні операції виконуються набагато швидше. При оголошенні можна привласнити змінній деяке початкове значення, тобто ініціалізувати її, наприклад:
int а, b =1; float x = 0.1, у = 0.1f;
Тут описані:
При ініціалізації можна використовувати не тільки константу, але і вираз - головне, щоб на момент опису він був обчислюваним, наприклад:
int b = 1, а = 100; int х = b * а + 25;
Ініціалізувати змінну прямо при оголошенні не обов'язково, але перед тим, як її використовувати в обчисленнях, це зробити все одно доведеться, інакше компілятор повідомить про помилку. Рекомендується ініціалізувати змінні при описі. Втім, іноді цю роботу робить за програміста компілятор, це залежить від місцезнаходження опису змінною. Як вже наголошувалося, програма на C# складається з класів, усередині яких описують методи і дані. Змінні, описані безпосередньо усередині класу, називаються полями класу. Їм автоматично привласнюється так зване “значення по умовчанню”- як правило, це 0 відповідного типу. Змінні, описані усередині методу класу, називаються локальними змінними. Їх ініціалізація покладається на програміста. У даному розділі розглядаються тільки локальні змінні простих вбудованих типів даних. Так звана зона дії змінної, тобто область програми, де можна використовувати змінну, починається в точці її опису і триває до кінця блоку, усередині якого вона описана. Блок - це код, взятий у фігурні дужки. Основне призначення блоку - угрупування операторів. В С# будь-яка змінна описана усередині якого-небудь блоку: класу, методу або блоку усередині методу. Ім'я змінної має бути унікальним в області її дії. Зона дії розповсюджується на вкладені в метод блоки, з цього виходить, що у вкладеному блоці не можна оголосити змінну з таким же ім'ям, як і в тому, що охоплює його, наприклад:
class X // початок опису класу X { int А; // поле А класу X int В; // поле В класу X
void Y() // метод Y класу X { int С; // локальна змінна С, зона дії - метод Y int А; // локальна змінна А (НЕ конфліктує з полем А) {// ============ вкладений блок 1 ============ int D; // локальна змінна D, зона дії - блок //int А; Неприпустимо! Помилка компіляції С = В; // привласнення змінній С поля В С = this.A; // привласнення змінній С поля А }// ============ кінець блоку 1 ===============
{ // ============ вкладений блок 2 ============ int D; // локальна змінна D, зона дії - блок } // ============ кінець блоку 2 ===============
} // кінець опису метода Y класу X
} // кінець опису класу X
У лістингу 3.1 приведений приклад програми, в якій описуються і виводяться на екран локальні змінні.
Лістинг 3.1. Опис змінних using System; namespace ConsoleApplicationl { class Classl { static void Main() { int i = 3; double у = 4.12; decimal d = 600m; string s = "Вася"; Console.Write("i = "); Console.WriteLine(i); Console.Write("y = "); Console.WriteLine(у); Console.Write("d = "); Console.WriteLine(d); Console.Write("s = "); Console.WriteLine(s); } } } У розглянутому прикладі використовується метод Write. Він робить те ж саме, що і Writeline, але не переводить рядок. Можна заборонити змінювати значення змінної, задавши при її описі ключове слово const, наприклад:
const int b = 1; const float x = 0.1, у = 0.1f; // const розповсюджується на обидві змінні
Такі величини називають константами. Вони застосовуються для того, щоб замість значень констант можна було використовувати в програмі їх імена. Це робить програму зрозумілішою і полегшує внесення до неї змін, оскільки змінити значення досить тільки в одному місці програми. Поліпшення читабельності відбувається тільки при осмисленому виборі імен констант. У добре написаній програмі взагалі не повинно зустрічатися інших чисел окрім 0 і 1, решта всіх чисел повинна задаватися іменованими константами з іменами, що відображають їх призначення. Іменовані константи повинні обов'язково ініціалізуватися при описі. При ініціалізації можна використовувати не тільки константу, але і вираз - головне, щоб він був обчислюваним на етапі компіляції, наприклад:
const int b = 1, а = 100; const int х = b * а + 25; 3.2. Операції і вирази
Вираз - це правило обчислення значення. У виразі беруть участь операнди, об'єднані знаками операцій. Операндами простого виразу можуть бути константи, змінні і виклики функцій. Наприклад, а + 2 - це вираз, в якому + є знаком операції, а і 2 -операнди. Пропуски усередині знаку операції, що складається з декількох символів, не допускаються. По кількості операндів операції, що беруть участь в одній операції, діляться на унарні, бінарні і тернарні. Операції С# показані в таблиці 3.1, в якій символ х показує розташування операнда і не є частиною знаку операції.
Таблиця 3.1.
Операції C#
Продовження таблиці 3.1
Операції у виразі виконуються в певному порядку відповідно до пріоритетів, як і в математиці. У таблиці 3.1 операції розташовані по спаданню пріоритетів, рівні пріоритети розділені в таблиці горизонтальними лініями. Результат обчислення виразу характеризується значенням і типом. Наприклад, нехай а і b - змінні цілого типу і описані так: int а = 2, b = 5; Тоді вираз а + b має значення 7 і тип int, а вираз а = b має значення, рівне поміщеному в змінну а, і тип, співпадаючий з типом цієї змінної. Якщо в одному виразі є сусідами декілька операцій однакового пріоритету, операції привласнення і умовна операція виконуються справа наліво, останні - зліва направо. Для зміни порядку виконання обчислення використовуються круглі дужки, рівень їх вкладеності практично обмежений. Наприклад, а + b + с означає (а + b) + с, а = b = с означає а = (b = с). Тобто спочатку обчислюється вираз b = с, а потім його результат стає правим операндом для операції привласнення змінної а. 3.2.1. Перетворення вбудованих арифметичних типів-значень Якщо операнди різного типу і/або операція для цього типу не визначена, перед обчисленнями автоматично виконується перетворення типу згідно правил, що забезпечують приведення коротших типів до довших для охорони значущості і точності. Автоматичне (неявне) перетворення можливе не завжди, а тільки якщо при цьому не може трапитися втрата значущості. Якщо неявного перетворення з одного типу в інший не існує, програміст може задати явне перетворення типу за допомогою операції (тип) х. Явне перетворення розглядається в цьому розділі трохи пізніше. Арифметичні операції не визначені для коротших, ніж int, типів. Це означає, що якщо у виразі беруть участь тільки величини типів sbyte, byte, short і ushort, перед виконанням операції вони будуть перетворені в int. Таким чином, результат будь-якої арифметичної операції має тип не менше int. Правила неявного перетворення ілюструє рис. 3.1. Якщо один з операндів має тип, зображений на нижчому рівні, ніж інший, то він буде приводитися до типу іншого операнда за наявності шляху між ними. Якщо шляху немає, виникає помилка компіляції. Якщо шляхів декілька, вибирається найбільш короткий, такий, що не містить пунктирних ліній. Перетворення виконується не послідовно, а безпосередньо з початкового типу в результуючий.
Рис. 3.1. Неявні арифметичні перетворення типів
Перетворення коротших, ніж int, типів виконується при привласненні. Зверніть увагу на те, що неявного перетворення з float і double в decimal не існує.
Дата добавления: 2014-12-27; Просмотров: 3969; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |