Интерфейс с программами и подпрограммами, написанными на разных языках программирования. Указатели на функции, страница 2

    }

  void swap(void *v[], int i, int j)

  { void *temp;

    temp = v[i];

    v[i] = v[j];

    v[j] = temp;

  }

2)  Вызов подпрограмм в том числе написанных на разных языках программирования.

Связь между подпрограммами можно осуществить тремя способами:

-  через общую зону (EXTERN - C, GLOBAL - Pascal, COMMON - Fortran)/

-  через регистры. Используется, если параметров не много, например в программах прерывания.

-  через стек.

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

Языки программирования принято разделять на процедурные (C, Pascal, Fortran, Basic) и непроцедурные (Lisp, Prolog, FORTH). В последних процедуры являются блоками программ, имеющие одну точку входа и одну точку выхода. Ассемблер одинаково легко можно использовать в качестве языка, способного использовать одинаково легко с обоими типами языков программирования.

Процедуры могут получать параметры из вызвавшей процедуры и передовать ей свои результаты с помощью следующих механизмов:

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

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

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

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

По имени, т.е. используют макроопределение, например директиву препроцессора C #define.

Для передачи параметров между языками высокого уровня существуют КОНВЕНЦИИ. Большинство языков программирования передают параметры вызывающей программе через СТЕК, а возвращаемое значение получают через определенный регистр. Например, в ПК возвращаемое значение получают через регистр AX (EAX) или через связку регистров DX:AX (EDX:EAX), если результаты не умещаются в одном регистре (например, при передаче адресов). В C значения, хранящиеся в этих регистрах или передаваемые в них устанавливать через макро _AX, _EAX, _DX, _EDX и т.д.

Конвенция PASCAL.

Этот способ применяется также в BASIC, FORTRAN, ADA, MODULA2 и состоит она в том, чтобы параметры помещать в стек в естественном поряде:

   Any_proc (int a,int b,int c,int d,int e)

запишется в виде:

         push a

         push b

         push c

         push d

         push e

         call Any_proc

Т.о. по окончании работы процедура Any_proc должна очистить стек до своего завершения. Сами параметры находятся в стеке в обратном порядке:

   Any_proc      proc

                 push   bp

                 push   bp,sp ; создать описание стека для

                              ; простого доступа к параметрам

   a             equ    [bp+12]

   b             equ    [bp+10]

   c             equ    [bp+8]

   d             equ    [bp+6]

   e             equ    [bp+4]

Завершение процедуры:

                 pop    bp

                 ret    10

   Any_proc      endp

Этот код в точности соответствует вызову

  Any_proc       proc  PASCAL a:word,b:word,c:word,d:word,e:word

Главный недостаток данной конвенции в сложности создания функций с переменным числом параметров (например, PRINTF), т.к. для этого сначала надо прочитать первый параметр, что сложно сделать.

Конвенция C.

Эта конвенция используется в языках C, C++, Prolog.

В этой конвенции параметры размещаются в стеке в обратном порядке:

         Any_proc (int a,int b,int c,int d,int e)

запишется в виде:

         push e

         push d

         push c

         push b

         push a

         call Any_proc

         add  sp,10   ; освободить стек

Процедура запишется так:

        Any_proc      proc

                 push   bp

                 push   bp,sp ; создать описание стека для

                              ; простого доступа к параметрам

   a             equ    [bp+4]

   b             equ    [bp+6]

   c             equ    [bp+8]

   d             equ    [bp+10]

   e             equ    [bp+12]

Завершение процедуры:

                 pop    bp

                 ret   

   Any_proc      endp

Здесь показан факт, что BP используется для хранения параметров и его нельзя менять.    

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

          push param2

          push param1

          call proc1

          call proc2

          add  sp,4

что эквивалентно

          proc1 (param1,param2);

          proc2 (param1,param2);

Это и позволяет компилятору C создавать самый компактный и быстрый код.

Известна также смешанная конвенция, когда расположение регистров в стеке аналогично C, но процедуры должны очистить стек по завершению работы сами, как в Pascal. Эта конвенция используется для системных функций WIN32 API.

Очень часто имена процедур изменяют, чтобы подчеркнуть используемую конвенцию. Например,

    call _someProc

подчеркивает, что вызов Ассемблера проводится в соответствии с конвенцией C.

Используемую конвенцию программирования можно заказать в Ассемблере, указав соответствующий идентификатор в описании процедур

        MODEL

И

        PROC.

В языке C можно заказать конвенцию C, PASCAL или передачу параметров через регистры, используя в турбо-языке адрес

   OPTION -> COMPILER -> ENTRY/EXIT CODE.