2016-03-10 3 views
9

У меня возникла проблема при работе с процедурами и строками в Delphi. Дело в том, что я ожидал увидеть выходную строку «1S2S3S4S5S6S», но фактический результат - «1234S5S6». В процессе отладки он говорит, что строковые переменные S1, S2, S3 и S6 не инициализированы (строки S1, S2, S3, S6 являются «строками», S4 и S5 имеют значение «S»). Может ли кто-нибудь объяснить мне это? Вот код:Процедуры Delphi со строковыми параметрами

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

Остерегайтесь: когда вы объявляете параметр с 'const', вы сообщаете компилятору, что он не должен ожидать изменения параметра на протяжении всей этой функции. Вы несете ответственность за то, чтобы вы поддержали это обещание; компилятор не может проверить его для вас. В этом случае вы изменяете 'S' через' S7', в то время как требование 'S2' и' S5' не изменится. –

ответ

17

Ваш параметр S7 объявлен как out параметром, поэтому компилятор установит переданную переменную в пустую строку, когда функция вызывается. Вы передаете ту же переменную S для всех параметров, включая выходной параметр, поэтому значение S будет стерто из памяти до того, как значения параметра будут использоваться внутри функции.

для дальнейшей разработки, процедура использует register соглашение о вызовах, где S1 .. S3 передаются в регистрах процессора (EAX, EDX, ECX и, соответственно) и S4 .. S6 передаются в стек вместо этого. Входная переменная string становится очищенной после того, как ее текущее значение вставляется в стек для S4 и S5 (S3 и S6 - это просто указатели на переменную) и до того, как значение присвоено S1 и S2. Так, S1 и S2 в конечном итоге ноль, S4 и S5 содержат указатели на исходные 'S' данных перед салфетку и S3 и S6 указывают на string переменную, которая была уничтожена.

Отладчик может показать вам все это в действии. Если поставить точку останова в строке, где MyProcedure() называется, а затем открыть окно CPU, вы увидите следующие инструкции по сборке:

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 

Чтобы это исправить, вам нужно использовать другую переменную для получения выходных :

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

в качестве альтернативы, изменить процедуру в функцию, которая возвращает новый String через его Result вместо использования параметра 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

Спасибо за этот совет, но я до сих пор не понимаю, почему S4 и S5 имеют значение «S», а другие нет. Что не так? – Alexander

+0

ОК, я получаю это сейчас :) Последний вопрос, почему это происходит в таком порядке? Я имею в виду, что значения S4, S5 сначала помещаются в стек, а затем S1, S2, S3 в регистры. – Alexander

+5

Если параметры регистра были сохранены для регистрации сначала, @Alexander, то эти регистры были бы недоступны для компилятора для вычисления значений параметров стека. Компилятор знает, что ему разрешено вычислять значения параметров в любом порядке, который он выбирает, поэтому он выбирает порядок, удобный для компилятора. –

Смежные вопросы