So I always heard that class fields (heap based) were initialized, but stack based variables were not. I also heard that record members (also being stack based) were also not initialized. The compiler warns that local variables are not initialized ([DCC Warning] W1036 Variable 'x' might not have been initialized), but does not warn for record members. So I decided to run a test.
I always get 0 from Integers and false from Booleans for all record members.
I tried turning various compiler options (debugging, optimizations, etc.) on and off, but there was no difference. All my record members are being initialized.
What am I missing? I am on Delphi 2009 Update 2.
program TestInitialization;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TR = Record
Public
i1, i2, i3, i4, i5: Integer;
a: array[0..10] of Integer;
b1, b2, b3, b4, b5: Boolean;
s: String;
End;
var
r: TR;
x: Integer;
begin
try
WriteLn('Testing record. . . .');
WriteLn('i1 ',R.i1);
WriteLn('i2 ',R.i2);
WriteLn('i3 ',R.i3);
WriteLn('i4 ',R.i4);
WriteLn('i5 ',R.i5);
Writeln('S ',R.s);
Writeln('Booleans: ', R.b1, ' ', R.b2, ' ', R.b3, ' ', R.b4, ' ', R.b5);
Writeln('Array ');
for x := 0 to 10 do
Write(R.a[x], ' ');
WriteLn;
WriteLn('Done . . . .');
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
ReadLn;
end.
Output:
Testing record. . . .
i1 0
i2 0
i3 0
i4 0
i5 0
S
Booleans: FALSE FALSE FALSE FALSE FALSE
Array
0 0 0 0 0 0 0 0 0 0 0
Done . . . .
Global variables are zero-initialized. Variables used in the context of the main begin..end block of a program can be a special case; sometimes they are treated as local variables, particularly for-loop indexers. However, in your example, r is a global variable and allocated from the .bss section of the executable, which the Windows loader ensures is zero-filled.
Local variables are initialized as if they were passed to the Initialize routine. The Initialize routine uses runtime type-info (RTTI) to zero-out fields (recursively - if a field is of an array or record type) and arrays (recursively - if the element type is an array or a record) of a managed type, where a managed type is one of:
AnsiString
UnicodeString
WideString
an interface type (including method references)
dynamic array type
Variant
Allocations from the heap are not necessarily initialized; it depends on what mechanism was used to allocate memory. Allocations as part of instance object data are zero-filled by TObject.InitInstance. Allocations from AllocMem are zero-filled, while GetMem allocations are not zero-filled. Allocations from New are initialized as if they were passed to Initialize.
I always get 0 from Integers and false from Booleans for all record members.
I tried turning various compiler options (debugging, optimizations, etc.) on and off, but there was no difference. All my record members are being initialized.
What am I missing?
Well, apart from your test using global instead of local variables: the important thing that you are missing is the distinction between variables that coincidentally appear to be initialised, and variables that actally are initialised.
BTW: This is the reason programmers who don't check their warnings make the common mistake of assuming their poorly written code is behaving correctly when the few tests they do; happen to have 0 and False defaults.... Want To Buy: random initialisation of local variables for debug builds.
Consider the following variation on your test code:
program LocalVarInit;
{$APPTYPE CONSOLE}
procedure DoTest;
var
I, J, K, L, M, N: Integer;
S: string;
begin
Writeln('Test default values');
Writeln('Numbers: ', I:10, J:10, K:10, L:10, M:10, N:10);
Writeln('S: ', S);
I := I + 1;
J := J + 2;
K := K + 3;
L := L + 5;
M := M + 8;
N := N + 13;
S := 'Hello';
Writeln('Test modified values');
Writeln('Numbers: ', I:10, J:10, K:10, L:10, M:10, N:10);
Writeln('S: ', S);
Writeln('');
Writeln('');
end;
begin
DoTest;
DoTest;
Readln;
end.
With the following sample output:
Test default values
Numbers: 4212344 1638280 4239640 4239632 0 0
S:
Test modified values
Numbers: 4212345 1638282 4239643 4239637 8 13 //Local vars on stack at end of first call to DoTest
S: Hello
Test default values
Numbers: 4212345 1638282 4239643 4239637 8 13 //And the values are still there on the next call
S:
Test modified values
Numbers: 4212346 1638284 4239646 4239642 16 26
S: Hello
Notes
The example works best if you compile with optimisation off. Otherwise, if you have optimisation on:
Some local vars will be manipulated in CPU registers.
And if you view the CPU stack while stepping through the code you'll note for example that I := I + 1 doesn't even modify the stack. So obviously the change cannot be carried through.
You could experiment with different calling conventions to see how that affects things.
You can also test the effect of setting the local vars to zero instead of incrementing them.
This illustrates how you are entirely dependent on what found its way onto the stack before your method was called.
Note that in the example code you provided, the record is actually a global variable, so it will be completely initialized. If you move all that code to a function, it will be a local variable, and so, per the rules given by Barry Kelly, only its string field will be initialized (to '').
I have a similar situation, and thought the same, but when I add other variables used before the record, the values become garbage, so before I use my record I had to initialize using
FillChar(MyRecord, SizeOf(MyRecord), #0)
Related
I'm working with Delphi and Assembly, so, i had a problem. I used a instruction(RDTSC) in Assembly of getting a 64-bits read time-stamp, the instruction put the numbers separately in two registers EAX and EDX. But it's ok, i get it with Delphi Integer variables. But now, i need to join those variables in 1 of 64-bits. It's like:
Var1 = 46523
var2 = 1236
So i need to put it into one variable like:
Var3 = 465231236
it's like a StrCat, but i'm don't know how to do it. Somebody can help me?
You certainly don't want to concatenate the decimal string representations of the two values. That is not the way you are expected to combine the two 32 bit values returned from RTDSC into a 64 bit value.
Combining 46523 and 1236 should not yield 465231236. That is the wrong answer. Instead, you want to take the high order 32 bits, and put them alongside the low order 32 bits.
You are combining $0000B5BB and $00004D4. The correct answer is either $0000B5BB00004D4 or $00004D40000B5BB, depending on which of the two values are the high and low order parts.
Implement this in code, for instance, using Int64Rec:
var
Value: UInt64;
...
Int64Rec(Value).Lo := Lo;
Int64Rec(Value).Hi := Hi;
where Lo and Hi are the low and high 32 bit values returned by RTDSC.
So, bits 0 to 31 are set to the value of Lo, and bits 32 to 63 are set to the value of Hi.
Or it can be written using bitwise operations:
Value := (UInt64(Hi) shl 32) or UInt64(Lo);
If all you need to do is read the time stamp counter, then you don't need to do any of this though. You can implement the function like this:
function TimeStampCounter: UInt64;
asm
RDTSC
end;
The register calling convention requires that a 64 bit value return value is passed back to the caller in EDX:EAX. Since the RDTSC places the values in those exact registers (not a coincidence by the way), you have nothing more to do.
All of this said, rather than using the time stamp counter, it is usually preferable to use the performance counter, which is wrapped by TStopWatch from System.Diagnostics.
The simple way is to use a record
type
TMyTimestamp = record
case Boolean of
true:
( Value: Int64 );
false:
( Value1: Integer; Value2: Integer );
end;
and you can store/read each value as you like
var
ts: TMyTimestamp;
begin
ts.Value1 := 46523;
ts.Value2 := 1236;
WriteLn( ts.Value ); // -> 5308579624379
ts.Value := 5308579624379;
WriteLn( ts.Value1 ); // -> 46523
WriteLn( ts.Value2 ); // -> 1236
end;
see: Docwiki: Variant Parts in Records
I'm trying to change the value of a TNotifyEvent, same I do with integers with a pointer to it's value. But, when I try to do it to TNotifyEvent I get an exception (access violation). How can I do this ?
My goal is change the value of an external variable. Here is a code to explain here I get the error:
procedure TForm11.Button3Click(Sender: TObject);
var
LInteger: integer;
LPinteger: ^Integer;
LPNotify: ^TNotifyEvent;
LNotify: TNotifyEvent;
begin
LInteger := 10;
LPinteger := #LInteger;
LPinteger^ := 20; //It's ok, it make both variables with the same value
Caption := Format('Pointer: %d | Value: %d', [LPinteger^, LInteger]);
LNotify := Button3Click;
LPNotify := #LNotify;
LPNotify^ := nil; //Here I get the error
Caption := Format('Pointer: %d | Value: %d', [Integer(LPNotify), Integer(#LNotify)]);
end;
Tks
There is a different treatment of the # operator for variables of procedural type. The documentation says:
In some situations it is less clear how a procedural variable should
be interpreted. Consider the statement:
if F = MyFunction then ...;
In this case, the occurrence of F results in a function call; the
compiler calls the function pointed to by F, then calls the function
MyFunction, then compares the results. The rule is that whenever a
procedural variable occurs within an expression, it represents a call
to the referenced procedure or function. In a case where F references
a procedure (which doesn't return a value), or where F references a
function that requires parameters, the previous statement causes a
compilation error. To compare the procedural value of F with
MyFunction, use:
if #F = #MyFunction then ...;
#F converts F into an untyped pointer variable that contains an
address, and #MyFunction returns the address of MyFunction.
To get the memory address of a procedural variable (rather than the
address stored in it), use ##. For example, ##F returns the address of
F.
This is your scenario. Instead of
LPNotify := #LNotify;
you need
LPNotify := ##LNotify;
If you compiled with the typed address option enabled, then the compiler would have rejected LPNotify := #LNotify as a type mismatch. I can find no sound explanation for Embarcadero continuing with typed address defaulting to disabled.
The final line of your function should probably be
Caption := Format(
'Pointer: %d | Value: %d',
[Int64(#LNotify), Int64(TMethod(LPNotify^))]
);
I'm assuming that you use the 32 bit compiler for the Int64 casts.
I've seen some algorithms designed to append an elment at the end of a linked list here and browsing other website, then I wrote a small procedure that i believe it should append a given element at the end of the list but it doesn't seems to work.
My question here is, why it doesn't work?
I defined pointers and nodes as follows:
Pointer = ^Node;
Node = record
about : element;
next : Pointer;
end;
And the following procedure receives a linked list L and a q element that should be appended to the end of L
First I define the record that I'll insert afterwards
var INS : Poniter;
........
INS.about := q;
And the procedure goes as follows:
{temp := L} {I'll use this for my attempt below }
if L<>NIL then
begin
while L^.next<>NIL do
begin
L:= L^.next;
end;
L^.next := INS;
INS^.next := NIL;
{L:=temp;} {I'll explain this in my attempt below}
end
else
begin
L:= INS;
end;
I also have a small procedure that prints all the elements of the linked list
procedure displayElements(L : pointer);
begin
while L <> nil do
begin
writeln(L^.about);
L := L^.next;
end
end;
The problem: after i run de program it only displays the last two entries of the list.
Conjecture about the problem: I believe it only shows the last two because when i run the procedure displayElements the pointer L is already one element before NIL -because i used the second algorithm-.
Attempt to a solution: Alright, i think i need to put L back at the very first place so that when i use displayElements it get all the elements in the list. But how could I do that?, I tried what i commented above saving L in temp but it didn't work.
Any ideas?. Thanks.
Here's a very simple program which will do what you want. Maintaining a 'tail' pointer means that you don't have to traverse the list every time that you want to add a value. If this were your code, then you would be missing the 'tail:= tmp' line in Insert: without this, Display prints the first and last entries, but not those in the middle.
type
node = ^MyRec;
MyRec = record
value: integer;
next: node
end;
var
head, tail: node;
Procedure Insert (v: integer);
var
tmp: node;
begin
new (tmp);
tmp^.value:= v;
tmp^.next:= nil;
if head = nil
then head:= tmp
else tail^.next:= tmp;
tail:= tmp;
end;
Procedure Display;
var
tmp: node;
begin
tmp:= head;
while tmp <> nil do
begin
writeln (tmp^.value);
tmp:= tmp^.next
end;
end;
begin
head:= nil;
Insert (5);
Insert (10);
Display;
Insert (15);
Display;
readln
end.
[Edit]
Judging by your comments, some further explanation is required. [Professorial mode on] When I started programming some thirty years ago (OMSI Pascal on a PDP 11/70), linked lists and pointers appeared in every self-respecting program, but since the rise of Delphi in 1990, such complexities have been hidden and most programmers now never see a naked pointer.
Linked lists come in different formats: the simple and the complex. The simple types differ at the points of insertion and deletion: a stack inserts and deletes at the same end, a queue inserts at one end and deletes at the other, a list allows insertion and deletion at any place. More complex types are trees and graphs. Your question is asking about the implementation of a queue - insertion is always at the rear, deletion is at the front.
In order to implement this properly, we need two variables: 'head' points to the head of the queue and 'tail' points to the end of the queue. These variables are normal global variables; memory is allocated for them in the data segment of the program. A pointer is a simple variable whose value is the memory address of another variable. Initially, 'head' does not point to anything so its value is nil (think of this as 0).
Normally in textbooks, the construction of a queue is accompanied by little boxes showing how memory is allocated, but I don't know how to do that here and so the explanation will be a little wordy.
When the first insertion occurs, the memory manager in the run time system allocates 12 bytes from the heap and sets the value of the local variable 'tmp' to be the address of the first of those 12 bytes (this is 'new (tmp)'). Of those 12 bytes, the 'value' part is set to 5 and the 'next' part is set to nil. Then the program checks what the value of 'head' is: if it is nil, then the value (ie address of the memory block allocated above) is copied from 'tmp' to 'head'. If 'head' already points to something, then the value of 'tmp' is copied to 'tail^.next' (which previously would have been nil). Then the value of 'tmp' is copied to the tail variable. Thus 'head' always points to the beginning of the queue and does not change whereas 'tail' points to the end of the queue and changes every time a new node is inserted.
Let's do some debugging:
When the program starts, head = nil, tail is undefined
After 'insert (5)', head = $870874, tail = $870874
After 'insert (10)', head = $870874, tail = $870880
After 'insert (15)', head = $870874, tail = $87088C
When displaying,
tmp:= head ......... tmp = $870874 (ie head)
tmp:= tmp^.next .... tmp = $870880
tmp:= tmp^.next .... tmp = $87088C
tmp:= tmp^.next .... tmp = nil
If your program has more than one queue, then you will need two variables for each queue, and 'Insert' will have to be changed in order to accept two parameters (which will be the head and tail of the given queue).
There is no need to write 'new (head)' and 'new (tail)' - doing so will cause the original pointers to the beginning and end of the queue to be lost.
[Professorial mode off] I hope that this explanation helps.
I have following problem.
When I try to run the code with arun file.obj (I have compiled with algol.exe file)
BEGIN
INTEGER PROCEDURE fun(tab,index,lower,upper);
INTEGER tab,index,lower,upper;
BEGIN
INTEGER t;
text (1, "Start");
t := 0;
FOR index := lower STEP 1 UNTIL upper DO
t := t + tab;
fun := t;
END;
INTEGER ARRAY t[1:10];
INTEGER i,result,lower,upper;
lower := 1;
upper := 10;
FOR i := 1 STEP 1 UNTIL 10 DO
t[i] := i;
i := 1;
result := fun(t[i],i,lower,upper);
END FINISH;
I am still getting error:
ERROR 3
ADD PBASE PROC LOC
07D4 0886 1 13
083A 0842 0 115
The compiler I use is "The Rogalgol Algol60" product of RHA (Minisystems) Ltd.
Error 3 means "3 Procedure called where the actual and the formal parameter types do not match."
But I do not understand why. The reason of error is t[i] (If I change to i - it is ok).
Someone knows what I am doing wrongly?
I compile the code on the dosbox (linux)
Problem is that the index of the integer array that you're passing to your procedure isn't the same as the integer that he's expecting. I can't remember what an integer array is full of, but I guess it isn't integers... Have to admit I never use them, but can't remember why. Possibly because of limitations like this. I stick to Real arrays and EBCDIC ones.
You can almost certainly fix it by defining a new integer, j; inserting "j := t[i];" before your invocation of 'fun'; then invoking 'fun' with 'j' rather than t[i].
BTW you may want to make the array (and the 'for' loop) zero-relative. ALGOL is mostly zero-relative and I think it may save memory if you go with the flow.
Let me know if this helps....
I know marking string parameters as const can make a huge performance difference, but what about ordinal types? Do I gain anything by making them const?
I've always used const parameters when handling strings, but never for Integer, Pointer, class instances, etc.
When using const I often have to create additional temporary variables, which replace the now write-protected parameters, so I'm wondering: Do I gain anything from marking ordinal parameters as const?
You need to understand the reason, to avoid "cargo-cult programming." Marking strings as const makes a performance difference because you no longer need to use an interlocked increment and decrement of the refcount on the string, an operation that actually becomes more expensive, not less, as time goes by because more cores means more work that has to be done to keep atomic operations in sync. This is safe to do since the compiler enforces the "this variable will not be changed" constraint.
For ordinals, which are usually 4 bytes or less, there's no performance gain to be had. Using const as optimization only works when you're using value types that are larger than 4 bytes, such as arrays or records, or reference-counted types such as strings and interfaces.
However, there's another important advantage: code readability. If you pass something as const and it makes no difference whatsoever to the compiler, it can still make a difference to you, since you can read the code and see that the intention of it was to have this not be modified. That can be significant if you haven't seen the code before (someone else wrote it) or if you're coming back to it after a long time and don't remember exactly what you were thinking when you originally wrote it.
You can't accidentally treat them like var parameters and have your code compile. So it makes your intentions clear.
Declaring ordinal types const makes no difference because they are copied anyway (call-by-value), so any changes to the variable do not affect the original variable.
procedure Foo (Val : Integer)
begin
Val := 2;
end;
...
SomeVar := 3;
Foo (SomeVar);
Assert (SomeVar = 3);
IMHO declaring ordinal types const makes no sense and as you say requires you to introduce local variables often.
It depends on how complex is your routine and how it is used. If it is used many places and required that the value stay the same, declare it as "const" to make it cleared and safe. For string type, there was a bug (for Delphi 7 as I stump on it) that causes memory corruption if declare as "const". Below is sample codes
type
TFoo = class
private
FStr: string;
public
procedure DoFoo(const AStr: string);
begin
FStr := AStr; //the trouble code 1
......
end;
procedure DoFoo2;
begin
.....
DoFoo(FStr); //the trouble code 2
end;
end;
There's a huge speed improvement using Const with strings:
function test(k: string): string;
begin
Result := k;
end;
function test2(Const k: string): string;
begin
Result := k;
end;
function test3(Var k: string): string;
begin
Result := k;
end;
procedure TForm1.Button1Click(Sender: TObject);
Var a: Integer;
s,b: string;
x: Int64;
begin
s := 'jkdfjklf lkjj3i2ej39ijkl jkl2eje23 io3je32 e832 eu283 89389e3jio3 j938j 839 d983j9';
PerfTimerInit;
for a := 1 to 10000000 do
b := test(s);
x := PerfTimerStopMS;
Memo1.Lines.Add('default: '+x.ToString);
PerfTimerInit;
for a := 1 to 10000000 do
b := test2(s);
x := PerfTimerStopMS;
Memo1.Lines.Add('const: '+x.ToString);
PerfTimerInit;
for a := 1 to 10000000 do
b := test3(s);
x := PerfTimerStopMS;
Memo1.Lines.Add('var: '+x.ToString);
end;
default: 443 const: 320 var: 325
default: 444 const: 303 var: 310
default: 444 const: 302 var: 305
Same with Integers:
default: 142 const: 13 var: 14
Interestingly though, in 64-bit there seems to be almost no difference with strings (default mode is only a bit slower than Const):
default: 352 const: 313 var: 314