** storage、memory 和 calldata 是数据存储位置的关键概念,用于控制变量的存储方式和生命周期。**
1. Storage
功能: 用于链上持久化存储变量。变量存储在以太坊区块链的状态中,即合约的存储空间。
特点:
持久化,函数调用结束后仍然存在。
写入和读取成本较高(消耗较多 Gas)。
默认用于状态变量。
使用场景:
保存需要持久化的数据(如用户余额、合约状态等)。
定义合约级别的状态变量。
pragma solidity ^0.8.0;
contract StorageExample {
uint256 public storedValue; // 默认是 storage
function setValue(uint256 _value) public {
storedValue = _value; // 数据持久化存储在区块链上
}
function getValue() public view returns (uint256) {
return storedValue; // 从 storage 中读取
}
}
2. Memory
功能: 用于函数执行时的临时数据存储。变量只存在于函数调用期间,调用结束后被销毁。
特点:
非持久化,函数调用完成后即丢失。
读取和写入速度快,Gas 成本低。
常用于函数内部的局部变量。
使用场景:
函数中的临时计算。
传递或处理数据结构(如数组、字符串)。
pragma solidity ^0.8.0;
contract MemoryExample {
function multiply(uint256 a, uint256 b) public pure returns (uint256) {
uint256 result = a * b; // 局部变量存储在 memory 中
return result;
}
function reverseArray(uint256[] memory arr) public pure returns (uint256[] memory) {
uint256 len = arr.length;
uint256[] memory reversed = new uint256Unsupported embed; // 临时数组存储在 memory 中
for (uint256 i = 0; i < len; i++) {
reversed[i] = arr[len - 1 - i];
}
return reversed;
}
}
3. Calldata
功能: 专用于外部函数的输入参数。存储在调用数据中,变量只读且不可修改。
特点:
不可更改,只能读取。
Gas 成本最低,直接引用调用者提供的数据。
必须用于 external 函数的动态大小参数(如数组或字符串)。
使用场景:
外部函数接收用户输入的不可变数据。
提升效率,避免数据复制。
pragma solidity ^0.8.0;
contract CalldataExample {
function sumArray(uint256[] calldata arr) external pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
sum += arr[i]; // 直接从 calldata 中读取
}
return sum;
}
}
对比总结

实战示例:结合三者使用
pragma solidity ^0.8.0;
contract DataExample {
// 状态变量存储在 storage 中
uint256[] public storageArray;
// 添加元素到存储数组
function addToStorage(uint256 _value) public {
storageArray.push(_value); // 修改 storage,消耗较高 Gas。
}
// 处理临时数组数据
function processInMemory(uint256[] memory inputArray) public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < inputArray.length; i++) {
sum += inputArray[i]; // 临时存储在 memory 中。
}
return sum;
}
// 外部调用传递不可变数据
function readFromCalldata(uint256[] calldata inputArray) external pure returns (uint256) {
return inputArray.length; // 直接读取 calldata,节省 Gas。
}
}
如何选择:
• storage:对于需要持久存储的数据(状态变量),必须使用。
• memory:在函数内部使用临时数据时,尤其是小型结构或数组。
• calldata:对于外部函数调用的大型数组或只读参数,使用 calldata 可以显著节省 Gas。
