Inline (C++)

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

In C++ werden die Anweisungen von Unterprogrammen[1], die mit dem Zusatz inline definiert sind, an jeder Aufrufstelle erneut eingesetzt.

Ein Unterprogramm inline zu definieren entspricht dem klassischen Austausch von "Geschwindigkeit der Ausführung" gegen "Platzbedarf des Maschinen-Codes". Da man bei der Programmierung von Mikrocontrollern mitunter in der einen oder anderen Richtung optimieren muss, kann dieses Feature interessant sein.

Mit inline wird:

  • ein Unterprogrammsprung-Befehl vermieden (→ schneller),
  • ebenso wie der Rücksprung-Befehl am Ende, aber dafür
  • bei Aufrufen an vielen verschiedenen Stellen mehr Code erzeugt.
  • Für sehr kleine Unterprogramme[2] wird evtl. sogar weniger Code erzeugt (→ doppelter Gewinn) und
  • für leere Unterprogramme wird überhaupt kein Code erzeugt.[3]
  • Durch den fehlenden Rücksprung-Befehl (Punkt 2) wird auch kein Speicher für die Rücksprungadresse im Stack benötigt.

Ohne inline wird:

  • für das Unterprogramm nur einmal Code erzeugt[4], der bei jedem Aufruf
  • über einen entsprechenden Maschinenbefehl erreicht wird.
  • Eventuell sind unmittelbar nach Eintritt in das Unterprogramm weitere Maschinebefehle nötig[5],
  • ebenso wie am Ende, wenn das Unterprogramm an die Aufrufstelle zurückkehrt.

Als inline definierte Funktionen sind keine Makros. Sie können funktionsähnliche Makros ersetzen, verhalten sich aber "semantisch korrekt" und sind deshalb vorzuziehen. Insbesondere gilt, dass inline-Unterprogramme

  • lokale Hilfsvariablen haben können und
  • Seiteneffekte auch bei mehrfacher Verwendung eines Parameters nur einmalig stattfinden.

Damit sind die folgenden Unterprograme (Vertauschen zweier Ganzzahlen, Bestimmung des Maximums zweier Ganzzahlen) unproblematisch.

inline void swap (int *xp, int *yp)
{
    int tmp = *xp; *xp = *yp; *yp = tmp;
}

inline int max (int a, int b)
{
    return (a > b) ? a : b;
}

Bei "Tricksereien" mit Makros wäre im Fall von swap dagegen die Verwendung

int i, j;
...
if (i < j)
    swap (&i, &j);
else  ...

ein Syntaxfehler. (Ein Makro für swap müsste wegen der Hilfsvariablen zu einem ein Block {...} expandieren, womit das Semikolon am Ende der Zeile nicht stehen darf.)

Ebenso ist mit einer inline-Funktion

j = max (i++, 10);

kein Problem. (Ein Makro würde wegen des rein textuellen Ersatzes seiner Parameter die Inkrementierung u.U. doppelt auslösen.)

Daß man eine Funktion als inline deklariert, heißt aber nicht zwangsläufig, daß der Compiler den Code bei einem Aufruf wirklich inline einfügen muss. Vielmehr ist das Schlüsselwort inline nur ein Hinweis, daß der Programmierer es für sinnvoll hält. Es steht dem Compiler frei, die Funktion trotzdem wie sonst auch einzeln zu übersetzen und bei jeder Verwendung einen normalen Aufruf einzusetzen. Üblicherweise wird das anhand der Codegröße vom Compiler entschieden, wobei die Grenze meist mit Compileroptionen verschoben werden kann. Die semantische Korrektheit im Vergleich zu funktionsähnlichen Makros ist trotzdem gegeben.

Normalerweise wird die Deklaration einer Funktion in einer Headerdatei (Endung .h) vorgenommen, die Definition dagegen in einer .cpp, die möglicherweise getrennt vom restlichen Programm kompiliert wird. Da der Compiler den als inline deklarierten Code überall einsetzen soll, wo die Funktion aufgerufen wird, muß er auch überall den Code kennen. Deshalb schreibt man bei inline-Funktionen die gesamte Definition gleich in die Headerdatei. Das dabei sonst auftretende Linkerproblem (Funktion mehrfach definiert) entfällt. Dies gilt auch, wenn der Compiler das Schlüsselwort inline ignoriert (in diesem Fall bekommt der Linker eventuell die kompilierte Funktion mehrfach präsentiert und nimmt dann stillschweigend nur eine davon, in der Hoffung daß sie alle gleichwertig sind). Dies kann man "mißbrauchen", um eine kleine Bibliothek von Hilfsfunktionen einfach als Headerdatei zu schreiben, ohne getrennt zu kompilierenden Quelltext verwenden zu müssen.

Bei der Definition von Klassen sind alle Methoden, die innerhalb der Klasse definiert werden (nicht nur deklariert), automatisch inline. Man kann sich das Schlüsselwort also dort sparen.

Fußnoten

  1. Der Begriff Unterprogramm schließt hier Funktionen (C-Terminologie) und Methoden (C++-Terminologie) ein
  2. Sehr kleine Unterprogramme kommen insbesondere bei der Objektorientierter Programmierung vor, bei der die Erfahrung und guter Stil zu der Richtlinie geführt haben, Attribute von Klassen privat zu machen und für Zugriff public Methoden bereit zu stellen.
  3. Normalerweise gibt es keinen Grund, leere Unterprogramme zu schreiben – ausgenommen universell gehaltene Programmteile, die applikationsspezifisch verfeinert und mit zusäzlichen Features ausgestattet werden können.
  4. Erkennt ein Compiler, dass eine Funktion/Methode statisch nur einmal verwendet wird, kann er auch ohne das Schlüsselwort inline die Funktion inlinen.
  5. Beispielsweise kann es sein, dass unmittelbar nach Eintritt in das Unterprogramm noch Register gesichert und am Ende restauriert werden müssen, wenn diese innerhalb des Unterprogramms für Zwischenberechnungen benutzt werden. Die Details regelt die Calling-Sequence, die jedoch abhängig vom verwendeten Compiler ist.