Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Раньше мы уже встречались с условным ходом выполнения программ, однако ограничивались короткими описаниями команд и поверхностным разбором выполняемых ими операций. Управляемый ход выполнения, без сомнения, самая важная веха развития программирования своего времени и затмевает многие последующие, даже такие, как структурное программирование или ООП. Только с высоты высокоуровневого языка кажется, что в условии if
нет ничего любопытного и оно лишено какого‑либо разнообразия. Но для компилятора это простор для самодеятельности! И в близких по духу ситуациях он может построить кардинально различающийся код.
В процессорной архитектуре x86 предусмотрен весьма «оригинальный» набор команд для перенаправления хода выполнения программного кода. «Оригинальный» не значит хороший или плохой, это значит, что он отличается от набора микропроцессоров других архитектур и производителей: ARM, MIPS, SPARC и так далее. Как ты знаешь, первый процессор этой серии создавался впопыхах для временной замены еще не готового iAPX-432, последний, в свою очередь, должен был уметь в многозадачность и управление памятью на аппаратном уровне. Чего в итоге не случилось. А x86 продолжил свое развитие. Поэтому сейчас, когда за годы эволюции процессоров серии x86 скопился набор инструкций, хакерам приходится разгребать весь этот хлам, дабы раскрутить порядок выполнения машинных инструкций.
Существует два вида алгоритмов — безусловные и условные. Порядок действий безусловного алгоритма всегда постоянен и не зависит от входных данных. Например, a = b + c. Порядок действий условных алгоритмов, напротив, зависит от данных, поступающих «на вход». Например:
если c не равно нулю,
то: a = b/c;
иначе: вывести сообщение об ошибке
Обрати внимание на выделенные жирным шрифтом ключевые слова «если», «то» и «иначе», называемые операторами условия или условными операторами. Без них не обходится ни одна программа (вырожденные примеры наподобие «Hello, World!» не в счет). Условные операторы — сердце любого языка программирования. Поэтому чрезвычайно важно уметь их правильно определять.
В общем виде (не углубляясь в синтаксические подробности отдельных языков) оператор условия схематично изображается так:
IF (условие) THEN { оператор1; оператор2; } ELSE { операторa; операторb; }
Задача компилятора — преобразовать эту конструкцию в последовательность машинных команд, выполняющих оператор1, оператор2, если условие истинно, и операторa, операторb — если оно ложно. Однако микропроцессоры серии 80x86 поддерживают весьма скромный набор условных команд, ограниченный фактически одними условными переходами. Программистам, знакомым лишь с IBM PC, такое ограничение не покажется чем‑то неестественным, между тем существует масса процессоров, поддерживающих префикс условного выполнения инструкции. То есть вместо того, чтобы писать:
TEST ECX,ECX
JNZ xxx
MOV EAX,0x666
там поступают так:
TEST ECX,ECX
IFZ MOV EAX,0x666
IFZ
и есть префикс условного выполнения, разрешающий выполнение следующей команды только в том случае, если установлен флаг нуля. В этом смысле микропроцессоры 80x86 можно сравнить с ранними диалектами языка Basic, не разрешающими использовать в условных выражениях никакой другой оператор, кроме GOTO. Сравни:
10 IF A=B THEN GOTO 30
20 GOTO 40
30 PRINT "A=B"
40 ... // Прочий код программы
IF A=B THEN PRINT "A=B"
Если ты когда‑нибудь программировал на старых диалектах Basic, то, вероятно, помнишь, что гораздо выгоднее выполнять GOTO
, если условие ложно, а в противном случае продолжать нормальное выполнение программы. Как видишь, вопреки расхожему мнению, навыки программирования на Basic отнюдь не бесполезны, особенно в дизассемблировании программ.
Большинство компиляторов (даже не оптимизирующих) инвертируют истинность условия, транслируя конструкцию
IF (условие) THEN { оператор1; оператор2; }
в следующий псевдокод:
IF (NOT условие) THEN continue
оператор1;
оператор2;
continue:
…
Следовательно, для восстановления исходного текста программы нам придется вновь инвертировать условие и «подцепить» блок операторов { оператор1; оператор2; } к ключевому слову THEN
. Иначе говоря, если откомпилированный код выглядит так:
10 IF A<>B THEN 30
20 PRINT "A=B"
30 ...// Прочий код программы
можно с уверенностью утверждать, что в исходном тексте присутствовали строки IF
. А если программист, наоборот, проверял переменные A и B на неравенство, то есть в коде присутствовала конструкция IF
? Все равно компилятор инвертирует истинность условия и сгенерирует следующий код:
10 IF A=B THEN 30
20 PRINT "A<>B"
30 ... // Прочий код программы
Конечно, встречаются и компиляторы, страдающие многословием. Их легко распознать по безусловному переходу, следующему сразу же после условного оператора:
IF (условие) THEN do
GOTO continue
do:
оператор1;
оператор2;
continue:
…
В таком случае инвертировать условие не нужно. Впрочем, если это сделать, ничего страшного не произойдет, разве что код программы станет менее понятным, да и то не всегда.
Рассмотрим теперь, как транслируется полная конструкция
IF (условие) THEN { оператор1; оператор2; } ELSE { операторa; операторb; }.
Одни компиляторы поступают так:
IF (условие) THEN do_it
// Ветка ELSE
операторa;
операторb;
GOTO continue
do_it:
//Ветка IF
оператор1;
оператор2;
continue:
А другие — так:
IF (NOT условие) THEN else
// Ветка IF
оператор1;
оператор2;
GOTO continue
else:
// Ветка ELSE
операторa;
операторb
continue:
Разница между ними в том, что вторые инвертируют истинность условия, а первые — нет. Поэтому, не зная «нрава» компилятора, определить, как выглядел подлинный исходный текст программы, невозможно! Однако это не создает проблем, ибо условие всегда можно записать так, как это удобно. Допустим, не нравится тебе вот такая конструкция:
IF (c <> 0) THEN a = b / c ELSE PRINT "Ошибка!"
Пиши ее так:
IF (c == 0) THEN PRINT "Ошибка!" ELSE a = b / c
И никаких гвоздей!
|
|