Você está na página 1de 31

An introduction to the Delphi Language

Learn the basics of the Delphi language

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.

Delphi Language: tutorials


Delphi language, a set of object-oriented extensions to standard Pascal, is the language of
Delphi. Delphi Pascal is a high-level, compiled, strongly typed language that supports
structured and object-oriented design. Its benefits include easy-to-read code, quick
compilation, and the use of multiple unit files for modular programming.
Here's a list of tutorials, an introduction to Delphi Pascal, that will help you learn Delphi
Pascal. Each tutorial will help you to understand a particular feature of Delphi Pascal
language, with practical and easy to understand code snippets.
Variable Scope
Object Pascal Variable Scope: now you see me, now you don't.
Typed constants
How to implement persistent values between function calls.
Loops
Repeating operations in Object Pascal in Object Pascal in Object Pascal in Object Pascal.
Decisions
Making decisions in Object Pascal or NOT.
Functions and Procedures
Creating user defined subroutines in Object Pascal.
Routines in Delphi: Beyond the Basics
Extending Object Pascal functions and procedures with default parameters and method
overloading.
Statements/Properties/Variables
The basic layout of a Pascal/Delphi program.
String Types in Delphi
Understanding and managing string data types in Delphi's Object Pascal. Learn about
differences between Short, Long, Wide and null-terminated strings.

Ordinal and Enumerated Data Types


Extend Delphi's built-in types by constructing your own types.
Arrays in Object Pascal
Understanding and using array data types in Delphi.
Records in Delphi
Learn about records, Delphi's Pascal data structure that can mix any of Delphi's built in types
including any types you have created.
Variant Records in Delphi
Why and when to use variant records, plus creating an array of records.
Pointers in Delphi
An introduction to pointer data type in Delphi. What are pointers, why, when and how to
use them.
Recursions in Delphi
Writing and using recursive functions in Object Pascal.
Some exercises for you...
Since this Course is an online course, there is much you can do to prepare for the next
chapter. At the end of each chapter I'll try to provide several tasks for you to get more
familiar with Delphi and the topics we discuss in the current chapter.
To the next chapter: A Beginner's Guide to Delphi Programming
This is the end of the sixth chapter, in the next chapter, we'll deal with more sophisticated
articles on the Delphi language.
If you need any kind of help at this point, please post to the Delphi Programming Forum
where all the questions are answered and beginners are treated as experts.

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):

procedure TForm1.Button1Click(Sender: TObject);


begin
ShowMessage(FloatToStr(SomeNumber));
end;

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;

Sharing variables across procedures (event handlers)


Occasionally we will want to share the values of variables (and constants) across event
handlers or across units. For example, if an application is designed to perform a calculation
involving one SomeNumber at a time, that SomeNumber should be available to all
procedures in a unit.
Depending on where we declare a variable, the variable can be thought of as a true global
variable accessible by any other code in the application, or a unit-level variable accessible
by any code in the unit.
Unit level variables - unit level scope
We put the declaration statements for unit-level variables in a var section in the unit's
implementation section. Unit-level constants are declared in a const section.
Let's look at the second example:
0. We will be modifying our first example (be sure to have it)
1. Add declaration of SomeNumber, so that implementation code of the unit looks like:
...
implementation
{$R *.DFM}
var
SomeNumber: Double;
...

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;

Be sure to add Unit1 to the uses clause of Unit2.


Conclusion
That's it. I hope you have had the power to come to the end of this article. As we can see,
there is much to be stated about variable scope in Object Pascal. Of course, there is more:
static variables (or typed constants) are something we could name "constant variables". I'll
be dealing with static variables in some of the future articles...

Understanding Typed Constants in Delphi

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.

Variable typed constants


Although we declare typed constants in the const section of a procedure, it is important to
remember that they are not constants. At any point in your application, if you have access to
the identifier for a typed constant you'll be able to modify its value.
To see typed constants at work, put a button on a blank form, and assign the following code
to the OnClick event handler:
procedure TForm1.Button1Click(Sender: TObject) ;
const
clicks : Integer = 1; //not a true constant
begin
Form1.Caption := IntToStr(clicks) ;
clicks := clicks + 1;
end;

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.

Constant typed constants

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-}

Therefore, the first example code looks like:


procedure TForm1.Button1Click(Sender: TObject) ;
const
{$J+}
clicks : Integer = 1; //not a true constant
{$J-}
begin
Form1.Caption := IntToStr(clicks) ;
clicks := clicks + 1;
end;

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.

Understanding and Using Loops


The loop is a common element in all programming languages. Delphi has three control
structures that execute blocks of code repeatedly: for, repeat ... until and while ... do.

The FOR loop


Suppose we need to repeat an operation a fixed number of times.
// show 1,2,3,4,5 message boxes
var j: integer;
begin
for j := 1 to 5 do

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.

Nested FOR loops


Writing a for loop within another for loop (nesting loops) is very useful when you want to fill
/ display data in a table or a grid.
var k,j: integer;
begin
//this double loop is executed 4x4=16 times
for k:= 1 to 4 do
for j:= 4 downto 1 do
ShowMessage('Box: '+ IntToStr(k)+ ',' + IntToStr(j)) ;
end;

Understanding and Using Decisions

Sign Up for Our Free Newsletters

About Today Electronics & Gadgets Delphi


Delphi Categories

if language = Delphi then


begin
Use(language)
end
else
Skip(language) ;
Branching
If you want to control the flow of code execution depending on what the program has
already done or what it has just encountered you need to use one of the two Delphi Pascal
branching statements: if statements and case statements.

The IF THEN ELSE statement


The if statement is used to test for a condition and then execute sections of code based on
whether that condition is True or False. The condition is described with a Boolean
expression, If the condition is True, the code flow branches one way. If the condition is False,
the flow branches in another direction. Let's see this behavior on an example:
var iNumber : Integer;
begin
//some value must be
//assigned to iNumber here!
if iNumber = 0 then
ShowMessage('Zero value encountered!') ;
end;

continue reading below our video


Tech 101: Recovering Lost Data
Play
0:00
/
2:51
Fullscreen
If the number (assigned to iNumber variable) is 0, the expression iNumber = 0 evaluates to
True and the message is displayed; otherwise, nothing is displayed. If we want more than
one thing to happen when the tested condition is True, we can write multiple statements in
a begin ... end block.
var iNumber : Integer;
begin
//some value must be
//assigned to iNumber here!

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.

The CASE statement


Although, we can use the if statement for very complex (nested) condition testing, the case
statement is usually easier to read (debug!) and the code runs more quickly.
The case statement makes it clear that a program has reached a point with many branches;
multiple if-then statements do not.
var
iNumber : Integer;
begin
//some value must be
//assigned to iNumber here!
case iNumber of
0 : ShowMessage('Zero value') ;
1..10 : ShowMessage('Less than 11, greater than 0') ;
-1, -2, -3 : ShowMessage('Number is -1 or -2 or -3') ;
else
ShowMessage('I do not care') ;
end;
end;

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.

Understanding and Using Functions and


Procedures
Have you ever found yourself writing the same code over and over to perform some
common task within event handlers? Yes! It's time for you to learn about programs within a
program. Let's call those mini programs subroutines.

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;

continue reading below our video


How Does 3D Printing Work?
Play
0:00
/
1:09
Fullscreen
Once subroutines have been defined, we can call them one or more times:
procedure TForm1.Button1Click(Sender: TObject) ;
begin
SayHello('Delphi User') ;
end;
procedure TForm1.Button2Click(Sender: TObject) ;
begin
SayHello('Zarko Gajic') ;
ShowMessage('You are ' + IntToStr(YearsOld(1973)) + ' years old!') ;
end;

Functions and Procedures


As we can see, both functions and procedures act like mini programs. In particular, they can
have their own type, constants and variable declarations inside them.
Take a closer look at a (miscellaneous) SomeCalc function:
function SomeCalc
(const sStr: string;
const iYear, iMonth: integer;
var iDay:integer): boolean;
begin

...
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.

Positioning and Calling Subroutines


Subroutines are always placed inside the implementation section of the unit. Such
subroutines can be called (used) by any event handler or subroutine in the same unit that is
defined after it.
Note: the uses clause of a unit tells you which units it can call. If we want a specific
subroutine in a Unit1 to be usable by the event handlers or subroutines in another unit (say
Unit2), we have to:

Add Unit1 to the uses clause of Unit2


Place a copy of the header of the subroutine in the interface section of the Unit1.

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

About Today Electronics & Gadgets Delphi


Delphi Categories

Updated November 24, 2014.


Functions and procedures are an important part of the Delphi language. Starting with Delphi
4, Delphi allows us to work with functions and procedures that support default parameters
(making the parameters optional), and permits two or more routines to have identical
name, but operate as totally different routines.
Let's see how Overloading and default parameters can help you code better...

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;

continue reading below our video


9 Tips to Extend Your Phone's Battery
Play
0:00
/
0:59
Fullscreen
These declarations create two functions, both called SumAsStr, that take different number
of parameters, that are of different types. When we call an overloaded routine, the compiler
must be able to tell which routine we want to call.
For example, SumAsStr(6, 3) calls the first SumAsStr function, because its arguments are
integer-valued.
Note that Delphi will help you pick the right implementation with the help of code
completion and code insight.
On the other hand, if we try to call the SumAsStr function as follows:
SomeString := SumAsStr(6.0,3.0)

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.

Two units - one routine


Let's say we have one routine in unit A, and unit B uses unit A, but declares a routine with
the same name. The declaration in unit B does not need the overload directive: we should
use unit A's name to qualify calls to A's version of the routine from unit B.
Something like:
unit B;
...
uses A;

...
procedure RoutineName;
begin
Result := A.RoutineName;
end;

An alternative to using overloaded routines is to use default parameters, which usually


results in less code to write and maintain.

Default / Optional Parameters


In order to simplify some statements we can give a default value for the parameter of a
function or procedure, and we can call the routine with or without the parameter, making it
optional. To provide a default value, end the parameter declaration with the = symbol
followed by a constant expression.
For example, given the declaration
function SumAsStr (a,b : extended; Digits : integer = 2) : string;

the following function calls are equivalent.


SumAsStr(6.0, 3.0)
SumAsStr(6.0, 3.0, 2)

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) ;

Overloading with Default Parameters


When using both function or procedure overloading and default parameters, don't introduce
ambiguous routine declarations.
Consider the following declarations:
procedure DoIt(A:extended; B:integer = 0) ; overload;
procedure DoIt(A:extended) ; overload;

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.

STATEMENTS PROPERTIES VARIABLES

Ads

Delphi Image Componentswww.imageen.comPowerful Native Image Display


and Editing Library for Delphi
Download Full Free Bookswww.readingfanatic.comBegin w/ Reading Fanatic for
Free Access to Unlimited eBooks!
Download A Free Audiobookmobile.audible.comStart your 30-Day Free Trial
today. Download The App & Start Listening!
Delphi for Beginners:
The basic layout of a Pascal/Delphi program.

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:

procedure TForm1.Button1Click(Sender: TObject);


begin
Form1.Caption := 'Ha! New caption ';
Button1.Left := Button1.Left - 10;

with Button1 do begin


Top := Top - 10;
Width := Width + 5;
end
end;

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;

Obviously, line of code like


Middle:= 275;

will generate an error! ("Left side cannot be assigned to")


However, following line of code if quite usual/acceptable:
SomeValue := Middle + 200;

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...

STRING TYPES IN DELPHI


Delphi For Beginners:
Understanding and managing string data types in Delphi's Object Pascal. Learn about differences between
Short, Long, Wide and null-terminated strings.

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';

This creates a variable called ssmall whose maximum length is 50 characters.


Note: When we assign a value to a Short String variable, the string is truncated if it exceeds the maximum
length for the type. When we pass short strings to some Delphi's string manipulationg routine, they are
converted to and from long string.

String / Long / Ansi


Delphi 2 brought to Object Pascal Long String type. Long string (in Delphi's help AnsiString)
represents a dynamically allocated string whose maximum length is limited only by available
memory. All 32-bit Delphi versions use long strings by default. I recomend using long strings
whenever you can.
var s: String;
s := 'The s string can be of any size...';

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;

Mixing Delphi's strings


We can freely mix all four different kinds of strings, Delphi will give it's best to make sence of

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.

ORDINAL DATA TYPES


Delphi For Beginners:
Extend Delphi's built-in types by constructing your own types.

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)

Gives the index of the lement

Pred(X)

Goes to the element listed before X in the type

Succ(X)

Goes to the element listed after X in the type

Dec(X;n) Moves n elements back (if n is omitted moves 1 element back)


Inc(X;n)

Moves n elements forward (if n is omitted moves 1 element forward)

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.

Enumerated Data Types


The easiest way to create a new example of an ordinal type is simply to list a bunch of
elements in some order. The values have no inherent meaning, and their ordinality follows
the sequence in which the identifiers are listed. In other words, an enumeration is a list of
values.
type TWeekDays = (Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday, Sunday);

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 :)

Understanding and Using Array data types in


Delphi
he concept of arrays in Delphi is simple: arrays allow us to refer to a series of variables by the
same name and to use a number (an index) to tell them apart. Arrays have both upper and
lower bounds, and the elements of the array are contiguous within those bounds.
Elements of the array are values that are all of the same type (string, integer, record, custom
object).
In Delphi, there are two types of arrays: a fixed-size array which always remains the same
size - static array, and a dynamic array whose size can change at run-time.

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;

declares a variable called Appointments that holds an one-dimensional array (vector) of 7


integer values. Given this declaration, Appointments[3] denotes the fourth integer value in
Appointments. The number in the brackets is called the index.
If we create a static array but dont assign values to all its elements, the unused elements
contain random data; they are like uninitialized variables. The following code can be used to
set all elements in the Appointments array to 0.
for k := 0 to 6 do Appointments[k] := 0;

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;

To compute the number of elements in a multidimensional array, multiply the number of


indexes.
Ads

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;

Note: here's How to Declare and Initialize Constant Arrays

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.

For example, the following variable declaration


var Students : array of string;

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.

Understanding and Using Record Data Types


in Delphi

Suppose we want to create three one-dimensional arrays for 50 members in our


programming community. The first array is for names, the second for e-mails, and the third
for number of uploads (components or applications) to our community.
Each array (list) would have matching indexes and plenty of code to maintain all three lists in
parallel. Of course, we could try with one three-dimensional array, but what about it's type?
We need string for names and e-mails, but an integer for the number of uploads.
The way to work with such a data structure is to use Delphi's record structure.

TMember = record ...


For example, the following declaration creates a record type called TMember, the one we
could use in our case.
type
TMember = record
Name : string;
eMail : string;
Posts : Cardinal;
end;

continue reading below our video


Get Better Pictures from Your Phone
Play
0:00
/
2:52
Fullscreen
Essentially, a record data structure can mix any of Delphi's built in types including any types
you have created. Record types define fixed collections of items of different types. Each
item, or field, is like a variable, consisting of a name and a type.
TMember type contains three fields: a string value called Name (to hold the name of a
member), a value of a string type called eMail (for one e-mail), and an integer (Cardinal)
called Posts (to hold the number of submissions to our community).
Once we have set up the record type, we can declare a variable to be of type TMember.
TMember is now just as good variable type for variables as any of Delphi's built in types like
String or Integer. Note: the TMember type declaration, does not allocate any memory for
the Name, eMail, and Posts fields;
To actually create an instance of TMember record we have to declare a variable of TMember
type, as in the following code:
var DelphiGuide, AMember : TMember;

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;

We can now copy the values of DelphiGuides fields to AMember:


AMember := DelphiGuide;

Record Scope and visibility


Record type declared within the declaration of a form (implementation section), function, or
procedure has a scope limited to the block in which it is declared. If the record is declared in
the interface section of a unit it has a scope that includes any other units or programs that
use the unit where the declaration occurs.
To learn more about Delphi's variable scope go the Variable Scope article.

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;

To access the fifth member we use:


with DPMembers[5] do begin
Name := 'First name Last';
eMail := 'FirstLast@domain.com'
Posts := 0;
end;

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

Records as Record fields

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;

Record with "unknown" fields


A record type can have a variant part (I don't mean Variant type variable). Variant records
are used, for example, when we want to create a record type that has fields for different
kinds of data, but we know that we will never need to use all of the fields in a single record
instance. To learn more about Variant parts in Records take a look at Delphi's help files. The
use of a variant record type is not type-safe and is not a recommended programming
practice, particularly for beginners.
However, variant records can be quite usefull, if you ever find yourself in a situation to use
them, here's the secont part of this article: "However, variant records can be quite usefull, if
you ever find yourself in a situation to use them, here's the secont part of this article:
Records in Delphi - Part 2"

Você também pode gostar