Псевдонимы (вычисления)
В вычислениях псевдонимы можно получить через описывают ситуацию, в которой доступ к ячейке данных в памяти разные символические имена в программе. Таким образом, изменение данных через одно имя неявно изменяет значения, связанные со всеми псевдонимами, чего программист может не ожидать. В результате псевдонимы особенно затрудняют понимание, анализ и оптимизацию программ. Анализаторы псевдонимов предназначены для получения и вычисления полезной информации для понимания псевдонимов в программах.
Примеры
[ редактировать ]Переполнение буфера
[ редактировать ]Например, большинство реализаций языка программирования C не выполняют проверку границ массива . Затем можно использовать реализацию языка программирования компилятором и соглашения о языке ассемблера компьютерной архитектуры для достижения эффектов псевдонимов путем записи за пределы массива (тип переполнения буфера ). Это вызывает неопределенное поведение в соответствии со спецификацией языка C; однако многие реализации C будут демонстрировать описанные здесь эффекты наложения псевдонимов.
создается массив Если в стеке с переменной, расположенной в памяти непосредственно рядом с этим массивом , можно выполнить индексацию вне массива и напрямую изменить переменную, изменив соответствующий элемент массива. Например, если есть int
массив размером 2 (для этого примера назовем его arr
), рядом с другим int
переменная (назовите ее i
), arr[2]
(т. е. третий элемент) будет иметь псевдоним i
если они соседствуют в памяти.
# включаем <stdio.h> int main () { int arr [ 2 ] = { 1 , 2 }; интервал я = 10 ; /* Запись за конец обр. Неопределенное поведение в стандарте C, в некоторых реализациях будет записываться в i. */ обр [ 2 ] = 20 ; printf ( "элемент 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, из-за псевдонимов, но компилятор может сохранить в регистре и вывести 10 /* размер arr по-прежнему равен 2. */ printf ( "arr size: %lu \n " , ( long ) ( sizeof ( arr ) / sizeof ( int ) ))); }
Это возможно в некоторых реализациях C, поскольку массив представляет собой блок непрерывной памяти, а ссылки на элементы массива просто смещаются от адреса начала этого блока, умноженного на размер одного элемента. Поскольку в C нет проверки границ, возможна индексация и адресация за пределами массива. Обратите внимание, что вышеупомянутое поведение псевдонимов является неопределенным поведением . Некоторые реализации могут оставлять пространство между массивами и переменными в стеке, например, для выравнивания переменных по областям памяти, кратным собственному размеру слова архитектуры . Стандарт C обычно не определяет, как данные должны располагаться в памяти. (ИСО/МЭК 9899:1999, раздел 6.2.6.1).
Компилятор не будет ошибкой пропускать эффекты псевдонимов для обращений, выходящих за пределы массива.
Псевдонимы указателей
[ редактировать ]Другая разновидность псевдонимов может встречаться в любом языке, который может ссылаться на одно место в памяти более чем одним именем (например, с помощью указателей ). Это обычная проблема с функциями, которые принимают аргументы-указатели, и их устойчивость (или ее отсутствие) к псевдонимам должна быть тщательно документирована, особенно для функций, которые выполняют сложные манипуляции с переданными им областями памяти.
Указанный псевдоним
[ редактировать ]В некоторых случаях может быть желательным контролируемое поведение псевдонимов (то есть заданное поведение псевдонимов, в отличие от того, которое обеспечивается расположением памяти в C). Это обычная практика в Фортране . Язык Perl программирования в некоторых конструкциях определяет поведение псевдонимов, например в foreach
петли. Это позволяет напрямую изменять определенные структуры данных с меньшим количеством кода. Например,
мой @array = ( 1 , 2 , 3 ); foreach my $element ( @array ) { # Увеличиваем $element, тем самым автоматически # изменяя @array, поскольку $element ''привязывается'' # к каждому элементу @array по очереди. $элемент ++ ; } напечатайте "@array \n" ;
в результате выведет «2 3 4». Если кто-то хочет обойти эффекты псевдонимов, можно скопировать содержимое индексной переменной в другую и изменить копию.
Конфликты с оптимизацией
[ редактировать ]Оптимизаторам часто приходится делать консервативные предположения о переменных, когда возможно наложение псевдонимов. Например, зная значение переменной (например, x
равен 5) обычно допускает определенные оптимизации (например, постоянное распространение ). Однако компилятор не может использовать эту информацию после присвоения другой переменной (например, в C, *y = 10
) потому что это может быть так *y
это псевдоним x
. Это может произойти после такого задания, как y = &x
. В результате этого поручения *y
, значение x
также будет изменено, поэтому распространение информации, которая x
равно 5 к утверждениям, следующим *y = 10
было бы потенциально неправильно (если *y
действительно это псевдоним x
). Однако, если есть информация об указателях, процесс распространения констант может сделать запрос типа: can x
быть псевдонимом *y
? Тогда, если ответ отрицательный, x = 5
можно безопасно размножать.
Еще одна оптимизация, на которую влияет псевдонимы, — это изменение порядка кода. Если компилятор решит, что x
не имеет псевдонимов *y
, затем код, который использует или изменяет значение x
можно переместить перед заданием *y = 10
, если это улучшит планирование или позволит дополнительную оптимизацию цикла выполнить .
Чтобы обеспечить такую оптимизацию предсказуемым образом, стандарт ISO для языка программирования C (включая его новую редакцию C99 , см. раздел 6.5, параграф 7) определяет, что незаконно (за некоторыми исключениями) получать доступ к одной и той же ячейке памяти с помощью указателей разные типы. Поэтому компилятор может предположить, что такие указатели не являются псевдонимами. Это правило, известное как правило строгого псевдонимов , иногда позволяет добиться впечатляющего увеличения производительности. [1] но известно, что он нарушает некоторый в остальном действительный код. Некоторые программные проекты намеренно нарушают эту часть стандарта C99. Например, Python 2.x сделал это для реализации подсчета ссылок , [2] и потребовались изменения в базовых структурах объектов в Python 3, чтобы включить эту оптимизацию. Ядро Linux делает это, потому что строгое использование псевдонимов вызывает проблемы с оптимизацией встроенного кода. [3] В таких случаях при компиляции с помощью gcc опция -fno-strict-aliasing
вызывается для предотвращения нежелательных оптимизаций, которые могут привести к появлению неожиданного кода.
Аппаратное псевдонимирование
[ редактировать ]Термин «алиасинг» также используется для описания ситуации, когда из-за выбора конструкции оборудования или сбоя оборудования один или несколько доступных битов адреса не используются в процессе выбора памяти. [4] Это может быть конструктивным решением, если доступно больше адресных битов, чем необходимо для поддержки установленных устройств памяти. В случае сбоя один или несколько адресных битов могут быть закорочены вместе или принудительно заземлены ( логический 0) или напряжение питания (логическая 1).
- Пример
В этом примере предполагается, что память имеет 8 ячеек, требующих только 3 адресных строки (или бита , поскольку 2 3 = 8). Биты адреса (с именами от A2 до A0) декодируются для выбора уникальных ячеек памяти следующим образом, в стандартном режиме двоичного счетчика :
А2 | А1 | А0 | Местоположение памяти |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 2 |
0 | 1 | 1 | 3 |
1 | 0 | 0 | 4 |
1 | 0 | 1 | 5 |
1 | 1 | 0 | 6 |
1 | 1 | 1 | 7 |
В таблице выше каждая из 8 уникальных комбинаций битов адреса выбирает другую ячейку памяти. Однако, если один бит адреса (скажем, A2) будет закорочен на землю, таблица будет изменена следующим образом:
А2 | А1 | А0 | Местоположение памяти |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 2 |
0 | 1 | 1 | 3 |
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 2 |
0 | 1 | 1 | 3 |
В этом случае, когда A2 всегда равно нулю, первые четыре ячейки памяти дублируются и снова отображаются как вторые четыре. Ячейки памяти с 4 по 7 стали недоступны.
Если бы это изменение произошло с другим битом адреса, результаты декодирования были бы другими, но в целом эффект был бы тот же: потеря одного бита адреса сокращает доступное пространство памяти вдвое, что приводит к дублированию (алиасингу) бита адреса. оставшееся пространство.
См. также
[ редактировать ]- Сглаживание
- Псевдонимы использования слова применительно к обработке сигналов, включая компьютерную графику.
Ссылки
[ редактировать ]- ^ Майк Эктон (1 июня 2006 г.). «Понимание строгого псевдонимов» .
- ^ Нил Шеменауэр (17 июля 2003 г.). «Строгое псевдонимы ANSI и Python» .
- ^ Линус Торвальдс (26 февраля 2003 г.). «Re: Неверная компиляция без -fno-strict-aliasing» .
- ^ Майкл Барр (27 июля 2012 г.). «Программное тестирование памяти» .
Внешние ссылки
[ редактировать ]- Понимание строгого псевдонимов – статья Майка Эктона
- Псевдонимы, приведение указателей и gcc 3.3 – информационная статья в списке рассылки NetBSD
- Анализ псевдонимов на основе типов в C++ — Информационная статья об анализе псевдонимов на основе типов в C++.
- Понимание строгого псевдонимов C/C++ — статья о строгом псевдониме, взятая из вики разработчика boost.