solidity基础 -- 存储类型
在Solidity智能合约开发中,存储类型是一个至关重要的概念。它不仅影响合约的性能,还决定了数据的存储位置和生命周期。Solidity提供了三种主要的存储类型:storage
、memory
和calldata
。本文将结合给定的代码示例,并通过更多实例详细介绍 这三种储存类型的区别。
storage
:用于合约状态变量,数据持久保存在区块链上,对其修改会影响合约的长期状态。memory
:用于局部变量,仅在函数执行期间存在,函数结束后数据消失,可修改,但不会影响storage
中的数据。calldata
:专门用于函数参数,是只读的,存储在调用数据中,在函数执行期间不可变。
1. Storage:状态变量与持久化存储
storage
是Solidity中最持久的存储类型,它用于存储合约的状态变量。这些变量会永久存储在区块链上,直到被显式修改或删除。storage
变量的生命周期与合约的生命周期相同,这意味着它们在合约部署后一直存在。
在我们的示例合约中,myStructs
是一个storage
类型的映射,它存储了每个地址对应的MyStruct
结构体。当我们通过myStructs[msg.sender]
访问或修改它时,实际上是在操作区块链上的持久化数据。
myStructs[msg.sender] = MyStruct({foo: 123, text: "bar"});
这行代码将一个MyStruct
实例存储到myStructs
映射中,它会永久保存在区块链上,直到被覆盖或删除。
使用场景
-
存储合约的状态变量,如用户余额、配置参数等。
-
在函数中引用和修改状态变量。
注意事项
-
storage
变量的读写操作成本较高,因为它们需要与区块链交互。 -
修改
storage
变量会消耗更多的Gas。
2. Memory:临时存储与局部变量
memory
是Solidity中用于临时存储数据的存储类型。它用于存储函数内部的局部变量,这些变量仅在函数执行期间存在,函数执行结束后,存储在memory
中的数据会被销毁。
在示例合约中,readOnly
是一个memory
类型的变量,它是myStructs[msg.sender]
的一个副本。对readOnly
的修改不会影响原始的storage
变量:
MyStruct memory readOnly = myStructs[msg.sender];
readOnly.foo = 456;
这行代码将myStructs[msg.sender]
的内容复制到memory
中,并修改了副本的foo
属性。然而,这个修改不会反映到链上的storage
变量中。
使用场景
-
存储函数内部的临时变量。
-
创建数据的副本进行操作,而不影响原始数据。
注意事项
-
memory
变量的生命周期仅限于函数执行期间。 -
创建
memory
变量需要消耗Gas,但比storage
操作更高效。
3. Calldata:函数参数的只读存储
calldata
是Solidity中一种特殊的存储类型,它用于存储函数的输入参数。calldata
变量是只读的,不能被修改。它们存储在内存中,但与memory
不同,calldata
变量不能被分配或重新赋值。
在示例合约中,y
和s
是calldata
类型的参数:
function examples(uint[] calldata y, string calldata s) external returns (uint[] memory)
这行代码声明了两个calldata
参数。y
是一个uint
数组,s
是一个字符串。这些参数在函数调用时由调用者提供,并且在函数内部只能被读取,不能被修改。
使用场景
-
存储函数的输入参数。
-
用于传递大量数据,如数组或结构体,而不需要复制到
memory
。
注意事项
-
calldata
变量是只读的,不能被修改。 -
calldata
变量不能被分配或重新赋值。
4.示例合约:DataLocations
在我们开始之前,先来看一个简单的Solidity合约,它展示了这三种存储类型的使用:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DataLocations {
struct MyStruct {
uint foo;
string text;
}
mapping (address => MyStruct) public myStructs;
function examples(uint[] calldata y, string calldata s) external returns (uint[] memory) {
myStructs[msg.sender] = MyStruct({foo: 123, text: "bar"});
MyStruct storage myStruct = myStructs[msg.sender];
myStruct.text = "foo";
// 修改存储在`storage`中的状态变量
MyStruct memory readOnly = myStructs[msg.sender];
readOnly.foo = 456;
// 修改存储在`memory`中的副本,不影响链上数据
_internal(y);
uint[] memory memArr = new uint[](3);
memArr[0] = 234;
return memArr;
}
function _internal(uint[] calldata y) private {
uint x = y[0];
}
}
其他实例:深入理解存储类型
为了进一步理解这三种存储类型的区别,我们再来看一个简单的示例合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract StorageTypes {
uint public storageVar = 10; // 存储在`storage`中的状态变量
function example() public pure returns (uint memoryVar) {
uint memory memoryVar = 20; // 存储在`memory`中的局部变量
return memoryVar;
}
function example2(uint calldataVar) public pure returns (uint) {
return calldataVar; // `calldata`变量,只读
}
}
总结
storage
:用于合约状态变量,数据持久保存在区块链上,对其修改会影响合约的长期状态。memory
:用于局部变量,仅在函数执行期间存在,函数结束后数据消失,可修改,但不会影响storage
中的数据。calldata
:专门用于函数参数,是只读的,存储在调用数据中,在函数执行期间不可变。
通过深入理解这三种储存类型的区别,开发者可以更好地优化智能合约的性能和资源使用,确保合约的正确性和稳定性。
原文地址:https://blog.csdn.net/Dalik1018/article/details/145311494
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!