Sign in Join
Wiki copytemporary

Function/ru

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

Функция — это подпрограмма, которая, в отличие от процедур, возвращает значение. Вызов функции практически заменяется возвращаемым значением. Если состояние переключателя компилятора {$extendedSyntax} отключено, вызовы функций не могут отображаться как непродуктивные операторы, но должны быть целиком или частью выражения.

Слово function является зарезервированным словом.

Возвращаемое значение

В дополнение к обычной процедуре формальная сигнатура функции содержит тип возвращаемого значения: список формальных параметров должен сопровождаться двоеточием и типом возвращаемого значения. Например, следующая функция возвращает Boolean

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

При реализации функций есть несколько способов определить возвращаемое значение функции.

pascal
program functionDemo(input, output, stderr);

{$mode objFPC}

// традиционный синтаксис:
// результат сохраняется в переменной
// его имя такое же, как у функции
function myLine(const x: real): real;
begin
    myLine := 0.5 * x + 2;
end;

Если {$modeswitch result+}, заданный {$mode objFPC} и {$mode Delphi}, внутри блока реализации также доступен результат специального идентификатора:

pascal
// используя специальный идентификатор `result`
function myParabola(const x: real): real;
begin
    result := sqr(x) - 1;
end;

Кроме того, в {$mode objFPC} подпрограмма exit также установит возвращаемое значение и покинет стековый кадр. В предыдущих двух примерах могли появиться дополнительные операторы, и они были бы выполнены, уже после того, как exit подпрограмма выполнится. Это поведение оператора return в C или других языках программирования.

pascal
// использование процедуры выхода exit
function even(const x: longint): Boolean;
begin
  exit(not odd(x));
end;

В языке ассемблера применяются другие правила. Если тип возвращаемого значения является целым числом, используется регистр-накопитель, при условии, что он помещается туда:

pascal
// на ассемблере:
// возвращаемый тип умещается в один регистр => использовать регистр-накопитель
function zero(const x: int64): Boolean;
{$ifDef CPUx86_64}
assembler; register;
{$asmMode intel}
asm// xor изменяет флаги => поместите его перед тестом// rax := 0    (удалить остаток)// x = 0 ?// rax := ZF// Когда вы изучаете вывод ассемблера// вы заметите, что компилятор автоматически вставляет код// который перемещает содержимое rax в нужное место в стеке,// пока не появится подсказка noStackFrame// (автоматически устанавливается некоторыми уровнями оптимизации)// инструктирует компилятор опустить кадр стека, если это возможно.
    // xor изменяет флаги => поместите его перед тестом
    xor rax, rax     // rax := 0    (удалить остаток)
    
    test x, x        // x = 0 ?
    setz al          // rax := ZF
    
        // Когда вы изучаете вывод ассемблера
        // вы заметите, что компилятор автоматически вставляет код
        // который перемещает содержимое rax в нужное место в стеке,
        // пока не появится подсказка noStackFrame
        // (автоматически устанавливается некоторыми уровнями оптимизации)
        // инструктирует компилятор опустить кадр стека, если это возможно.
END;
{$else}
begin
    result := x = 0;
end;
{$endIf}

В противном случае, в зависимости от того, какой режим {$asmMode} активен, можно использовать макрос @result (Intel) или __result (AT&T).

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

// на ассемблере:
// возвращаемый тип не помещается в аккумулятор => макрос @result дает адрес
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}

Первоначально Паскаль ожидал ровно одно присваивание переменной результата (в зависимости от того, что используется). Однако FPC не запрещает множественные присваивания. Он выдаст предупреждение, если не был использован ни один из возможных идентификаторов результата или процедура выхода не написана.

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.

Обратите внимание, что в случае перегрузок операторов в формальной подписи должен быть объявлен специальный вид функции, идентификатор переменной результата. Подробнее см. в статье.

Замечания

Pure functions это функции, не зависящие от внешнего состояния.

См.также


From the Free Pascal wiki, page "Function/ru", as it stood on 2022-05-31 after a change by Zoltanleo, 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.