繁体   English   中英

在 Ethereum Solidity 中,“memory”关键字的作用是什么?

[英]In Ethereum Solidity, what is the purpose of the "memory" keyword?

在查看示例合同时,有时 arrays 在具有“内存”的方法中声明,有时则不是。 有什么不同?

如果没有memory关键字,Solidity 会尝试在storage 中声明变量。

首席 Solidity 开发人员 chriseth:“您可以将存储视为具有虚拟结构的大型阵列……一种您无法在运行时更改的结构 - 它由合约中的状态变量决定”。

也就是说,存储结构在合约创建时基于你的合约级变量声明是一成不变的,并且不能被未来的方法调用改变。 但是——该存储的内容可以通过 sendTransaction 调用进行更改。 这种调用会改变“状态”,这就是合约级变量被称为“状态变量”的原因。 所以一个变量 uint8 storagevar; 在合约级别声明的可以更改为 uint8 (0-255) 的任何有效值,但 uint8 类型值的“槽”将始终存在。

如果在函数中声明变量时没有使用memory关键字,那么 Solidity 将尝试使用当前编译的存储结构,但会产生意外结果。 memory告诉 Solidity 在方法运行时为变量创建一块空间,保证它的大小和结构供将来在该方法中使用。

内存不能在合约级别使用。 只在方法上。

请参阅条目“内存关键字是什么?它有什么作用?” 在常见问题解答中。 我在这里引用:

以太坊虚拟机具有三个可以存储物品的区域。

第一个是“存储”,所有合约状态变量都在其中。 每个合约都有自己的存储空间,并且在函数调用之间是持久的,并且使用起来非常昂贵。

第二个是“内存”,用于保存临时值。 它在(外部)函数调用之间被擦除,使用起来更便宜。

第三个是栈,用来存放小的局部变量。 它几乎可以免费使用,但只能保存有限数量的值。

对于几乎所有类型,您都无法指定它们的存储位置,因为每次使用它们时都会复制它们。

所谓的存储位置很重要的类型是结构和数组。 例如,如果您在函数调用中传递此类变量,则它们的数据如果可以保留在内存中或保留在存储中,则不会被复制。 这意味着您可以在被调用的函数中修改它们的内容,并且这些修改在调用者中仍然可见。

存储位置有默认值,具体取决于它涉及的变量类型:

  • 状态变量总是在存储中
  • 函数参数总是在内存中
  • struct、array或mapping类型的局部变量默认引用存储
  • 值类型的局部变量(即既不是数组,也不是结构体也不是映射)存储在堆栈中

memory定义了 Solidity 中的数据位置之一,它只能在运行时临时保存值。
Solidity 中的memory变量只能在方法中声明,通常用于方法参数中。 一个不能保存在区块链中的短期变量......它只在函数执行期间保存值,并且它的值在执行后销毁。

看一下示例函数f() ,其中我使用memory关键字声明了一个指针……它不会改变变量User的值,而如果使用storage声明它,它将更改存储在区块链中的变量User的值和 value不会被破坏...

struct User {
 string name;
}
User[] users;

function f() external {
 User memory user = users[0]; // create a pointer
 user.name = "example name" // can't change the value of struct User
}

当人们在 Solidity 中谈论StorageMemory时,实际上可以指代这两个词的两种不同用法 这会引起很多混乱。

两种用途是:

  1. Solidity 合约存储数据的位置
  2. Solidity 变量如何存储值

每个示例:

1. Solidity 合约存储数据的位置:正如 Yilmaz 正确指出的那样,在第一次使用时,存储和 memory 可以被认为分别类似于硬盘驱动器(长期、持久存储)和 RAM(临时)。

例如:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract StorageMemory1{
    uint storageVariable;

    constructor() {
    }

    function assignToValue(uint memoryVariable) public {
        storageVariable = memoryVariable;
    }
}

在上面的例子中,'storageVariable' 的值将被保存,即使我们随着时间的推移执行不同的函数 但是,当调用“assignToValue”function 时会创建“memoryVariable”,然后在 function 完成后永远消失。

2. Solidity 变量如何存储值:如果您看到类似“数据位置必须是变量的“存储”、“内存”或“调用数据”之类的错误,但没有给出。 那么这就是它所指的。 使用示例可以最好地理解这一点。

例如:

您将使用以下代码收到上述错误:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract StorageMemory2 {
    uint[] public values;

    function doSomething() public
    {
        values.push(5);
        values.push(10);

        uint[] newArray = values; // The error will show here
    }
}

但如果你加上“记忆”这个词:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import 'hardhat/console.sol'; // to use console.log

contract StorageMemory2 {
    uint[] public values;

    function doSomething() public
    {
        values.push(5);
        values.push(10);

        console.log(values[0]); // it will log: 5

        uint[] storage newArray = values; // 'newArray' references/points to 'values'

        newArray[0] = 8888;

        console.log(values[0]); // it will log: 8888
        console.log(newArray[0]); // it will also log: 8888
    }
}

注意添加单词 'storage' 的作用:它使 'newArray' 变量引用(或指向) 'values' 变量,并且修改 'newArray' 也会修改 'values'

但是,如果我们改为使用 'memory' ,请注意记录的内容:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import 'hardhat/console.sol'; // to use console.log

contract StorageMemory2 {
    uint[] public values;

    function doSomething() public
    {
        values.push(5);
        values.push(10);

        console.log(values[0]); // it will log: 5

        uint[] memory newArray = values; // 'newArray' is a separate copy of 'values'

        newArray[0] = 8888;

        console.log(values[0]); // it will log: 5
        console.log(newArray[0]); // it will log: 8888
    }
}

使用 memory 创建一个复制变量,它不引用“值”数组。

如果您有兴趣,可以使用“ calldata ”以只读方式传递变量:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract CallDataExample {
    uint[] public values;

    function doSomething() public
    {
        values.push(5);
        values.push(10);

        modifyArray(values);
    }

    function modifyArray(uint[] calldata arrayToModify) pure private {
        arrayToModify[0] = 8888; // you will get an error saying the array is read only
    }
}
  • 存储保存函数调用之间的数据。 它就像电脑硬盘。 变量是存储数据

  • 内存临时存放数据的地方,比如 RAM。 函数参数是内存数据

假设一个数组是一种存储类型

// state variables are placed in Storage
int[] public numbers

function Numbers()public{
    numbers.push(5)
    numbers.push(10)
    int[] storage myArray=numbers
    // numbers[0] will also be changed to 1
    myArray[0]=1 
}

int[] storage myArray=numbers在这种情况下 myArray 将指向与“numbers”相同的地址。 (它类似于引用对象在 javascript 中的行为方式)。 请参阅我在函数中添加 5,然后将 10 添加到放入存储中的“数字”。 但是如果你在 remix 上部署代码并得到numbers[0] ,你会得到 1 因为myArray[0]=1

如果您将 myArray 定义为内存,那将是另一回事。

// state variables are placed in Storage
int[] public numbers

function Numbers() public{
    numbers.push(5)
    numbers.push(10)
    int[] memory myArray=numbers
    myArray[0]=1 
}

在这种情况下,“数字”数组被复制到内存中,而 myArray 现在引用了一个与“数字”地址不同的内存地址。 如果您部署此代码并达到numbers[0]您将获得 5。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM