    Функция f() завершается в строках 51-53, сначала устанавливая стековый 
указатель равным Ьр, чем освобождается зарезервированный участок стека, 
выделяющийся в строке 33. С этого момента целая переменная х прекращает свое 
существование. В строке 52 восстанавливается сохраненный регистр Ьр, после 
чего инструкция ret осуществляет возврат в место вызова функции f(). 
    Наряду с другими действиями, оставшиеся строки задают отладочную 
информацию (строки 55, 56), запоминают в сегменте данных программы (строки 
59-62) символьную строку, использующуюся при вызове функции printfO, 
экспортируют глобальные имена (строки 66, 67) и импортируют внешнюю 
библиотечную функцию printfO (строка 68). 
    Даже в этом простом примере за кулисами выполняется огромная работа! 
Следует понять, что "раскопки" в скомпилированном тексте требуют большого 
труда. Необходимо быть уверенным в том, что результат будет стоит приложенных 
усилий, прежде чем пуститься в подобное путешествие по нижнему уровню вашей 
программы. 

    Функции, возвращающие значения	

    Есть несколько способов, с помощью которых функция возвращает значение: 
слова передаются в регистре ах, байты — в al; дальние указатели передаются 
функциями в паре регистров dx:ax, в dx содержится сегмент, в ах — смещение 
адреса; вещественные значения передаются в стеке математического сопроцессора 
80x87; структуры — непосредственно в стеке; массивы — по адресу. 
    Существует много вариантов схем возврата значения. Для точного определения 
того, как обрабатываются отдельные типы возвращаемых значений, скомпилируйте с 
опцией -S короткий пример и изучите результирующий ассемблерный код. 
Бессмысленно запоминать множество правил возврата значений, которые могут 
измениться в будущих реализациях компилятора. Всегда следует изучить 
действительный способ возврата значения на примере функции, перед тем как 
делать какие-либо умозаключения по поводу того, как работает" низкоуровневый код. 

    Функции — члены класса 

    Объекты классов и их функции-члены усложняют ассемблерный текст. Взгляните 
на пример программы с простым классом (листинг 17.3). Скомпилируйте его с 
помощью команды bcc -S classf и исследуйте результирующий ассемблерный код в 
файле CLASSF.ASM. 

    Листинг 17.3.  CLASSF.СРР (скомпилируйте с опцией -S) 

    1:   «include <iostream.h> 
    2: 
    3: class TClass { 
    4: public: 
    5:    int x; 
    6:    void SetX(int n); 
    7:    int GetX(); 
    8: }; 
    9: 
    10: main() 
    11: { 
    12:   TClass q;
    13: 
    14:  q.SetX(123); 
    15:  cout « "q.GetXO == " « q.GetX() «  '\n'; 
    16:     return 0; 
    17:   }
    18: 
    19: void TClass::SetX(int n) 
    20: { 
    21:  x = n; 
    22: } 
    23: 
    24: int TClass::GetX() 
    25: { 
    26:    return x; 
    27: } 

    В классе TClass в строке 38 объявляется один глобальный член (int x) и две 
функции-члена для чтения и записи значения х. В ассемблерном коде функция-член 
SetX() после компиляции в версии 3.1 выглядит следующим образом: 

    push bp		;Сохранить Ьр 
    mov bp, sp	;Адресовать стек указателем bp 
    push si		;Сохранить si, используемый далее 
    mov si,word ptr  [bp+4]      ;Адресовать объект через 'this' с" помощью si 

    
    442     Часть IV. Разработка приложений 
