C/Łączenie z innymi językami: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
m Dodano kategorię "C" za pomocą HotCat
m Update syntaxhighlight tags - remove use of deprecated <source> tags
Linia 11:
W naszym przykładzie założymy, że w pliku f1.S zawarty będzie kod, napisany w asemblerze, a f2.c to kod z programem w języku C. Program w języku C będzie wykorzystywał jedną funkcję, napisaną w języku asemblera, która wyświetli prosty napis "Hello world". Z powodu ograniczeń technicznych zakładamy, że program uruchomiony zostanie w środowisku [[w:POSIX|POSIX]] na platformie i386 i skompilowany kompilatorem gcc. Używaną składnią asemblera będzie AT&T (domyślna dla asemblera GNU) Oto plik f1.S:
 
<sourcesyntaxhighlight lang="asm">
.text
.globl _f1
Linia 29:
.string "Hello world\n"
len = . - tekst
</syntaxhighlight>
</source>
 
{{uwaga|W systemach z rodziny UNIX należy pominąć znak "_" przed nazwą funkcji f1}}
 
Teraz kolej na f2.c:
<sourcesyntaxhighlight lang="c">
extern void f1 (void); /* musimy użyć słowa extern */
int main ()
Linia 41:
return 0;
}
</syntaxhighlight>
</source>
 
Teraz możemy skompilować oba programy:
Linia 54:
==== Argumenty ====
Do komunikacji z funkcją język C korzysta ze stosu. Argumenty odkładane są w kolejności od ostatniego do pierwszego. Ponadto na końcu odkładany jest tzw. '''adres powrotu''', dzięki czemu po wykonaniu funkcji program "wie", w którym miejscu ma kontynuować działanie. Ponadto, początek funkcji w asemblerze wygląda tak:
<sourcesyntaxhighlight lang="asm">
pushl %ebp
movl %esp, %ebp
</syntaxhighlight>
</source>
Zatem na stosie znajdują się kolejno: zawartość rejestru EBP, adres powrotu a następnie argumenty od pierwszego do n-tego.
 
Linia 63:
Na architekturze i386 do zwracania wyników pracy programu używa się rejestru EAX, bądź jego
"mniejszych" odpowiedników, tj. AX i AH/AL. Zatem aby funkcja, napisana w asemblerze zwróciła "1" przed rozkazem ret należy napisać:
<sourcesyntaxhighlight lang="asm">
movl $1, %eax
</syntaxhighlight>
</source>
 
==== Nazewnictwo ====
Linia 74:
==== Łączymy wszystko w całość ====
Pora, abyśmy napisali jakąś funkcję, która pobierze argumenty i zwróci jakiś konkretny wynik. Oto kod f1.S:
<sourcesyntaxhighlight lang="asm">
.text
.globl _funkcja
Linia 84:
popl %ebp
ret /* ... i zwracamy wynik dodawania... */
</syntaxhighlight>
</source>
oraz f2.c:
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
extern int funkcja (int a, int b);
Linia 94:
return 0;
}
</syntaxhighlight>
</source>
Po skompilowaniu i uruchomieniu programu powinniśmy otrzymać wydruk:
2+3=5
Linia 103:
Ze wstawek asemblerowych korzysta się tak:
 
<sourcesyntaxhighlight lang="c">
int main ()
{
asm ("nop");
}
</syntaxhighlight>
</source>
 
W tym wypadku wstawiona zostanie instrukcja "nop" (no operation), która tak naprawdę służy tylko i wyłącznie do konstruowania pętli opóźniających.
Linia 115:
Język C++ z racji swojego podobieństwa do C będzie wyjątkowo łatwy do łączenia. Pewnym utrudnieniem może być obiektowość języka C++ oraz występowanie w nim przestrzeni nazw oraz możliwość [[C++/Przeciążanie funkcji|przeciążania funkcji]]. Oczywiście nadal zakładamy, że główny program piszemy w C, natomiast korzystamy tylko z pojedynczych funkcji, napisanych w C++. Ponieważ język C nie oferuje tego wszystkiego, co daje programiście język C++, to musimy "zmusić" C++ do wyłączenia pewnych swoich możliwości, aby można było połączyć ze sobą elementy programu, napisane w dwóch różnych językach. Używa się do tego następującej konstrukcji:
 
<sourcesyntaxhighlight lang="c">
extern "C" {
/* funkcje, zmienne i wszystko to, co będziemy łączyć z programem w C */
}
</syntaxhighlight>
</source>
 
W zrozumieniu teorii pomoże Ci prosty przykład:
plik f1.c:
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
extern int f2(int a);
Linia 132:
return 0;
}
</syntaxhighlight>
</source>
 
oraz plik f2.cpp:
<sourcesyntaxhighlight lang="c">
#include <iostream>
using namespace std;
Linia 145:
}
}
</syntaxhighlight>
</source>
Teraz oba pliki kompilujemy:
gcc f1.c -c -o f1.o