#                              函数

By [startha](https://paragraph.com/@starha) · 2023-09-13

---

函数
--

Solidity语言的函数非常灵活，可以进行各种复杂的操作。本教程中，我们将会概述函数的基本概念，并通过一些示例演示如何使用函数。

        function <function name>(<parameter types>) {internal|external|public|private} [pure|view|payable] [returns (<return types>)]
    

看着有点复杂，让我们从前往后逐个解释（方括号的是可写可不写的关键字）：

1.  `function`：声明函数时的固定用法。要编写函数，就需要以`function`关键字开头。
    
2.  `<function name>`：函数名。
    
3.  `(<parameter types>)`：圆括号内写入函数参数，即输入到函数的变量类型和名称。
    
4.  `{internal|external|public|private}`：函数可见性说明符，共有四种。
    
    *   `public`：内部和外部均可见。
        
    *   `private`：只能从本合约内部可访问，继承的合约也不能使用。
        
    *   `external`：只能从合约外部访问(但内部可以通过`this.f()来调用，`f\`是函数名)。
        
    *   `internal`：只能从合约内部访问，继承的合约可以用。
        
    
    **注意1**：合约中定义的函数需要明确指定可见性，它们没有默认值。
    
    **注意2**：`public|private|internal`也可用于修饰状态变量。`public`变量会自动生成同名的`getter`函数，用于查询数值。未标明可见性的状态变量默认为`internal`。
    
5.  `[purl|view|payable]`：决定函数权限/功能的关键字。`payable`(可支付)很好理解，带着它的函数，运行的时候可以给合约转入ETH。`pure`和`view`的介绍见下一节。
    
6.  `[returns ()]`：函数返回变量类型和名称。
    

到底什么是`Pure`和`View`？
-------------------

刚开始学习`solidity`时，`pure`和`view`关键字可能令人费解，因为其他编程语言中没有类似的关键字。`solidity`引入这两个关键字主要因为以太坊交易需要支付gas fee。合约的状态变量存储在链上，gas fee很贵，如果计算不改变链上状态，就可以不用支付gas。博客`pure`和`view`关键字的函数是不改写链上状态的，因此用户直接调用它们是不需要支付gas（注意，合约中非`pure`/`view`函数调用`pure`/`view`函数时需要支付gas）

在以太坊中，以下语句被视为修改链上状态：

1.  写入状态变量。
    
2.  释放事件。
    
3.  创建其他合约。
    
4.  使用`selfdestruct`。
    
5.  通过调用发送以太币。
    
6.  调用任何未标记`view`或`pure`的函数。
    
7.  使用低级别调用（low-level calls）。
    
8.  使用包含某些操作码内联汇编。
    

*   `pure`：函数不读取和修改链上状态变量。
    
*   `view`：函数读取但不写入链上状态变量。
    
*   非`pure`或`view`：可读可写链上状态变量。
    

代码
--

### 1\. pure 和 view

我们在合约定义一个状态变量`number`，初始化为5。

        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        contract FunctionTypes{
            uint256 public number = 5;
        }
    

定义一个`add()`函数，每次调用会让`number`增加1。

        function add() external {
            number = number + 1;
        }
    

如果`add()`函数被标记为`pure`,比如`function add() pure external`，就会报错。因为`pure`是不能读取合约里面状态变量，更不能修改。那`pure`函数能做什么？举个例子，你可以给函数传递一个参数`_number`，然后让它返回`_number + 1`，这个操作不会读取或修改状态变量。

        //pure  即不查看也不修改状态变量
        function addPure(uint256 _number) external pure returns(uint256 new_number){
            new_number = _number + 1;
        }
    

如果`add()`函数被标记为`view`，比如`function add() view external`，也会报错。因为`view`能读取，但不能够修改状态变量。我们可以稍微修改一下函数，读取但不修改`number`，返回一个新的变量。

### 返回值：return和returns

solidity中与函数输出香港的有两个关键字：`return`和`returns`。他们的区别在于：

*   `returns`：跟函数后面，用于声明返回变量类型和变量名。
    
*   `return`：用于函数主体中，返回指定的变量。
    

        // 返回多个变量
        function returnMultiple() public pure returns(uint256, bool, uint256[3] memory){
            return(1, true, [uint256(1),2,5]);
        }
    

在上述代码中，我们利用`returns`关键字声明了有多个返回值的`returnMultiple()`函数，然后我们在函数主体使用`return(1, true, [uint256(1),2,5])`确定了返回值。

### 命名式返回

我们可以在`returns`中标明返回变量的名称。Solidity会初始化这些变量，并且自动返回函数值，无需使用`return`。

        // 命名式返回
        function returnNamed() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
            _number = 2;
            _bool = false;
            _array = [uint256(3),2,1];
        }
    

在上述代码中，我们用`returns(uint256 _number, bool _bool, uint256[3] memory _array)`声明了返回变量类型及变量名。这样，在主体中只需为变量`_number`、`_bool`、和`_array`赋值，即可自动返回。

当然，你也可以在命名式返回中用`return`来返回变量：

        // 命名式返回，依然支持return
        function returnNamed2() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
            return(1, true, [uint256(1),2,5]);
        }
    

### 解析式赋值

Solidity支持使用解析式赋值柜子来读取函数的全部或部分返回值。

*   读取所有返回值：声明变量，然后将要赋值的变量用`,`隔开，按顺序排列。
    
*   读取部分返回值：声明要读取的返回值对应的变量，不读取的留空。在下面的代码中，我们只读取了`_bool`,而不读取返回的`_number`和`_array`。
    

    // 读取返回值，解构式赋值
        function readReturn() public pure{
            // 读取全部返回值
            uint256 _number;
            bool _bool;
            bool _bool2;
            uint256[3] memory _array;
            (_number, _bool, _array) = returnNamed();
            
            // 读取部分返回值，解构式赋值
            (, _bool2, ) = returnNamed();
        }

---

*Originally published on [startha](https://paragraph.com/@starha/zZp60KUTbNOcDHnYxovU)*
