Escolar Documentos
Profissional Documentos
Cultura Documentos
By Zarko Gajic
Delphi Expert
Before you start developing more sophisticated applications by using the RAD features of
Delphi, you should learn the basics of the Delphi Pascal language.
VARIABLE SCOPE
Delphi For Beginners:
Object Pascal Variable Scope
As mentioned in some of the previous articles understanding Object Pascal variable scope is
one of key elements in building applications with Delphi/Object Pascal.
Scope of Variables and Constants
The term scope refers to the availability of a variable or constant declared (or used) in one
part of a program to other parts of a program.
Unless we specify otherwise, changing the value of a variable named, let's say, SomeNumber
in one procedure (function) will not affect another variable with the same name in another
procedure (function).
Since Delphi requires us to declare variables, it's a lot harder to fall into the trap caused by
side effects accidentally. As we know by now, every variable used in some procedure has to
be declared in the var section of the event handler.
In general, we declare a variable where we want to use it. For example, if we want to use a
variable in an event handler, we declare the variable within the event handler.
Local Scope (+ variable declaration and initialization)
Most variables have local scope, which means that the variable is visible only within the
code block in which it is declared (usually: Event Handler for some method). In particular, an
event handler will not normally have access to the value of a variable in another event
handler.
If we want to be sure a variable is local within an event handler, we have to declare it in the
var section inside the event handler. Since we must declare a variable before we can use it, if
we can use a variable without declaring it locally, we know that there is a variable with
greater scope with the same name somewhere around project.
Let us look at the first example:
1. Start Delphi, this will give us (by default) new application with one blank form.
2. Double click somewhere on the form (to create OnCreate event handler)
3. Write down this code:
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage(FloatToStr(SomeNumber));
end;
4. If you try to run your project now, you will be prompted with: "Undeclared Identifier:
'SomeNumber'" error. This means that we haven't declared SomeNumber variable in our
project (note: entire project, not FormCreate event handler).
5. To declare SomeNumber variable as double type change your code to:
procedure TForm1.FormCreate(Sender: TObject);
var SomeNumber: double;
begin
ShowMessage(FloatToStr(SomeNumber));
end;
6. Run your project, message box will appear with some strange (value of the memory region
where variable is stored) number. Delphi will also give us "Variable 'SomeNumber' might not
have been initialized" warning. This means that, before using declared variable, it is a good
practice to initialize it (just to be sure). For that purpose add this line of code before
ShowMessage...
SomeNumber := 123.45;
7. Now, when you run your project message box will display 123,45 (no errors, no warnings).
Finally, we can see why local variables are called local...
8.Add one TButton component to form and double-click it to create Buttons OnClick event
handler. Add the following code (so that OnClick looks like):
Again we have "Undeclared Identifier: 'SomeNumber'" error. This is what local variables are
all about: even if we have declared (and initialized) SomeNumber variable in OnCreate event
handler of the form, SomeNumber is not accessible in the OnClick handler of TButtton. We
simply cannot use SomeNumber (with 123.45 value) in the OnClick (at least for now). This
means that SomeNumber from OnCreate and SomeNumber from OnClick are two different
variables (that can, of course, hold different values)
9. Don't close this project, jet. We will need it again...
10. Add the following line before ShowMessage in the OnClick event handler (we will need it
later, don't worry about this for now)
SomeNumber:=555.55;
2. Run your program. As you can see, we don't have "Undeclared Identifier: 'SomeNumber'"
error in OnClick handler of the TButton. When program starts message box will appear with
123.45 value. When you click on the Button1 message box will display 555.55; that's why we
need step 10 in the first example - we have initialized SomeNumber to 555.55 in the OnClick
event of the Button1.
What's this? We have two SomeNumber variables in our project and they both hold
different values.
Obviously, we have to be careful when assigning values to unit-level variables. Although we
can use the same variable (or constant) name for both local and unit-level variables, this is
not a good idea. Any var (or const) declaration contained in a procedure takes precedence
over global (unit-level) declarations. Duplicating the names makes the global variable
invisible to the procedure (Delphi doesn't tell us whether a global variable has been defined
with the same name as a local variable). That is why SomeNumber holds the 123,45 value in
the OnCreate event handler of the form (we cannot use global variable SomeNumber in the
OnCreate procedure)
Note 1: If you really have to use two variables with the same SomeNumber name (one global
and one local), you can access the global SomeNumber variable value in the forms OnCreate
procedure with the call to unit1. SomeNumber (unit1 is the name of the unit with the global
SomeNumber variable). That is, something like
unit1.SomeNumber:=444.44;
will change value of the global variable SomeNumber inside OnCreate event handler of the
form (remember that there is a SomeNumber variable local to this procedure which will stay
unchanged)
Note 2: As global SomeNumber is global to the unit we can access (more important: change)
its value from any other procedure inside this unit. However click to Button 1 will reset
SomeNumber value to 555.55. Better way to initialize global variables is inside initialization
section of the unit.
Global variables - program level scope
If we want to create true global variables (or/and constants) in a project, we have to place
the declaration in the interface section of the unit. Variables declared in the interface
section will be visible (accessible) to any unit which uses that unit.
For example, to change SomeNumber variable value that is declared in Unit1 from Unit2, use
this statement:
Unit1.SomeNumber:=999.99;
When Delphi invokes an event handler, the old values of local variables are wiped out. What
if we want to keep track of how many times a button has been clicked? We could have the
values persist by using a unit-level variable, but it is generally a good idea to reserve unitlevel variables only for sharing information. What we need are usually called static variables
or typed constants in Delphi.
Variable or constant?
Typed constants can be compared to initialized variables-variables whose values are defined
on entry to their block (usually event handler). Such a variable is initialized only when the
program starts running. After that, the value of a typed constant persists between successive
calls to their procedures.
Using typed constants is a very clean way of implementing automatically initialized variables.
To implement these variables without typed constants, we'll need to create an initialization
section that sets the value of each initialized variable.
Notice that every time you click on the button, forms caption increments steadily.
Now try the following code:
procedure TForm1.Button1Click(Sender: TObject) ;
var
clicks : Integer;
begin
Form1.Caption := IntToStr(clicks) ;
clicks := clicks + 1;
end;
We are now using uninitialized variable for the clicks counter. Notice that weird value in the
forms caption after you click on the button.
You have to agree that idea of modifiable constants sound a bit strange. In 32 bit versions of
Delphi Borland decided to discourage their use, but support them for Delphi 1 legacy code.
We can enable or disable Assignable typed constants on the Compiler page of the Project
Options dialog box.
If you've disabled Assignable typed constants for a given project, when you attempt to
compile previous code Delphi will give you 'Left side cannot be assigned to' error upon
compilation. You can, however, create assignable typed constant by declaring:
{$J+}
const clicks : Integer = 1;
{$J-}
Conclusion
It's up to you to decide whether you want typed constants to be assignable or not.
Important thing here is that besides ideal for counters, typed constants are ideal for making
components alternately visible or invisible, or we can use them for switching between any
Boolean properties. Typed constants can also be used inside TTimer's event handler to keep
track of how many times even has been triggered.
If you want some more beginners material check the rest of the Delphi For Beginners
programming topics.
begin
ShowMessage('Box: '+IntToStr(j)) ;
end;
end;
The value of a control variable (j), which is really just a counter, determines how many times
a for statement runs. The keyword for sets up a counter. In the preceding example, the
starting value for the counter is set to 1. The ending value is set to 5.
When the for statement begins running the counter variable is set to the starting value.
Delphi than checks whether the value for the counter is less than the ending value.
If the value is greater, nothing is done (program execution jumps to the line of code
immediately following the for loop code block). If the starting value is less than the ending
value, the body of the loop is executed (here: the message box is displayed). Finally, Delphi
adds 1 to the counter and starts the process again.
Sometimes it is necessary to count backward. The downto keyword specifies that the value
of a counter should be decremented by one each time the loop executes (it is not possible to
specify an increment / decrement other than one). An example of a for loop that counts
backward.
var j: integer;
begin
for j := 5 downto 1 do
begin
ShowMessage('T minus ' + IntToStr(j) + 'seconds') ;
end;
ShowMessage('For sequence executed!') ;
end;
Note: it's important that you never change the value of the control variable in the middle of
the loop. Doing so will cause errors.
if iNumber = 0 then
begin
ShowMessage('Zero value encountered!') ;
Exit; // exit from the current procedure
end;
//if iNumber is 0 the folowing
//code will never be executed
ShowMessage('Nobody likes 0, ha!') ;
end;
More often, we will want to process multiple statements if a condition is True or False.
var iNumber : Integer;
begin
//some value must be
//assigned to iNumber here!
if iNumber < 0 then
begin
//statements ...
ShowMessage('Your number is negative!') ;
//statements ...
end
else
begin
//statements ...
ShowMessage('Your number is positive or zero!') ;
//statements ...
end;
end;
Note: Each statement in the begin..end block ends with a semicolon. We cannot have a
semicolon before or after the else keyword. The if-then-else statement, is a single
statement, therefore we cannot place a semicolon in the middle of it.
An if statement can be quite complex. The condition can be turned into a series of conditions
(using the and, or and not Boolean operators), or the if statement can nest a second if
statement.
var
iNumber : Integer;
begin
if iNumber = 0 then
begin
ShowMessage('Zero number not allowed!') ;
exit;
end
else
//no need to use begin-end here
if iNumber < 0 then
ShowMessage('Your number is negative!')
else
ShowMessage('Your number is positive!') ;
end;
Note: When you write nested if statements choose a consistent, clear indentation style. This
will help you and anyone else who reads your code see the logic of the if statement and how
the code flows when your application runs.
What follows the case keyword is usually called the selector. The selector is a variable or
expression taken from either the char type or any integer type (an ordinal type). String type
are invalid!. However, the StringToCaseSelect custom function enables you to use the Case
statement with string type variables
As you can see, the individual case statements use a single constant, a group of constants
(separated by comma), or a range of constants (double dot separated). We can even add an
else keyword to take care of all the remaining cases at once.
Note 1: Only one case statement will be executed, we cannot have overlapping conditions in
the case statements.
Note 2: If you want to include more than one statement in the part following the colon (:),
place the begin and end keywords around the multiple statements.
Intro to subroutines
Subroutines are an important part of any programming language, and Delphi is no exception.
In Delphi, there are generally two types of subroutines: a function and a procedure. The
usual difference between a function and a procedure is that a function can return a value,
and a procedure generally will not do so. A function is normally called as a part of an
expression.
Take a look at the following examples:
procedure SayHello(const sWhat:string) ;
begin
ShowMessage('Hello ' + sWhat) ;
end;
function YearsOld(const BirthYear:integer): integer;
var
Year, Month, Day : Word;
begin
DecodeDate(Date, Year, Month, Day) ;
Result := Year - BirthYear;
end;
...
end;
Every procedure or function begins with a header that identifies the procedure or function
and lists the parameters the routine uses, if any. The parameters are listed within
parentheses. Each parameter has an identifying name and usually has a type. A semicolon
separates parameters in a parameter list from one another.
sStr, iYear and iMonth are called constant parameters.
Ads
Delphi
Component Delphi 7
Form Function
Excel for Beginners
How to Code
Constant parameters cannot be changed by the function (or procedure). The iDay is passed
as a var parameter, and we can make changes to it, inside the subroutine.
Functions, since they return values, must have a return type declared at the end of the
header. The return value of a function is given by the (final) assignment to its name. Since
every function implicitly has a local variable Result of the same type as the functions return
value, assigning to Result has the same effect as assigning to the name of the function.
This means that subroutines whose headers are given in the interface section are global in
scope.
When we call a function (or a procedure) inside its own unit, we use its name with whatever
parameters are needed. On other hand, if we call a global subroutine (defined in some other
unit, e.g. MyUnit) we use the name of the unit followed by a period.
...
//SayHello procedure is defined inside this unit
SayHello('Delphi User') ;
//YearsOld function is defined inside MyUnit unit
Dummy := MyUnit.YearsOld(1973) ;
...
Note: functions or procedures can have their own subroutines embedded inside them. An
embedded subroutine is local to the container subroutine and cannot be used by other parts
of the program. Something like:
procedure TForm1.Button1Click(Sender: TObject) ;
function IsSmall(const sStr:string):boolean;
begin
//IsSmall returns True if sStr is in lowercase, False otherwise
Result:=LowerCase(sStr)=sStr;
end;
begin
//IsSmall can only be uses inside Button1 OnClick event
if IsSmall(Edit1.Text) then
ShowMessage('All small caps in Edit1.Text')
else
ShowMessage('Not all small caps in Edit1.Text') ;
end;
Sign Up for Our Free Newsletters
Overloading
Simply put overloading is declaring more than one routine with the same name. Overloading
allows us to have multiple routines that share the same name, but with different number of
parameters and their types.
As an example, let's consider the following two functions:
{Overloaded routines must be declared
with the overload directive}
function SumAsStr(a, b :integer): string; overload;
begin
Result := IntToStr(a + b) ;
end;
function SumAsStr(a, b : extended; Digits:integer): string; overload;
begin
Result := FloatToStrF(a + b, ffFixed, 18, Digits) ;
end;
we'll get the following error: "there is no overloaded version of 'SumAsStr' that can be called
with these arguments", meaning that we should also include the Digits parameter used to
specify the number of digits after the decimal point.
Note: there is only one rule when writing overloaded routines: an overloaded routine must
differ in at least one parameter type.
The return type, instead, cannot be used to distinguish among two routines.
...
procedure RoutineName;
begin
Result := A.RoutineName;
end;
Note: parameters with default values must occur at the end of the parameter list, and must
be passed by value or as const. A reference (var) parameter cannot have a default value.
When calling routines with more than one default parameter we cannot skip parameters
(like in VB):
function SkipDefParams(var A:string; B:integer=5, C:boolean=False):boolean;
...
//this call generates an error message
CantBe := SkipDefParams('delphi', , True) ;
The call to DoIt procedure like DoIt(5.0) does not compile. Because of the default parameter
in the first procedure, this statement might call both procedures, because it is impossible to
tell which procedure is meant to be called.
Ads
Starting with this article I'll try to present fundamentals of Object Pascal Programming.
If you are new to Delphi, consider reading previous features for Delphi Beginners:
Why Delphi? (what is Delphi and what can it do for you)
Getting Started (an overview of Delphi programming and a simple application)
Working With Components (introduction to Delphi VCL)
Database Form Wizard (developing my own, first Delphi database application)
Visual programming is fun. Unfortunately, event handlers that you've seen, in those articles,
didn't do much. To fully understand and take advantage of Delphi, we must become
comfortable with the dialect of Object Pascal built into Delphi.
Statements or Long live the ";"
A statement in Delphi should be thought as a complete "sentence." Generally, statements
describe algorithmic actions that can be executed. Statements do things to Delphi objects.
These objects may be properties of components, numeric expressions or some other more
exotic gadgets.
Delphi distinguishes between simple statement and compound statements. A simple
statement is one executable line of code (not one line of code in Code Editor). A compound
statement is a block of simple statements surrounded by begin/end keywords.
//simple statement
Form1.Caption := 'Ha! New caption ';
//compound statement
begin
Top := Top - 10;
Width := Width + 5;
end
As you can see, a semicolon (;) is always required to separate two simple statement.
Comments
Comments are lines of text in your code that are there for documentation purposes.
Comments do not take up any room in the compiled code. There are three ways to indicate a
comment:
// one-line comment
{this is also a comment - can be several lines}
(* this is another comment *)
Commenting out executable statements to help debug your programs is a common and
often necessary technique in programming languages. You'd be surprised how quickly you
forget what code you wrote is supposed to do.
Properties and Values or Long Live the ":="
To design the look of your application interface, you set the values of object properties using
the Object Inspector. This is referred to as making design time settings. On the other hand,
resetting properties via code (run time settings) is one of the most common tasks in Delphi.
Any property you can set at design time can also be set at runtime by using code. There are
also properties that can be accessed only at runtime. These are known as runtime-only
properties.
When we want to change a property setting (value) for a Delphi object we have to place the
object's name followed by a period (.) and the name of the property on the left side of the
colon equal (:=) combination - this is not some kind of a smiley.
ObjectName.Property := Value;
When you need to reset a large number of properties at one time, you probably would not
prefer to have to retype the name of the object each time. The with keyword lets you do this
by eliminating the need to type the object name over and over again.
Enough theory. Let's take a look at a real example:
Variables
Variables in Pascal hold informations (values). Variables have to be declared before they can
be used. We do this after the var keyword. The var keyword can be used in several places in
the code, such as at the beginning of the code of a function or procedure, to declare
variables local to the routine, or inside a unit to declare global variables.
When declaring a variable, we must state its type. The type of a variable defines the set of
values the variable can have.
var
SomeValue : Integer;
NewAdress : string;
IsThisWorthy : boolean;
GimmeMoney : Currency;
Something : variant;
A, B, C : char;
Lookout : Pointer;
As you can see, we can declare more than one variable of the same type by placing a comma
between them. (SomeValue is variable of Integer type.)
Assigning values to variables
After you declare a variable, you can then use it to manipulate data in memory. Just as for
setting properties, Delphi uses the := sign for assigning values to variables. Like:
GimmeMoney := 323,23; //Curreny type variable
IsThisWorthy := True; //boolean type
NewAdress := 'My new home adress'; //string type
Delphi enforces strict rules on what kind of assignments we can make between variable
types. This means that something like
GimmeMoney := 'one thousand';
will give "Incompatible Types" error message. We can't assign string value to currency type
variable.
Another example (you should really try this one, it's fun):
procedure TForm1.Button2Click(Sender: TObject);
var
PosL, PosT : Integer;
TLCap: string;
begin
Randomize;
// assign value to PosL and PosT variables
PosL:=Random(Form1.ClientWidth-Button2.Width);
PosT:=Random(Form1.ClientHeight-Button2.Height);
// assign value to TLCap variable
TLCap:='Position: ';
TLCap:=TLCap + IntToStr(Button2.Left) + ' - ';
TLCap:=TLCap + IntToStr(Button2.Top);
//use variables to change object properties
Form1.Caption:=TLCap;
with Button2 do begin
Left:=PosL;
Top:=PosT;
end
end;
Constants
Constants (values that do not change during program execution) are declared using the
const keyword. To declare a constant you don't need to specify a data type, but only assign
an initial value.
const
Min
= 30;
Max
= 500;
Middle = (Max + Min) div 2;
ErrStr = 'Some errors occured';
MaxAmount = 123.45;
Final notes
My idea was to show you some basics of Delphi's built in programming language. Object
Pascal is far more complex and can give much more to Delphi developer. In some future
articles I'll be writing about Loops and Decisions, Procedures, Functions, Arrays, Custom data
types and so on...
As like in any programming language, in Delphi, variables are placeholders used to store
values; they have names and data types. The data type of a variable determines how the bits
representing those values are stored in the computer's memory.
When we have a variable that will contain some array of characters, we can declare it to be
of type String.
Delphi provides a healthy assortment of string operators, functions and procedures. Before
assigning a String data type to a variable, we need to thorughly understand Delphi's four
string types.
Short String
Simply put, Short String is a counted array of (ANSII) characters, with up to 255 characters in
the string. The first byte of this array stores the length of the string. Since this was the main
string type in Delphi 1 (16 bit Delphi), the only reason to use Short String is for backward
compatibility.
To create a ShortString type variable we use:
var s: ShortString;
s := 'Delphi Programming';
//S_Length := Ord(s[0]));
//which is the same as Length(s)
The s variable is a Short string variable capable of holding up to 256 characters, its memory is
a statically allocated 256 bytes. Since this is usually wastefull - unlikely will your short string
spread to the maximum length - second approach to using Short Strings is using subtypes of
ShortString, whose maximum length is anywhere from 0 to 255.
var ssmall: String[50];
ssmall := 'Short string, up to 50 characters';
The s variable can hold from zero to any practical number of characters. The string grows or
shrinks as you assign new data to it.
We can use any string variable as an array of characters, the second character in s has the
index 2. The following code
s[2]:='T';
assigns T to the second character os the s variable. Now the few of the first characters in s
look like: TTe s str....
Don't be mislead, you can't use s[0] to see the length of the string, s is not ShortString.
Reference counting, copy-on-write
Since memory allocation is done by Delphi, we don't have to worry about garbage collection.
When working with Long (Ansi) Strings Delphi uses reference counting. This way string
copying is actually faster for long strings than for short strings.
Reference counting, by example:
var s1,s2: String;
s1 := 'first string';
s2 := s1;
When we create string s1 variable, and assign some value to it, Delphi allocates enough
memory for the string. When we copy s1 to s2, Delphi does not copy the string value in
memory, it ony increases the reference count and alters the s2 to point to the same memory
location as s1.
To minimize copying when we pass strings to routines, Delphi uses copy-on-write techique.
Suppose we are to change the value of the s2 string variable; Delphi copies the first string to
a new memory location, since the change should affect only s2, not s1, and they are both
pointing to the same memory location.
Wide String
Wide strings are also dynamically allocated and managed, but they don't use reference
counting or the copy-on-write semantics. Wide strings consist of 16-bit Unicode characters.
About Unicode character sets
The ANSI character set used by Windows is a single-byte character set. Unicode stores each
character in the character set in 2 bytes instead of 1. Some national languages use
ideographic characters, which require more than the 256 characters supported by ANSI.
With 16-bit notation we can represent 65,536 different characters. Indexing of multibyte
strings is not reliable, since s[i] represents the ith byte (not necessarily the i-th character) in
s.
If you must use Wide characters, you should declare a string variable to be of the WideString
type and your character variable of the WideChar type. If you want to examine a wide string
one character at a time, be sure to test for multibite characters. Delphi doesn't support
automatic type conversions betwwen Ansi and Wide string types.
var s : WideString;
c : WideChar;
s := 'Delphi_ Guide';
s[8] := 'T';
//s='Delphi_TGuide';
Null terminated
A null or zero terminated string is an array of characers, indexed by an integer starting from
zero. Since the array has no length indicator, Delphi uses the ASCII 0 (NULL; #0) character to
mark the boundary of the string.
This means there is essentially no difference between a null-terminated string and an
array[0..NumberOfChars] of type Char, where the end of the string is marked by #0.
We use null-terminated strings in Delphi when calling Windows API functions. Object Pascal
lets us avoid messing arround with pointers to zero-based arrays when handling nullterminated strings by using the PChar type. Think of a PChar as being a pointer to a nullterminated string or to the array that represents one. For more info on pointers, check:
Pointers in Delphi.
For example, The GetDriveType API function determines whether a disk drive is a removable,
fixed, CD-ROM, RAM disk, or network drive. The following procedure lists all the drives and
their types on a users computer. Place one Button and one Memo component on a form and
asign an OnClick handler of a Button:
procedure TForm1.Button1Click(Sender: TObject);
var
Drive: Char;
DriveLetter: String[4];
begin
for Drive := 'A' to 'Z' do
begin
DriveLetter := Drive + ':\';
case GetDriveType(PChar(Drive + ':\')) of
DRIVE_REMOVABLE:
Memo1.Lines.Add(DriveLetter + ' Floppy Drive');
DRIVE_FIXED:
Memo1.Lines.Add(DriveLetter + ' Fixed Drive');
DRIVE_REMOTE:
Memo1.Lines.Add(DriveLetter + ' Network Drive');
DRIVE_CDROM:
Memo1.Lines.Add(DriveLetter + ' CD-ROM Drive');
DRIVE_RAMDISK:
Memo1.Lines.Add(DriveLetter + ' RAM Disk');
end;
end;
end;
what we are trying to do. The assignment s:=p, where s is a string variable and p is a PChar
expression, copies a null-terminated string into a long string.
Character types
In addition to four string data types, Delphi has three character types: Char, AnsiChar, and
WideChar. A string constant of length 1, such as 'T', can denote a character value. The
generic character type is Char, which is equivalent to AnsiChar. WideChar values are 16-bit
characters ordered according to the Unicode character set. The first 256 Unicode characters
correspond to the ANSI characters.
Intro to Types
Delphi's programming language is an example of a strongly typed language. This means that
all variables must be of some type. A type is essentially a name for a kind of data. When we
declare a variable we must specify its type, which determines the set of values the variable
can hold and the operations that can be performed on it.
Many of Delphi's built-in data types, such as Integer or String, can be refined or combined to
create new data types. In this article we'll see how to create custom ordinal data types in
Delphi.
Ordinal types
The defining characteristics of ordinal data types are: they must consist of a finitive number
of elements and they must be ordered in some way.
The most common examples of ordinal daty types are all the Integer types as well as Char
and Boolean type. More preciselly, Object Pascal has twelve predefined ordinal types:
Integer, Shortint, Smallint, Longint, Byte, Word, Cardinal, Boolean, ByteBool, WordBool,
LongBool, and Char. There are also two other classes of user-defined ordinal types:
enumerated types and subrange types.
In any ordinal types, it must make sense to move backward or forward to the next element.
For example, real types are not ordinal because moving backward or forward doesn't make
sense: the question "What is the next real after 2.5?" is meaningless.
Since, by definition, each value except the first has a unique predecessor and each value
except the last has a unique successor, several predefined function are used when working
with ordinal types:
Function Effect
Ord(X)
Pred(X)
Succ(X)
Low(X)
Returns the lowest value in the range of the ordinal data type X.
High(X)
Returns the highest value in the range of the ordinal data type X.
For example, High(Byte) returns 255 because the highest value of type Byte is 255, and
Succ(2) returns 3 because 3 is the successor of 2.
Note: If we try to use Succ when at the last element Delphi will generate a run-time exception if the range
checking is on.
Once we define an enumerated data type, we can declare variables to be of that type:
var SomeDay : TWeekDays;
The primary purpose of an enumerated data type is to make clear what data your program
will manipulate. An enumerated type is really just a shorthand way of assigning sequential
values to constants. Given these declarations, Tuesday is a constant of type TWeekDays.
Delphi allows us to work with the elements in an enumerated type using an index that
comes from the order that they were listed in. In the previous example: Monday in the
TWeekDays type declaration has the index 0, Tuesday has the index 1, and so on. The
functions listed in the table before let us, for example, use Succ(Friday) to "go to" Saturday.
Now we can try something like:
for SomeDay := Monday to Sunday do
if SomeDay = Tuesday then
ShowMessage('Tuesday it is!');
The Delphi Visual Component Library uses enumerated types in many places. For example,
the position of a form is defined as follows:
TPosition = (poDesigned, poDefault, poDefaultPosOnly,
poDefaultSizeOnly, poScreenCenter);
We use Position (through the Object Inspector) to get or set the size and placement of the
form.
Subrange Types
Simply put, a subrange type represents a subset of the values in another ordinal type. In
general, we can define any subrange by starting with any ordinal type (including a previously
defined enumerated type) and using a double dot:
type TWorkDays = Monday .. Friday;
Here TWorkDays includes the values Monday, Tuesday, Wednesday, Thursday and Friday.
That's all ... go enumerate :)
Static Arrays
Suppose we are writing a program that lets a user enter some values (e.g. the number of
appointments) at the beginning of each day. We would choose to store the information in a
list. We could call this list Appointments, and each number might be stored as
Appointments[1], Appointments[2], an so on.
To use the list, we must first declare it.
continue reading below our video
Get Better Pictures from Your Phone
Play
0:00
/
2:52
Fullscreen
For example:
var Appointments : array[0..6] of Integer;
Sometimes we need to keep track of related information in an array. For example, to keep
track of each pixel on your computer screen, you need to refer to its X and Y coordinates.
This can be done using a multidimensional array to store the values.
With Delphi, we can declare arrays of multiple dimensions. For example, the following
statement declares a two-dimensional 7 by 24 array:
var DayHour : array[1..7, 1..24] of Real;
Delphi
Component Delphi 7
Array Storage
For Beginners
Static String
The DayHour variable, declared above, sets aside 168 (7*24) elements, in 7 rows and 24
columns. To retreive the value from cell in the third row and seventh column we would use:
DayHour[3,7] or DayHour[3][7]. The following code can be used to set all elements in the
DayHour array to 0.
for i := 1 to 7 do
for j := 1 to 24 do
DayHour[i,j] := 0;
Dynamic Arrays
Sometimes you may not know exactly how large to make an array. You may want to have
the capability of changing the size of the array at run time.
A dynamic array declares its type, but not its size. The actual size of a dynamic array can be
changed at run time by the use of the SetLength procedure.
creates a one-dimensional dynamic array of strings. The declaration does not allocate
memory for Students. To create the array in memory, we call SetLength procedure. For
example, given the declaration above,
SetLength(Students, 14) ;
allocates an array of 14 strings, indexed 0 to 13. Dynamic arrays are always integer-indexed,
always starting from 0 to one less than their size in elements.
To create a two-dimensional dynamic array, use the following code:
var Matrix: array of array of Double;
begin
SetLength(Matrix, 10, 20)
end;
which allocates space for a two-dimensional, 10 x 20, array of Double floating-point values.
Note: To remove a dynamic array's memory space we assign nil to the array variable, like:
Matrix := nil;
Very often, your program doesn't know at compile time how many elements will be needed,
that number will not be known until runtime. With dynamic arrays you can allocate only as
much storage as is required at a given time. In other words, the size of dynamic arrays can
be changed at run time, which is one of the key advantages to dynamic arrays. The next code
creates an array of integer values and then calls the Copy function to resize the array.
var
Vector: array of Integer;
k : integer;
begin
SetLength(Vector, 10) ;
for k := Low(Vector) to High(Vector) do
Vector[k] := i*10;
...
//now we need more space
SetLength(Vector, 20) ;
//here, Vector array can hold up to 20 elements
//(it already has 10 of them)
end;
Note 1: SetLength function creates a larger (or smaller) array, and copies the existing values
to the new array.
Note 2: The Low and High functions ensure you access every array element without looking
back in your code for the correct lower and upper index values.
Now, when we have a record, we use a dot to isolate the fields of DelphiGuide:
DelphiGuide.Name := 'Zarko Gajic';
DelphiGuide.eMail := 'delphi@aboutguide.com';
DelphiGuide.Posts := 15;
Ads
Record Records
Public Record
Delphi
Component Delphi 7
For Beginners
Note: the above piece of code could be rewritten with the use of with keyword:
with DelphiGuide do
begin
Name := 'Zarko Gajic';
eMail := 'delphi@aboutguide.com';
Posts := 15; end;
An Array of Records
Since TMember acts like any other Object Pascal type, we can declare an array of record
variables:
var DPMembers : array[1..50] of TMember;
Or, to display information (e-mail, for example) about every member we could use:
var k: cardinal;
for k:= 1 to 50 do
ShowMessage(DPMembers[k].eMail) ;
Note: Here's how to declare and initialize a constant array of records in Delphi
Since a record type is legitimate as any other Delphi type, we can have a field of a record be
a record itself. For example, we could create ExpandedMember to keep track of what the
member is submitting along with the member information:
type
TExpandedMember = record
SubmitType : string;
Member : TMember;
end;
Filling out all the information needed for a single record is now somehow harder. More
periods (dots) are required to access the fields of TExpandedMember:
var SubTypeMember :TExpandedMember;
SubTypeMember.SubmitType := 'VCL';
SubTypeMember.Member.Name := 'vcl Programmer';
SubTypeMember.Member.eMail := 'vcl@aboutguide.com';
SubTypeMember.Member.Name := 555;