Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

• Коды операций для управления программой

• Коды операций для оценки выражений

• Коды операций для осуществления доступа к значениям в памяти (через параметры, локальные переменный и т.п.)

Чтобы продемонстрировать некоторые возможности реализации членов средствами CIL, в табл. 15.5 предлагаются описания некоторых из наиболее часто используемых кодов операций, непосредственно связанных с логикой реализации членов. Кроме того, коды операций в данной таблице сгруппированы по функциональности.

Таблица 15.5. Коды операций CIL, связанные с реализацией членов

Коды операций Описание
add, sub, mul, div, rem Позволяют выполнять сложение, вычитание, умножение и деление для пар значений (rem возвращает остаток от деления)
and, or, not, xor Позволяют
выполнять соответствующие бинарные операции для пар значений
ceq, cgt, clt Позволяют сравнивать пару значений из стека различными способами, например: ceq: сравнение в отношении равенства cgt: сравнение в отношении "больше" clt: сравнение в отношении "меньше"
box, unbox Используются для конвертирования ссылочных типов и типов, характеризуемых значениями
ret Используется для выхода из метода и (если это необходимо) возвращения значения вызывающей стороне
beq, bgt, ble, blt, switch Используются (в дополнение к множеству других родственных кодов операций) для управления логикой ветвления в методах, например: beq: переход к заданной метке, если выполняется равенство bgt: переход к заданной метке, если больше ble: переход к заданной метке, если меньше или равно blt: переход к заданной метке, если меньше Все коды операций, связанные с ветвлением, требуют указания метки CIL-кода, по которой должен осуществляться переход в том случае, когда соответствующее сравнение возвращает true
call Используется для вызова члена указанного типа
newarr, newobj Позволяет разместить в памяти новый массив или новый объект (cоответственно)

Следующая большая категория кодов операций CIL (подмножество которой показано в табл. 15.6) используется для загрузки аргументов в виртуальный стек выполнения. Обратите внимание на то, что эти относящиеся к загрузке коды операций имеют префикс ld (load – загрузка).

Таблица 15.6. Коды операций CIL для помещения данных в стек

Код операции  Описание
ldarg (с множеством вариаций) Помещает в стек аргумент метода. Вдобавок к общей операции ldarg (для которой требуется указать индекс, идентифицирующий аргумент), есть множество ее вариаций. Например, ldarg с числовым суффиксом (ldarg_0) используется для загрузки соответствующего аргумента. Другие вариации ldarg позволяют с помощью кодов констант CIL из табл. 15.4 указать конкретный тип загружаемых данных (например, ldarg_I4 для int32), а также тип данных и значение (ldarg_I4_5 для загрузки int32 со значением 5)
ldc (с множеством вариаций) Помещает в стек значение константы
ldfld (с множеством вариаций) Помещает в стек значение поля уровня экземпляра
ldloc (с множеством вариаций) Помещает в стек значение локальной переменной
ldobj Читает все значения объекта, размещенного в динамической памяти, и помещает их в стек
ldstr Помещает в стек строковое значение 

Вдобавок к множеству специальных кодов операций загрузки, CIL предлагает набор кодов операций, которые непосредственно "выталкивают" из стека самое верхнее значение. Как продемонстрировали первые несколько примеров этой главы, удаление значения из стека обычно выполняется с целью последующего сохранения этого значения в локальной памяти для дальнейшего использования (например, в качестве параметра при последующем вызове метода). С учетом этого становится ясно, почему многие коды операций, связанные с удалением текущего значения из виртуального стека выполнения, имеют префикс st (store – сохранять). Соответствующие описания приведены в табл. 15.7.

Таблица 15.7. Коды операций для извлечения данных из cтека

Код операции Описание
pop Удаляет значение, находящееся в настоящий момент на вершине стека, но не обеспечивает сохранение этого значения
starg Сохраняет
значение из вершины стека в аргументе метода с указанным индексом
stloc (c множеством вариаций) Удаляет значение, находящееся на вершине стека, и запоминает это значение в переменной с указанным индексом из списка локальных переменных
stobj Копирует значение указанного типа из стека в память по указанному адресу
stsfld Заменяет значение статического поля значением из cтека

Следует также знать о том, что различные коды операций CIL при выполнении своих задач неявно удаляют значения из стека. Например, при вычитании одного числа из другого с помощью операции sub следует учитывать то, что прежде чем выполнить соответствующее вычисление, sub "вытолкнет" из стека два доступных значения. После выполнения операции в стек добавляется результат (как неожиданно!).

Директива .maxstack

При реализации метода непосредственно средствами CIL нужно помнить о специальной директиве, которая называется .maxstack. Как следует из ее названия, директива .maxstack задает максимальное число переменных, которые может вместить стек в любой момент времени при выполнении метода. К счастью, директива .maxstack имеет значение по умолчанию (8), которого оказывается достаточно для подавляющего большинства методов, создаваемых разработчиками. Но у вас также есть возможность определить это значение явно, чтобы при желании вручную указать числа локальных переменных в стеке.

.method public hidebysig instanсе void Speak cil managed {

 // В контексте этого метода в стек помещается ровно

 // одно значение (строковый литерал).

 .maxstack 1

 ldstr "Всем привет…"

 call void [mscorlib]System.Consolr::WriteLine(string)

 ret

}

Объявление локальных переменных

Давайте выясним, как объявляются локальные переменные. Предположим, что мы должны построить в терминах CIL метод MyLocalVariables, не имеющий никаких аргументов и возвращающий void. В этом методе мы должны определить три локальные переменные типов System.String, System.Int32 и System.Object. В C# соответствующий программный код мог бы выглядеть так, как показано ниже (напомним, что локальные переменные не получают значения по умолчанию, поэтому им перед использованием необходимо присвоить начальные значения).

public static void MyLocalVariables {

 string myStr = "CIL me dude…";

 int myInt = 33;

 object myObj = new object;

}

Если создавать MyLocalVariables непосредственно в CIL, можно было бы написать следующее,

.method public hidebysig static void MyLocalVariables cil managed {

 .maxstack 6

 // Определение трех локальных переданных.

 .locals init ([0] string myStr, [1]int32 myInt, [2]object myObj)

 // Загрузка строки в виртуальный стек выполнения.

ldstr "CIL me dude…"

 // Извлечение текущего значения и сохранение его

 // в локальной переменной [0].

 stloc.0

 // Загрузка константы типа 'i4'

 // (сокращение для int32) со значением 33.

 ldc.i4 33

 // Извлечение текущего значения и сохранение его

 // в локальной переменной [1].

 stloc.1

 // Создание нового объекта и помещение его в стек.

newobj instance void [mscorlib]System.Object::.ctor

 // Извлечение текущего значения и сохранение его

Поделиться с друзьями: