Псевдоніми (програмування)
У програмуванні, псевдоніми представляють собою ситуацію (aliasing), при якій посилання на місце розташування даних в пам'яті, може здійснюватись за допомогою декількох символічних імен в програмі. Таким чином, модифікація даних за допомогою одного імені, призводить до модифікації значення, яке асоціюється з іншими іменами-аліасами, що може бути не очікуваним для програміста. В результаті, наявність псевдонімів призводить до ситуацій, які важко зрозуміти, аналізувати і оптимізувати. Аналізатори псевдонімів, мають на меті забезпечити аналіз з видачею корисної інформації для відстеження псевдонімів у програмі.
Приклади
Перевірка виходу за межі масиву
Наприклад, мова програмування C не виконує перевірки виходу за межі масиву. Скориставшись особливостями реалізації мови програмування за допомогою компілятора і його особливостями утворення асемблерного коду на даній архітектурі, можна досягти не явного ефекту псевдонімів.
Якщо масив був створений у стековій пам'яті, разом із змінною, яка розташовується у пам'яті одразу після масиву, можна змінюючи масив, вийти за його межі і безпосередньо змінити значення цієї змінної, при доступі ніби до елементу масиву. Наприклад, якщо в нас є масив arr з двома елементами типу int, далі за ним оголошена інша змінна (назвімо її i), тоді arr[2] (тобто третій елемент масиву), буде вказувати на i, тобто стати його псевдонімом, якщо вони знаходяться один за одним у пам'яті.
#include <stdio.h>
int main()
{
int arr[2] = { 1, 2 };
int i=10;
/* arr[2] -- псевдонім для i */
arr[2] = 20;
printf("element 0: %d \t", arr[0]); // виводить на екран 1
printf("element 1: %d \t", arr[1]); // виводить на екран 2
printf("element 2: %d \t", arr[2]); // виводить на екран 20
printf("i: %d \t\t", i); // також виведе на екран 20, а не 10, через псевдування
/* Розмір масиву досі дорівноє 2. */
printf("arr size: %d \n", (sizeof(arr) / sizeof(int)));
}
Таке можливо в деяких реалізаціях мови C, оскільки масиви займають послідовний блок пам'яті, а елементи масиву зазвичай знаходяться за допомогою зсуву адреси на фіксовану кількість байт відповідно до порядкового номера елементу, від початку масиву. Оскільки C не перевіряє межі масиву, можлива індексація і адресація за межі масиву. Відмітимо, що згадана поведінка наявності псевдонімів, є невизначеною. Деякі компілятори можуть залишити простір між масивом і змінними в стеку, наприклад, для того щоб вирівняти адреси змінних в комірках пам'яті, так щоб вони були кратні word. Стандарт не визначає як повинні розміщатися дані в пам'яті.
Псевдоніми — вказівники
Інша варіація псевдонімів може трапитись в будь-якій мові програмування, в якій можливо зіслатися на адресу у пам'яті за допомогою більше ніж одного імені (наприклад, за допомогою вказівників).
Використання
Контрольоване використання псевдонімів може бути іноді бажаним. Це звична практика у мові програмування Fortran. Мова програмування Perl визначає, в деяких своїх конструкціях, використання псевдонімів, наприклад в циклах foreach. Це дозволяє модифікувати конкретні структури даних пишучи більш оптимальний код. Наприклад:
my @array = (1, 2, 3);
foreach my $element (@array) {
# Increment $element, thus automatically
# modifying @array, since $element is ''aliased''
# to each of @array's elements in turn.
$element++;
}
print "@array \n";
Виведе на екран результат: «2 3 4». Якщо програміст хоче уникнути утворення псевдонімів, він може створити копію вмісту змінної і змінити значення для копії.
Конфлікти при оптимізації
Оптимізаторам доводиться робити спрощення коду, відповідно змінних, які представлені вказівниками. Наприклад, знаючи точне значення змінної (наприклад, що x = 5), зазвичай дозволяє оптимізувати її підставивши замість неї константу. Але компілятор не може використовувати подібну інформацію після того, як відбулося присвоєння значення іншій змінній (наприклад, *y = 10), оскільки ймовірно, що *y є аліасом x. Це може трапитись, якщо змінній присвоїли адресу змінної x таким чином: y = &x. В результаті присвоєння значення для *y, значення x також зміниться, отже вважати дійсною інформацію, що x = 5 для виразів, що знаходяться після *y = 10 буде потенційно не вірним. Але, якщо ми маємо інформацію щодо вказівників, при оптимізації можна перевірити чи дійсно x і *y є псевдонімами. У разі якщо ні, x можна замінювати константами і далі по коду, без помилок.
Інша проблема оптимізації при наявності псевдонімів це зміна порядку коду. Якщо компілятор вирішує, що x не є псевдонімом *y, тоді код, який вносить зміни або використовує значення змінної x, можна перемістити перед операцією *y = 10, якщо це необхідно для поліпшення планування, або зробити можливими додаткові цикли оптимізації.
Для того щоб зробити таку оптимізацію більш передбаченою, починаючи зі стандарту мови C (версія C99) визначає деякі додаткові правила і обмеження щодо вказівників. Це правило називається правило строгих псевдонімів (strict aliasing rule), який іноді дозволяє добитися вражаючого збільшення ефективності програми, але при порушенні цього правила може призвести до поламки працездатного коду. При компіляції програми за допомогою компілятору gcc, можна задати додаткову опцію -fno-strict-aliasing, яка виключає небажану оптимізацію, яка може призвести до непередбачуваної поведінки.
Посилання
- Understanding Strict Aliasing — Mike Acton
- Aliasing, pointer casts and gcc 3.3 — Informational article on NetBSD mailing list
- Type-based alias analysis in C++ — Informational article on type-based alias analysis in C++
- Understand C/C++ Strict Aliasing — Article on strict aliasing originally from the boost developer's wiki