# storage、memory 和 calldata 的区别

By [Untitled](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936) · 2024-12-05

---

\*\*     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;
        }
    }
    

**对比总结**

![对比总结](https://storage.googleapis.com/papyrus_images/6fb6172c3810b60dae66f270722a651370ca62bf2ddc1b40ccdc3c590732fb99.png)

对比总结

**实战示例：结合三者使用**

    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。

---

*Originally published on [Untitled](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936/storage-memory-calldata)*
