Занятие 2. Арифметические операции и работа со стеком #
Следующим шагом мы освоим простейшие операции с константами, арифметикой и манипуляциями над стеком.
Виртуальная исполняющая машина .Net Framework не имеет регистров, как большинство аппаратных процессоров и полностью основана на работе со стеком. Через стек передаются параметры для процедур и возвращаются результаты, в стеке производятся арифметические и логические операции и т.д.
Самый простой способ занести какое-либо значение в стек, это вызвать команду ldc, а точнее один из ее вариантов:
Команда | Тип операнда в CIL | Описание |
---|---|---|
ldc.i4 | int32 | Загрузка целых чисел (System.Int32) |
ldc.i8 | int64 | Загрузка целых чисел (System.Int64) |
ldc.r4 | float32 | Загрузка чисел с плавающей запятой (System.Single) |
ldc.r8 | float64 | Загрузка чисел с плавающей запятой (System.Double) |
Например так:
ldc.r4 123.456
В результате выполнения этой команды в стек будет загружена константа 123.456, причем представлена она будет в формате 32-разрябного числа с плавающей точкой.
Кроме того, можно заносить в стек указатели на строковые константы с помощью команды ldstr (с ней мы сталкивались ранее).
После того, как в стеке появились значения, над ними можно производить различные операции. Вот перечень основных арифметических операций доступных в CIL:
Операция | Описание |
---|---|
add | сложение |
sub | вычитание |
mul | умножение |
div | деление |
rem | остаток от деления |
and | побитовое И |
or | побитовое ИЛИ |
xor | побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ |
shl | сдвиг целых чисел влево |
shr | сдвиг целых чисел со знаком вправо |
shr.un | сдвиг целых чисел без знака вправо |
neg | изменение знака числа |
not | побитовое НЕ (для целых чисел) |
Ни одна из приведенных в таблице команд не требует операндов – они извлекаются из стека. При этом нужно учитывать порядок помещения операндов в стек: сначала первый (делимое, уменьшаемое, …), затем второй (делитель, вычитаемое, …).
В процессе работы со стеком нужно помнить несколько правил:
- каждая команда (или метод стандартной библиотеки) извлекает из стека нужное ей количество операндов (унарная – один, бинарная – два, …, отдельные команды вообще не требуют исходных данных) и возвращает результат в стек
- для операций add, div, mul, rem и sub оба операнда должны иметь одинаковый тип (допускаются как целые, так и вещественные). Если типы операндов не совпадают нужно использовать специальные операции преобразования
- количество слотов в стеке ограниченно и задается директивой .maxstack
- при выходе из процедуры стек должен быть пуст. Неиспользованные данные должны быть из него удалены.
В процессе работы могут быть полезны некоторые специальные команды манипулирования стеком:
Операция | Описание |
---|---|
dup | копирует (дублирует) содержимое вершины стека |
pop | удаляет элемент из вершины стека |
Чтобы опробовать эти возможности мы разработаем программу, которая будет вычислять расстояние от начала координат, до некоторой точки на плоскости (по известной формуле \(\sqrt{x^2+y^2}\) . Для простоты мы будем задавать значение координат этой точки в виде констант.
.assembly Step_2 {}
.method static void main()
{
.entrypoint
.maxstack 3
ldc.r8 3.0
dup
mul
ldc.r8 4.0
dup
mul
add
call float64 [mscorlib]System.Math::Sqrt(float64)
call void [mscorlib]System.Console::WriteLine(float64)
call string [mscorlib]System.Console::ReadLine()
pop
ret
}
К данному коду требуется ряд пояснений:
- все операции в данной программе выполняются над вещественными числами повышенной точности (формат double в С#, или float64 в CIL)
- возведение в квадрат заменяется последовательностью команд dup и mul
- для извлечения квадратного корня используется статический метод Sqrt из класса System.Math, который содержит большой набор различных математических функций
- чтобы увидеть результат работы нашей программы в консоли мы вызываем процедуру ReadLine из класса System.Console, которая блокирует дальнейшее выполнение до тех пор, пока пользователь не наберет строку с клавиатуры и не нажмет Enter. Но так как нам не нужна эта строка (точнее мы просто ждем, пока не будет нажата Enter) занесенную в стек строку нам следует удалить командой pop.
Задание #
- Проверьте работу приведенной программы.
- Модифицируйте программу так, чтобы она находила расстояние между произвольными точками \(\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}\)