函数
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类型由类型检查器推断。
请注意,尽管函数可能看起来像是多个参数的函数,实际上它是一个单一张量类型参数的函数。要了解差异,请参阅函数应用。然而,参数张量的组成部分通常被称为函数参数。