Related
Wanted to check with you experts if there are any drawbacks in this funtion. Will it work properly on the various Windows OS ? I am using Delphi Seattle (32 and 64 bit exe's). I am using this instead of Findfirst for its speed.
function GetFileDetailsFromAttr(pFileName:WideString):int64;
var
wfad: TWin32FileAttributeData;
wSize:LARGE_INTEGER ;
begin
Result:=0 ;
if not GetFileAttributesEx(pwidechar(pFileName), GetFileExInfoStandard,#wfad) then
exit;
wSize.HighPart:=wfad.nFileSizeHigh ;
wSize.LowPart:=wfad.nFileSizeLow ;
result:=wsize.QuadPart ;
end;
The typical googled samples shown with this command does not work for filesize > 9GB
function GetFileAttributesEx():Int64 using
begin
...
result:=((&wfad.nFileSizeHigh) or (&wfad.nFileSizeLow))
Code with variant record is correct.
But this code
result:=((&wfad.nFileSizeHigh) or (&wfad.nFileSizeLow))
is just wrong, result cannot overcome 32-bit border
Code from link in comment
result := Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32);
is wrong because it does not account how compiler works with 32 and 64-bit values. Look at the next example showing how to treat this situation properly (for value d, e):
var
a, b: DWord;
c, d, e: Int64;
wSize:LARGE_INTEGER ;
begin
a := 1;
b := 1;
c := Int64(a) or Int64(b shl 32);
d := Int64(a) or Int64(b) shl 32;
wSize.LowPart := a;
wSize.HighPart := b;
e := wsize.QuadPart;
Caption := Format('$%x $%x $%x', [c, d, e]);
Note that in the expression for c 32-bit value is shifted by 32 bits left and looses set bit, then zero transforms to 64-bit.
Unbound to how you get the filesize: it would even be faster if you'd use a type (manual) that exists for ~25 years already to assign the filesize directly to the function's result instead of using an intermediate variable:
Int64Rec(result).Hi:= wfad.nFileSizeHigh;
Int64Rec(result).Lo:= wfad.nFileSizeLow;
end;
In case this isn't obvious to anyone here's what the compilation looks like:
Above: the intermediate variable w: LARGE_INTEGER first gets assigned the two 32bit parts and then is assigned itself to the function's result. Cost: 10 instructions.
Above: the record Int64Rec is used to cast the function's result and assign both 32bit parts directly, without the need of any other variable. Cost: 6 instructions.
Environment used: Delphi 7.0 (Build 8.1), compiler version 15.0, Win32 executable, code optimization: on.
I have a math question which I try to solve with a Delphi function. I am trying to split up an integer into (nearly) equal pieces. Example:
You have 10 testcases and you want to execute them in 3 parts. So the best way to balance the workload would be to execute 3, 3, and 4 rather than 1, 1 and 8.
procedure calc_stream_numbers;
var div_res,rest:double;
total,streams:integer;
begin
total := 10;
streams := 3;
div_res := total DIV streams;
rest := total MOD streams;
showmessage('Items: ' +IntToStr(total)+#13#10+
'Streams: '+IntToStr(streams)+#13#10+
'Result: '+FloatToStr(div_res)+
'Rest: '+FloatToStr(rest));
end;
This gives me the division result of 10 / 3 = 3 (Rest 1) So I could execute 3 x 3 Testcases which makes 9 in total. To execute all 10 of them, I have to add the remaining one to any of the 3 streams. But If you have a more complicated example..
I am sure there is a mathematical expression for this but I must admit, I have noe clue. Maybe you are able to enlighten me how such a division is called and if there is any built-in delphi function.
Here it is: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
One dimension is the amount of workloads, another one is amount of workers
Well, 2D Graphics is targeted at being eye-candy, so at fairly distributing "long" segments throughout "short" ones.
In your case we can bias all the "long" ones to one end.
function SplitIntoEqualParts(Total, Count: Integer): TArray<Integer>;
var
i: Integer;
delta, delta1, extra: Word;
begin
Assert( Count < High(Word) ); // RTL DivMod limitation
SetLength(Result, Count);
DivMod(Total, Count, delta, extra);
// one division operation is 2 times faster than two separate operations :-D
delta1 := Succ(delta);
for i := 0 to extra-1 do
Result[i] := delta1;
for i := extra to Count-1 do
Result[i] := delta;
end;
http://delphi.about.com/library/rtl/blrtlDivMod.htm
Is there a DivMod that is *not* Limited to Words (<=65535)?
How do I implement an efficient 32 bit DivMod in 64 bit code
You can code it like this:
function BalancedWorkload(Total, Count: Integer): TArray<Integer>;
var
i: Integer;
begin
SetLength(Result, Count);
for i := 0 to Count-1 do
begin
Result[i] := Total div Count;
dec(Total, Result[i]);
dec(Count);
end;
end;
You have Total units to split up into Count parts. We start with Total div Count for the first item. Then we have Total - (Total div Count) units left which need to be split into Count - 1 parts. Continue in this way until there's nothing left to do.
This algorithm could easily be written in a recursive way, but the iterative approach above somehow seems simpler to me.
Your entire approach assumes that you can predict ahead of time how long each task will take. But what if, for whatever reason, some tasks take longer than others? Then your approach will not be efficient. A better approach is to have a threadsafe queue of tasks. Let each consumer pull tasks off the queue and process them until there are no tasks left.
This function will calculate an array of integer containing the balanced workloads for each testcase.
function SplitInteger(Value: Integer; divisor: Integer): TArray<Integer>;
var
d,i : Integer;
begin
SetLength(Result,divisor);
if (divisor = 0) then
Exit;
d := Value div divisor;
for i := 0 to divisor - 1 do
Result[i] := d;
// Fill up with the remains
for i := 0 to (value mod divisor) - 1 do
Inc(Result[i]);
end;
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 am building a single application to Calculate Min Max and Avg of Values in a List.
It is actually Temperatures. So I think I am Almost correct but there are 2 Errors.
var
Count, Average, Sum,i, Max, Min, K : Integer;
Temperatures : Array of Integer;
NoItems : Double;
begin
Count := 0;
Sum := 0;
Max := 0;
Min := 0;
Average := 0;
Count := lstTemp.Items.Count;
{Calculate Sum of Values in the list}
for i := 0 to Count - 1 do
Sum := Sum + StrToInt(lstTemp.Items[i]);
{Calculate Min and Max}
SetLength(Temperatures,Count);
for K:=0 to Count-1 do
Temperatures[K] := lstTemp.Items[K];
if (Temperatures[K] > Max) then
Max := Temperatures[K];
if (Temperatures[K] < Min) then
Min := Temperatures[K];
{Calculate Average}
Average := Sum / Count;
edtAvg.Text:=IntToStr(Average); //Display Average
edtAvg.Text:=IntToStr(Min); //Display Minimum Temp.
edtAvg.Text:=IntToStr(Max); //Display Maximum Temp.
end;
So the 2 Errors are
Error: Incompatible types: got "AnsiString" expected "LongInt"
This is for Average := Sum / Count;
Error: Incompatible types: got "Set Of Byte" expected "Double"
This Error is for Temperatures[K] := lstTemp.Items[K];
Any Ideas how to solve this?
Sum and Count are both Integers so I dont know why it shouldnt work!
Thanks
There is a number of problems. First, when you write
for K:=0 to Count-1 do
Temperatures[K] := lstTemp.Items[K];
if (Temperatures[K] > Max) then
Max := Temperatures[K];
if (Temperatures[K] < Min) then
Min := Temperatures[K];
you actually do
for K:=0 to Count-1 do
Temperatures[K] := lstTemp.Items[K];
if (Temperatures[K] > Max) then
Max := Temperatures[K];
if (Temperatures[K] < Min) then
Min := Temperatures[K];
which is nonsense. You want all these lines to be part of the for loop:
for K:=0 to Count-1 do
begin
Temperatures[K] := lstTemp.Items[K];
if (Temperatures[K] > Max) then
Max := Temperatures[K];
if (Temperatures[K] < Min) then
Min := Temperatures[K];
end;
Second, in order for this algorithm to work, the initial value of Min (Max) needs to be larger (smaller) than the values in the list. This might work for Max := 0, but probably not for Min := 0. You need to set Min to a very large value before you run the loop, obviously. The best value you can use is the highest-possible signed 32-bit integer value, that is, 2^31 - 1, which is the value of the MaxInt constant.
Third,
Temperatures[K] := lstTemp.Items[K];
is probably wrong. Temperatures is an array of integers, while lstTemp.Items[K] is a string (at least according to StrToInt(lstTemp.Items[i])), so you need
Temperatures[K] := StrToInt(lstTemp.Items[K]);
Fourth, you declare Average as an integer, but it needs to be a floating-point number (obviously), like real or double.
Fifth,
edtAvg.Text:=IntToStr(Average); //Display Average
edtAvg.Text:=IntToStr(Min); //Display Minimum Temp.
edtAvg.Text:=IntToStr(Max); //Display Maximum Temp.
is not techncally incorrect, but will most likely not do what you want.
Sixth, although not an error, there is no need for you to initialise Count and Average to 0. Finally, you only need a single for loop.
There is (at least in Delphi 2010 - unit Math) one function that will calculate the mean and standard deviation in one step and functions that return the minimum and maximum values in an array. BTW, Mean is the arithmetic average of all the values and is the correct term. (I copied an example that I am working on and modified to your example - it compiles at least):
type
a = array of double;
var
Temperatures : a;
Average,stddev3, Max, Min : extended;
// Compiler insists on extended for these properties
begin
Max := Math.MaxValue(Temperatures);
Min := Math.MinValue(Temperatures);
Math.MeanAndStdDev(Temperatures ,Average,stddev3);
end;
For the maximum value in an array use (it takes an array of double and returns double):
function MaxValue(const Data: array of Double): Double;
For the minimum value use the corresponding:
function MinValue(const Data: array of Double): Double;
I agree that average cannot be an integer, but there are 2 similar functions for integer arrays:
function MinIntValue(const Data: array of Integer): Integer; and
function MaxIntValue(const Data: array of Integer): Integer;
0909EM's reply was very well done, but I have a few disagreements. First, I don't believe there's a need to set any sentinel value at all; simply use the first temperature value. Second, if we put a Begin and End around every single line If statement we'd approach COBOL-like levels of English verbosity. As it is, it's a crying shame this simple problem takes so much code. Third, I would not use StrToIntDef. Remember these lines from the Zen Of Python (I don't care if you don't know Python; everyone should memorize it, at least until we get an I Ching of Intersimone):
Errors should never pass silently.
Unless explicitly silenced.
If a user passes incorrect data into the temperature stats procedure, StrToIntDef is going to silently convert these values to zeroes, an unexpected and undesired behavior. The caller is going to get back answers that they assume are ok (because of no errors), yet will have incorrect values (especially the average). It is a far better thing to let the procedure blow up so testing will reveal the incorrect input.
I'd also replace the For loops with For...in. I banged this together:
program temps;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes, Generics.Collections, Math;
Var
someTemps : TStringList;
Procedure TempStats(temperatures : TStringList);
Var
temps : TList<Real>;
minTemp, maxTemp, sumTemps : Real;
numTemps : Integer;
tempStr : String;
temp : Real;
avgTemp : Real;
Begin
numTemps := temperatures.Count;
If numTemps > 0 then
Begin
temps := TList<Real>.Create;
For tempStr in temperatures Do
temps.Add(StrToFloat(tempStr));
minTemp := temps[0];
maxTemp := temps[0];
sumTemps := 0;
For temp in temps Do
Begin
minTemp := Min(minTemp, temp);
maxTemp := Max(maxTemp, temp);
sumTemps := sumTemps + temp;
End;
avgTemp := sumTemps / numTemps;
WriteLn(avgTemp:0:2);
WriteLn(minTemp:0:2);
WriteLn(maxTemp:0:2);
temps.Free;
End
Else
WriteLn('No temperatures passed.');
End;
Begin
someTemps := TStringList.Create;
someTemps.AddStrings(TArray<String>.Create('72', '93', '84', '76', '82'));
TempStats(someTemps);
ReadLn;
someTemps.Clear;
TempStats(someTemps);
someTemps.Free;
ReadLn;
end.
Firstly, Consider using StrToIntDef (String To Integer with a Default value) instead of StrToInt (String to Integer) this will yield the following...
value := StrToIntDef('Abcdef', 0); // value will be zero
vs
value := StrToInt('Abcdef'); // exception
But the question is do you want integers or floating point values for your temperatures? (eg 1 or 1.6?) If you want floating point values, maybe use StrToFloatDef...
Second, I've seen lots of grads that use Delphi make this mistake, try to always use begin and end, it'll help... because it makes it really clear what you are doing inside a if/for/while and what you intend to do outside..
for i := 0 to lstTemp.Items.Count - 1 do
begin
// Sum all the items in the list
Sum := Sum + StrToIntDef(lstTemp.Items[i], 0);
end;
Next up your array is a bit pointless, the SetLength and adding items bit is OK, but its not very functional, when you could just use the items in the list. All you need to do is hang onto the max and min values.
Then your last problem is that Average isn't going to be a whole integer, its going to have a fractional part. Eg. 5 divided by 2 is 2.5, not 2 and not 3. You could use trunc to return just the integer part, or change Average so that its a floating point number...
for K:=0 to lstTemp.Items.Count-1 do
begin
if (StrToIntDef(lstTemp.Items[K], 0) > Max) then
begin
Max := StrToIntDef(lstTemp.Items[K], 0);
end;
if (StrToIntDef(lstTemp.Items[K], 1000) < Min) then // note, really high number
begin
Min := StrToIntDef(lstTemp.Items[K], 1000);
end;
end;
{Calculate Average}
Average := Trunc(Sum / Count); // do you really want to trunc this? I suspect not.
if Min = 1000 then // just incase
begin
Min := 0;
end;
The final problem you will face is that your always setting the text of the same text box...
edtAvg.Text:=IntToStr(Average); //Display Average
edtMin.Text:=IntToStr(Min); //Display Minimum Temp. (I assume this is supposed to be edtMin)
edtMax.Text:=IntToStr(Max); //Display Maximum Temp. (I assume this is supposed to be edtMax)
I suppose the final improvement I'd make is noticing that you only need one for loop...
for K:=0 to lstTemp.Items.Count-1 do
begin
// Sum all the items in the list
Sum := Sum + StrToIntDef(lstTemp.Items[K], 0);
if (StrToIntDef(lstTemp.Items[K], Low(Integer)) > Max) then // A really low value
begin
Max := StrToIntDef(lstTemp.Items[K], Low(Integer));
end;
if (StrToIntDef(lstTemp.Items[K], High(Integer)) < Min) then // A really high value
begin
Min := StrToIntDef(lstTemp.Items[K], High(Integer));
end;
end;
The most important idea on how to solve this is to read your error messages properly. On a previous question you commented: "the error was saying it is an overloaded function or something". That attitude is not going to help you understand the problem. You need to read the error messages properly.
In this question you give the following description of your errors:
So the 2 Errors are Error: Incompatible types: got "AnsiString" expected "LongInt" This is for Average := Sum / Count; Error: Incompatible types: got "Set Of Byte" expected "Double" This Error is for Temperatures[K] := lstTemp.Items[K];
However, the description does not correspond to the errors you should be seeing based on the code provided.
It looks like you didn't read your errors, and just blindly started making changes in the hopes you would accidentally do something right. Because you didn't read the errors, you didn't notice that they changed. So when you came to us looking for help, you provided old errors with new code or vice-versa.
If you had actually read your error messages properly, you might have been able to solve the problem yourself. At the least, you would have been able to ask a better question with a description that actually matched the code.
Average := Sum / Count;
Average, Sum and Count are all declared as Integer. The error message you should be getting is: "Incompatible types: Integer and Extended".
If you read the error message, it should give you a clue to read up on Integer and Extended.
The problem here is that, in maths, division produces a Rational number. And correspondingly the result of a division operation in a program is not an Integer. So you need to declare Average as either Double or Extended.
Temperatures[K] := lstTemp.Items[K];
Temperatures is declared as an array of Integer. You haven't shown the declaration of lstTemp, but based on other code it's one of the standard Delphi Controls that has Items declared as TStrings. So the error message you should be getting is: "Incompatible types: Integer and string".
If you read the error message, it should give you a clue to do the same thing you did 5 lines earlier.
The reason for this error is that Delphi is a "strongly typed" language. The compiler tries to prevent you from making certain kinds of mistakes because it is much better catch them early. Imagine what might happen if one of the values in lstTemp were 'Hello'. That cannot be converted to an Integer; and would cause a "run-time" error in your program.
To fix this problem you need to tell the compiler: "I know the value is a string and could be any string, but I want you to convert it to an Integer". You do this by calling the StrToInt function. NOTE: You will still get a run time error if an invalid string is passed to the function, but by being forced to explicitly do the conversion, you can think about whether you want to do some pre-validation of your input data.
You asked about the errors reported by the compiler. That's just one kind of error you'll face when programming - and usually the easiest to resolve. You'll also encounter logic errors: where your program compiles successfully, but doesn't behave correctly. Andreas's excellent answer has covered those already, so I'll not repeat them.
However, I will give you some valuable advice. Once you've gotten over the hurdle of resolving compiler errors, and are able to do so easily - you need to as quickly as possible:
Get into the habit of testing your code thoroughly.
Learn how to use the integrated debugger.
Learn about its limitations.
Learn other debugging techniques: logging, profiling, pre- and post-condition checking.
Finally, as a response to alcalde's rant about there not being any simple functions to get Min, Max, Sum or Avg: I offer another possible implementation.
Basically the rant was about the fact that he'd far rather write something along the lines of:
begin
if (lstTemp.Count > 0) then
begin
edtMin.Text := lstTemp.Min;
edtMax.Text := lstTemp.Max;
edtAvg.Text := lstTemp.Average;
end
else
begin
ShowMessage('List is empty');
end;
end;
Obviously the above code won't compile, but with a little work we can achieve something similar.
He's perfectly right on two counts: (1) that this implementation would be cleaner, much easier to maintain and with less chance of errors. (2) Delphi doesn't provide a way to simply do that.
In fact, if you follow a top-down design approach, this might be your initial pseudo-code. You should be taught about top-down design, if not demand your money back. :)
The whole point behind the top-down-design approach is that you're looking for an ideal implementation. You're not worrying about what is/isn't there. If the current library and tools don't provide a Min function, you can write your own.
You are a programmer, you have the power!
I sometimes like to call this "wishful thinking programming". You're wishing if other things were in place, I could implement the functionality much more easily like "this". Then you go about making your wish come true.
Without further ado, here's the implementation. You will need to use the Math unit.
type
{ We will call existing functions that take TDoubleArray as input }
TDoubleArray = array of Double;
TStringsHelper = class(TStrings)
{ A useful class to help us convert TStrings into TDoubleArray }
public
class function Using(AStrings: TStrings): TStringsHelper;
function AsDoubleArray: TDoubleArray;
end;
{ TStringsHelper }
function TStringsHelper.AsDoubleArray: TDoubleArray;
var
LoopI: Integer;
begin
SetLength(Result, Count);
for LoopI := 0 to Count - 1 do
begin
Result[LoopI] := StrToFloat(Strings[LoopI]);
end;
end;
class function TStringsHelper.Using(AStrings: TStrings): TStringsHelper;
begin
Result := TStringsHelper(AStrings);
end;
var
LTemperatures: TDoubleArray;
begin
{ This code is almost the same as our "ideal" implementation }
if (lstTemp.Items.Count > 0) then
begin
LTemperatures := TStringsHelper.Using(lstTemp.Items).AsDoubleArray;
edtMin.Text := FloatToStr(MinValue(LTemperatures));
edtMin.Text := FloatToStr(MaxValue(LTemperatures));
edtMin.Text := FloatToStr(Mean(LTemperatures));
end
else
begin
ShowMessage('List is empty');
end;
end;
What values are in lstTemp.Items[i]?
I suppose the values are integers (without floating points), because you are using IntToStr.
Average cannot be an integer. Integer is a number (4 bytes) without a floating point. A simple numbers, such as 2,3,50,1500, -100
Assume that Sum = 100, and the Count = 3.
What Average will be?
So, you have to use float variable type, Double for example.
I hope it helps...
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)