Зауваження . Присвоєння x = x інколи вважають синонімом порожнього оператора . Чи завжди воно безпечне ? Для фундаментальних типів — так . Проте з оглядом на майбутнє зауважимо , що у разі програмованих типів обчислення лівобічного значення може призвести до руйнування правобічного ( див . підрозділ 4.6.3 ) . 2.3.3 . Перетворення типів Тепер звернімося до проблеми , якої за строгої типізації не мало б бути , а саме до перетворення типів . Воно виникає , зокрема , у мішаних арифметичних виразах , що водночас містять об’єкти різних числових типів . Навіть у мовах програмування зі скромним набором числових типів ( один цілий і один дійсний тип ) виникає проблема обчислення мішаних виразів вигляду k * x , де k — ціле число , а x — дійсне . Оскільки результат має бути дійсний , то доцільно виконати перетворення величини k до дійсного типу та застосувати операцію множення дійсних чисел . До того ж навряд чи можна очікувати , що в наборі команд комп’ютера є арифметичні операції для всіх можливих комбінацій арифметичних типів . Тому для виконання будь-якої операції над числовими даними різних типів доводиться здійснювати потрібні зведення типів . Так , щоб додати коротке ціле число до довгого , його перетворюють на довге . Щоб помножити ціле число на дійсне , його слід перетворити на дійсне . Як виконати додавання короткого та довгого дійсних чисел ? Спочатку необхідно зробити з короткого дійсного числа довге , потім визначити тип результату залежно від типу об’єкта , що його зберігатиме . Так у програмі виникають неявні перетворення типів ( implicit type conversion ) , про окремі з яких компілятор може попереджати , водночас залишаючи інші непоміченими . Строга типізація , узагалі кажучи , мала б заборонити використання мішаних виразів і , відповідно , неявних перетворень . Однак традиція перетворень так укоренилися , що типізації довелося поступитися . Із перетвореннями типів ми знову зустрінемося в об’єктному програмуванні . У мові C + + діє концепція розширення типу ( promotion ) , згідно з якою в разі змішування типів простіші перетворюються на складніші , які забезпечують ширший діапазон значень . У разі одночасного використання аргументів різних типів у виразах компілятор , де це потрібно , автоматично перетворює типи . Так , якщо арифметичні операції виконують над інтегральними типами ( наприклад , char і short ) , вони розширюються до цілого типу int ; коротші дійсні типи перетворюються на відповідні довші . Якщо йдеться про виконання присвоєння , то обчислений у правій частині результат перед зберіганням набуде типу лівобічного значення value . Окрім неявних можливі також явні перетворення типів ( type cast ) за допомогою кількох особливих операцій перетворення . Зокрема , мова C + + успадкувала від C дві « старомодні » операції : вирази int ( monday ) та ( int ) monday перетворюють константу monday типу week на значення цілого типу . Обернене перетворення — ( week ) i чи week ( i ) . Зауваження . Перетворення типів у програмах , узагалі кажучи , небажані — передусім тому , що вони не завжди коректні . Хотілося б принаймні мати можливість контролювати всі перетворення , які відбуваються в програмі . При цьому особливо небезпечними вважаються « старомодні » перетворення . Проблема в тому , що вони замасковані : немає іншого ефективного способу виявляти місця , де вони виконуються , окрім як перечитати всі тексти . Власні перетворення мова С + + виконує більш диференційовано за допомогою операцій const_cast , dynamic_cast , reinterpret_cast і static_cast , кожна з яких має спеціальне призначення . Останнє перетворення , наприклад static_cast ( monday ) , діє так , як аналогічне перетворення в мові C . Його статичність означає , що всі потрібні перевірки виконуються на етапі компіляції . Це найнадійніше з перетворень . Призначення інших перетворень розглянемо пізніше . Перевага явного позначення для операцій перетворення полягає в тому , що їх можна легко знайти в програмі , виконавши простий пошук за словом « _cast » . 2.4 . Указники Програмування роботи з пам’яттю — найважливіша і разом з тим найнебезпечніша з погляду надійності частина C / C + + - програм . Якщо до розглянутих раніше сталих і змінних можна застосувати термін пряме адресування , оскільки імена адресують значення безпосередньо ( direct addressing ) , і відповідальність за зв’язок змінної з її адресою несе компілятор , то указники ( pointers ) роблять це непрямо ( indirect addressing ) , а за коректність адреси відповідає сама програма . Якщо проаналізувати можливі джерела помилок при застосуванні прямої адресації порівняно з непрямою , то побачимо , що до помилок , викликаних неініціалізованими змінними чи указниками , будуть додані помилки обчислення адрес . Володіння технікою програмування указників — своєрідний « вищий пілотаж » у процедурному програмуванні . 2.4.1 . Указник як засіб непрямого адресування Щоб указник мав значення , попередньо потрібно надати йому це значення , зв’язавши указник з адресою певного місця пам’яті . Якщо воно невідоме на момент визначення указника , то слід ініціалізувати його нулем . На рис . 2.1 наведено приклад визначення й ініціалізації указника , значення якого — нульова адреса . Говорять , що такий указник на жодне місце в пам’яті не вказує , а тому його значення невизначене . Невизначений указник певною мірою безпечний , оскільки рівність нулеві можна перевірити . Він стає небезпечним ( призводить до аварійного завершення програми ) у разі спроби звернутися до значення , що міститься за нульовою адресою . Невизначений указник не слід плутати із « засміченим » ( теж у певному розумінні невизначеним або , точніше кажучи , визначеним некоректно ) . Якщо звичайну змінну було визначено без ініціалізації , наприклад так : double х ; чого за правилами гарного тону слід уникати , то для неї відведено місце , до якого ще не потрапило змістовне значення : відповідна область пам’яті просто містить « сміття » . Маємо коректне лівобічне та невизначене правобічне значення змінної . Так само незаданим або , інакше кажучи , « засміченим » буде значення неініціалізованого указника px . Однак у такому разі воно набагато небезпечніше , тому що може виявитись адресою області пам’яті , не призначеної чи навіть забороненої для використання за допомогою цього указника . Некоректним у такому разі стає лівобічне значення , а питання коректності правобічного за цієї умови вже навіть не виникає : наслідки можуть стати непередбачуваними ( рис . 2.2 ) . Указники , що ведуть « у нікуди » , називають завислими ( dangling pointer ) : double * px ; Зауваження . Розглядаючи техніку роботи з указниками , можна провести аналогію між структурами керування та структурами даних . Як уже було сказано під час вивчення парадигм , структуроване програмування повністю витіснило з практики вживання оператора переходу goto . Так само , як оператор переходу призводить до важко контрольованих передавань керування в програмі , використання указників може спричинити виникнення несистематизованих зв’язків між даними . Тому під час роботи з пам’яттю потрібно скрізь , де це можливо , надавати перевагу безпечнішим рішенням , наприклад відсилкам ( підрозділ 2.5 ) , особливо якщо мова йде про передавання параметрів . Проте це виходить за межі можливостей мови C . Тому , дотримуючись її правил , доводиться використовувати звичайні указники , особливо для обміну значеннями параметрів . При цьому варто дотримуватися дисципліни програмування , яка б виключала виникнення некоректних значень указників . Від багатьох помилок убезпечують найпростіші правила : указник потрібно ініціалізувати в момент створення ( невизначений указник ініціалізується нулем ) і знову робити невизначеним у разі видалення значення , на яке він указував . Далі ( у підрозділі 5.5 ) будуть запропоновані безпечніші рішення , що ґрунтуються на використанні вдосконалених безпечних інтелектуальних указників . Указники , подібно до будь-яких інших змінних , мають значення , але це лише адреси , що вказують на самі дані . У попередньому прикладі указник px містить « сміття » , а тому вказує невідомо на що . Важко уявити , до яких наслідків може призвести спроба записати дані за такою адресою . У кращому випадку це спричинить вихід за межі пам’яті , в гіршому — потрапляння до не призначеної для програми області й , можливо , ненавмисного псування життєво важливих даних .