Delphi-PRAXiS
Seite 4 von 10   « Erste     234 56     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Procedure vs Function, Vor- und Nachteile (https://www.delphipraxis.net/196015-procedure-vs-function-vor-und-nachteile.html)

p80286 16. Apr 2018 09:36

AW: Procedure vs Function, Vor- und Nachteile
 
Interessantes Beispiel was Himitsu da gepostet hat
Delphi-Quellcode:
function Test: string;
begin
   Result := Result + 'a';
end;

for i := 1 to 5 do
   S := Test;
 ShowMessage(S); // 'aaaaa'
nicht nur, das ich ein seltsames Ergebnis bei einer fehlenden Initialisierung bekomme, nein, mein Virenscanner hat auch einen Trojaner gefunden:

Zitat:

Fuery.A!Cl

Kategorie: Trojaner

Beschreibung: Dieses Programm ist gefährlich. Es führt Befehle eines Angreifers aus.

Empfohlene Aktion: Entfernen Sie diese Software unverzüglich.

Elemente:
file:C:\compiler\borland\delphi7\Projects\Project1 .exe
process:pid:1048,ProcessStart:131683403956390456


Gruß
K-H

himitsu 16. Apr 2018 10:28

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von p80286 (Beitrag 1399360)
nein, mein Virenscanner hat auch einen Trojaner gefunden:

Das liegt aber nur sekundär an diesem Code.
Dein altes Delphi 7 und dass zuviele böse Programme in Delphi geschrieben wurden und weil viele Virensignaturen einfach nur Schrott sind.

https://www.virustotal.com/de/ zur Gegenprobe

Und die eigene Software kann man als False-Positive melden, dann wird die Signatur meist recht schnell angepasst.
https://www.microsoft.com/en-us/wdsi/filesubmission

Amateurprofi 16. Apr 2018 11:41

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von bernau (Beitrag 1399351)
Zitat:

Zitat von himitsu (Beitrag 1399299)
Zitat:

Zitat von Zacherl (Beitrag 1399294)
In seltenen Fällen (bei Übergabe großer Strukturen bzw. Strings, etc.) kann ein
Delphi-Quellcode:
var
Parameter sinnvoll sein, um Kopieroperationen zu vermeiden. Muss man sich im Normalfall aber keine Gedanken drüber machen.

Gerade da ist es total wurscht,
denn bei gemanagten Typen ala String, Variant, Interface und dynamischen Arrays macht der Delphi-Compiler aus
Delphi-Quellcode:
function Beispiel(...): string;
eben auch ein
Delphi-Quellcode:
procedure Beispiel(...; var Result: string);
und kopiert nichts um.

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:
function Test: string;
begin
  Result := Result + 'a';
end;

for i := 1 to 5 do
  S := Test;
ShowMessage(S); // 'aaaaa'
aber wenn man sich das jetzt gedanklich als Prozedur ansieht, dann ist sofort klar, warum es so ist.

Damit würde ich aber schlaflose Nächte haben.

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.

Doch, Result ist definitiv initialisiert und besitzt keinen Zufallswert.
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

Zacherl 16. Apr 2018 12:21

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.

himitsu 16. Apr 2018 12:27

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Doch, Result ist definitiv initialisiert und besitzt keinen Zufallswert.
Bei "Function Test:String;" ist Result ein Zeiger auf eine Stringvariable und da Strings immer initialisiert werden, ist damit auch Result initialisiert.
Bitte meine Beiträge dazu genau lesen.

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).

Amateurprofi 16. Apr 2018 12:42

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von himitsu (Beitrag 1399374)
Zitat:

Doch, Result ist definitiv initialisiert und besitzt keinen Zufallswert.
Bei "Function Test:String;" ist Result ein Zeiger auf eine Stringvariable und da Strings immer initialisiert werden, ist damit auch Result initialisiert.
Bitte meine Beiträge dazu genau lesen.

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).

Sehe ich anders.
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.

Zacherl 16. Apr 2018 14:19

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von Amateurprofi (Beitrag 1399375)
Sehe ich anders.
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.

Selbst bei den
Delphi-Quellcode:
class operator
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
Delphi-Quellcode:
var
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 (
Delphi-Quellcode:
Result
) 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.

himitsu 16. Apr 2018 14:30

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von Zacherl (Beitrag 1399380)
Selbst bei den class operator 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

Was aber eventuell nichts mit diesem Problem zu tun hat,
denn wenn dort dieser Result-Record auch als VAR-Parameter in eine neue leere Variable geht, dann wäre es praktisch das Gleiche.

bernau 16. Apr 2018 15:00

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von Amateurprofi (Beitrag 1399370)
Doch, Result ist definitiv initialisiert und besitzt keinen Zufallswert.

Mag sein, dass beim ersten Aufruf von "Test" das Result mit "" initialisiert ist.

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.

Zacherl 16. Apr 2018 15:04

AW: Procedure vs Function, Vor- und Nachteile
 
Zitat:

Zitat von himitsu (Beitrag 1399384)
Zitat:

Zitat von Zacherl (Beitrag 1399380)
Selbst bei den class operator 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

Was aber eventuell nichts mit diesem Problem zu tun hat,
denn wenn dort dieser Result-Record auch als VAR-Parameter in eine neue leere Variable geht, dann wäre es praktisch das Gleiche.

Damit wollte ich vor allem die Inkonsistenz aufzeigen.

Zitat:

Zitat von bernau (Beitrag 1399387)
Was übrigens irgendwann einen Stackoverflow nach sich zieht, wenn "Test" nur oft genug ausgeführt wird.

Nee, das nicht. Strings werden in Delphi Heap-allocated.

Zitat:

Zitat von bernau (Beitrag 1399387)
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.

Das ist ein wichtiger Punkt. Bin auch der Meinung, dass zumindest die "nicht initialisiert" Meldung trotzdem angezeigt oder die ganze Optimierung anders umgesetzt werden sollte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:36 Uhr.
Seite 4 von 10   « Erste     234 56     Letzte »    

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