Sign in Join
Wiki copytemporary

Function

Deutsch · English · Español · Français · Suomi · Русский

A function is a routine that, in contrast to procedures, returns a value. A call of a function is virtually substituted by its return value. If the {$extendedSyntax} compiler switch state is off, function calls can not appear as non-productive statements, but have to be or be part of an expression.

The word function is a reserved word.

Return value

In addition to a normal procedure, a function’s formal signature contains a return type: The formal parameter list has to be succeeded by a colon and return type. For instance the following function returns a Boolean.

pascal
function myFunction(const firstParameter: real): Boolean;

When implementing functions there are several ways to define the function’s return value.

pascal
program functionDemo(input, output, stderr);

{$mode objFPC}

// traditional syntax:
// the result is stored in the variable
// its name is the same as the function's
function myLine(const x: real): real;
begin
    myLine := 0.5 * x + 2;
end;

If {$modeswitch result+}, which is set by {$mode objFPC} and {$mode Delphi}, inside the implementation block the special identifier result is available, too:

pascal
// using special `result` identifier
function myParabola(const x: real): real;
begin
    result := sqr(x) - 1;
end;

Additionally, in {$mode objFPC} the routine exit will set the return value, too, and leave the stack frame. In the previous two examples further statements could have appeared, and they would have been executed, whilst after an exit the routine is done. This is the behavior a return statement in C or other programming languages has.

pascal
// using exit routine
function even(const x: longint): Boolean;
begin
    exit(not odd(x));
end;

In assembly language other rules apply. If the return type is an integral value, the accumulator register is used, provided it fits in there:

pascal
// in assembly language:
// return type fits into a single register => use accumulator register
function zero(const x: int64): Boolean;
{$ifDef CPUx86_64}
assembler; register;
{$asmMode intel}
asm// xor modifies flags => put it in front of test// rax := 0    (remove residue)// x = 0 ?// rax := ZF// When you examine the assembler output// you will notice the compiler automatically inserts code// that moves the contents of rax to the right spot on the stack,// unless the noStackFrame hint// (automatically set by some optimization levels)// instructs the compiler to omit the stack frame if possible.
    // xor modifies flags => put it in front of test
    xor rax, rax     // rax := 0    (remove residue)
    
    test x, x        // x = 0 ?
    setz al          // rax := ZF
    
    // When you examine the assembler output
    // you will notice the compiler automatically inserts code
    // that moves the contents of rax to the right spot on the stack,
    // unless the noStackFrame hint
    // (automatically set by some optimization levels)
    // instructs the compiler to omit the stack frame if possible.
END;
{$else}
begin
    result := x = 0;
end;
{$endIf}

Otherwise, depending on which {$asmMode} is active, the @result (Intel) or __result (AT&T) macro can be used.

pascal
type
    bodyAttributes = record
        surfaceArea: real;
        volume: real;
    end;

// in assembly language:
// return type doesn't fit into accumulator => @result macro gives address
function sphere(const radius: real): bodyAttributes;
{$ifDef CPUx86_64}
assembler;
{$asmMode intel}
const
    three: longint = 3;
    four: longint = 4;
var
    r: real;
asm// r := (@radius+0)^// rax := @result// radius// radius radius// 4 radius radius// pi 4 radius radius// 4*pi radius radius// radius 4*pi radius// radius radius 4*pi radius// radius^2 4*pi radius// 4*pi*radius^2 radius// 4*pi*radius^3// 3 4*pi*radius^3// 4/3*pi*radius^3
    pextrq r, radius, 0 // r := (@radius+0)^
    lea rax, @result    // rax := @result
    fld r               // radius
    
    fld st(0)           // radius radius
    fild four           // 4 radius radius
    fldpi               // pi 4 radius radius
    fmul                // 4*pi radius radius
    fxch                // radius 4*pi radius
    fld st(0)           // radius radius 4*pi radius
    fmul                // radius^2 4*pi radius
    fmul                // 4*pi*radius^2 radius
    fst [rax].bodyAttributes.surfaceArea
    
    fmul                // 4*pi*radius^3
    fild three          // 3 4*pi*radius^3
    fdivp               // 4/3*pi*radius^3
    fst [rax].bodyAttributes.volume
END;
{$else}
begin
    sphere.surfaceArea := 4 * pi() * sqr(radius);
    sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
end;
{$endIf}

Originally Pascal expected exact one assignment to the result variable (whichever is used). FPC however does not prohibit multiple assignments. It will emit a warning if none of the possible result identifiers were used or the exit routine is not written.

Warning
Function result does not seem to be set

You can get this warning if the compiler thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks.

pascal
begin
    writeLn(sphere(2.0).surfaceArea);
end.

Note, in the case of operator overloads, a special form of a function, a result variable identifier has to be declared in the formal signature. See the article for details.

Remarks

Pure functions are functions that do not depend on an external state.

See also


From the Free Pascal wiki, page "Function", as it stood on 2021-02-20 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.