Monday, November 2, 2015

Textual description of firstImageUrl

Unterprogramm - Wikipedia


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); ] ++ ; }

Diese Funktion gibt keinen Wert zurück, sondern ändert die Variable, deren Adresse als Parameter übergeben wird. es würde mit " function4 (& variable_to_increment); " aufgerufen werden.

Kleines grundlegendes Beispiel [ edit ]

 Beispiel  ()                                 'Ruft die Subroutine    Sub   Beispiel                              ' Beginnt die Subroutine [19659217] TextWindow .  WriteLine  ( "Dies ist ein Beispiel für eine Subroutine in Microsoft Small Basic." )    'Was die Subroutine macht   EndSub   ] 'Beendet die Subroutine  

Im obigen Beispiel ruft Example () die Subroutine [17] auf. Um die eigentliche Subroutine zu definieren, muss das Schlüsselwort Sub verwendet werden Name des Unterprogramms nach Sub . Nachdem der Inhalt gefolgt ist, EndSub muss eingegeben werden.

Visual Basic 6-Beispiele [ edit ]

In der Sprache von Visual Basic 6 werden Unterprogramme als Funktionen oder subs (oder bezeichnet. Methoden in Verbindung mit einer Klasse). In Visual Basic 6 werden verschiedene Begriffe Types verwendet, um zu definieren, was als Parameter übergeben wird. Eine nicht spezifizierte Variable wird standardmäßig als Variantentyp registriert und kann als ByRef (Standard) oder ByVal übergeben werden. Wenn eine Funktion oder ein Unterprogramm deklariert wird, erhält sie außerdem eine öffentliche, private oder befreundete Bezeichnung, die festlegt, ob auf sie außerhalb des Moduls oder Projekts zugegriffen werden kann, in dem sie deklariert wurde.

  • Durch den Wert [ByVal] - eine Möglichkeit, den Wert eines Arguments an eine Prozedur zu übergeben, indem eine Kopie des Werts übergeben wird, anstatt die Adresse zu übergeben. Folglich kann der tatsächliche Wert der Variablen nicht durch die Prozedur geändert werden, an die sie übergeben wird.
  • Mit Verweis [ByRef] - eine Möglichkeit, den Wert eines Arguments durch Übergeben einer Adresse an eine Prozedur zu übergeben der Variablen, anstatt eine Kopie ihres Wertes zu übergeben. Dadurch kann die Prozedur auf die tatsächliche Variable zugreifen. Daher kann der tatsächliche Wert der Variablen durch die Prozedur geändert werden, an die sie übergeben wird. Wenn nicht anders angegeben, werden Argumente als Referenz übergeben.
  • Public (optional) - Gibt an, dass die Funktionsprozedur für alle anderen Prozeduren in allen Modulen zugänglich ist. Wenn die Prozedur in einem Modul verwendet wird, das die Option Private enthält, ist das Verfahren außerhalb des Projekts nicht verfügbar.
  • Private (optional) - Gibt an, dass die Funktionsprozedur nur für andere Prozeduren in dem Modul verfügbar ist, in dem sie verfügbar ist wird erklärt.
  • Friend (optional) - wird nur in einem Klassenmodul verwendet. Zeigt an, dass die Function-Prozedur im gesamten Projekt sichtbar ist, für einen Controller einer Instanz eines Objekts jedoch nicht sichtbar ist. Private Function1 () 'Some Code Here [19659243] Ende Funktion

Die Funktion gibt keinen Wert zurück und muss als eigenständige Funktion aufgerufen werden, z. B. Funktion1

 Private   Funktion   Funktion2  ( )   as   Integer       Funktion2   =   5   Ende   Funktion  

Diese Funktion liefert ein Ergebnis (die Nummer 5), und der Aufruf kann Teil von sein ein Ausdruck, zum Beispiel x + Funktion2 ()

 Private   Funktion   Funktion3  (19659262) ByVal   intValue   
  • Integer 19659250] as String Dim strArray ( 6 )
  • als String strArray . ( "M") T W T F S S S ) Funktion3 = strArray ( intValue ) Ende Ende Ende Ende Ende Ende ] Die Funktion wandelt eine Zahl zwischen 0 und 6 in den Anfangsbuchstaben des entsprechenden Wochentags um, nämlich 0 in 'M', 1 in 'T', ..., 6 in 'S'. Das Ergebnis des Aufrufs kann einer Variablen zugewiesen werden, z. B. num_day = Function3 (number) .

     Private   Funktion   Funktion4  ( ByRef   intValue   als   Integer   Integer   und  

    ] + 1 End Funktion

  • Diese Funktion gibt keinen Wert zurück, sondern ändert die Variable, deren Adresse als Parameter übergeben wird. es würde mit " Function4 (variable_to_increment) " aufgerufen werden.

    PL / I-Beispiel [ edit ]

    In PL / I kann eine aufgerufene Prozedur einen Deskriptor übergeben, der Informationen über das Argument liefert, z und Array-Grenzen. Dadurch kann die Prozedur allgemeiner sein, und der Programmierer muss diese Informationen nicht weitergeben. Standardmäßig übergibt PL / I Argumente als Referenz. Eine (triviale) Subroutine zum Ändern des Vorzeichens jedes Elements eines zweidimensionalen Arrays könnte folgendermaßen aussehen:

      change_sign: Prozedur (Array);     Array (*, *) Float deklarieren;     array = -array;     end change_sign; 

    Dies könnte mit verschiedenen Arrays wie folgt aufgerufen werden:

      / * erstes Array grenzt von -5 bis +10 und 3 bis 9 * /   deklarieren Sie Array1 (-5: 10, 3: 9) Float;   / * Zweites Array begrenzt 1 bis 16 und 1 bis 16 * /   deklariere array2 (16,16) float;   call change_sign (array1);   call change_sign (array2); 

    Lokale Variablen, Rekursion und Wiedereintritt [ edit ]

    Für ein Unterprogramm kann es zweckmäßig sein, einen bestimmten - Scratch - Speicherplatz zu verwenden. das heißt, Speicher, der während der Ausführung dieses Unterprogramms verwendet wird, um Zwischenergebnisse zu speichern. In diesem Arbeitsbereich gespeicherte Variablen werden als lokale Variablen bezeichnet, und der Arbeitsbereich wird als Aktivierungsprotokoll bezeichnet. Ein Aktivierungsdatensatz hat normalerweise eine Rücksprungadresse, die angibt, wohin die Kontrolle zurückgegeben werden soll, wenn das Unterprogramm beendet ist.

    Ein Unterprogramm kann eine beliebige Anzahl und Art von Aufrufstellen haben. If recursion is supported, a subprogram may even call itself, causing its execution to suspend while another nested execution of the same subprogram occurs. Recursion is a useful means to simplify some complex algorithms and break down complex problems. Recursive languages generally provide a new copy of local variables on each call. If the programmer desires the value of local variables to stay the same between calls, they can be declared static in some languages, or global values or common areas can be used. Here is an example of recursive subroutine in C/C++ to find Fibonacci numbers:

    int fib(int n) { 	if(n<=1) return n; 	return fib(n-1)+fib(n-2); } 

    Early languages like Fortran did not initially support recursion because variables were statically allocated, as well as the location for the return address. Most computers before the late 1960s such as the PDP-8 did not have support for hardware stack registers.[citation needed]

    Modern languages after ALGOL such as PL/1 and C almost invariably use a stack, usually supported by most modern computer instruction sets to provide a fresh activation record for every execution of a subprogram. That way, the nested execution is free to modify its local variables without concern for the effect on other suspended executions in progress. As nested calls accumulate, a call stack structure is formed, consisting of one activation record for each suspended subprogram. In fact, this stack structure is virtually ubiquitous, and so activation records are commonly termed stack frames.

    Some languages such as Pascal and Ada also support nested subroutines, which are subroutines callable only within the scope of an outer (parent) subroutine. Inner subroutines have access to the local variables of the outer subroutine that called them. This is accomplished by storing extra context information within the activation record, also termed a display.

    If a subprogram can be executed properly even when another execution of the same subprogram is already in progress, that subprogram is said to be reentrant. A recursive subprogram must be reentrant. Reentrant subprograms are also useful in multi-threaded situations, since multiple threads can call the same subprogram without fear of interfering with each other. In the IBM CICS transaction processing system, quasi-reentrant was a slightly less restrictive, but similar, requirement for application programs that were shared by many threads.

    In a multi-threaded environment, there is generally more than one stack. An environment that fully supports coroutines or lazy evaluation may use data structures other than stacks to store their activation records.

    Overloading[edit]

    In strongly typed languages, it is sometimes desirable to have a number of functions with the same name, but operating on different types of data, or with different parameter profiles. For example, a square root function might be defined to operate on reals, complex values or matrices. The algorithm to be used in each case is different, and the return result may be different. By writing three separate functions with the same name, the programmer has the convenience of not having to remember different names for each type of data. Further if a subtype can be defined for the reals, to separate positive and negative reals, two functions can be written for the reals, one to return a real when the parameter is positive, and another to return a complex value when the parameter is negative.

    In object-oriented programming, when a series of functions with the same name can accept different parameter profiles or parameters of different types, each of the functions is said to be overloaded.

    Here is an example of subroutine overloading in C++:

    #include   double area(double h, double w) {    return h * w; }  double area(double r) {    return r * r * 3.14; }  int main() {    double rectangle_area = area(3, 4);    double circle_area = area(5);     std::cout << "Area of a rectangle is " << rectangle_area << std::endl;    std::cout << "Area of a circle is " << circle_area << std::endl;     return 0; } 

    In this code there are two functions of same name but they have different parameters.

    As another example, a subroutine might construct an object that will accept directions, and trace its path to these points on screen. There are a plethora of parameters that could be passed in to the constructor (colour of the trace, starting x and y co-ordinates, trace speed). If the programmer wanted the constructor to be able to accept only the color parameter, then he could call another constructor that accepts only color, which in turn calls the constructor with all the parameters passing in a set of default values for all the other parameters (X and Y would generally be centered on screen or placed at the origin, and the speed would be set to another value of the coder's choosing).

    Closures[edit]

    A closure is a subprogram together with the values of some of its variables captured from the environment in which it was created. Closures were a notable feature of the Lisp programming language, introduced by John McCarthy. Depending on the implementation, closures can serve as a mechanism for side-effects.

    Conventions[edit]

    A wide number of conventions for the coding of subroutines have been developed. Pertaining to their naming, many developers have adopted the approach that the name of a subroutine should be a verb when it does a certain task, an adjective when it makes some inquiry, and a noun when it is used to substitute variables.

    Some programmers suggest that a subroutine should perform only one task, and if a subroutine does perform more than one task, it should be split up into more subroutines. They argue that subroutines are key components in code maintenance, and their roles in the program must remain distinct.

    Proponents of modular programming (modularizing code) advocate that each subroutine should have minimal dependency on other pieces of code. For example, the use of global variables is generally deemed unwise by advocates for this perspective, because it adds tight coupling between the subroutine and these global variables. If such coupling is not necessary, their advice is to refactor subroutines to accept passed parameters instead. However, increasing the number of parameters passed to subroutines can affect code readability.

    Return codes[edit]

    Besides its main or normal effect, a subroutine may need to inform the calling program about exceptional conditions that may have occurred during its execution. In some languages and programming standards, this is often done through a return codean integer value placed by the subroutine in some standard location, which encodes the normal and exceptional conditions.

    In the IBM System/360, where a return code was expected from the subroutine, the return value was often designed to be a multiple of 4—so that it could be used as a direct branch table index into a branch table often located immediately after the call instruction to avoid extra conditional tests, further improving efficiency. In the System/360 assembly language, one would write, for example:

               BAL  14,SUBRTN01    go to subroutine, storing return address in R14            B    TABLE(15)      use returned value in reg 15 to index the branch table,  *                              branching to the appropriate branch instr. TABLE      B    OK             return code =00   GOOD                  }            B    BAD            return code =04   Invalid input         } Branch table            B    ERROR          return code =08   Unexpected condition  } 

    Optimization of subroutine calls[edit]

    There is a significant runtime overhead in a calling a subroutine, including passing the arguments, branching to the subprogram, and branching back to the caller. The overhead often includes saving and restoring certain processor registers, allocating and reclaiming call frame storage, etc.. In some languages, each subroutine call also implies automatic testing of the subroutine's return code, or the handling of exceptions that it may raise. In object-oriented languages, a significant source of overhead is the intensively used dynamic dispatch for method calls.

    There are some seemingly obvious optimizations of procedure calls that cannot be applied if the procedures may have side effects. For example, in the expression (f(x)-1)/(f(x)+1)the function f must be called twice, because the two calls may return different results. Moreover, the value of x must be fetched again before the second call, since the first call may have changed it. Determining whether a subprogram may have a side effect is very difficult (indeed, undecidable).[citation needed] So, while those optimizations are safe in purely functional programming languages, compilers of typical imperative programming usually have to assume the worst.

    Inlining[edit]

    A method used to eliminate this overhead is inline expansion or inlining of the subprogram's body at each call site (versus branching to the subroutine and back). Not only does this avoid the call overhead, but it also allows the compiler to optimize the procedure's body more effectively by taking into account the context and arguments at that call. The inserted body can be optimized by the compiler. Inlining however, will usually increase the code size, unless the program contains only one call to the subroutine, or the subroutine body is less code than the call overhead.

    See also[edit]

    References[edit]

    1. ^ U.S. Election Assistance Commission (2007). "Definitions of Words with Special Meanings". Voluntary Voting System Guidelines. Archived from the original on 2012-12-08. Retrieved 2013-01-14.
    2. ^ Subrata Dasgupta (7 January 2014). It Began with Babbage: The Genesis of Computer Science. Oxford University Press. pp. 155–. ISBN 978-0-19-930943-6.
    3. ^ a b J.W. Mauchly, "Preparation of Problems for EDVAC-type Machines" (1947), in Brian Randell (Ed.), The Origins of Digital Computers, Springer, 1982.
    4. ^ Wheeler, D. J. (1952). "The use of sub-routines in programmes". Proceedings of the 1952 ACM national meeting (Pittsburgh) on - ACM '52 (PDF). p. 235. doi:10.1145/609784.609816.
    5. ^ Wilkes, M. V.; Wheeler, D. J.; Gill, S. (1951). Preparation of Programs for an Electronic Digital Computer. Addison-Wesley.
    6. ^ Dainith, John. ""open subroutine." A Dictionary of Computing. 2004". Encyclopedia.com. Retrieved January 14, 2013.
    7. ^ Donald E. Knuth. The Art of Computer Programming, Volume I: Fundamental Algorithms. Addison-Wesley. ISBN 0-201-89683-4.
    8. ^ O.-J. Dahl; E. W. Dijkstra; C. A. R. Hoare (1972). Structured Programming. Akademische Presse. ISBN 0-12-200550-3.
    9. ^ a b Isaacson, Walter (18 September 2014). "Walter Isaacson on the Women of ENIAC". Fortune. Archived from the original on 12 December 2018. Retrieved 2018-12-14.
    10. ^ Planning and Coding of Problems for an Electronic Computing Instrument, Pt 2, Vol. 3 https://library.ias.edu/files/pdfs/ecp/planningcodingof0103inst.pdf (see pg 163 of the pdf for the relevant page)
    11. ^ Guy Lewis Steele Jr. AI Memo 443. 'Debunking the "Expensive Procedure Call" Myth; or, Procedure call implementations considered harmful". Section "C. Why Procedure Calls Have a Bad Reputation".
    12. ^ Frank, Thomas S. (1983). Introduction to the PDP-11 and Its Assembly Language. Prentice-Hall software series. Prentice-Hall. p. 195. ISBN 9780134917047. Retrieved 2016-07-06. We could supply our assembling clerk with copies of the source code for all of our useful subroutines and then when presenting him with a mainline program for assembly, tell him which subroutines will be called in the mainline [...]
    13. ^ "ARM Information Center". Infocenter.arm.com. Retrieved 2013-09-29.
    14. ^ "Overview of x64 Calling Conventions". Msdn.microsoft.com. Retrieved 2013-09-29.
    15. ^ "Function Types". Msdn.microsoft.com. Retrieved 2013-09-29.
    16. ^ ""what is meant by a free function"".
    17. ^ "Microsoft Small Basic". www.smallbasic.com.

    No comments:

    Post a Comment