Зачем изучать ассемблер
дата публикации: 2016-02-11

К ассемблеру у всех разное отношение. Начинающие программисты его либо боготворят, либо считают мусором прошлого столетия. Рискну высказать свое мнение по этому поводу. Во-первых, я бы не допускал категоричных и полярных выражений в сторону ассемблера, а во-вторых, я с уважением отношусь к разработкам прошлого, ведь программисты прошлого века не могли представить себе, что проектирование программ станет доступным любому школьнику, который при этом вовсе не представляет, как работает программа изнутри. А нужны ли знания внутреннего устройства программы, если и так все работает? Железо сейчас достаточно мощное и "лишний" или непонятный код не мешает функционированию программы.

Когда я готовился к прочтению курса программирования на языке С, я четко понимал, что первые занятия следует посвятить ассемблеру как минимум затем, чтобы потом у студентов не возникало вопросов относительно ссылок и указателей. Например, начинающие программисты с трудом видят отличие между функциями fun1 и fun2:

void fun1(int, int);
void fun2(int *, int *);

int main(int argc, char *argv[])
{
    int a = 5, b = 6;
    printf("a = %i; b = %i\n",a,b);
    fun1(a, b);
    printf("a = %i; b = %i\n",a,b);
    fun2(&a, &b);
    printf("a = %i; b = %i\n",a,b);
    return 0;
}

void fun1(int a, int b)
{
    a *= 2;
    b *= 3;
}

void fun2(int *a, int *b)
{
    (*a) *= 2;
    (*b) *= 3;
}

Результат выполнения программы:

a = 5; b = 6
a = 5; b = 6
a = 10; b = 18

Как можно видеть, при вызове первой функции исходные данные не сменили свое значение, при вызове второй функции сменили. Какое отношение этот пример имеет к ассемблеру, скажете вы. А вот именно прямое. Дело в том, что человек, не работавший никогда с ассемблером, может не понимать что такое адрес ячейки памяти, а, следовательно, не увидеть разницы в описанных функциях. Понятно, что первая функция тривиальна и скорее просто выполняет пустую работу. Используя компилятор Turbo C получим код ассемблера для этого примера (приведу только часть кодов процедур):

; код операции a *= 2; первой процедуры
mov	dx,2
mov	ax,si
imul	dx
mov	si,ax
; код операции (*a) *= 2; второй процедуры
mov	dx,2
mov	ax,word ptr [si]
imul	dx
mov	word ptr [si],ax

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

Используя короткий пример, я попытался наглядно показать целесообразность изучения ассемблера на начальных этапах знакомства с языком C. При дальнейшем изучении программирования, когда речь заходит, скажем, о векторах указателей на экземпляры какого-либо класса или об указателях на указатели, знание ассемблера просто необходимо, иначе код может быть спроектирован с ошибками.

Что относительно ассемблерных вставок в код, то тут мое мнение неоднозначно, с одной стороны это позволяет повысить скорость работы кода, но с другой стороны современные компиляторы достаточно хорошо оптимизируют код. В связи с этим хочу отметить, что большинство самописных алгоритмов на ассемблере будут уступать по скорости алгоритмам, реализованным в стандартных, библиотечных функциях C, связано это с тем, что разработчики библиотек как правило обладают широкими знаниями ассемблера и понимания большого количества тонкостей аппаратной реализации тех или иных операций.

Исходя из представленных выше доводов, хочу рекомендовать всем интересующимся языками программирования изучение основ ассемблера, это расширит кругозор, позволит лучше понимать код.