2016-03-10 5 views
9

Ich hatte ein Problem beim Arbeiten mit Prozeduren und Strings in Delphi. Tatsache ist, dass ich erwartet habe, die Ausgabezeichenfolge "1S2S3S4S5S6S" zu sehen, aber die tatsächliche Ausgabe ist "1234S5S6". Während des Debug-Prozesses heißt es, dass die S1-, S2-, S3- und S6-String-Variablen nicht initialisiert sind (S1, S2, S3, S6 sind Strings, S4 und S5 haben den Wert "S"). Kann mir das jemand erklären? Hier ist der Code:Delphi-Prozeduren mit String-Parametern

program StringTest; 

{$APPTYPE CONSOLE} 

procedure MyProcedure(S1: String; const S2: String; var S3: String; 
         S4: String; const S5: String; var S6: String; 
         out S7: String); 
begin 
    S7 := '1' + S1 + '2' + S2 + '3' + S3 + '4' + S4 + '5' + S5 + '6' + S6; 
end; 

procedure Work; 
var 
    S: String; 
begin 
    S := 'S'; 
    MyProcedure(S, S, S, S, S, S, S); 
    writeln(S); 
end; 

begin 
    Work; 
    readln; 
end. 
+4

Vorsicht: Wenn Sie einen Parameter mit 'const' deklarieren, sagen Sie dem Compiler, dass er nicht erwarten sollte, dass sich der Parameter für die Dauer dieser Funktion ändert. Es liegt in Ihrer Verantwortung sicherzustellen, dass Sie dieses Versprechen einhalten; Der Compiler kann es nicht für Sie überprüfen. In diesem Fall modifizieren Sie 'S' durch' S7', während Sie gleichzeitig 'S2' und' S5' nicht ändern. –

Antwort

17

Ihr S7 Parameter als out Parameter deklariert wird, so wird der Compiler die übergebene Variable auf eine leere Zeichenfolge gesetzt, wenn die Funktion aufgerufen wird. Sie übergeben dieselbe S Variable für alle Parameter, einschließlich des Ausgabeparameters, sodass der Wert S aus dem Speicher gelöscht wird, bevor die Parameterwerte in der Funktion verwendet werden.

erarbeiten weiter, wird das Verfahren der register Aufrufkonvention verwenden, wo S1 .. S3 werden in CPU-Register übergeben (EAX, EDX und ECX, respectively) und S4 .. S6 statt auf dem Stack übergeben werden. Die Eingabe string Variable wird gelöscht gelöscht, nachdem der aktuelle Wert auf dem Stapel für S4 und S5 (S3 und S6 sind nur Zeiger auf die Variable) und bevor der Wert S1 und S2 zugewiesen wird. Also, S1 und S2 Ende bis null, S4 und S5 enthalten Zeiger auf die ursprünglichen 'S' Daten vor dem Wischen und S3 und S6 zeigen auf dem string Variable, die abgewischt wurde.

Der Debugger kann Ihnen alles in Aktion zeigen. Wenn Sie einen Haltepunkt an der Linie setzen, wo MyProcedure() genannt wird, und dann die CPU-Ansicht öffnen, sehen Sie die folgende Montageanleitung finden Sie unter:

StringTest.dpr.17: MyProcedure(S, S, S, S, S, S, S); 
00405A6C 8B45FC   mov eax,[ebp-$04] // [ebp-$04] is the current value of S 
00405A6F 50    push eax   // <-- assign S4 
00405A70 8B45FC   mov eax,[ebp-$04] 
00405A73 50    push eax   // <-- assign S5 
00405A74 8D45FC   lea eax,[ebp-$04] 
00405A77 50    push eax   // <-- assign S6 
00405A78 8D45FC   lea eax,[ebp-$04] 
00405A7B E8B0EDFFFF  call @UStrClr  // <-- 'out' wipes out S! 
00405A80 50    push eax   // <-- assign S7 
00405A81 8D4DFC   lea ecx,[ebp-$04] // <-- assign S3 
00405A84 8B55FC   mov edx,[ebp-$04] // <-- assign S2 
00405A87 8B45FC   mov eax,[ebp-$04] // <-- assign S1 
00405A8A E8B9FEFFFF  call MyProcedure 

Um dies zu beheben, müssen Sie eine andere Variable verwenden, um die Ausgabe zu erhalten :

procedure Work; 
var 
    S, Res: String; 
begin 
    S := 'S'; 
    Proc(S, S, S, S, S, S, Res); 
    WriteLn(Res); 
end; 

Alternativ ändern, das Verfahren in eine Funktion, die eine neue String über seine Result anstelle der Verwendung eines out Parameter liefert:

function MyFunction(S1: String; const S2: String; var S3: String; 
         S4: String; const S5: String; var S6: String): String; 
begin 
    Result := '1' + S1 + '2' + S2 + '3' + S3 + '4' + S4 + '5' + S5 + '6' + S6; 
end; 

procedure Work; 
var 
    S: String; 
begin 
    S := 'S'; 
    WriteLn(MyFunction(S, S, S, S, S, S)); 
end; 
+0

Danke für diesen Rat, aber ich verstehe immer noch nicht, warum S4 und S5 Wert "S" haben und die anderen nicht. Was ist los mit dir? – Alexander

+0

OK, ich bekomme es jetzt :) Letzte Frage, warum passiert das in dieser Reihenfolge? Ich meine die Werte von S4, S5 werden zuerst geschoben, dann S1, S2, S3 zu den Registern. – Alexander

+5

Wenn die Registerparameter zuerst in den Registern @Alexander gespeichert würden, wären diese Register für den Compiler nicht verfügbar, um die Werte der Stapelparameter zu berechnen. Der Compiler weiß, dass es erlaubt ist, Parameterwerte in beliebiger Reihenfolge zu berechnen, also wählt er eine Reihenfolge, die für den Compiler geeignet ist. –