Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

czwartek, 10 lutego 2011

Kurs C: Funkcje inline


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
Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

7 komentarzy:

  1. Uwaga!!!
    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

    OdpowiedzUsuń
    Odpowiedzi
    1. Dobrze, że wspomniałeś o optymalizacji - jej włączenie to podstawa :-)
      Przy okazji warto więc poczytać:
      Błędy kompilacji programu
      Funkcje opóźnienia _delay_xx()
      Atmel Studio - czyli środowisko programistyczne (IDE)

      Usuń
    2. 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.
      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

      Usuń
    3. 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ń
    4. 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ę :=)
      Pozdrawiam
      Rumcajs

      Usuń
    5. 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ń
  2. 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ń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.