Autor: Dondu
Kurs języka C: Spis treści
Normalne funkcje przez kompilator zamieniane są na fragmenty kodu maszynowego i umieszczane w innych fragmentach pamięci programu niż program go wywołujący. Wywołanie takiej funkcji powoduje wykonanie skoku do tego fragmentu pamięci programu. Takie funkcje z reguły na początku posiadają rozkazy umieszczające na stosie (w pamięci ram) wartości rejestrów wykorzystywanych przez daną funkcję, a ściągają je ze stosu podczas wychodzenia z funkcji rozkazem powrotu.
inline = In Line
(ang. w linii)
Funkcje inline to funkcje, które w wynikowym kodzie maszynowym wstawiane są w miejsce, gdzie użyłeś je w kodzie C. Nie zawierają zatem rozkazów umieszczania rejestrów na stosie, ani ściągania z niego. Nie zawierają także rozkazów powrotu z funkcji.
Skoro funkcja jest wstawiana w kodzie maszynowym w miejscu jej użycia w kodzie C, to powstaje pytanie, jak to jest realizowane, gdy mam zdefiniowaną jedną funkcję inline, a użyję jej w kilku miejscach programu?
Odpowiedź jest prosta: Kompilator wstawi kod funkcji w każde miejsce, w którym zostanie użyta, przez co wynikowy kod maszynowy będzie z reguły większy niż przy zastosowaniu zwykłych funkcji.
Stąd płynie także wniosek, że w większości przypadków funkcje inline powinny być krótkie, czyli realizować bardzo małe fragmenty kodu.
Return w funkcji inline
Return w funkcji inline ma dla programisty C dokładnie takie samo znaczenie, jak w zwykłych funkcjach. Różnica polega na tym, że w kodzie maszynowym nie będzie umieszczony rozkaz powrotu z funkcji, ponieważ nigdzie nie wykonaliśmy skoku, ale efekt będzie taki jak napisałeś w kodzie C.
Ale tym sobie głowy nie zaprzątaj, bo dla Ciebie nie ma to większego znaczenia, a użycie w funkcji inline instrukcji return upodabnia je do zwykłych, przez co ułatwia pisanie kodu.
Kiedy stosować funkcje inline?
Przede wszystkim w miejscach, w których zależy nam na szybkości działania programu, ponieważ nie tracimy w ten sposób czasu na operacjach skoku do funkcji, operowania na stosie i powrotu z funkcji.
Poniżej przykład funkcji inline.
Przykład 1 (w kompilatorze)
inline int obliczenia(int a, int b, int c){ //funkcja oblicza wartość według wzoru: y = a*b + c int y; y = a*b; y = y + c; return y; } int main(void) { int a = 5; int b = 15; int c = 3; printf( "a*b + c = %d \n", obliczenia(a, b, c) ); a = 80; printf( "a*b + c = %d \n", obliczenia(a, b, c) ); a = 97; c = 19; printf( "a*b + c = %d \n", obliczenia(a, b, c) ); return 0; }
Niestety w kompilatorze CManiak nie zauważysz różnicy pomiędzy funkcją zwykłą, a funkcją inline, ponieważ nie pokazuje on wynikowego kodu maszynowego.
Dlatego poniżej pokazuję kod odpowiadający powyższemu kodowi, tak jak można zrozumieć działanie kompilatora, polegające na podstawieniu kodu funkcji inline w miejscach, gdzie w kodzie przykładu 1 wywoływaliśmy funkcję.
Przykład 2 (w kompilatorze)
int main(void) { int a = 5; int b = 15; int c = 3; //odpowiednik wywołania funkcji obliczenia() oraz użycia y do wydrukowania { int y; y = a*b; y = y + c; printf( "a*b + c = %d \n", y ); } //zmieniamy wartość argumentów a = 80; //odpowiednik wywołania funkcji obliczenia() oraz użycia y do wydrukowania { int y; y = a*b; y = y + c; printf( "a*b + c = %d \n", y ); } //zmieniamy wartość argumentów a = 97; c = 19; //odpowiednik wywołania funkcji obliczenia() oraz użycia y do wydrukowania { int y; y = a*b; y = y + c; printf( "a*b + c = %d \n", y ); } return 0; //zakończ wykonywanie programu }
Zaskoczeniem dla Ciebie może być użycie w kodzie C przykładu 2 nawiasów klamrowych { ... } oznaczających początek i koniec bloku. Chodzi o to, że wstawiając kod funkcji inline kompilator musiał wyodrębnić zmienną y, którą definiujemy w funkcji. Więcej na ten temat: Zmienne lokalne blokowe
W rzeczywistości kompilator w zależności od parametrów optymalizacji i wielu innych czynników, może przekształcić przykład 1 w nieco inny niż przykład 2, ale z punktu widzenia programisty efekt końcowy działania programu, będzie taki sam.
Funkcja inline vs makrodefinicje
Podobną funkcjonalność realizuje się poprzez makrodefinicje (#define ...). Warto znać różnice pomiędzy tymi metodami:
- funkcje inline traktowane pod względem kontroli zgodności typów (zarówno argumentów jak i zwracanej danej) są traktowane przez kompilator jak zwykłe funkcje, przez co nie popełnisz błędów trudnych do wykrycia.
- funkcje inline są znacznie czytelniejsze w kodzie niż makrodefinicje,
- argumentami funkcji inline mogą być zmienne, a makrodefinicji nie.
Kurs języka C: Spis treści
34
Uwaga!!!
OdpowiedzUsuńW kompilatorze avr-gcc (WinAVR 20100110) 4.3.3 przy wyłączonej optymalizacji podczas kompilacji wyrzucane są błędy dla funkcji zdefiniowanych jako inline, np. coś takiego:
nastawa.o: In function `przestaw':
C:\AVR-proj\C_Progr\NF5/nastawa.c:188: undefined reference to `stan_wyjsciowy'
Po włączeniu jakiejkolwiek optymalizacji kompilacja przebiega bezproblemowo.
Pozdrawiam
Rumcajs
Dobrze, że wspomniałeś o optymalizacji - jej włączenie to podstawa :-)
UsuńPrzy okazji warto więc poczytać:
Błędy kompilacji programu
Funkcje opóźnienia _delay_xx()
Atmel Studio - czyli środowisko programistyczne (IDE)
W przypadku aplikacji tworzonych dla potrzeb np. PKP, gdzie wymagany jest wysoki stopień bezpieczeństwa, jednym z wymogów jest to, aby kod nie był optymalizowany - taka ciekawostka.
UsuńDla zainteresowanych - stosuje się tam również sterowanie poprzez wykorzystanie dwóch sterowników. Wypracowany sygnał sterujący w każdym z nich sumuje się logicznie - dopiero wtedy sygnał sterujący wysyłany jest do układu wykonawczego.
Pozdrawiam
Rumcajs
Jest to ciekawostka. Co ma wspólnego optymalizacja z bezpieczeństwem? Zawsze wydawało mi się, że PKP to instytucja odlotowa, ale żeby aż tak?
UsuńCo ma wspólnego optymalizacja z bezpieczeństwem - to pytanie dla twórców wymogów systemów bezpiecznych - może chodzi o jednoznaczność wykonywanych instrukcji niezależnie od użytego kompilatora? Nie wiem, nie znam się :=)
UsuńPozdrawiam
Rumcajs
Chodzi o odpowiedzialność! Jeśli coś zawiedzie (będzie niebezpieczne) programista nie może powiedzieć "TO NIE JA, TO KOMPILATOR...", innymi słowy MUSI BYĆ KTOŚ KTO ODPOWIADA ZA PROGRAM i musi to być ŻYWY KTOŚ.....
UsuńHmm, jak prawidłowo umieścić funkcję inline w kodzie i nagłówku funkcji? Z tego co pamiętam, gdy do zwykłej funkcji w .c i .h dodałem inline, to były problemy z kompilacją. Gdy kod funkcji przeniosłem do .h, to się skompilowało... jak dla mnie dziwnie ;) Chyba że coś źle robię i w .h lub .c nie powinienem dodać "inline"?
OdpowiedzUsuń