A record consider it as a group of data.
The contained data may have different datatypes of data.

A simple record can be defined like below :
//simple record
type
  TPerson = record
    Name : string;
    Surname : string;
    Age  : integer;
  end;
A person in this case has a name a surname and an age.
Person is the container of the three attributes.
Records are fixed in size.

Now in order to access in a variable record we can do it with two methods (using a dot or the keyword with) shown both below :
procedure TForm1.Button1Click(Sender: TObject);
var John, Nick  : TPerson;    

begin

  //first way of access
  John.Name:='John';
  John.Surname:='Black';
  John.Age:=54;
  PrintPerson(John);

  //second way of access
  with Nick do begin
    Name:='Nick';
    Surname:='TheGreek';
    Age:=50;
  end;
  PrintPerson(Nick);   
  
end;
The PrintPerson is a procedure like this :
procedure PrintPerson(P:TPerson);
begin
  Form1.Memo1.Lines.Add('Name: ' + P.Name);
  Form1.Memo1.Lines.Add('Surname: ' + P.Surname);
  Form1.Memo1.Lines.Add('Age: ' + IntToStr(P.Age));
  Form1.Memo1.Lines.Add('----------------');
end;   
A record can hold also enumerated datatypes
//enumerations
type
   TSpecies = ( sDog, sCat, sHorse, sElephant );
   
   
//record with enumeration
type
  TAnimal = record
    Species : TSpecies;
    Color : TColor;
    Name : string;
    Age  : integer;
  end; 
Notice that TColor is a datatype defined at Graphics unit.
TColor is a RGB (3 x 8bit) datatype. In TColor alpha channel (transparency) is not included.
The value $FF0000 represents pure blue(B), $00FF00 is pure green(G), and $0000FF is pure red(R).
procedure TForm1.Button1Click(Sender: TObject);
var 
    Dambo : TAnimal;

begin

  with Dambo do begin
    Species := sElephant;
    Color := clGray; //same as : $808080
    Name := 'Dambo';
    Age := 2;
  end;
  PrintAnimal(Dambo);  

end;  
Again the PrintAnimal procedure is shown below :
procedure PrintAnimal(A:TAnimal);
begin
  Form1.Memo1.Lines.Add('Species: ' + GetEnumName(TypeInfo(TSpecies), Ord(A.Species)) );
  Form1.Memo1.Lines.Add('Color: ' + IntToHex(A.Color,8));
  Form1.Memo1.Lines.Add('Name: ' + A.name);
  Form1.Memo1.Lines.Add('Age: ' + IntToStr(A.age));
  Form1.Memo1.Lines.Add('----------------');
end; 
A record can also contain another record or records.
A domestic animal for example is an animal with an owner (person).
//record of record(s)
//a domestic animal is an animal that has an owner of type TPerson
type
  TDomesticAnimal = record
    Animal : TAnimal;
    Owner : TPerson;
    PedigreeNumber : integer;
  end; 
A domestic animal may have a "new" TPerson (Dada example) or an existing TPerson (Milo example)
procedure TForm1.Button1Click(Sender: TObject);
var John  : TPerson;
    Dada, Milo : TDomesticAnimal;  
		
begin

  with Dada do begin
    Animal.Species:=sDog;
    Animal.Color:=clWhite;
    Animal.Name:='Dada';
    Animal.Age:=4;
    Owner.name:='Mario';
    Owner.surname:='Rossi';
    Owner.age:=19;
    PedigreeNumber:=1254;
  end;
  PrintDomesticAnimal(Dada);

  John.Name:='John';
  John.Surname:='Black';
  John.Age:=54;
  
  with Milo do begin
    Animal.Species:=sDog;
    Animal.Color:=clBlack;
    Animal.Name:='Milo';
    Animal.Age:=9;
    Owner:=John;
    PedigreeNumber:=1255;
  end;
  PrintDomesticAnimal(Milo);   
Again the PrintDomesticAnimal procedure this time is shown below :
procedure PrintDomesticAnimal(DA:TDomesticAnimal);
begin
  Form1.Memo1.Lines.Add('Animal Species: ' + 
                        GetEnumName(TypeInfo(TSpecies), Ord(DA.Animal.Species)) );
  Form1.Memo1.Lines.Add('Animal Color: ' + IntToHex(DA.Animal.Color,8));
  Form1.Memo1.Lines.Add('Animal Name: ' + DA.Animal.name);
  Form1.Memo1.Lines.Add('Animal Age: ' + IntToStr(DA.Animal.age));
  Form1.Memo1.Lines.Add('Animal Pedigree: ' + IntToStr(DA.PedigreeNumber));
  Form1.Memo1.Lines.Add('Owner Name: ' + DA.Owner.name);
  Form1.Memo1.Lines.Add('Owner Surname: ' + DA.Owner.surname);
  Form1.Memo1.Lines.Add('Owner Age: ' + IntToStr(DA.Owner.age));
  Form1.Memo1.Lines.Add('----------------');
end;   
Records are stored in the stack memory.
What does this means ?
Stack is a region of computer memory that stores temporary variables created by each procedure and function.
In a more technical way is a LIFO (last in first out) order pile.
Every time a function declares a new local variable, it is "pushed" (added) onto the stack.
The last element can be also removed (pop).
So all the local variables seen till now are located in the stack.

Notice that global variables are stored in the global memory and not in the stack memory.
The global memory is reserved by your application when the program starts and remains allocated until your program ends.

More about memory allocation we will see in a later article.
For now keep in mind that local variables allocate memory from the stack.

Pascal aligns record fields on natural boundaries to improve performance.
Normally records have their elements aligned to multiples of (bytes) :
1 for byte
2 for word, smallint
4 for longword, integer, cardinal, single
8 for qword, Int64, double
These alignments [padding] ensure optimal access performance.

For example :
// Declare a simple (unpacked) record
type
  TSimpleRecord = Record
    ByteOne: Byte;  //1 byte; Total=1 [multiple of 1] -> Total=1
    ByteTwo: Byte;  //1 byte; Total=2 [multiple of 1] -> Total=2
    ByteThree: Byte;//1 byte; Total=3 [multiple of 1] -> Total=3
    ByteFour: Byte; //1 byte; Total=4 [multiple of 1] -> Total=4
    ByteFive: Byte; //1 byte; Total=5 [multiple of 1] -> Total=5
    WordOne: Word;  //2 bytes; Total=7 [multiple of 2] -> Total=8 [padded]
    IntOne: Integer;//4 bytes; Total=12 [multiple of 4] -> Total=12
    RealOne: double;//8 bytes; Total=20 [multiple of 8] -> Total=24 [padded]
  end;
In order to find the storage byte size of a type or variable we can use the function SizeOf.
Notice how the SimpleRecord allocates 24 bytes of stack memory instead of 19 bytes.
This fact is called padding alignment.
procedure TForm1.Button1Click(Sender: TObject);
var SimpleRecord : TSimpleRecord;   
  
  begin
    Form1.Memo1.Lines.Add('Default record size = '+IntToStr(SizeOf(SimpleRecord)));
    //this prints 24	
  end;
Packed records are records with no alignment padding.
The Packed keyword compress the data into the smallest byte storage but lowers the performance.

For example :
{
The Packed keyword minimises the storage taken up by the defined record.
The default padding is removed.
The Packed keyword compress the data into the smallest byte storage but lowers the performance.
}

// Declare a packed record
type
  TPackedRecord = Packed Record
    ByteOne: Byte;  //1 byte; Total=1
    ByteTwo: Byte;  //1 byte; Total=2
    ByteThree: Byte;//1 byte; Total=3
    ByteFour: Byte; //1 byte; Total=4
    ByteFive: Byte; //1 byte; Total=5
    WordOne: Word;  //2 bytes; Total=7
    IntOne: Integer;//4 bytes; Total=11
    RealOne: double;//8 bytes; Total=19
  end;      
Now :
procedure TForm1.Button1Click(Sender: TObject);
var PackedRecord : TPackedRecord;  
  
  begin
    Form1.Memo1.Lines.Add('Packed record size = '+IntToStr(SizeOf(PackedRecord))); 
    //this prints 19	
  end;
Bitpacked records is like packed records but in this case, the compiler will attempt to align ordinal types on bit boundaries.
Bitpacked records are excellent for rappresenting a bit to bit byte contruction.
We can define the follow subrange :
type
   TBit = 0 .. 1;  //subrange    
Now we can now define Bitpacked records like below :
type
  TEightBitByte = Bitpacked record //if it was a packed record it would be 8 bytes !
    Bit0 : TBit; //1 byte -> but can be represented by 1 bit since it is a bitpacked record
    Bit1 : TBit;
    Bit2 : TBit;
    Bit3 : TBit;
    Bit4 : TBit;
    Bit5 : TBit;
    Bit6 : TBit;
    Bit7 : TBit;
  end;

type
  THalfByte = Bitpacked record //if it was packed record it would be 4 bytes !
    //Half byte is also called nibble
    Bit0 : TBit; //1 byte -> can be represented by 1 bit
    Bit1 : TBit;
    Bit2 : TBit;
    Bit3 : TBit; //Total 4 bits -> 1 byte with padding
  end;      
In order to measure the allocated memory in bits of this datatype we are using the BitSizeOf function.
procedure TForm1.Button1Click(Sender: TObject);
var 
  EightBitByte  : TEightBitByte;
  HalfByte : THalfByte;
                               
							
begin
  Form1.Memo1.Lines.Add('The size in bits of EightBitByte = ' + 
                         IntToStr(BitSizeOf(EightBitByte)) +
                        ', in bytes = ' + IntToStr(SizeOf(EightBitByte)));
  Form1.Memo1.Lines.Add('The size in bits of Bit0 = ' + 
                        IntToStr(BitSizeOf(EightBitByte.Bit0)));

  EightBitByte.Bit0:=1;
  Form1.Memo1.Lines.Add('EightBitByte.Bit0 = ' +  IntToStr(EightBitByte.Bit0));


  Form1.Memo1.Lines.Add('The size in bits of HalfByte = ' + 
                        IntToStr(BitSizeOf(HalfByte)) +
                        ', in bytes = ' + IntToStr(SizeOf(HalfByte))); 

end;						
Lastly we can define variant records.
Variant records are useful when some fields of the record definition may vary.
The variant part (case) must be last in the record.
Below we can see two examples. In the first case we are using a simple chase of chars.
In the second example we are using a case of TShapeType enumerations defined before
//enumerations
type
   TShapeType = ( stSquare, stRectangle, stCircle );
   
//Variant Record . A variant records can also be a packed record.
type
  TShape = record
    name : string;
    color: TColor;
    //from here and below you cannot use dynamic types like strings
    //since inside variant records the compiler would not know which
    //of the tagged cases it should initialize/finalize.
    case typeFirstLetter : char of
       'S': (side : integer); //square
       'R': (x,y: integer); //rectangle
       'C': (radius : integer); //circle
  end;

type
  TShapeBetter = record
    name : string;
    color: TColor;
    case ShapeType : TShapeType of
       stSquare: (side : integer);
       stRectangle: (x,y: integer);
       stCircle: (radius : integer);
  end;
We can access the defined variant record like a normal record :
procedure TForm1.Button1Click(Sender: TObject);
var 
  ShapeBetter : TShapeBetter;

begin

  ShapeBetter.name:='Large square';
  ShapeBetter.color:=clBlue;
  ShapeBetter.ShapeType:=stSquare;
  ShapeBetter.side:=40;
  Form1.Memo1.Lines.Add(ShapeBetter.name + ' with size: ' +  IntToStr(ShapeBetter.side));

end;
That's all for now. In the next tutorial we will talk about arrays.
This demo example can be downloaded from here

Part 1: Introduction - Part 2: Hello World - Part 3: Data types, constants and variables - Part 4: Operators - Part 5: Decisions and Loops - Part 6: Procedures and Functions - Part 7: Custom datatypes - Part 8: Enumerations, subranges and sets - [[ Part 9: Records ]]


Copyright © TrustFm.net 1998-2024 - Made by TrustFm - All Rights Reserved Worldwide