2016-03-10 35 views
9

Ho riscontrato un problema mentre lavoravo con le procedure e le stringhe in Delphi. Il fatto è che mi aspettavo di vedere la stringa di output "1S2S3S4S5S6S" ma l'output effettivo è "1234S5S6". Durante il processo di debug si dice che le variabili di stringa S1, S2, S3 e S6 non sono inizializzate (S1, S2, S3, S6 sono stringhe '', S4 e S5 hanno valore 'S'). Qualcuno può spiegarmi questo? Ecco il codice:Procedure Delphi con parametri stringa

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

Attenzione: quando dichiari un parametro con 'const', stai dicendo al compilatore che non dovrebbe aspettarsi che il parametro cambi per la durata di quella funzione. È tua responsabilità assicurarti di mantenere questa promessa; il compilatore non può controllarlo per te. In questo caso, stai modificando 'S' tramite' S7' mentre allo stesso tempo affermando che 'S2' e' S5' non cambieranno. –

risposta

17

vostro parametro S7 è dichiarato come parametro di out, in modo che il compilatore impostare la variabile passata ad una stringa vuota quando viene chiamata la funzione. Stai passando la stessa variabile S per tutti i parametri, incluso il parametro di output, quindi il valore di S viene cancellato dalla memoria prima che i valori dei parametri vengano utilizzati all'interno della funzione.

Elaborare ulteriormente, la procedura utilizza la convenzione register chiamata, dove S1 .. S3 vengono passati in registri della CPU (EAX, EDX, e ECX, rispettivamente) e S4 .. S6 vengono passati in pila invece. La grandezza di ingresso string sta ottenendo spazzato chiaro dopo il suo valore attuale viene inserito nello stack per S4 e S5 (S3 e S6 sono solo rimandi alla variabile), e prima che il valore viene assegnato a S1 e S2. Così, S1 e S2 finiscono nullo, S4 e S5 contengono puntatori ai dati originali 'S' prima del wipe, e S3 e S6 sono rivolte l'una verso la variabile string che è stato spazzato via.

Il debugger può mostrare tutto questo in azione. Se si mette un punto di interruzione alla linea dove MyProcedure() si chiama, e quindi si apre la vista della CPU, potrete vedere le seguenti istruzioni di montaggio:

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 

Per risolvere questo problema, è necessario utilizzare una variabile diversa per ricevere l'uscita :

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

alternativa, modificare la procedura in una funzione che restituisce un nuovo String tramite il suo Result invece di utilizzare un parametro out:

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

Grazie per questo consiglio, ma ancora non capisco perché S4 e S5 hanno valore "S" e gli altri no. Cosa c'è che non va? – Alexander

+0

OK, lo sto facendo ora :) Ultima domanda, perché succede in questo ordine? Intendo che i valori di S4, S5 vengono prima impilati, poi S1, S2, S3 ai registri. – Alexander

+5

Se i parametri del registro sono stati memorizzati per primi in registri, @Alexander, allora quei registri non sarebbero disponibili per il calcolo del calcolo dei valori dei parametri dello stack. Il compilatore sa che è consentito calcolare i valori dei parametri nell'ordine che preferisce, quindi sceglie un ordine conveniente per il compilatore. –