Wykład 4 - 3 godz
Zakres tematyczny
1. Łączenie procedur w różnych językach
Czasami przychodzi moment, kiedy programy napisane w języku C/C++ muszą wywołać programy napisane w innych językach, lub kiedy program napisany w innym języku musi wywołać funkcję napisaną w języku C/C++. Taki proces nazywany jest mixed-language programing- programowanie w mieszanych językach. Np. kiedy jakiś szczególny podprogram dostępny jest w języku innym niż C/C++, lub kiedy algorytm opisany jest w sposób bardziej naturalny w innym języku, występuje potrzeba użycia w programie, więcej niż jednego języka.
Na dzisiejszym wykładzie omówimy najważniejsze zasady mixed language programing.
Tworzenie wywołań w językach mieszanych
Programowanie w językach mieszanych MLP, sprowadza się do odwołań do funkcji, procedur lub podprogramów, w zależności od konwencji przyjętej dla danego języka. Np.:, BASIC-kowski moduł główny, może wykonywać specyficzne zadanie, które chciałby programista zaprogramować oddzielnie. Zamiast wywoływać jednak podprogram BASIC-kowski, decyduje się wywołać funkcję C.
Wywołanie funkcji napisanych w różnych językach polega na wywołaniu funkcji umieszczonych w różnych modułach. Zamiast kompilowania wszystkich modułów jednym kompilatorem, używa się różnych kompilatorów (różnych języków). Na marginesie należy tu zaznaczyć, iż uwaga ta dotyczy kompilatorów języków tego samego producenta. np. Microsoft czy też Borland, co związane jest z różnicami w formatach modułów obj.
Dla powyższego przykładu oznacza to, że moduł główny kompilowany jest w kompilatorze BASIC, inny zbiór zawierający funkcję C kompilatorem C, a dopiero potem oba zbiory obj-towe linkowane są w jeden kod wynikowy.
Na rysunku powyżej widzimy wywołanie z poziomu języka BASIC funkcji prn() języka C, podobnej do podprogramu w języku BASIC. Są dwie istotne różnice pomiędzy tym wywołaniem mieszanym, z wywołanie pomiędzy dwoma BASIC-kowskimi modułami:
- podprogram prn jest zaimplementowany w języku C, używając standardu tego języka
- wykonanie wywołania w BASIC'u jest poprzedzone użyciem instrukcji DECLARE używającej słowa kluczowego CDECL do stworzenia kompatybilności z C.T a interfejsowa instrukcja, zmienia nazewnictwo i konwencje wywołań. Każdy język dostarcza swoich własnych form interfejsów.
Można wykonywać wywołania mieszane nie zważając na to, czy funkcje wywoływane z innych modułów zwracają wartości.
Językowe odpowiedniki dla wywołań funkcji (procedur, podprogramów)
Język Zwracająca wartość Nie zwracająca wartości
Asemblery procedure procedure
BASIC FUNCTION procedure Subprogram
C/C++ function void function
FORTRAN FUNCTION SUBROUTINE
PASCAL Function Procedure
Jak widać z tabelki moduł C może wykonać wołanie fortranowskiego SUBROUTINE, będącego odpowiednikiem void function z języka C, itp.
Wymagania dotyczące konwencji językowych
W mieszanym programowaniu, wywołujący program musi przestrzegać tych samych konwencji, jak program wywoływany. Konwencje te można pogrupować w następujące grupy:
-jak kompilator traktuje identyfikatory, włączając nazwy funkcji i zmiennych - konwencja nazewnictwa
-jak zaimplementowane jest wywołanie podprogramu - konwencja wywołań
-jak przekazywane są parametry - konwencja przesyłania parametrów.
Konwencja nazewnictwa
Zarówno program wywołujący jak i wywoływany podprogram, musza zgadzać się na płaszczyźnie nazw identyfikatorów. Identyfikatory mogą odnosić się do podprogramów (funkcji, procedur) lub zmiennych mających publiczny lub globalny zakres. Każdy język zmienia nazwy identyfikatorów.
Termin "konwencja nazewnictwa" odnosi się do sposobu w jaki procesor zmienia nazwy podprogramów zanim umieści je w zbiorze obj-towym. Każdy język zmienia te nazwy na swój sposób. Programista może wybierać pomiędzy różnymi konwencjami nazewnictwa, aby upewnić się że nazwy w wywołującym programie zgadzają się z tymi w programie wywoływanym. Jeśli nazwy wywoływanego podprogramu są przechowywany w każdym zbiorze obj-towym inaczej, wtedy linker nie byłby w stanie znaleźć powiązań. Będzie wtedy raportował o nierozerwalnym zewnętrznych powiązaniach.
Większość kompilatorów języka ładuje kod maszynowy do zbioru obj-towego. Przechowywane tam też są nazwy publicznych podprogramów i zmiennych. Linker może wtedy porównać nazwę podprogramu wywoływanego w jednym module z nazwą podprogramów zdefiniowanych w innym module i rozpoznaje powiązania. Nazwy tworzone są zgodnie ze standardem znaków ASCII
BASIC, FORTRAN i PASCAL używają podobnej konwencji nazewnictwa. Tłumaczą każdą literę na duże znaki.
Każdy język rozpoznaje różną liczbę znaków. I tak np. FORTRAN rozpoznaje pierwszych 31 znaków w nazwie (chyba że nazwy identyfikatorów są obcinane - ustawieniem opcji $TRUNCATE), Pascal pierwsze 8, Basic pierwszych 40 znaków. Jeśli nazwa zawiera więcej znaków niż dopuszcza dany kompilator, pozostałe znaki nie są po prostu umieszczane w zbiorze obj-towym.
Kompilatory języka C nie zamieniają każdej litery na duże. Nazwa każdego podprogramu uzupełniana jest od przodu znakiem podkreślenia. Kompilator tego języka rozpoznaje 31 znaków nazwy identyfikatora (32 włączając znak podkreślenia). Używając odpowiednich opcji kompilatora (np./H) można zmienić liczbę rozpoznawanych znaków.
Kompilator języka C++..................................................... Rozpoznaje pierwszych 247 znaków nazwy.
Słowa kluczowe języków programowania mieszanego zajmują się automatycznie różnicami w konwencji nazewnictwa tak długo jak przestrzegane są dwie zasady:
1. ograniczać nazewnictwo identyfikatorów do 6 znaków, jeśli:
a). włączane procedury kompilowane były kompilatorem Fortran <5.0
b) włączane procedury kompilowane były kompilatorem z opcją /4Yt lub w metakomendą $TRUNCATE
2. nie używać opcji linkera /NOIGNORECASE. Z modułami C/C++ oznacza to, że w czasie programowania nie można liczyć na rozpoznawanie dużych i małych liter.
Jeśli używamy opcji /Gc (generate Pascal-style function call) podczas kompilacji , lub deklarujemy funkcję lub zmienną ze słowem kluczowym __pascal, kompilator przekształca nazwy identyfikatorów na duże litery.
Zwróćmy uwagę, że np. kompilator Basci'u wprowadza poprzedzający nazwę funkcji znak podkreślenia do zbioru obj-towego, ponieważ słowo CDECL każe kompilatorowi Basic'u używać konwencji nazewnictwa charakterystycznej dla języka C. Powoduje również zamianę wszystkich znaków nazw na małe litery (co prawda nie jest to konwencja przyjęta dla języka C, ale uznawana jest powszechnie przy pisania programów w tym języku).
Konwencja wywołań
Termin ten odnosi się do sposobu w jaki kompilator realizuje wywołania. Wybór konwencji polega na zrealizowaniu odpowiedniej wygenerowanej przez kompilator instrukcji maszynowej wykonującej (i powracającej z ) wywołanie funkcji, procedury lub subroutine.
................
Wybór konwencji wywołań oddziaływuje na program w trzech aspektach:
1. określenia kolejności w jakiej zostaną przesłane parametry do innego podprogramu. Można to określić przy pomocy specjalnych deklaracji lub używając instrukcji interfejsowych, dotyczących programowania mieszanego. Konwencje te dotyczą podprogramu wywołującego
2. dla podprogramu wywoływanego będzie to określenie kolejności odbierania parametrów do nich przesyłanych. W większości programów sposób odbierania parametrów określany jest w nagłówkach podprogramów, jednak np. Basic zawsze używa swojego własnego sposobu odbierania parametrów.
3. oba podprogramy: wywołujący i wywoływany muszą zgadzać się jeśli chodzi o odpowiedzialność za uporządkowanie stosu w czasie usuwania parametrów.
Innymi słowy: każde wywołanie podprogramu używa pewnej konwencji wywołań. Każdy nagłówek podprogramu zawiera określenie lub ustawienie innej konwencji. Musza być one kompatybilne dla obu podprogramów. W każdym języku z wyjątkiem Basica jest możliwość zmiany tej konwencji, w momencie wywołania lub przy pomocy deklaracji w wywoływanym podprogramie. Zwykle jednak łatwiej jest dostosować konwencje wywoływanego programu.
Języki C++, Basic, Fortran i Pascal używają tego samego standardu wywołań, natomiast język C ma odmienną.
Jeśli chodzi o uporządkowanie stosu, to języki :C++, Pascal, Fortran i Basic przesyłają parametry inaczej jak język C. Składają one parametry na stos w kolejności icj pojawienia się w kodzie źródłowym np. Basic-kowe wywołania:
CALL Calc(A,B)
składuje na stosie parametr A, przed parametrem B. W tej konwencji przyjęte jest też, że za uporządkowanie stosu odpowiada procedura wywołana bezpośrednio przed przejęciem sterowania przez wywołujący moduł.
W konwencji C parametry przesyłane są w odwrotnej kolejności. Np. wywołanie funkcji C:
calc(a,b)
składuje na stosie najpierw parametr b, a potem dopiero a. W przeciwieństwie od innych języków wysokiego poziomu, konwencja języka C określa, że podprogram wywołujący porządkuje stos natychmiast po zwrócenia sterowania przez wywoływany podprogram.
Konwencja wywołań Basic'u, Pascal'a i Fotran'u sprawia, że kod obj-towy jest mniejszy niż C. Jednak Z kolei jak państwo wiecie konwencja języka C umożliwia wywołanie funkcji ze zmienną liczba parametrów. Jeśli w języku C++ używamy wywołania funkcji ze zmienna liczbą argumentów, funkcja używa automatycznie konwencji wywołań dla języka C.
Należy unikać używania słowa kluczowego __fastcall lub opcji /Gr przesyłającej parametry do rejestrów, Tworzy to niekompatybilności z programami napisanymi w innych językach.
Konwencja przesyłania parametrów
Oprócz zgodności konwencji nazewnictwa i wywołań, programy muszą zgadzać się co do sposobu w jakim przesyłane są parametry. Zapewnia to poprawną transmisję danych i prawidłowe efekty pracy programu. Jak państwo wiecie w języku C były dwa sposoby przesyłania parametrów: poprzez wartość i adres, dla C++ doszło jeszcze przesyłania przez referencje. Z wyjątkiem języka Basic który przyjął jedynie przesyłanie przez adres (typu near - w obrębie jednego segmentu), pozostałe języki mają możliwość wykorzystania takich samych sposobów przesyłania parametrów.
Kiedy programujemy w językach mieszanych należy:
- upewnić się, czy podprogram wywołujący i wywoływany używają tego samego sposobu przesyłania parametrów. Każdy język ma mechanizmy umożliwiające zmianę tych metod.
Każdy język dostosowuje określoną metodę przesyłania parametrów dla określonych typów zmiennych, ale programista ma możliwość sterowania (zmiany tego sposobu - oczywiście w rozsądny sposób). Rysunek przedstawia przyporządkowanie "by default" poszczególnych metod, poszczególnym rodzajom zmiennych:
Język przez adres Near przez adres Far przez wartość
BASIC wszystkie - -
C/C++ małe tablice duże tablice pozostale
FORTRAN wszystkie(medium model) wszystkie (large model) z atrybutami
PASCAL var, const vars,consts inne parametry
Kompilacja i linkowanie
Po odpowiednim napisaniu programu, z zastosowaniem odpowiednich konwencji, pora na kompilowanie i linkowanie poszczególnych modułów.
Kompilacja z odpowiednim modelem pamięci
W językach Pascal, Basic, Fortran nie ma specjalnych opcji wymaganych do kompilacji zbiorów źródłowych, będących częścią programu napisanego w językach mieszanych.
Ponieważ języki te używają tylko adresów kodu typu FAR należy wybrać jedną z dwu technik dla programu w języku C/C++ wywołującego podprogramy w jednym z tych języków:
- kompilować program w modelu: medium, huge lub large (używających również kodu adresu typu FAR)
- używanie słowa kluczowego __far do definicji funkcji publicznych języka C/C++.
Wybranie modelu pamięci w C,C++ lub Fortranie powoduje ustawienie rozmiaru wskaźnika danych "by default", chociaż mogą być one zmieniane słowami kluczowymi: __near, __far. Określa on również czy obiekt umieszczany jest w bieżącym segmencie danych. Jeśli nie, nie może być przesyłany przez adres typu __near.
Linkowanie z bibliotekami
W większości przypadków, można w prosty sposób łączyć moduły kompilowane różnymi językami. Aby zagwarantować, że wszystkie wymagane biblioteki linkowane będą w odpowiedniej kolejności, należy wykonać jedną z następujących czynności:
1. umieścić wszystkie biblioteki w tej samej kartotece co źródła
2. w zmiennej LIB określić katalogi zawierające wszystkie potrzebne biblioteki
3. pozwolić, aby linker promptował (prosił o określenie z ręki) biblioteki
W każdym powyższym przypadku, linker znajdzie biblioteki w kolejności jaka jest wymagana dla niego.
Link/NOD mod1 mod2,,,GRAFIX+LLIBCE+LLIBFOR
mod1,mod2 - dwa moduły napisane w językach mieszanych
GRAFIX- biblioteka użytkownika
LLIBCE - biblioteka C
LLIBFORE - biblioteka Fortranowska
Wywoływanie podprogramów języków wyższego poziomu
z poziomu C
Interfejsem do innych języków programowania w języku C jest użycie słów kluczowych __fortran/__pascal. Ich użycie powoduje, że podprogramy będą wywoływane używając Fortran/Pascal-owych konwencji nazewnictwa i wywołania. Konwencje te działają również dla Basic'a.
Aby poprawnie zrealizować wywołanie mieszane należy wykonać następujące działania:
1. napisać prototypy każdej wywoływanej mieszanej procedury. Prototyp powinien zawierać deklaracje extern, chociaż nie jest to wymogiem. Zamiast użycia w/w słów kluczowych, można stosować opcję /Gc. Powoduje ona, że wszystkie funkcje używają fortranowo/pascalowej konwencji z wyjątkiem tych które stosują słowo kluczowe __cdecl .
2. Przesłać zmienne lub adresy zmiennych, w sposób odpowiadający danej konwencji.
3. wykonać wywołanie funkcji w programie tak jak gdyby była to funkcja C
4. kompilować moduły C w medium huge lub large model, lub użyć __far w prototypie funkcji. Zapewni to wykonanie wywołania typu far.
Użycie słów kluczowych __pascal lub __fortran
Dwie zasady kierują użyciem tych słów:
1. modyfikują identyfikatory znajdujące się bezpośrednio po ich prawej stronie
2. razem z nimi mogą być użyte słowa __near, __far. Sekwencje:
__fortran__far
__far__fortran
sa ekwiwalentne
Słowo kluczowe:
__pascal - deklaruje podprogram Pascal-owy
__fortran - deklaruje podprogram Fortranowski
obie - deklarują podprogram BASIC-owski
Użycie tych słów daje ten sam efekt. Użycie jednego lub drugiego nie powoduje żadnych różnic, z wyjątkiem wewnętrznej dokumentacji programu.
Przykłady:
short __pascal func(short sarg1,short sarg2);
deklaracja funkcji func Basic-owa, Pascal-owa lub Fortran-owską pobierającą 2 argumenty short i zwracającą wartość short.
void (__fortran *func)(long larg);
deklaracja funkcji func Basic-owa, Pascal-owa lub Fortran-owską pobierającej zmienną long i nie zwracającej żadnej wartości. Void odpowiednie jest do użycia dla podprogramu w BASIC, procedury w PASCAL lub subroutine w FORTRAN, które nie zwracają wartości.
short __near__pascal func(__near double *darg);
równoważne:
short __pascal__near func(__near double *darg);
deklaracja funkcji func Basic-owa, Pascal-owa lub Fortran-owską typu near pobierającą argument przez referencjeadres) i zwracającą wartość short.
Przy wywoływaniu podprogramu w Basicu musimy używać w/w słów. Dla podprogramów Fortranowskich lub Pascalowych mamy wybór: albo adaptować C do konwencji F/P, albo adaptować Fortran lub Pascal do konwencji C. Ustawia się wówczas atrybut C w nagłówku definicji podprogramu. Następujące przykłady ilustrują sposób postępowania:
dla podprogramu Fortranowskiego:
SUBROUTINE FFROMC [C] (N)
INTEGER *2 N
dla podprogramu w Pascalu:
PROCEDURE Pfromc(n:integer) [C];
aby zaadaptować procedure C do konwencji P/F deklarujemy funkcje jako__pascal lu __fortran np.:
void __pascal CfromP(int n);
Wywołanie BASIC z poziomu C
Żaden podprogram napisany w Basic'u nie zostanie wykonany, jeśli program główny nie będzie napisany również w tym języku . Jest to spowodowane, wymaganiami co do środowiska, które musi być inicjowane w sposób unikalny dla tego języka. Żaden inny język nie wymaga takiej szczególnej inicjalizacji.
Jednakże, program może startować z poziomu Basic, wywoływać funkcję C wykonującą większość programu i wtedy wywołać podprogram w Basicu. Rysunek poniżej ilustruje jak to zrobić:
Przy wywoływaniu Basic z poziomu C należy postępować zgodnie z następującymi zasadami:
1. Napisać moduł główny w języku BASIC. Potrzebna jest instrukcja DECLARE określająca interfejs z C
2. W module C napisać prototyp dla podprogramu Basic i dołączyć informacje o typie parametrów. Użyć słów kluczowych __pascal lub __fortran
3. Upewnić się, czy wszystkie dane przesyłane są jako wskaźniki near. Basic może przesyłać parametry na różne sposoby, ale nie może otrzymać ich w inny sposób niż wskaźnik near. Jeśli chcemy przesłać dane nie mieszczące się w bieżącym segmencie, należy je przekopiować do zmiennej w segmencie bieżącym
4. Skompilować moduł C w modelu medium lub large
Poniższy program demonstruje program Basicowski wywołujący funkcję C, która z kolei wywołuje funkcję Basicowska, zwracającą podwojoną liczbę przesłaną do niej. Drukuje te dwie liczby:
'BASIC program
'The main program is in BASIC becouse of BASIC's startup
'requirements. The BASIC main program calls the C function Cprog
'Cprog calls the BASIC subroutine Db1
'
DEFINT A-Z
DECLARE SUB Cprog CDECL()
CALL Cprog
END
'
FUNCTION Db1(N) STATIC
Db1 = N*2
END FUNCTION
'
SUB Printnum(A,B) STATIC
PRINT "The first number is";A
PRINT "The second number is";B
END SUB
/* C source; compile in medium or large model
The parameters are declared as near pointer becouse of Basic requirements*/
int __fortran db1(int __near *N);
void __fortran printnum(int __near *A, int __near *B);
void cprog()
{
int a=5,b=6;
printf("%D times 2 is %d
,a,db1(&a) );
printnum(&a,&b);
}
Konwencje nazewnictwa i wywołań określane są przez słowo kluczowe CDECL w deklaracji Basicowej i __fortran w deklaracji funkcji db1 i printnum w module C.
Wywołanie podprogramu Fortranowskiego z poziomu C
W języku Fortran egzystują dwa rodzaje podprogramów: subroutine oraz function. Function zwraca wartość , subroutine nie. Poniżej przedstawione zostaną dwa przykłady ilustrujące różnice w użyciu function i subroutine.
Wywołannie subroutine z poziomu C
/* C source file - calls FORTRAN subroutine
compile in medium or large model */
extern void __fortran maxparam(int __near *I, int __near *J);
/*Declared as void becouse there is no return value;
Fortran keword causes C to use FORTRAN/PASCAL
calling and naming convenctios
two integer parameters, passed by near adresse */
main()
{
int a=5, b=7;
printf("a = %d, b = %d", a,b);
maxparam(&a,&b);
printf(" a=%d, b=%d", a,b);
}
C FORTRAN source file, subroutine MAXPARAM
C
$NOTRUNCATE
SUBROUTINE MAXPARAM(I,J)
INTEGER*2 I [NEAR]
INTEGER*2 J [NEAR]
C
C I and J received by near adress becouse of NEAR attribute
C
IF (I.GT.J) THEN
J=I
ELSE
I=J
ENDIF
END
Procedura maxparam przypisuje większemu parametrowi wartość mniejszego.
W tym przykładzie C program adoptuje konwencje Fortranowskie przy pomocy użycia w prototypie funkcji słowa kluczowego __fortran.
Ponieważ z treści wynika, iż parametry mogą zmieniać swoją wartość musimy je przesłać przez adres(referencje). W tym przypadku wybrano typ near. W związku z tym w programie Fortranowskim należy je również zadeklarować jako NEAR.
Gdybyśmy kompilowali program fortranowski w modelu medium, a parametry w C zadeklarowane byłyby jako FAR podobną operacje należałoby wykonać w programie fortranowskim.
Wywołanie function z poziomu C
Poniższy przykład demonstruje wywołanie z poziomu C Fortranowskiej funkcji liczącej silnię:fact:
/* C source file - calls FORTRAN function
compile in medium or large memory model */
int __fortran fact(int N);
/* Fortran keyword causes C to use FORTRAN calling and naming convenction.
Integer parameter passed by value */
main()
{
int x=3,y=4;
printf("The factorial of x is %4d",fact(x) );
printf("The factorial of y is %4d",fact(y) );
printf("The factorial of x+y is %4d",fact(x+y) );
}
C FORTRAN source file - factorial function
C
C N is received by value, because of VALUE attribute
C NOTRUNCATE - no truncate identifier name to 6 characters
C
$NOTRUNCATE
INTEGER *2 FUNCTION FACT (N)
INTEGER*2 N[VALUE]
C
INTEGER *2 I
FACT = 1
DO 100 I=1,N
FACT=FACT*I
100 CONTINUE
RETURN
END
Ponieważ w przeciwieństwie do poprzedniego przykładu parametry nie ulegały zmianie zdecydowano przesłać je przez wartość (sposób default dla języka C). Wymagało to określenia atrybutu VALUE dla przesyłanych parametrów w definicji funkcji Fortranowskiej.
Wywoływanie podprogramów Pascalowskich z poziomu C
W pascalu mamy do czynienia z dwoma rodzajami podprogramów: procedurą i funkcją. Procedura nie zwraca wartości, funkcja zwraca.
Procedura Pascalowa wywoływana z poziomu C
Procedura Pascalowska maxparam jest odpowiednikiem takiej samej w języku Fortran:
/* C source file - calls Pascal procedure. Compile in medium or large memory model */
void __pascal maxparam(int __near *a, int __near*b);
/* Declare as void becouse there is no return value.The __pascval keyword causes C to use PASCAL calling and naming convenction. Two integer parameters passed by near adress */
main()
{
int a=5,b=7;
printf("a=%d, b=%d",a,b);
maxparam(&a,&b);
printf("a=%d,b=%d",a,b);
}
{ Pascal source code = maxparam procedure }
MODULE Psub;
PROCEDURE Maxparam(var a:integer;var:b:integer);
begin
if a>b then
b:=a
else
a:=b;
end;
end.
W powyższym przykładzie ponieważ parametry przesyłane były przez adress near można było użyć w procedurze słowa VAR. Dla adresu far konieczne byłoby użcie VARS. Konwewncja pascalowa określana jest przez słowo kluczowe __pascal
Wywołanie funkcji Pascalowskiej z poziomu C
Funkcja jest odpowiednikiem funkcji z Fortranu:
/* C source file - calls PASCAL function
compile in medium or large memory model */
int __pascal fact(int N);
/* Pascal keyword causes C to use PASCAL calling and naming convenction.
Integer parameter passed by value */
main()
{
int x=3,y=4;
printf("The factorial of x is %4d",fact(x));
printf("The factorial of y is %4d",fact(y));
printf("The factorial of x+y is %4d",fact(x+y));
}
{ Pascal source code - factorial function }
MODULE Pfun;
FUNCTION Fact (n:integer):integer;
begin
fact:=1;
while n>0 do
begin
fact :=fact*n;
n:=n-1;
end;
end;
end.
Dla języka C++ interfejs pomiędzy językiem C++ i innym językiem wyższego poziomu odbywa się poprzez odniesienie do C. Mimo, że język C jest podzbiorem języka C++, ze względu na różnice między sposobem pracy ze stosem w momencie wywoływania funkcji (kolejność porządkowania, kolejność składowania parametrów na stosie) należy używając na poziomie C++ funkcji napisanych w C, dołączyć interfejs: extern "C"
{
void prin; // odwołanie do funkcji w języku C
}
extern "C" { int __pascal fact(int n); } //deklaruje funkcję napisaną w konwencji Pascala
Owołanie C do języków asemblerowych
Najczęstszym sposobem pisania "wstawek" asemblerowych jest użycie tzw. inline asemblera. Można również tworzyć samodzielne moduły w dostępnych asemblerach zewnętrznych. Jednak asembler "inline" jest bardziej efektywny od samodzielnych asemblerów. Oto kilka zalet takiego podejścia:
1. kod asemblera inline włączony jest do kodu języka C. Kod napisany w asemblerach zewnętrznych musi być umieszczony w oddzielnych plikach.
2. krótkie wstawki asemblerowe mogą optymalizować program
3. nie wymagają też wykonywania wywołania funkcji tak jak by to było a asemblerach zewnętrznych . Są to po prostu linie kodu tyle tylko, że w asemblerze
Nie będziemy mówili o asemblerach zewnętrznych. Powiemy tylko parę słów o asemblerze "inline";
Zakres użycia tej techniki jest bardzo szeroki jednak najczęściej stosowany jest do:
- poprawiania szybkości programu
-zmniejsza potrzeb co do zajętości pamięci
-użycie w prosty sposób funkcji DOS i BIOS z instrukcja INT
Asembler inline umożliwia umieszczenie kodu asemblerowego bezpośrednio w kodzie C. Jest wbudowany w kompilator i nie wymaga zewnętrznych asemblerów
Kod asemblerowy poprzedzony musi zostać słowem kluczowym asm:
__asm
{
mov ah,2
mov dl,7
int 21h
}
kod ten równoważny jest zapisowi:
__asm mov ah,2
__asm mov dl,7
__asm int 21h
Jeśli słowo kluczowe asm używane jest przed każdą instrukcja wtedy mogą być one umieszczone w jednej lini:
__asm mov ah,2 __asm mov dl,7 __asm int 21h
Funkcja asemblera inline w kodzie C
#include
int power2(int num,int power);
void main(void)
{
printf("3 times 2 to the powerr of 5 is %d
",power2(3,5) );
}
int power2(int num,int power)
{
__asm
{
mov ax,num;
mov cx,power;
shl ax,cl;
}
/* return with result in ax */
}
Wywoływanie funkcji C w bloku asemblerowym
W bloku __asm możemy wywoływać funkcje biblioteczne języka C: wykonuje to instrukcja CALL:
#include
char format[]="%s %s";
char hello[] = "Hello";
char world[] = "world";
void main(void)
{
__asm
{
mov ax, offset world
push ax
mov ax, offset hello
push ax
mov ax, offset
push ax
call printf
}
}
funkcja printf zbiera swoje argumenty, które umieszczane są na stosie.
Powyższy kod emuluje zapis C:
printf(format,hello,world);
Informacje dodatkowe
Domyślne przyjęcie konwencji nazewnictwa i wywołań
Język Konwencja wywołań Konwencja nazewnictwa Przesyłanie parametrów
BASIC Fortran/Pascal Case insesitive Near adress
C C Case sensitive Value(scalar value)
address(array)
C++ Fortran/Pascal Case sensitive Value(scalar value)
address(array)
FORTRAN Fortran/Pascal Case insesitive address
PASCAL Fortran/Pascal Case insesitive Value
Odpowiedniki typów danych
BASIC C/C++ FORTRAN Pascal
x% short INTEGER *2 INTEGER2
INTEGER int - INTEGER
- unsigned short - WORD
- unsigned - -
x& long INTEGER*4 INTEGER4
LONG - INTEGER -
- unsigned long - -
x! float REAL *4 REAL4
x(default) - REAL REAL
x# double REAL*8 REAL8
DOUBLE - DOUBLE PRECISION -
- long double - -
- unsigned char CHARACTER *1 CHAR
SINGLE - - -