在Solidity里存储(storage)、内存(memory)和调用数据(calldata)这三种,这篇文章详细介绍了它们各自有着不同的特性和使用场景。
在 Solidity 里,数据存储方式主要分为存储(storage
)、内存(memory
)和调用数据(calldata
)这三种,它们各自有着不同的特性和使用场景,它们的选择会直接影响存储方式和访问成本。
但是很多小白搞不清楚它们的正确用法以及如何区分,下面我们来详细说明一下:
在 Solidity 中,正确的数据存储位置不仅关系到合约的执行效率,还直接影响交易成本(gas)。
storage
中的数据会被永久保存到区块链上,这意味着每次交易或合约交互后,数据的修改都会被记录下来。storage
会消耗较多的 gas
,所以在使用时要谨慎考虑。storage
中。contract StorageExample {
uint256 public value; // 存储在 storage 中
function setValue(uint256 _value) public {
value = _value; // 修改 storage 变量,需支付 Gas
}
}
contract MemoryExample {
function add(uint a, uint b) public pure returns (uint) {
// 函数参数 a 和 b 存储在 memory 中
// 局部变量 result 也存储在 memory 中
uint result = a + b;
return result;
}
}
function updateName(string calldata _name) external pure returns (string memory) {
return _name; // 只能读取 _name,不能修改
}
为了方便大家更直观清晰的区分它们之间的区别,这里整理了一个表格:
存储位置 | 持久性 | 适用范围 | 修改成本 |
---|---|---|---|
storage | 持久存储 | 状态变量、全局变量 | 高 |
memory | 临时存储 | 局部变量、传递数据 | 低 |
calldata | 只读存储 | 外部函数饿输入参数 | 无 |
原因:storage 是区块链的永久存储,写操作非常昂贵。
优化建议:
原因:复杂数据类型(如数组和结构体)传递会导致内存拷贝。
优化建议:如果数据是只读的,使用 calldata。
问题:频繁访问 block 或 msg 的全局变量会增加 Gas 消耗。
优化建议:在局部变量中缓存全局变量,减少重复读取。
像我刚刚学习的时候,就很分不清 storage、memory、calldata 与 stack 有什么区别,到底 stack 应该怎么用呢?
在 Solidity 里,Stack(栈)是 EVM(以太坊虚拟机)用来管理函数调用和临时数据的一种后进先出(LIFO)数据结构。它是一种重要的数据存储机制,主要用于临时存储和计算中间值。
Stack Too Deep
错误)。uint a = 5;
uint b = 10;
uint c = a + b; // a 和 b 被压入栈,加法操作后结果 c 压入栈
function add(uint a, uint b) public pure returns (uint) {
return a + b; // 参数和结果均在栈中处理
}
function check(uint256 value) public pure returns (bool) {
if (value > 10) { // 编译为 PUSH value, PUSH 10, GT, JUMPI
return true;
}
return false;
}
当函数局部变量或表达式复杂度超过 16 个栈槽 时,编译器会报错。
function tooManyVariables() public pure {
uint256 a; uint256 b; uint256 c; // ... 超过 16 个变量
}
修复方案:
2. 优化 gas 消耗
栈操作直接影响 gas 成本:
// 优化前:多次压栈
uint256 x = a + b;
uint256 y = a + b;
// 优化后:复用计算结果 uint256 sum = a + b; uint256 x = sum; uint256 y = sum;
- 使用内联汇编:手动控制栈操作(需谨慎)
function optimizedAdd(uint256 a, uint256 b) public pure returns (uint256) { assembly { let result := add(a, b) mstore(0x80, result) // 存储到内存 } }
### 栈与内存、存储的对比
| 特性 | Stack | Memory | Storage |
| ---------- | ------- | ----------- | -------- |
| **作用域** | 当前执行上下文 | 函数执行期间 | 合约生命周期 |
| **Gas 成本** | 最低(纯计算) | 中等(临时扩展) | 最高(链上存储) |
| **访问速度** | 最快 | 快 | 慢 |
| **容量限制** | 1024 元素 | 动态扩展(需 Gas) | 无硬性限制 |
|**持久性**|非持久化|非持久化|持久化|
| **适用场景** | 操作数和中间结果 | 临时复杂数据存储 | 持久化变量 |
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!