( Delphi 过程 和 函数 的基本实例 ) Delphi Procedures and Functions
About Procedures and Functions
Procedures and functions, referred to collectively as routines, are self-contained statement blocks that can be
called from different locations in a program. A function is a routine that returns a value when it executes. A
procedure is a routine that does not return a value.
Function calls, because they return a value, can be used as expressions in assignments and operations. For
example:
I := SomeFunction(X);
calls SomeFunction and assigns the result to I. Function calls cannot appear on the left side of an assignment
statement.
Procedure calls - and, when extended syntax is enabled ({$X+}), function calls - can be used as complete
statements. For example:
DoSomething;
calls the DoSomething routine; if DoSomething is a function, its return value is discarded.
Procedures and functions can call themselves recursively.
Declaring Procedures and Functions
When you declare a procedure or function, you specify its name, the number and type of parameters it takes,
and, in the case of a function, the type of its return value; this part of the declaration is sometimes called the
prototype, heading, or header. Then you write a block of code that executes whenever the procedure or
function is called; this part is sometimes called the routine's body or block.
Procedure Declarations
A procedure declaration has the form:
procedure procedureName(parameterList); directives;
localDeclarations;
begin
statements
end;
where procedureName is any valid identifier, statements is a sequence of statements that execute when the
procedure is called, and (parameterList), directives;, and localDeclarations; are optional.
Here is an example of a procedure declaration:
procedure NumString(N: Integer; var S: string);
var
V: Integer;
begin
V := Abs(N);
S := ;
repeat
S := Chr(V mod 10 + Ord('0')) + S;
V := V div 10;
until V = 0;
if N < 0 then S := '-' + S;
end;
Given this declaration, you can call the NumString procedure like this:
NumString(17, MyString);
This procedure call assigns the value '17' to MyString (which must be a string variable).
Within a procedure's statement block, you can use variables and other identifiers declared in the
localDeclarations part of the procedure. You can also use the parameter names from the parameter list (like N
and S in the previous example); the parameter list defines a set of local variables, so don't try to redeclare the
parameter names in the localDeclarations section. Finally, you can use any identifiers within whose scope the
procedure declaration falls.
Function Declarations
A function declaration is like a procedure declaration except that it specifies a return type and a return value.
Function declarations have the form:
function functionName(parameterList): returnType; directives;
localDeclarations;
begin
statements
end;
where functionName is any valid identifier, returnType is a type identifier, statements is a sequence of
statements that execute when the function is called, and (parameterList), directives;, and localDeclarations; are
optional.
The function's statement block is governed by the same rules that apply to procedures. Within the statement
block, you can use variables and other identifiers declared in the localDeclarations part of the function,
parameter names from the parameter list, and any identifiers within whose scope the function declaration falls.
In addition, the function name itself acts as a special variable that holds the function's return value, as does the
predefined variable Result.
As long as extended syntax is enabled ({$X+}), Result is implicitly declared in every function. Do not try to
redeclare it.
For example:
function WF: Integer;
begin
WF := 17;
end;
defines a constant function called WF that takes no parameters and always returns the integer value 17. This
declaration is equivalent to:
function WF: Integer;
begin
Result := 17;
end;
Here is a more complicated function declaration:
function Max(A: array of Real; N: Integer): Real;
var
X: Real;
I: Integer;
begin
X := A[0];
for I := 1 to N - 1 do
if X < A[I] then X := A[I];
Max := X;
end;
You can assign a value to Result or to the function name repeatedly within a statement block, as long as you
assign only values that match the declared return type. When execution of the function terminates, whatever
value was last assigned to Result or to the function name becomes the function's return value. For example:
function Power(X: Real; Y: Integer): Real;
var
I: Integer;
begin
Result := 1.0;
I := Y;
while I > 0 do
begin
if Odd(I) then Result := Result * X;
I := I div 2;
X := Sqr(X);
end;
end;
Result and the function name always represent the same value. Hence:
function MyFunction: Integer;
begin
MyFunction := 5;
Result := Result * 2;
MyFunction := Result + 1;
end;
returns the value 11. But Result is not completely interchangeable with the function name. When the function
name appears on the left side of an assignment statement, the compiler assumes that it is being used (like
Result) to track the return value; when the function name appears anywhere else in the statement block, the
compiler interprets it as a recursive call to the function itself. Result, on the other hand, can be used as a
variable in operations, typecasts, set constructors, indexes, and calls to other routines.
If the function exits without assigning a value to Result or the function name, then the function's return value is
undefined.
Calling Conventions
When you declare a procedure or function, you can specify a calling convention using one of the directives
register, pascal, cdecl, stdcall, and safecall. For example,
function MyFunction(X, Y: Real): Real; cdecl;
Calling conventions determine the order in which parameters are passed to the routine. They also affect the
removal of parameters from the stack, the use of registers for passing parameters, and error and exception
handling. The default calling convention is register.
The register and pascal conventions pass parameters from left to right; that is, the left most parameter is
evaluated and passed first and the rightmost parameter is evaluated and passed last. The cdecl, stdcall, and
safecall conventions pass parameters from right to left.
For all conventions except cdecl, the procedure or function removes parameters from the stack upon returning.
With the cdecl convention, the caller removes parameters from the stack when the call returns.
The register convention uses up to three CPU registers to pass parameters, while the other conventions pass all
parameters on the stack.
The safecall convention implements exception 'firewalls.' On Win32, this implements interprocess COM error
notification.
The table below summarizes calling conventions.
Calling conventions :
Directive Parameter order Clean-up Passes parameters in registers?
register
Left-to-right
Routine
Yes
pascal
Left-to-right
Routine
No
cdecl
Right-to-left
Caller
No
stdcall
Right-to-left
Routine
No
safecall
Right-to-left
Routine
No
The default register convention is the most efficient, since it usually avoids creation of a stack frame. (Access
methods for published properties must use register.) The cdecl convention is useful when you call functions
from shared libraries written in C or C++, while stdcall and safecall are recommended, in general, for calls to
external code. On Win32, the operating system APIs are stdcall and safecall. Other operating systems generally
use cdecl. (Note that stdcall is more efficient than cdecl.)
The safecall convention must be used for declaring dual-interface methods. The pascal convention is
maintained for backward compatibility.
The directives near, far, and export refer to calling conventions in 16-bit Windows programming. They have no
effect in Win32 and are maintained for backward compatibility only.
Forward and Interface Declarations
The forward directive replaces the block, including local variable declarations and statements, in a procedure or
function declaration. For example:
function Calculate(X, Y: Integer): Real; forward;
declares a function called Calculate. Somewhere after the forward declaration, the routine must be redeclared
in a defining declaration that includes a block. The defining declaration for Calculate might look like this:
function Calculate;
... { declarations }
begin
... { statement block }
end;
Ordinarily, a defining declaration does not have to repeat the routine's parameter list or return type, but if it
does repeat them, they must match those in the forward declaration exactly (except that default parameters
can be omitted). If the forward declaration specifies an overloaded procedure or function, then the defining
declaration must repeat the parameter list.
A forward declaration and its defining declaration must appear in the same type declaration section. That is,
you can't add a new section (such as a var section or const section) between the forward declaration and the
defining declaration. The defining declaration can be an external or assembler declaration, but it cannot be
another forward declaration.
The purpose of a forward declaration is to extend the scope of a procedure or function identifier to an earlier
point in the source code. This allows other procedures and functions to call the forward-declared routine before
it is actually defined. Besides letting you organize your code more flexibly, forward declarations are sometimes
necessary for mutual recursions.
The forward directive has no effect in the interface section of a unit. Procedure and function headers in the
interface section behave like forward declarations and must have defining declarations in the implementation
section. A routine declared in the interface section is available from anywhere else in the unit and from any
other unit or program that uses the unit where it is declared.
External Declarations
The external directive, which replaces the block in a procedure or function declaration, allows you to call
routines that are compiled separately from your program. External routines can come from object files or
dynamically loadable libraries.
When importing a C function that takes a variable number of parameters, use the varargs directive. For
example:
function printf(Format: PChar): Integer; cdecl; varargs;
The varargs directive works only with external routines and only with the cdecl calling convention.
Linking to Object Files
To call routines from a separately compiled object file, first link the object file to your application using the $L
(or $LINK) compiler directive. For example:
{$L BLOCK.OBJ}
links BLOCK.OBJ into the program or unit in which it occurs. Next, declare the functions and procedures that
you want to call:
procedure MoveWord(var Source, Dest; Count: Integer); external;
procedure FillWord(var Dest; Data: Integer; Count: Integer); external;
Now you can call the MoveWord and FillWord routines from BLOCK.OBJ.
On the Win32 platform, declarations like the ones above are frequently used to access external routines written
in assembly language. You can also place assembly-language routines directly in your Delphi source code.
Importing Functions from Libraries
To import routines from a dynamically loadable library (.DLL), attach a directive of the form
external stringConstant;
to the end of a normal procedure or function header, where stringConstant is the name of the library file in
single quotation marks. For example, on Win32
function SomeFunction(S: string): string; external 'strlib.dll';
imports a function called SomeFunction from strlib.dll.
You can import a routine under a different name from the one it has in the library. If you do this, specify the
original name in the external directive:
external stringConstant1 name stringConstant2;
where the first stringConstant gives the name of the library file and the second stringConstant is the routine's
original name.
The following declaration imports a function from user32.dll (part of the Win32 API):
function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer;
stdcall; external 'user32.dll' name 'MessageBoxA';
The function's original name is MessageBoxA, but it is imported as MessageBox.
Instead of a name, you can use a number to identify the routine you want to import:
external stringConstant index integerConstant;
where integerConstant is the routine's index in the export table.
In your importing declaration, be sure to match the exact spelling and case of the routine's name. Later, when
you call the imported routine, the name is case-insensitive.
To postpone the loading of the library that contains the function to the moment the function is actually
needed, append the delayed directive to the imported function:
function ExternalMethod(const SomeString: PChar): Integer; stdcall; external 'cstyle.dll' delayed;
delayed ensures that the library that contains the imported function is not loaded at application startup, but
rather when the first call to the function is made. For more information on this topic, see the Libraries and
Packages - Delayed Loading topic.
Overloading Procedures and Functions
You can declare more than one routine in the same scope with the same name. This is called overloading.
Overloaded routines must be declared with the overload directive and must have distinguishing parameter
lists. For example, consider the declarations:
function Divide(X, Y: Real): Real; overload;
begin
Result := X/Y;
end
function Divide(X, Y: Integer): Integer; overload;
begin
Result := X div Y;
end;
These declarations create two functions, both called Divide, that take parameters of different types. When you
call Divide, the compiler determines which function to invoke by looking at the actual parameters passed in the
call. For example, Divide(6.0, 3.0) calls the first Divide function, because its arguments are real-valued.
You can pass to an overloaded routine parameters that are not identical in type with those in any of the
routine's declarations, but that are assignment-compatible with the parameters in more than one declaration.
This happens most frequently when a routine is overloaded with different integer types or different real types -
for example:
procedure Store(X: Longint); overload;
procedure Store(X: Shortint); overload;
In these cases, when it is possible to do so without ambiguity, the compiler invokes the routine whose
parameters are of the type with the smallest range that accommodates the actual parameters in the call.
(Remember that real-valued constant expressions are always of type Extended.)
Overloaded routines must be distinguished by the number of parameters they take or the types of their
parameters. Hence the following pair of declarations causes a compilation error:
function Cap(S: string): string; overload;
...
procedure Cap(var Str: string); overload;
...
But the declarations:
function Func(X: Real; Y: Integer): Real; overload;
...
function Func(X: Integer; Y: Real): Real; overload;
...
are legal.
When an overloaded routine is declared in a forward or interface declaration, the defining declaration must
repeat the routine's parameter list.
The compiler can distinguish between overloaded functions that contain AnsiString/PAnsiChar,
UnicodeString/PChar and WideString/PWideChar parameters in the same parameter position. String constants
or literals passed into such an overload situation are translated into the native string or character type, which is
UnicodeString/PChar.
procedure test(const A: AnsiString); overload;
procedure test(const W: WideString); overload;
procedure test(const U: UnicodeString); overload;
procedure test(const PW: PWideChar); overload;
var
a: AnsiString;
b: WideString;
c: UnicodeString;
d: PWideChar;
e: string;
begin
a := 'a';
b := 'b';
c := 'c';
d := 'd';
e := 'e';
test(a); // calls AnsiString version
test(b); // calls WideString version
test(c); // calls UnicodeString version
test(d); // calls PWideChar version
test(e); // calls UnicodeString version
test('abc'); // calls UnicodeString version
test(AnsiString ('abc')); // calls AnsiString version
test(WideString('abc')); // calls WideString version
test(PWideChar('PWideChar')); // calls PWideChar version
end;
Variants can also be used as parameters in overloaded function declarations. Variant is considered more
general than any simple type. Preference is always given to exact type matches over variant matches. If a variant
is passed into such an overload situation, and an overload that takes a variant exists in that parameter position,
it is considered to be an exact match for the Variant type.
This can cause some minor side effects with float types. Float types are matched by size. If there is no exact
match for the float variable passed to the overload call but a variant parameter is available, the variant is taken
over any smaller float type.
For example:
procedure foo(i: integer); overload;
procedure foo(d: double); overload;
procedure foo(v: variant); overload;
var
v: variant;
begin
foo(1); // integer version
foo(v); // variant version
foo(1.2); // variant version (float literals -> extended precision)
end;
This example calls the variant version of foo, not the double version, because the 1.2 constant is implicitly an
extended type and extended is not an exact match for double. Extended is also not an exact match for Variant,
but Variant is considered a more general type (whereas double is a smaller type than extended).
foo(Double(1.2));
This typecast does not work. You should use typed consts instead:
const d: double = 1.2;
begin
foo(d);
end;
The above code works correctly, and calls the double version.
const s: single = 1.2;
begin
foo(s);
end;
The above code also calls the double version of foo. Single is a better fit to double than to variant.
When declaring a set of overloaded routines, the best way to avoid float promotion to variant is to declare a
version of your overloaded function for each float type (Single, Double, Extended) along with the variant
version.
If you use default parameters in overloaded routines, be careful not to introduce ambiguous parameter
signatures.
You can limit the potential effects of overloading by qualifying a routine's name when you call it. For example,
Unit1.MyProcedure(X, Y) can call only routines declared in Unit1; if no routine in Unit1 matches the name and
parameter list in the call, an error results.
Local Declarations
The body of a function or procedure often begins with declarations of local variables used in the routine's
statement block. These declarations can also include constants, types, and other routines. The scope of a local
identifier is limited to the routine where it is declared.
Nested Routines
Functions and procedures sometimes contain other functions and procedures within the local-declarations
section of their blocks. For example, the following declaration of a procedure called DoSomething contains a
nested procedure.
procedure DoSomething(S: string);
var
X, Y: Integer;
procedure NestedProc(S: string);
begin
...
end;
begin
...
NestedProc(S);
...
end;
The scope of a nested routine is limited to the procedure or function in which it is declared. In our example,
NestedProc can be called only within DoSomething.
For real examples of nested routines, look at the DateTimeToString procedure, the ScanDate function, and other
routines in the SysUtils unit.
About Procedures and Functions
Procedures and functions, referred to collectively as routines, are self-contained statement blocks that can be
called from different locations in a program. A function is a routine that returns a value when it executes. A
procedure is a routine that does not return a value.
Function calls, because they return a value, can be used as expressions in assignments and operations. For
example:
I := SomeFunction(X);
calls SomeFunction and assigns the result to I. Function calls cannot appear on the left side of an assignment
statement.
Procedure calls - and, when extended syntax is enabled ({$X+}), function calls - can be used as complete
statements. For example:
DoSomething;
calls the DoSomething routine; if DoSomething is a function, its return value is discarded.
Procedures and functions can call themselves recursively.
Declaring Procedures and Functions
When you declare a procedure or function, you specify its name, the number and type of parameters it takes,
and, in the case of a function, the type of its return value; this part of the declaration is sometimes called the
prototype, heading, or header. Then you write a block of code that executes whenever the procedure or
function is called; this part is sometimes called the routine's body or block.
Procedure Declarations
A procedure declaration has the form:
procedure procedureName(parameterList); directives;
localDeclarations;
begin
statements
end;
where procedureName is any valid identifier, statements is a sequence of statements that execute when the
procedure is called, and (parameterList), directives;, and localDeclarations; are optional.
Here is an example of a procedure declaration:
procedure NumString(N: Integer; var S: string);
var
V: Integer;
begin
V := Abs(N);
S := ;
repeat
S := Chr(V mod 10 + Ord('0')) + S;
V := V div 10;
until V = 0;
if N < 0 then S := '-' + S;
end;
Given this declaration, you can call the NumString procedure like this:
NumString(17, MyString);
This procedure call assigns the value '17' to MyString (which must be a string variable).
Within a procedure's statement block, you can use variables and other identifiers declared in the
localDeclarations part of the procedure. You can also use the parameter names from the parameter list (like N
and S in the previous example); the parameter list defines a set of local variables, so don't try to redeclare the
parameter names in the localDeclarations section. Finally, you can use any identifiers within whose scope the
procedure declaration falls.
Function Declarations
A function declaration is like a procedure declaration except that it specifies a return type and a return value.
Function declarations have the form:
function functionName(parameterList): returnType; directives;
localDeclarations;
begin
statements
end;
where functionName is any valid identifier, returnType is a type identifier, statements is a sequence of
statements that execute when the function is called, and (parameterList), directives;, and localDeclarations; are
optional.
The function's statement block is governed by the same rules that apply to procedures. Within the statement
block, you can use variables and other identifiers declared in the localDeclarations part of the function,
parameter names from the parameter list, and any identifiers within whose scope the function declaration falls.
In addition, the function name itself acts as a special variable that holds the function's return value, as does the
predefined variable Result.
As long as extended syntax is enabled ({$X+}), Result is implicitly declared in every function. Do not try to
redeclare it.
For example:
function WF: Integer;
begin
WF := 17;
end;
defines a constant function called WF that takes no parameters and always returns the integer value 17. This
declaration is equivalent to:
function WF: Integer;
begin
Result := 17;
end;
Here is a more complicated function declaration:
function Max(A: array of Real; N: Integer): Real;
var
X: Real;
I: Integer;
begin
X := A[0];
for I := 1 to N - 1 do
if X < A[I] then X := A[I];
Max := X;
end;
You can assign a value to Result or to the function name repeatedly within a statement block, as long as you
assign only values that match the declared return type. When execution of the function terminates, whatever
value was last assigned to Result or to the function name becomes the function's return value. For example:
function Power(X: Real; Y: Integer): Real;
var
I: Integer;
begin
Result := 1.0;
I := Y;
while I > 0 do
begin
if Odd(I) then Result := Result * X;
I := I div 2;
X := Sqr(X);
end;
end;
Result and the function name always represent the same value. Hence:
function MyFunction: Integer;
begin
MyFunction := 5;
Result := Result * 2;
MyFunction := Result + 1;
end;
returns the value 11. But Result is not completely interchangeable with the function name. When the function
name appears on the left side of an assignment statement, the compiler assumes that it is being used (like
Result) to track the return value; when the function name appears anywhere else in the statement block, the
compiler interprets it as a recursive call to the function itself. Result, on the other hand, can be used as a
variable in operations, typecasts, set constructors, indexes, and calls to other routines.
If the function exits without assigning a value to Result or the function name, then the function's return value is
undefined.
Calling Conventions
When you declare a procedure or function, you can specify a calling convention using one of the directives
register, pascal, cdecl, stdcall, and safecall. For example,
function MyFunction(X, Y: Real): Real; cdecl;
Calling conventions determine the order in which parameters are passed to the routine. They also affect the
removal of parameters from the stack, the use of registers for passing parameters, and error and exception
handling. The default calling convention is register.
The register and pascal conventions pass parameters from left to right; that is, the left most parameter is
evaluated and passed first and the rightmost parameter is evaluated and passed last. The cdecl, stdcall, and
safecall conventions pass parameters from right to left.
For all conventions except cdecl, the procedure or function removes parameters from the stack upon returning.
With the cdecl convention, the caller removes parameters from the stack when the call returns.
The register convention uses up to three CPU registers to pass parameters, while the other conventions pass all
parameters on the stack.
The safecall convention implements exception 'firewalls.' On Win32, this implements interprocess COM error
notification.
The table below summarizes calling conventions.
Calling conventions :
Directive Parameter order Clean-up Passes parameters in registers?
register
Left-to-right
Routine
Yes
pascal
Left-to-right
Routine
No
cdecl
Right-to-left
Caller
No
stdcall
Right-to-left
Routine
No
safecall
Right-to-left
Routine
No
The default register convention is the most efficient, since it usually avoids creation of a stack frame. (Access
methods for published properties must use register.) The cdecl convention is useful when you call functions
from shared libraries written in C or C++, while stdcall and safecall are recommended, in general, for calls to
external code. On Win32, the operating system APIs are stdcall and safecall. Other operating systems generally
use cdecl. (Note that stdcall is more efficient than cdecl.)
The safecall convention must be used for declaring dual-interface methods. The pascal convention is
maintained for backward compatibility.
The directives near, far, and export refer to calling conventions in 16-bit Windows programming. They have no
effect in Win32 and are maintained for backward compatibility only.
Forward and Interface Declarations
The forward directive replaces the block, including local variable declarations and statements, in a procedure or
function declaration. For example:
function Calculate(X, Y: Integer): Real; forward;
declares a function called Calculate. Somewhere after the forward declaration, the routine must be redeclared
in a defining declaration that includes a block. The defining declaration for Calculate might look like this:
function Calculate;
... { declarations }
begin
... { statement block }
end;
Ordinarily, a defining declaration does not have to repeat the routine's parameter list or return type, but if it
does repeat them, they must match those in the forward declaration exactly (except that default parameters
can be omitted). If the forward declaration specifies an overloaded procedure or function, then the defining
declaration must repeat the parameter list.
A forward declaration and its defining declaration must appear in the same type declaration section. That is,
you can't add a new section (such as a var section or const section) between the forward declaration and the
defining declaration. The defining declaration can be an external or assembler declaration, but it cannot be
another forward declaration.
The purpose of a forward declaration is to extend the scope of a procedure or function identifier to an earlier
point in the source code. This allows other procedures and functions to call the forward-declared routine before
it is actually defined. Besides letting you organize your code more flexibly, forward declarations are sometimes
necessary for mutual recursions.
The forward directive has no effect in the interface section of a unit. Procedure and function headers in the
interface section behave like forward declarations and must have defining declarations in the implementation
section. A routine declared in the interface section is available from anywhere else in the unit and from any
other unit or program that uses the unit where it is declared.
External Declarations
The external directive, which replaces the block in a procedure or function declaration, allows you to call
routines that are compiled separately from your program. External routines can come from object files or
dynamically loadable libraries.
When importing a C function that takes a variable number of parameters, use the varargs directive. For
example:
function printf(Format: PChar): Integer; cdecl; varargs;
The varargs directive works only with external routines and only with the cdecl calling convention.
Linking to Object Files
To call routines from a separately compiled object file, first link the object file to your application using the $L
(or $LINK) compiler directive. For example:
{$L BLOCK.OBJ}
links BLOCK.OBJ into the program or unit in which it occurs. Next, declare the functions and procedures that
you want to call:
procedure MoveWord(var Source, Dest; Count: Integer); external;
procedure FillWord(var Dest; Data: Integer; Count: Integer); external;
Now you can call the MoveWord and FillWord routines from BLOCK.OBJ.
On the Win32 platform, declarations like the ones above are frequently used to access external routines written
in assembly language. You can also place assembly-language routines directly in your Delphi source code.
Importing Functions from Libraries
To import routines from a dynamically loadable library (.DLL), attach a directive of the form
external stringConstant;
to the end of a normal procedure or function header, where stringConstant is the name of the library file in
single quotation marks. For example, on Win32
function SomeFunction(S: string): string; external 'strlib.dll';
imports a function called SomeFunction from strlib.dll.
You can import a routine under a different name from the one it has in the library. If you do this, specify the
original name in the external directive:
external stringConstant1 name stringConstant2;
where the first stringConstant gives the name of the library file and the second stringConstant is the routine's
original name.
The following declaration imports a function from user32.dll (part of the Win32 API):
function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer;
stdcall; external 'user32.dll' name 'MessageBoxA';
The function's original name is MessageBoxA, but it is imported as MessageBox.
Instead of a name, you can use a number to identify the routine you want to import:
external stringConstant index integerConstant;
where integerConstant is the routine's index in the export table.
In your importing declaration, be sure to match the exact spelling and case of the routine's name. Later, when
you call the imported routine, the name is case-insensitive.
To postpone the loading of the library that contains the function to the moment the function is actually
needed, append the delayed directive to the imported function:
function ExternalMethod(const SomeString: PChar): Integer; stdcall; external 'cstyle.dll' delayed;
delayed ensures that the library that contains the imported function is not loaded at application startup, but
rather when the first call to the function is made. For more information on this topic, see the Libraries and
Packages - Delayed Loading topic.
Overloading Procedures and Functions
You can declare more than one routine in the same scope with the same name. This is called overloading.
Overloaded routines must be declared with the overload directive and must have distinguishing parameter
lists. For example, consider the declarations:
function Divide(X, Y: Real): Real; overload;
begin
Result := X/Y;
end
function Divide(X, Y: Integer): Integer; overload;
begin
Result := X div Y;
end;
These declarations create two functions, both called Divide, that take parameters of different types. When you
call Divide, the compiler determines which function to invoke by looking at the actual parameters passed in the
call. For example, Divide(6.0, 3.0) calls the first Divide function, because its arguments are real-valued.
You can pass to an overloaded routine parameters that are not identical in type with those in any of the
routine's declarations, but that are assignment-compatible with the parameters in more than one declaration.
This happens most frequently when a routine is overloaded with different integer types or different real types -
for example:
procedure Store(X: Longint); overload;
procedure Store(X: Shortint); overload;
In these cases, when it is possible to do so without ambiguity, the compiler invokes the routine whose
parameters are of the type with the smallest range that accommodates the actual parameters in the call.
(Remember that real-valued constant expressions are always of type Extended.)
Overloaded routines must be distinguished by the number of parameters they take or the types of their
parameters. Hence the following pair of declarations causes a compilation error:
function Cap(S: string): string; overload;
...
procedure Cap(var Str: string); overload;
...
But the declarations:
function Func(X: Real; Y: Integer): Real; overload;
...
function Func(X: Integer; Y: Real): Real; overload;
...
are legal.
When an overloaded routine is declared in a forward or interface declaration, the defining declaration must
repeat the routine's parameter list.
The compiler can distinguish between overloaded functions that contain AnsiString/PAnsiChar,
UnicodeString/PChar and WideString/PWideChar parameters in the same parameter position. String constants
or literals passed into such an overload situation are translated into the native string or character type, which is
UnicodeString/PChar.
procedure test(const A: AnsiString); overload;
procedure test(const W: WideString); overload;
procedure test(const U: UnicodeString); overload;
procedure test(const PW: PWideChar); overload;
var
a: AnsiString;
b: WideString;
c: UnicodeString;
d: PWideChar;
e: string;
begin
a := 'a';
b := 'b';
c := 'c';
d := 'd';
e := 'e';
test(a); // calls AnsiString version
test(b); // calls WideString version
test(c); // calls UnicodeString version
test(d); // calls PWideChar version
test(e); // calls UnicodeString version
test('abc'); // calls UnicodeString version
test(AnsiString ('abc')); // calls AnsiString version
test(WideString('abc')); // calls WideString version
test(PWideChar('PWideChar')); // calls PWideChar version
end;
Variants can also be used as parameters in overloaded function declarations. Variant is considered more
general than any simple type. Preference is always given to exact type matches over variant matches. If a variant
is passed into such an overload situation, and an overload that takes a variant exists in that parameter position,
it is considered to be an exact match for the Variant type.
This can cause some minor side effects with float types. Float types are matched by size. If there is no exact
match for the float variable passed to the overload call but a variant parameter is available, the variant is taken
over any smaller float type.
For example:
procedure foo(i: integer); overload;
procedure foo(d: double); overload;
procedure foo(v: variant); overload;
var
v: variant;
begin
foo(1); // integer version
foo(v); // variant version
foo(1.2); // variant version (float literals -> extended precision)
end;
This example calls the variant version of foo, not the double version, because the 1.2 constant is implicitly an
extended type and extended is not an exact match for double. Extended is also not an exact match for Variant,
but Variant is considered a more general type (whereas double is a smaller type than extended).
foo(Double(1.2));
This typecast does not work. You should use typed consts instead:
const d: double = 1.2;
begin
foo(d);
end;
The above code works correctly, and calls the double version.
const s: single = 1.2;
begin
foo(s);
end;
The above code also calls the double version of foo. Single is a better fit to double than to variant.
When declaring a set of overloaded routines, the best way to avoid float promotion to variant is to declare a
version of your overloaded function for each float type (Single, Double, Extended) along with the variant
version.
If you use default parameters in overloaded routines, be careful not to introduce ambiguous parameter
signatures.
You can limit the potential effects of overloading by qualifying a routine's name when you call it. For example,
Unit1.MyProcedure(X, Y) can call only routines declared in Unit1; if no routine in Unit1 matches the name and
parameter list in the call, an error results.
Local Declarations
The body of a function or procedure often begins with declarations of local variables used in the routine's
statement block. These declarations can also include constants, types, and other routines. The scope of a local
identifier is limited to the routine where it is declared.
Nested Routines
Functions and procedures sometimes contain other functions and procedures within the local-declarations
section of their blocks. For example, the following declaration of a procedure called DoSomething contains a
nested procedure.
procedure DoSomething(S: string);
var
X, Y: Integer;
procedure NestedProc(S: string);
begin
...
end;
begin
...
NestedProc(S);
...
end;
The scope of a nested routine is limited to the procedure or function in which it is declared. In our example,
NestedProc can be called only within DoSomething.
For real examples of nested routines, look at the DateTimeToString procedure, the ScanDate function, and other
routines in the SysUtils unit.