在Solidity中,有一些数据类型是引用类型,如:
- 数组(string和bytes是特殊的数组,也是引用类型)
- 结构体(struct)
- 映射(mapping)
在Solidity中使用引用类型的时候,必须指定数据的位置。
存储位置
在合约中声明的变量都有一个存储位置,用于指明变量的值存储在哪里。
Solidity提供了三种类型的存储位置:
- storage
- memory
- calldata
storage
链上存储空间。该存储位置用于存储永久数据,只要合约存在数据就一直有效。存储的 Gas 较高。
其中:
- 状态变量的存储方式强制是
storage
; - 局部变量的存储方式可以声明成
storage
、memory
或calldata
; - 映射(mapping)只能存储在
storage
中。
memory
内存存储。即数据存储在内存中,数据只在其生命周期内(函数调用期间)有效。
calldata
调用数据。一个特殊的只读数据位置,用来存储函数调用的参数(包括内部和外部函数),类似于 memory
。使用 calldata
变量的好处是,它不用将数据的副本保存到内存中,并确保不会修改数据。
总结:
- 状态变量的存储方式都是
storage
; - 局部变量的存储方式可以声明成
storage
、memory
或calldata
; - 公用函数(public)和外部函数(external)中,函数参数的存储方式只能是
memory
或calldata
; - 私有函数(private)和内部函数(internal)中,函数参数的存储方式还可以是
storage
; - 映射(mapping)只能存储在
storage
中,且不能在函数内部定义,映射一般声明为状态变量; - 一般建议将函数参数和局部变量的存储方式声明为
memory
。
合约例子
下面是一个合约例子,用来演示数据存储位置的用法。
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;// 变量存储位置和作用域contract DataStorages {// 状态变量强制为 storagestring public str;bytes public bs;uint[] public arr;mapping(address => uint) map;struct Student {string name;uint score;}Student[] public students;// 公共函数(public)和外部函数(external)中,存储位置可以声明成 meomory 和 calldata// 私有函数(private)和内部函数(internal)中,存储位置还可以声明成 storage// 字符串的存储位置function setString(string memory _str) public {str = _str;}// 字节的存储位置function setBytes(bytes memory _bs) public {bs = _bs;}// 数组的存储位置function setArray(uint[] memory _arr) public { for(uint i = 0; i < _arr.length; i++) {arr.push(_arr[i]);}}// 结构体的存储位置function setStruct(Student calldata _student) public {students.push(_student);}// 在内部函数(internal)中,变量可以声明成 storagefunction getLength(uint[] storage _arr) internal view returns(uint) {return(_arr.length);}// 调用内部函数function test() public view returns(uint) {return getLength(arr);}}
**输出:**我们在Remix中编译、部署和运行这个合约例子,执行结果如下图: