Bei der Computerprogrammierung ist eine Subroutine eine Folge von Programmbefehlen, die eine bestimmte Aufgabe ausführen, die als Einheit verpackt ist. Diese Einheit kann dann in Programmen verwendet werden, wo diese bestimmte Aufgabe ausgeführt werden soll.
Unterprogramme können in Programmen definiert werden oder separat in Bibliotheken, die von vielen Programmen verwendet werden können. In verschiedenen Programmiersprachen kann eine Subroutine als -Prozedur als -Funktion als -Routine als Methode oder als -Unterprogramm bezeichnet werden. Manchmal wird der Oberbegriff Callable Unit verwendet. [1]
Das Unterprogramm legt nahe, dass sich eine Subroutine ähnlich verhält wie ein Computerprogramm, das in einem größeren Programm als ein Schritt verwendet wird oder ein anderes Unterprogramm. Eine Subroutine wird häufig so codiert, dass sie während einer Ausführung des Programms, auch von anderen Subroutinen, mehrmals und von mehreren Stellen aus gestartet werden kann, um dann zur nächsten Anweisung nach [19459009)zurückzukehren( return ) aufrufen, sobald die Aufgabe der Subroutine abgeschlossen ist. Die Idee einer Subroutine wurde ursprünglich von John Mauchly während seiner Arbeit an ENIAC [2] konzipiert und im Januar 1947 in einem Harvard-Symposium mit dem Titel "Vorbereitung von Problemen für EDVAC-Maschinen" aufgenommen. [3]. Maurice Wilkes, David Wheeler und Stanley Gill werden im Allgemeinen die formale Erfindung dieses Konzepts zugeschrieben, die sie als geschlossene -Unterroutine bezeichnet, [4][5] im Gegensatz zu einer offenen -Unterroutine oder einem Makro. [6]
Subroutinen sind ein leistungsfähiges Programmierwerkzeug, [7] und die Syntax vieler Programmiersprachen enthält Unterstützung für das Schreiben und Verwenden dieser Programme. Die vernünftige Verwendung von Unterprogrammen (zum Beispiel durch den Ansatz der strukturierten Programmierung) führt häufig zu einer erheblichen Verringerung der Kosten für die Entwicklung und Pflege eines großen Programms, während dessen Qualität und Zuverlässigkeit erhöht werden. [8] Unterprogramme, die häufig in Bibliotheken zusammengefasst werden, sind ein wichtiger Mechanismus für Freigabe- und Handelssoftware. Die Disziplin der objektorientierten Programmierung basiert auf Objekten und Methoden (Subroutinen, die diesen Objekten oder Objektklassen zugeordnet sind).
Bei der Kompilierungsmethode, die als Threadcode bezeichnet wird, handelt es sich bei dem ausführbaren Programm im Wesentlichen um eine Folge von Unterprogrammaufrufen.
Hauptkonzepte [ edit ]
Der Inhalt einer Subroutine ist ihr Körper. Dies ist der Programmcode, der ausgeführt wird, wenn die Subroutine aufgerufen oder aufgerufen wird. Eine Subroutine kann so geschrieben werden, dass sie erwartet, einen oder mehrere Datenwerte von dem aufrufenden Programm zu erhalten (um seine Parameter oder Formalparameter zu ersetzen). Das aufrufende Programm stellt tatsächliche Werte für diese Parameter bereit, die als Argumente bezeichnet werden. Unterschiedliche Programmiersprachen verwenden möglicherweise unterschiedliche Konventionen zum Übergeben von Argumenten:
Konvention | Beschreibung | Gemeinsame Verwendung |
---|---|---|
Aufruf durch Wert | Das Argument wird ausgewertet und die Kopie des Werts wird an die Subroutine übergeben. | Standardeinstellung in den meisten Algol-ähnlichen Sprachen nach Algol 60, z. B. Pascal, Delphi, Simula, CPL, PL / M, Modula, Oberon, Ada und viele andere. C, C ++, Java (Referenzen auf Objekte und Arrays werden ebenfalls per Wert übergeben) |
Call by reference | Verweis auf Argumente, normalerweise wird seine Adresse übergeben. | Nach Algol 60 in den meisten Algol-ähnlichen Sprachen auswählbar, wie z. B. Algol 68, Pascal, Delphi, Simula, CPL, PL / M, Modula , Oberon, Ada und viele andere. C ++, Fortran, PL / I |
Aufruf nach Ergebnis | Parameterwert wird bei Rückkehr aus der Subroutine | Ada OUT-Parameter in das Argument zurückkopiert |
Aufruf von value-result | Der Parameterwert wird bei der Eingabe in die Subroutine und bei der Rückkehr erneut zurückgegeben. | Algol |
Aufruf mit Namen | Ersetzen Sie die Parameter wie ein Makro durch die nicht bewerteten Argumentausdrücke. | Algol, Scala |
Aufruf durch konstanten Wert | Wie Aufruf durch Wert, mit der Ausnahme, dass der Parameter als Konstante behandelt wird. | PL / I NONASSIGNABLE-Parameter, Ada IN-Parameter |
Die Unterroutine kann einen berechneten Wert (ihren Rückgabewert) an ihren Aufrufer zurückgeben oder verschiedene Ergebniswerte oder Ausgabeparameter bereitstellen. Tatsächlich werden Subroutinen häufig zur Verwendung mathematischer Funktionen verwendet, bei denen der Zweck der Subroutine lediglich die Berechnung eines oder mehrerer Ergebnisse ist, deren Werte vollständig durch die an die Subroutine übergebenen Argumente bestimmt werden. (Beispiele können die Berechnung des Logarithmus einer Zahl oder der Determinante einer Matrix umfassen.)
Ein Unterprogrammaufruf kann auch Nebenwirkungen haben, wie etwa das Ändern von Datenstrukturen in einem Computerspeicher, das Lesen oder Schreiben auf ein Peripheriegerät, das Erstellen einer Datei, das Anhalten des Programms oder der Maschine oder sogar das Verzögern der Programmausführung für ein angegebenes Zeit. Ein Unterprogramm mit Nebenwirkungen kann bei jedem Aufruf unterschiedliche Ergebnisse zurückgeben, auch wenn es mit denselben Argumenten aufgerufen wird. Ein Beispiel ist eine Zufallszahlenfunktion, die in vielen Sprachen verfügbar ist und bei jedem Aufruf eine andere Pseudozufallszahl zurückgibt. Die weit verbreitete Verwendung von Unterprogrammen mit Nebenwirkungen ist ein Merkmal imperativer Programmiersprachen.
Eine Subroutine kann so codiert werden, dass sie sich an einer oder mehreren Stellen rekursiv aufrufen kann, um ihre Aufgabe auszuführen. Diese Methode ermöglicht die direkte Implementierung von Funktionen, die durch mathematische Induktion und rekursive Divide-and-Conquer-Algorithmen definiert sind.
Eine Subroutine, deren Zweck darin besteht, eine boolesche Funktion zu berechnen (dh eine Ja / Nein-Frage zu beantworten) wird manchmal als Prädikat bezeichnet. In logischen Programmiersprachen werden oft vage alle Unterprogramme Prädikate genannt, da sie in erster Linie vage Erfolg oder Misserfolg bestimmen. [ Zitat benötigt ]
Sprachunterstützung [ edit ]
Hochprogrammiersprachen enthalten normalerweise spezifische Konstrukte für:
- begrenzen den Teil des Programms (Hauptteil), aus dem die Unterroutine besteht.
- weisen der Unterroutine einen Bezeichner (Namen) zu.
- geben die Namen und Datentypen ihrer Parameter an, und die Rückgabewerte
- geben einen privaten Wert an Benennungsbereich für seine temporären Variablen
- identifizieren Variablen außerhalb der Subroutine, auf die innerhalb der Subroutine zugegriffen werden kann.
- Aufruf der Subroutine
- liefert Werte für ihre Parameter
- Das Hauptprogramm enthält die Adresse des Unterprogramms
- des Unterprogramms Das Programm enthält die Adresse der nächsten Anweisung des Funktionsaufrufs im Hauptprogramm
- . Geben Sie die Rückgabewerte aus seinem Hauptteil
- an. Rückkehr zum aufrufenden Programm
- . Entsorgen Sie die Werte, die von einem Aufruf
- zurückgegeben werden, und behandeln Sie alle Ausnahmebedingungen Bedingungen, die während des Aufrufs aufgetreten sind
- packen Subroutinen in ein Modul, eine Bibliothek, ein Objekt, eine Klasse usw. ein.
Einige Programmiersprachen wie Pascal, Fortran, Ada und viele Dialekte von BASIC unterscheiden zwischen den beiden n Funktionen oder Funktionsunterprogramme, die einen expliziten Rückgabewert für das aufrufende Programm bereitstellen, und Subroutinen oder Prozeduren, die dies nicht tun. In diesen Sprachen sind Funktionsaufrufe normalerweise in Ausdrücken eingebettet (z. B. kann eine sqrt
-Funktion als y = z + sqrt (x)
bezeichnet werden). Prozeduraufrufe verhalten sich entweder syntaktisch als Anweisungen (z. B. kann eine Prozedur print
als bezeichnet werden, wenn x> 0 dann print (x)
ist oder explizit durch eine Anweisung wie aufgerufen wird. CALL
oder GOSUB
(zB call print (x)
) Andere Sprachen wie C und Lisp unterscheiden nicht zwischen Funktionen und Unterprogrammen.
In streng funktionalen Programmiersprachen wie Haskell können Unterprogramme keine Nebenwirkungen haben, was bedeutet, dass sich verschiedene interne Zustände des Programms nicht ändern. Funktionen geben immer dasselbe Ergebnis zurück, wenn sie wiederholt mit denselben Argumenten aufgerufen werden. Solche Sprachen unterstützen normalerweise nur Funktionen, da Unterprogramme, die keinen Wert zurückgeben, keine Verwendung haben, es sei denn, sie können einen Nebeneffekt verursachen.
In Programmiersprachen wie C, C ++ und C # können Unterprogramme auch einfach als Funktionen bezeichnet werden, die nicht mit mathematischen Funktionen oder funktionaler Programmierung zu verwechseln sind. Dies sind unterschiedliche Konzepte.
Der Compiler einer Sprache übersetzt normalerweise Prozeduraufrufe und kehrt nach einer genau definierten Aufrufkonvention in Maschinenbefehle zurück, sodass Subroutinen getrennt von den Programmen, die sie aufrufen, kompiliert werden können. Die Anweisungssequenzen, die den Anweisungen call und return entsprechen, werden Prolog und Epilog der Prozedur genannt.
Vorteile [ edit ]
Zu den Vorteilen, wenn ein Programm in Subroutinen aufgeteilt wird, gehören:
- Eine komplexe Programmieraufgabe in einfachere Schritte zerlegen: Dies ist eines der zwei Hauptwerkzeuge der strukturierten Programmierung zusammen mit Datenstrukturen.
- Doppelten Code innerhalb eines Programms reduzieren
- Wiederverwendung von Code über mehrere Programme hinweg ermöglichen
- Aufteilen einer großen Programmieraufgabe auf verschiedene Programmierer oder verschiedene Phasen eines Projekts
- Ausblenden von Implementierungsdetails vor Benutzern der Subroutine
- Verbesserung der Lesbarkeit von Code durch Ersetzen eines Codeblocks durch einen Funktionsaufruf, für den ein beschreibender Funktionsname verwendet wird Beschreiben Sie den Codeblock. Dies macht den Aufrufcode kurz und lesbar, auch wenn die Funktion nicht wiederverwendet werden soll.
- Verbesserung der Rückverfolgbarkeit (dh die meisten Sprachen bieten Möglichkeiten zum Abrufen der Aufrufablaufverfolgung, die die Namen der beteiligten Subroutinen und möglicherweise noch mehr Informationen wie z Dateinamen und Zeilennummern); Wenn der Code nicht in Subroutinen zerlegt wird, wird das Debugging stark beeinträchtigt.
Nachteile [ edit ]
Das Aufrufen einer Subroutine (gegenüber der Verwendung von Inline-Code) verursacht in der Aufforderung einen gewissen Rechenaufwand Mechanismus.
Ein Unterprogramm erfordert normalerweise einen Standard-Verwaltungscode - sowohl beim Eintritt in die Funktion als auch beim Verlassen der Funktion (Funktionsprolog und Epilog - normalerweise Speichern von Universalregistern und Rücksprungadresse als Minimum).
Geschichte [ edit ]
Die Idee einer Subroutine wurde ausgearbeitet, nachdem Rechenmaschinen schon länger existierten. Die arithmetischen und bedingten Sprungbefehle wurden im Voraus geplant und haben sich relativ wenig geändert. Die speziellen Anweisungen für Prozeduraufrufe haben sich jedoch im Laufe der Jahre stark verändert. Die ersten Computer und Mikroprozessoren, wie der Manchester Baby und der RCA 1802, hatten keine einzige Unterprogrammaufrufanweisung. Unterprogramme könnten implementiert werden, aber es war für Programmierer erforderlich, die Aufrufsequenz - eine Reihe von Anweisungen - an jeder Aufrufstelle zu verwenden.
Im Januar 1947 legte John Mauchly allgemeine Notizen bei "Ein Symposium für digitale Rechenmaschinen für Großrechner" vor. unter gemeinsamer Patenschaft der Harvard University und des Bureau of Ordnance, United States Navy. Hier bespricht er den seriellen und parallelen Betrieb
... der Aufbau der Maschine muss kein bisschen kompliziert sein. Da alle für dieses Verfahren wesentlichen logischen Merkmale verfügbar sind, ist es möglich, eine Codierungsanweisung zum Platzieren der Unterprogramme im Speicher an Stellen zu entwickeln, die der Maschine bekannt sind, und auf eine Weise, daß sie leicht in Gebrauch genommen werden können. 19659010] Mit anderen Worten, man kann die Subroutine A als Division und Subroutine B als komplexe Multiplikation und Subroutine C als Bewertung eines Standardfehlers einer Zahlenfolge usw. durch die Liste der Subroutinen bestimmen, die für ein bestimmtes Problem benötigt werden. ... Alle diese Unterprogramme werden dann in der Maschine gespeichert und müssen nur noch kurz durch die Nummer angesprochen werden, wie in der Codierung angegeben. [3]
Kay McNulty hatte eng mit John Mauchly zusammengearbeitet das ENIAC-Team und entwickelte eine Idee für Unterprogramme für den ENIAC-Computer, den sie in den späten 1940er Jahren programmierte. [9] Sie und die anderen ENIAC-Programmierer verwendeten die Unterprogramme, um bei der Berechnung von Flugkörperflugbahnen zu helfen.
] Goldstine und von Neumann haben am 16. August 1948 eine Arbeit geschrieben, in der die Nützlichkeit von Unterprogrammen erörtert wurde. [10]
Einige sehr frühe Computer und Mikroprozessoren, wie der IBM 1620, der Intel 8008 und der PIC Mikrocontroller verfügen über einen Unterprogrammaufruf mit einer einzigen Anweisung, der zum Speichern von Rücksprungadressen einen dedizierten Hardwarestapel verwendet. Diese Hardware unterstützt nur wenige Ebenen der Unterprogrammverschachtelung, kann aber rekursive Unterprogramme unterstützen. Maschinen vor der Mitte der 1960er Jahre - wie der UNIVAC I, der PDP-1 und der IBM 1130 - verwenden normalerweise eine Aufrufkonvention, die den Befehlszähler im ersten Speicherbereich der aufgerufenen Subroutine speichert. Dies ermöglicht beliebig tiefe Ebenen der Unterprogrammverschachtelung, unterstützt jedoch keine rekursiven Unterprogramme. Der PDP-11 (1970) ist einer der ersten Computer mit einer Subroutine-Aufrufanweisung zum Stapeln; Diese Funktion unterstützt sowohl die beliebig tiefe Verschachtelung von Subroutinen als auch rekursive Subroutinen. [11]
Sprachunterstützung [ edit ]
In den frühen frühen Assemblern war die Unterstützung für Unterprogramme begrenzt. Subroutinen wurden nicht explizit voneinander oder vom Hauptprogramm getrennt, und der Quellcode einer Subroutine konnte tatsächlich mit dem anderer Subprogramme durchsetzt werden. Einige Assembler bieten vordefinierte Makros an, um die Aufruf- und Rückkehrsequenzen zu generieren. In den 1960er-Jahren hatten Assembler in der Regel eine weitreichendere Unterstützung sowohl für Inline- als auch für separat zusammengestellte Unterprogramme, die miteinander verbunden werden konnten.
Unterprogrammbibliotheken [ edit ]
Selbst bei diesem umständlichen Ansatz erwiesen sich Unterprogramme als sehr nützlich. Zum einen erlaubten sie die Verwendung desselben Codes in vielen verschiedenen Programmen. Darüber hinaus war der Arbeitsspeicher auf frühen Computern eine sehr knappe Ressource, und Subroutinen ermöglichten erhebliche Einsparungen bei der Größe von Programmen.
Viele frühe Computer luden die Programmanweisungen von einem Lochstreifen in den Speicher. Jede Subroutine könnte dann durch ein separates Bandstück bereitgestellt werden, das vor oder nach dem Hauptprogramm (oder "mainline" [12]) geladen oder gespleißt wird; und dasselbe Unterprogramm-Band kann dann von vielen verschiedenen Programmen verwendet werden. Ein ähnlicher Ansatz wurde bei Computern angewendet, die für ihre Haupteingabe Lochkarten verwendeten. Der Name der Unterprogrammbibliothek bedeutete ursprünglich eine Bibliothek im wahrsten Sinne des Wortes, in der indizierte Sammlungen von Kassetten oder Kartendecks für die kollektive Verwendung aufbewahrt wurden.
Rückkehr durch indirekten Sprung [ edit ]
Um den Bedarf an selbstmodifizierendem Code zu beseitigen, gaben Computerdesigner schließlich einen indirekten Befehl dessen Operand, Anstelle der Rücksprungadresse selbst war der Ort einer Variablen oder eines Prozessorregisters, die die Rücksprungadresse enthielt.
Auf diesen Computern speichert das aufrufende Programm die Rücksprungadresse in einer Variablen, anstatt den Rücksprung des Unterprogramms zu modifizieren, so dass es, wenn das Unterprogramm abgeschlossen ist, einen indirekten Sprung ausführt, der die Ausführung an die durch den vordefinierten Ort angegebene Position lenkt Variable.
Sprung zur Subroutine [ edit ]
Ein weiterer Fortschritt war der Sprung zur Subroutine der das Speichern der Rücksprungadresse mit dem aufrufenden Sprung kombinierte Overhead signifikant minimieren.
In IBM System / 360 würden beispielsweise die Verzweigungsbefehle BAL oder BALR, die für Prozeduraufrufe vorgesehen sind, die Rücksprungadresse in einem in der Anweisung angegebenen Prozessorregister speichern. Zur Rückkehr musste das Unterprogramm nur eine indirekte Verzweigungsanweisung (BR) durch dieses Register ausführen. Wenn das Unterprogramm dieses Register zu einem anderen Zweck benötigt (z. B. zum Aufrufen eines anderen Unterprogramms), würde es den Inhalt des Registers in einem privaten Speicherplatz oder einem Registerstapel speichern.
In Systemen wie dem HP 2100 führt der JSB-Befehl eine ähnliche Aufgabe aus, mit der Ausnahme, dass die Rücksprungadresse in dem Speicherort gespeichert wurde, der das Ziel der Verzweigung war. Die Ausführung der Prozedur würde tatsächlich am nächsten Speicherplatz beginnen. In der Assemblersprache HP 2100 würde man zum Beispiel schreiben
... JSB MYSUB (Ruft die Subroutine MYSUB auf.) BB ... (Kehrt nach MYSUB hierher zurück.)
um eine Subroutine namens MYSUB aus dem Hauptprogramm aufzurufen. Das Unterprogramm würde als codiert
MYSUB NOP (Speicher für die Rücksendeadresse von MYSUB) AA ... (Start von MYSUBs Körper.) ... JMP MYSUB, I (kehrt zum aufrufenden Programm zurück.)
Der JSB-Befehl platzierte die Adresse des NEXT-Befehls (nämlich BB) an der als Operand angegebenen Stelle (nämlich MYSUB) und verzweigte danach an die NEXT-Stelle (nämlich AA = MYSUB + 1). Die Unterroutine könnte dann zum Hauptprogramm zurückkehren, indem sie den indirekten Sprung JMP MYSUB ausführt, der zu der an der Position MYSUB gespeicherten Stelle verzweigt.
Compiler für Fortran und andere Sprachen können diese Anweisungen leicht verwenden, wenn sie verfügbar sind. Dieser Ansatz unterstützte mehrere Anrufebenen; Da der Rücksendeadresse, den Parametern und den Rückgabewerten einer Subroutine feste Speicherplätze zugewiesen wurden, waren rekursive Aufrufe nicht möglich.
Eine ähnliche Methode wurde übrigens in den frühen achtziger Jahren von Lotus 1-2-3 verwendet, um die Abhängigkeiten der Neuberechnung in einer Kalkulationstabelle zu ermitteln. In jeder Zelle wurde ein Ort reserviert, um die Rücksprungadresse zu speichern. Da Zirkelverweise für eine natürliche Neuberechnungsreihenfolge nicht zulässig sind, ermöglicht dies einen Baumspaziergang, ohne Platz für einen Stapelspeicher zu reservieren, der auf kleinen Computern wie dem IBM PC sehr begrenzt war.
Aufrufstapel [ edit ]
Die meisten modernen Implementierungen eines Unterprogrammaufrufs verwenden einen Aufrufstapel, einen Sonderfall der Stapeldatenstruktur, um Unterprogrammaufrufe und -rückgaben zu implementieren. Jeder Prozeduraufruf erstellt einen neuen Eintrag namens -Stackrahmen am oberen Rand des Stapels. Wenn die Prozedur zurückkehrt, wird der Stack-Frame aus dem Stack gelöscht und der Speicherplatz kann für andere Prozeduraufrufe verwendet werden. Jeder Stack-Frame enthält die privaten Daten des entsprechenden Aufrufs, die normalerweise die Parameter und internen Variablen der Prozedur sowie die Rücksprungadresse enthalten.
Die Aufrufsequenz kann durch eine Folge gewöhnlicher Befehle implementiert werden (ein Ansatz, der immer noch in Architekturen mit reduziertem Befehlssatz (RISC) und sehr langen Befehlsworten (VLIW) verwendet wird), aber viele traditionelle Maschinen, die seit den späten 1960er Jahren entwickelt wurden, sind bereits enthalten spezielle Anweisungen für diesen Zweck.
Der Aufrufstapel wird normalerweise als zusammenhängender Speicherbereich implementiert. Es ist eine willkürliche Entwurfsauswahl, ob der unterste Teil des Stapels die niedrigste oder höchste Adresse in diesem Bereich ist, sodass der Stapel im Speicher nach vorne oder nach hinten wachsen kann. Letztere wählten jedoch viele Architekturen. [] Zitat benötigt
Bei einigen Konstruktionen, insbesondere bei einigen Forth-Implementierungen, wurden zwei separate Stacks verwendet, von denen einer hauptsächlich für Steuerinformationen dient (wie Rückgabeadressen und Schleifenzähler) und der andere für Daten. Ersteres war oder arbeitete wie ein Aufrufstack und war für den Programmierer nur indirekt durch andere Sprachkonstrukte zugänglich, während letzteres direkter zugänglich war.
Als zum ersten Mal stapelbasierte Prozeduraufrufe eingeführt wurden, bestand eine wichtige Motivation darin, kostbares Gedächtnis zu sparen. [ Zitat benötigt ] Bei diesem Schema muss der Compiler nicht gesondert reservieren Speicherplatz für die privaten Daten (Parameter, Rücksprungadresse und lokale Variablen) jeder Prozedur. Der Stack enthält zu jedem Zeitpunkt nur die privaten Daten der Aufrufe, die derzeit aktiv sind (19459010) (dh die aufgerufen wurden, aber noch nicht zurückgekehrt sind). Aufgrund der Art und Weise, wie Programme normalerweise aus Bibliotheken zusammengestellt wurden, war und ist es nicht ungewöhnlich, Programme zu finden, die Tausende von Unterprogrammen enthalten, von denen zu jeder Zeit nur eine Handvoll aktiv ist. Zitat benötigt ] Für solche Programme kann der Aufrufstapelmechanismus beträchtliche Speichermengen einsparen. In der Tat kann der Aufrufstapelmechanismus als die früheste und einfachste Methode für die automatische Speicherverwaltung angesehen werden.
Ein weiterer Vorteil der Aufrufstapelmethode besteht jedoch darin, dass sie rekursive Unterprogrammaufrufe zulässt, da jeder verschachtelte Aufruf derselben Prozedur eine separate Instanz seiner privaten Daten erhält.
Verzögertes Stapeln [ edit ]
Ein Nachteil des Aufrufstapelmechanismus sind die erhöhten Kosten eines Prozeduraufrufs und seiner zugehörigen Rückgabe. Erläuterung erforderlich Zu den zusätzlichen Kosten gehören das Inkrementieren und Dekrementieren des Stack-Zeigers (und in einigen Architekturen das Überprüfen des Stack-Überlaufs) sowie der Zugriff auf die lokalen Variablen und Parameter über Frame-relative Adressen anstelle von absolute Adressen. Die Kosten können in einer erhöhten Ausführungszeit oder einer erhöhten Prozessorkomplexität oder in beiden realisiert werden.
Dieser Overhead ist am offensichtlichsten und anstößig in Blattprozeduren oder die zurückkehren, ohne selbst Prozeduraufrufe zu machen. [13][14][15] Um diesen Overhead zu reduzieren, versuchen viele moderne Compiler um die Verwendung eines Aufrufstapels zu verzögern, bis er wirklich benötigt wird Zitat erforderlich ] Beispielsweise kann der Aufruf eines Verfahrens P die Rücksprungadresse speichern und Parameter der aufgerufenen Prozedur in bestimmten Prozessorregistern und Übertragen der Steuerung durch einen einfachen Sprung auf den Körper der Prozedur. Wenn Prozedur P zurückkehrt, ohne einen anderen Aufruf durchzuführen, wird der Aufrufstapel überhaupt nicht verwendet. Wenn P ein anderes Verfahren aufrufen muss Q verwendet es den Aufrufstapel, um den Inhalt aller Register (wie z. B. der Absenderadresse) zu speichern, die nach benötigt werden. Q kehrt zurück.
C- und C ++ - Beispiele [ edit ]
In den Programmiersprachen C und C ++ werden Unterprogramme als -Funktionen bezeichnet (weiter klassifiziert als -Elementfunktionen. wenn mit einer Klasse verbunden, oder freie Funktionen [16] wenn nicht). Diese Sprachen verwenden das spezielle Schlüsselwort void
um anzuzeigen, dass eine Funktion keine Parameter übernimmt (insbesondere in C) oder keinen Wert zurückgibt. Beachten Sie, dass C / C ++ - Funktionen Nebeneffekte haben können, einschließlich der Änderung von Variablen, deren Adressen als Parameter übergeben werden (d. H. durch Verweis übergeben ). Beispiele:
void function1 ( void ) { / * irgendein Code * / ]
Die Funktion gibt keinen Wert zurück und hat einen Wert von bis als eigenständige Funktion bezeichnet werden, z. B. function1 ();
int Funktion2 ( void ) { return 5 ; ; Nummer 5), und der Aufruf kann Teil eines Ausdrucks sein, z. B. x + function2 ()
char Funktion3 ( int Nummer ) { Char Auswahl
'S' 'M' 'T' W '' T ' 'F' 'S' }; Rückkehr Auswahl Nummer ] ]; Die Funktion wandelt eine Zahl zwischen 0 und 6 in den Anfangsbuchstaben des entsprechenden Wochentags um, nämlich 0 in 'S', 1 in 'M', ..., 6 in 'S'. Das Ergebnis des Aufrufs kann einer Variablen zugewiesen werden, z. B. num_day = function3 (number);
.
void Funktion4 ( int * pointer_to_var )
(19659189); ] ++ ; }
No comments:
Post a Comment