简体   繁体   English

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

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

When looking at sample contracts, sometimes arrays are declared in methods with "memory" and sometimes they aren't.在查看示例合同时,有时 arrays 在具有“内存”的方法中声明,有时则不是。 What's the difference?有什么不同?

Without the memory keyword, Solidity tries to declare variables in storage .如果没有memory关键字,Solidity 会尝试在storage 中声明变量。

Lead Solidity dev chriseth: “You can think of storage as a large array that has a virtual structure… a structure you cannot change at runtime - it is determined by the state variables in your contract”.首席 Solidity 开发人员 chriseth:“您可以将存储视为具有虚拟结构的大型阵列……一种您无法在运行时更改的结构 - 它由合约中的状态变量决定”。

That is, the structure of storage is set in stone at the time of contract creation based on your contract-level variable declarations and cannot be changed by future method calls.也就是说,存储结构在合约创建时基于你的合约级变量声明是一成不变的,并且不能被未来的方法调用改变。 BUT -- the contents of that storage can be changed with sendTransaction calls.但是——该存储的内容可以通过 sendTransaction 调用进行更改。 Such calls change “state” which is why contract-level variables are called “state variables”.这种调用会改变“状态”,这就是合约级变量被称为“状态变量”的原因。 So a variable uint8 storagevar;所以一个变量 uint8 storagevar; declared at the contract level can be changed to any valid value of uint8 (0-255) but that “slot” for a value of type uint8 will always be there.在合约级别声明的可以更改为 uint8 (0-255) 的任何有效值,但 uint8 类型值的“槽”将始终存在。

If you declare variables in functions without the memory keyword, then solidity will try to use the storage structure, which currently compiles, but can produce unexpected results.如果在函数中声明变量时没有使用memory关键字,那么 Solidity 将尝试使用当前编译的存储结构,但会产生意外结果。 memory tells solidity to create a chunk of space for the variable at method runtime, guaranteeing its size and structure for future use in that method. memory告诉 Solidity 在方法运行时为变量创建一块空间,保证它的大小和结构供将来在该方法中使用。

memory cannot be used at the contract level.内存不能在合约级别使用。 Only in methods.只在方法上。

See the the entry "What is the memory keyword? What does it do?"请参阅条目“内存关键字是什么?它有什么作用?” in the FAQ.在常见问题解答中。 I quote it here:我在这里引用:

The Ethereum Virtual Machine has three areas where it can store items.以太坊虚拟机具有三个可以存储物品的区域。

The first is “storage”, where all the contract state variables reside.第一个是“存储”,所有合约状态变量都在其中。 Every contract has its own storage and it is persistent between function calls and quite expensive to use.每个合约都有自己的存储空间,并且在函数调用之间是持久的,并且使用起来非常昂贵。

The second is “memory”, this is used to hold temporary values.第二个是“内存”,用于保存临时值。 It is erased between (external) function calls and is cheaper to use.它在(外部)函数调用之间被擦除,使用起来更便宜。

The third one is the stack, which is used to hold small local variables.第三个是栈,用来存放小的局部变量。 It is almost free to use, but can only hold a limited amount of values.它几乎可以免费使用,但只能保存有限数量的值。

For almost all types, you cannot specify where they should be stored, because they are copied everytime they are used.对于几乎所有类型,您都无法指定它们的存储位置,因为每次使用它们时都会复制它们。

The types where the so-called storage location is important are structs and arrays.所谓的存储位置很重要的类型是结构和数组。 If you eg pass such variables in function calls, their data is not copied if it can stay in memory or stay in storage.例如,如果您在函数调用中传递此类变量,则它们的数据如果可以保留在内存中或保留在存储中,则不会被复制。 This means that you can modify their content in the called function and these modifications will still be visible in the caller.这意味着您可以在被调用的函数中修改它们的内容,并且这些修改在调用者中仍然可见。

There are defaults for the storage location depending on which type of variable it concerns:存储位置有默认值,具体取决于它涉及的变量类型:

  • state variables are always in storage状态变量总是在存储中
  • function arguments are always in memory函数参数总是在内存中
  • local variables of struct, array or mapping type reference storage by default struct、array或mapping类型的局部变量默认引用存储
  • local variables of value type (ie neither array, nor struct nor mapping) are stored in the stack值类型的局部变量(即既不是数组,也不是结构体也不是映射)存储在堆栈中

memory defines one of the data location in Solidity that can holds the value temporary only during the runtime. memory定义了 Solidity 中的数据位置之一,它只能在运行时临时保存值。
the memory variables in Solidity can only be declared within the methods and usually used in method parameters. Solidity 中的memory变量只能在方法中声明,通常用于方法参数中。 a short term variable that cannot be saved in blockchain... that holds the value only during the execution of a function and its value destroys after execution.一个不能保存在区块链中的短期变量......它只在函数执行期间保存值,并且它的值在执行后销毁。

take a look at example function f() in which I declared a pointer using memory keyword.. it will not alter the value of variable User , whereas if it was declared using storage it will change the value of variable User stored in blockchain and value will not be destroyed...看一下示例函数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
}

When people talk about Storage and Memory in Solidity, they can actually be referring to two different uses of these words .当人们在 Solidity 中谈论StorageMemory时,实际上可以指代这两个词的两种不同用法 And this causes a lot of confusion.这会引起很多混乱。

The two uses are:两种用途是:

  1. Where a Solidity contract store data Solidity 合约存储数据的位置
  2. How Solidity variables store values Solidity 变量如何存储值

Examples of each:每个示例:

1. Where a Solidity contract store data: As Yilmaz correctly points out, in the first usage storage and memory can be thought of as similar to a hard drive (long-term, persistent storage) and RAM (temporary) respectively. 1. Solidity 合约存储数据的位置:正如 Yilmaz 正确指出的那样,在第一次使用时,存储和 memory 可以被认为分别类似于硬盘驱动器(长期、持久存储)和 RAM(临时)。

For example:例如:

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

contract StorageMemory1{
    uint storageVariable;

    constructor() {
    }

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

In the example above the value of 'storageVariable' will be saved even as we execute different functions over time .在上面的例子中,'storageVariable' 的值将被保存,即使我们随着时间的推移执行不同的函数 However, 'memoryVariable' is created when the 'assignToValue' function is called and then disappears forever after the function is complete.但是,当调用“assignToValue”function 时会创建“memoryVariable”,然后在 function 完成后永远消失。

2. How Solidity variables store values: If you see an error that says something like 'Data location must be "storage", "memory" or "calldata" for variable, but none was given.' 2. Solidity 变量如何存储值:如果您看到类似“数据位置必须是变量的“存储”、“内存”或“调用数据”之类的错误,但没有给出。 then this is what it is referring to.那么这就是它所指的。 This is best understood using an example.使用示例可以最好地理解这一点。

For example:例如:

You would get the above error with the following code:您将使用以下代码收到上述错误:

// 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
    }
}

But if you add the word '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[] 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
    }
}

Notice what adding the word 'storage' does: it makes the 'newArray' variable reference (or point to) the 'values' variable, and modifying 'newArray' also modifies 'values' .注意添加单词 'storage' 的作用:它使 'newArray' 变量引用(或指向) 'values' 变量,并且修改 'newArray' 也会修改 'values'

However, if we instead use 'memory' , notice what gets logged:但是,如果我们改为使用 '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
    }
}

Using memory creates a copy variable, which does not reference the 'values' array.使用 memory 创建一个复制变量,它不引用“值”数组。

And in case you are interested, ' calldata ' can be used to pass a variable as read-only:如果您有兴趣,可以使用“ 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
    }
}
  • Storage holds data between function calls.存储保存函数调用之间的数据。 it is llike computer hard drive.它就像电脑硬盘。 variables are storage data变量是存储数据

  • Memory temporary place to store data, like RAM.内存临时存放数据的地方,比如 RAM。 Function args are memory data函数参数是内存数据

Let s say one array is a storage type假设一个数组是一种存储类型

// 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 in this case myArray will point to the same address as "numbers". int[] storage myArray=numbers在这种情况下 myArray 将指向与“numbers”相同的地址。 (it is similar to how referencing objects behave in javascript). (它类似于引用对象在 javascript 中的行为方式)。 See in the function I added 5, then 10 to "numbers" which is placed into Storage.请参阅我在函数中添加 5,然后将 10 添加到放入存储中的“数字”。 But if you deploy the code on remix and get numbers[0] , you will get 1 because of myArray[0]=1但是如果你在 remix 上部署代码并得到numbers[0] ,你会得到 1 因为myArray[0]=1

if you define myArray as memory it will be a different story.如果您将 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 
}

In this case, "numbers" array is copied into Memory, and myArray now references a memory address which different from "numbers" address.在这种情况下,“数字”数组被复制到内存中,而 myArray 现在引用了一个与“数字”地址不同的内存地址。 if you deploy this code and reach numbers[0] you will get 5.如果您部署此代码并达到numbers[0]您将获得 5。

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

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