函数
FunC 程序本质上是一系列函数声明/定义和全局变量声明。本节涵盖了第一个主题。
任何函数声明或定义都以一个共同的模式开始,接下来有三种情况之一:
-
单个
;
,表示函数已声明但尚未定义。它可能会在同一文件中的后面或在传递给 FunC 编译器的其他文件中定义。例如,int add(int x, int y);
是一个名为
add
类型为(int, int) -> int
的函数的简单声明。 -
汇编函数体定义。这是通过低层级 TVM 原语定义函数以便在 FunC 程序中后续使用的方法。例如,
int add(int x, int y) asm "ADD";
是同一个
add
函数的汇编定义,类型为(int, int) -> int
,将转换为 TVM 操作码ADD
。 -
常规块语句函数体定义。这是定义函数的常用方式。例如,
int add(int x, int y) {
return x + y;
}是
add
函数的常规定义。
函数声明
如前所述,任何函数声明或定义都以一个共同的模式开始。以下是该模式:
[<forall declarator>] <return_type> <function_name>(<comma_separated_function_args>) <specifiers>
其中 [ ... ]
对应于可选条目。
函数名
函数名可以是任何标识符,也可以以 .
或 ~
符号开头。这些符号的含义在声明部分解释。
例如,udict_add_builder?
、dict_set
和 ~dict_set
都是有效且不同的函数名。(它们在 stdlib.fc 中定义。)
特殊函数名
FunC(实际上是 Fift 汇编器)有几个预定义的保留函数名,具有预定义的id。
main
和recv_internal
的 id 为 0recv_external
的 id 为 -1run_ticktock
的 id 为 -2
每个程序必须有一个 id 为 0 的函数,即 main
或 recv_internal
函数。
run_ticktock
在特殊智能合约的 ticktock 交易中被调用。
接收内部消息
recv_internal
在智能合约接收到内部入站消息时被调用。
当 TVM 初始化 时,栈上有一些变量,通过在 recv_internal
中设置参数,我们使智能合约代码能够了解其中的一些变量。那些代码不知道的变量将永远躺在栈底,从未被触及。
因此,以下每个 recv_internal
声明都是正确的,但具有较少变量的声明将稍微节省一些gas(每个未使用的参数都会增加额外的 DROP
指令)
() recv_internal(int balance, int msg_value, cell in_msg_cell, slice in_msg) {}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) {}
() recv_internal(cell in_msg_cell, slice in_msg) {}
() recv_internal(slice in_msg) {}
接收外部消息
recv_external
用于入站外部消息。
返回类型
返回类型可以是类型部分中描述的任何原子或复合类型。例如,
int foo();
(int, int) foo'();
[int, int] foo''();
(int -> int) foo'''();
() foo''''();
都是有效的函数声明。
也允许类型推断。例如,
_ pyth(int m, int n) {
return (m * m - n * n, 2 * m * n, m * m + n * n);
}
是 pyth
函数的有效定义,类型为 (int, int) -> (int, int, int)
,用于计算勾股数。
函数参数
函数参数由逗号分隔。以下是参数的有效声明方式:
- 普通声明:类型 + 名称。例如,
int x
是函数声明() foo(int x);
中类型为int
、名称为x
的参数声明。 - 未使用的参数声明:只有类型。例如,
是类型为
int first(int x, int) {
return x;
}(int, int) -> int
的有效函数定义。 - 推断类型的参数声明:只有名称。例如,
是类型为
int inc(x) {
return x + 1;
}int -> int
的有效函数定义。x
的int
类型由类型检查器推断。
请注意,尽管函数可能看起来像是多个参数的函数,实际上它是一个单一张量类型参数的函数。要了解差异,请参阅函数应用。然而,参数张量的组成部分通常被称为函数参数。
函数调用
非修改方法
非修改函数支持使用 .
的简短函数调用形式
example(a);
a.example();
如果函数至少有一个参数,它可以作为非修改方法被调用。例如,store_uint
的类型为 (builder, int, int) -> builder
(第二个参数是要存储的值,第三个是位长度)。begin_cell
是创建新构建器的函数。以下代码等效:
builder b = begin_cell();
b = store_uint(b, 239, 8);
builder b = begin_cell();
b = b.store_uint(239, 8);
因此,函数的第一个参数可以在函数名前传递给它,如果用 .
分隔。代码可以进一步简化:
builder b = begin_cell().store_uint(239, 8);