![]() |
Procedure vs Function, Vor- und Nachteile
Hallo, der Titel ist ja recht Aussagekräftig
Beispiele
Delphi-Quellcode:
Aber was Unterscheidet die Beiden noch? Gibt es im Speicher Unterschiede? Was ist performanter?
// Funktionen Vorteil, sie geben etwas direkt wieder
Function Beispiel : Boolean; begin Result := True; end; // Proceduren können ja auch etwas wiedergeben per Var // Nachteil, man benötigt eben eine Variable um das Var auszuwerten Procedure Beispiel (var bResult: Boolean); begin bResult := True; end; Gibt es Unterschiede wie Delphi es handhabt wenn eine Function/Procedure eingeleitet bzw. am Ende angekommen ist? Oder wie schauts aus in Libraries (DLL), man kann ja beides EXPORT machen. Was fällt Euch noch zum Thema Procedure vs Function, Vor- und Nachteile ein? Ich würde gerne Eure Meinungen dazu erfragen, egal wie verwegen, falls es abstrakte Unterschiede gibt. Oder Beispiele wo man für eine Sache eher das nehmen sollte wohingegen bei anderen Sachen nur das andere genommen werden sollte mit einer Mini-Begründung. Falls sich jemand hier einklinkt und Antwortet, Danke dafür! |
AW: Procedure vs Function, Vor- und Nachteile
Ziemlich wurscht. Wenn man einen Rückgabewert hat, sind Funktionen zu bevorzugen, da sie intuitiver sind und genau für diesen Zweck entwickelt wurden. In seltenen Fällen (bei Übergabe großer Strukturen bzw. Strings, etc.) kann ein
Delphi-Quellcode:
Parameter sinnvoll sein, um Kopieroperationen zu vermeiden. Muss man sich im Normalfall aber keine Gedanken drüber machen.
var
Auf Assembler-Ebene gibt es keine signifikanten Unterschiede. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Delphi-Quellcode:
mit
var
Delphi-Quellcode:
verwechselt.
const
Zudem verwechselt ihr beide
Delphi-Quellcode:
mit
var
Delphi-Quellcode:
.
out
|
AW: Procedure vs Function, Vor- und Nachteile
Da würde ich gerne nochmal nachhaken
Delphi-Quellcode:
Was Unterscheidet Beispiel1 von Beispiel2? Beides schreibt doch bResult um?
Procedure Beispiel1 (var bResult: Boolean);
begin bResult := True; end; Procedure Beispiel2 (out bResult: Boolean); begin bResult := True; end; |
AW: Procedure vs Function, Vor- und Nachteile
|
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
edit Zitat:
|
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
denn bei gemanagten Typen ala String, Variant, Interface und dynamischen Arrays macht der Delphi-Compiler aus
Delphi-Quellcode:
eben auch ein
function Beispiel(...): string;
Delphi-Quellcode:
und kopiert nichts um.
procedure Beispiel(...; var Result: string);
Leider ist das auch das Problem/die Urache, dass bei Funktionen für das Result keine "wurde nicht initialisiert"-Meldung vom Compiler kommt, da es eben doch initialisiert wurde (außerhalb) und mit dem Ergebnis, dass bei vergessenen Initialisierung schnell etwas Komisches heraus kommt,
Delphi-Quellcode:
aber wenn man sich das jetzt gedanklich als Prozedur ansieht, dann ist sofort klar, warum es so ist.
function Test: string;
begin Result := Result + 'a'; end; for i := 1 to 5 do S := Test; ShowMessage(S); // 'aaaaa' |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Man schreibt S bei jedem Schleifendurchlauf doch neu und pro "Test" wird nur 1x a hinzugefügt. Sollte das nicht nur "a" angezeigt werden? |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
|
AW: Procedure vs Function, Vor- und Nachteile
Aber Test() hat doch keinerlei Parameter. Wie kommt diese Prozedur denn an die vorherigen Zuweisungen?
|
AW: Procedure vs Function, Vor- und Nachteile
Weil der Programmierer bewußt die Compiler-Warnung "Result wurde nicht initialisizert !" ignoriert. Somit behält result u.U. seinen Wert und es wird dazu ein 'a' addiert.
|
AW: Procedure vs Function, Vor- und Nachteile
Boing... jetzt habs ich kapiert. Also ein klassischer Fehler.
|
AW: Procedure vs Function, Vor- und Nachteile
Wie gesagt, der Compiler macht aus solchen Funktionen eben eine Prozedur mit VAR-Parameter
Delphi-Quellcode:
Grundsätzlich ist das für den Entwickler egal.
procedure Test(var Result: string); //function Test: string;
begin Result := Result + 'a'; end; for i := 1 to 5 do Test(S); //S := Test; ShowMessage(S); // 'aaaaa' Er muß eben nur selber aufpassen, dass er solche Variablen initialisiert. Am Enfachsten grundsätzlich immer alle Result-Variablen selber initialisieren, wenn mit dem Startwert gerechnet wird. Für den Compiler ist es so halt viel anfacher die Result-Variable zu verwalten, denn bei gemanagten Typen muß er selber dafür sorgen, dass letztendlich diese Variable immer ordnungsgemäß freigegeben werden und das ist hier einfacher, wenn er direkt die äußere Variable nimmt. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Muss ich irgendwo was ändern das der eine Warnung wirft bzw hab ich irgendwo was abgeschaltet was das verhindert? edit
Delphi-Quellcode:
So ist's in meinem Delphi ohne Warnungen/Fehler kompiliert worden.
function Test1: string;
begin Result := Result + 'a'; end; function Test2: string; begin Result := 'a' + Result; end; procedure TForm1.Button1Click(Sender: TObject); var i : Byte; S : String; begin for i := 1 to 5 do S := Test1; Label1.Caption := S; end; procedure TForm1.Button2Click(Sender: TObject); var i : Byte; S : String; begin for i := 1 to 5 do S := Test2; Label2.Caption := S; end; |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
![]() |
AW: Procedure vs Function, Vor- und Nachteile
Bei Delphi 7 kann man in den Projektoptionen unter Compilermeldungen auswählen, ob die Warnung 'Dr Rückgabewert der Funktion könnte undefiniert sein.' ausgegeben werden soll oder nicht.
Sieht bei den Meldungen dann sinngemäß so aus:
Code:
[Warnung] Unit1.pas(Zeile): Rückgabewert der Funktion 'TForm1.NameDerFunktion' könnte undefiniert sein
|
AW: Procedure vs Function, Vor- und Nachteile
@himitsu: Ja schon, Du schreibst werden nicht Angezeigt, Ghostwalker schreibt das der Programmierer diese Warnung ignoriert.
Wenn er also eine Angezeigt bekommt, das hätte ich auch gerne, weshalb ich da nochmal nachhakte. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Delphi-Quellcode:
Es geht aber noch schlimmer:
function Test: string;
begin Result := Result + 'a'; end;
Delphi-Quellcode:
Niemand wird dir sagen können, was dieses Programm tut.
function Test: Integer;
begin Result := Result + 1; end; begin ShowMessage(IntToStr(Test)); end; |
AW: Procedure vs Function, Vor- und Nachteile
Doch, es geht u. a. um die entsprechende Warnung:
Ghostwalker schrieb hier: ![]() Und Kodezwerg schrieb dort: ![]() Darauf bezog ich mich und nicht darauf, dass das zur Diskussion stehende Codefragment nicht zwingend klar und verständlich ist, sondern nur als Beispiel für den Unterschied zwischen Prozeduren und Funktionen und ggfls. daraus resultierenden Fallstricken dient. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Und diesen Wert zeigt das Programm dir dann an. Aber das ganze kann natürlich auch bei komplizierteren Funktionen durchaus noch ganz andere Effekte haben. Genau deswegen gibts die Warnung. Mit Ignorieren war schlicht gemeint, das die entsprechende Warnung ausgeschalten war, oder die Warnung vom Programmierer bewußt nicht gelesen wurde. :) |
AW: Procedure vs Function, Vor- und Nachteile
Delphi-Quellcode:
function Test: Integer;
begin Result := Result + 1; end;
Delphi-Quellcode:
Genau das sagte ich doch.
function Test: string; // procedure Test(var Result: string);
begin Result := Result + 'a'; end; Bei Ersten kommt die Fehlermeldung, da der Integer wirklich als lokale Variable existiert, als Result in EAX zurückgeliefert und anschließend der äußeren Variable zugewiesen wird. Während beim String (vom Compiler speicherverwalteter Zeigertyp) keine lokale Variable existiert und direkt in den referenzierten Parameter geschrieben wird.
Delphi-Quellcode:
function Test: Integer;
begin Result := Result + 1; raise Exception.Create(''); end; i := 123; try i := Test; except end; ShowMessage(i.toString); // immer 123
Delphi-Quellcode:
Hängt davon ab, ob der Compiler für das Result automatisch eine lokale Variable erstellt und anschließend umkopiert oder direkt das Result nimmt.
function Test: string; // procedure Test(var Result: string);
begin Result := Result + 'a'; raise Exception.Create(''); end; s := 'x'; try s := Test; except end; ShowMessage(S); // je nach Compileroptiomierung auch mal 'xa' und nicht 'x'
Delphi-Quellcode:
In einer Schleife mehrfach Test aufgerufen bekommt die Funktion aber praktisch immer wieder die selbe Variable rein.
s := 'x';
try Test(temp); // temp := Test; s := temp; except end; // oder s := 'x'; try Test(s); // s := Test; except end; |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Danke für den Tipp, ich habe auch probiert Ihn umzusetzen, nun hab ich alle Meldungen On/Off durch mit dem Resultat wie himitsu schrieb, es gibt keine Warnung für das
Delphi-Quellcode:
Beispiel.
procedure Test(var Result: string); begin Result := Result + 'a'; end;
Dennoch Danke! Auch nochmal an himitsu für die vielen Beispiele und Erklärungen! |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Delphi-Quellcode:
ist genau wie (meistens)
var
Delphi-Quellcode:
ein "pass by reference", also stimmt das schon mit der Vermeidung von unnötigen Kopieroperationen. Da ein Output Parameter ja aber gewünscht ist, macht
const
Delphi-Quellcode:
hier keinen Sinn :gruebel:
const
Zitat:
Delphi-Quellcode:
ist runtergebrochen auf den generierten Code exakt gleichwertig zu
var
Delphi-Quellcode:
(auch wenn out gegebenenfalls aus Gründen der Übersichtlichkeit zu bevorzugen ist).
out
Code:
005CE2D4 E8BFFFFFFF call Beispiel1 // function
Unit2.pas.48: Beispiel2(X); 005CE2D9 8D45FB lea eax,[ebp-$05] 005CE2DC E8C7FFFFFF call Beispiel2 // procedure mit var Unit2.pas.49: Beispiel3(X); 005CE2E1 8D45FB lea eax,[ebp-$05] 005CE2E4 E8CFFFFFFF call Beispiel3 // procedure mit out Zitat:
|
AW: Procedure vs Function, Vor- und Nachteile
Prozedur für Füllen von Objekten ala Listen, wo es als Result oft ungünstig ist.
Und Prozedur hat eher Nachteile, denn du brauchst da immer eine "selbstdefinierte" Variable und man kann es nicht direkt weitergeben, z.B. als Parameter an eine andere Prozedur/Funktion oder für's Zusammenrechnen mehrerer Werte ohne Zwischenvariablen. |
AW: Procedure vs Function, Vor- und Nachteile
VAR und OUT ist praktisch das Gleiche, außer dass bei OUT per Definition kein Eingabewert existiert, also die Variable initialisiert werden müsste, aber für Typen ala String/Array/Interface bricht der Compiler dieses, da sonst die ordnungsgemäße Speicherverwaltung nicht mehr garantiert werden kann.
|
AW: Procedure vs Function, Vor- und Nachteile
und das man bei OUT innerhalb der procedure/function nicht lesend darauf zugreifen sollte, so hab ich das verstanden, richtig?
|
AW: Procedure vs Function, Vor- und Nachteile
Man darf, vor allem nachdem man schon etwas zugewiesen hat.
Vor der ersten Zuweisung ist der Wert einfach "undefiniert", so wie bei lokalen Variablen (Integer und so). |
AW: Procedure vs Function, Vor- und Nachteile
Danke, ich habe das mit VAR und OUT nun dank Euch/Dir verinnerlicht! Super Forum & Super Community, ich freue mich hier sein zu dürfen.
|
AW: Procedure vs Function, Vor- und Nachteile
Das die Warnung bei gemanagten Datentypen garnicht erscheint ist mir ehrlich gesagt bisher nicht aufgefallen:oops:.
Das mit var und out war mir bisher auch nicht bewußt, da ich in der Regel bei solchen Fällen immer var benutzt habe. Man lernt nie aus :) In den letzten Versionen scheint sich da auch (positiv) am Compiler doch einiges geändert zu haben, denn selbst in folgendem Fall, erkennt der Compiler, das result nicht undefiniert sein kann (war schonmal anders :)).
Delphi-Quellcode:
function Test(wupti:boolean):integer;
begin if (wupti) then result := 1 else result := 0; end; |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Result ist definitiv nicht initialisiert und besitzt einen Zufallswert, der ggf. dann von dem vorherigen Funktionsaufruf noch zufällig vorhanden ist. Mag sein, dass das jetzt so funktioniert. Dieses Verhalten ist aber nirgends dokumentiert. (Oder ich habe es noch nicht gesehen.) Bei einer nächsten Compilerfunktion wird das ggf. ganz anders sein. |
AW: Procedure vs Function, Vor- und Nachteile
Interessantes Beispiel was Himitsu da gepostet hat
Delphi-Quellcode:
nicht nur, das ich ein seltsames Ergebnis bei einer fehlenden Initialisierung bekomme, nein, mein Virenscanner hat auch einen Trojaner gefunden:
function Test: string;
begin Result := Result + 'a'; end; for i := 1 to 5 do S := Test; ShowMessage(S); // 'aaaaa' Zitat:
K-H |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Dein altes Delphi 7 und dass zuviele böse Programme in Delphi geschrieben wurden und weil viele Virensignaturen einfach nur Schrott sind. ![]() Und die eigene Software kann man als False-Positive melden, dann wird die Signatur meist recht schnell angepasst. ![]() |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Bei "Function Test:String;" ist Result ein Zeiger auf eine Stringvariable und da Strings immer initialisiert werden, ist damit auch Result initialisiert. Im nachstehenden Assembler-Auszug kannst du das schön sehen. Vor dem Aufruf der Funktion Test wird mit lea eax,[ebp-$04] die Adresse der lokalen Variablen S in EAX gestellt. Das in [ebp-4] tatsächlich die Variable S gespeichert ist, siehst du 6 Zeilen tiefer, wo S als Parameter für ShowMessage in EAX gestellt wird. Recht hättest du bei z.B. Function Test:Integer;, da ist Result nicht initialisiert. C_Main.pas.5152: for I:=1 to 5 do 0060FFF4 BB05000000 mov ebx,$00000005 C_Main.pas.5153: S:=Test; 0060FFF9 8D45FC lea eax,[ebp-$04] 0060FFFC E8B7FFFFFF call Test C_Main.pas.5152: for I:=1 to 5 do 00610001 4B dec ebx 00610002 75F5 jnz $0060fff9 C_Main.pas.5154: ShowMessage(S); 00610004 8B45FC mov eax,[ebp-$04] 00610007 E8C0A2ECFF call ShowMessage |
AW: Procedure vs Function, Vor- und Nachteile
Unintuitiv ist das Verhalten allerdings schon. Bei einer Funktion in Delphi erwarte ich grundsätzlich temporäre R-Values. Andere Sprachen - wie z.b. C++ - erlauben ja auch die Rückgabe von Referenzen, da ist das Verhalten aber einheitlich.
|
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Ja, es wird initialisiert, aber eben nicht dort, wo man vermuten könnte (zu Beginn der Funktion), also ist aus Sicht der Funktion dieses Result eben nicht "wirklich" initialisiert, zumindestens nicht unbeding mit 0 (einem Leerstring). |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Es ist ein grundsätzliches Konzept von Delphi, dass Strings IMMER initialisiert werden, was ja auch eine Voraussetzung für die Referenzzählung ist. Somit kann es keinen nicht-initialisierten String geben, und auch aus Sicht der Funktion gilt, dass der String (wie auch immer) initialisiert ist und einen gültigen Inhalt hat. Anders wäre es zum Beispiel bei einem Double - da kann auch was ungültiges drin sein. Recht hast du natürlich damit, dass die Funktion nicht davon ausgehen kann, dass Result bei Aufruf der Funktion leer ist. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Delphi-Quellcode:
werden temporäre R-Values verwendet. Wenn man beispielsweise die Addition eines Record überläd, wird ein komplett neuer temporärer Record erstellt und als Ergebnis des Operators zurückgeliefert, welcher dann letztlich der L-Value zugewiesen wird (was meiner Meinung nach schon ziemlicher Schwachsinn ist, aber okay ...). Dadurch dass der String intern als
class operator
Delphi-Quellcode:
Parameter übergeben wird, spart Delphi sich natürlich die unnötige Kopiererei, wird dadurch an dieser Stelle aber extrem inkonsequent. Das erwartete Verhalten wäre, dass beim Betreten der Funktion eine neue lokale String Instanz (
var
Delphi-Quellcode:
) mit einem leeren String initialisiert wird. Auf dieser Instanz kann man dann lokal arbeiten und nach Rückkehr der Funktion wird der Wert dann der entsprechenden left-hand-side zugewiesen. Wäre sicherlich etwas komplizierter die Referenzzählung über Funktionsgrenzen hinweg zu gestalten (Delphi räumt lokale Objekte ja vor dem Verlassen der Funktion frei), aber ist keineswegs ein Ding der Unmöglichkeit. Die hier momentan Verwendete Optimierung ist ein reiner Hack.
Result
|
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
denn wenn dort dieser Result-Record auch als VAR-Parameter in eine neue leere Variable geht, dann wäre es praktisch das Gleiche. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Wenn Result bei jedem Aufruf von "Test" initialisiert wäre, dann würde als Ergebnis immer "a" herhauskommen. Ist aber nicht so. Es ist immer der Wert des vorherigen Result vorhanden. Was für mich erst mal undefiniert ist. Da man ja nicht weis, von wo überall "Test" aufgerufen wird, muss man davon ausgehen, dass Result einen string mit dem Buchstaben "a" in zufälliger Länge hat. Was übrigens irgendwann einen Stackoverflow nach sich zieht, wenn "Test" nur oft genug ausgeführt wird. Zusätzlich kommt hinzu, dass das Verhalten sich mit dem nächsten Compiler ändern kann. Dann wird vielleicht Result bei jedem Aufruf der Funktion auf "" gesetzt. Mann weis ja nie. Es sei denn, dass dieses Verhalten so dokumentiert ist. |
AW: Procedure vs Function, Vor- und Nachteile
Zitat:
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:00 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz