tags:

views:

845

answers:

7

I believe that local integer variables are not initialized to zero in delphi. The initial value is whatever happens to be at that memory location. So in the code below the first time the button is clicked the first message shows a integer value. How come the second time it's clicked it doesn't show 3 but instead shows the same integer value? It continues to show the same integer value each time I click the button. The value is different only when I stop and restart the program. Where is 3 being stored as it looks like the same memory location is used each time the button is clicked in the same run of the program?

procedure TForm1.Button1Click(Sender: TObject);

var
int1 : integer;

begin
   showmessage(inttostr(int1)) ;
   int1 := 3;
end;

end.
+4  A: 

Firstly, you're correct that local variables aren't initialised.

Also you can't guarantee that int1 is being stored in the same memory location on each invocation. In this case it could be that the reason you're seeing the same value each time is because it is using the same location (by chance) but the Delphi compiler has optimised away your final

int1 := 3;

statement as it has no effect. (You can add another showmessage(inttostr(int1)) call after that line and see if that makes a difference.) Another possibility is that the memory location used for int1 is reused between calls to your button handler (say in the Windows message loop) and that happens to always reset it to the value you're seeing.

Mark Pim
+1  A: 

This will only be true if you execute no other code between two clicks on Button1, most importantly code that uses the same amount of (or more) stack space than the code to arrive at procedure TForm1.Button1Click() uses. Unless you overwrite values on the stack they will still contain the same value.

What you could do to test this is to add another button with an OnClick handler that calls a method with more parameters than ShowMessage() has. If you click that button between two clicks on Button1, the value of int1 should indeed change. That is because the stack location for int1 in Button1Click() will then be used for one of the parameters in your other handler, and get overwritten.

The assignment should be optimized away, you can see this as the line should not have the blue dot in the gutter area. There should be a compiler hint as well.

Edit: As you commented there seems to be no change in behaviour when another button is clicked. From that I can only assume that the VCL code (which is executed before the OnClick handler is called) uses so much stack space that the memory location for int1 is always initialized with some (stable) value. This code does always the same, so if the addresses of the involved objects (Application, parent form and button) do not change the value will also stay the same. OTOH restarting the application will yield a new, equally stable value in the uninitialized local variable.

Note that this is all highly dependent on the VCL code executed before the handler, so changes to the VCL might change it too. It may even be that various Delphi versions already behave differently.

mghie
I put in a second button as you suggested but it did not alter the behaviour on clicking button1. Lieven answer seems to be the correct one.
kjack
+1  A: 

Local variables are in the stack frame. And they are not initialized.

If you access a method twice, you have a chance the stackpointer is euqal which gives the same values.

Example:

Stack before call:

> More stack  (memory location X)

If Button1Click is called, the stack is like:

> Int1
> Return address
> Sender
> Self pointer
> More stack  (memory location X)

If the next time, Button1Click is called, the stackpointer is still on location X, and no other function has changed the values, you will find the same value for Int1.

If you have secure information, it is always wise to clear the local variables (but you have a chance that the optimizer removes these states. So you neet to disable optimizing).

Just for fun, add another button:

procedure TForm1.Button2Click(Sender: TObject);
var
  int1 : integer;
begin
  showmessage(inttostr(int1)) ;
  int1 := 777;
end;

And check:

  • click 1: garbage
  • click 1 again: 3
  • click 2: 3
  • click 1: 777
Gamecat
I did this and I got garbage for all clicks and it was the same garbage for both buttons every time! Again it only changed once I stopped and restarted the program. I think the garbage figure actually refers to TButton as Lieven says
kjack
Actually just ran it again and the garbage for the 2 buttons is different but it is still garbage on both
kjack
@kjack: It is not garbage, it is a leftover pointer to the button that was clicked, originating from the VCL code that ultimately calls your OnClick handler. That's why it is a different value for different buttons being clicked.
mghie
Self and Sender aren't on the stack. They're in registers. Int1 is likely in a register as well. The temporary location for the return value from IntToStr will be on the stack, but if Int1 is on the stack, they'll be in different places.
Rob Kennedy
+10  A: 

kjack,

It contains whatever value is in the stack frame at that time. In your case, this will be Sender. If you'd take the integer and typecast it to an object you'll notice the "pattern".

procedure TForm1.Button1Click(Sender: TObject);

var
int1 : integer;

begin
   ShowMessage(TObject(int1).ClassName);
   showmessage(inttostr(int1)) ;
   int1 := 3;
end;

end.
Lieven
Not exactly, int1 and Sender are not defined on the same space. (Although this is possible using the absolute directive).
Gamecat
yes ShowMessage(TObject(int1).ClassName); keeps showing TButton so I think you're right. I'm a bit lost at the moment though
kjack
@Gamecat, can you elaborate? Sender is always passed in eax. int1 get's its initial value from [ebp-4]. As for "this" code, it always resolves to Sender. Am I missing something?
Lieven
It may not always resolve to Sender, given enough changes to VCL code.
mghie
@mghie. Perhaps true but I doubt codegear will change the ButtonClick eventhandler's parameter list in the near future.
Lieven
No, Lieven, Sender is not passed in EAX. It's passed in EDX. Self is passed in EAX. Int1 might come from the stack, or it might come from a register; that's the compilers decision. And the act of calling the ClassName function may change the generated code to something else.
Rob Kennedy
@Rob, EDX, you're right offcourse, my mistake. For the op's original question with that particular piece of code and always using the same compiler at the exact same hour of the exact same day (you'll get the picture :), I'm pretty sure int1 will always get initialized to [ebp-4] being Self.
Lieven
Cool. I learn something new every day. Probably best not to use it in my code though! :)
Toby Allen
+1  A: 

Do you want it to be the same? If so, you could use a local const:

procedure TForm1.Button1Click(Sender: TObject);
const
  i: integer = 0;
begin
  i := i + 1;
  edit1.text := intToStr(i);
end;

You have to assure 'Assignable typed constants' is switched on though.

Vegar
A: 

Initializng variables may or may not be done by the memory manager.

I would consider it good practice of any memory manager to initialize all variables to zero (0x0000). Thats done in .Net as well.

oɔɯǝɹ