值类型和引用类型

本章学习在 Solidity 中,什么是值类型和引用类型,以及它们之间的区别。

视频Bilibili  |  Youtube
官网binschool.app
推特@BinSchool    DiscordBinDAO   微信:bkra50 

Solidity 中的数据类型,除了按照组成结构分为基本类型复合类型外,还可以按照传递和使用时的特征分为值类型(value types)引用类型(reference types)

1. 值类型

值类型的数据被独立存储在内存中,每次进行赋值操作或者作为函数参数传递时,它们会在内存中复制一份,所以,对这些副本的修改不会影响原始数据。

Solidity 中值类型包括:整形、布尔型、地址型、枚举型、固定长度字节型等。

当调用一个函数时,如果参数为值类型,那么在函数内部对参数数据进行修改,是不会影响到原始数据的,因为操作的数据是原始数据的副本。

我们以固定长度字节型 bytes2 为例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ValueOps {
  // 更新值类型的值
  function update() external pure returns(bytes2){
     // 原始数据 data 的值为 0x1234
     bytes2 data = 0x1234;
    // 调用函数修改它的值
    updateValue(data);
    return data;
  }

   // 修改第一个字符为 0x4567
  function updateValue(bytes2 value) internal pure {
    value = 0x4567;
  }
}

update 函数调用函数 updateValue,参数 data 是值类型 bytes2

updateValue 中对参数变量的数据进行了修改,那么它是否会影响原始数据呢?

我们将以上合约代码复制到 Remix,进行编译,并部署到区块链上,然后调用 update 函数。

我们可以看到,返回的结果是 0x1234,而不是 0x4567,这表明:原始数据未被修改。

由于函数参数的数据类型是值类型,所以,它只是原始数据的副本,对它进行的修改,不会影响原始数据。它与原始数据是独立的两份数据。

2. 引用类型

引用类型,在内存中存储的是对数据的引用,而不是实际的数据本身,实际数据存放在单独的位置。当您创建引用类型的变量时,实际上分配了一个存储指向数据的地址的内存位置。这与 C 语言中的指针非常相似。

Solidity 中引用类型共有三种:数组、结构体 struct 和映射 mapping

当调用一个函数时,如果参数为引用类型,那么在函数内部对参数数据的修改会影响到原始数据。这是因为操作的是引用,而这个引用指向了原始数据,而不是数据的副本。

我们以动态长度字节型 bytes 为例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ReferenceOps {
  // 更新引用类型的值
  function update() external pure returns(string memory){
    // 原始数据 data 的值为 "1234"
    bytes memory data = "1234";
    // 调用函数修改它的第一个字符为 '5'
    updateValue(data);
    // 将其转为字符串,输出最终结果
    return string(data);
  }

  // 修改第一个字符为 '5'
  function updateValue(bytes memory value) internal pure {
    value[0] = '5';
  }
}

update 函数调用函数 updateValue,参数 data 是值类型 bytes 。

updateValue 中对参数变量的数据进行了修改,那么它是否会影响原始数据呢?

我们将以上合约代码复制到 Remix,进行编译,并部署到区块链上,然后调用 update 函数。

我们可以看到,返回的结果是 "5234",而不是 "1234",这表明:原始数据被修改了。

由于函数参数的数据类型是引用类型,所以,它与原始数据实际上指向同一份数据,任何修改都是对同一份数据进行的修改。

我们可以把 value 看做是原始数据 data 的别名。