值类型和引用类型
本章学习在 Solidity
中,什么是值类型和引用类型,以及它们之间的区别。
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
的别名。